How to resolve merge conflicts in Git
A Git merge conflict happens when two branches change the same lines. How to read the markers, resolve each file, and the check that stops a bad commit.
A Git merge conflict happens when two branches change the same part of a file and Git cannot tell which version to keep. Git stops partway through the merge, marks the clashing lines inside the file, and waits for you. To resolve it:
- Run
git statusto see which files conflict. - Open each one, decide what the final version should say, and delete the conflict markers.
- Run
git addon each fixed file. - Run
git committo finish the merge.
If you would rather not deal with it now, git merge --abort puts everything back exactly as it was before the merge started.
Below is a real conflict, measured on Git 2.50, worked through from start to finish. On the main branch, a setting was changed from color: blue to color: red. On a feature branch, the same line was changed to color: green, and a file called notes.txt that main had edited was deleted. Then main merged feature.
What a merge conflict looks like
git merge feature
Auto-merging config.txt
CONFLICT (content): Merge conflict in config.txt
CONFLICT (modify/delete): notes.txt deleted in feature and modified in HEAD. Version HEAD of notes.txt left in tree.
Automatic merge failed; fix conflicts and then commit the result.
There are two different conflicts here. The first is the familiar kind: both branches edited the same line. The second is a modify/delete conflict: one branch changed a file and the other deleted it. That one has no markers to edit, which surprises people the first time.
git status lists everything that needs attention:
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
both modified: config.txt
deleted by them: notes.txt
In the short form, git status --short, those show as UU config.txt and UD notes.txt. A U means unmerged.
Reading the conflict markers
Here is config.txt after the failed merge:
title: Shop
<<<<<<< HEAD
color: red
=======
color: green
>>>>>>> feature
footer: (c) 2026
- Between
<<<<<<< HEADand=======is your version, from the branch you are on (here,main). - Between
=======and>>>>>>> featureis the incoming version, from the branch being merged. - Everything outside the markers, like the
titleandfooterlines, merged cleanly. Git only stops you for the lines it could not decide.
Show the original as well: zdiff3
The default markers show both new versions but not what the line said before either branch touched it. One setting adds that:
git config --global merge.conflictStyle zdiff3
The same conflict then looks like this:
title: Shop
<<<<<<< HEAD
color: red
||||||| 1eb5361
color: blue
=======
color: green
>>>>>>> feature
footer: (c) 2026
The middle section shows the common ancestor: the line was blue, and both branches changed it. That is often the information you need to decide. If one side had left it alone, you would just take the other side's change. When both changed it, you have to know why. zdiff3 has been available since Git 2.35, and it is worth turning on permanently.
Resolving: edit, add, commit
Edit the file into the version you want. You might take one side, combine them, or write something new. Then delete all three marker lines:
title: Shop
color: purple
footer: (c) 2026
Mark it resolved by staging it:
git add config.txt
For the modify/delete conflict, the decision is whether the file survives. git add notes.txt keeps it with your changes. git rm notes.txt accepts the deletion. Here the deletion was accepted.
When git status shows no unmerged paths, finish the merge:
git commit --no-edit
git merge --continue does the same thing. The history now shows the two branches joining:
* f427a5d Merge branch 'feature'
|\
| * 50f594f feature: green, drop notes
* | 20777d4 main: red, edit notes
|/
* 1eb5361 base
When Git conflicts and when it does not
Two branches editing the same file is not enough for a conflict. Git compares changes line by line, and the boundary is tighter than most people expect. Starting from a five-line file (one to five):
- One branch changed line 1 and the other changed line 5. The merge succeeded automatically, and the result had both changes:
ONE two three four FIVE. - One branch changed line 1 and the other changed line 2. The merge conflicted, even though no single line was edited twice.
Changes to adjacent lines conflict because Git needs some unchanged lines around each change to work out where it belongs. With no untouched line between the two edits, it cannot be sure they are independent, so it asks you.
Binary files are the other special case. When both branches changed an image, Git reported CONFLICT (content): Merge conflict in logo.png like any other conflict. But it cannot insert text markers into a PNG, so there is nothing to edit. Pick one version with --ours or --theirs, described next, then git add it.
Taking one whole side: --ours and --theirs
When you know one version of a file is right in its entirety, you do not need to edit markers by hand:
git checkout --theirs config.txt # take the incoming branch's version
git checkout --ours config.txt # keep your branch's version
git add config.txt
Measured, --theirs left color: green in the file and --ours left color: red. git restore --theirs and git restore --ours are the newer spellings of the same thing.
During a rebase, ours and theirs swap
This trips up almost everyone. In a rebase, Git replays your commits on top of the other branch. So while it is working, "ours" is the branch you are rebasing onto, and "theirs" is your own work.
Measured on a rebase of a branch called mine onto main, where main had written upstream into a file and mine had written mine: git checkout --ours produced upstream, and git checkout --theirs produced mine. That is the reverse of what the words suggest. The merge vs rebase post covers why rebasing works that way.
Git will let you commit the markers
This is the mistake that reaches production. git add marks a file as resolved, but it does not check whether you actually resolved it. Measured: staging config.txt with all the markers still inside and committing it worked. The commit went through, and the markers went with it.
Git has a check for exactly this, which it does not run on its own:
git diff --cached --check
config.txt:2: leftover conflict marker
config.txt:4: leftover conflict marker
config.txt:6: leftover conflict marker
config.txt:8: leftover conflict marker
It exits with status 2 when it finds any, so it also works in a pre-commit hook or a CI step. Make it a habit before committing a merge. If the markers have already been committed, fix the file and make a new commit; git diff --check HEAD~1 HEAD reports the same lines for a commit that already exists.
Backing out: git merge --abort
git merge --abort
This returns the repository to the moment before git merge. Measured, git status came back clean and config.txt said color: red again. Use it whenever a merge turns out bigger than you expected. You can always try again later, perhaps after talking to whoever wrote the other branch.
The same escape exists for the other commands that can conflict: git rebase --abort and git cherry-pick --abort. Conflicts from applying a stash work a little differently, because git stash pop keeps the stash when it conflicts. The git stash post covers that case.
Commands that help in the middle of a conflict
git diffshows the conflict as a combined diff, marking which side each line came from.git log --merge --onelinelists only the commits that touched the conflicted files. Here it showed exactly the two that clashed:20777d4 main: red, edit notesand50f594f feature: green, drop notes.git show :1:config.txt,:2:and:3:print the base, ours and theirs versions in full. Here they gaveblue,redandgreen.git mergetoolopens a visual merge tool, if you have one configured. VS Code also highlights conflicts directly in the editor, with buttons above each one to accept the current change, the incoming change, or both.
Conflicts from git pull
git pull is a fetch followed by a merge, so it can conflict exactly like a merge:
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
Automatic merge failed; fix conflicts and then commit the result.
Resolve it the same way. If you pull with rebase, finish with git rebase --continue instead of git commit. The two halves of a pull are explained in git pull vs git fetch.
Fewer conflicts next time
Conflicts are not a sign that something went wrong. They happen whenever two people touch the same lines. You can make them smaller and rarer:
- Merge or rebase
maininto your branch often, so differences stay small. - Keep branches short-lived, and avoid reformatting whole files in a feature branch, because a reformat touches every line.
- Tell your team when you are about to restructure a shared file.
Quick reference
git status # which files conflict
git config --global merge.conflictStyle zdiff3 # show the original too
git checkout --ours <file> # keep your version (swapped in a rebase)
git checkout --theirs <file> # take the incoming version
git add <file> # mark resolved (does not check content)
git rm <file> # accept a deletion
git diff --cached --check # catch leftover markers
git commit # finish (or git merge --continue)
git merge --abort # undo the whole attempt
Want to practise branching and merging where every Git command runs in your browser? Start Git Basics.
More from the blog
Git vs GitHub: they are not the same thing
Git vs GitHub: Git is the version control tool on your computer, GitHub is a website that hosts Git repositories. What each one does and how they fit.
Read moreEach child in a list should have a unique key prop
The unique key prop warning in React still renders your list. Here is the bug it predicts, shown with real output from two runs.
Read moreReady to write some code?
Put this into practice - start your first free lesson. No setup, no credit card.