How merge and rebase actually work
Merge finds the lowest common ancestor (merge base), does a three-way merge of base/ours/theirs, and writes one commit with two parents — true history. Rebase replays your commits onto a new base as brand-new SHAs — linear history, rewritten. Same result tree, different graph.
Two branches have diverged. You can reconcile them with git merge or git rebase, and the holy wars over which to use mostly come from people who never looked at what each one does to the object graph. This lesson is about the mechanism, not the command flags. Once you can see that merge writes a two-parent commit and rebase replays your commits as new objects, every confusing behaviour — recurring conflicts, “why did my SHAs change,” the golden rule — becomes obvious.
By the end you will be able to explain the merge base, why a three-way merge needs it, and exactly what rebase rewrites and why that produces linear history.
After this lesson you can explain the merge base (lowest common ancestor), describe how a three-way merge combines base/ours/theirs into one two-parent commit, contrast that with how rebase replays commits as new SHAs onto a new base, and state the tradeoff and the golden rule of rebasing.
Both start by finding the merge base — the lowest common ancestor. When feature and main have diverged, they still share history up to some commit. The merge base is the most recent commit reachable from both tips:
git merge-base main feature # -> SHA where the two branches last agreedThis is the reference point everything else is measured against: it tells git which changes are uniquely yours and which are uniquely theirs since the split. Get the base wrong and you cannot tell a real edit from “this was always here.”
Merge does a three-way merge: base vs. ours vs. theirs. A two-way diff of the two tips cannot tell who changed a line — only that they differ. So git compares three snapshots: the merge base, ours (the current branch), and theirs (the branch being merged). For each file region:
- changed on only one side → take that side automatically;
- changed on both sides to the same thing → take it;
- changed on both sides differently → conflict, you resolve it.
The base is what makes “only one side changed” decidable — without it, every difference would look like a conflict.
Merge writes ONE commit with TWO parents, preserving the branch shape. The result of a successful merge is a new merge commit whose first parent is the old main tip and whose second parent is the feature tip:
git switch main
git merge feature # -> creates a merge commit, parents: [main, feature]History now visibly forks and rejoins. Nothing was rewritten: both original branches still exist verbatim, and the graph records that a merge happened, when, and what came together. This is true history — including the messy reality that two lines of work proceeded in parallel.
Rebase replays your commits one by one onto a new base as brand-new commits. Instead of joining the branches, rebase moves yours. git switch feature; git rebase main does, conceptually:
- Compute the patch of each
featurecommit since the merge base. - Reset
featureto the tip ofmain. - Re-apply those patches in order, creating a new commit for each — new parent, new timestamp, therefore a new SHA.
The old commits are abandoned (reachable only via reflog). The result is a straight line: main’s history, then your commits on top, as if you had started from the current main all along.
git switch feature
git rebase main # replays feature's commits onto main's tip▸Edge cases
Why rebase conflicts can recur per commit. A merge resolves the combined difference once, in a single merge commit. A rebase re-applies your commits individually, so if commit 2 and commit 4 both touch the same conflicting region, you may resolve a conflict on commit 2 and hit a related one again on commit 4 — once per replayed commit. This per-commit replay is exactly why long rebases feel repetitive, and it is the problem the next lesson’s tool (rerere) exists to solve by recording and replaying your resolutions.
The tradeoff: true history vs. clean linear history — and the golden rule. Merge keeps an accurate, branching record at the cost of a busier graph. Rebase produces a tidy, bisect-friendly straight line at the cost of rewriting commits into new SHAs. That rewrite is safe on commits only you have — and dangerous on shared ones:
The golden rule of rebasing: never rebase commits that exist outside your local repository (that others may have based work on).
Rewriting public commits gives everyone else a divergent history and forces confusing force-pushes and re-merges. Rebase your private feature branch before sharing; merge once it is public.
One divergence, both outcomes.
Start from a base commit B. main adds commit M; your feature adds F1 then F2. The branches have diverged, and git merge-base main feature returns B.
Merge path:
git switch main && git merge feature
# new commit X with parents [M, F2]; graph: B -> M -> X and B -> F1 -> F2 -> XF1, F2, and M are untouched; X records that they came together. git log --graph shows a visible diamond.
Rebase path:
git switch feature && git rebase main
# F1 -> F1' (parent now M, new SHA)
# F2 -> F2' (parent now F1', new SHA)
# graph: B -> M -> F1' -> F2' (one straight line)The content of the final tree is the same in both cases — same files. But the merge keeps four commits plus a merge commit in a fork, while the rebase leaves a clean line of three commits, with F1 and F2 replaced by new-SHA copies F1'/F2'. If feature had already been pushed, those rewritten SHAs would clash with the copies others hold — the golden rule violated.
▸Common mistake
“Rebase avoids conflicts” is a myth. Rebase and merge surface the same conflicting edits — neither can magically combine two incompatible changes to one line. Rebase often feels like more conflict work, because it replays commit by commit and can re-present a related conflict several times, whereas merge resolves the whole divergence in one pass. Choose rebase for a clean history, not to dodge conflicts.
What is the key structural difference between the commits produced by merge and by rebase?
Merge and rebase both begin at the merge base, the lowest common ancestor of the two branch tips. Merge runs a three-way merge of base/ours/theirs and writes a single commit with two parents, preserving the branching history exactly as it happened. Rebase instead replays your commits onto a new base, creating brand-new commits with new SHAs and discarding the originals, yielding linear history at the cost of rewriting it. The tradeoff is true history versus clean history, governed by the golden rule: never rebase commits others may already have. Next you will meet hooks — scripts git runs around these operations to enforce your team’s rules.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.