Introduction
The merge base of two commits is their best common ancestor: the foundation Git uses for three-way merges. Choosing the right base is what makes "automatic" merges actually work.
Finding it
git merge-base A B
git merge-base --all A B # all best common ancestors
git merge-base --octopus A B C
git merge-base --is-ancestor X Y # exit 0 if X is ancestor of Y
git merge-base --fork-point main feature
Definition
A common ancestor C of A and B is a "best" common ancestor if no other common ancestor of A and B is a descendant of C. Most pairs have a single best common ancestor. When the graph contains crisscross merges, several can exist; that is what --all reports.
How three-way merge uses it
Given commits A and B with merge base M, Git for each file:
- Reads the file at M, A, and B.
- If only one side changed, take that side.
- If both sides changed identically, take either.
- If both sides changed differently, run a three-way text merge or emit conflict markers.
Recursive and ort strategies
When several merge bases exist, the recursive strategy merges them first, producing a virtual base, and uses that for the final three-way merge. The ort strategy (default since 2.33) does the same with a faster, more memory-efficient implementation.
git merge -s ort feature
git merge -s recursive feature
git merge -s resolve feature # only one merge base, no recursion
Fork point
For rebase, Git often needs to know where a topic branch left a base branch. --fork-point consults the reflog of the base to find the most recent point at which the topic and base shared history:
git rebase --fork-point main feature
git merge-base --fork-point main feature
Worked example
A---B---C main
\
D---E feature
git merge-base main feature # B
A merge of main into feature three-way merges using B as the base.
Inspecting why a merge conflicts
git merge-base HEAD feature
git diff <base> HEAD -- file
git diff <base> feature -- file
This pair of diffs is exactly what Git's merge sees.
Common mistakes
Assuming there is always exactly one merge base; crisscross histories produce several. Treating --fork-point as a substitute for --onto; they solve different problems. Picking --strategy=ours when you meant --strategy-option=ours; the first discards their changes entirely. Finally, recomputing the merge base by hand from git log output and getting it wrong; use git merge-base rather than eyeballing.