Compare two texts
Two texts, compared by line or by word. The answer is a real unified diff, the format that patch applies.
How it works
Checking whether line one matches line one and line two matches line two is not a diff. Insert a single line at the top of a file and that comparison reports every following line as changed, which is true and useless. A diff has to find the longest run of lines the two texts genuinely share, then describe the rest as insertions and removals. This page does that with Myers' algorithm from 1986, the one GNU diff and git both run, in the linear-space form so the memory cost is two integer arrays rather than a table.
The block below is a patch, not a picture
It opens with two header lines naming the file. Then one `@@` header per changed region, carrying the start line and the line count for each side. Then the lines: a leading space for unchanged, a minus sign for removed, a plus sign for added. That is a machine-readable format rather than a display convention, so saving the block to a file and running `patch -p1 < changes.patch` applies it. `git apply` reads the same thing.
The filename in those two headers matters, because `patch -p1` strips the leading `a/` or `b/` and then goes looking for whatever is left. A header saying `text.txt` will apply to a file of that name and refuse anything else, so there is a field under Fine tune for setting it to what your file is really called. The alternative is to name the target on the command line instead: `patch yourfile < changes.patch` ignores the headers entirely.
That claim was measured rather than asserted. Every diff here was compared against GNU diffutils 3.8 over 39 hand-built pairs, three whitespace settings and four context widths, and matched byte for byte on all 452 runs. The 140 of those with a non-empty diff were then fed to GNU patch 2.7.6, which reproduced the second text exactly every time. Twelve deliberate single-line breakages of this code were caught by that instrument, so the agreement is not the sort a broken implementation could also produce.
Lines for code, words for prose
Line granularity is what you want for source, configuration and data. Change three characters in a long line and the whole line reports as replaced, which is the right unit when the line is a statement. It is the wrong unit for a paragraph, where a one-word edit reports as sixty words gone and sixty-one arrived, and the reader has to find the difference by eye. That is what most free comparison tools hand back for prose.
Word granularity compares whitespace-delimited words instead, and marks them inline: removed words inside `[-` and `-]`, added words inside `{+` and `+}`. That is git's `--word-diff=plain` notation, and it is deliberately not a patch. Word boundaries are not line boundaries, so no `@@` header can describe the result and `patch` has nothing to anchor to. It also drops line structure entirely, so two texts differing only in where the lines wrap come back identical, which is what you want for a paragraph and wrong for a config file. The counts and the percentage switch units with it, which is why the row labels name the unit.
Differences you cannot see
A trailing space is invisible on screen and a difference to a byte comparison, so reporting one leaves a reader hunting for a change that is not visible. Trailing blanks are therefore ignored by default, and the row that counts how many lines were spared tells you when the setting earned its keep. A missing newline on the final line is the same category of invisible, and it is folded in under the same setting. That last decision was taken from `diff -Z`, which prints nothing at all for `abc` against `abc` plus a newline. Under the exact setting both are reported, as plain `diff` reports them.
The one invisible difference this page cannot show you is a line ending, and the reason is worth knowing. A browser textarea folds CRLF and a lone carriage return down to a single newline before any script can read the value. Measured on the built page through the editing pipeline as well as the value property: `a\r\nb` and `a\rb` both arrive as `a\nb`. So a Windows file and a Unix file pasted into these two boxes are already identical by the time the comparison starts, and no option could change that.
Which denominator the percentage uses
Three comparison tools will give three similarity figures for one pair of texts, because they divide by three different things. Some use the longer text, some count characters, some quietly report a ratio of changed lines to total lines. The figure here is twice the unchanged count divided by the sum of the two token counts, in whichever unit the granularity is set to. That is the same definition Python's `difflib.SequenceMatcher.ratio` uses. It reaches 100% only when the two token sequences are equal, and the row label names the unit because switching from lines to words moves the number.
Two large texts that differ a lot is the slow case
Myers' cost is proportional to the total length multiplied by the size of the difference, so the intuition that big inputs are slow is only half right. Measured here at 6,000 lines a side: three milliseconds when one line in a hundred changed, 392 milliseconds when half of them were rewritten, and 523 milliseconds with no line in common at all.
The search therefore carries a work ceiling that trips at roughly 600 milliseconds. Past it the region still being searched is reported as one replacement instead of a minimal script, and the note under the answer says so rather than leaving you with a diff that is quietly worse than it looks.
Each box takes 200,000 characters, about 5,000 lines of source. Longer input is trimmed and the note gives both figures. There is no upper bound on how similar the texts have to be: the cheap cases stay cheap, and it is the pair with nothing in common that costs, which is the opposite of what a size limit alone would suggest.
Questions
Why does a moved block show up as a deletion and an insertion?
Because a unified diff has no way to say `this moved`. The format describes one file becoming another through removals and additions, and a block that travels is removed from where it was and added where it went. Git can detect renames of whole files but not moved regions inside one, and the diff it prints for a move looks exactly like this. The default example on this page contains a swap of two lines so you can see the shape.
Can I really paste this into patch?
Yes, at line granularity. Copy the block, save it as `changes.patch`, and run `patch -p1 < changes.patch` in the directory holding the file. Set the filename field under Fine tune to your file's name first, or name the file on the command line and let patch ignore the headers. Word granularity is not applicable, because its markup sits inside lines and no hunk header can describe it.
Another tool says my two texts are 61% similar and you say 74%.
You are both right about different quantities. This page divides twice the unchanged token count by the sum of the two token counts, which is what Python's difflib reports. A tool dividing by the longer text alone gives a lower figure whenever the texts differ in length, and one counting characters rather than lines gives a different figure again. Check the unit in the row label before comparing two tools, and remember that switching this page from lines to words changes its own answer.
I pasted a Windows file and a Unix file and it found nothing.
That is correct, and it is the browser rather than this page. A textarea normalises CRLF and lone carriage returns to plain newlines before a script can read what you pasted, so both texts arrive with identical line endings. Two files differing only in that respect are genuinely identical here. If you need to see the difference you need a byte-level comparison on the files themselves, which `cmp` will give you.
The second box is empty and it is showing everything as removed.
That is the honest answer to the comparison you asked for: a text becoming nothing is a deletion of every line, and `diff` prints exactly the same thing. The note under the answer says the second text is empty so the state is not mistaken for a fault. Start typing or pasting and the diff narrows with every keystroke.
What is the `\ No newline at end of file` line for?
It marks a line that has no newline after it, which matters because a file ending in a newline and one that does not are different files. GNU diff prints the same marker, and patch needs it to reconstruct the second text byte for byte. Under either ignore-whitespace setting the distinction is treated as invisible and the marker still labels the line, matching what `diff -Z` does.