How can I get a side-by-side diff when I do "git diff"?
Asked Answered
L

19

264

When I type git diff, I'd like to see a side-by-side diff, like with diff -y, or like to display the diff in an interactive diff tool like kdiff3. How can this be done?

Lanalanae answered 6/10, 2011 at 3:10 Comment(4)
possible duplicate of How do I view 'git diff' output with visual diff program?Unworthy
Note: you have a side-by-side diff on GitHub.Keffiyeh
Duplicate of How do I view 'git diff' output with my preferred diff tool/ viewer?Sikkim
Damn, this is annoying that you can't do it like with diff. I better get used to reading the non-side-by-side format, which is hard for a visual person.Triumphant
T
101

Although Git has an internal implementation of diff, you can set up an external tool instead.

There are two different ways to specify an external diff tool:

  1. setting the GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables.
  2. configuring the external diff tool via git config

ymattw's answer is also pretty neat, using ydiff

See also:

When doing a git diff, Git checks both the settings of above environment variables and its .gitconfig file.

By default, Git passes the following seven arguments to the diff program:

path  old-file  old-hex old-mode  new-file  new-hex new-mode

You typically only need the old-file and new-file parameters. Of course most diff tools only take two file names as an argument. This means that you need to write a small wrapper-script, which takes the arguments which Git provides to the script, and hands them on to the external git program of your choice.

Let's say you put your wrapper-script under ~/scripts/my_diff.sh:

#!/bin/bash
# un-comment one diff tool you'd like to use

# use standard diff command with options: (2023)
/usr/bin/diff -y "$2" "$5" 

# side-by-side diff with custom options:
# /usr/bin/sdiff -w200 -l "$2" "$5" 

# using kdiff3 as the side-by-side diff:
# /usr/bin/kdiff3 "$2" "$5"

# using Meld 
/usr/bin/meld "$2" "$5"

# using VIM
# /usr/bin/vim -d "$2" "$5"

you then need to make that script executable:

chmod a+x ~/scripts/my_diff.sh

you then need to tell Git how and where to find your custom diff wrapper script. You have three choices how to do that: (I prefer editing the .gitconfig file)

  1. Using GIT_EXTERNAL_DIFF, GIT_DIFF_OPTS

    e.g. in your .bashrc or .bash_profile file you can set:

     GIT_EXTERNAL_DIFF=$HOME/scripts/my_diff.sh
     export GIT_EXTERNAL_DIFF
    
  2. Using git config

    use "git config" to define where your wrapper script can be found:

     git config --global diff.external ~/scripts/my_diff.sh
    
  3. Editing your ~/.gitconfig file

    you can edit your ~/.gitconfig file to add these lines:

     [diff]
       external = ~/scripts/my_diff.sh
    

Note:

Similarly to installing your custom diff tool, you can also install a custom merge-tool, which could be a visual merging tool to better help visualizing the merge. (see the progit.org page)

See: http://fredpalma.com/518/visual-diff-and-merge-tool/ and https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration

Trilinear answered 6/10, 2011 at 3:14 Comment(5)
Does this retain git terminal coloring?Triumphant
This is great, but it launches a new viewer for every file. Any way to create a consolidated diff in, say, meld?Petuntse
@Trilinear I am getting error for vim as im: Warning: Output is not to a terminalBohi
Can the meld version be configured to do a directory diff, where I can choose which file(s) I want to see the diff for? Currently it runs a separate meld command for each file, and I have to quit meld in order to see the next file. I'd rather have meld show me a directory listing of changed files like it behaves when meld is used from Mercurial.Philbrook
I don't want to vote this answer down. But the answer from ymattw was very easy to implement.Usia
S
269

Try git difftool

Use git difftool instead of git diff. You'll never go back.

UPDATE to add an example usage:

Here is a link to another stackoverflow that talks about git difftool: How do I view 'git diff' output with my preferred diff tool/ viewer?

For newer versions of git, the difftool command supports many external diff tools out-of-the-box. For example vimdiff is auto supported and can be opened from the command line by:

cd /path/to/git/repo
git difftool --tool=vimdiff

Other supported external diff tools are listed via git difftool --tool-help here is an example output:

'git difftool --tool=<tool>' may be set to one of the following:
        araxis
        kompare
        vimdiff
        vimdiff2

The following tools are valid, but not currently available:
        bc3
        codecompare
        deltawalker
        diffuse
        ecmerge
        emerge
        gvimdiff
        gvimdiff2
        kdiff3
        meld
        opendiff
        tkdiff
        xxdiff
Scifi answered 6/10, 2011 at 3:11 Comment(8)
Or maybe you will go back if you get This message is displayed because 'diff.tool' is not configured.. Perhaps update answer with minimal how-to configure this thing, so that it display side-by-side diffs in terminal, which is what OP asked for? GUI tools are quite useless on remote server where you connect using ssh.Irrepealable
Interesting point, though I don't think I've personally ever needed to use git while SSH'd. Once of the nice things about DVCS is the Distributed part: at least in my environments it's never a hassle to locally clone whatever repo I want to poke around.Scifi
At least in my config, git difftool with vimdiff doesn't always line up the two files/buffers correctly.Hofuf
Thats nice, and so down below in the answer list :O I use git difftool -y to prevent tkdiff promptProng
Related: make meld your git difftool in Windows & Linux: https://mcmap.net/q/12247/-git-mergetool-with-meld-on-windowsBradwell
We need command line diffing for other reasons, like over plaintext chat when collaborating (e.g. a code review) or when doing something over ssh.Triumphant
Related: open all differences at once instead of serially by using dir-diff option: https://mcmap.net/q/12824/-git-difftool-open-all-diff-files-immediately-not-in-serialAscendant
Just a heads-up that the list of Available tools seems to be based on certain app names being in $PATH. "Araxis" is a tool for Windows and Mac whose name in the shell is compare. compare also happens to be a program included with ImageMagick on Linux, so if you've never heard of Araxis and see it on your machine you probably don't actually have it and passing it to --tool= will do nothing.Joachima
T
101

Although Git has an internal implementation of diff, you can set up an external tool instead.

There are two different ways to specify an external diff tool:

  1. setting the GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables.
  2. configuring the external diff tool via git config

ymattw's answer is also pretty neat, using ydiff

See also:

When doing a git diff, Git checks both the settings of above environment variables and its .gitconfig file.

By default, Git passes the following seven arguments to the diff program:

path  old-file  old-hex old-mode  new-file  new-hex new-mode

You typically only need the old-file and new-file parameters. Of course most diff tools only take two file names as an argument. This means that you need to write a small wrapper-script, which takes the arguments which Git provides to the script, and hands them on to the external git program of your choice.

Let's say you put your wrapper-script under ~/scripts/my_diff.sh:

#!/bin/bash
# un-comment one diff tool you'd like to use

# use standard diff command with options: (2023)
/usr/bin/diff -y "$2" "$5" 

# side-by-side diff with custom options:
# /usr/bin/sdiff -w200 -l "$2" "$5" 

# using kdiff3 as the side-by-side diff:
# /usr/bin/kdiff3 "$2" "$5"

# using Meld 
/usr/bin/meld "$2" "$5"

# using VIM
# /usr/bin/vim -d "$2" "$5"

you then need to make that script executable:

chmod a+x ~/scripts/my_diff.sh

you then need to tell Git how and where to find your custom diff wrapper script. You have three choices how to do that: (I prefer editing the .gitconfig file)

  1. Using GIT_EXTERNAL_DIFF, GIT_DIFF_OPTS

    e.g. in your .bashrc or .bash_profile file you can set:

     GIT_EXTERNAL_DIFF=$HOME/scripts/my_diff.sh
     export GIT_EXTERNAL_DIFF
    
  2. Using git config

    use "git config" to define where your wrapper script can be found:

     git config --global diff.external ~/scripts/my_diff.sh
    
  3. Editing your ~/.gitconfig file

    you can edit your ~/.gitconfig file to add these lines:

     [diff]
       external = ~/scripts/my_diff.sh
    

Note:

Similarly to installing your custom diff tool, you can also install a custom merge-tool, which could be a visual merging tool to better help visualizing the merge. (see the progit.org page)

See: http://fredpalma.com/518/visual-diff-and-merge-tool/ and https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration

Trilinear answered 6/10, 2011 at 3:14 Comment(5)
Does this retain git terminal coloring?Triumphant
This is great, but it launches a new viewer for every file. Any way to create a consolidated diff in, say, meld?Petuntse
@Trilinear I am getting error for vim as im: Warning: Output is not to a terminalBohi
Can the meld version be configured to do a directory diff, where I can choose which file(s) I want to see the diff for? Currently it runs a separate meld command for each file, and I have to quit meld in order to see the next file. I'd rather have meld show me a directory listing of changed files like it behaves when meld is used from Mercurial.Philbrook
I don't want to vote this answer down. But the answer from ymattw was very easy to implement.Usia
P
98

You can also try git diff --word-diff. It's not exactly side-by-side, but somehow better, so you might prefer it to your actual side-by-side need.

Pathos answered 11/11, 2013 at 12:1 Comment(6)
This is the easiest way. What's even better is git diff --word-diff=colorAbvolt
@Abvolt --word-diff=color gives me an invalid option error. Which version was it introduced in?Taproom
@Trengot I run git 1.7.9 which is from 02/2012Abvolt
@Abvolt default installed version here is 1.7.1. Could explain the difference. git diff --color-words does work.Taproom
Yes, git diff --color-words is the way to go on modern git versions.Webster
Somehow better, not very poetic, but accurate!Fatally
C
60

ydiff

Formerly called cdiff, this tool can display side by side, incremental, and colorful diff.

Instead of doing git diff, do:

ydiff -s -w0

This will launch ydiff in side-by-side display mode for each of the files with differences.

Install with:

python3 -m pip install --user ydiff

-or-

brew install ydiff

For git log, you can use:

ydiff -ls -w0

-w0 auto-detects your terminal width. See the ydiff GitHub repository page for detail and demo.

Tested in Git 2.18.0, ydiff 1.1.

Cultigen answered 1/2, 2013 at 15:14 Comment(5)
@RyneEverett: Can you explain how to do the equivalent of git diff | cdiff -s with icdiff?Noach
Just run ydiff -s from a git/svn/hg workspace, you don't have to pipe in.Cultigen
if you want to limit the diff to a specific file through Git's history, cd <git repo> and then run ydiff -ls <path/to/file>Rainproof
I installed it with pip, but still I the the command ydiff on the console not found, because it is intalled as ~/.local/bin/ydiffMaybellemayberry
the ydiff does not work for meTrilinear
S
34

You can do a side-by-side diff using sdiff as follows:

$ git difftool -y -x sdiff  HEAD^ | less

where HEAD^ is an example that you should replace with whatever you want to diff against.

I found this solution here where there are a couple of other suggestions also. However, this one answer's the OP's question succinctly and clearly.

See the man git-difftool for an explanation of the arguments.


Taking the comments on board, you can create a handy git sdiff command by writing the following executable script:

#!/bin/sh
git difftool -y -x "sdiff -w $(tput cols)" "${@}" | less

Save it as /usr/bin/git-sdiff and chmod +x it. Then you'll be able to do this:

$ git sdiff HEAD^

Extra Tip

As suggested in comments you can use icdiff to do what sdiff does with colored output:

$ more /usr/bin/git-sdiff
#!/bin/sh
git difftool -y -x "icdiff --cols $(tput cols)" "${@}" | less --raw-control-chars
Softhearted answered 30/8, 2016 at 10:57 Comment(0)
V
15

For unix, combining just git and the built-in diff:

git show HEAD:path/to/file | diff -y - path/to/file

Of course, you can replace HEAD with any other git reference, and you probably want to add something like -W 170 to the diff command.

This assumes that you are just comparing your directory contents with a past commit. Comparing between two commits is more complex. If your shell is bash you can use "process substitution":

diff -y -W 170 <(git show REF1:path/to/file) <(git show REF2:path/to/file)

where REF1 and REF2 are git references – tags, branches or hashes.

Vespid answered 7/5, 2018 at 18:49 Comment(1)
Thanks -- your command 'git show HEAD:path/to/file' was what I needed to come up with my own solution, 'vimdfiff <(git show HEAD:path/to/file) path/to/file'. The bits still aren't lined up correctly, but that's the best solution I've got right now.Idocrase
P
14

I recently implemented a tool that does exactly this: https://github.com/banga/git-split-diffs

Here's how to use it:

npm install -g git-split-diffs

git config --global core.pager "git-split-diffs --color | less -RFX"

And this is how it looks in your terminal (with the default theme):

Preview of side by side diffs

As you can see, it also supports syntax highlighting and highlighting changed words within lines

Pervasive answered 10/5, 2021 at 2:58 Comment(4)
wow. this tool is so great. thanks man for this awesomeness!Febrific
but its a little bit slow, takes 2 seconds to load just a fileFebrific
There are some notes on performance at github.com/banga/git-split-diffs#performance, but if this seems different, it would be great if you could file an issue. Thanks!Pervasive
This was perfect! Thank you! Works great when I run git diff --stagedEndsley
O
9
export GIT_EXTERNAL_DIFF='meld $2 $5; echo >/dev/null'

then simply:

git diff
Okajima answered 12/10, 2012 at 8:22 Comment(3)
`meld .' works too! And it shows all of the changes in a consolidated window.Petuntse
@Petuntse that works perfectly! So simple and practical :)Niece
meld works also quite good with XQartz-X-Display-Redirect. (Debian to macOS)Whangee
B
9

If you'd like to see side-by-side diffs in a browser without involving GitHub, you might enjoy git webdiff, a drop-in replacement for git diff:

$ pip install webdiff
$ git webdiff

This offers a number of advantages over traditional GUI difftools like tkdiff in that it can give you syntax highlighting and show image diffs.

Read more about it here.

Bummer answered 20/11, 2014 at 20:43 Comment(0)
W
8

I use colordiff.

On Mac OS X, install it with

$ sudo port install colordiff

On Linux is possibly apt get install colordiff or something like that, depending on your distro.

Then:

$ git difftool --extcmd="colordiff -ydw" HEAD^ HEAD

Or create an alias

$ git alias diffy "difftool --extcmd=\"colordiff -ydw\""

Then you can use it

$ git diffy HEAD^ HEAD

I called it "diffy" because diff -y is the side-by-side diff in unix. Colordiff also adds colors, that are nicer. In the option -ydw, the y is for the side-by-side, the w is to ignore whitespaces, and the d is to produce the minimal diff (usually you get a better result as diff)

Woodhead answered 17/7, 2014 at 14:5 Comment(4)
add -y to skip the Launch 'colordiff' [Y/n]: prompt.Wenwenceslaus
are you sure it is git alias diffy "difftool --extcmd=\"colordiff -ydw\""? Shouldn't it be git config --global alias.diffy "difftool --extcmd=\"colordiff -ydw\"" ?Flews
Please correct to apt install colordiff or apt-get install colordiff, apt get install colordiff is not working.Whangee
Got dynamic width working using this: git difftool -y -x 'colordiff -y --width=$(tput cols) --tabsize=4 --expand-tabs --ignore-all-space' HEAD~1 - HEAD~1 is just an example, and I'm sure someone can create an alias or a script from that.Phonology
B
6

I personally really like icdiff !

If you're on Mac OS X with HomeBrew, just do brew install icdiff.

To get the file labels correctly, plus other cool features, I have in my ~/.gitconfig:

[pager]
    difftool = true
[diff]
    tool = icdiff
[difftool "icdiff"]
    cmd = icdiff --head=5000 --highlight --line-numbers -L \"$BASE\" -L \"$REMOTE\" \"$LOCAL\" \"$REMOTE\"

And I use it like: git difftool

Bedraggled answered 29/10, 2015 at 13:45 Comment(0)
A
4

This question showed up when I was searching for a fast way to use git builtin way to locate differences. My solution criteria:

  • Fast startup, needed builtin options
  • Can handle many formats easily, xml, different programming languages
  • Quickly identify small code changes in big textfiles

I found this answer to get color in git.

To get side by side diff instead of line diff I tweaked mb14's excellent answer on this question with the following parameters:

$ git diff --word-diff-regex="[A-Za-z0-9. ]|[^[:space:]]"

If you do not like the extra [- or {+ the option --word-diff=color can be used.

$ git diff --word-diff-regex="[A-Za-z0-9. ]|[^[:space:]]" --word-diff=color

That helped to get proper comparison with both json and xml text and java code.

In summary the --word-diff-regex options has a helpful visibility together with color settings to get a colorized side by side source code experience compared to the standard line diff, when browsing through big files with small line changes.

Appetizing answered 20/7, 2015 at 12:27 Comment(0)
B
4

Use delta.

In your gitconfig file (usually ~/.gitconfig or ~/.config/git/config),

add:

[core]
  pager = delta --light --side-by-side 
Beef answered 21/3, 2023 at 5:55 Comment(0)
S
3

Open Intellij IDEA, select a single or multiple commits in the "Version Control" tool window, browse changed files, and double click them to inspect changes side by side for each file.

With the bundled command-line launcher you can bring IDEA up anywhere with a simple idea some/path

version control view diff view

Snatchy answered 5/12, 2017 at 15:29 Comment(0)
A
2

Here's an approach. If you pipe through less, the xterm width is set to 80, which ain't so hot. But if you proceed the command with, e.g. COLS=210, you can utilize your expanded xterm.

gitdiff()
{
    local width=${COLS:-$(tput cols)}
    GIT_EXTERNAL_DIFF="diff -yW$width \$2 \$5; echo >/dev/null" git diff "$@"
}
Adlai answered 27/6, 2016 at 9:14 Comment(1)
Funny. I signed by name with a pseudonym but that was ignored... Thanks for outing me, Stack Overflow. :(Adlai
D
2

Several others already mentioned cdiff for git side-by-side diffing but no one gave a full implementation of it.

Setup cdiff:

git clone https://github.com/ymattw/cdiff.git
cd cdiff
ln -s `pwd`/cdiff ~/bin/cdiff
hash -r # refresh your PATH executable in bash (or 'rehash' if you use tcsh)
        # or just create a new terminal

Edit ~/.gitconfig inserting these lines:

[pager]
        diff = false
        show = false

[diff]
        tool = cdiff
        external = "cdiff -s $2 $5 #"

[difftool "cdiff"]
        cmd = cdiff -s \"$LOCAL\" \"$REMOTE\"

[alias]
        showw = show --ext-dif

The pager off is needed for cdiff to work with Diff, it is essentially a pager anyway so this is fine. Difftool will work regardless of these settings.

The show alias is needed because git show only supports external diff tools via argument.

The '#' at the end of the diff external command is important. Git's diff command appends a $@ (all available diff variables) to the diff command, but we only want the two filenames. So we call out those two explicitly with $2 and $5, and then hide the $@ behind a comment which would otherwise confuse sdiff. Resulting in an error that looks like:

fatal: <FILENAME>: no such path in the working tree
Use 'git <command> -- <path>...' to specify paths that do not exist locally.

Git commands that now produce side-by-side diffing:

git diff <SHA1> <SHA2> 
git difftool <SHA1> <SHA2>
git showw <SHA>

Cdiff usage:

'SPACEBAR' - Advances the page of the current file.
'Q'        - Quits current file, thus advancing you to the next file.

You now have side-by-side diff via git diff and difftool. And you have the cdiff python source code for power user customization should you need it.

Devine answered 2/2, 2018 at 6:23 Comment(0)
F
2

This may be a somewhat limited solution, but does the job using the system's diff command without external tools:

diff -y  <(git show from-rev:the/file/path) <(git show to-rev:the/file/path)
  • filter just the change lines use --suppress-common-lines (if your diff supports the option).
  • no colors in this case, just the usual diff markers
  • can tweak the column width --width=term-width; in Bash can get the width as $COLUMNS or tput cols.

This can be wrapped into a helper git-script too for more convenience, for example, usage like this:

git diffy the/file/path --from rev1 --to rev2
Fortney answered 18/7, 2020 at 19:27 Comment(0)
K
2

There are multiple solutions:

Solution 1 : Meld :

Install meld (in ubuntu I used sudo apt install meld). Then configure it like bellow.

git config --global diff.tool meld
git config --global difftool.meld.path "$(which meld)"
git config --global difftool.prompt false

git config --global merge.tool meld
git config --global mergetool.meld.path "$(which meld)"

Solution 2 : Delta :

If you decide to use cli, then install delta. The config I use is:

git config --global core.pager 'delta'
git config --global interactive.diffFilter 'delta --color-only'
git config --global delta.side-by-side true
git config --global delta.line-numbers true
git config --global delta.syntax-theme 'Solarized (dark)'

Solution 3 : Melt :

You can also use Melt. It's syntax highlighting is done with bat. This is also a cli tool.

Kiernan answered 15/6, 2023 at 0:50 Comment(0)
T
1

There are a lot of good answers on this thread. My solution for this issue was to write a script.

Name this 'git-scriptname' (and make it executable and put it in your PATH, like any script), and you can invoke it like a normal git command by running

$ git scriptname

The actual functionality is just the last line. Here's the source:

#!/usr/bin/env zsh
#
#   Show a side-by-side diff of a particular file how it currently exists between:
#       * the file system
#       * in HEAD (latest committed changes)

function usage() {
    cat <<-HERE
    USAGE

    $(basename $1) <file>

    Show a side-by-side diff of a particular file between the current versions:

        * on the file system (latest edited changes)
        * in HEAD (latest committed changes)

HERE
}

if [[ $# = 0 ]]; then
    usage $0
    exit
fi

file=$1
diff -y =(git show HEAD:$file) $file | pygmentize -g | less -R
Trumpeter answered 2/1, 2016 at 20:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.