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 @@
@@ -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 @@
@@ -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