Difftastic: a structural diff that parses your code before comparing it
a structural diff that understands syntax 🟥🟩
At a glance
- What is it?
- Difftastic compares files by syntax rather than by line, using tree-sitter parsers and a graph search over AST nodes. It is a review tool for humans, not a patch generator, and its README is candid about performance and crash fixes.
- Who is it for?
- Adopt difftastic if you review code by eye and want reformatting noise out of the way, and you accept that it will not produce patches you can apply. Do not adopt it as a merge tool or as a patch generator; the README states both are non-goals.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What difftastic solves, and who it is for
A line diff answers a question about text: which lines differ. If you move a function into a new block, or run a formatter that re-wraps a call across several lines, the line diff reports a large change even though the program means the same thing. Difftastic answers a question about structure instead. Its README describes it as "a structural diff tool that compares files based on their syntax", and the examples show the consequence: whitespace changes that carry no syntactic meaning are ignored, and a reformatted expression that is now split over multiple lines is shown as what actually changed.
The audience is narrow and specific. This is a tool for a person reading a diff during review, in a terminal. It is not a library that produces machine-applicable output. The README's non-goals section says plainly that difftastic output is "intended for human consumption, and it does not generate patches that you can apply later", and directs you to diff if you need a patch. That single sentence rules out a whole category of use: you cannot put difftastic in a pipeline that applies changes.
It supports over 30 programming languages according to the README, with the full list in the manual. For a file with an unrecognised extension, it falls back to a line-oriented diff with word highlighting, so a language outside the list degrades rather than fails.
How the structural diff works: tree-sitter, ASTs and Dijkstra
The mechanism is stated in the FAQ. Difftastic parses both files with tree-sitter, turning each into a syntax tree, and then treats the comparison as a graph problem solved with Dijkstra's algorithm. The README points to a blog post and to an internals section of the manual for the design. The repository layout backs this up: there is a vendored_parsers/ directory holding tree-sitter parsers, and the Cargo manifest's include list ships vendored_parsers/highlights/*.scm along with the C and header sources of those parsers, so parsing and syntax highlighting are compiled into the binary rather than loaded from the host system.
Because the unit of comparison is a syntax node rather than a line, the tool can report an addition and a removal on the same line, and it tracks the relationship between line numbers in the old and new file. That is the capability a line diff cannot express, and it is also why difftastic cannot emit a patch: the mapping it computes is not a set of line edits.
The parse step is also the failure surface. The README states that by default difftastic falls back to a line-oriented diff whenever parse errors are encountered, and calls this "a conservative choice to ensure that difftastic never claims that two syntactically different files are the same". Parse errors arise from language features the parser does not understand, from languages that rely on a preprocessor before parsing such as C++, or from genuine syntax mistakes in the file. That fallback is the honest design choice, but it means a broken file quietly gets the ordinary treatment.
Installing difftastic and running a first diff
The README does not carry install steps inline. It says: "For installation instructions, see Installation in the manual", pointing at difftastic.wilfred.me.uk/installation.html. The manual is the authoritative source for your platform, and there is a Chinese translation linked from the README. What the repository does tell you is the shape of the release: the Cargo manifest carries a binstall metadata section with a package URL template pointing at GitHub release downloads, and an override that uses a zip archive for x86_64-pc-windows-msvc, so prebuilt binaries are published per target. The crate itself is named difftastic on crates.io, and the installed binary is difft.
Once the binary is on your PATH, the basic invocation takes two files. The README's own examples use a C file:
difft foo1.c foo2.cWhat you should see is a side-by-side display in which changed syntax is highlighted in context rather than whole lines being marked. The README notes that this side-by-side display "usually works well, but can be confusing", so do not treat an odd-looking render as a bug on first contact.
If your files are large or the parser is struggling, the README gives an environment variable to allow a small number of parse errors before the fallback triggers:
export DFT_PARSE_ERROR_LIMIT=20
difft foo1.c foo2.cFor a repository-wide check rather than a visual diff, difftastic can compare ASTs without computing a diff, which the README says is much faster. This is the form to use in a script, because it communicates through the exit code:
difft --check-only --exit-code before.js after.jsThe README states the exit code is 0 when there are no syntactic changes and 1 when changes are found. That makes it usable as a gate, not just as a viewer.
Wiring difftastic into git, and what it will not do
Git integration is documented in the manual rather than the README, which links to difftastic.wilfred.me.uk/git.html for the configuration instructions and notes compatibility with mercurial as well. The FAQ confirms the pairing works and points Emacs users at a magit blog post and at difftastic.el. Outside that, the answer to integration is blunt: asked whether difftastic integrates with a favourite tool, the README replies "Probably not. Difftastic is young. Consider writing a plugin for your favourite tool, and I will link it in the README!"
Two behaviours are worth knowing before you put it in front of a team. First, difftastic always treats order as significant. The README says diffing set(1, 2) against set(2, 1) will show changes, and suggests sorting JSON keys before passing the files in:
difft <(jq --sort-keys < file_1.json) <(jq --sort-keys < file_2.json)If your data is genuinely unordered, a structural diff will report reordering as change, and there is no flag to tell it otherwise.
Second, merge conflict markers are understood as of version 0.50, released 2023-08-16. Passing a conflicted file as a single argument makes difftastic reconstruct the two conflicting versions and diff those:
difft file_with_conflicts.jsThat is inspection of a conflict, not resolution of one. AST merging is listed as a non-goal, and the README explains why: AST diffing is lossy from a text diff's point of view because insignificant whitespace is ignored, while merging needs whitespace tracked.
Known issues: performance, memory and display
The README's Known Issues section is unusually direct, and it should shape where you deploy this. Performance comes first: difftastic "scales relatively poorly on files with a large number of changes, and can use a lot of memory". The phrasing matters. This is not a claim that difftastic is slow on ordinary commits; it is a warning about the case where a large fraction of a file has changed. That is also the case where a structural diff has the least to offer, because there is little unchanged structure left to align.
Display is the second item. The side-by-side view "usually works well, but can be confusing". Anyone who has shown a syntax-aware diff to a colleague has met this: when the alignment is right, it is far clearer than a line diff, and when it is wrong, it is harder to read than the thing it replaced.
Robustness is the third, and it is the one to weigh most carefully. The README states that difftastic "regularly has releases that fix crashes". That is a statement about the project's own release history, and it is consistent with the release cadence visible in the repository: 0.69.0 on 2026-04-30, 0.70.0 on 2026-08-07, and 0.71.0 on 2026-09-18, with the Cargo manifest already at 0.72.0. Frequent releases are a sign of movement, not a guarantee of stability, and a crash in a diff tool is annoying rather than dangerous because it produces no output you would act on.
One more limitation is worth stating plainly because it is a design boundary rather than a bug. Difftastic is not a merge tool. If your workflow depends on automated three-way merging, this is the wrong tool, and no configuration will change that.
Difftastic against delta and other diff viewers
The most common comparison is with delta, a syntax-highlighting pager for git, diff and grep output. The difference in approach is the whole story. Delta takes the line diff git already produced and renders it better: colours, side-by-side layout, line numbers, navigation. It does not parse your source, so a reformatted block is still a large diff; it just looks nicer. Difftastic changes what is being compared. It parses both files and aligns syntax nodes, so a reformatted block can come out as no change at all. Delta will work on any text, including logs and prose. Difftastic needs a parser for the language, and falls back to a line-oriented diff with word highlighting when the extension is unrecognised.
That makes them complementary rather than competing. Delta improves the reading experience of an existing diff; difftastic changes the content of the diff. If your pain is that git output is hard to read, delta addresses it directly. If your pain is that a formatter run or a block move produces a diff nobody can review, delta cannot help, because the underlying line diff is still the input.
For AST merging specifically, the README names mergiraf as the tool that does merges based on a tree-sitter AST, and repeats that difftastic does not. That is the right pointer if merging is what you actually need. A structural diff and a structural merge sound adjacent but have different requirements, chiefly around whitespace tracking, and difftastic has chosen not to cross that line.
Maintenance, licensing and upgrade cost
The repository is not archived, and the last push was on 2026-09-18. The release history shows 0.69.0 on 2026-04-30, 0.70.0 on 2026-08-07 and 0.71.0 on 2026-09-18, so the project is being released on a roughly monthly to quarterly cadence, with the manifest version ahead of the last published release. None of that is a promise about the next six months.
The upgrade cost is dominated by the parser vendoring. Because tree-sitter parsers are compiled in, a release that bumps a parser can change how a language is parsed, which can change diff output for files you did not touch. If you gate anything on difft --check-only --exit-code, treat a difftastic upgrade as a change that can flip that exit code, and re-run it across the repository after upgrading rather than assuming stability. The Cargo manifest also sets rust-version = "1.85.0" with a stated goal of supporting at least 12 months of Rust versions and being conservative about upgrades to help packagers, so building from source has a defined floor.
On licensing: difftastic itself is MIT, and the LICENSE file is the reference. The README adds that the repository includes tree-sitter parsers by other authors under vendored_parsers/, and that these are "a mix of the MIT license and the Apache license", with per-parser terms in vendored_parsers/*/LICENSE. Sample files under sample_files/ are MIT unless stated otherwise. If you redistribute a binary, the vendored parsers are the part to check, since the licence is not uniform across them. That is a factual description of what the repository says, not legal advice.
Editorial conclusion
Adopt difftastic if you review code by eye and want reformatting noise out of the way, and you accept that it will not produce patches you can apply. Do not adopt it as a merge tool or as a patch generator; the README states both are non-goals. Before rolling it out, verify two things on your own files: how it behaves when the parser hits errors, since the default is a fallback to a line-oriented diff, and how it performs on the largest files in your repository, since the README lists scaling on files with many changes as a known issue. Start with difft --check-only --exit-code in a pre-commit hook, where the AST comparison runs without computing a diff at all.
Frequently asked questions
How does the difftastic diff algorithm work?
Difftastic parses both files with tree-sitter and treats the comparison as a graph problem, solved with Dijkstra's algorithm. The README points to a blog post and to an internals section of the manual for the full design.
How do I use difftastic?
Install it using the instructions in the manual at difftastic.wilfred.me.uk/installation.html, then run difft with two files, for example difft foo1.c foo2.c. To compare syntax without computing a diff, use difft --check-only --exit-code before.js after.js, which exits 0 when there are no syntactic changes and 1 when there are.
What is the difference between difftastic and git delta?
Delta renders the line diff git already produced, adding syntax highlighting and a side-by-side layout, while difftastic changes what is compared by parsing both files and aligning syntax nodes. Difftastic therefore ignores whitespace that is not syntactically significant, which a line-oriented viewer cannot do.
What is difftastic an alternative to?
The README compares difftastic with line-oriented tools such as diff, which it directs users to when they need an applicable patch, and names mergiraf as the tool to use for tree-sitter based AST merging, which difftastic does not do.
How does difftastic compare with a semantic diff?
Difftastic is itself a structural diff: it parses both files with tree-sitter and aligns syntax nodes, so whitespace that carries no syntactic meaning is ignored. The README notes it is not line-oriented, and that it falls back to a line-oriented diff with word highlighting for unrecognised file extensions.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/wilfred-difftastic)