When Paths Reunite
Two rivers can merge into one. But what of two histories? When parallel timelines must converge, we face one of computing's deepest challenges: reconciling divergent paths.
The Merge Problem
Alice and Bob both start from the same chess position. Each explores a different continuation:
shared state at move 15:
board: [... position ...]
turn: white
Alice's branch:
16. queen d1 → h5
17. (black responds)
18. queen h5 → f7 checkmate!
Bob's branch:
16. knight f3 → e5
17. (black responds)
18. knight e5 → c6Now they want to merge. But their histories diverge at move 16. The queen can't be both at h5 and at d1 (on its way to e5). These timelines are incompatible.
When Merging Is Easy
Sometimes parallel changes don't conflict:
shared document:
line 1: "Introduction"
line 2: "Chapter one begins..."
line 3: "Conclusion"
Alice adds to line 2:
line 2: "Chapter one begins with a question."
Bob adds to line 3:
line 3: "Conclusion: We found the answer."These changes are independent. Alice modified line 2; Bob modified line 3. The merge is straightforward---we can visualize it by showing each person's contribution in a different color:
The merged document incorporates both changes seamlessly:
merged document:
line 1: "Introduction"
line 2: "Chapter one begins with a question." // Alice
line 3: "Conclusion: We found the answer." // BobWhen Merging Is Hard: Conflicts
But what if both touched the same thing?
shared: "The answer is 42."
Alice: "The answer is 43."
Bob: "The answer is forty-two."Both changed the same line. Whose version is correct? The system cannot know. This requires human judgment.
CONFLICT in line 1:
<<<<<<< Alice
The answer is 43.
=======
The answer is forty-two.
>>>>>>> Bob
Please resolve manually.Merge Strategies
Different systems handle conflicts differently:
Last write wins: The most recent change overwrites others.
// Simple but lossy
if alice_timestamp > bob_timestamp:
result = alice_version
else:
result = bob_versionFirst write wins: The earliest change is preserved.
// Opposite policy
result = whichever_was_written_firstManual resolution: Ask a human.
// Honest but slow
if conflict:
notify_human()
wait_for_resolution()Automatic merge with conflict detection: Merge what's possible, flag what isn't.
// Git's approach
for each change:
if no_conflict:
apply_automatically()
else:
mark_as_conflict()Events vs Snapshots: Different Merge Challenges
Merging snapshots: Compare final states, find differences.
merge(snapshot_A, snapshot_B, common_ancestor):
for each field:
if A[field] == B[field]:
result[field] = A[field] // same: easy
else if A[field] == ancestor[field]:
result[field] = B[field] // only B changed
else if B[field] == ancestor[field]:
result[field] = A[field] // only A changed
else:
CONFLICT // both changedMerging events: Reconcile histories, order events.
merge(events_A, events_B, common_events):
divergent_A = events_A - common_events
divergent_B = events_B - common_events
// Can these events be interleaved?
if compatible(divergent_A, divergent_B):
return common_events + interleave(divergent_A, divergent_B)
else:
CONFLICTEvent merging must consider:
- Order: Which events should come first?
- Causality: Did one event depend on a state that another event changed?
- Commutativity: Does order matter? (Sometimes A then B = B then A)
The Git Model
Git is masterfully designed for merging:
main: A → B → C
\
feature: D → E
merge: A → B → C → M
\ /
D → E- Find the common ancestor (B)
- Compute differences: main changed C; feature changed D, E
- Apply both sets of changes to B
- If conflicts, mark them for human resolution
- Create merge commit M recording both parent histories
Conflict-Free Replicated Data Types (Preview)
What if we designed our data so conflicts couldn't happen?
Conflict-free replicated data types (CRDTs) achieve this through clever design:
// A "grow-only set" never has conflicts
// Items can only be added, never removed
Alice adds: {1, 2, 3}
Bob adds: {2, 3, 4}
Merge: {1, 2, 3, 4} // union, always works // A "counter" that tracks increments separately
Alice: +3
Bob: +2
Merge: +5 // just add them upWe'll explore the mathematics behind this in the next chapter.
Merging is hard when we think of states as arbitrary blobs. But what if states had algebraic structure? What if we could prove that certain operations always merge cleanly? In the next chapter, we discover the algebra of state.