Lesson 8 of 30
Rebase doesn't move commits — it replays them. Same changes, brand-new commits, on top of a different base. Powerful for local cleanup, dangerous the moment someone else has pulled the commits you're rewriting.
16 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
git merge combines two histories by creating a new commit that has both branches as parents — the diamond shape from the previous lesson. git rebase takes a completely different approach: it replays your branch's commits, one at a time, on top of a different starting point, as if you'd written them there in the first place.
The critical detail people miss: rebase doesn't move your existing commits — it can't, because a commit's hash is derived from its content plus its parent. Change the parent, and you get a different hash, which means a different commit object entirely. So git rebase main while on feature takes each commit unique to feature, computes the same diff, and creates a brand-new commit with that diff applied on top of main's current tip. The old commits still exist in the object store for a while (reflog can find them), but your branch pointer now refers to the new ones. The result looks like you built your feature on top of the latest main all along — history reads as a straight line, no diamond.
This is exactly why the "never rebase a shared/public branch" rule exists. If anyone else has already pulled your original commits and built on top of them, rebasing rewrites those commits into new ones with new hashes. Your collaborators' history and your rewritten history now disagree about what the "real" commits are — pushing your rebase forces a --force push, and everyone who already has the old commits has to manually reconcile a history that no longer matches. Rebase is safe and genuinely useful on a branch only you're working on, before anyone else has fetched it.
$ git log --oneline --graph --all
* c3d4e5f (feature) tweak login copy
| * b2c3d4e (main) fix header typo
|/
* a1b2c3d initial commit
$ git switch feature
$ git rebase main
Successfully rebased and updated refs/heads/feature.
$ git log --oneline --graph --all
* d4e5f6a (HEAD -> feature) tweak login copy
* b2c3d4e (main) fix header typo
* a1b2c3d initial commit
Notice c3d4e5f is gone from the log, replaced by d4e5f6a — same content, same message, different hash, different parent. That's a genuinely new commit object, not the old one relocated. History is now a straight line: main's fix, then feature's tweak, as if feature had been branched after the header fix instead of before it.
Both sides start identical: main has commits A, B, C. feature branched off B and has its own commits D, E.
git rebase doesn't relabel c3d4e5f as living somewhere new — it creates d4e5f6a and abandons c3d4e5f. Anyone still holding a reference to the old hash is now looking at a commit your branch no longer includes.git rebase --continue, and hit another on commit 4 — it's iterative, not a single conflict resolution like git merge gives you.Rebase only makes sense once you know commits are immutable, hash-derived objects, not editable records — that's the whole reason "rewriting" a commit actually means creating a new one and discarding the old. It's the identical content-addressable model from the mental-model lesson, just applied to explain why rebase can't quietly mutate history in place the way a text editor mutates a file.