Salta ai contenuti

difftastic

difftastic (comando: difft) è uno strumento di diff strutturale che confronta i file in base al loro albero sintattico anziché riga per riga. Scritto in Rust, comprende la grammatica di oltre 50 linguaggi di programmazione per mostrare modifiche significative come codice spostato, variabili rinominate e blocchi riformattati.

Installazione

# Using cargo (Rust)
cargo install difftastic

# macOS
brew install difftastic

# Ubuntu/Debian
sudo apt-get install difftastic
# Or download from releases
curl -sSfL https://github.com/Wilfred/difftastic/releases/latest/download/difft-x86_64-unknown-linux-gnu.tar.gz | \
    tar xz -C /usr/local/bin/

# Arch Linux
sudo pacman -S difftastic

# Nix
nix-env -i difftastic

# Verify installation
difft --version

Utilizzo base

# Compare two files
difft old_file.py new_file.py

# Compare two directories
difft old_dir/ new_dir/

# Read from stdin (use - for stdin)
echo "fn main() {}" | difft - new_file.rs

Git Integration

Configure as Git Difftool

# Set difftastic as the default git difftool
git config --global diff.tool difftastic
git config --global difftool.difftastic.cmd 'difft "$LOCAL" "$REMOTE"'
git config --global difftool.prompt false

# Use with git difftool
git difftool          # Compare working tree vs staged
git difftool HEAD     # Compare working tree vs HEAD
git difftool HEAD~1   # Compare HEAD vs HEAD~1
git difftool main..feature  # Compare branches

Configure as Default Git Diff

# Set difftastic as the default diff driver via GIT_EXTERNAL_DIFF
# Add to your shell profile (~/.bashrc, ~/.zshrc):
export GIT_EXTERNAL_DIFF=difft

# Now all git diff commands use difftastic
git diff
git diff HEAD~1
git diff main..feature
git log -p  # Show patches with difftastic
git show    # Show commit with difftastic

.gitconfig Setup

# Add to ~/.gitconfig

[diff]
    tool = difftastic

[difftool]
    prompt = false

[difftool "difftastic"]
    cmd = difft "$LOCAL" "$REMOTE"

# Optional: create alias for convenience
[alias]
    dft = difftool
    dlog = "!f() { GIT_EXTERNAL_DIFF=difft git log -p --ext-diff $@; }; f"
    dshow = "!f() { GIT_EXTERNAL_DIFF=difft git show --ext-diff $@; }; f"
# Use the aliases
git dft                  # Structural diff
git dlog                 # Log with structural diffs
git dlog -5              # Last 5 commits with structural diffs
git dshow HEAD           # Show latest commit with structural diff
git dshow abc123         # Show specific commit

Supported Languages

difftastic supports 50+ languages including:

Bash          C             C++           C#
Clojure       CMake         CSS           Dart
Elixir        Elm           Erlang        Go
Haskell       HCL           HTML          Java
JavaScript    JSON          Julia         Kotlin
LaTeX         Lua           Make          Nix
OCaml         Perl          PHP           Python
R             Ruby          Rust          Scala
SQL           Swift         TOML          TypeScript
YAML          Zig           and more...

# Check if a language is supported
difft --list-languages

# difftastic auto-detects language from file extension
# Falls back to text-based diff for unsupported languages

Display Modes

Side-by-Side (Default)

# Side-by-side display (default)
difft --display side-by-side old.py new.py

# Or equivalently
difft old.py new.py

Inline

# Inline display (shows changes in sequence, like unified diff)
difft --display inline old.py new.py

# Useful for narrow terminals or piping
difft --display inline old.py new.py | less

Side-by-Side Only

# Side-by-side with no syntax highlighting for unchanged parts
difft --display side-by-side-show-both old.py new.py

Color Output

# Color output (enabled by default for terminals)
difft old.py new.py

# Force color output (for piping)
difft --color always old.py new.py

# Disable color
difft --color never old.py new.py

# Color when piped to less
difft --color always old.py new.py | less -R

Width Control

# Set display width (default: terminal width)
difft --width 120 old.py new.py

# Narrow display for small terminals
difft --width 80 old.py new.py

# Wide display for large monitors
difft --width 200 old.py new.py

# Tab width (default: 4)
difft --tab-width 2 old.py new.py

Context Lines

# Set number of context lines around changes (default: 3)
difft --context 5 old.py new.py

# Minimal context
difft --context 0 old.py new.py

# More context
difft --context 10 old.py new.py

Language Override

# Force a specific language parser
difft --language python old.txt new.txt

# Useful when file extension doesn't match content
difft --language javascript config.txt config_new.txt

# Use text-based diff (disable structural parsing)
difft --language text old.py new.py

Handling Large Diffs

# Set byte limit for files (skip very large files)
difft --byte-limit 1000000 old_file new_file

# Set graph limit (controls structural diff accuracy vs speed)
difft --graph-limit 500000 old_file new_file

# For very large files, fall back to text diff
difft --language text large_old.json large_new.json

Practical Examples

# Compare before/after refactoring
difft src/old_module.py src/new_module.py

# Review a specific commit structurally
GIT_EXTERNAL_DIFF=difft git show abc123

# Compare branches
GIT_EXTERNAL_DIFF=difft git diff main..feature -- "*.py"

# Compare config file changes
difft /etc/nginx/nginx.conf /etc/nginx/nginx.conf.new

# Check formatting-only changes (structural diff ignores whitespace)
# Reformat a file and diff - only real changes shown
black old.py  # Format with black
difft old.py.bak old.py  # Shows no diff if only formatting changed

# Compare JSON structures
difft config_v1.json config_v2.json

Why Structural Diff

Benefits over traditional line-based diff:

1. Ignores formatting changes
   - Reformatted code shows no diff if logic unchanged
   - Whitespace-only changes are hidden

2. Understands code structure
   - Moved functions are shown as moves, not delete+add
   - Renamed variables shown inline

3. Better noise reduction
   - Comment changes don't obscure code changes
   - Import reordering shown cleanly

4. Language-aware matching
   - Matches corresponding brackets, blocks, functions
   - Understands string literals vs code

Limitations:
- Slower than line-based diff for very large files
- Some languages may not be fully supported
- Graph limit may cause fallback to text diff

Environment Variables

# Set default display mode
export DFT_DISPLAY=inline

# Set default width
export DFT_WIDTH=120

# Set default color mode
export DFT_COLOR=always

# Set default context lines
export DFT_CONTEXT=5

# Set default tab width
export DFT_TAB_WIDTH=4

# Set byte limit
export DFT_BYTE_LIMIT=1000000

# Set graph limit
export DFT_GRAPH_LIMIT=500000