40

I'm new to git on OS X, and I'm using it via the command line. I come from the world of Tortoise SVN and Beyond Compare on Windows.

I want to be able to send diffs to FileMerge.

I was able to do this with TextMate simply by using:

git diff | mate

But I'm not sure how to get that set up so I can use FileMerge instead?

Cajunluke
  • 3,103
  • 28
  • 28
doug
  • 1,789
  • 3
  • 12
  • 5

5 Answers5

56

Although it's not exactly the same as piping stdin into a script, you can do this:

git difftool -t opendiff -y

That will launch FileMerge once for each file. Doing the whole project tree at once takes a little scripting.

See also this question.

Community
  • 1
  • 1
Erin Dees
  • 1,677
  • 2
  • 13
  • 21
  • thanks!!!!!!!!!!!!!! so "difftool" basically is like a regular "diff" command but giving the choice for an external diff tool? and "opendiff" calls FileMerge specifically? and i can still pass a path to a specific file to diff just one? sorry, at home now and unable to test on my work machine ;) – Doug Mar 18 '10 at 05:29
  • actually, that isn't doing anything for me i see. my command line just returns a prompt. i tried having filemerge fired up, nothing. – doug Mar 18 '10 at 16:25
  • Hi, Doug. It might be having trouble finding the "opendiff" binary on your path. What happens when you create a couple of text files, and then type "opendiff file1.txt file2.txt" in Terminal? – Erin Dees Mar 18 '10 at 20:42
  • 3
    This doesn't work for me. `git difftool` always just dumps its output to the terminal. – Uncommon Mar 04 '13 at 21:13
  • 1
    The -d option will do a single directory diff rather than calling the tool on each changed file. `git difftool -d -t opendiff` – miner49r Jul 05 '13 at 17:10
  • 1
    opendiff has a couple of issues as the difftool. I wrote a little bash script to work around the problems: https://gist.github.com/miner/e73fc98a83a8fe05d9ef000d46d68a9f – miner49r Apr 25 '16 at 13:22
15

Create an executable script git-diff-cmd.sh

#!/bin/bash

xattr -w com.apple.TextEncoding "UTF-8;134217984" "$2"
xattr -w com.apple.TextEncoding "UTF-8;134217984" "$5"

/usr/bin/opendiff "$2" "$5" -merge "$1"

Now edit your .gitconfig file to include the lines

[diff]
    external = <path-to>/git-diff-cmd.sh

...replacing <path-to> by the path to git-diff-cmd.sh. Now git diff will use FileMerge, and correctly display UTF-8 Unicode characters.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Syzygies
  • 591
  • 3
  • 11
  • This works great, but can anyone explain how I’d use this script with `difftool` instead? It seems when using `cmd = /git-diff-cmd.sh` the script doesn’t get the same variables as when using `external`. – Oli Studholme Jan 19 '12 at 04:15
  • 1
    Be sure to `chmod 755 git-diff-cmd.sh` lest you get `Permission denied`. – Sarah Vessels Mar 19 '13 at 17:44
  • @Boblet the script that Syzgies posted passes positional params 2, 5, & 1 to opendiff ignoring the rest. `difftool` **probably** just passes in 2 params – nhed Mar 21 '13 at 17:20
5

Here's a script (originally by Toby White) that I hacked up to compare the entire directory structure in FileMerge rather than opening each file individually.

#!/bin/sh
#
# This script was written by Toby White under an unknown license, and published
# on the Git mailing list:
#
#   http://kerneltrap.org/mailarchive/git/2007/11/21/435536
#
# Superficial changes were made by Nathan de Vries to allow the script to be
# run under Leopard.
#
# Adapted by Daniel Miller : http://stackoverflow.com/a/12957945/10840
# - allow changes to be saved back to the working copy when diffing against HEAD
# - work when FileMerge is already open
# - always compare archived copies so ignored files are excluded from the diff
# - allow diff of unstaged changes (no arguments); creates a dangling commit
# - allow diff of subdirectory within the repo
#
# Known issues:
# - Always uses the same two directories (/tmp/git-opendiff-old and
#   /tmp/git-opendiff-new); THEY WILL BE DELETED IF THEY ALREADY EXIST.
#   Ugly, I know, but it makes the script work even if FileMerge is open.

OLD=
NEW=
FILEPATH=
HAS_ARGS=no
IGNORE_TO_PATH=no

# loosely based on https://stackoverflow.com/a/14787208/10840
while [ "$#" -ge 1 ]; do
    HAS_ARGS=yes
    case "$1" in
        -h)
            echo "usage: $0 [--cached | <ref> [<ref>]] [-- <path>]"
            exit 0
            ;;
        --cached)
            # diff staged changes
            NEW=$(git write-tree)
            OLD=HEAD
            IGNORE_TO_PATH=yes
            shift
            ;;
        --)
            shift
            FILEPATH="$@"
            break
            ;;
        *)
            if [[ "$IGNORE_TO_PATH" == "no" ]]; then
                if [ -z "$OLD" ]; then
                    OLD="$1"
                else
                    NEW="$1"
                    IGNORE_TO_PATH=yes
                fi
            fi
            shift
            ;;
    esac
done
if [ -z "$OLD" ]; then
    OLD=HEAD
fi
if [[ "$HAS_ARGS" == "no" ]]; then
    # diff unstaged changes
    # http://stackoverflow.com/a/12010656/10840
    NEW=$(git stash create)
    echo "diff unstaged changes"
fi

TMP_OLD=/tmp/git-opendiff-old
TMP_NEW=/tmp/git-opendiff-new
test -d $TMP_OLD && rm -rf $TMP_OLD; mkdir $TMP_OLD
test -d $TMP_NEW && rm -rf $TMP_NEW; mkdir $TMP_NEW

TMP_OLD=$TMP_OLD/$OLD; mkdir -p $TMP_OLD
git archive --format=tar $OLD $FILEPATH | (cd $TMP_OLD; tar xf -)

if test -z "$NEW"; then
    SAVE_TO=$(git rev-parse --show-cdup)
    test -z "$cdup" && SAVE_TO=.
    git archive --format=tar HEAD $FILEPATH | (cd $TMP_NEW; tar xf -)
    opendiff $TMP_OLD/$FILEPATH $TMP_NEW/$FILEPATH -merge $SAVE_TO &> /dev/null &
else
    TMP_NEW=$TMP_NEW/$NEW; mkdir -p $TMP_NEW
    git archive --format=tar $NEW $FILEPATH | (cd $TMP_NEW; tar xf -)
    opendiff $TMP_OLD/$FILEPATH $TMP_NEW/$FILEPATH &> /dev/null &
fi

Put this somewhere on your path. I prefer ~/bin/git-opendiff, which means git opendiff ... works as expected.

Update: diff unstaged changes when called with no arguments, added -h (help) option.

Update: diff subdirectory with -- <path>. Also better argument parsing.

millerdev
  • 10,011
  • 2
  • 31
  • 27
  • I added this script but get `>git opendiff README.md` `git: 'opendiff' is not a git command. See 'git --help'.` – Harry Moreno Apr 21 '16 at 14:50
  • @HarryMoreno sounds like the script is probably not in your path. Also, your usage looks problematic. This script is for comparing entire trees, not individual files. – millerdev Apr 21 '16 at 20:10
0

I don't know what's happening to me since I updated OS X and now opendiff is not working as it used to be. So I copied this script from here and it worked.

#!/bin/sh 
dir=$PWD
until [ -e "$dir/.git" ]; do
  if [ "$dir" == "/" ]; then
    echo "Not a git repository" >&2
    exit 1;
  fi
  dir=`dirname "$dir"`
done
open -a FileMerge -n -W --args -left "$dir/$1" -right "$2" -merge "$5"
0

First, start by telling git you want to use FileMerge (aka opendiff) as your default difftool:

git config --global diff.tool 

(This only needs to be done once)

Then it's as simple as

git difftool
Eric
  • 16,003
  • 15
  • 87
  • 139