Difftastic — Structural Diff Tool for Smarter Code Review
In this tutorial, you'll learn about Difftastic. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Difftastic is a structural diff tool that understands syntax — comparing code at the AST level rather than line-by-line — producing diffs that show actual changes inside functions and expressions without noise from whitespace or reformatting.
What You'll Learn
How Difftastic parses code into ASTs and diffs at the expression level, integrates with Git as an external diff tool, handles moved and reformatted code, compares JSON and other structured data, and works with 30+ programming languages.
Why Difftastic Matters
Traditional line-based diffs (git diff) break when code is reformatted, when whitespace changes, or when functions are moved. A reformatted function shows as entirely deleted and re-added. Difftastic parses before/after into abstract syntax trees and compares tree nodes. A reformatted function shows zero changes. A moved block shows as moved, not deleted-and-added. This makes code review faster and more accurate. Doda Browser uses Difftastic for all Pull Request reviews — developers see semantic changes, not formatting noise.
Learning Path
flowchart LR A[hyperfine] --> B[Difftastic
You are here] B --> C[procs & bottom] B --> D[bat & delta] style B fill:#f90,color:#fff
Installation
# macOS
brew install difftastic
# Ubuntu/Debian
sudo apt install difftastic
# Fedora
sudo dnf install difftastic
# Arch
sudo pacman -S difftastic
# Cargo
cargo install difftastic
# Download binary
curl -LO https://github.com/Wilfred/difftastic/releases/download/0.57.0/difft-x86_64-unknown-linux-gnu.tar.gz
tar xzf difft-x86_64-unknown-linux-gnu.tar.gz
sudo install difft /usr/local/bin/
Basic Usage
# Compare two files
difft old.py new.py
# Compare two directories
difft src/ src-modified/
# Diff against stdin
echo '{"a":1}' | difft - --language json <(echo '{"a":2}')
Expected output for a simple change:
# old.py
def greet(name):
return f"Hello, {name}!"
# new.py
def greet(name, title=""):
return f"Hello, {title} {name}!".strip()
1 1 def greet(
2 - name
2 + name,
3 + title="",
4 ):
3 5 return (
4 - f"Hello, {name}!"
6 + f"Hello, {title} {name}!".strip()
5 7 )
Git Integration
As an External Diff Tool
# Configure Git to use difftastic
git config --global diff.external difft
# Or use it only when invoked explicitly
git config --global alias.dft '!difft'
# Then:
git dft # Show unstaged changes
git dft --staged # Show staged changes
git dft HEAD~1 HEAD # Compare two commits
As a Git Pager (Recommended)
# Configure difftastic as the diff pager
git config --global pager.difftool true
git config --global diff.tool difftastic
git config --global difftool.difftastic.cmd 'difft "$LOCAL" "$REMOTE"'
Language Support
Difftastic supports 30+ languages with tree-sitter grammars:
# Check if a language is supported
difft --check-language file.rs
# Manually specify language
difft --language python old.py new.py
difft --language json data-old.json data-new.json
# List all supported languages
difft --list-languages
Supported languages include:
- Python, JavaScript, TypeScript, JSX, TSX
- Rust, Go, Java, Kotlin
- C, C++, C#, Objective-C
- Ruby, PHP, Perl
- Swift, Scala, Haskell, Elm
- JSON, YAML, TOML, XML
- CSS, SCSS, HTML
- Markdown, LaTeX
Understanding Structural Diffs
Reformatted Code Shows Zero Changes
// Old (one line)
const items = [1, 2, 3, 4, 5, 6, 7, 8];
// New (formatted across lines)
const items = [
1, 2, 3, 4,
5, 6, 7, 8,
];
Difftastic shows: no changes. The AST is identical. Traditional diff shows all 8 lines as changed.
Renamed Functions
# Old
def process_data(input):
result = transform(input)
return validate(result)
# New (function renamed)
def process_and_validate(input):
result = transform(input)
return validate(result)
Difftastic shows only the function name as changed, not the entire body.
Whitespace-Only Changes
# Old
x = 1
y = 2
# New (indentation change)
x = 1
y = 2
Show as no changes (AST is identical).
Code Movement
When code is moved within a file, Difftastic shows it as moved rather than as a delete + add:
1 1 def first():
2 2 return "first"
3 +
4 - def second():
5 - return "second"
4 + def third():
5 + return "third"
3 6
7 - def third():
8 - return "third"
7 + def second():
8 + return "second"
Advanced Features
Display Modes
# Side-by-side display (default on wide terminals)
difft --display side-by-side old.py new.py
# Inline display
difft --display inline old.py new.py
Context Control
# Show more context around changes
difft --context 10 old.py new.py
# Show only changed lines
difft --context 0 old.py new.py
File Filtering
# Only compare specific file types
difft --extension py src/ src-modified/
# Exclude files matching a pattern
difft --exclude 'node_modules' src/ src-modified/
# Compare only specific files
difft old/src/main.py new/src/main.py
Tab Width
# Set tab width for display
difft --tab-width 4 old.py new.py
Integration with delta
delta and Difftastic serve different purposes — use them together:
# delta for Git diff formatting (line-based)
# Difftastic for structural diffs (AST-based)
# Configure both:
git config --global core.pager delta
git config --global diff.external difft
# Use delta for git log, difftastic for structural comparisons
git log -p # delta-formatted
difft HEAD~1 HEAD # AST-aware diff
Real-World Use Cases
Code Review Before Commit
# See what you actually changed (ignoring formatting)
difft --staged
# Compare your branch with main
difft main...HEAD
Detecting Unintended Changes
# After a large refactor, verify no logic changed
difft refactored/ original/
# Compare generated files
difft --language json api-spec-v1.json api-spec-v2.json
Merge Conflict Resolution
# Compare merge conflict sides
difft file.conflict.orig file.conflict.local
difft file.conflict.orig file.conflict.remote
Common Errors
1. "Unsupported language" Error
Difftastic does not have a tree-sitter grammar for this file type. Specify the language manually: difft --language python file.xyz. Add support by contributing a grammar.
2. Binary Files Show No Diff
Difftastic only works with text files. Binary files (images, PDFs) are skipped with a note. Use --force to attempt comparison anyway.
3. Large Files Are Slow
Parsing and diffing ASTs is computationally expensive for large files. Files over ~10,000 lines may be slow. Consider narrowing the comparison scope.
4. Difftastic Shows No Changes for Obvious Differences
The files may have the same AST but different formatting. Difftastic considers reformatted identical code as no change. Use --display inline to verify.
5. Git Integration Shows "external diff died" Error
The difft binary is not in Git's PATH. Use the full path in config: git config --global diff.external /usr/local/bin/difft.
6. Difftastic Output Too Wide for Terminal
Use --display inline for narrow terminals, or set --width 80 to cap the output width.
7. Moved Code Not Detected
Code movement detection works best with contiguous blocks. Interleaved changes may not be recognized as moves.
Practice Questions
1. How does Difftastic differ from traditional line-based diff tools? Difftastic parses code into ASTs and compares tree nodes. Traditional tools compare lines as text. This means Difftastic ignores formatting, whitespace, and code movement that would appear as changes in line-based diffs.
2. How do you configure Difftastic as Git's external diff tool?
git config --global diff.external difft — then all git diff commands use Difftastic.
3. What happens when you compare reformatted code with Difftastic? If the AST is identical, Difftastic shows no changes. The reformatting is invisible.
4. Which programming languages does Difftastic support?
30+ languages including Python, JavaScript, TypeScript, Rust, Go, Java, C++, Ruby, and JSON. Run difft --list-languages for the full list.
5. How do you compare JSON files structurally with Difftastic?
difft --language json old.json new.json — it ignores key ordering and shows structural differences in JSON values.
Challenge: Take a real project and intentionally: (1) reformat a file (change indentation, break long lines), (2) rename a function, (3) move a function to a different position in the file, (4) make an actual logic change. Run Difftastic on each change and observe which are detected as changes and which are not. Compare with git diff. Then set up Difftastic as your Git diff tool and use it for a week of code review.
What's Next
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-24.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro