From 1252bbe1c685450ec76ac750e8b0c2e0b762f93f Mon Sep 17 00:00:00 2001 From: Tim Henigan Date: Fri, 24 Feb 2012 14:48:57 -0500 Subject: contrib: add git-diffall script 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 Signed-off-by: Junio C Hamano --- contrib/diffall/git-diffall | 261 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100755 contrib/diffall/git-diffall (limited to 'contrib/diffall/git-diffall') diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall new file mode 100755 index 0000000000..9bbd27f4a5 --- /dev/null +++ b/contrib/diffall/git-diffall @@ -0,0 +1,261 @@ +#!/bin/sh +# Copyright 2010 - 2012, Tim Henigan +# +# 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=] {0,2} [-- *] + + --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= + --extcmd= 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 -- cgit v1.2.1 From a22a9477fcd64a58fafc75d12320cda2c3ce9dbb Mon Sep 17 00:00:00 2001 From: Tim Henigan Date: Wed, 14 Mar 2012 12:38:02 -0400 Subject: contrib/diffall: comment actual reason for 'cdup' The comment from an earlier commit did not reflect the actual reason this operation is needed. Signed-off-by: Tim Henigan Signed-off-by: Junio C Hamano --- contrib/diffall/git-diffall | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib/diffall/git-diffall') diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall index 9bbd27f4a5..d706a6dee3 100755 --- a/contrib/diffall/git-diffall +++ b/contrib/diffall/git-diffall @@ -36,7 +36,9 @@ fi start_dir=$(pwd) -# needed to access tar utility +# 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" -- cgit v1.2.1 From c5770f79068fb1317c1fd19da7e7bfcc075132f3 Mon Sep 17 00:00:00 2001 From: Tim Henigan Date: Wed, 14 Mar 2012 12:38:03 -0400 Subject: contrib/diffall: create tmp dirs without mktemp mktemp is not available on all platforms. Instead of littering the code with a work-around, this commit replaces mktemp with a one-line Perl script. Signed-off-by: Tim Henigan Signed-off-by: Junio C Hamano --- contrib/diffall/git-diffall | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'contrib/diffall/git-diffall') diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall index d706a6dee3..443f646a77 100755 --- a/contrib/diffall/git-diffall +++ b/contrib/diffall/git-diffall @@ -45,13 +45,10 @@ cd "$cdup" || { 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 -} - +# 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" 2>/dev/null' EXIT left= -- cgit v1.2.1 From e33e01d07703d0c2c662c30e745dc93b543641c0 Mon Sep 17 00:00:00 2001 From: Tim Henigan Date: Wed, 14 Mar 2012 12:38:04 -0400 Subject: contrib/diffall: eliminate use of tar The 'tar' utility is not available on all platforms (some only support 'gnutar'). An earlier commit created a work-around for this problem, but a better solution is to eliminate the use of 'tar' completely. Signed-off-by: Tim Henigan Signed-off-by: Junio C Hamano --- contrib/diffall/git-diffall | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'contrib/diffall/git-diffall') diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall index 443f646a77..f981ac1bd3 100755 --- a/contrib/diffall/git-diffall +++ b/contrib/diffall/git-diffall @@ -202,10 +202,14 @@ then 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) - } + while read name + do + if test -e "$name" + then + mkdir -p "$tmp/$right_dir/$(dirname "$name")" + cp "$name" "$tmp/$right_dir/$name" + fi + done < "$tmp/filelist" fi # Populate the tmp/left_dir directory with the files to be compared -- cgit v1.2.1 From 97549084f660c43fe4b6dc34bf60afef4948b432 Mon Sep 17 00:00:00 2001 From: Tim Henigan Date: Wed, 14 Mar 2012 12:38:05 -0400 Subject: contrib/diffall: eliminate duplicate while loops There were 3 instances of a 'while read; do' that used identical logic to populate '/tmp/right_dir'. This commit groups them into a single loop. Signed-off-by: Tim Henigan Signed-off-by: Junio C Hamano --- contrib/diffall/git-diffall | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'contrib/diffall/git-diffall') diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall index f981ac1bd3..91a31c8780 100755 --- a/contrib/diffall/git-diffall +++ b/contrib/diffall/git-diffall @@ -179,38 +179,32 @@ fi 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 +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 - done < "$tmp/filelist" -elif test -n "$compare_staged" -then - while read name - do + 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 - done < "$tmp/filelist" -else - while read name - do + else if test -e "$name" then mkdir -p "$tmp/$right_dir/$(dirname "$name")" cp "$name" "$tmp/$right_dir/$name" fi - done < "$tmp/filelist" -fi + fi +done < "$tmp/filelist" # Populate the tmp/left_dir directory with the files to be compared while read name -- cgit v1.2.1 From bfe392e3674770e8a0312d4945b0a5f2c9007eab Mon Sep 17 00:00:00 2001 From: Tim Henigan Date: Wed, 14 Mar 2012 12:38:06 -0400 Subject: contrib/diffall: fix cleanup trap on Windows Prior to this commit, the cleanup trap that removes the tmp dir created by the script would fail on Windows. The error was silently ignored by the script. On Windows, a directory cannot be removed while it is the working directory of the process (thanks to Johannes Sixt on the Git list for this info [1]). This commit eliminates the 'cd' into the tmp directory that caused the error. [1]: http://article.gmane.org/gmane.comp.version-control.git/193086 Signed-off-by: Tim Henigan Signed-off-by: Junio C Hamano --- contrib/diffall/git-diffall | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'contrib/diffall/git-diffall') diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall index 91a31c8780..84f2b654d7 100755 --- a/contrib/diffall/git-diffall +++ b/contrib/diffall/git-diffall @@ -49,7 +49,7 @@ cd "$cdup" || { 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" 2>/dev/null' EXIT +trap 'rm -rf "$tmp"' EXIT left= right= @@ -233,9 +233,8 @@ do fi done < "$tmp/filelist" -cd "$tmp" -LOCAL="$left_dir" -REMOTE="$right_dir" +LOCAL="$tmp/$left_dir" +REMOTE="$tmp/$right_dir" if test -n "$diff_tool" then -- cgit v1.2.1