difftastic 0.71.0 compares files by syntax tree instead of by line. The Windows build is a single 116351488-byte difft.exe inside a 10446790-byte zip.
Line-based diff answers "which lines changed". On a real edit that is often the wrong question, because moving one call inside an if block rewrites four lines while changing one thing.
I compared two Python files that differ in six places, then the same pair padded to 4000 copies each, and timed both against git diff --no-index on the small pair.
One 116351488-byte exe
The release zip holds a single executable. It contains the tree-sitter grammars for every supported language, which is why a diff tool is 116 MB.
| Item | Value | Note |
|---|---|---|
| Release zip | 10446790 bytes | difft-0.71.0-x86_64-pc-windows-msvc.zip |
| difft.exe | 116351488 bytes | Standalone, no runtime |
| Version output | 94 bytes | One line naming version and target |
| Config written | None | Environment variables only |
The 116 MB is grammars, not UI
There is no interface to draw and no editor to embed. The weight is the parser set: one compiled grammar per language, and every one has to be present for the tool to recognise a file by extension.
It reads files or directories
Pass two paths and it compares them. Pass two directories and it walks both, which is the same shape as git diff --no-index.
Start-up costs 191 ms
Three runs of difft --version took a median of 191 milliseconds, and the output line is 94 bytes.
| Command | Median | Output |
|---|---|---|
| difft --version | 191 ms | 94 bytes |
| difft a.py b.py (447 vs 519 bytes) | 518 ms | 722 bytes |
| git diff --no-index a.py b.py | 177 ms | 751 bytes |
191 ms is the floor for any diff
Because a version check and a two-file comparison share the same load path, no comparison can finish faster than this. A small file pair at 518 ms spends 327 ms on parsing and tree comparison.
Three times slower than git on the same pair
518 ms against 177 ms on a 447-byte file is 2.9 times. The extra time buys grammar parsing: git only has to look at lines, difft has to build two syntax trees and match their nodes.
Big input takes 5 seconds
Concatenating the same pair 4000 times gives two files of 1788000 bytes with the same six differences repeated throughout. Comparing those took 5001 ms and produced 6910985 bytes of output.
| Input | Time | Output |
|---|---|---|
| 447 vs 519 bytes | 518 ms | 722 bytes |
| 1788000 vs 1788000 bytes | 5001 ms | 6910985 bytes |
| Growth factor | 9.7 times slower | 9572 times more output |
Output grows faster than input
1788000 bytes in produced 6910985 bytes out, a ratio of 3.9. Every repeated difference is drawn in full, so a file made of the same block 4000 times is the worst case for any diff tool.
Five seconds is over the interactive limit
Above roughly one second a diff stops feeling instant. For files in the megabyte range, line-based tools answer in a fraction of that because they never build a tree.
What the output actually shows
The screenshot is one comparison of the small pair. It is split into two hunks, marked 1/2 and 2/2, and each hunk shows only the region around a change.
| Element | Meaning |
|---|---|
| 1/2 and 2/2 headings | Two separate change regions in one file |
| Red text on left | Removed tokens, e.g. 5000 |
| Green text on right | Added tokens, e.g. 10000 |
| Line numbers on both sides | Original and new position kept aligned |
| Coloured keywords and strings | Syntax highlighting inside the diff |

Six changes became two hunks
The six edits land in two clusters: three near the top around the constant and the function signature, three near the bottom around the tuple and the print. difft groups them by proximity instead of showing six separate blocks.
The tuple line shows the difference inline
One line in the second hunk has four unchanged city names and one added one, coloured separately inside the same line. Line-based diff would show that as one deleted line and one added line, doubling the reading.
Flags worth knowing
The tool takes two positional paths and a handful of environment variables. There is no config file to edit.
| Setting | Effect |
|---|---|
| --color=never | Plain text output, no escape codes |
| DFT_PAGER | Overrides the pager, set to cat to disable paging |
| --paging | Not a flag in 0.71.0, the parser rejects it |
> set DFT_PAGER=cat
> difft a.py b.py
b.py --- 1/2 --- Python
1 LIMIT = 5000 1 LIMIT = 10000
--paging is rejected
Passing --paging=never returns "unexpected argument" and suggests quoting it after --. The pager is controlled by the DFT_PAGER environment variable instead.
Plain output for piping
--color=never is what the timing runs used, because escape codes would inflate the measured output. For reading, the colour is the point.
130 lines of languages
Running difft --list-languages prints 130 lines: each language takes a name line plus one or two lines of file extensions, so the list covers roughly 45 languages.
| Entry | Extensions |
|---|---|
| Ada | *.ada *.adb *.ads |
| Apex | *.apexc *.trigger |
| Assembly | several per dialect |
Recognition is by extension
A file with an extension in that list gets tree matching; anything else falls back to line diff with no warning. Renaming a script to .txt silently downgrades the comparison.
The grammars are why the exe is 116 MB
Every entry in that list is a compiled grammar linked into difft.exe. Forty-five parsers plus the matching engine is the whole 116351488 bytes.
Where it earns a place
Reviewing a refactor where code moved without changing behaviour, in a language the tool has a grammar for.
| Task | Fit | Why |
|---|---|---|
| Reviewing moved code | High | Tree matching ignores pure reordering |
| Spotting one character inside a line | High | Inline token colour instead of whole-line swap |
| Files above 1 MB | Low | 5001 ms and 6.9 MB of output on 1.8 MB input |
| Unknown file types | Low | Falls back to line mode without a grammar |
Pair it with git, do not replace it
git answers in 177 ms and is always available. difft costs 518 ms on the same pair but shows which token inside the line moved. Use git for speed and difft when a diff is hard to read.
Licence and cost
MIT. Free for personal and commercial use, source on GitHub, written in Rust. The Windows build is one exe with no installer and no registry writes.