ReadbackLesson 04In progress
2 steps · 30 minutes · one terminal

MERGE, AND WHERECONFLICTS COME FROM

A merge looks like an operation on two branches. It is an operation on three commits — and the third one, which nobody thinks about, decides whether you get a fast-forward, a merge commit, or a conflict.

The commit nobody thinks about

Claim · no terminal needed

An 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?”

Answer before you move on

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.

Here is the part everyone has backwards

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.

What you can check after this step

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-base

Here 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.

an honest state, not a theme
Two tips, and the commit they both reachStep 02
$ 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:

When the merge base equals a tip
$ 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.

618f600
Initial commit — and the merge base
main
cd8223b
Add a README
docs
No fork: 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.
Predict before you read on

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?

When there is more than one common ancestor

“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 this on a real repo before you merge anything

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.

3 of 5 steps remain

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.

Read the free reference
Subscriptions open soon
the free preview stays