Concept · Merging & history

Three-way merges and conflicts

Git compares both sides with their common ancestor; a conflict is a line both sides changed.

3 minmerge baseconflict markersdiff3zdiff3

Comparing only the two branch tips can't tell you who changed what. If a line differs, did you change it, or did they? A three-way merge adds a third version, the merge base: the most recent commit both branches share.

text
         ┌─ ours   (main)
base ────┤
         └─ theirs (feature)

For each region of each file, Git compares both sides with the base:

Ours vs baseTheirs vs baseResult
unchangedunchangedkeep
changedunchangedtake ours
unchangedchangedtake theirs
changedchanged, identicallytake it
changedchanged, differentlyconflict

So most merges resolve automatically. A conflict means one specific thing: both sides changed the same region relative to the base, in different ways. Git can't know which one you want, or whether you want both.

Reading conflict markers

Turn on the base section. It shows what the code looked like before either change, which is often the key to resolving correctly:

bash
git config --global merge.conflictStyle zdiff3
text
<<<<<<< HEAD
const timeout = 5_000
||||||| base
const timeout = 3_000
=======
const timeoutMs = 3_000
>>>>>>> feature

Here one side changed the value and the other renamed the variable. Neither side's version is right on its own. The correct resolution keeps both changes: const timeoutMs = 5_000.

Resolving

  1. git status lists the conflicted files.
  2. Edit each one to the correct combined result, and remove the markers.
  3. Build and run the tests. A merge without conflicts can still be wrong. If one side renamed a function and the other added a call to the old name, both changes apply cleanly and the result doesn't compile.
  4. git add the files, then git commit (or git rebase --continue).

git merge --abort gets you back to the state before the merge. git rerere remembers how you resolved a conflict and reapplies it if the same conflict comes back, which helps with long-running branches.

Fewer conflicts

Conflicts grow with how long branches diverge and how many people edit the same lines. Small, short-lived branches merged often (trunk-based development) avoid most of them. Moving code and changing it in the same commit is a reliable way to create them.

See fast-forward vs merge commits for what's recorded after the merge.

Learn it in depth