The commit nobody thinks about
Claim · no terminal neededAn agent finishes a task, opens a pull request, and GitHub says “This branch has conflicts that must be resolved.” Or it stops mid-run with CONFLICT (content) and waits for you. Either way the question is the same, and it is not “how do I fix this” — it is “why did this conflict, when the other four merges today did not?”
First, from lesson 02, because it has been a while: you delete a branch whose commits are all reachable from main. What is destroyed?
Now the new material. Most people carry a two-thing model of merging: there is your branch and their branch, and git tries to combine them. Under that model a conflict happens when the two branches “touched the same thing”, which is vague enough to feel true and is why conflicts feel arbitrary.
Git does not merge two branches. It merges three commits: the two branch tips, and their best common ancestor — the merge base. Every question you have about a merge is answered by that third commit, and you can print it before you merge anything.
This is not a metaphor. Pro Git §3.2 states the mechanism plainly: “Git does a simple three-way merge, using the two snapshots pointed to by the branch tips and the common ancestor of the two.” Three snapshots in, one snapshot out.
And because each of those three is a commit, and a commit is a complete snapshot (lesson 01), git is never comparing “changes”. It has no changes to compare — it has three full trees, and it works out what each side did relative to the base by diffing.
Nothing yet. The next step prints the third commit, and shows that one comparison against it tells you whether a merge is even necessary.
Print the third commit
Claim + proof · git merge-baseHere is a repository in the shape an agent leaves constantly: it branched off, did its work, and meanwhile main moved. Two tips, one shared ancestor.
$ git log --graph --oneline --all * 70ace0c Raise the retry budget | * 304a9c7 Move to us-east-1 |/ * 618f600 Initial commit
The swept line is the merge base. Both branches reach it; neither is at it. That fork is the only reason a merge has any work to do.
git merge-base is not a diagnostic you reach for when things go wrong. It is the input to the merge, and you can run it at any time — including before you merge, which is the whole point of this lesson.
Now the same command on a repository where a branch was created and main never moved:
$ git merge-base main docs $ git rev-parse main 618f6003b0ae43ec1d92d20468c65c3d29640d73 618f6003b0ae43ec1d92d20468c65c3d29640d73
The same hash twice. main has not moved since docs branched, so the merge base is main. There is no fork, and therefore nothing to combine.
docs is simply further along the same line. Merging docs into main cannot require a new snapshot, because cd8223b already contains everything main has. All that is missing is the name.Two branches, and git merge-base reports a hash equal to one of the tips. What is the smallest possible thing git could do to merge them — and which of the three trees from lesson 03 would it have to touch?
“Best” common ancestor is doing quiet work in that definition. Histories that have been merged back and forth can have several, and the default strategy handles it: “When there is more than one common ancestor that can be used for 3-way merge, it creates a merged tree of the common ancestors and uses that as the reference tree” (git-merge). git merge-base --all lists them. You will not meet this for a while; it is here so that “the merge base” never becomes a thing you assume is unique.
Run git merge-base main <branch> and git rev-parse main. If they match, that merge is a fast-forward and cannot conflict. If they differ, the branches have genuinely diverged — and everything in the next two steps applies.
Fast-forward or merge commit
You have read the first 2 steps in full — a claim and the verification that settles it. The rest of this lesson, and the 9 lessons after it, open when subscriptions do. Nothing that is free today will be taken away then.