skip to content

What does setting Git's merge.conflictStyle to diff3 or zdiff3 add to conflict markers?

level: middleimportance: should knowfreq 45%

answer

  1. The default rendering hides one of three versions
  2. A fourth marker line exists
  3. Ancestor text, not just the two results
  4. One variant trims shared edges
  5. git checkout --conflict re-renders an existing conflict

basics

~20 s

Both styles add a third section, opened by |||||||, showing the merge-base version of the conflicting region, so you can see what each side changed rather than only the two results. zdiff3 additionally moves lines common to both sides out of the conflict block.

solid answer

~50 s

The default `merge` conflict style shows only two candidates: yours and theirs. That hides the most useful piece of information — what the region originally looked like. Setting `merge.conflictStyle = diff3` makes Git emit a third block, introduced by a `|||||||` line, containing the **merge-base** version. Now you can diff each side against the ancestor mentally and see who changed what: often one side merely reformatted and the other made the real change, which is invisible in the two-way view. `zdiff3` (Git 2.35 and later) is the same three-way output with an extra pass that pulls lines identical on both sides out of the conflict region, shrinking the hunk to the part that genuinely disagrees. Set it globally with `git config --global merge.conflictStyle zdiff3`. If you already have a conflict in the working tree you can re-materialise it in another style with `git checkout --conflict=diff3 <file>`.

go deeper

for a junior

Know that the two blocks you normally see are not the whole story and that Git can also show the original text. Naming the config key is enough at this stage.

for a middle

Explain the four-marker layout, what the base block buys you when resolving, and that the trimming variant shrinks the hunk to the genuinely divergent lines. Mention re-rendering an existing conflict.

for a senior

Show it as part of a resolution practice: base-aware resolving, index stages as the real source of truth, and marker checks that catch the extra marker line as well as the classic three.

for a principal

Argue for or against standardising this in repo-level config: the readability win against tooling that parses markers, and how much of conflict pain is rendering versus how work is sliced.

## The information the default style throws away A Git conflict is a three-way disagreement — base, ours, theirs — but the default conflict style, `merge`, prints only two of the three. You see your version and their version and are expected to reconstruct intent from that. Frequently you cannot. If your side reads `timeout = 30` and their side reads `timeout = 60`, you have no idea whether one of you changed it or both did; whether the base was `30` (they raised it) or `45` (you both lowered it, differently) changes the right answer completely. ## What diff3 adds Setting the config key `merge.conflictStyle` to `diff3` makes Git write a third section into every conflict hunk. The layout becomes: `<<<<<<<` and your side, then a `|||||||` line introducing the **merge-base** version of the same region, then `=======`, then their side, then `>>>>>>>`. With the base visible, resolving stops being a guess. You read the hunk as two diffs from a shared original and can usually combine both intents instead of picking a side. It also exposes the common case where one side did not really change the logic at all — a rename, a reflow, an import reorder — which the two-way view makes look like a substantive disagreement. The cost is size. Conflict hunks become roughly half again as long, and in a large conflicted region the base block can be big. ## What zdiff3 adds on top `zdiff3`, available from Git 2.35, produces the same three sections but runs a *zealous* pass first: lines at the start or end of the conflict region that are identical on **both** sides are hoisted out of the conflict entirely and left as ordinary merged text. Only the genuinely divergent core stays between the markers. In practice this often turns a twenty-line conflict — most of it identical boilerplate that happened to sit inside the conflicting region — into a three-line one. If your Git is recent enough, `zdiff3` is generally the better default than `diff3`; the extra context is preserved but the noise is not. ## Setting and changing it The key is ordinary Git config, so it obeys the usual scopes: `git config --global merge.conflictStyle zdiff3` for yourself, or a repository-local setting for a repo whose conflicts are habitually gnarly. It affects how future conflicts are *written* into the working tree. If you are already staring at a conflict written in the default style, you do not have to abort and retry: `git checkout --conflict=diff3 <file>` (or `--conflict=zdiff3` on a Git that supports it) rewrites that file's conflict hunks from the index stages in the chosen style. The same mechanism gives you `git checkout -m <file>`, which re-creates the conflict markers after you have mangled the file — useful when a botched manual edit has lost the original hunk. All of these work because the three versions are still sitting in the index at stages 1, 2 and 3; the working-tree text is only a rendering of them. ## How this interacts with tooling Merge tools that read the index directly — via `git mergetool` — usually show you base, ours and theirs in separate panes regardless of `merge.conflictStyle`, because they are pulling the stages, not parsing markers. The conflict style matters most for people who resolve conflicts in a plain editor, which is still the majority of resolutions. It also matters for automated checks: anything that greps for `<<<<<<<` should be aware that `|||||||` is an equally valid marker line to catch. ## Why interviewers ask It is a cheap probe for whether someone resolves conflicts deliberately or by reflex. A candidate who has never wondered what the original text was tends to resolve by picking whichever side looks familiar. A candidate who runs `diff3` or `zdiff3` is signalling that they treat a conflict as two intents to reconcile, and that they know a conflict is fundamentally three-way even though the default rendering is two-way.

  • You already have a conflicted file written in the default style. Can you get the base section without redoing the merge?
    Yes. The base, ours and theirs versions are still in the index at stages 1, 2 and 3, so `git checkout --conflict=diff3 <file>` re-renders that file's hunks in the three-way style. `git checkout -m <file>` similarly regenerates the markers if you have already mangled the file. No abort or re-merge is needed.
  • Why can a three-way view make the correct resolution obvious when the two-way view cannot?
    Because it turns a comparison into two diffs from a shared original. You can see that one side changed the logic while the other only reformatted, or that both sides made compatible edits that should be combined. With only two candidates, an unchanged side and a deliberately reverted side look identical.

saying these in an interview costs you the question

  • Thinks diff3 adds the incoming branch's name only
  • Believes the extra section is a Git-generated suggestion
  • Assumes changing the style requires aborting the merge
  • Says the ancestor version is unavailable once a conflict exists
  • Confuses merge.conflictStyle with the merge strategy

context