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

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