Search code examples
gitgit-diff

How can I do a three way graphical diff (not merge) with git?


I have two branches in git that are similar and I want to manually pull those changes into a third branch that is quite different. Is it possible to do a three way graphical diff of those changes?

Graphical diff of two branches is currently setup and working well with 'meld'. The following displays a graphical meld window as expected:

  • git diff branch1 branch2 -- myfilename

I tried to do the following, but I get the ASCII output of 'diff --cc'.

  • git diff master branch1 branch2 -- myfilename

Is that a valid syntax to get a 3-way diff? What is the proper .gitconfig settings to have it open meld? I'm using Git version 1.8.2.1.

My expectation is to have the three files opened in a meld window, then I can look at the changes between branch1/branch2 and then visually make sure the same change is in master. I just realized that I can do this by checking out each file from the three branches independently, then passing the filenames as arguments directly to meld. The checkout can be done as per this SO answer: https://stackoverflow.com/a/888623/350265


Solution

  • The manual one-liner command

    You can invoke meld on three versions of the file without using temporary files by combining the <(cmd) syntax in bash with your git show idea:

    meld <(git show master:file) <(git show branch1:file) <(git show branch2:file)
    

    This will pop up meld with files dev/fd/61, /dev/fd/62 and /dev/fd/63 refering to the file in each of the three branches. The names are not very friendly, but you'll get used to that. The point is that it will show what you want to see.

    Scripting it

    The obvious next step is to simplify the syntax with a script:

    Create file ~/bin/git-meld3 (or anywhere else on your PATH):

    #!/bin/bash
    meld <(git show $1:$4) <(git show $2:$4) <(git show $3:$4)
    

    Make it executable:

    chmod +x ~/bin/git-meld3
    

    Call it:

    git meld3 master branch1 branch2 myfilename
    

    The command works with any committish:

    git meld3 master 36d1cf756 HEAD^^^ myfilename
    

    A more flexible script

    This ~/bin/git-meld script accepts two or three committishes:

    #!/bin/bash
    if [[ $# -eq 3 ]]; then
       meld <(git show $1:$3) <(git show $2:$3)
    elif [[ $# -eq 4 ]]; then
       meld <(git show $1:$4) <(git show $2:$4) <(git show $3:$4)
    else
       echo Usage: git meld committish1 committish2 [committish3] file >&2
       exit 1
    fi
    

    PS

    On my own machine, I have to invoke meld like this: python2.6 /usr/bin/meld, possibly because it's not installed correctly, so this is my actual ~/bin/git-meld3 script:

    #!/bin/bash
    python2.6 /usr/bin/meld <(git show $1:$4) <(git show $2:$4) <(git show $3:$4)
    

    Meld is old! the script says it requires Python 2.4, but it fails to compile with 2.7 or 3. Fortunately, it does run with 2.6, so I was able to test my solution.