Git And Collaboration Workflows
Rebase Fundamentals
Rebase copies commits onto a new base. It can produce a linear review history, but the copied commits receive new object IDs.
Why This Matters
A rebase is useful for updating a private branch or preparing a focused series, but careless rebasing of shared work causes duplicate histories and confusing force pushes.
Working Model
Git identifies commits reachable from the topic branch but not the chosen upstream, resets the topic to the new base, and reapplies those changes in order. Conflicts are resolved one replayed commit at a time.
Practical Rules
- Fetch before rebasing so the target base is current.
- Rebase only work whose history you are allowed to rewrite.
- Use
git rebase --continue,--skip, or--abortdeliberately. - Run tests after the complete rebase, not only after individual conflicts.
- Push rewritten private branches with
--force-with-lease, never an unqualified force.
Failure Modes
- Rebasing a shared branch after others based work on its old commits.
- Resolving every conflict by choosing one side without preserving combined behavior.
- Using
git pull --rebasewithout understanding local uncommitted changes. - Assuming identical patches have identical commit IDs after rebasing.
Verification
- Compare the pre-rebase and post-rebase ranges.
- Inspect the graph and merge base.
- Run the same tests used before rebasing.
- Use
git push --force-with-leaseonly after confirming the expected remote tip.
Official References
What You Should Be Able To Do
After this lesson, you should be able to explain how rebase copies commits, when history rewriting is appropriate, and how to finish or abort a conflicted rebase, choose a suitable approach for a real PHP project, and verify the result instead of relying on assumptions.
Rebase Copies Commits Onto A New Base
Rebase takes commits from one line of history and replays their changes on another base commit. The resulting commits have new IDs because their parent changed and the snapshot may change after conflict resolution. Rebase does not move time backward; it creates a new version of your local work.
This is useful when your feature branch should be reviewed as if it started from the current main. Instead of adding a merge commit from main into your branch, rebase copies your feature commits onto the updated base. The history becomes linear, and reviewers can often read your work more easily.
Local Versus Shared History
The main safety rule is simple: rebase commits you own; coordinate before rebasing commits others may have based work on. If you rebase a pushed branch and force-push it, collaborators with the old commits now have a different history. They must reconcile their work with your rewritten branch.
For a personal pull-request branch, rebasing and force-pushing may be normal. Use --force-with-lease, not plain --force, because it refuses to overwrite remote work you have not fetched. For shared branches, merge or revert is usually safer.
Rebase And Conflict Resolution
During rebase, Git applies one commit at a time. A conflict means the current replayed commit does not apply cleanly to the new base. Resolve the files, stage the result, and run git rebase --continue. If the rebase is wrong, git rebase --abort returns to the original branch state.
Because each commit is replayed separately, you may see conflicts that a merge would show only once. The benefit is that each resolved commit can remain logical. The cost is attention. Do not blindly accept conflict resolutions just because the first one worked.
What Rebase Changes In Review
A rebase can make a pull request easier to review by removing old merge commits and showing the feature against current code. It can also make review harder if a force push rewrites commits reviewers already examined. When rewriting after review, leave a comment explaining the change or use git range-diff to compare the old and new series.
Rebase can also reveal hidden assumptions. A test that passed before may fail after replaying onto the latest base because another branch changed an API, migration, or dependency. That is useful information. Fix the feature against the code it will actually merge into.
Rebase Is Not A Cleanup Wand
Do not use rebase to hide meaningful history after a problem has been shared. If a commit broke production and was reverted, the revert is useful history. If a branch contains a series of local work-in-progress commits before review, cleaning them up is reasonable. Context decides.
Also avoid rebasing just to satisfy a preference for linear history when the team uses merge commits for audit. Repository history is a collaboration artifact. Follow project policy unless there is a clear reason to discuss changing it.
Rebase Workflow
A common safe workflow is: fetch, inspect the branch, create a safety branch if the work is important, run git rebase origin/main, resolve conflicts, run tests, inspect the graph, then push with --force-with-lease if this is your review branch and policy allows it.
The safety branch is cheap insurance. If the rebase becomes confusing, you can compare or return to the old branch. Reflog is another backup, but an explicit branch is easier to see.
Rebase With Migrations And Generated Files
PHP projects often include migrations, lock files, and generated clients. Rebasing across changes to these files can produce syntactically valid but semantically wrong results. Two migrations may now run in the wrong order. A lock file may need regeneration. A generated API client may need to match the current schema.
After rebase, run the project-specific checks that cover those artifacts. Do not assume a conflict-free rebase means the branch is still correct.
After this lesson, you should be able to explain rebase as commit replay, decide when rewriting local history is appropriate, resolve conflicts during rebase, use --force-with-lease safely, communicate rewritten review branches, and retest PHP-specific artifacts after replaying work onto a new base.
Rebasing A Pull Request Safely
Before rebasing a pull request branch, fetch the remote and record the current branch tip. A temporary name such as backup/my-feature-before-rebase is cheap insurance. Then rebase onto the intended base, resolve conflicts commit by commit, and run the checks that match the changed area. Only push with --force-with-lease after confirming the remote branch has not advanced unexpectedly.
If the rebase only moved commits onto a newer base, say so in the pull request. If you also changed content, summarize it. Reviewers need to know whether they can trust earlier review comments or should read the branch again. A force push without explanation wastes review time.
Rebase is also useful for discovering accidental dependency on old code. When a commit no longer applies cleanly, ask whether the new base already solved the problem, changed the API, or exposed a hidden conflict. The conflict is not just an obstacle; it is information about integration.
Rebase And Bisectability
Some repositories care that every commit in a series builds and passes tests. Others squash merge and care mainly about the final diff. Know the local expectation before spending time polishing every intermediate commit. If commits are preserved, do not reorder them into a sequence where an early commit calls a function introduced later.
A well-rebased series can make debugging easier. When each commit represents one logical change, git bisect can identify a regression more precisely. When one commit contains migration, behavior, formatting, and generated output, bisect points to a bundle rather than a cause.
Common Rebase Mistakes
One mistake is rebasing the wrong branch. Always check git status -sb before starting. If you meant to update feature/refunds but are on main, stop. Another mistake is rebasing with uncommitted work. Stash or commit intentionally first so conflict resolution does not mix with unrelated local edits.
A third mistake is force-pushing without fetching. --force-with-lease protects you only when your local view of the remote is current enough to notice unexpected work. Fetch first, inspect, then push. If the lease fails, do not override it; find out who changed the branch.
Finally, teams sometimes rebase to hide conflicts instead of resolving them thoughtfully. A conflict resolution should preserve behavior from both the feature and the new base. After a difficult rebase, inspect the final diff against the merge base, not only the conflict markers you removed.
Practice Exercise
Create a branch from an older commit, make two small commits, then advance main with a conflicting edit. Rebase the branch onto main, resolve the conflict, and inspect the graph. Repeat the exercise with a merge so you can compare the history shape and review experience. The goal is not to memorize one preferred command; it is to see exactly what history each integration method creates. Write down when each result would be appropriate on your team, including who must be warned before a rewritten branch is pushed. A branch is safe to force-push only when it is yours, the remote has not advanced unexpectedly, the repository policy permits rewritten review branches, and required checks pass after the rewrite. If any condition is uncertain, ask before pushing.
Practice
Practice: Update A Private Branch
A private feature branch is three commits behind origin/main. Write the safe rebase workflow.
Your answer must:
- state the intended outcome;
- show the commands, data flow, or implementation shape;
- identify at least one unsafe alternative;
- explain how the result will be verified.
Show solution
Start clean, fetch, record the old tip, run git rebase origin/main, resolve each conflict, run checks, inspect the graph, and push with --force-with-lease if the branch was already published.
The important part is not memorising one command or vendor screen. The solution makes the invariant, failure behavior, and verification evidence explicit.
Practice: Abort A Confused Rebase
A conflict resolution is no longer trustworthy midway through a rebase. Explain how to return to the pre-rebase state.
Your answer must:
- state the intended outcome;
- show the commands, data flow, or implementation shape;
- identify at least one unsafe alternative;
- explain how the result will be verified.
Show solution
Inspect git status, preserve any external notes if needed, then run git rebase --abort. Confirm the branch tip and working tree match the recorded pre-rebase state before trying a better approach.
The important part is not memorising one command or vendor screen. The solution makes the invariant, failure behavior, and verification evidence explicit.
Practice: Choose Merge Or Rebase
Compare updating a private feature branch with updating a shared release branch.
Your answer must:
- state the intended outcome;
- show the commands, data flow, or implementation shape;
- identify at least one unsafe alternative;
- explain how the result will be verified.
Show solution
Rebase is reasonable for the private branch because its owner controls the history. Prefer a merge or another non-rewriting workflow for the shared release branch because other work may already depend on its commit IDs.
The important part is not memorising one command or vendor screen. The solution makes the invariant, failure behavior, and verification evidence explicit.