Merge vs Rebase
The most-asked git interview question. Both integrate changes from one branch into another; they produce different histories and have different trade-offs.
What they do
Starting state:
main: A───B───C
\
feature: D───E───F
Merge
git checkout main
git merge feature
main: A───B───C───────M
\ /
feature: D───E───F
Creates a new “merge commit” M with two parents (C and F). All original commits stay; history shows the branch existed.
Rebase
git checkout feature
git rebase main
main: A───B───C
\
feature: D'───E'───F'
Replays each feature commit on top of main. New commits D'/E'/F' have new SHAs (different parent → different hash). History is linear.
Then a fast-forward merge moves main to F':
git checkout main
git merge feature # fast-forward
main: A───B───C───D'───E'───F'
Side by side
| Merge | Rebase | |
|---|---|---|
| History shape | branched | linear |
| Original commits preserved? | yes | no — rewritten with new SHAs |
| Adds a merge commit? | yes (unless fast-forward) | no |
| Conflicts resolved | once, in the merge commit | per commit being replayed |
| Safe to do on shared branches? | yes | no — rewrites history |
Easier to read git log |
shows real timeline | shows narrative |
git blame accuracy |
preserves original authorship | preserved (author kept; only commit hash changes) |
When to use which
Use merge when:
- The branch is shared (others have pulled it). Rewriting public history breaks their checkouts.
- You want history to record that a branch existed.
- The branch is long-lived (release branch, integration branch).
Use rebase when:
- The branch is yours and unpushed (or only you’ve pulled).
- You want a clean linear history before the PR is merged.
- You’re updating a feature branch with the latest
mainbefore merging.
The standard workflow: rebase your feature branch onto latest main locally, then either fast-forward merge or squash-merge into main.
The “golden rule of rebase”
Never rebase a branch that other people have based work on.
If you rebase feature and someone else has commits on top of feature, their history diverges from yours. They’ll get phantom duplicates when they try to merge.
If you must rebase a shared branch (rare), coordinate with everyone, force-push (--force-with-lease), and have everyone reset their local copy.
git pull --rebase vs git pull (merge)
git pull # fetches + merges (creates merge commits if you had local commits)
git pull --rebase # fetches + rebases your local commits on top
git pull (merge) on a busy branch produces noisy “Merge branch ‘main’ of github.com:…” commits. git pull --rebase keeps history clean.
Set as default:
git config --global pull.rebase true
Squash merge — the third option
GitHub/GitLab “Squash and merge” combines all PR commits into one commit on main:
main: A───B───C
\
feature: D───E───F
After "Squash and merge":
main: A───B───C───[D+E+F as one commit S]
Pros: clean main history (one commit per PR), trivial to revert a feature. Cons: loses the per-commit context of how the feature was built; the original branch commits don’t exist on main.
The most common modern team setup: feature branches are messy (WIP commits, fixups), squash-merge to main = one tidy commit per PR. Rebase only used to update the feature branch with latest main.
Three merge strategies on the PR side
| GitHub option | What ends up on main |
|---|---|
| Create a merge commit | preserves all feature commits + adds merge commit |
| Squash and merge | one commit on main with all changes |
| Rebase and merge | feature commits replayed on main, no merge commit |
Most teams pick squash or rebase-merge for application code. Library/framework codebases often prefer regular merge to preserve granular history.
Conflicts: merge vs rebase experience
Merge conflict: resolve once, all conflicting changes land in the merge commit.
Rebase conflict: resolve once per commit being replayed. If 3 of your 10 commits touch the conflicting file, you may resolve 3 times. Use git rerere to remember resolutions and replay them automatically (see 12_conflict_resolution.md).
Common interview confusions
- “Rebase deletes commits.” — it copies them to new SHAs and abandons the originals. Originals stay in reflog for ~90 days.
- “Merge commits are bad.” — only when redundant. A real integration of two parallel histories deserves a merge commit. Pull-merge commits (“Merge branch ‘main’ of …”) are noise.
- “Fast-forward merge is the same as rebase.” — fast-forward happens after a rebase (or when there are no diverging commits). Rebase rewrites history; fast-forward just moves a pointer.
- “You can rebase pushed commits as long as you
--force.” — you can, but you’ll wreck anyone who pulled them. Use--force-with-leaseand only on branches you own.
Interview angle
- “Rebase vs merge — when do you pick which?” — rebase to keep your local feature branch up to date with main and produce linear history; merge for shared/long-lived branches and to preserve “this was a parallel branch” record.
- “What’s the golden rule of rebase?” — never rebase a branch that other people have based work on. Rewrites history; their copies diverge.
- “What’s a squash merge and when do teams use it?” — combines all PR commits into one on main. Used for clean main history (one commit per feature) at the cost of losing per-commit story.
- “What’s the difference between
git pullandgit pull --rebase?” — pull merges remote into local (creates a merge commit if you had local commits); pull –rebase rewrites your local commits on top of the remote. - “Three merge buttons on GitHub PRs — what do they do?” — merge commit (preserves everything + merge commit), squash and merge (one commit on main), rebase and merge (replays commits, linear, no merge commit).