Git path · Combining work · Topic 5 of 13

Resolving merge conflicts

Not started

A merge compares both sides with their common ancestor (the merge base). A change made on only one side is taken automatically. When both sides changed the same lines differently, Git cannot know which result you want, so it stops: it writes both versions into the file between conflict markers, keeps all three versions in the index, and waits for you to decide.

Why it matters: Conflicts are routine, not emergencies. Knowing what Git has already done and what it is waiting for turns them into a two-minute job.

  1. 1Only overlapping changes conflict

    Git merges line changes from both sides automatically unless both changed the same region relative to the common ancestor.

  2. 2Three versions are in the index

    Stage 1 is the common ancestor, 2 is yours (HEAD), 3 is theirs. The file on disk has markers around the disagreement.

  3. 3Resolve, add, continue

    Edit the file to the right result, git add it to mark it resolved, then commit (or rebase --continue).

  4. 4You can always back out

    git merge --abort or git rebase --abort puts everything back as it was before you started.

1

How it works

The explanation, with examples you can run.

Make a conflict

git init -q -b main demo && cd demoprintf 'port = 8080\n' > app.conf && git add app.conf && git commit -q -m "Add config"git switch -q -c featureprintf 'port = 9090\n' > app.conf && git commit -q -am "Use 9090"git switch -q mainprintf 'port = 8000\n' > app.conf && git commit -q -am "Use 8000"git merge feature
Auto-merging app.confCONFLICT (content): Merge conflict in app.confAutomatic merge failed; fix conflicts and then commit the result.

The merge is paused, not failed. Look at what Git left you:

cat app.confgit status --shortgit ls-files --stage
<<<<<<< HEADport = 8000=======port = 9090>>>>>>> featureUU app.conf100644 2bc054ac1ac9ba969571a07977ba7b834faa7f81 1	app.conf100644 fc6e07056afbbfe74c1514428e320de0312467f1 2	app.conf100644 3792a7b6aed34427e6bf21b15a1d8005b9f16375 3	app.conf

UU means unmerged on both sides. The index holds the base (stage 1), ours (stage 2, HEAD) and theirs (stage 3, feature). git show :1:app.conf prints any of them, and git checkout --ours app.conf or --theirs takes one side whole.

See the original too

The default markers don't show what the line was before either change. The zdiff3 style adds the base, which usually makes the right answer obvious:

git merge --abortgit -c merge.conflictStyle=zdiff3 merge feature > /dev/nullcat app.conf
<<<<<<< HEADport = 8000||||||| 3221dfcport = 8080=======port = 9090>>>>>>> feature

Both sides changed 8080, so this is a real disagreement that needs a person. Set it for good with git config --global merge.conflictStyle zdiff3.

Resolve and finish

Write the result you want, remove every marker, stage it and commit:

printf 'port = 9090\n' > app.confgit add app.confgit commit -q --no-editgit log --oneline --graph
*   5f7f472 Merge branch 'feature'|\  | * 32c5fbc Use 9090* | 4a59ec3 Use 8000|/  * 3221dfc Add config

Habits that help

  • Read both commits first. git log --merge -p app.conf shows the commits on each side that touched the file, so you resolve by intent, not by guessing.
  • Use a merge tool if you like one. git mergetool opens a configured tool (VS Code, IntelliJ, vimdiff) with all three versions.
  • Build and test before committing. Git only checks markers are gone. Code can merge without conflicts and still be broken, for example when one side renames a function the other side calls.
  • In a rebase, "ours" is the base. Rebase replays your commits on top of the other branch, so --ours is the branch you are rebasing onto. Resolve, git add, then git rebase --continue; the same conflict may come back for each replayed commit.
  • Let Git remember. git config --global rerere.enabled true records how you resolved a conflict and reapplies it when the same conflict appears again, handy for long rebases.
  • Keep branches short. The longer two branches stay apart, the more their changes overlap.
2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.How do you tell Git that a conflicted file is resolved?
  2. 2.What does the zdiff3 conflict style add to the markers?
  3. 3.During a rebase, which side is "ours"?

0 of 3 right so far. Get all three to finish this topic.