4. Merging

Three-Way Merge

seniorversion-control-internalsmerging

A three-way merge resolves two divergent versions of a file (ours and theirs) against their common ancestor (base), line by line:

  • Neither side changed a line, or both changed it to the SAME new value: no conflict -- use that value.
  • Only ONE side changed the line: that side's change wins automatically.
  • BOTH sides changed the line, to DIFFERENT values: a conflict.

Scope note: this exercise works at fixed line positions -- base, ours, and theirs are all the same length, and merging is purely about each side's content change at a given position (not line insertions/deletions, which need a full diff-alignment pass first -- see the real-world grounding note below for where that lives in actual Git).

Consecutive conflicting lines must be coalesced into ONE conflict block (not a separate marker pair per line), using the same textual format Git embeds directly into a file's content:

<<<<<<< OURS
<ours' lines for this block>
=======
<theirs' lines for this block>
>>>>>>> THEIRS

Implement three_way_merge(base, ours, theirs), returning a MergeResult(lines, has_conflict) (given -- don't modify it): lines is the merged file with any conflict blocks embedded as marker lines, and has_conflict is True iff at least one conflict block was produced. Raise ValueError if base, ours, and theirs aren't all the same length.

Example

base   = ["a", "b", "c"]
ours   = ["a", "OURS", "c"]
theirs = ["a", "THEIRS", "c"]

three_way_merge(base, ours, theirs).lines
# -> ["a", "<<<<<<< OURS", "OURS", "=======", "THEIRS", ">>>>>>> THEIRS", "c"]

Constraints

  • Adjacent conflicting positions form ONE block, not one block per position -- check the "consecutive conflicts" test carefully.
  • The auto-resolve rule always picks the CHANGED value when only one side changed (never silently falls back to base).

Real-world grounding

  • Git's line-level three-way file merge (the exact only-one-side-changed-wins / both-changed-differently-conflicts policy, plus <<<<<<</=======/>>>>>>> marker formatting), used by both git merge-file and the recursive/ort merge strategies: xdiff/xmerge.c
  • Git's current default merge strategy, which runs this kind of per-file content merge as one step inside a much larger tree-level merge (also handling renames, directory/file conflicts, and which files even need a three-way merge in the first place): merge-ort.c

Real Git first diffs base against ours and base against theirs (so insertions and deletions shift line numbers correctly on each side independently) before ever comparing lines to each other -- this exercise's fixed-position model implements the core merge POLICY that runs once that alignment step is done.

hints
three_way_merge.pysign in to run
Loading...
test result

This is a preview. Sign in to run this exercise against the real tests.