How diff tools work โ€” the algorithm behind 'what changed'

Comparing two versions of a document by eye is slow and error-prone; a diff does it in milliseconds and highlights exactly what changed. The algorithm behind it is one of computer science's neat results and it shapes what a diff can and can't show โ€” which is worth knowing whether you're reviewing a contract in the text diff tool or a code change on GitHub.

The problem: minimal edits

Given an old text and a new one, a diff wants the shortest script of insertions and deletions that turns one into the other โ€” the "edit script". Shortest matters: a diff that deletes everything and inserts everything is technically correct and useless. Finding the shortest is equivalent to finding the longest run of material both versions share, in order, and treating everything else as changed.

Longest common subsequence

The shared material is the longest common subsequence (LCS): the longest sequence of items (lines, usually) that appears in both texts in the same order, not necessarily contiguously. Computing it exactly is a dynamic-programming table of size old ร— new โ€” fine for documents, slow for huge files โ€” and the standard practical algorithm is Eugene Myers's 1986 method, which finds the edit script in time proportional to the size of the difference, so two nearly identical files diff almost instantly. Git's default diff is a Myers variant; alternatives ("patience", "histogram") choose among equally short scripts the one that looks most sensible to humans, by anchoring on unique lines first.

Lines, words, characters

The unit matters. Line diffs (the default for code) treat any change to a line as replacing the whole line, which is right for code and wrong for prose, where one changed word marks a whole paragraph. Word diffs compare within lines and show the changed words; character diffs go finer and are best for short strings. Most tools do a line diff first and then a word-level pass inside changed lines. Whitespace handling is a separate choice: ignoring it hides reformatting noise and hides accidental changes inside strings; a tool that lets you toggle it is the honest one.

Reading a unified diff

--- old.txt
+++ new.txt
@@ -12,4 +12,5 @@
 unchanged context
-a removed line
+the line that replaced it
+an added line
 more context

The @@ header gives the line ranges in each file; lines beginning with a space are context, โˆ’ removed, + added. It is the format of patches, pull requests and the output of diff -u, and it is designed so a program can apply it to the old file to produce the new one โ€” which is what "applying a patch" means.

Where diffs fall down

  • Moved blocks show as a deletion here and an insertion there; plain diffs don't detect moves.
  • Reformatting (re-wrapped paragraphs, re-indented code) swamps the real change; diff after normalising whitespace, or with a word diff.
  • Structured data: two JSON files with reordered keys are semantically identical and textually all different โ€” use a structural comparison like JSON diff.
  • Binary files and images have no meaningful text diff; compare with checksums (How checksums verify downloads and backups โ€” and which one to use) or visually with the image compare tool.
  • Rich documents: Word's track changes is a diff over its own structure; exporting to plain text first (PDF to Word or the reverse) loses formatting changes but gets the words compared.

Sources and further reading

The claims in this guide rest on these references, which were checked when the guide was last updated. Spotted an error? The contact page says how to report it.

  1. Diff โ€” Wikipedia
  2. Longest common subsequence โ€” Wikipedia

Try the tool

Frequently asked questions

How does a diff decide what changed?

It finds the longest sequence of lines common to both versions in order (the longest common subsequence) and marks everything else as removed or added โ€” the shortest edit script.

Why does the diff show a whole line changed when I fixed one word?

Line-level diffs treat any changed line as replaced. Use a word- or character-level diff for prose, or a tool that highlights changed words inside changed lines.

What do โˆ’ and + mean in a diff?

Lines beginning with โˆ’ are in the old version only, + in the new only, and lines with a space are unchanged context. The @@ header gives line numbers.

Can a diff detect moved paragraphs?

Standard diffs can't โ€” a move appears as a deletion and an insertion. Some tools add move detection as a separate pass.