Try the Text Diff

Why Git Merge Conflicts Occur Exactly Where They Do — Three-Way Merge, diff3, and Semantic Conflicts

Three-way merge — Git's algorithm for combining two branches — uses a third input (the common ancestor) to determine who changed what, enabling automatic combination when edits don't overlap. Here's why merge conflicts occur exactly where they do (both sides changed the same region), the diff3 conflict format that shows the base alongside ours/theirs, why "no merge conflicts" doesn't mean the code is correct (semantic conflicts), and why "ours" and "theirs" flip meaning between git merge and git rebase.

June 28, 2026 7 min read
Share: Facebook WhatsApp LinkedIn Email
Why Git Merge Conflicts Occur Exactly Where They Do — Three-Way Merge, diff3, and Semantic Conflicts

Three-way merge — the technique Git uses when combining two branches that have diverged from a common ancestor — is a fundamentally different operation from two-way diff, and understanding the difference explains why merge conflicts occur, why they occur where they do, and what "accepting ours vs theirs" actually means

The previous articles on this site covered how diff works and Myers algorithm, practical diff workflows, plagiarism detection, and granularity (character vs line level). This article addresses three-way merge — the algorithm behind Git's merge command, why it produces merge conflicts at specific locations, and how to read a conflict marker to understand what each option represents.


Two-way vs three-way diff: the fundamental difference

Two-way diff compares two versions of a file and shows what changed between them. It answers: "what is different between A and B?" This is what a basic text diff tool shows.

Three-way merge has three inputs: the original version (called the "base" or "ancestor"), and two modified versions (typically "ours" and "theirs" in a Git merge). It answers: "given that both sides started from the same base and independently made changes, how do we combine those changes?"

Why the third input (base) matters:

Version A (base): The cat sat on the mat. Version B (ours): The cat sat on the rug. Version C (theirs): The large cat sat on the mat.

Two-way diff of B vs C: "rug" vs "mat" different, "large" vs "" different — but which to keep? The two-way diff can't tell you; it just shows you they differ.

Three-way merge with base A:

  • B changed "mat" → "rug" (our edit)
  • C changed "" → "large " before "cat" (their edit)
  • These edits don't overlap → merge result: The large cat sat on the rug. (both changes applied)

The third input (what the file looked like before anyone edited it) allows the algorithm to determine who changed what — and therefore whether changes can be combined automatically.


When merge conflicts occur

A merge conflict occurs when the same region of a file was changed by both sides in ways that can't be automatically combined:

Base: The cat sat on the mat. Ours: The cat sat on the rug. Theirs: The cat sat on the floor.

Both sides changed "mat" — but to different things. The algorithm can determine that both sides edited this region (since neither matches the base), but can't determine which edit to keep. A conflict marker is inserted:

<<<<<<< HEAD
The cat sat on the rug.
=======
The cat sat on the floor.
>>>>>>> feature-branch

What the conflict marker means:

  • <<<<<<< HEAD: start of "ours" (the current branch, HEAD)
  • =======: divider between the two versions
  • >>>>>>> feature-branch: end of "theirs" (the incoming branch being merged)

The base version (what was there before either change) is not shown in the standard conflict format — it's available with git checkout --conflict=diff3 which shows all three versions.


The diff3 conflict format: seeing the base

The standard two-section conflict marker (<<<, ===, >>>) shows "ours" and "theirs" but not the base. This makes it harder to understand what changed from the base to each version.

The diff3 format adds a third section:

<<<<<<< HEAD
The cat sat on the rug.
||||||| base
The cat sat on the mat.
=======
The cat sat on the floor.
>>>>>>> feature-branch

Now you can see:

  • Base: "mat"
  • Ours: changed "mat" → "rug"
  • Theirs: changed "mat" → "floor"

This context makes resolving conflicts easier — you understand what each side intended to change, not just what the two current versions say. Many experienced Git users set diff3 as the default conflict style: git config --global merge.conflictstyle diff3.


Semantic conflicts: when Git succeeds but the code is wrong

Three-way merge can successfully combine changes with no conflict markers — yet produce code that doesn't work. These "semantic conflicts" are the hardest merge problems:

Example:

  • Base: function calculateTotal(price, tax) where tax is a rate (e.g., 0.2 for 20%)
  • Ours: we added calculateTotal call in a new feature: calculateTotal(99.99, 0.20)
  • Theirs: they refactored calculateTotal to accept tax as a percentage rather than a rate: calculateTotal(price, tax) now expects tax as 20, not 0.2

Git merges this successfully — our call site and their function change are in different files, no line-level conflict. But the merged code silently multiplies price by 20 instead of 0.20, producing a 2000% tax rate instead of 20%.

This is why "no merge conflicts" doesn't mean "the merge is correct." Automated tests run after merging catch many semantic conflicts — this is one reason CI/CD pipelines that run tests on merged code (not just individual branches) catch problems that code review on separate branches misses.


Ours vs theirs: what these terms mean in different Git contexts

In a git merge:

  • "Ours" = the current branch (HEAD) before the merge
  • "Theirs" = the branch being merged in

In a git rebase:

  • "Ours" = the branch being rebased onto (the upstream)
  • "Theirs" = the commits being replayed (your branch's changes)

This reversal confuses many Git users. During git rebase origin/main:

  • Each commit from your branch is replayed one at a time onto origin/main
  • For each replay, "ours" is the origin/main state (the target of the replay)
  • "Theirs" is your commit being replayed

Practical impact: if you always resolve conflicts by taking "ours" assuming it means "my changes," a rebase will silently discard your changes in favour of origin/main content — the opposite of what you intended.


How to use the Text Diff tool on sadiqbd.com

  1. For code review: paste the "ours" and "theirs" versions (extracted from a conflict marker) to get a line-by-line diff showing exactly what each side changed — useful for understanding conflicts without needing to set up a local merge environment
  2. For document revision comparison: paste two versions of a document to see the changes — this is a two-way diff, appropriate when there's no "base" version or when you just want to see the delta between current state and previous state
  3. For conflict resolution assistance: paste the base, ours, and theirs versions separately to understand what each side changed from the starting point before deciding how to resolve

Frequently Asked Questions

What's the difference between git merge and git rebase for conflict resolution? Both combine diverged branches, but through different mechanisms. git merge creates a new "merge commit" that has two parents — it preserves the branch history but adds a merge commit. git rebase rewrites your branch's commits as if they were made directly on top of the target branch — it produces a cleaner linear history but changes commit hashes (rewrites history). For conflict resolution, both require resolving the same logical conflicts, but rebase may present them one commit at a time (if multiple commits each conflict), while merge presents all conflicts from all commits together. The practical guidance: merge for integrating complete branches (especially shared/public branches where history matters); rebase for cleaning up local work before merging into a shared branch.

Is the Text Diff tool free? Yes — completely free, no sign-up required.

Try the Text Diff tool free at sadiqbd.com — compare any two texts and see what changed, line by line.

Share: Facebook WhatsApp LinkedIn Email

Text Diff

Free, instant results — no sign-up required.

Open Text Diff →
Similar Tools
Word & Character Counter Lorem Ipsum Generator Remove Duplicate Lines Sort Lines Text to Slug Character Frequency String Repeater Find & Replace
How Diff Works: Git, Three-Way Merge, and the Myers Algorithm Behind It
Text Tools
How Diff Works: Git, Three-Way Merge, and the Myers Algorithm Behind It
Text Diff in Practice: Legal Redlining, Code Review, and How AI Writing Assistants Show Changes
Text Tools
Text Diff in Practice: Legal Redlining, Code Review, and How AI Writing Assistants Show Changes
How Plagiarism Detection Actually Works: From Exact-Match Shingling to Semantic Similarity, and Why Each Has Limits
Text Tools
How Plagiarism Detection Actually Works: From Exact-Match Shingling to Semantic Similarity, and Why Each Has Limits
Why Text Diff Tools Work at Line Level (Not Character Level): Edit Distance, Granularity, and the Unified Diff Format
Text Tools
Why Text Diff Tools Work at Line Level (Not Character Level): Edit Distance, Granularity, and the Unified Diff Format