text merging
three way merge
algorithm
version control
text processing

Three Way Merge Algorithms for Text

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

A three-way merge combines two edited versions of a file by comparing both of them against a shared base version. This is the standard merge model in version control because it can tell the difference between "both people changed the same line" and "one side changed it while the other left it alone."

The Three Inputs

A three-way merge needs:

  • 'base: the common ancestor'
  • 'local: one edited version'
  • 'remote: the other edited version'

That extra base version is what makes the algorithm better than simply comparing local and remote directly. Without the base, the merge engine cannot tell who changed what.

The Core Decision Rule

At a high level, line-based three-way merge follows this logic:

  • if local and remote are the same, take either one
  • if local differs from base and remote equals base, take local
  • if remote differs from base and local equals base, take remote
  • if both differ from base in incompatible ways, report a conflict

That sounds simple, but real merge tools also need to align inserts, deletes, and moved blocks, which is where diff algorithms enter the picture.

Real Systems Usually Build On Diff Algorithms

Three-way merge engines are usually built on top of a two-way diff algorithm such as an LCS-based diff or Myers diff. The diff step determines which chunks changed from base to local and from base to remote.

The merge step then tries to combine those change hunks.

So a practical text merge engine is often:

  1. diff base to local
  2. diff base to remote
  3. align the edits
  4. auto-merge non-overlapping changes
  5. mark overlapping edits as conflicts

This is why merge quality depends both on the merge strategy and on the quality of the diff algorithm underneath it.

A Small Runnable Example

The following Python example shows a simplified line-by-line merge. It is not a full replacement for Git, but it demonstrates the decision logic clearly.

python
1def merge_lines(base, local, remote):
2    merged = []
3    for i, base_line in enumerate(base):
4        local_line = local[i]
5        remote_line = remote[i]
6
7        if local_line == remote_line:
8            merged.append(local_line)
9        elif local_line != base_line and remote_line == base_line:
10            merged.append(local_line)
11        elif remote_line != base_line and local_line == base_line:
12            merged.append(remote_line)
13        else:
14            merged.append(
15                "<<<<<<< LOCAL\n"
16                + local_line
17                + "\n=======\n"
18                + remote_line
19                + "\n>>>>>>> REMOTE"
20            )
21    return merged
22
23
24base = ["name=app", "timeout=10", "retries=3"]
25local = ["name=app", "timeout=20", "retries=3"]
26remote = ["name=app", "timeout=10", "retries=5"]
27
28for line in merge_lines(base, local, remote):
29    print(line)

Because the edits affect different lines relative to the base, this merge succeeds automatically.

Why Conflicts Happen

Conflicts happen when both sides changed the same region in different ways or when surrounding edits make it impossible to align hunks confidently.

For example:

  • 'base: timeout=10'
  • 'local: timeout=20'
  • 'remote: timeout=30'

Both sides changed the same line differently, so a text merge engine cannot know which value is correct.

That is a semantic problem, not just a syntactic one.

Line-Based Merge Is Practical, Not Perfect

Most source control systems merge text by lines because it is fast and predictable. But this also means merges can be awkward when the true structure is not line-oriented.

For example:

  • JSON key reordering can create noisy conflicts
  • code formatting changes can obscure logical edits
  • moved functions may appear as delete-plus-insert instead of a move

Language-aware merge tools sometimes improve this, but line-based merge remains the default because it is general and robust.

Common Pitfalls

The most common misunderstanding is thinking three-way merge magically resolves all simultaneous edits. It only resolves edits that are compatible relative to the base.

Another pitfall is underestimating the diff algorithm. Poor hunk alignment produces worse merges even when the high-level merge logic is correct.

Developers also confuse textual conflicts with semantic correctness. A clean merge can still produce broken code.

Finally, formatting-only commits often make later merges harder because they change many lines without changing behavior.

Summary

  • Three-way merge uses base, local, and remote.
  • The base version is what lets the algorithm attribute changes correctly.
  • Real merge engines depend on both diff quality and merge logic.
  • Non-overlapping edits usually merge automatically.
  • Conflicts remain when both sides change the same region incompatibly.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.