Diff Checker

Compare two texts side by side, down to the words that changed inside a line

Try one:

Paste something into both boxes.

Any two texts have hundreds of ways to describe how they differ, and almost all of them are useless to read. Picking the one a person would have written down by hand is the job a diff algorithm actually does.

Original      Changed

  a             a
  b             c
  c             b

One answer: delete b, keep c, insert b     (2 edits)
Another:    keep a, insert c, delete c     (2 edits)

Both are correct. Both are two edits. Only one of them
reads like what somebody did.

This uses Myers' algorithm, published in 1986 and still what git, most editors and nearly every code review tool run. It finds the shortest edit script, meaning the fewest insertions and deletions that turn one text into the other. That is not the same thing as "readable", though the two agree nearly all the time.

Its other property is what makes it usable. It runs in time proportional to the size of the input multiplied by the number of differences, so two versions of the same file, which differ in a handful of lines, take barely longer than reading them.

Two levels of highlight, and why both are needed

A line-level diff tells you a line changed. It cannot tell you whether somebody fixed a typo or rewrote the sentence, and on a reformatted file it marks everything and says nothing.

So changed lines are compared a second time, word by word, and only the words that actually moved are marked inside the row. Whitespace is kept as its own token during that pass, and that is what makes an indentation change visible instead of invisible.

Character-by-character is available and is usually worse: on two ordinary sentences it highlights the letters they happen to share and reads as static. Where it earns its keep is on things with no word boundaries at all: a hash, a base64 blob, some enormous identifier.

Whitespace is a decision, not a detail

  const x = 1;        vs        const x = 1;

Whitespace matters              one line changed
Ignore leading and trailing     identical
Ignore how much whitespace      identical
Ignore all whitespace           identical

Four settings, because "ignore whitespace" means four different things depending on what you are looking at. Re-indenting a file wants the second. Comparing text that has been through a formatter wants the third. Checking whether two minified files are the same wants the fourth. Debugging why a YAML file stopped parsing wants the first, and wants it badly.

In every case the lines are still shown exactly as they were written. The setting changes what counts as equal, never what is displayed, because a tool that quietly edited the input to make the comparison work would be lying about both sides.

Line endings, and the diff where everything changed

A file written on Windows ends its lines with \r\n and one written anywhere else ends them with \n. Compared strictly, every single line differs, and the diff is useless.

Carriage returns are normalised away here before anything is compared, so that never happens. It is worth knowing that git does the same thing throughcore.autocrlf, and that the classic "my whole file shows as changed" pull request is this and nothing else.

A trailing newline counts as the end of the last line, not an empty line after it. That is the other place tools disagree and produce a phantom difference at the end of every file.

The patch format

--- a
+++ b
@@ -1,4 +1,4 @@
 the first line
-the second line
+the second line, edited
 the third line
 the fourth line

-  a line removed        @@ -start,count +start,count @@
+  a line added          three lines of context either side
   a line unchanged

The unified format is what diff -u, git diff andpatch all speak. The header line for each hunk gives the starting line and the number of lines on each side, and three lines of unchanged context either side let the patch be applied to a file that has drifted slightly since.

Copy the patch from above and patch -p0 < file.patch will apply it. The one thing to check first is the file names on the--- and +++ lines, which are placeholders here because this tool never saw a file.

Where a line-based diff gives up

Two texts with nothing in common have as many differences as they have lines, and lining them up throws up coincidental matches. Every blank line and every closing brace pairs off, joined by walls of red and green. Past a few thousand differences that is what you get, so past that point this stops trying and says one text was replaced by the other. Which it was.

The same limit is why a diff of two minified files is unhelpful: the whole file is one line, so a line diff has exactly one thing to say. Format both first, or switch to character granularity and accept that the result will be a mosaic.

What a diff cannot see

A block of text that moved is a deletion in one place and an insertion in another, with nothing connecting them. Some tools detect moves as a separate pass; this one does not, and neither does git diff without--color-moved.

Nor is a diff a comparison of meaning. Two JSON documents with their keys in a different order are the same data and a completely different diff. Sort the keys first and the diff collapses down to what actually changed. The JSON formatter on this site will do that at every level.