You know the scene. You changed three lines of real logic, the formatter reflowed the surrounding function, and git hands you a wall of red and green: eighty deletions, eighty additions, and the actual change buried somewhere in the middle. The reviewer squints, the PR drags, and someone mutters "just squash it." None of this is your fault. It's the diff's fault. Line-based diff tools compare text, and code was never really text — it has structure, and the structure is usually what changed.
Difftastic is a CLI diff tool that respects that structure. Written in Rust by Wilfred Hughes, MIT-licensed, in development since 2018 and sitting at more than 26,000 GitHub stars, it works the way a careful reviewer does: parse both versions with tree-sitter into syntax trees, then diff the trees against each other, node by node. Under the hood that comparison is a graph problem, walked with Dijkstra's algorithm. You install a single binary called difft and point it at two files:
difft before.py after.py
Three things look different right away.
Formatting changes disappear. If your formatter split an expression across multiple lines, difftastic sees the same tree on both sides — and shows no diff where a text tool would show dozens of lines.
Wrappers get spotted. Add a map() around a call, or an await, and difftastic matches the delimiters exactly and highlights only the wrapper you added — not the whole body inside it.
Line numbers you can read. No more decoding @@ -5,6 +5,7 @@. Difftastic prints the real before-and-after line numbers of each change.
Worth flagging: difftastic is deliberately conservative. If either file fails to parse — say, minified JavaScript — it falls back to a plain line-oriented diff rather than pretending to understand something it can't. It also refuses to cry wolf: in check-only mode, difft --check-only --exit-code before.js after.js returns 0 when nothing syntactic changed, which makes a surprisingly good gate for "did this commit actually touch any code?"
Git integration is one command:
git config --global diff.external difft
After that, plain git diff runs through difft; for git log and git show you add --ext-diff. Prefer a lighter touch? Set difftastic up as a difftool and call git difftool — or alias it to git dft — only when you want it.
Language support travels with tree-sitter, which is both wide and current: C++ and Rust, Go and Haskell, Python, JavaScript, TypeScript, and data formats like JSON, YAML, and TOML. The release notes show the maintainers refreshing parsers constantly; Dockerfile and Fish support landed in 0.70 (August 2026), and 0.71 followed in September. This is a tool under active, careful maintenance, not a weekend project.
Hughes is candid that building it was "the most fascinating, most frustrating, and most challenging program I've ever written," and the README concedes that bad diff results still happen. Don't expect miracles on enormous generated files. But for everyday code review, difftastic changes the experience: you argue about the change, not the diff. And the frontier is already moving — mergiraf, a related tool, applies the same structural thinking to merges instead of diffs. That's a future line-based tools never had.