Rebase vs merge
Should you merge main into your feature branch or rebase onto it? Learn what each does to history and when to pick which.
What you'll learn
- What rebase does: it replays your commits on top of another branch, rewriting their SHAs into a linear chain
- When to choose merge (preserve true history) vs rebase (clean linear log)
- Interactive rebase with git rebase -i: squash, reword, and drop commits before sharing
Before you start
The setup: two branches that have diverged
Suppose main has moved forward since you branched off. Your feature branch has two commits (F1, F2) and main has gained one (M1):
main: A --- B --- M1
\
feature: F1 --- F2
You need to incorporate M1 into your feature branch. You have two options.
Option 1: Merge
Running git merge main from your feature branch creates a new merge commit (Mc) that has two parents — the tip of your branch and the tip of main.
# from your feature branch
git merge main
main: A --- B --- M1
\ \
feature: F1 --- F2 --- Mc
The merge commit records exactly when and how the two lines of work joined. That is the entire point: the history is a faithful record of what actually happened.
Merge trade-offs
| Pros | Cons |
|---|---|
| Preserves the true branching history | Merge commits add noise to git log on busy projects |
| Safe — never rewrites existing SHAs | History can look tangled with many parallel branches |
| Ideal for long-lived, shared branches |
Option 2: Rebase
Rebase (short for “re-apply onto a new base”) takes each of your branch’s commits and replays them — one by one — on top of the tip of the target branch. Each replayed commit gets a brand-new SHA because its parent has changed.
# from your feature branch
git rebase main
Git detaches your commits and re-applies them:
Before:
main: A --- B --- M1
\
feature: F1 --- F2
After rebase:
main: A --- B --- M1
\
feature: F1' --- F2'
F1' and F2' contain the same diffs as F1 and F2, but they have new SHAs and M1 is their ancestor. There is no merge commit — the result is a linear history.
Rebase trade-offs
| Pros | Cons |
|---|---|
Linear, easy-to-read git log | Rewrites SHAs — dangerous if others have your commits |
| No merge commit noise | Conflicts must be resolved commit-by-commit |
Makes git bisect and git log --oneline cleaner | Obscures the true parallelism of the work |
Side-by-side diagram
Resolving conflicts during a rebase
Because rebase replays commits one at a time, a conflict can appear at any replay step. Git pauses and tells you which commit it is applying:
git rebase main
# CONFLICT (content): Merge conflict in src/auth.ts
# error: could not apply f1a3c2e... Add login validation
Fix the conflict in your editor, stage the resolved file, then continue:
# after editing the conflicted file
git add src/auth.ts
git rebase --continue
Git moves on to the next commit and repeats until done. If you want to bail out entirely:
git rebase --abort # restores the branch to its pre-rebase state
The Golden Rule
Interactive rebase: tidying up before you share
git rebase -i (interactive rebase) lets you rewrite the history of your own local commits before pushing. You can squash noisy “WIP” commits into one clean commit, reword a message, or drop an accidental commit entirely.
# rewrite the last 3 commits interactively
git rebase -i HEAD~3
Git opens an editor with a list of commits and action keywords:
pick a1b2c3f Add login validation
pick 9d4e5f6 WIP: fix typo
pick 3c7a8b9 Fix edge case in token expiry
# Commands:
# p, pick = use commit as-is
# r, reword = use commit but edit the message
# s, squash = fold into the previous commit
# d, drop = remove the commit entirely
Change pick to squash on the second line and save:
pick a1b2c3f Add login validation
squash 9d4e5f6 WIP: fix typo
pick 3c7a8b9 Fix edge case in token expiry
Git combines the first two commits and prompts you for a combined message. The result is a cleaner two-commit history ready to push as a PR.
Interactive rebase is powerful precisely because it operates only on local, un-pushed commits — which is exactly where the Golden Rule permits rewriting.
When to use which
| Situation | Reach for |
|---|---|
| Syncing a local feature branch with main before opening a PR | git rebase main |
| Merging a finished PR into main on GitHub | git merge (or a squash merge via the UI) |
| Combining noisy WIP commits before pushing | git rebase -i |
| Long-lived shared branches that multiple people push to | git merge |
| You want a permanent record that two features were developed in parallel | git merge |