How Text Diff Works: Reading Changes Like a Code Reviewer
From the Myers algorithm to unified diff hunks, learn how diff tools decide what changed and how to compare configs, logs and documents without noise.
Every code review, every git diff and every "what changed in this config?" conversation relies on a diff algorithm. Most developers use diffs dozens of times a day without thinking about how they work. Knowing a little about the algorithm explains why diffs sometimes look strange, and how to get cleaner ones.
Try the examples in the Online Diff Tool.
A short history
The original Unix diff was written by Douglas McIlroy and James Hunt at Bell Labs and shipped in 1974. Their 1976 paper, "An Algorithm for Differential File Comparison", described an approach based on finding the longest common subsequence of lines.
In 1986, Eugene Myers published "An O(ND) Difference Algorithm and Its Variations". Its insight was that when two files are mostly similar, which is the normal case, you can find the minimal edit script in time proportional to the file size times the number of differences, rather than the product of the two file sizes. GNU diff uses it, and it is Git's default diff algorithm to this day.
What a diff actually computes
A line diff answers one question: what is the smallest set of whole-line insertions and deletions that turns file A into file B?
A: B:
apple apple
banana blueberry
cherry cherry
date date
elderberry
The minimal edit script is: delete banana, insert blueberry, insert elderberry. In unified diff format:
@@ -1,4 +1,5 @@
apple
-banana
+blueberry
cherry
date
+elderberry
Lines starting with a space are context, - is removed and + is added. The @@ -1,4 +1,5 @@ header says this hunk covers 4 lines starting at line 1 of the original and 5 lines starting at line 1 of the new version.
Hunks and context
Showing an entire 2,000-line file to highlight three changes would be useless. Diff tools group nearby changes into hunks and show a few unchanged lines around each for orientation. Three lines of context is the long-standing default for diff -u and git diff, and it is what the RedBison tool uses.
What a diff does not understand
Because a line diff only knows insertions and deletions of whole lines, it has blind spots:
- Moves. Move a function from the top of a file to the bottom, and the diff shows it deleted in one place and added in another.
- Renames inside lines. Rename a variable used on 40 lines and you get 40 changed lines, even though the change is conceptually one rename.
- Meaning. A diff does not know that reordering keys in a JSON object, or attributes in an XML element, changes nothing semantically.
This is why reviewers sometimes face a huge diff for a small change, and why it is good practice to separate refactors (moves, renames, formatting) from behaviour changes in different commits.
Myers is also not the only option. Git offers --patience and --histogram, which anchor on lines that appear exactly once in both files. They often produce more readable diffs for code where many lines are just } or blank, because Myers can "slide" a change to an unintuitive position among identical lines.
Taming whitespace noise
The single biggest source of noisy diffs is whitespace:
- An editor converts tabs to spaces, or the reverse.
- Trailing whitespace is stripped on save.
- Line endings change between Windows (CRLF) and Unix (LF).
- A formatter such as Prettier or Black is run for the first time.
Any of these can mark every line as changed. The Ignore whitespace option collapses runs of whitespace and trims lines before comparing, so only real edits remain. In Git, git diff -w does the same, and git diff --ignore-cr-at-eol handles line endings.
Ignore case is useful for content where case does not matter, such as SQL keywords, HTML tag names or hostnames.
A team-level fix helps too: an .editorconfig file and a .gitattributes entry like * text=auto keep whitespace and line endings consistent, so diffs stay clean.
Practical comparisons
Configuration drift
Two servers should be identical but one behaves differently. Export both configs, paste them side by side and turn on Ignore whitespace. Sorting keys first, for example by running JSON through a formatter, makes structured configs much easier to compare.
Generated output
When you change a code generator, a template or a build script, diff its output before and after. If you expected one line to change and forty did, you have learned something important before it ships.
Logs from a good run and a bad run
Strip timestamps and request IDs first, since those differ on every line, then diff. The first hunk often points straight at where the two runs diverged.
Documents
For prose, line-based diffs work best when each sentence is on its own line, a practice sometimes called "semantic line breaks". Otherwise, changing one word in a long paragraph marks the entire paragraph as changed.
Reading diffs well
A few habits from years of code review:
- Read the file list first. Know the shape of the change before the details.
- Look at what was removed, not just what was added. Deleted validation or error handling is easy to miss.
- Expand context when in doubt. Three lines is a default, not a rule.
- Watch for whitespace-only hunks that hide a real change among formatting noise.
- Be suspicious of large diffs with small descriptions.
Privacy
Diffing often involves configs, contracts or logs you would not want on someone else's server. The RedBison diff tool reads uploaded files with the browser's FileReader and compares everything locally.
Related tools
- JSON Formatter to normalize JSON before comparing
- Hash Generator for a quick "are these identical?" check on large text
- Regex Tester to strip timestamps or IDs from logs before diffing