{"thread":{"id":"29757","subject":"[PATCH v4] contrib: added git-diffall","startedAt":"2012-02-24T19:48:57Z","lastAt":"2012-02-27T18:17:17Z","messageCount":2,"participants":["Tim Henigan","Junio C Hamano"],"isPatch":true,"patchVersion":4,"patchTotal":null},"messages":[{"id":"185520","messageId":"1330112937-7134-1-git-send-email-tim.henigan@gmail.com","threadId":"29757","inReplyTo":null,"subject":"[PATCH v4] contrib: added git-diffall","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-02-24T19:48:57Z","receivedAt":"2012-02-24T19:48:57Z","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 v4:\n  - Corrected location of hard-coded tmp dir\n  - Renamed 'path_sep' to 'dashdash_seen'\n  - Documented '--extcmd' in the README\n  - Changed test for '--extcmd=<command>' to use $#\n  - Documented that '--' is mandatory if a pathspec is given\n\n  v4 matches commit 251177b8e4 on GitHub [1].\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\n contrib/diffall/README      |   31 +++++\n contrib/diffall/git-diffall |  261 +++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 292 insertions(+), 0 deletions(-)\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..507f17d\n--- /dev/null\n+++ b/contrib/diffall/README\n@@ -0,0 +1,31 @@\n+The git-diffall script provides a directory based diff mechanism\n+for git.\n+\n+To determine what diff viewer is used, the script requires either\n+the 'diff.tool' or 'merge.tool' configuration option to be set.\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 commits\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+The '--extcmd=<command>' option allows the user to specify a custom\n+command for viewing diffs.  When given, configured defaults are\n+ignored and the script runs $command $LOCAL $REMOTE.  Additionally,\n+$BASE is set in the environment.\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..b9d51ca\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=/tmp/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+dashdash_seen=\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 $# == 1\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\tdashdash_seen=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 \"$dashdash_seen\"\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.1\n"},{"id":"185534","messageId":"7vipis9m4y.fsf@alter.siamese.dyndns.org","threadId":"29757","inReplyTo":"1330112937-7134-1-git-send-email-tim.henigan@gmail.com","subject":"Re: [PATCH v4] contrib: added git-diffall","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-27T18:17:17Z","receivedAt":"2012-02-27T18:17:17Z","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> +\t-x|--e|--ex|--ext|--extc|--extcm|--extcmd)\n> +\t\tif test $# == 1\n\nThat is not POSIX.\n\n\tif test $# = 1\n\nNo need to resend; I'll fix it up locally.\n"}]}