The problem with reviewing a refactor
A refactor is the case where a line-by-line comparison is least useful. Renaming a variable touches every line that mentions it. Extracting a function moves a block and changes its indentation. Reordering members of a class rewrites nothing at all and yet, read strictly top to bottom, appears to delete and recreate half the file. The diff is enormous and the behaviour may be identical.
What you actually want to know is narrower: which of these changes could alter what the program does. Getting to that question means first clearing away the changes that provably cannot.
Comparing structure rather than lines
For JavaScript, TypeScript, TSX, Python, JSON, CSS and HTML, the text is parsed into a syntax tree and the comparison happens between meaningful blocks — a whole function is one unit, so relocating it is one change rather than a run of deleted lines followed by a run of added ones. Every other language — C, C++, Go, Rust, Java, C#, PHP, Ruby, shell, SQL, YAML, and the rest — still loads and compares fine, just without a syntax tree: the text is split into blocks separated by blank lines, or line by line if it has none. A relocated block is still reported as a move. What you lose is the finer reporting a grammar buys: a function is not recognised as a single unit, and a renamed variable inside an otherwise-identical block is reported as an edit rather than a rename.
The language is detected automatically — a fast keyword pass narrows the candidates, then the shortlist is trial-parsed and whichever grammar produces the fewest errors wins — and you can override the choice in the picker. Overriding is worth doing when a file is short enough to be ambiguous, which is where automatic detection is least reliable.
Why a rename is not reported as a rewrite
Each block is fingerprinted twice: once exactly as written, and once with local identifier names normalised away. Two blocks whose normalised fingerprints agree while their literal ones differ are the same logic with different names, and that is reported as a match rather than as a rewrite.
So renaming a parameter throughout a function produces one modified block with the changed identifiers highlighted, not a wholesale replacement. This is the single biggest difference in output when reviewing a refactor, because renames are the most common thing a refactor does and the thing a textual comparison handles worst.
The normalisation is of local names. Renaming something exported and used across a file changes the call sites too, and those are genuine differences in those blocks — correctly so, since a caller referring to a different name is a real change to that caller.
Which mode to use, and what the two scores tell you together
Smart matches blocks wherever they ended up, so a file whose functions were reordered scores near-identical. Strict compares top to bottom in order, with word-level highlighting inside changed lines. Both feed the same similarity formula, which makes the two numbers directly comparable — and the gap between them is the most informative single reading you can take.
A high Smart score with a low Strict score means the content survived but the order changed: a reorganisation. Both high means a small, localised edit. Both low means substantial rewriting, and the diff is worth reading in full rather than skimmed. Strict is also the mode to use when you are specifically checking that sequence was preserved, because Smart is built to treat relocation as inexpensive and will therefore hide exactly what you are looking for.
Reformatting is a real difference here
Worth being direct about, because it is the most likely thing to make a code comparison noisy: this tool has no option to ignore whitespace. Reindenting a file, changing tab width, rewrapping long argument lists, or running a formatter over one side and not the other all produce genuine differences in the comparison, because the comparison reads the text as written.
The fix is to normalise before comparing rather than during. Run the same formatter over both sides, then compare the results — the differences that remain are the ones you were looking for. This is more reliable than any ignore-whitespace setting anyway: a formatter produces one canonical form, whereas ignoring whitespace hides real changes to string literals and to languages where indentation is syntax.
Capitalisation is different — there is a Case sensitive switch, on by default and correctly so for code. Whitespace and case covers when to reach for it.
The limit that matters most: a high score is not a safety claim
An order-independent comparison treats relocation as cheap. In code, relocation is not always cheap. Two statements that both write the same variable cannot be swapped without changing behaviour. A call moved before the initialisation it depends on is broken. An import reordered past a module with side effects at load time may change what runs and when.
Where a relocated block reads or writes state that a neighbouring block also writes, the row carries a caution marker saying reordering may not have preserved behaviour. It is advisory, deliberately conservative, and cannot catch every case — a function called for its effect on something the analysis cannot see will not be flagged.
Read a near-100% Smart score as "almost nothing was added or removed", which is exactly what it computes. It is not a statement that the two versions behave identically. Nothing that reads only the text can make that statement — that is what tests and review are for. Moved and reordered blocks goes into the mechanism, and how to read a diff covers the rest of the output.
Comparing SQL queries, database migrations, and scripts
Languages without full tree-sitter AST grammars — such as SQL queries, database migration scripts, shell scripts, Go, Rust, or C++ — are still compared with structural block intelligence. The engine groups statements separated by blank lines into cohesive units.
When comparing complex SQL statements (like two versions of a multi-table JOIN or view definition), a restructured query block is still matched as a move rather than a rewrite. If you are comparing raw query strings or unformatted migrations, running them through a SQL formatter first gives you the cleanest, most readable diff.
Safe for proprietary source code: zero cloud telemetry
Pasting internal source code, proprietary algorithms, database schemas, or infrastructure scripts into cloud diff tools introduces severe data leak risks. Many web utilities log inputs to remote databases or route them through external analytics pipelines.
This tool executes all tokenization, diffing, and WebAssembly tree-sitter parsers directly in a client-side Web Worker on your local machine. No source code ever leaves your browser, and no server endpoint receives your text. For verification steps using your browser's Network tab, see our guide to client-side private comparison.
Large files
Very large inputs step down rather than hanging, and the status line always says when part of the analysis was skipped. Past roughly a megabyte the syntax-tree path gives way to simpler strategies, and past a couple of megabytes it is dropped entirely in favour of prose or sequential comparison. Move detection is preserved for as long as possible, since it is the thing hardest to do by eye.
When you see a notice saying only exact block matching ran, edited blocks in that comparison are being reported as an addition plus a deletion rather than as modifications. The result is real but coarser than usual, and the difference is not marked anywhere except in that notice.