Resolving merge conflicts

Git · Practical · 12 min

Conflicts are routine in team work; resolving them calmly and correctly is a core skill.

A conflict happens when both branches changed the same lines (or one edited a file the other deleted) since the merge base. Git stops mid-merge, merges everything it can, and marks the rest. git status lists the files under "Unmerged paths".

Inside a conflicted file you will see:

<<<<<<< HEAD
total = round(sum(items), 2)
=======
total = sum(items).quantize(CENT)
>>>>>>> feature/decimal

The top part is your current branch (HEAD), the bottom is the incoming branch. Your job is to edit the file into what the code should be, which might be one side, the other, or a combination, and delete all markers. Then:

  • git add <file> to mark it resolved.
  • When all files are resolved, git commit (or git merge --continue) to create the merge commit.

Tips that make conflicts easier:

  • Set git config --global merge.conflictStyle zdiff3. Markers then also show the base version (|||||||), so you can see what each side actually changed.
  • git checkout --ours file or --theirs file takes one side wholesale for a file.
  • git mergetool opens a configured visual tool; most editors have a built-in conflict UI.
  • Run the tests before committing the merge; a textually clean merge can still be semantically broken.

If it goes wrong, git merge --abort returns you to the state before the merge. The same markers and commands appear during rebase, cherry-pick and stash pop, where you continue with that command's --continue instead.

Example

git merge feature/decimal
# CONFLICT (content): Merge conflict in billing.py
# Automatic merge failed; fix conflicts and then commit the result.
git status --short
# UU billing.py
$EDITOR billing.py                     # remove markers, write the right code
grep -n '<<<<<<<\|>>>>>>>' billing.py  # make sure none are left
git add billing.py
python -m pytest -q                    # still works?
git merge --continue                   # creates the merge commit

# Or bail out entirely
git merge --abort

Remember

  • Conflict = both sides changed the same region since the merge base
  • Edit to the correct result, remove markers, git add, then continue
  • zdiff3 conflict style shows the base version too
  • git merge --abort returns to the pre-merge state

Watch out for

  • Committing a file that still contains <<<<<<< markers.
  • Blindly taking --ours or --theirs without reading what the other side changed.

Check yourself

  1. During a merge on main of feature, what is between <<<<<<< HEAD and =======?
  2. How do you tell Git a conflicted file is resolved?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging