Created
April 9, 2010 20:27
-
-
Save thenigan/361548 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh -e | |
# Copyright 2010, Tim Henigan <[email protected]> | |
# | |
# 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 |
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
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':
Note: all forms take an optional path limiter [--] []