a structural diff that understands syntax 🟥🟩
 
 
 
 
 
Go to file
Wilfred Hughes 2d845d9026 Simplify screenshot to exclude window chrome 2021-09-06 00:15:50 +07:00
.github/workflows Try llvm-cov for coverage 2021-09-04 16:00:22 +07:00
img Simplify screenshot to exclude window chrome 2021-09-06 00:15:50 +07:00
sample_files Treat integer values as atoms in CSS 2021-09-05 11:39:49 +07:00
src Associate more file extensions with Clojure 2021-09-05 19:43:47 +07:00
vendor Configure typescript TSX 2021-09-05 19:35:23 +07:00
.codecov.yml Treat codecov as informational and never mark the status as red 2021-09-05 20:18:53 +07:00
.gitignore Ignore local notes 2021-07-27 23:49:31 +07:00
CHANGELOG.md Associate more file extensions with Clojure 2021-09-05 19:43:47 +07:00
Cargo.lock Roll version 2021-09-05 11:23:00 +07:00
Cargo.toml Roll version 2021-09-05 11:23:00 +07:00
LICENSE Add LICENSE file 2021-07-04 11:41:39 +07:00
README.md TSX is now supported 2021-09-05 19:37:28 +07:00
build.rs Configure typescript TSX 2021-09-05 19:35:23 +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 (and JSX)
  • JSON
  • OCaml
  • Python
  • Rust
  • TypeScript (and TSX)

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.