Skip to content

About

Structural AST Diff Engine using Tree-sitter

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

diffmantic

Stop Diffing Text, Start Diffing Logic.

CI Latest Release License: MIT Go Version


diffmantic inline diff demo


Note

Diffmantic is under active development and not yet ready for public use. We're getting closer to a public release, but features and internals are still shifting quickly.

Why diffmantic?

Line-based diffs like git diff break down when you refactor code. Move a function down 50 lines, and git shows it as a full Delete and re-add. Rename a parameter, and entire lines light up red and green.

Now with AI tools generating massive PRs with Moved functions and renamed symbols everywhere, the actual Change gets buried in noise. So human reviewers end up just giving in.

diffmantic fixes this by parsing your code into ASTs using Tree-sitter. It tracks structural shifts, so it knows when a function was Moved instead of deleted, and shows exact inline node edits instead of lighting up entire lines.

It works as a standalone CLI, a drop-in for git diff, or a backend for editor plugins via JSON output.

Features

  • Move Detection. When you move a function or a block, diffmantic tracks it as a Move. Not a delete + re-add. Moved functions, blocks, and statements are all first-class, with distinct colors for swapped or concurrent moves.
  • Update & Rename Detection. Shows exactly what changed inside a syntax node. A variable rename, a string literal swap, a type change, you see the precise edit, not a wall of red and green.
  • Side-by-Side & Inline Views. Fast side-by-side view by default, streaming directly through your pager. Uses full-width hybrid hunks for pure additions and deletions so you never get stuck in cramped columns. Also supports standard inline view (-f inline), line wrapping (--wrap), and git apply patches (-p).
  • Directory Diffing. Run diffm dir_a dir_b to diff entire directory trees recursively, streaming changed files through a single pager session.
  • Git Integration. Run diffm in any Git repo to stream a pager-backed diff of unstaged changes, staged changes with --cached, or any two revisions.
  • JSON Output. Stable schema with AST actions, line alignment, and character-level highlight spans. Includes selective --ui and --full modes for editor plugins and frontends.
  • 10 Core Languages. Go, Java, JavaScript, TypeScript, TSX, Python, Rust, Zig, C, C++, Lua. Full AST normalization and matching rules powered by Tree-sitter.
  • Line Diff Fallback. For unsupported file types or plain text files, Diffmantic automatically falls back to line-based diffing so you can diff any file.

Supported Languages

Language Extensions
Go .go
Java .java
JavaScript .js .jsx .mjs .cjs
TypeScript / TSX .ts .tsx .mts .cts
Python .py
Rust .rs
Zig .zig
C .c .h
C++ .cpp .cc .cxx .hpp .hh
Lua .lua

Note: Fully supported languages include tailored AST normalization (stripping punctuation noise and flattening comment/string blocks). Unrecognized languages fall back to line diffing.

Installation

Install Script (recommended)

curl -fsSL https://raw.githubusercontent.com/HarshK97/diffmantic/main/install.sh | sh

This installs the diffm binary to ~/.local/bin. Make sure it's in your $PATH.

It auto-detects your OS and architecture, grabs the right binary from GitHub Releases, and verifies the SHA256 checksum.

# Install to a specific directory
curl -fsSL https://raw.githubusercontent.com/HarshK97/diffmantic/main/install.sh | sh -s -- --dir=/usr/local/bin

# Install a specific version
curl -fsSL https://raw.githubusercontent.com/HarshK97/diffmantic/main/install.sh | sh -s -- --version=v0.11.0

Homebrew

brew install HarshK97/tap/diffmantic

Download Binary

Prebuilt binaries for Linux, macOS, and Windows (amd64 + arm64) are on the Releases page.

Build from Source

Requires Go 1.26+ and a C compiler for the native Tree-sitter bridge.

git clone https://github.com/HarshK97/diffmantic.git
cd diffmantic

# Build binary
make build

# Install binary and man pages (defaults to ~/.local)
make install

# Or install directly with Go:
go install github.com/HarshK97/diffmantic/cmd/diffm@latest

Manual Pages

Full documentation is available via Unix manual pages:

man diffm
# or
man diffmantic

Usage

Git Status Mode (default in a repo)

# Show unstaged changes in any Git repository
diffm

# Show only staged changes
diffm --cached

Git Revision Diffing

# Diff working tree against HEAD
diffm HEAD

# Diff between two commits, tags, or branches
diffm HEAD~1 HEAD
diffm main...feature-branch

Directory-to-Directory Diff

# Diff two directories recursively with pager
diffm dir-a/ dir-b/

File-to-File Diff

# Side-by-side diff with pager (default when a terminal is attached)
diffm before.go after.go

# Inline diff with pager
diffm before.go after.go -f inline

# Wrap long lines to terminal width in inline mode
diffm before.go after.go -f inline --wrap

# Force strict 50/50 side-by-side columns (disables hybrid full-width expansion)
diffm before.go after.go --force-sbs

# Standard patch suitable for git apply
diffm before.go after.go -p

# JSON output for editor plugins and automation
diffm before.go after.go -f json

# Fast UI mode (line alignment and highlight spans without action tree)
diffm before.go after.go -f json --ui

# Full envelope (actions, line alignment, and highlight spans)
diffm before.go after.go -f json --full

# Human-readable action list
diffm before.go after.go -f actions

Environment Variables

diffmantic can be configured via environment variables. Command-line flags always take precedence over environment variables.

Variable Default Description
DIFFM_FORMAT side-by-side Default diff output format (side-by-side, inline, json, actions)
DIFFM_TAB_WIDTH 4 Number of spaces per tab stop
DIFFM_IGNORE_COMMENTS 0 Ignore comments during AST diffing (1, true, yes)
DIFFM_PARSE_ERROR_LIMIT 0 Maximum parse errors permitted before falling back to line diffing
DIFFM_SIZE_LIMIT 1024 Maximum file size in KB for AST parsing before fallback (0 to disable)
DIFFM_LINE_LIMIT 10000 Maximum line count for AST parsing before fallback (0 to disable)
DIFFM_NO_PAGER Unset If set to any non-empty value, disables the interactive terminal pager

How It Works

diffmantic matches ASTs in four phases, combining the GumTree algorithm, Zhang-Shasha tree edit distance, and Chawathe edit script generation:

  1. Top-Down Matching. We look for identical subtrees by height. When we find an exact match, all nodes in the subtree get mapped together.
  2. Bottom-Up Matching. For unmatched nodes, we look for counterparts of the same type that share already-matched children. If the Dice similarity score is high enough, we match them.
  3. Recovery. Inside matched containers, we run LCS alignment on unmatched children (first by label, then by structural shape). For small subtrees, Zhang-Shasha (1989) tree edit distance is used as a precise fallback.
  4. Action Generation & Post-Processing. We produce a raw edit script (insert, delete, update, move) using Chawathe et al. (1996) edit script generation and then refine it. Child edits get collapsed into clean subtree operations, comment changes get normalized, and related moves get grouped.

Editor Integrations

Neovim

diffmantic.nvim is the Neovim plugin that started this whole project. It currently ships with its own embedded alpha engine and is being migrated to use the diffm CLI as its backend via JSON output.

VS Code

Planned. The JSON output is built to support editor integration, so if you want to build one, the plumbing is there.

JSON Schema

diffm -f json outputs a stable v1 schema with child-index paths. Take a look at diffm file-a file-b -f json to see the full structure.

License

MIT. See LICENSE.

Acknowledgements

The engine is based on foundational research in AST differencing, tree edit distance, and edit script generation:

About

Structural AST Diff Engine using Tree-sitter

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages