* [PATCH v3] contrib: added git-diffall
@ 2012-02-24 0:49 Tim Henigan
2012-02-24 20:03 ` Junio C Hamano
0 siblings, 1 reply; 2+ messages in thread
From: Tim Henigan @ 2012-02-24 0:49 UTC (permalink / raw)
To: git, gitster; +Cc: davvid, stefano.lattarini, tim.henigan
The 'git difftool' command 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>
---
This script has been hosted on GitHub [1] since April 2010. Enough people
have found it useful that I hope it will be considered for inclusion in
the standard git install, either in contrib or as a new core command.
Changes in v3:
- Fixed a bug that caused failures if file names included spaces
- Added unique suffix to tmp dir name (tmp/git-diffall-tmp.$$)
- Renamed "common_ancestor" to "merge_base"
- Cleaned up README to be more accurate
- Added useful error message if --extcmd is final option, but no
command was specified
- Removed spaces after redirection operators
v3 matches commit f36e4881e5 on GitHub [1].
Changes in v2:
- Changed to #!/bin/sh
- Eliminated use of 'which' statements
- Fixed trap function to actually run on abnormal exit
- Simplified path concatenation logic ($IFS)
- Corrected indentation errors
- Improved readability of while loop
- Cleaned up quoting of variables
v2 matches commit 5d4b90de3 on GitHub [1].
[1]: https://github.com/thenigan/git-diffall
contrib/diffall/README | 24 ++++
contrib/diffall/git-diffall | 261 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 285 insertions(+)
create mode 100644 contrib/diffall/README
create mode 100755 contrib/diffall/git-diffall
diff --git a/contrib/diffall/README b/contrib/diffall/README
new file mode 100644
index 0000000..111f3f6
--- /dev/null
+++ b/contrib/diffall/README
@@ -0,0 +1,24 @@
+The git-diffall script provides a directory based diff mechanism
+for git. The script relies on the diff.tool configuration option
+to determine what diff viewer is used.
+
+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 commit
+ 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>*]
+
+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
diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall
new file mode 100755
index 0000000..e00fe89
--- /dev/null
+++ b/contrib/diffall/git-diffall
@@ -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=/git-diffall-tmp.$$
+ mkdir "$tmp" || exit 1
+}
+
+trap 'rm -rf "$tmp" 2>/dev/null' EXIT
+
+left=
+right=
+paths=
+path_sep=
+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 -z $2
+ then
+ echo You must specify the tool for use with --extcmd
+ usage
+ else
+ diff_tool=$2
+ shift
+ fi
+ ;;
+ --)
+ path_sep=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 "$path_sep"
+ 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
--
1.7.9.GIT
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v3] contrib: added git-diffall
2012-02-24 0:49 [PATCH v3] contrib: added git-diffall Tim Henigan
@ 2012-02-24 20:03 ` Junio C Hamano
0 siblings, 0 replies; 2+ messages in thread
From: Junio C Hamano @ 2012-02-24 20:03 UTC (permalink / raw)
To: Tim Henigan; +Cc: git, davvid, stefano.lattarini
Tim Henigan <tim.henigan@gmail.com> writes:
> The 'git difftool' command 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.
Shouldn't the primary interface "--extcmd=" also be advertised here?
> diff --git a/contrib/diffall/README b/contrib/diffall/README
> new file mode 100644
> index 0000000..111f3f6
> --- /dev/null
> +++ b/contrib/diffall/README
> @@ -0,0 +1,24 @@
> +The git-diffall script provides a directory based diff mechanism
> +for git. The script relies on the diff.tool configuration option
> +to determine what diff viewer is used.
Same here.
> +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 commit
I seem to recall the previous round said "named commits" here...
> + 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>*]
Doesn't 3. above make "--" mandatory if you give a pathspec? I.e.
... take an optional pathspec [-- <pathspec>]*
> diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall
> new file mode 100755
> index 0000000..e00fe89
> --- /dev/null
> +++ b/contrib/diffall/git-diffall
> @@ -0,0 +1,261 @@
> ...
> +# 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=/git-diffall-tmp.$$
Missing /tmp in front?
> + mkdir "$tmp" || exit 1
> +}
> + ...
> +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 -z $2
testing $# would be more logical, no?
> + then
> + echo You must specify the tool for use with --extcmd
> + usage
> + else
> + diff_tool=$2
> + shift
> + fi
> + ;;
> + --)
> + path_sep=1
The name path_sep makes one wonder "are we somehow talking about
differences between '/' vs '\' here?" In other parts of the system, this
is typically called "dashdash_seen"; the meaning of it is "expect nothing
but pathspecs from here on", so "expect_pathspec" could be an alternative
name for it.
> ...
> +elif test -n "$compare_staged"
> +then
> + while read name
> + do
> + ls_list=$(git ls-files -- "$name")
Ok, the use of $name is now slightly tightened with the surrounding dq
throughout the script. Better (even though $paths are still prone to
splitting by $IFS).
Thanks, will replace what was queued in 'pu'.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2012-02-24 20:03 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-24 0:49 [PATCH v3] contrib: added git-diffall Tim Henigan
2012-02-24 20:03 ` Junio C Hamano
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.