Semantic, AST-aware diff tool for meaningful code comparisons.
difft old.ts new.ts
# Shows files side by side with syntax highlighting
difft --display=inline old.ts new.ts
# Traditional unified diff format
difft --display=side-by-side old.ts new.ts
# Explicit side-by-side
difft --skip-unchanged old.ts new.ts
# Only show files that changed
difft --context 5 old.ts new.ts
# Show 5 lines of context around changes
difft --language=python file1 file2
# Force specific language parser
# List supported languages
difft --list-languages
# Force color output (for piping)
difft --color=always old.ts new.ts | less -R
# Disable color
difft --color=never old.ts new.ts
# Set terminal width
difft --width 120 old.ts new.ts
# Tab width
difft --tab-width 4 old.ts new.ts
# One-time use
GIT_EXTERNAL_DIFF=difft git diff
GIT_EXTERNAL_DIFF=difft git show HEAD~1
GIT_EXTERNAL_DIFF=difft git log -p
# With options
GIT_EXTERNAL_DIFF="difft --display=inline" git diff
# Add to ~/.gitconfig
git config --global diff.external difft
# Or add to .gitconfig directly:
# [diff]
# external = difft
# Disable for specific command
git --no-ext-diff diff
# Configure
git config --global diff.tool difftastic
git config --global difftool.difftastic.cmd 'difft "$LOCAL" "$REMOTE"'
git config --global difftool.prompt false
# Use
git difftool HEAD~1
git difftool main feature-branch
# In repo's .git/config
git config diff.external difft
# Or in .gitattributes for specific files
*.rs diff=difftastic
# Compare directories
difft dir1/ dir2/
# Compare with options
difft --skip-unchanged dir1/ dir2/
| Scenario | Traditional | difft |
|---|---|---|
| Reformatted code | Shows all lines as changed | Shows only semantic changes |
| Moved function | Delete + Add | Recognizes as move |
| Renamed variable | Many line changes | Highlights just the rename |
| Added whitespace | Shows as change | Ignores (no semantic change) |
| Reordered imports | All imports changed | Shows specific additions/removals |
Traditional diff:
-function foo() { return 42; }
+function foo() {
+ return 42;
+}
difft:
(no changes - semantically identical)
Traditional diff:
-function helper() { ... }
function main() { ... }
+function helper() { ... }
difft:
function helper() { ... } → (moved to line 10)
difft parses actual ASTs for many languages:
# List all
difft --list-languages
# For large files, limit context
difft --context 3 large1.ts large2.ts
# Skip binary files
difft --skip-unchanged dir1/ dir2/
# Force text mode for unknown formats
difft --language=text file1 file2
# Pipe to pager
difft old.ts new.ts | less -R
# Save to file
difft --color=never old.ts new.ts > changes.txt
# Check if files differ (exit code)
difft old.ts new.ts > /dev/null
echo $? # 0 = same, 1 = different
# Review specific commit
GIT_EXTERNAL_DIFF=difft git show abc123
# Review PR changes
GIT_EXTERNAL_DIFF=difft git diff main...feature-branch
# Review staged changes
GIT_EXTERNAL_DIFF=difft git diff --cached
# Save original
cp module.ts module.ts.bak
# Refactor...
# Compare
difft module.ts.bak module.ts
# Full diff between branches
GIT_EXTERNAL_DIFF=difft git diff main feature-branch
# Specific file across branches
difft <(git show main:src/index.ts) <(git show feature:src/index.ts)
# Specific file between commits
difft <(git show HEAD~2:src/index.ts) <(git show HEAD:src/index.ts)
Create ~/.config/difft/config.toml:
# Display mode
display = "side-by-side"
# Context lines
context = 3
# Tab width
tab-width = 4
# Color theme (try different values)
color = "always"
GIT_EXTERNAL_DIFF=difft or configure globally--skip-unchanged for cleaner output--display=inline when sharing diffs--language=X when auto-detection fails