Text and config diff

Paste two versions of a file, config block or log excerpt and see exactly which lines changed. The comparison runs in this tab — nothing you paste is uploaded anywhere.

Up to 4,000 lines per side. Cmd/Ctrl + Enter compares.

Nothing compared yet.

diff
added removed unchanged similarity

Paste two versions above and press Compare. Removed lines are marked , added lines +.

How it works

A diff is a longest-common-subsequence problem

Understanding what the algorithm actually optimises for explains most of the moments where a diff looks wrong — and tells you how to structure changes so review stays cheap.

Both inputs are cut into lines and every distinct line is mapped to an integer, so the inner comparison loop compares numbers instead of strings. The algorithm then fills a table of (n+1) × (m+1) cells to find the longest common subsequence: the longest ordered set of lines that appears in both sides, not necessarily contiguously. Lines on that path are unchanged; every line of the original that is off the path is a deletion, and every line of the changed side that is off the path is an insertion. That is the whole model — there is no notion of "edited line", only a removal next to an addition, which is why a one-character fix prints as then +.

The cost is O(n × m) in both time and memory, which is why this page trims identical leading and trailing lines before building the table (an optimal LCS always keeps a matching first or last pair, so trimming cannot change the result) and refuses anything past a fixed cell budget rather than allocating it. It is also why real diff tools cap themselves: two 20,000-line files that share nothing would be a 400-million-cell table.

The second consequence is subtler and matters more day to day: the LCS is not unique. When several subsequences tie for longest, the tie is broken by a rule, not by meaning — which is how a diff ends up anchored to a stray closing brace or blank line and produces a hunk that is technically minimal and humanly unreadable. Nothing in the algorithm knows what a function is. Every "smart" behaviour you have seen — move detection, patience or histogram diff, indentation heuristics — is a layer bolted on top precisely because plain LCS output is often the wrong story about what happened.

A moved block reads as two edits

Move a 40-line function from the bottom of a file to the top and the diff shows 40 deletions and 40 additions with no signal that they are the same code. LCS preserves order, so relocated text cannot be matched. This is the single biggest reason reviewing a refactor is painful — the reviewer has to diff the two halves in their head. The practical fix is procedural, not technical: move code in one commit that changes nothing else, then change behaviour in the next, so each diff tells one story.

Line-based is a convention, not a limit

Character-level diffs are more precise, but the entire ecosystem — patch,git apply, code review, merge conflict markers — speaks in whole lines, and a character-level patch cannot be applied by any of it. The usual compromise, used here, is line-based structure with a word-level highlight inside a replaced pair. That refinement is deliberately suppressed when the two lines share less than about 40% of their characters: an intra-line diff of two unrelated lines invents a relationship that does not exist and misleads more than plain −/+ would.

Nothing leaves this machine

The comparison is plain computation, so it runs in the page itself: there is no upload, no API call and no server-side log of what you pasted. That matters because the things people most often want to diff are exactly the things that should not be pasted into a random web form — nginx and Terraform config, Kubernetes manifests, environment files, production log excerpts, customer records. Load the page once and it will keep working with the network off.

Limits and honest caveats

  • Size caps. 4,000 lines per side, 2,000,000 characters across both inputs, and 4,000,000 comparison cells after identical top and bottom lines are trimmed. Past any of those the diff is refused with a message naming the cap, because attempting it would hang the tab rather than finish. For anything larger usegit diff or diff -u locally.
  • Line endings are normalised. CRLF and CR become LF before comparing, so two files that differ only in line endings are reported as identical. Usefile or cat -A if that is the thing you are chasing.
  • A trailing newline is not a line. Like every diff tool, a final newline terminates the last line instead of starting an empty one — so inputs differing only by a trailing newline compare as identical. Git reports that case explicitly with\ No newline at end of file; this page cannot.
  • Ignore options hide real differences. With ignore case orignore whitespace on, lines that match only after normalising are shown as unchanged, displaying the original side's text. Useful for reviewing a re-indentation; misleading if whitespace is significant, as in YAML, Makefiles, Python or Dockerfile heredocs.
  • No Unicode normalisation. Visually identical text in NFC and NFD form (common when a string has travelled through macOS filenames) compares as different, and invisible characters such as a zero-width space or a non-breaking space show as a changed line with no visible cause.
  • Out of scope. No rename or move detection, no three-way merge, no conflict resolution, no binary or image diff, and no syntax awareness — a JSON key reordering is a change here even though the documents are equivalent.

Found something you’d rather not fix yourself?

Configuring DNS, TLS and email authentication properly — without breaking live traffic — is part of our cybersecurity and managed services work.

Talk to an engineer