Rebase vs Merge
Your feature branch and main can both advance after they split. Before opening or completing a pull request, you may need the newer main changes on your feature branch. Merge and rebase solve that problem differently.
The starting history
Section titled “The starting history”A──B──C main └──F1──F2 feature/loginB is the shared ancestor. C was added to main; F1 and F2 were added to the feature branch. Neither tip is an ancestor of the other, so simply moving a branch label forward cannot combine them.
Option 1: merge main into the feature branch
Section titled “Option 1: merge main into the feature branch”git switch feature/logingit fetch origingit merge origin/mainA──B──C─────────┐ main ends at C └──F1──F2──M feature/login ends at MM is a new merge commit with two parents: F2 and C. The existing commit objects keep their identities. The graph records when the two lines came together. This is a good default when commits have already been shared or preserving the branch’s development history matters.
Fetching first only updates origin/main, your local view of the remote branch. It does not move local main or your feature branch. The subsequent merge is the integration step.
Option 2: rebase the feature branch
Section titled “Option 2: rebase the feature branch”git switch feature/logingit fetch origingit rebase origin/mainA──B──C──F1′──F2′ feature/login └ mainGit finds the commits on the feature branch since the common ancestor, then replays their changes onto C. The resulting F1′ and F2′ are new commit objects with new hashes: their parents changed. The originals may remain temporarily recoverable through the reflog, but the branch now points to the replayed commits. Rebase gives a linear history; it does not magically combine both histories without rewriting the feature commits.
When you later merge this rebased feature into an unchanged main, a fast-forward is possible. Whether the hosting platform creates a merge commit, squashes, or rebases at pull-request completion depends on the chosen merge method and repository rules.
If a rebase stops at a conflict
Section titled “If a rebase stops at a conflict”Git pauses while replaying the conflicting commit. Inspect the files, choose the correct final content, remove conflict markers, and stage the resolved files:
git statusgit add path/to/resolved-file.csgit rebase --continueRepeat for each conflict. If you need to return to the pre-rebase state instead:
git rebase --abortDo not run git rebase --continue before resolving and staging the conflict. Run the project tests after the replay: individually valid commits may interact differently with the updated base.
Which one should you choose?
Section titled “Which one should you choose?”| Situation | Usually choose | Why |
|---|---|---|
| Updating a local, unpublished feature branch | Rebase or merge | Rebase can keep the history linear; merge preserves the original graph |
| Updating a branch other people use | Merge | Rebase changes commit identities they may already have |
| Integrating an approved pull request | Repository’s PR merge policy | The team may require merge, squash, or rebase merge |
| Keeping a detailed record of integration points | Merge | The merge commit records the join |
Never casually rebase shared main or another shared branch. If a feature branch was already pushed, a rebase will normally make an ordinary push fail because the remote still points to the old history. On a branch that only you own, after checking nobody else depends on it and after coordinating around an open pull request, you may use:
git push --force-with-lease origin feature/login--force-with-lease refuses the update if the remote branch moved since your last observation; it is safer than plain --force, but it still rewrites that remote branch and can disrupt collaborators or invalidate review context. Never use it to bypass protection on shared main.
Backend example
Section titled “Backend example”Your feature/login branch adds a .NET authentication endpoint. Meanwhile, main changes the user entity. Fetch first. If your branch is yours alone, rebasing onto origin/main can let you adapt the endpoint to the new entity before the pull request. If teammates also use the feature branch, merge origin/main into it instead. In either case, run dotnet test and push the updated feature branch for CI and review.
Remember
Section titled “Remember”- Merge adds a join commit when histories diverged and preserves existing commits.
- Rebase replays feature commits on a new base, producing new hashes.
- Conflicts are possible with either method; a linear graph is not proof that the code works.
- Avoid rewriting shared history. Use the repository’s PR policy for the final integration.
Check your understanding
Section titled “Check your understanding”- Why does
F1get a new hash after rebasing ontoC? - Which method would you normally use if teammates have already based work on your feature branch?
- What is the difference between
git rebase --abortandgit rebase --continue?