{"thread":{"id":"29716","subject":"[PATCH v3] contrib: added git-diffall","startedAt":"2012-02-24T00:49:41Z","lastAt":"2012-02-24T20:03:32Z","messageCount":2,"participants":["Tim Henigan","Junio C Hamano"],"isPatch":true,"patchVersion":3,"patchTotal":null},"messages":[{"id":"185309","messageId":"1330044581-792-1-git-send-email-tim.henigan@gmail.com","threadId":"29716","inReplyTo":null,"subject":"[PATCH v3] contrib: added git-diffall","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-02-24T00:49:41Z","receivedAt":"2012-02-24T00:49:41Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"The 'git difftool' command allows the user to view diffs using an\nexternal tool.  It runs a separate instance of the tool for each\nfile in the diff. This makes it tedious to review changes spanning\nmultiple files.\n\nThe 'git-diffall' script instead prepares temporary directories\nwith the files to be compared and launches a single instance of\nthe external diff tool to view them (i.e. a directory diff).\n\nThe 'diff.tool' or 'merge.tool' configuration variable is used\nto specify which external tool is used.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n\nThis script has been hosted on GitHub [1] since April 2010. Enough people\nhave found it useful that I hope it will be considered for inclusion in\nthe standard git install, either in contrib or as a new core command.\n\nChanges in v3:\n    - Fixed a bug that caused failures if file names included spaces\n    - Added unique suffix to tmp dir name (tmp/git-diffall-tmp.$$)\n    - Renamed \"common_ancestor\" to \"merge_base\"\n    - Cleaned up README to be more accurate\n    - Added useful error message if --extcmd is final option, but no\n      command was specified\n    - Removed spaces after redirection operators\n\n    v3 matches commit f36e4881e5 on GitHub [1].\n\nChanges in v2:\n    - Changed to #!/bin/sh\n    - Eliminated use of 'which' statements\n    - Fixed trap function to actually run on abnormal exit\n    - Simplified path concatenation logic ($IFS)\n    - Corrected indentation errors\n    - Improved readability of while loop\n    - Cleaned up quoting of variables\n\n    v2 matches commit 5d4b90de3 on GitHub [1].\n\n[1]: https://github.com/thenigan/git-diffall\n\n contrib/diffall/README      |   24 ++++\n contrib/diffall/git-diffall |  261 +++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 285 insertions(+)\n create mode 100644 contrib/diffall/README\n create mode 100755 contrib/diffall/git-diffall\n\ndiff --git a/contrib/diffall/README b/contrib/diffall/README\nnew file mode 100644\nindex 0000000..111f3f6\n--- /dev/null\n+++ b/contrib/diffall/README\n@@ -0,0 +1,24 @@\n+The git-diffall script provides a directory based diff mechanism\n+for git.  The script relies on the diff.tool configuration option\n+to determine what diff viewer is used.\n+\n+This script is compatible with most common forms used to specify a\n+range of revisions to diff:\n+\n+  1. git diffall: shows diff between working tree and staged changes\n+  2. git diffall --cached [<commit>]: shows diff between staged\n+     changes and HEAD (or other named commit)\n+  3. git diffall <commit>: shows diff between working tree and named\n+     commit\n+  4. git diffall <commit> <commit>: show diff between two named commit\n+  5. git diffall <commit>..<commit>: same as above\n+  6. git diffall <commit>...<commit>: show the changes on the branch\n+     containing and up to the second , starting at a common ancestor\n+     of both <commit>\n+\n+Note: all forms take an optional path limiter [--] [<path>*]\n+\n+This script is based on an example provided by Thomas Rast on the\n+Git list [1]:\n+\n+[1] http://thread.gmane.org/gmane.comp.version-control.git/124807\ndiff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall\nnew file mode 100755\nindex 0000000..e00fe89\n--- /dev/null\n+++ b/contrib/diffall/git-diffall\n@@ -0,0 +1,261 @@\n+#!/bin/sh\n+# Copyright 2010 - 2012, Tim Henigan <tim.henigan@gmail.com>\n+#\n+# Perform a directory diff between commits in the repository using\n+# the external diff or merge tool specified in the user's config.\n+\n+USAGE='[--cached] [--copy-back] [-x|--extcmd=<command>] <commit>{0,2} -- <path>*\n+\n+    --cached     Compare to the index rather than the working tree.\n+\n+    --copy-back  Copy files back to the working tree when the diff\n+                 tool exits (in case they were modified by the\n+                 user).  This option is only valid if the diff\n+                 compared with the working tree.\n+\n+    -x=<command>\n+    --extcmd=<command>  Specify a custom command for viewing diffs.\n+                 git-diffall ignores the configured defaults and\n+                 runs $command $LOCAL $REMOTE when this option is\n+                 specified. Additionally, $BASE is set in the\n+                 environment.\n+'\n+\n+SUBDIRECTORY_OK=1\n+. \"$(git --exec-path)/git-sh-setup\"\n+\n+TOOL_MODE=diff\n+. \"$(git --exec-path)/git-mergetool--lib\"\n+\n+merge_tool=\"$(get_merge_tool)\"\n+if test -z \"$merge_tool\"\n+then\n+\techo \"Error: Either the 'diff.tool' or 'merge.tool' option must be set.\"\n+\tusage\n+fi\n+\n+start_dir=$(pwd)\n+\n+# needed to access tar utility\n+cdup=$(git rev-parse --show-cdup) &&\n+cd \"$cdup\" || {\n+\techo >&2 \"Cannot chdir to $cdup, the toplevel of the working tree\"\n+\texit 1\n+}\n+\n+# mktemp is not available on all platforms (missing from msysgit)\n+# Use a hard-coded tmp dir if it is not available\n+tmp=\"$(mktemp -d -t tmp.XXXXXX 2>/dev/null)\" || {\n+\ttmp=/git-diffall-tmp.$$\n+\tmkdir \"$tmp\" || exit 1\n+}\n+\n+trap 'rm -rf \"$tmp\" 2>/dev/null' EXIT\n+\n+left=\n+right=\n+paths=\n+path_sep=\n+compare_staged=\n+merge_base=\n+left_dir=\n+right_dir=\n+diff_tool=\n+copy_back=\n+\n+while test $# != 0\n+do\n+\tcase \"$1\" in\n+\t-h|--h|--he|--hel|--help)\n+\t\tusage\n+\t\t;;\n+\t--cached)\n+\t\tcompare_staged=1\n+\t\t;;\n+\t--copy-back)\n+\t\tcopy_back=1\n+\t\t;;\n+\t-x|--e|--ex|--ext|--extc|--extcm|--extcmd)\n+\t\tif test -z $2\n+\t\tthen\n+\t\t\techo You must specify the tool for use with --extcmd\n+\t\t\tusage\n+\t\telse\n+\t\t\tdiff_tool=$2\n+\t\t\tshift\n+\t\tfi\n+\t\t;;\n+\t--)\n+\t\tpath_sep=1\n+\t\t;;\n+\t-*)\n+\t\techo Invalid option: \"$1\"\n+\t\tusage\n+\t\t;;\n+\t*)\n+\t\t# could be commit, commit range or path limiter\n+\t\tcase \"$1\" in\n+\t\t*...*)\n+\t\t\tleft=${1%...*}\n+\t\t\tright=${1#*...}\n+\t\t\tmerge_base=1\n+\t\t\t;;\n+\t\t*..*)\n+\t\t\tleft=${1%..*}\n+\t\t\tright=${1#*..}\n+\t\t\t;;\n+\t\t*)\n+\t\t\tif test -n \"$path_sep\"\n+\t\t\tthen\n+\t\t\t\tpaths=\"$paths$1 \"\n+\t\t\telif test -z \"$left\"\n+\t\t\tthen\n+\t\t\t\tleft=$1\n+\t\t\telif test -z \"$right\"\n+\t\t\tthen\n+\t\t\t\tright=$1\n+\t\t\telse\n+\t\t\t\tpaths=\"$paths$1 \"\n+\t\t\tfi\n+\t\t\t;;\n+\t\tesac\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n+# Determine the set of files which changed\n+if test -n \"$left\" && test -n \"$right\"\n+then\n+\tleft_dir=\"cmt-$(git rev-parse --short $left)\"\n+\tright_dir=\"cmt-$(git rev-parse --short $right)\"\n+\n+\tif test -n \"$compare_staged\"\n+\tthen\n+\t\tusage\n+\telif test -n \"$merge_base\"\n+\tthen\n+\t\tgit diff --name-only \"$left\"...\"$right\" -- $paths >\"$tmp/filelist\"\n+\telse\n+\t\tgit diff --name-only \"$left\" \"$right\" -- $paths >\"$tmp/filelist\"\n+\tfi\n+elif test -n \"$left\"\n+then\n+\tleft_dir=\"cmt-$(git rev-parse --short $left)\"\n+\n+\tif test -n \"$compare_staged\"\n+\tthen\n+\t\tright_dir=\"staged\"\n+\t\tgit diff --name-only --cached \"$left\" -- $paths >\"$tmp/filelist\"\n+\telse\n+\t\tright_dir=\"working_tree\"\n+\t\tgit diff --name-only \"$left\" -- $paths >\"$tmp/filelist\"\n+\tfi\n+else\n+\tleft_dir=\"HEAD\"\n+\n+\tif test -n \"$compare_staged\"\n+\tthen\n+\t\tright_dir=\"staged\"\n+\t\tgit diff --name-only --cached -- $paths >\"$tmp/filelist\"\n+\telse\n+\t\tright_dir=\"working_tree\"\n+\t\tgit diff --name-only -- $paths >\"$tmp/filelist\"\n+\tfi\n+fi\n+\n+# Exit immediately if there are no diffs\n+if test ! -s \"$tmp/filelist\"\n+then\n+\texit 0\n+fi\n+\n+if test -n \"$copy_back\" && test \"$right_dir\" != \"working_tree\"\n+then\n+\techo \"--copy-back is only valid when diff includes the working tree.\"\n+\texit 1\n+fi\n+\n+# Create the named tmp directories that will hold the files to be compared\n+mkdir -p \"$tmp/$left_dir\" \"$tmp/$right_dir\"\n+\n+# Populate the tmp/right_dir directory with the files to be compared\n+if test -n \"$right\"\n+then\n+\twhile read name\n+\tdo\n+\t\tls_list=$(git ls-tree $right \"$name\")\n+\t\tif test -n \"$ls_list\"\n+\t\tthen\n+\t\t\tmkdir -p \"$tmp/$right_dir/$(dirname \"$name\")\"\n+\t\t\tgit show \"$right\":\"$name\" >\"$tmp/$right_dir/$name\" || true\n+\t\tfi\n+\tdone < \"$tmp/filelist\"\n+elif test -n \"$compare_staged\"\n+then\n+\twhile read name\n+\tdo\n+\t\tls_list=$(git ls-files -- \"$name\")\n+\t\tif test -n \"$ls_list\"\n+\t\tthen\n+\t\t\tmkdir -p \"$tmp/$right_dir/$(dirname \"$name\")\"\n+\t\t\tgit show :\"$name\" >\"$tmp/$right_dir/$name\"\n+\t\tfi\n+\tdone < \"$tmp/filelist\"\n+else\n+\t# Mac users have gnutar rather than tar\n+\t(tar --ignore-failed-read -c -T \"$tmp/filelist\" | (cd \"$tmp/$right_dir\" && tar -x)) || {\n+\t\tgnutar --ignore-failed-read -c -T \"$tmp/filelist\" | (cd \"$tmp/$right_dir\" && gnutar -x)\n+\t}\n+fi\n+\n+# Populate the tmp/left_dir directory with the files to be compared\n+while read name\n+do\n+\tif test -n \"$left\"\n+\tthen\n+\t\tls_list=$(git ls-tree $left \"$name\")\n+\t\tif test -n \"$ls_list\"\n+\t\tthen\n+\t\t\tmkdir -p \"$tmp/$left_dir/$(dirname \"$name\")\"\n+\t\t\tgit show \"$left\":\"$name\" >\"$tmp/$left_dir/$name\" || true\n+\t\tfi\n+\telse\n+\t\tif test -n \"$compare_staged\"\n+\t\tthen\n+\t\t\tls_list=$(git ls-tree HEAD \"$name\")\n+\t\t\tif test -n \"$ls_list\"\n+\t\t\tthen\n+\t\t\t\tmkdir -p \"$tmp/$left_dir/$(dirname \"$name\")\"\n+\t\t\t\tgit show HEAD:\"$name\" >\"$tmp/$left_dir/$name\"\n+\t\t\tfi\n+\t\telse\n+\t\t\tmkdir -p \"$tmp/$left_dir/$(dirname \"$name\")\"\n+\t\t\tgit show :\"$name\" >\"$tmp/$left_dir/$name\"\n+\t\tfi\n+\tfi\n+done < \"$tmp/filelist\"\n+\n+cd \"$tmp\"\n+LOCAL=\"$left_dir\"\n+REMOTE=\"$right_dir\"\n+\n+if test -n \"$diff_tool\"\n+then\n+\texport BASE\n+\teval $diff_tool '\"$LOCAL\"' '\"$REMOTE\"'\n+else\n+\trun_merge_tool \"$merge_tool\" false\n+fi\n+\n+# Copy files back to the working dir, if requested\n+if test -n \"$copy_back\" && test \"$right_dir\" = \"working_tree\"\n+then\n+\tcd \"$start_dir\"\n+\tgit_top_dir=$(git rev-parse --show-toplevel)\n+\tfind \"$tmp/$right_dir\" -type f |\n+\twhile read file\n+\tdo\n+\t\tcp \"$file\" \"$git_top_dir/${file#$tmp/$right_dir/}\"\n+\tdone\n+fi\n-- \n1.7.9.GIT\n"},{"id":"185356","messageId":"7vzkc8xaln.fsf@alter.siamese.dyndns.org","threadId":"29716","inReplyTo":"1330044581-792-1-git-send-email-tim.henigan@gmail.com","subject":"Re: [PATCH v3] contrib: added git-diffall","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-24T20:03:32Z","receivedAt":"2012-02-24T20:03:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tim Henigan <tim.henigan@gmail.com> writes:\n\n> The 'git difftool' command allows the user to view diffs using an\n> external tool.  It runs a separate instance of the tool for each\n> file in the diff. This makes it tedious to review changes spanning\n> multiple files.\n>\n> The 'git-diffall' script instead prepares temporary directories\n> with the files to be compared and launches a single instance of\n> the external diff tool to view them (i.e. a directory diff).\n>\n> The 'diff.tool' or 'merge.tool' configuration variable is used\n> to specify which external tool is used.\n\nShouldn't the primary interface \"--extcmd=\" also be advertised here?\n\n> diff --git a/contrib/diffall/README b/contrib/diffall/README\n> new file mode 100644\n> index 0000000..111f3f6\n> --- /dev/null\n> +++ b/contrib/diffall/README\n> @@ -0,0 +1,24 @@\n> +The git-diffall script provides a directory based diff mechanism\n> +for git.  The script relies on the diff.tool configuration option\n> +to determine what diff viewer is used.\n\nSame here.\n\n> +This script is compatible with most common forms used to specify a\n> +range of revisions to diff:\n> +\n> +  1. git diffall: shows diff between working tree and staged changes\n> +  2. git diffall --cached [<commit>]: shows diff between staged\n> +     changes and HEAD (or other named commit)\n> +  3. git diffall <commit>: shows diff between working tree and named\n> +     commit\n> +  4. git diffall <commit> <commit>: show diff between two named commit\n\nI seem to recall the previous round said \"named commits\" here...\n\n> +  5. git diffall <commit>..<commit>: same as above\n> +  6. git diffall <commit>...<commit>: show the changes on the branch\n> +     containing and up to the second , starting at a common ancestor\n> +     of both <commit>\n> +\n> +Note: all forms take an optional path limiter [--] [<path>*]\n\nDoesn't 3. above make \"--\" mandatory if you give a pathspec?  I.e.\n\n\t... take an optional pathspec [-- <pathspec>]*\n\n> diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall\n> new file mode 100755\n> index 0000000..e00fe89\n> --- /dev/null\n> +++ b/contrib/diffall/git-diffall\n> @@ -0,0 +1,261 @@\n> ...\n> +# mktemp is not available on all platforms (missing from msysgit)\n> +# Use a hard-coded tmp dir if it is not available\n> +tmp=\"$(mktemp -d -t tmp.XXXXXX 2>/dev/null)\" || {\n> +\ttmp=/git-diffall-tmp.$$\n\nMissing /tmp in front?\n\n> +\tmkdir \"$tmp\" || exit 1\n> +}\n> + ...\n> +while test $# != 0\n> +do\n> +\tcase \"$1\" in\n> +\t-h|--h|--he|--hel|--help)\n> +\t\tusage\n> +\t\t;;\n> +\t--cached)\n> +\t\tcompare_staged=1\n> +\t\t;;\n> +\t--copy-back)\n> +\t\tcopy_back=1\n> +\t\t;;\n> +\t-x|--e|--ex|--ext|--extc|--extcm|--extcmd)\n> +\t\tif test -z $2\n\ntesting $# would be more logical, no?\n\n> +\t\tthen\n> +\t\t\techo You must specify the tool for use with --extcmd\n> +\t\t\tusage\n> +\t\telse\n> +\t\t\tdiff_tool=$2\n> +\t\t\tshift\n> +\t\tfi\n> +\t\t;;\n> +\t--)\n> +\t\tpath_sep=1\n\nThe name path_sep makes one wonder \"are we somehow talking about\ndifferences between '/' vs '\\' here?\"  In other parts of the system, this\nis typically called \"dashdash_seen\"; the meaning of it is \"expect nothing\nbut pathspecs from here on\", so \"expect_pathspec\" could be an alternative\nname for it.\n\n> ...\n> +elif test -n \"$compare_staged\"\n> +then\n> +\twhile read name\n> +\tdo\n> +\t\tls_list=$(git ls-files -- \"$name\")\n\nOk, the use of $name is now slightly tightened with the surrounding dq\nthroughout the script.  Better (even though $paths are still prone to\nsplitting by $IFS).\n\nThanks, will replace what was queued in 'pu'.\n"}]}