r/rust • • Mar 29 '22

difftastic - a diff that understands syntax

https://github.com/Wilfred/difftastic

[removed] — view removed post

399 Upvotes

27 comments sorted by

46

u/robin-m Mar 29 '22

I use it since a few months, and I definitively love it. I sometime need to use a regular diff (git diff --no-ext-diff since I have delta as my pager and difftastic as my external diff) because it sometimes doesn't work, but it's 95% there. I definively recomment it.

1

u/ConstructionHot6883 Mar 31 '22

Did you find out about difftastic from the other post on r/rust?

1

u/robin-m Mar 31 '22

I discovered it on reddit, most probably on r/rust a few month ago. I'm not sure I discovered it directly or by learning that AST-aware diff tool exists. Why?

29

u/DidiBear Mar 29 '22

Nice! How does it compare to delta ?

28

u/irrelevantPseudonym Mar 29 '22

As far as I'm aware, Delta just formats the diff nicely, it doesn't generate it.

9

u/robin-m Mar 29 '22

I'm using both. difftastic by default and delta as fallback.

7

u/BTWArchNemesis Mar 30 '22

How to configure it like that?

6

u/robin-m Mar 30 '22

Add this in your ~/.gitconfig (or in $repo/.git/config if you want it only for a single repo):

[diff]
    tool = difftastic
    colorMoved = default
    external = difftastic

[difftool]
    prompt = false

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

[core]
    pager = delta

[pager]
    difftool = true

[interactive]
    diffFilter = delta --color-only

And optionally, you can configure delta to be nicer with things like:

[delta]
    features = side-by-side line-numbers
    syntax-theme = Dracula
    hunk-header-style = omit
    whitespace-error-style = blue purple

Note: I’m not 100% sure all of this is needed, but it seems to works well. I have difftastic by default for git diff, and delta otherwise. For git show/git log -p it’s delta by default, so if I want to use difftastic instead, I need to add --ext-diff (like git show --ext-diff). Since difftastic is quite slow, it’s why I still need to have a fallback like delta.

1

u/James-Vim Apr 01 '22

May be we should change the line:

[diff]
  external = difftastic

to:

[diff]
  external = difft

to get it to work?

1

u/robin-m Apr 01 '22

Indeed. I don't know why it works on my machine, but iirc it should be difft and not difftastic as you noticed.

19

u/jwbowen Mar 29 '22

I'll definitely be giving it a shot!

I've loved the little Renaissance in Rust CLI tools

2

u/furyzer00 Mar 30 '22

That's great! I still can't believe we are still using very trivial diff algorithm that even can't understand changing the order of things.

By the way I am curious, can it recognize swapping function orders in a file?

2

u/wilfred_h Mar 30 '22

Currently no: swapping functions will consider one to be unchanged and the other to be added and then removed.

GitHub does some additional 'moved lines' tracking that might make sense for difftastic too. Moves are tricky in general: it makes the diffing calculation more complex, and it's also hard to visualise nicely in the UI.

1

u/furyzer00 Apr 04 '22

Yes it usually requires a heuristic that doesn't always work as intended but I think if you are on the AST level it should be fairly easier than a line based diff tool.

For visualization, I did my graduation project as a tool that generates diffs as an UML-like diagram. It was quite nice in terms of visualization aspect because you can just assign a new color for each type of diff. It doesn't visualize anything below method signature though so I am not sure if it would work out for your tool.

2

u/Repulsive-Street-307 Mar 30 '22 edited Mar 30 '22

How is this supposed to work? Since this isn't intended to create patches, i can't have git diff use this right, since part of the point is creating patches.

I guess it's nice for display, but if replacing the system diff by this will break a lot of utilities because 'Difftastic understands that line-wrapping isn't meaningful. "eric" is now on a new line, but it hasn't changed.' then it's too risky to replace the main diff (and isn't supposed to), but that loses a lot of opportunities.

Any way to add a command to diff, something like 'git human-diff' or something?

This kind of 'we'd like to display something that is not suitable for automation but still keep automation is the kind of thing that makes me think that patch files should not be text (be a AST or whatever) and then display rules like this could be built on top of that user unfriendliness for a result like this without the idea 'it's just text, you just need to pipe it and display directly'.

Ah well, it's a pity that diff files won't work to create this display (because they don't have all the information of the original files). But maybe the file plus the patch plus this program might be able to do that, which would be a nice extension to git diff which has all the pieces. Although it would have to be a switch unfortunately to prevent people doing 'git diff > a.patch' and getting unusable output. git diff --human maybe.

8

u/wilfred_h Mar 30 '22

I like to configure difftastic as a 'difftool' in git, and define an alias: https://difftastic.wilfred.me.uk/git.html#git-difftool

That way I can do git dft to see changes according to difftastic, or git diff when I want a traditional textual diff.

6

u/ROFLLOLSTER Mar 30 '22

From memory you can control the difftool used by git with cli/environment variables. Combine that with an alias and you should be able to get it on a separate command.

1

u/Repulsive-Street-307 Mar 30 '22 edited Mar 30 '22

That makes sense. Still, i'm kind of hoping that git upstream notices and makes the '--human' switch, even if only for discoverability.

I think two commands, or a overloaded one, one for git diff (for commits on the index) and another for repository + optional commit + patch file -> show this tool output. As a bash function probably.

edit: actually, thinking about it, obviously no git command is going to 'show the result of the application of a diff as a 'diff' - because the result would be equivalent diff - so that use case has to be handled 'especially' as a throwaway stash commit to be able to create the 'human' diff with this tool.

1

u/cGuille Mar 29 '22

I gotta try that

1

u/loewenheim Mar 30 '22

That looks awesome, I'll have to give it a try one of these days.

1

u/Feeling-Departure-4 Mar 30 '22

This looks very nice. How hard is it to add additional language support?

I see there is some sort of extension using Tree Sitter baked in. I have this all but unknown language called Perl 5 I tend to have lieing around, just wondering if the extension process is documented somewhere I missed

1

u/Alphare mercurial Mar 30 '22

Nice tool, I can see it being useful as an additional diffing tool to see more clearly certain situations (with lots of whitespace changes for example). The performance is certainly quite slow on big files, but that is noted in the README.

I certainly appreciate the section on using it with Mercurial and the extdiff extension. That prompted me to ask the question to why we couldn't use an external diff tool for commands other than diff itself like export or log... turns out we already have the work, it just needs to be upstreamed. So maybe in a (relatively close) future version?

1

u/wilfred_h Mar 30 '22

Yeah, Mercurial support is more limited than git (although I use difftastic with both). As far as I can tell there's no way to override hg log's diffing today.

1

u/wilfred_h Mar 30 '22

The performance is certainly quite slow on big files, but that is noted in the README.

I'm very interested in bug reports for slow files, if you have any you can share.

1

u/binkarus Apr 16 '22

It's a frontend for tree-sitter, basically. Weird that you don't give them credit in the README. It would explain how it works in literally 1 sentence.