DiffKit / Guides / Resolve merge conflicts in Git

How to resolve merge conflicts in Git

A merge conflict isn’t an error. Git is telling you that two branches changed the same lines, and it won’t guess which version you meant. Here’s why that happens and the exact steps to fix it.

Why conflicts happen: the three-way merge

Git doesn’t merge by comparing your branch with theirs directly. It finds the merge base, the last commit both branches share, and asks two questions for every region of the file: did my side change it since the base, and did their side? If only one side changed a line, git takes that change. If neither did, the line stays. Only when both sides changed the same region in different ways does git stop and write conflict markers.

Try it: toggle which lines each branch edits. Git compares both branches with their common ancestor (the base) and decides each line on its own.

Base (merge-base)

  1. title: Quarterly report
  2. owner: dana
  3. format: pdf
  4. pages: 12
  5. draft: true

Your branch (HEAD)

Their branch (incoming)

Merge result 1 conflict

title: Q3 reportonly your branch changed it
owner: samonly their branch changed it
format: htmlboth made the identical change
<<<<<<< HEADpages: 14=======pages: 16>>>>>>> their-branchboth changed it differently
draft: truenobody changed it

In the starting setup, pages conflicts because each branch wrote a different number, while format merges cleanly even though both branches edited it, because the edits are identical. Tick draft on both sides and it merges cleanly too. And a line changed by one branch only never conflicts, however much the other branch changed elsewhere. Git actually works in hunks rather than single lines, so edits on adjacent lines can also collide.

Step by step: resolving a conflict

  1. See what conflicted. Run git status. Files under “Unmerged paths” need attention.
  2. Open each file and find the blocks between <<<<<<< and >>>>>>>. The top half is yours (HEAD), the bottom half is incoming.
  3. Decide per block: keep one side, keep both, or write a combination. Delete all three marker lines. Paste the file into the merge conflict resolver if you’d rather click than edit markers by hand.
  4. Check it still works. Build and run the tests. A textually clean merge can still be logically broken.
  5. Mark it resolved with git add <file>.
  6. Finish: git commit for a merge, or git rebase --continue / git cherry-pick --continue.
$ git merge feature/security-headers
Auto-merging server.js
CONFLICT (content): Merge conflict in server.js
Automatic merge failed; fix conflicts and then commit the result.

$ git status --short
UU server.js

# …edit server.js, remove the markers…
$ git add server.js
$ git commit          # git pre-fills "Merge branch 'feature/security-headers'"

Make conflicts easier to read

By default git shows only your side and theirs. Switch on the zdiff3 style (Git 2.35+) and every conflict also shows the base version, so you can see what each side actually changed:

git config --global merge.conflictStyle zdiff3

With the base visible, “both changed the port” becomes “I read it from the environment, they changed the default to 8080”, and the correct merge, doing both, is obvious. DiffKit’s resolver shows this “common ancestor” block automatically when it’s present.

Avoiding conflicts in the first place

  • Merge or rebase from the main branch often, so your branch never drifts far from it.
  • Keep pull requests small and focused. Fewer touched lines means fewer collisions.
  • Agree on a formatter (Prettier, Black, gofmt) so whitespace-only edits don’t collide.
  • Regenerate lock files with the package manager rather than merging them by hand.
  • Enable rerere if you rebase long-lived branches repeatedly.

Resolved, but want to double-check the result? Compare the merged file with either parent in the diff checker, or read how diffs work under the hood.

Questions people ask

How do I abort a merge that has conflicts?

Run git merge --abort to return to the state before the merge. For a rebase use git rebase --abort, and for a cherry-pick git cherry-pick --abort.

How do I see which files have conflicts?

git status lists them under “Unmerged paths”. git diff --name-only --diff-filter=U prints just the file names, which is handy in scripts.

How do I accept all changes from one side for a whole file?

git checkout --ours path/to/file keeps your version, and git checkout --theirs path/to/file takes the incoming one. Then git add the file. Remember that during a rebase “ours” and “theirs” are swapped.

Can I stop resolving the same conflict again and again?

Yes. Enable git’s rerere (“reuse recorded resolution”) with git config --global rerere.enabled true. Git records how you resolved a conflict and replays it automatically the next time the same conflict appears, which is common with long-running branches or repeated rebases.

Why did git say CONFLICT (modify/delete)?

One branch edited a file and the other deleted it. There are no text markers to resolve. Decide whether the file should exist: git add <file> keeps it, git rm <file> deletes it.

What’s the difference between merge and rebase conflicts?

The markers look the same, but a rebase replays your commits one at a time, so you may resolve conflicts once per commit. After each one, git add the files and run git rebase --continue instead of committing.

Other comparisons