Merge conflicts
A Socratic walk-through of merge conflicts — reasoned out one step at a time, not lectured.
The question we started with
THE QUESTION #How can many people edit the same thing at once without silently overwriting each other?
Two people open the same file in the morning and both change it. By the afternoon both want to save. Something has to decide what the file now says, and the honest options are narrower than they look: keep one version and lose the other, refuse the second save, or combine the two. Version control claims the third — and usually does it without asking anyone. What could possibly make that safe?
Reasoning it through
REASONING #Start with the cheapest answer, because it tells you what the expensive one is buying. Give the file a lock: one writer at a time, everyone else waits. This genuinely works, and it is what a document on a shared drive does. What it costs is throughput — work serialises, and someone goes to lunch still holding the lock. Version control took the opposite bet, letting everybody edit and sorting it out afterwards. That bet only pays if "sorting it out" is usually automatic.
So can it be? Hand a program two files, yours and mine, and ask for the merged result. Look at what it actually has to work with. A line appears in your copy and not in mine. Did you add it, or did I delete it? Those want opposite outcomes, and nothing in the two files distinguishes them. With two documents in hand, every difference is ambiguous, and no cleverness rescues it.
The fix is not a better algorithm. It is a third document. Both versions descend from one they once shared — the merge base, the most recent commit reachable from both lines of work, which Git will compute for you with git merge-base. Compare each side against that ancestor instead of against each other, and every difference acquires a direction: present in yours and absent from the ancestor means you added it; present in the ancestor and absent from mine means I deleted it. The ambiguity was never in the edits. It was in having too few documents.
Now the rule almost writes itself. A region changed on one side only: take the changed side. Unchanged on both: keep it. Changed identically on both: take it once. Changed on both, differently: nothing in the three documents says which was intended — so stop and say so. That is a conflict, and notice what it actually is. Not a failure of the tool but its refusal to guess. Every clean merge you have ever enjoyed is the same procedure quietly exercising the first three cases.
Worth asking what "region" means here, because that is where the honesty of the whole scheme sits. It means lines. The comparison works on line-oriented chunks with a little context around them, and has no idea that one chunk is a function and another a comment. That is why two people editing the same line collide while two people editing adjacent functions do not, and why binary files conflict wholesale — there is no line structure to compare. Implementations layer heuristics on top: rename detection, so a file moved on one side and edited on the other still merges, and strategies for branches that share more than one equally recent ancestor.
Which brings the real limit into view. Suppose I rename a function and you, in a different file, add a call to the old name. The changed regions never touch. The merge is clean, and the result does not build. Subtler versions compile perfectly and behave wrongly. A semantic conflict passes textual merging every time, because textual merging compares characters and can be no wiser than that. This is why the merge is not the end of the process and the test suite is.
The analogy
THE ANALOGY #Two editors each take a photocopy of the same manuscript page home and mark it up. To combine their work you do not lay the two marked copies side by side — you put the untouched original between them. Then anything one editor changed and the other left alone is simply adopted, and only where both struck out the same sentence and wrote different replacements does anyone have to be asked what they meant.
Manuscript pages do not have to work. Two edits can each be locally correct and jointly incoherent — a character renamed on page 3 and still called the old name on page 40 — and no comparison of the marked-up copies, however careful, will reveal it.
Clarifying the model
THE MODEL #A few refinements follow from the same mechanism. Resolving a conflict is authoring, not choosing: the tool writes both versions into the file between marker lines, but the correct resolution is often neither side, and sometimes something new that reconciles both intentions. Rebasing feels different from merging for a structural reason — it replays your commits one at a time, each against a different base, so a single underlying disagreement can surface as a conflict several times over, where a merge meets it once.
And conflict frequency is a property of how work is divided, not of the tool. Two long-lived branches touching the same files will conflict no matter how good the merge algorithm is, because their shared ancestor keeps receding. Much of the argument for integrating continuously is really an argument for keeping the merge base recent — shrinking the window in which two people can disagree.
A picture of it
THE PICTURE #How to readRead left to right. The first commit on the trunk is the merge base — the last state both people shared, and the third document the algorithm needs. Alice and Bob then diverge, each committing with no knowledge of the other. Alice's footer change touches a region Bob never went near, so it merges without comment; her intro change and his hit the same lines, and that overlap is what the second merge stops on. The picture also shows why the base matters: the further left that first commit sits, the more edits pile up on each branch and the more chances they have to land on the same region.
What became clearer
WHAT CLEARED #Automatic merging is not clever, and that is the point. It works because a third document — the common ancestor — turns ambiguous differences into directed ones, letting a tool adopt any change made on one side alone with no judgement at all. A conflict is what remains after that: the small set of places where both sides changed the same thing differently, handed back with an admission that the tool has no basis to decide. What the mechanism cannot see is meaning, so two edits that never touch textually can still be incompatible, and only running the thing will tell you.
Where to go next
ONWARD #- How real-time collaborative editors avoid merge bases entirely, using operations designed to commute.
- What a merge that understood syntax trees rather than lines could and could not fix.
Key terms
TERMS #| Term | What it means |
|---|---|
| Merge base | the most recent version common to both lines of work; the third document a three-way merge compares against. |
| Three-way merge | combining two versions by comparing each against their common ancestor rather than against each other. |
| Conflict markers | the delimiters a tool writes into a file to present both competing versions of a region for a human to resolve. |
| Optimistic concurrency | letting everyone edit freely and reconciling afterwards, as opposed to locking a resource in advance. |
| Semantic conflict | two changes that merge cleanly as text but are logically incompatible, so the merged result is broken. |
Every term the collection defines is gathered in the glossary.