How to Resolve Merge Conflicts in Git (Step by Step)

To resolve a merge conflict in Git, open each conflicted file, keep the lines you want between the <<<<<<<, ======= and >>>>>>> markers and delete the markers, then run git add <file> and git commit. If you'd rather not merge after all, git merge --abort puts everything back.

git status                 # list the conflicted files
# edit each file and remove the markers
git add config.ini         # mark it resolved
git commit                 # finish the merge (or: git merge --continue)

What a merge conflict looks like

In this example, main changed a timeout from 30 to 45 while a feature branch changed the same line to 60. Merging gives (real output, Git 2.45):

$ git merge feature
Auto-merging config.ini
CONFLICT (content): Merge conflict in config.ini
Automatic merge failed; fix conflicts and then commit the result.

git status lists the file under Unmerged paths as both modified, and reminds you of the two ways out:

On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   config.ini

Only the overlapping part is a conflict. The Git documentation is explicit that changes where "you changed an area of the file while the other side left that area intact" are merged automatically. The file you open contains just the disputed hunk inside markers.

How to read the conflict markers

<<<<<<< HEAD
timeout = 45
=======
timeout = 60
>>>>>>> feature
retries = 3
  • <<<<<<< HEAD to =======: your side, the branch you're on (main). Editors label this the "current" change.
  • ======= to >>>>>>> feature: their side, the branch being merged in. Editors call this the "incoming" change.
  • retries = 3 is outside the markers, so it merged cleanly and needs nothing.

To resolve, replace the whole block, markers included, with the text that should exist after the merge. That can be one side, the other, or a combination written by hand. Here, if 60 is correct:

timeout = 60
retries = 3

See the original version with diff3 markers

The default markers don't show what the line said before either branch touched it, which is often the clue you need. Turn on the three-way style:

git config --global merge.conflictStyle zdiff3

If your Git is too old to know zdiff3, use diff3. To redraw an existing conflict in this style without restarting the merge:

git checkout --conflict=diff3 config.ini

Real output:

<<<<<<< ours
timeout = 45
||||||| base
timeout = 30
=======
timeout = 60
>>>>>>> theirs

The ||||||| base section is the common ancestor. Now you can see both sides raised the timeout from 30, which tells you the real question is how high it should go, not whose edit wins.

Take one whole side of a file: --ours and --theirs

When you know one branch's version of a file is correct in full (a lock file, generated code, a binary), skip the hand editing:

git checkout --ours config.ini     # keep the version from the branch you're on
git checkout --theirs config.ini   # keep the version from the branch being merged
git add config.ini

git restore --ours config.ini does the same. This replaces the whole file, including non-conflicting changes from the other side within it, so use it per file and only when that's what you mean.

To resolve every conflicting hunk in favour of one side automatically while still merging the other side's non-conflicting changes, pass a strategy option when you start the merge:

git merge -X theirs feature

The Git docs describe -X ours as forcing "conflicting hunks to be auto-resolved cleanly by favoring our version" and warn not to confuse it with -s ours, a different strategy that discards everything the other branch did.

Useful commands while you resolve

CommandWhat it shows
git diff --name-only --diff-filter=UOnly the files that still have conflicts
git diffA combined diff of the conflict against both sides
git show :1:config.iniThe common ancestor's version
git show :2:config.iniYour version (HEAD)
git show :3:config.iniTheir version (MERGE_HEAD)
git log --merge -p config.iniThe commits on each side that touched the file
git diff --checkLeftover conflict markers and whitespace errors

If you want to compare the two versions side by side outside the terminal, save git show :2:config.ini and git show :3:config.ini to files and paste them into a diff viewer such as larely's diff checker, which runs in the browser.

Mark the file resolved and finish

git add config.ini
git commit --no-edit

git commit on its own opens your editor with a prepared Merge branch 'feature' message; --no-edit accepts it. git merge --continue does the same after checking a merge is actually in progress.

Check before you stage: git add does not look for conflict markers. In testing, staging a file that still contained <<<<<<< HEAD succeeded silently and Git showed it as resolved. If you commit that, the markers ship in your code. Search for <<<<<<< or run git diff --check first; it prints lines such as config.ini:1: leftover conflict marker. After staging, use git diff --cached --check.

How to undo or restart a merge

git merge --abort

This tries to rebuild the state before the merge. The Git docs warn it may not be able to restore uncommitted changes that were in your working directory when the merge started, which is why it's best to commit or stash before merging (see git stash explained). If you already finished the merge commit and regret it, undo it like any other commit: how to undo the last commit.

To restart one file from its conflicted state after a bad edit, run git checkout -m config.ini, which recreates the conflict markers.

Resolving conflicts during a rebase

A rebase replays your commits one at a time, so it can stop several times. Real output:

$ git rebase main
Auto-merging config.ini
CONFLICT (content): Merge conflict in config.ini
error: could not apply 21ff8d7... raise timeout
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".

Edit and git add as above, then git rebase --continue (not git commit). Repeat for each commit that conflicts. Watch the labels: during a rebase, HEAD and "ours" mean the branch you're rebasing onto, and "theirs" is your own commit, the reverse of a merge. The trade-offs between the two approaches are in git merge vs rebase.

Resolving conflicts in VS Code or a merge tool

VS Code highlights each conflict and shows inline actions above it: Accept Current Change, Accept Incoming Change, Accept Both Changes and Compare Changes. For larger conflicts, the Resolve in Merge Editor button opens a three-way view. After saving, stage the file with the + (Stage Changes) button in the Source Control view, which is the same as git add.

From the terminal, git mergetool launches a configured graphical tool for each conflicted file. git mergetool --tool-help lists the tools Git can find on your system, and git config --global merge.tool meld (or kdiff3, vimdiff) sets a default. It leaves *.orig backup files behind unless you set mergetool.keepBackup to false.

If the same conflict keeps coming back

Long-lived branches that both edit the same files will keep conflicting. Merge main into your branch, or rebase onto it, more often so each conflict stays small. If you rebase the same branch repeatedly, git config --global rerere.enabled true makes Git record how you resolved a hunk and reuse that resolution next time it sees the identical conflict.

Frequently asked questions

What causes a merge conflict in Git?
A conflict happens when both branches changed the same lines of the same file since they split, or when one branch edited a file the other deleted. Git merges non-overlapping changes automatically and only stops where it cannot decide which version is right.
How do I cancel a merge that has conflicts?
Run git merge --abort. It tries to put the branch and working directory back to how they were before the merge started. For a rebase use git rebase --abort, and for a cherry-pick git cherry-pick --abort.
Does git add check that conflict markers are gone?
No. git add stages whatever is in the file, conflict markers included, and Git then treats the file as resolved. Search the file for <<<<<<< before staging, or run git diff --check (git diff --cached --check once the file is staged), which reports each leftover conflict marker with its line number.
What do ours and theirs mean during a rebase?
They are swapped compared with a merge. During a rebase, ours (HEAD) is the branch you are rebasing onto, and theirs is your own commit being replayed. Check which side is which before using git checkout --ours or --theirs in a rebase.

Sources