Resolving Merge Conflicts

Read conflict markers with confidence, choose ours or theirs, use the three-way zdiff3 style and editor tools, resolve conflicts during a rebase, and prevent most conflicts before they happen.

Lesson 8 of 30Intermediate17 min read

Sooner or later, two people change the same lines of the same file on different branches, or you do it yourself on two of your own. When those branches meet, Git can't know which version is right, so it stops and asks you. That's a merge conflict.

Conflicts look alarming the first time: strange <<<<<<< lines appear in your files, and Git refuses to finish the merge. But they're a normal part of working with branches, and they're Git being careful rather than something going wrong. Nothing is lost; Git has kept both versions and is waiting for your decision.

Building on the merging lesson, you'll read conflict markers with extra context, keep one side in one command, handle conflicts during a rebase, back out safely and make conflicts rare.

Why conflicts happen

Git merges automatically whenever it can. Changes to different files, or to different parts of the same file, combine without any help. A conflict only happens when both branches changed the same lines (or lines right next to each other) in different ways, or when one branch edited a file that the other deleted.

When that happens, Git:

  1. Merges everything it can.
  2. Writes conflict markers into the files it couldn't merge.
  3. Pauses the merge and lists the files under "Unmerged paths" in git status.

Nothing is committed until you resolve every file, git add it and finish the merge.

Reading conflict markers

Text
<<<<<<< HEAD
<p>Open 9 to 5, Mon to Sat</p>
=======
<p>Open 8 to 6</p>
>>>>>>> new-hours
  • Between <<<<<<< HEAD and ======= is your version: the branch you're on, also called "ours".
  • Between ======= and >>>>>>> new-hours is the incoming version: the branch being merged in, "theirs".

Your job is to edit the file so it contains exactly what the final version should be, which is often a combination of both, and delete all three marker lines. Then git add the file to mark it resolved.

See the original too: zdiff3

By default you only see the two new versions, not what the line looked like before either change. Turn on the three-way style once:

Terminal
git config --global merge.conflictStyle zdiff3

Conflicts then include a middle section, after |||||||, showing the common ancestor. Seeing that the original said "Open 9 to 5" makes it obvious that one branch added the days and the other changed the hours, so the right answer keeps both changes.

Choosing a whole side, tools and aborting

  • git checkout --ours file keeps your branch's version of the whole file; --theirs keeps the incoming one. Then git add it.
  • Editors help a lot. VS Code shows Accept Current, Accept Incoming and Accept Both buttons above each conflict, and a three-way merge editor.
  • git diff during a conflict shows only the unresolved parts.
  • git merge --abort (or git rebase --abort) cancels and puts everything back as it was before you started.

During a rebase, "ours" and "theirs" are swapped: ours is the branch you're rebasing onto, and theirs is your own commit being replayed. Read the labels in the markers rather than relying on memory.

Git Conflicts Syntax

Conflict commands

git status                         # lists unmerged files
git diff                           # shows unresolved parts
git checkout --ours file.txt       # keep my version
git checkout --theirs file.txt     # keep the incoming version
git add file.txt                   # mark as resolved
git commit                         # finish a merge
git rebase --continue              # finish a rebase step
git merge --abort                  # cancel the merge
git config --global merge.conflictStyle zdiff3

Git Conflicts Summary

  • Conflicts happen when both branches change the same lines differently; Git pauses and marks them.
  • HEAD (ours) is your current branch; the other label (theirs) is the incoming branch.
  • Edit the file to the final version, remove all markers, git add it, then commit or continue.
  • zdiff3 style shows the original lines too; --ours or --theirs keeps one whole side.
  • --abort cancels safely; in a rebase, ours and theirs are swapped.

Git Conflicts Examples

Each example shows the code, the output it produces and what each part does.

Example 1

A conflict, step by step

Terminal
git merge new-hours
git status
cat index.html
Output
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Auto-merging style.css
CONFLICT (content): Merge conflict in style.css
Automatic merge failed; fix conflicts and then commit the result.
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:   index.html
	both modified:   style.css

<h1>Welcome</h1>
<<<<<<< HEAD
<p>Open 9 to 5, Mon to Sat</p>
=======
<p>Open 8 to 6</p>
>>>>>>> new-hours
<footer>(c) 2024</footer>
Line-by-line explanation
  1. Line 1main added the opening days; new-hours changed the hours, on the same line. The two files changed in both branches conflict.
  2. Line 2"both modified" means each branch changed these files in ways Git couldn't combine.
  3. Line 3The heading and footer merged fine; only the clashing line has markers.
Example 2

Resolving: one by hand, one by choosing a side

Terminal
git checkout --theirs style.css
git add style.css
# edit index.html to: <p>Open 8 to 6, Mon to Sat</p> and delete the markers
git add index.html
git status --short
git commit --no-edit
git log --oneline --graph
Output
Updated 1 path from the index
M  index.html
M  style.css
*   2230dce Merge branch 'new-hours'
|\  
| * 71ed3ae Update opening hours
* | 350e8c3 Add opening days
|/  
* 1b206c5 Add home page
Line-by-line explanation
  1. Lines 1 to 2For the stylesheet, the new-hours colour is wanted as it is, so keep the incoming version.
  2. Lines 3 to 4For the page, combine both changes by hand: the new hours and the days.
  3. Line 5Both files are staged and resolved.
  4. Line 6--no-edit accepts Git's default merge message.
  5. Line 7The graph shows both branches joined by the merge commit.
Example 3

Context with zdiff3, and a conflict during rebase

Terminal
git config merge.conflictStyle zdiff3
git merge new-hours
cat index.html
git merge --abort
git switch new-hours
git rebase main
Output
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
<h1>Welcome</h1>
<<<<<<< HEAD
<p>Open 9 to 5, Mon to Sat</p>
||||||| 959899f
<p>Open 9 to 5</p>
=======
<p>Open 8 to 6</p>
>>>>>>> new-hours
Switched to branch 'new-hours'
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
error: could not apply ccbb356... Update opening hours
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Line-by-line explanation
  1. Line 1zdiff3 adds the original text from the common ancestor, after |||||||.
  2. Line 3Now it's clear: one side added "Mon to Sat", the other changed "9 to 5" to "8 to 6".
  3. Line 4--abort cancels the merge completely, as if it never started.
  4. Line 6Rebasing replays new-hours' commit onto main and hits the same conflict. Resolve, git add, then git rebase --continue.

Common Git Conflicts Mistakes

Committing with markers still in the file

Don't
git add index.html          # file still contains <<<<<<< and >>>>>>>
git commit
Do
grep -n "<<<<<<<\|>>>>>>>" index.html    # check nothing is left
git add index.html
git commit

Keeping only one side without reading

Don't
git checkout --ours index.html     # silently throws away the other branch's work
Do
# read both sides (and the original, with zdiff3), then combine them

Panicking in the middle of a merge

Don't
rm -rf project && git clone ...    # starting over loses unpushed work
Do
git merge --abort                  # or git rebase --abort

Git Conflicts in Real Projects

Conflicts in team work

On active teams, conflicts happen weekly in shared files like translation files and lock files. GitHub offers a web editor for small ones, and Git's rerere setting ("reuse recorded resolution") can remember how you resolved a conflict and repeat the fix next time.

Git Conflicts FAQ

How do I list the files that still have conflicts?

git status lists them under "Unmerged paths", usually marked "both modified". For just the names, run git diff --name-only --diff-filter=U. A file drops off the list once you git add it.

How do I resolve merge conflicts in VS Code?

Open the conflicted file and VS Code highlights each conflict, with clickable options above it such as Accept Current Change, Accept Incoming Change and Accept Both Changes. Pick one or edit the text by hand, save the file, then run git add on it as usual.

What do ours and theirs mean during a rebase?

They're the opposite of what you might expect. During a merge, "ours" is your current branch and "theirs" is the branch being merged in. During a rebase, Git is replaying your commits on top of the other branch, so "ours" is the branch you're rebasing onto and "theirs" is your own commit.

How do I start over on one file after resolving it badly?

Run git checkout --merge filename (or -m). It recreates the conflict markers in that file as they were when the merge stopped, so you can resolve it again. This only works before you commit the merge.

How do I resolve a conflict where one side deleted the file?

Git reports a "modify/delete" conflict, and git status shows the file as "deleted by them" or "deleted by us". To keep the file, run git add filename; to accept the deletion, run git rm filename. Then commit as for any other conflict.

Git Conflicts Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the command that keeps your own branch's version of a conflicted file.

git checkout -- style.css
Multiple choiceeasy

In conflict markers, what's between <<<<<<< HEAD and =======?

Multiple choicemedium

After editing a conflicted file, how do you mark it as resolved?

Multiple choicemedium

What does the zdiff3 conflict style add?

Multiple choicehard

During git rebase main, what does --theirs refer to?

Git Conflicts Quiz

5 questions to check what you learned.

  1. When does Git report a conflict?

    • When both branches change the same lines differently
    • Whenever two branches change any file
    • When a branch has more than ten commits
    • When you merge into main
  2. How do you cancel a merge that has conflicts?

    • git reset --merge-cancel
    • git merge --abort
    • Delete the .git folder
    • git stop
  3. What does "both modified" mean in git status?

    • Two people are editing at once
    • Both branches changed this file in ways Git couldn't combine
    • The file was saved twice
    • The file is staged
  4. Which habit prevents most painful conflicts?

    • Never using branches
    • Always using --theirs
    • Committing once a month
    • Keeping branches short-lived and merging main into them often
  5. What does Git's rerere feature do?

    • Reverts merges
    • Renames branches
    • Remembers how you resolved a conflict and reuses that resolution when the same conflict appears again
    • Re-runs tests