Great work!

XP to next level

BugEater

Anatomy of a Conflict: Reading the Three Markers

Learning Objectives

By the end of this lesson you will be able to:

  • Identify each of the three conflict markers and what it separates
  • Say with certainty which side of a conflict is yours and which is incoming
  • Find every conflicted file in a repository
  • Recognise a conflict marker that has been accidentally committed

What Git Writes Into the File

When Git cannot merge two versions of a region, it does not pick one and it does not stop halfway. It writes both versions into the file, separated by markers, and leaves the file for you.

timeout: 30
<<<<<<< HEAD
retries: 2
parallel: true
=======
retries: 5
parallel: false
>>>>>>> dev
base_url: https://staging.example.com

Three markers, each seven characters long:

Marker Meaning
<<<<<<< HEAD Start of your version — the branch you are standing on
======= The divider between the two versions
>>>>>>> dev End of the incoming version, from the branch named after it

Everything between <<<<<<< and ======= is what your current branch has. Everything between ======= and >>>>>>> is what the branch you are merging has. Everything outside the markers merged cleanly and is not in dispute.

Which Side Is Mine?

This trips up almost everyone at first, so here is the reliable rule:

The top half — labelled HEAD — is always the branch you are currently on. The bottom half is always what is coming in.

During git merge dev while standing on main:

  • Top (HEAD) = main
  • Bottom (dev) = dev

During a rebase, the labels invert, because a rebase replays your commits onto the other branch — so HEAD is the branch you are replaying onto, and the bottom is your own commit. If a conflict during a rebase feels backwards, that is why. When in doubt, don't reason about it: read the branch names Git printed after the markers.

Finding Every Conflicted File

git status
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/test-env.yaml
        both modified:   tests/auth/login.spec.js

git status is the authoritative list. Anything under Unmerged paths still needs a decision.

For a bare list, useful for scripting or for grepping:

git diff --name-only --diff-filter=U
config/test-env.yaml
tests/auth/login.spec.js

Most editors also highlight conflict regions and offer "Accept Current" / "Accept Incoming" buttons. Those buttons are just editing the text between the markers — exactly what you would do by hand.

Seeing More Context

The default markers show two versions. You can ask Git for three:

git config --global merge.conflictStyle zdiff3

After which conflicts look like this:

<<<<<<< HEAD
retries: 2
||||||| merge base
retries: 3
=======
retries: 5
>>>>>>> dev

The middle section is the original — what the line looked like at the merge base. This is genuinely valuable: seeing that the base was 3, yours is 2 and theirs is 5 tells you both sides made a deliberate change, which is a different situation from one side simply not having the update.

This setting is worth turning on permanently.

Committed Markers Are a Real Bug

If a <<<<<<< line reaches production, the file is corrupt. A YAML config fails to parse, a JavaScript file throws a syntax error, and a Markdown document displays gibberish to a customer.

This happens more than you would expect, usually when someone resolves the top half of a large conflict and misses a second conflicted region further down the same file.

Before staging anything, check:

git diff --check
config/test-env.yaml:4: leftover conflict marker

Or search the working tree directly:

grep -rn "^<<<<<<< \|^=======$\|^>>>>>>> " .

Pro Tip: This is one of the highest-value QA checks you can add to a CI pipeline: fail the build if any tracked file contains a line starting with <<<<<<<. It takes one line of shell script and catches a whole class of embarrassing production defects.

Key Takeaways

  • Git writes both versions into the file, wrapped in three seven-character markers
  • <<<<<<< HEAD starts your side, ======= divides, >>>>>>> branch ends the incoming side
  • Top is always the branch you are on — but the labels invert during a rebase, so read the names
  • git status lists every unmerged path; git diff --name-only --diff-filter=U gives the bare list
  • merge.conflictStyle = zdiff3 adds the original merge-base version and is worth enabling
  • A committed conflict marker is a genuine defect; git diff --check catches it before you commit

Quiz

In a conflicted file, what sits between <<<<<<< HEAD and =======?

Which command gives a bare list of just the conflicted file paths?

What does setting merge.conflictStyle to zdiff3 add to a conflict?

Why is a committed <<<<<<< line treated as a genuine production defect?