
Diff So Fancy: make Git diffs look good - aram
https://github.com/stevemao/diff-so-fancy
======
jimrandomh
A personal pet peeve of mine when reading diffs, is when a file has some
functions and you insert one and instead of looking like this:

    
    
         int someOldFunction()
         {
             // Function body
         }
        +
        +int newFunction()
        +{
        +    // New function body
        +}
    

It looks like this:

    
    
         int someOldFunction()
         {
             // Function body
        +}
        +
        +int newFunction()
        +{
        +    // New function body
         }
    

It's a small thing, but given that these diffs are equivalent, the one that
balances the curly braces within added blocks should be favored. But diff
utilities seem to get this pretty consistently wrong.

~~~
Klathmon
It is a small thing, but it throws me off everytime i see it, and then it
takes a few seconds of looking around before it dawns on me what happened.

The "user" in me would love a language aware diffing (and merging) system, but
the developer in me is already groaning about how much work that would end up
taking for arguably not that much benefit.

~~~
alexvoda
Maybe this will help:
[https://www.semanticmerge.com/](https://www.semanticmerge.com/)

~~~
jxramos
I really like SemanticMerge, it makes total sense, although the diff
experience is unlike other diff tools in terms of immediacy. But I think it
will be another great tool to add to the arsenal when you need to pull out the
big guns on crazy diffs.

~~~
m_fayer
Absolutely. I had it installed for some time and mostly thought "this is
really neat but I could live without it." And then there was a monster merge
with significant conflicts all over the place. I don't think it would have
been otherwise possible to pull it off with as little damage as there was if
not for this tool.

------
paulirish
[I'm the author of diff-so-fancy, Steve helped with shipping it as a
standalone script]

NPM?!? :)

A lot of people below are asking why a bash script (that depends on a perl
script) is being recommended to install via NPM? The short reason is that NPM
is the most straightforward way to get a script installed as a global binary
in a cross-platform manner. This approach has worked quite well with `git-
open`[0]. Asking all users to deal with the PATH is not my ideal.

In addition, I wanted a reasonable upgrade path, in case there are neccessary
bugfixes. It's not a great experience if users identify bugs but the fix means
they manually find it/download/PATH-ify each time. :/

That said, I'll add some Manual Install instructions to the readme so it's
clear how to do this on your own. :) ( Edit: Here they are…
[https://github.com/stevemao/diff-so-
fancy/blob/master/readme...](https://github.com/stevemao/diff-so-
fancy/blob/master/readme.md#manual-install) )

[0] [https://github.com/paulirish/git-open](https://github.com/paulirish/git-
open)

~~~
gkya
I had no reasons to use this package, but now I have one to not use it. I only
wanted to see what it was because the name suggested something fun.

Why whould someone who can't add $HOME/bin to $PATH be using git?

~~~
argonaut
I realize your question is rhetorical, but there are tons of people. Anyone
new to programming, in a CS course that uses git, for example, would be
familiar with basic git but many would be unfamiliar with the path (or on
Windows).

~~~
gkya
I concur, but they need not stay unfamiliar with it. The concept is easy: When
you type the name of a program and hit enter, I look it up in a list of
directories to see the first one that contains a file with that name. That
list is $PATH. Any programmer will have to deal with search paths and stuff at
some point in their life, and probably very early on, when they'll want to run
their own scripts.

~~~
lowboy
I agree that most programmers will run into $PATH at some point, but why force
an order on them? Maybe they just want to get started using things like fancy
diffs provided through package managers like npm.

------
phinnaeus
It looks like the diff-so-fancy script is bash, and the diff-highlight script
is perl. Why is it set up in npm? WTF?

~~~
lucideer
There's no _easy_ install methods for small simple scripts that doesn't
involve multiple manual steps, have an automated upgrade path, is cross
platform and is consistent+familiar to a large subset of developers.

Of the options out there for the above, npm - while hassle if you don't have
it already installed - is probably the closest balance of maintainer and end-
developer convenience.

~~~
hobarrera
make install?

~~~
lucideer
Nothing wrong with make, but to be fair, it doesn't really meet any of the
criteria I mentioned

\- no built-in remote repos / app directory so it's just the final step of
multiple install steps

\- no built-in automated upgrade path

\- unless you're fiddling with Cygwin or MSys2, it's not really as cross-
platform

\- likely to be less convenient for the maintainer than the package.json
standard

------
squiguy7
Why do I need NPM to install this? I guess I will just have to manually get
and link them...

~~~
jdimov10
Because there's been this NPM virus infecting our systems over the past few
years. It has been disgusing itself as a useful utility.

~~~
Scarblac
I'm going to assume that is a typo for disgusting.

~~~
richardwhiuk
disguising I think.

------
chaitanya
Might be better to link to the source directly:
[https://github.com/paulirish/dotfiles/blob/master/bin/diff-s...](https://github.com/paulirish/dotfiles/blob/master/bin/diff-
so-fancy)

------
FiloSottile
If you don't want your diff so fancy (pun intended, and I'm sorry) but you
still want the inline highlights, the script comes with git
([https://github.com/git/git/tree/master/contrib/diff-
highligh...](https://github.com/git/git/tree/master/contrib/diff-highlight)):

    
    
        ln -sf "$(brew --prefix)/share/git-core/contrib/diff-highlight/diff-highlight" ~/bin/diff-highlight
    

and add to .gitconfig

    
    
        [pager]
                log = diff-highlight | less
                show = diff-highlight | less
                diff = diff-highlight | less

------
pierrebeaucamp
I know he gave credit in the README, but why does this ~30 line shell script
need its own repo? Seems more like a cheap grab for Github Stars rather than
to provide actual value.

Edit: Even the screenshot is from Paul...

~~~
paulirish
A bit ago, I had indicated this script should be more accessible than copying
two separate files out of my dotfiles repo:
[https://github.com/paulirish/dotfiles/blob/master/bin/diff-s...](https://github.com/paulirish/dotfiles/blob/master/bin/diff-
so-fancy#L34)

Steve took the initiative to put this in it's own repo. Seems okay; I was
being rather slow to ship it for real.

~~~
pierrebeaucamp
All good then ;)

------
finnn
So, the whole npm thing seems weird to me, then it occurred to me that it
could be for malicious purposes. Would it be possible to upload a separate
package.json to npm that had eg a post-install script? I don't know much about
how npm works from the package publication side of things, but I assumed it
was similar to pypi where the code in the git repo doesn't have to be at all
related to the code in the package

------
timtadh

        git diff --word-diff=color
    

provides a really nice word diff with coloring similar to this project. Just
setup an alias in your global gitconfig:

    
    
        [alias]
                cdiff = diff --word-diff=color

------
landr0id
Looks good, but why does it use npm for installation?

~~~
ascorbic
Right: the package.json is almost as long as the script itself.

------
izolate
Remove contextual +/\- in favour of colour highlights? As a person with red-
green colourblindness all I can say is: Lol. Nope.

~~~
brokentone
That's a helpful perspective, but hopefully you already have workarounds on
the color issue. This is simply making diff pieces easier to copy / pasta --
which I for one have needed to do. Change my mind on a refactor midstream,
need to restore part of the file, etc.

~~~
tomjakubowski
You might investigate the tools your editor of choice provides for working
with diffs. You might be surprised at how easy it can be!

(In Emacs, if you're using git, magit is an amazing package. You can select a
commit from the logs, dive into the diff for a file caused by that commit,
highlight a region of the diff and revert the change in your working copy.
It's wonderful.)

~~~
joeheyming
Came here for someone to shout-out magit. It's amazing

------
mschuster91
what I'd like to see is that a/b in front of the filenames disappear. Getting
rid of that would FINALLY allow me to double-click on the filename (which is
configured to select the part between the spaces and copy it to the clipboard)
and paste it instantly for the next command... or to be able to do git diff >
foo.patch and on another system do patch < foo.patch without having to
remember the correct -p value.

~~~
datashaman
git diff --no-prefix

------
oabm
What happened to this line from the second file in the screenshot?

    
    
      -        var optionsGlassPane = new WebInspector.GlassPane(document);
    
    

An important part of viewing diffs for me is seeing what the old code was.

------
makecheck
I handle this in a way that is more agnostic to the type of revision control,
and fully flexible in coloring (using the most powerful scheme available).

For example, I shouldn't have to put up with basic colors if the terminal can
do better.

Here is how it works; starting with:

    
    
      #!/bin/bash 
      if [ -r ".svn" ] ; then
        exec svn diff ${1+"$@"} | my_colorize_diff
      else
        git diff ${1+"$@"} | my_colorize_diff
      fi
    

...where the "my_colorize_diff" script at the end of the pipe is as follows:

    
    
      #!/usr/bin/env perl
      # by Kevin Grant (kmg@mac.com)
      my $term_program = (exists $ENV{'TERM_PROGRAM'} && defined $ENV{'TERM_PROGRAM'}) ? $ENV{'TERM_PROGRAM'} : '';
      my $term = (exists $ENV{'TERM'} && defined $ENV{'TERM'}) ? $ENV{'TERM'} : 'vt100';
      my $is_xterm = ($term =~ /xterm/);
      my $is_24bit = ($term_program =~ /MacTerm/);
      print "\033#3BEGIN DIFF\n";
      print "\033#4BEGIN DIFF\n\033#5";
      while (<>) {
        if (/^\+/ && !/^\+\+/) {
          if ($is_24bit) {
            print "\033[48:2:150:200:150m", "\033[2K", "\033[38:2::88:m", "\033[1m";
          } elsif ($is_xterm) {
            print "\033[48;5;149m", "\033[2K", "\033[38;5;235m", "\033[1m";
          } else {
            print "\033[42m", "\033[2K", "\033[30m", "\033[1m";
          }
        } elsif (/^\-/ && !/^\-\-/) {
          if ($is_24bit) {
            print "\033[48:2:244:150:150m", "\033[2K", "\033[38:2:144:0::m";
          } elsif ($is_xterm) {
            print "\033[48;5;52m", "\033[2K", "\033[38;5;124m";
          } else {
            print "\033[41m", "\033[2K", "\033[37m";
          }
        } else {
          print "\033[3m";
        }
        chomp;
        print;
        print "\033[0m\n";
      }
      print "\033#3END DIFF\n";
      print "\033#4END DIFF\n\033#5";

~~~
thristian
For what it's worth, there's a lot of 24-bit-capable terminals that aren't
MacTerm. Even xterm supports the 24-bit-color sequences, although it picks the
closest entry in its 256-colour palette rather than using the 24-bit colour
directly.

Also, you seem to be assuming "xterm" supports 256 colours and everything else
doesn't. The best way to figure out how many colours the terminal supports is
$(tput colours). tput also looks up other useful sequences; you can "tput
bold" to turn on bold mode, "tput setaf 12" to set the foreground to colour 12
(bright yellow), "tput sgr0" to zero all active formatting, etc.

~~~
makecheck
Good point. Although, unless there are shells that have "tput" built-in, that
means more subprocesses to obtain basic information (which would slow down the
result a bit). In my case, the environment is sufficient to figure out what to
do.

------
jfountain2015
Nice improvement to diff but I think I'll still use `git difftool` with
Diffmerge
[https://sourcegear.com/diffmerge/](https://sourcegear.com/diffmerge/)

------
hobarrera
I didn't quite like this, put it does reference diff-hightlight, which is part
of git-contrib (so it may already be installed on your system, but just not in
your $PATH!):

[https://github.com/git/git/tree/master/contrib/diff-
highligh...](https://github.com/git/git/tree/master/contrib/diff-highlight)

For example, here's a diff where it improved readability enormously:

[https://i.imgur.com/8iQNaeu.png](https://i.imgur.com/8iQNaeu.png)

~~~
hobarrera
Yes, the code in that sample is horrible triplicate. Please ignore that.

------
felipesabino
I have an alias for the diff params bellow, which has basically the same
visual result, without the need to install anything.

    
    
      $ git diff --color --color-words --abbrev

------
_ZeD_
what are the advantages over colordiff[0]? (or a graphic differ like
kdiff3[1])

[0] [http://www.colordiff.org/](http://www.colordiff.org/) [1]
[http://kdiff3.sourceforge.net/](http://kdiff3.sourceforge.net/)

------
tremguy
> No pesky + or - at line-stars, making for easier copy-paste.

I wonder if easing copypasting is a good or bad thing..

------
mrinterweb
Vim's fugitive plugin also gives a similar split diff view. The command is
"Gdiff".

~~~
ilikepi
Even without the plugin, you can spawn a diff in vim via git's difftool
settings. Here's a basic version that can be used in one's .gitconfig:

    
    
        [diff]
          tool = customvim
        [difftool "customvim"]
          cmd = vim -R -f -d \"$LOCAL\" \"$REMOTE\"

------
stevemao
We've grown and it's out of control. diff-so-fancy moved to an org!!!
[https://github.com/so-fancy/diff-so-fancy](https://github.com/so-fancy/diff-
so-fancy)

------
nikolay
I find icdiff [0] (Improved Colored Diff) better.

[0]: [https://github.com/jeffkaufman](https://github.com/jeffkaufman)

------
GreaterFool
Is there any language aware diff tool? I think I saw some commercial product
for Java but other than that I haven't seen any attempts to do that.

------
samuell
When writing manuscripts in git, my favourite "trick" is git diff --word-diff
(of course aliased to git wdiff).

------
module17
It looks so good and real, I kept hitting `q` whilst looking at the image.

------
nqzero
the netbeans.team.diff tool is similar (showing the specific words that
changed), allows interactive editing, and does a good job even with large
insertions and deletions

------
AdmiralAsshat
So what does this offer over simply using vimdiff?

------
merb
this doesn't look useful. the left one is just fine. for everything else you
wouldn't need a cli diff tool...

~~~
nqzero
when one or 2 words change in a long or dense line, it's nice to have the
specific changes highlighted

imagine a for loop in which a variable (used on every line) was renamed, and
buried in the loop an assignment changed slightly (eg, + became -). with a
standard diff, it's really hard (for me) to pick up the minor change. with
word by word diffs, it's pretty easy

(i use netbeans diff, not this tool, but they appear similar)

~~~
djur
git has built-in word diffing with --word-diff, though. --word-diff=color does
almost exactly the same thing.

~~~
nqzero
thanks. this is very nice and i wasn't aware of it

note: need to pipe it to "less -r"

------
Animats
Hurts. Hurts bad. Tiny type in many primary colors on a black background. 1985
called, it wants its screen layout back.

