Skip to content

Instantly share code, notes, and snippets.

@thenigan
Created April 9, 2010 20:27
Show Gist options
  • Save thenigan/361548 to your computer and use it in GitHub Desktop.
Save thenigan/361548 to your computer and use it in GitHub Desktop.
#!/bin/sh -e
# Copyright 2010, Tim Henigan <tim.henigan@gmail.com>
#
# Perform a directory diff between commits in the repository using
# the external diff tool specified in the 'diff.tool' configuration
# option.
USAGE='<options> <commit>{0,2} -- <path>*
--cached Compare to the index rather than the working tree
commit SHA1 of a commit
path Limit the diff to the specified paths
'
. git-sh-setup
if [ -z $(git config --get diff.tool) ]; then
echo "Error: The 'diff.tool' configuration option must be set."
usage
fi
start_dir=$(pwd)
cd_to_toplevel # needed to access tar utility
# mktemp is not available on all platforms (missing from msysgit)
# Use a hard-coded tmp dir if it is not available
if [ -z $(which mktemp) ]; then
tmp=/tmp/git-diffall-tmp
else
tmp="$(mktemp -d)"
fi
mkdir "$tmp" "$tmp"/a "$tmp"/b
left=
right=
paths=
path_sep=
compare_staged=
common_anscestor=
while test $# != 0; do
case "$1" in
-h|--h|--he|--hel|--help)
usage
;;
--cached)
compare_staged=1
;;
--)
path_sep=1
;;
-*)
echo Invalid option: "$1"
usage
;;
*)
# could be commit, commit range or path limiter
case "$1" in
*...*)
left=${1%...*}
right=${1#*...}
common_anscestor=1
;;
*..*)
left=${1%..*}
right=${1#*..}
;;
*)
if [ -n "$path_sep" ]; then
if [ -z "$paths" ]; then
paths=$1
else
paths="$paths $1"
fi
elif [ -z "$left" ]; then
left=$1
elif [ -z "$right" ]; then
right=$1
else
if [ -z "$paths" ]; then
paths=$1
else
paths="$paths $1"
fi
fi
;;
esac
;;
esac
shift
done
# Determine the set of files which changed
if [ -n "$left" ] && [ -n "$right" ]; then
if [ -n "$compare_staged" ]; then
usage
elif [ -n "$common_anscestor" ]; then
git diff --name-only "$left"..."$right" -- "$paths" > "$tmp"/filelist
else
git diff --name-only "$left" "$right" -- "$paths" > "$tmp"/filelist
fi
elif [ -n "$left" ]; then
if [ -n "$compare_staged" ]; then
git diff --name-only --cached "$left" -- "$paths" > "$tmp"/filelist
else
git diff --name-only "$left" -- "$paths" > "$tmp"/filelist
fi
else
if [ -n "$compare_staged" ]; then
git diff --name-only --cached -- "$paths" > "$tmp"/filelist
else
git diff --name-only -- "$paths" > "$tmp"/filelist
fi
fi
# Exit immediately if there are no diffs
if [ ! -s "$tmp"/filelist ]; then
exit 0
fi
# Populate the tmp/b directory with the files to be compared
if [ -n "$right" ]; then
while read name; do
mkdir -p "$tmp"/b/"$(dirname "$name")"
git show "$right":"$name" > "$tmp"/b/"$name"
done < "$tmp"/filelist
elif [ -n "$compare_staged" ]; then
while read name; do
mkdir -p "$tmp"/b/"$(dirname "$name")"
git show :"$name" > "$tmp"/b/"$name"
done < "$tmp"/filelist
else
tar -c -T "$tmp"/filelist | (cd "$tmp"/b && tar -x)
fi
# Populate the tmp/a directory with the files to be compared
while read name; do
mkdir -p "$tmp"/a/"$(dirname "$name")"
if [ -n "$left" ]; then
git show "$left":"$name" > "$tmp"/a/"$name"
else
if [ -n "$compare_staged" ]; then
git show HEAD:"$name" > "$tmp"/a/"$name"
else
git show :"$name" > "$tmp"/a/"$name"
fi
fi
done < "$tmp"/filelist
cd "$tmp"
$(git config --get diff.tool) a b
# On exit, remove the tmp directory
cleanup () {
cd "$start_dir"
rm -rf "$tmp"
}
trap cleanup EXIT
@thenigan
Copy link
Author

TODO: Consider passing through difftool command line arguments (e.g. diff -ur a b).
TODO: After posting to git list, update StackOveflow questions to point to this script.
http://stackoverflow.com/questions/1259037/git-diff-external-viewer-get-all-diff-results
http://stackoverflow.com/questions/1220309/git-difftool-open-all-diff-files-immediately-not-in-serial

Different forms of 'git diff':

  1. git diff: shows diff between working tree and staged changes
  2. git diff --cached []: shows diff between staged changes and HEAD (or other named commit)
  3. git diff : shows diff between working tree and named commit
  4. git diff : show diff between two named commits
  5. git diff ..: same as above
  6. git diff ...: show the changes on the branch containing and up to the second , starting at a common ancestor of both

Note: all forms take an optional path limiter [--] []

@thenigan
Copy link
Author

This script is based on an example provided by Thomas Rast on the Git list [1]:
[1] http://thread.gmane.org/gmane.comp.version-control.git/124807

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment