a structural diff that understands syntax 🟥🟩
 
 
 
 
 
Go to file
Wilfred Hughes d99ada1573 Preserve leading whitespace when constructing comment nodes
Display relies on accurate content values. If we transform comment
contents, we would colour the wrong part of the source code when
displaying. See before/after.java sample files.
2021-09-05 11:13:05 +07:00
.github/workflows Try llvm-cov for coverage 2021-09-04 16:00:22 +07:00
img Adding a logo 2021-09-04 16:39:08 +07:00
sample_files Add Java test file 2021-09-04 19:04:21 +07:00
src Preserve leading whitespace when constructing comment nodes 2021-09-05 11:13:05 +07:00
vendor Add Java support 2021-09-04 18:59:16 +07:00
.codecov.yml Configure codecov to ignore rust in sample files or vendored code 2021-09-04 15:56:10 +07:00
.gitignore Ignore local notes 2021-07-27 23:49:31 +07:00
CHANGELOG.md Preserve leading whitespace when constructing comment nodes 2021-09-05 11:13:05 +07:00
Cargo.lock Add debug logging and log stats on shortest path found 2021-09-04 10:46:51 +07:00
Cargo.toml Add debug logging and log stats on shortest path found 2021-09-04 10:46:51 +07:00
LICENSE Add LICENSE file 2021-07-04 11:41:39 +07:00
README.md crates.io badge should link to the relevant crates.io page 2021-09-05 10:44:10 +07:00
build.rs cargo fmt 2021-09-04 22:19:15 +07:00
text_diff_notes.md Add notes on LCS weaknesses 2021-03-21 13:34:01 +07:00

README.md

it's difftastic!
crates.io codecov.io

Difftastic is an experimental structured diff tool that compares files based on their syntax.

screenshot

Difftastic supports the following languages:

  • Clojure
  • CSS
  • Emacs Lisp
  • Go
  • Java
  • JavaScript (including JSX)
  • JSON
  • OCaml
  • Python
  • Rust

If a file has an unrecognised extension, difftastic uses a line-oriented diff.

How It Works

(1) Parsing.

Difftastic uses tree-sitter for parsing. The concrete syntax tree is then converted to a sequence of atoms or (possibly nested) lists.

The command line flags --dump-ts and --dump-syntax will display the syntax trees for a given file. Difftastic also has a simple regex-based parser which can be enabled with the environment DFT_RX=1.

(2) Diffing.

Difftastic treats diff calculations as a graph search problem. It finds the minimal diff using Dijkstra's algorithm.

This is based on the excellent Autochrome project.

(3) Printing.

Difftastic prints a side-by-side diff that fits the current terminal. It will try to align unchanged nodes (see screenshot above).

An experimental inline diff mode is available by setting the environment variable INLINE=1.

Known Issues

Robustness. Difftastic is young and each release has fixed several crashes.

Comprehensible display. Minimal diffs can be confusing: replacing one function with another may have a small amount of common punctuation even though they're completely unrelated. Alignment of slightly modified lines is a major challenge too.

Changes to long lines. Difftastic uses a side-by-side display by default, which is unhelpful if the only changes are near the end of a line.

Performance. Difftastic scales relatively poorly on files with a large number of changes, and can use a lot of memory. This might be solved by A* search.

Non-goals

Patch files. If you want to create a patch that you can later apply, use diff. Difftastic ignores whitespace, so its output is lossy. (AST patching is also a hard problem.)

Installation

You can install the latest tag of difftastic with Cargo:

$ cargo install difftastic

Difftastic is still under heavy development, so there's usually major bugfixes since the latest release. I currently recommend you check out the repository and compile directly:

$ cargo build --release

This will give you a binary at ./target/release/difftastic that you can put in a directory on your $PATH.

Adding a parser

Add the tree-sitter-FOO git repository as a subtree.

$ git subtree add --prefix=vendor/tree-sitter-elisp git@github.com:Wilfred/tree-sitter-elisp.git main

Add a symlink to the C source directory (Cargo will not include the parent directory when packaging, because the parent has a Cargo.toml).

$ cd vendor
$ ln -s tree-sitter-elisp/src tree-sitter-elisp-src

Update build.rs and tree_sitter_parser.rs to include the definitions for the new parser.

Git Usage

Once you've compiled difftastic and it's on $PATH, you can use it with git commands. To see the changes to the current git repo in difftastic, add the following to your .gitconfig and run git difftool.

[diff]
        tool = difftastic

[difftool]
        prompt = false

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

Alternatively, to run difftastic as the default diff engine for a git command:

$ GIT_EXTERNAL_DIFF=difftastic git diff
$ GIT_EXTERNAL_DIFF=difftastic git log -p --ext-diff

GIT_EXTERNAL_DIFF also supports paths to binaries, so you can use debug builds too. For example, using git show on a specific commit.

$ GIT_EXTERNAL_DIFF=/path/to/difftastic/target/debug/difftastic git show abcdef123456

License

Difftastic is open source under the MIT license, see LICENSE for more details.

Files in sample_files/ are also under the MIT license unless stated otherwise in their header.

Further Reading

The wiki includes a thorough overview of alternative diffing techniques and tools.