You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
257 lines
5.6 KiB
257 lines
5.6 KiB
#!/bin/sh |
|
# Copyright 2010 - 2012, Tim Henigan <tim.henigan@gmail.com> |
|
# |
|
# Perform a directory diff between commits in the repository using |
|
# the external diff or merge tool specified in the user's config. |
|
|
|
USAGE='[--cached] [--copy-back] [-x|--extcmd=<command>] <commit>{0,2} [-- <path>*] |
|
|
|
--cached Compare to the index rather than the working tree. |
|
|
|
--copy-back Copy files back to the working tree when the diff |
|
tool exits (in case they were modified by the |
|
user). This option is only valid if the diff |
|
compared with the working tree. |
|
|
|
-x=<command> |
|
--extcmd=<command> Specify a custom command for viewing diffs. |
|
git-diffall ignores the configured defaults and |
|
runs $command $LOCAL $REMOTE when this option is |
|
specified. Additionally, $BASE is set in the |
|
environment. |
|
' |
|
|
|
SUBDIRECTORY_OK=1 |
|
. "$(git --exec-path)/git-sh-setup" |
|
|
|
TOOL_MODE=diff |
|
. "$(git --exec-path)/git-mergetool--lib" |
|
|
|
merge_tool="$(get_merge_tool)" |
|
if test -z "$merge_tool" |
|
then |
|
echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set." |
|
usage |
|
fi |
|
|
|
start_dir=$(pwd) |
|
|
|
# All the file paths returned by the diff command are relative to the root |
|
# of the working copy. So if the script is called from a subdirectory, it |
|
# must switch to the root of working copy before trying to use those paths. |
|
cdup=$(git rev-parse --show-cdup) && |
|
cd "$cdup" || { |
|
echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree" |
|
exit 1 |
|
} |
|
|
|
# set up temp dir |
|
tmp=$(perl -e 'use File::Temp qw(tempdir); |
|
$t=tempdir("/tmp/git-diffall.XXXXX") or exit(1); |
|
print $t') || exit 1 |
|
trap 'rm -rf "$tmp"' EXIT |
|
|
|
left= |
|
right= |
|
paths= |
|
dashdash_seen= |
|
compare_staged= |
|
merge_base= |
|
left_dir= |
|
right_dir= |
|
diff_tool= |
|
copy_back= |
|
|
|
while test $# != 0 |
|
do |
|
case "$1" in |
|
-h|--h|--he|--hel|--help) |
|
usage |
|
;; |
|
--cached) |
|
compare_staged=1 |
|
;; |
|
--copy-back) |
|
copy_back=1 |
|
;; |
|
-x|--e|--ex|--ext|--extc|--extcm|--extcmd) |
|
if test $# = 1 |
|
then |
|
echo You must specify the tool for use with --extcmd |
|
usage |
|
else |
|
diff_tool=$2 |
|
shift |
|
fi |
|
;; |
|
--) |
|
dashdash_seen=1 |
|
;; |
|
-*) |
|
echo Invalid option: "$1" |
|
usage |
|
;; |
|
*) |
|
# could be commit, commit range or path limiter |
|
case "$1" in |
|
*...*) |
|
left=${1%...*} |
|
right=${1#*...} |
|
merge_base=1 |
|
;; |
|
*..*) |
|
left=${1%..*} |
|
right=${1#*..} |
|
;; |
|
*) |
|
if test -n "$dashdash_seen" |
|
then |
|
paths="$paths$1 " |
|
elif test -z "$left" |
|
then |
|
left=$1 |
|
elif test -z "$right" |
|
then |
|
right=$1 |
|
else |
|
paths="$paths$1 " |
|
fi |
|
;; |
|
esac |
|
;; |
|
esac |
|
shift |
|
done |
|
|
|
# Determine the set of files which changed |
|
if test -n "$left" && test -n "$right" |
|
then |
|
left_dir="cmt-$(git rev-parse --short $left)" |
|
right_dir="cmt-$(git rev-parse --short $right)" |
|
|
|
if test -n "$compare_staged" |
|
then |
|
usage |
|
elif test -n "$merge_base" |
|
then |
|
git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist" |
|
else |
|
git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist" |
|
fi |
|
elif test -n "$left" |
|
then |
|
left_dir="cmt-$(git rev-parse --short $left)" |
|
|
|
if test -n "$compare_staged" |
|
then |
|
right_dir="staged" |
|
git diff --name-only --cached "$left" -- $paths >"$tmp/filelist" |
|
else |
|
right_dir="working_tree" |
|
git diff --name-only "$left" -- $paths >"$tmp/filelist" |
|
fi |
|
else |
|
left_dir="HEAD" |
|
|
|
if test -n "$compare_staged" |
|
then |
|
right_dir="staged" |
|
git diff --name-only --cached -- $paths >"$tmp/filelist" |
|
else |
|
right_dir="working_tree" |
|
git diff --name-only -- $paths >"$tmp/filelist" |
|
fi |
|
fi |
|
|
|
# Exit immediately if there are no diffs |
|
if test ! -s "$tmp/filelist" |
|
then |
|
exit 0 |
|
fi |
|
|
|
if test -n "$copy_back" && test "$right_dir" != "working_tree" |
|
then |
|
echo "--copy-back is only valid when diff includes the working tree." |
|
exit 1 |
|
fi |
|
|
|
# Create the named tmp directories that will hold the files to be compared |
|
mkdir -p "$tmp/$left_dir" "$tmp/$right_dir" |
|
|
|
# Populate the tmp/right_dir directory with the files to be compared |
|
while read name |
|
do |
|
if test -n "$right" |
|
then |
|
ls_list=$(git ls-tree $right "$name") |
|
if test -n "$ls_list" |
|
then |
|
mkdir -p "$tmp/$right_dir/$(dirname "$name")" |
|
git show "$right":"$name" >"$tmp/$right_dir/$name" || true |
|
fi |
|
elif test -n "$compare_staged" |
|
then |
|
ls_list=$(git ls-files -- "$name") |
|
if test -n "$ls_list" |
|
then |
|
mkdir -p "$tmp/$right_dir/$(dirname "$name")" |
|
git show :"$name" >"$tmp/$right_dir/$name" |
|
fi |
|
else |
|
if test -e "$name" |
|
then |
|
mkdir -p "$tmp/$right_dir/$(dirname "$name")" |
|
cp "$name" "$tmp/$right_dir/$name" |
|
fi |
|
fi |
|
done < "$tmp/filelist" |
|
|
|
# Populate the tmp/left_dir directory with the files to be compared |
|
while read name |
|
do |
|
if test -n "$left" |
|
then |
|
ls_list=$(git ls-tree $left "$name") |
|
if test -n "$ls_list" |
|
then |
|
mkdir -p "$tmp/$left_dir/$(dirname "$name")" |
|
git show "$left":"$name" >"$tmp/$left_dir/$name" || true |
|
fi |
|
else |
|
if test -n "$compare_staged" |
|
then |
|
ls_list=$(git ls-tree HEAD "$name") |
|
if test -n "$ls_list" |
|
then |
|
mkdir -p "$tmp/$left_dir/$(dirname "$name")" |
|
git show HEAD:"$name" >"$tmp/$left_dir/$name" |
|
fi |
|
else |
|
mkdir -p "$tmp/$left_dir/$(dirname "$name")" |
|
git show :"$name" >"$tmp/$left_dir/$name" |
|
fi |
|
fi |
|
done < "$tmp/filelist" |
|
|
|
LOCAL="$tmp/$left_dir" |
|
REMOTE="$tmp/$right_dir" |
|
|
|
if test -n "$diff_tool" |
|
then |
|
export BASE |
|
eval $diff_tool '"$LOCAL"' '"$REMOTE"' |
|
else |
|
run_merge_tool "$merge_tool" false |
|
fi |
|
|
|
# Copy files back to the working dir, if requested |
|
if test -n "$copy_back" && test "$right_dir" = "working_tree" |
|
then |
|
cd "$start_dir" |
|
git_top_dir=$(git rev-parse --show-toplevel) |
|
find "$tmp/$right_dir" -type f | |
|
while read file |
|
do |
|
cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}" |
|
done |
|
fi
|
|
|