Browse Source
The 'git difftool' allows the user to view diffs using an external tool. It runs a separate instance of the tool for each file in the diff. This makes it tedious to review changes spanning multiple files. The 'git-diffall' script instead prepares temporary directories with the files to be compared and launches a single instance of the external diff tool to view them (i.e. a directory diff). The 'diff.tool' or 'merge.tool' configuration variable is used to specify which external tool is used. Signed-off-by: Tim Henigan <tim.henigan@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
Tim Henigan
13 years ago
committed by
Junio C Hamano
2 changed files with 292 additions and 0 deletions
@ -0,0 +1,31 @@ |
|||||||
|
The git-diffall script provides a directory based diff mechanism |
||||||
|
for git. |
||||||
|
|
||||||
|
To determine what diff viewer is used, the script requires either |
||||||
|
the 'diff.tool' or 'merge.tool' configuration option to be set. |
||||||
|
|
||||||
|
This script is compatible with most common forms used to specify a |
||||||
|
range of revisions to diff: |
||||||
|
|
||||||
|
1. git diffall: shows diff between working tree and staged changes |
||||||
|
2. git diffall --cached [<commit>]: shows diff between staged |
||||||
|
changes and HEAD (or other named commit) |
||||||
|
3. git diffall <commit>: shows diff between working tree and named |
||||||
|
commit |
||||||
|
4. git diffall <commit> <commit>: show diff between two named commits |
||||||
|
5. git diffall <commit>..<commit>: same as above |
||||||
|
6. git diffall <commit>...<commit>: show the changes on the branch |
||||||
|
containing and up to the second, starting at a common ancestor |
||||||
|
of both <commit> |
||||||
|
|
||||||
|
Note: all forms take an optional path limiter [-- <path>*] |
||||||
|
|
||||||
|
The '--extcmd=<command>' option allows the user to specify a custom |
||||||
|
command for viewing diffs. When given, configured defaults are |
||||||
|
ignored and the script runs $command $LOCAL $REMOTE. Additionally, |
||||||
|
$BASE is set in the environment. |
||||||
|
|
||||||
|
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 |
@ -0,0 +1,261 @@ |
|||||||
|
#!/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) |
||||||
|
|
||||||
|
# needed to access tar utility |
||||||
|
cdup=$(git rev-parse --show-cdup) && |
||||||
|
cd "$cdup" || { |
||||||
|
echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree" |
||||||
|
exit 1 |
||||||
|
} |
||||||
|
|
||||||
|
# mktemp is not available on all platforms (missing from msysgit) |
||||||
|
# Use a hard-coded tmp dir if it is not available |
||||||
|
tmp="$(mktemp -d -t tmp.XXXXXX 2>/dev/null)" || { |
||||||
|
tmp=/tmp/git-diffall-tmp.$$ |
||||||
|
mkdir "$tmp" || exit 1 |
||||||
|
} |
||||||
|
|
||||||
|
trap 'rm -rf "$tmp" 2>/dev/null' 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 |
||||||
|
if test -n "$right" |
||||||
|
then |
||||||
|
while read name |
||||||
|
do |
||||||
|
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 |
||||||
|
done < "$tmp/filelist" |
||||||
|
elif test -n "$compare_staged" |
||||||
|
then |
||||||
|
while read name |
||||||
|
do |
||||||
|
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 |
||||||
|
done < "$tmp/filelist" |
||||||
|
else |
||||||
|
# Mac users have gnutar rather than tar |
||||||
|
(tar --ignore-failed-read -c -T "$tmp/filelist" | (cd "$tmp/$right_dir" && tar -x)) || { |
||||||
|
gnutar --ignore-failed-read -c -T "$tmp/filelist" | (cd "$tmp/$right_dir" && gnutar -x) |
||||||
|
} |
||||||
|
fi |
||||||
|
|
||||||
|
# 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" |
||||||
|
|
||||||
|
cd "$tmp" |
||||||
|
LOCAL="$left_dir" |
||||||
|
REMOTE="$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 |
Loading…
Reference in new issue