A fast, interactive file comparison tool.
Compare directories, archives, binaries, plists, text files, and git commits with a terminal UI or structured JSON output.
Download a pre-built binary for your platform from a release.
Use go to install a version:
go install github.com/block/drift/cmd/drift@latest # or @vX.X.XOr build from source:
gh repo clone block/drift cd drift go run ./cmd/drift --help# Compare two directories drift MyApp-v1.0 MyApp-v2.0 # Compare two archives (.ipa, .apk, .aar, .jar, .tar.gz, .tar.bz2) drift MyApp-v1.0.ipa MyApp-v2.0.ipa # Compare two binaries drift MyApp-v1.0/MyApp.app/MyApp MyApp-v2.0/MyApp.app/MyApp # Force a specific comparison mode drift -m binary MyApp-v1.0/MyApp.app/libcore.dylib MyApp-v2.0/MyApp.app/libcore.dylib # JSON output (non-interactive, for scripting) drift --json MyApp-v1.0 MyApp-v2.0drift can compare git commits, branches, and working tree changes - bringing its interactive TUI to your git workflow.
# View uncommitted changes (staged + unstaged + untracked) drift # Compare a ref against HEAD drift HEAD~3 drift main # Compare any two refs (commits, branches, tags) drift main feature-branch drift v1.0.0 v2.0.0 # Target a different repo with -C drift -C ~/projects/my-app HEAD~1 HEAD # Force git mode when a ref collides with a file path drift --git main featureWhen comparing git refs, the root node in the tree shows commit metadata including SHA, author, date, and links to the commit and pull request on GitHub.
Auto-detection resolves arguments as git refs when they don't match filesystem paths. Use --git to skip path detection entirely.
drift ships a skill that gives AI coding agents native access to structured file comparison via drift --json. The skill is included in every GitHub release.
Install with a single command:
Claude Code
mkdir -p ~/.claude/skills/drift && gh release download --repo block/drift --pattern 'skill.tar.gz' --output - | tar -xz -C ~/.claude/skills/driftCodex / Amp
mkdir -p ~/.agents/skills/drift && gh release download --repo block/drift --pattern 'skill.tar.gz' --output - | tar -xz -C ~/.agents/skills/driftAgent skill demo
drift auto-detects the comparison mode based on the inputs:
| Mode | Inputs | What it shows |
|---|---|---|
| git | Commits, branches, tags | File tree of changes between refs with commit metadata and per-file diffs |
| tree | Directories, archives | File tree with added/removed/modified indicators, per-file diffs |
| binary | Mach-O binaries | Sections, sizes, symbols, load commands. Requires nm and size |
| plist | Property lists (.plist) | Structured key-value diff. Binary plists require plutil |
| text | Everything else | Line-by-line unified diff |
Use -m <mode> to override auto-detection.
drift transparently extracts and compares the contents of:
.ipa(iOS app bundles).apk(Android app bundles).aar(Android libraries).jar(Java archives).tar,.tar.gz/.tgz,.tar.bz2
When stdout is a terminal, drift launches an interactive Bubbletea-based TUI with a split-pane layout: file tree on the left, detail diff on the right.
| Key | Action |
|---|---|
↑/k, ↓/j | Navigate tree |
→/enter/l | Expand node |
←/h | Collapse node |
tab | Switch pane focus |
n/N | Next/previous change |
f | Cycle filter (all → added → removed → modified) |
1-4 | Filter: all, added, removed, modified |
/ | Search (fuzzy match in tree, text search in detail) |
s | Swap A ↔ B |
c | Copy detail to clipboard |
pgup/pgdn | Scroll detail pane |
g/G | Jump to top/bottom |
? | Toggle full help |
q/ctrl+c | Quit |
Pass --json to get structured, machine-readable JSON output - ideal for CI pipelines, automation scripts, and AI-powered analysis.
drift --json MyApp-v1.0 MyApp-v2.0 drift --json MyApp-v1.0/MyApp.app/libcore.dylib MyApp-v2.0/MyApp.app/libcore.dylib drift --json MyApp-v1.0.ipa MyApp-v2.0.ipa | jq '.summary'Every JSON result includes:
| Field | Description |
|---|---|
path_a, path_b | The compared paths |
mode | Detected comparison mode (git, tree, binary, plist, text) |
root | The diff tree - each node has name, path, status, kind, size_a, size_b, and optional children |
summary | Aggregate counts: added, removed, modified, unchanged, size_delta |
Node status is one of: unchanged, added, removed, modified.
For single-file modes (binary, plist, text), a detail field is automatically included with mode-specific data:
- binary -
symbols(added/removed symbol names) andsections(segment/section size changes) - plist -
changeswithkey_path,status, and before/after values - text -
hunkswith line-level diffs (kind:context,added,removed)
Detect new files added between two builds:
drift --json MyApp-v1.0 MyApp-v2.0 | jq '[.root | .. | select(.status? == "added") | .path]'Get the total size delta:
drift --json MyApp-v1.0 MyApp-v2.0 | jq '.summary.size_delta'List changed symbols in a binary:
drift --json MyApp-v1.0/MyApp.app/libcore.dylib MyApp-v2.0/MyApp.app/libcore.dylib \ | jq '.detail.binary.symbols[] | select(.status == "added") | .name'Feed a comparison to an LLM for analysis:
drift --json MyApp-v1.0.ipa MyApp-v2.0.ipa | llm "Summarize what changed between these two builds"drift works on macOS, Linux, and Windows. Core features (directory/archive comparison, text diffing) work everywhere. Some features require external tools and degrade gracefully when they are unavailable:
| Tool | Used for | Availability |
|---|---|---|
git | Git mode (commit/branch/worktree comparison) | All platforms |
nm, size | Mach-O binary analysis | macOS (Xcode CLI Tools), Linux (binutils) |
plutil | Binary plist conversion | macOS only (XML plists work everywhere) |
xclip or xsel | Clipboard | Linux only (macOS and Windows work natively) |
