{"thread":{"id":"30029","subject":"[PATCH 0/9 v6] difftool: teach command to perform directory diffs","startedAt":"2012-03-22T19:52:15Z","lastAt":"2012-03-22T19:52:24Z","messageCount":10,"participants":["Tim Henigan"],"isPatch":true,"patchVersion":6,"patchTotal":9},"messages":[{"id":"187510","messageId":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":null,"subject":"[PATCH 0/9 v6] difftool: teach command to perform directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:15Z","receivedAt":"2012-03-22T19:52:15Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"Changes in v6:\n  - Rebased series to insure Getopt::Long worked correctly from the start.\n  - Squashed commits that added tests into the commits that added the\n    new features.\n\nSeries Overview:\n\n'git difftool' is a very useful command that allows git diffs to be opened\nin an external tool. Currently, difftool opens a separate instance of the\nexternal tool for each file that changed.  This can be tedious when many\nfiles have changed.\n\nThis series teaches difftool to perform directory diffs, so that all file\nchanges can be opened/reviewed in a single instance of the external tool.\n\nThis is the second phase of development for this feature.  The first phase\nwas added as a separate command (git diffall) in 1252bbe (contrib: add\ngit-diffall script).  During review of that script on the Git developers\nlist, an informal development roadmap was suggested [1]. The next phase\nof that plan is to integrate the 'git-diffall' feature into 'difftool'.\nThis series gets that done.\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/191297/focus=191383\n\n\nTim Henigan (9):\n  difftool: parse options using Getopt::Long\n  difftool: add '--no-gui' option\n  difftool: exit(0) when usage is printed\n  difftool: remove explicit change of PATH\n  difftool: stop appending '.exe' to git\n  difftool: eliminate setup_environment function\n  difftool: replace system call with Git::command_noisy\n  difftool: teach difftool to handle directory diffs\n  difftool: print list of valid tools with '--tool-help'\n\n Documentation/git-difftool.txt |   17 ++-\n git-difftool--helper.sh        |   19 ++-\n git-difftool.perl              |  264 +++++++++++++++++++++++++++-------------\n t/t7800-difftool.sh            |   57 +++++++++\n 4 files changed, 262 insertions(+), 95 deletions(-)\n\n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187511","messageId":"1332445944-10944-2-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 1/9 v6] difftool: parse options using Getopt::Long","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:16Z","receivedAt":"2012-03-22T19:52:16Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"Replace custom option/argument parser with standard Getopt::Long\nmodule.  This shortens the code and makes it easier to understand.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n\nChanges in v6:\n  - Patch 10/9 from the previous version of the series (difftool: fix\n    regression in '--prompt' options) has been squashed into this commit.\n    Now the '--[no-]prompt' options are properly handled from the start.\n\n\n git-difftool.perl |  111 ++++++++++++++++++++++-------------------------------\n 1 file changed, 46 insertions(+), 65 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 09b65f1..f4e28a6 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -15,17 +15,15 @@ use strict;\n use warnings;\n use Cwd qw(abs_path);\n use File::Basename qw(dirname);\n-\n-require Git;\n-\n-my $DIR = abs_path(dirname($0));\n-\n+use Getopt::Long qw(:config pass_through);\n+use Git;\n \n sub usage\n {\n \tprint << 'USAGE';\n-usage: git difftool [-t|--tool=<tool>] [-x|--extcmd=<cmd>]\n-                    [-y|--no-prompt]   [-g|--gui]\n+usage: git difftool [-t|--tool=<tool>]\n+                    [-x|--extcmd=<cmd>] [-g|--gui]\n+                    [--prompt] [-y|--no-prompt]\n                     ['git diff' options]\n USAGE\n \texit 1;\n@@ -33,6 +31,7 @@ USAGE\n \n sub setup_environment\n {\n+\tmy $DIR = abs_path(dirname($0));\n \t$ENV{PATH} = \"$DIR:$ENV{PATH}\";\n \t$ENV{GIT_PAGER} = '';\n \t$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n@@ -47,75 +46,57 @@ sub exe\n \treturn $exe;\n }\n \n-sub generate_command\n-{\n-\tmy @command = (exe('git'), 'diff');\n-\tmy $skip_next = 0;\n-\tmy $idx = -1;\n-\tmy $prompt = '';\n-\tfor my $arg (@ARGV) {\n-\t\t$idx++;\n-\t\tif ($skip_next) {\n-\t\t\t$skip_next = 0;\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($arg eq '-t' || $arg eq '--tool') {\n-\t\t\tusage() if $#ARGV <= $idx;\n-\t\t\t$ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];\n-\t\t\t$skip_next = 1;\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($arg =~ /^--tool=/) {\n-\t\t\t$ENV{GIT_DIFF_TOOL} = substr($arg, 7);\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($arg eq '-x' || $arg eq '--extcmd') {\n-\t\t\tusage() if $#ARGV <= $idx;\n-\t\t\t$ENV{GIT_DIFFTOOL_EXTCMD} = $ARGV[$idx + 1];\n-\t\t\t$skip_next = 1;\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($arg =~ /^--extcmd=/) {\n-\t\t\t$ENV{GIT_DIFFTOOL_EXTCMD} = substr($arg, 9);\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($arg eq '-g' || $arg eq '--gui') {\n-\t\t\teval {\n-\t\t\t\tmy $tool = Git::command_oneline('config',\n-\t\t\t\t                                'diff.guitool');\n-\t\t\t\tif (length($tool)) {\n-\t\t\t\t\t$ENV{GIT_DIFF_TOOL} = $tool;\n-\t\t\t\t}\n-\t\t\t};\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($arg eq '-y' || $arg eq '--no-prompt') {\n-\t\t\t$prompt = 'no';\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($arg eq '--prompt') {\n-\t\t\t$prompt = 'yes';\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($arg eq '-h') {\n-\t\t\tusage();\n-\t\t}\n-\t\tpush @command, $arg;\n+# parse command-line options. all unrecognized options and arguments\n+# are passed through to the 'git diff' command.\n+my ($difftool_cmd, $extcmd, $gui, $help, $prompt);\n+GetOptions('g|gui' => \\$gui,\n+\t'h' => \\$help,\n+\t'prompt!' => \\$prompt,\n+\t'y' => sub { $prompt = 0; },\n+\t't|tool:s' => \\$difftool_cmd,\n+\t'x|extcmd:s' => \\$extcmd);\n+\n+if (defined($help)) {\n+\tusage();\n+} \n+if (defined($difftool_cmd)) {\n+\tif (length($difftool_cmd) > 0) {\n+\t\t$ENV{GIT_DIFF_TOOL} = $difftool_cmd;\n+\t} else {\n+\t\tprint \"No <tool> given for --tool=<tool>\\n\";\n+\t\tusage();\n \t}\n-\tif ($prompt eq 'yes') {\n+}\n+if (defined($extcmd)) {\n+\tif (length($extcmd) > 0) {\n+\t\t$ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;\n+\t} else {\n+\t\tprint \"No <cmd> given for --extcmd=<cmd>\\n\";\n+\t\tusage();\n+\t}\n+}\n+if (defined($gui)) {\n+\tmy $guitool = \"\";\n+\t$guitool = Git::config('diff.guitool');\n+\tif (length($guitool) > 0) {\n+\t\t$ENV{GIT_DIFF_TOOL} = $guitool;\n+\t}\n+}\n+if (defined($prompt)) {\n+\tif ($prompt) {\n \t\t$ENV{GIT_DIFFTOOL_PROMPT} = 'true';\n-\t} elsif ($prompt eq 'no') {\n+\t} else {\n \t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n \t}\n-\treturn @command\n }\n \n setup_environment();\n+my @command = (exe('git'), 'diff', @ARGV);\n \n # ActiveState Perl for Win32 does not implement POSIX semantics of\n # exec* system call. It just spawns the given executable and finishes\n # the starting program, exiting with code 0.\n # system will at least catch the errors returned by git diff,\n # allowing the caller of git difftool better handling of failures.\n-my $rc = system(generate_command());\n+my $rc = system(@command);\n exit($rc | ($rc >> 8));\n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187512","messageId":"1332445944-10944-3-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 2/9 v6] difftool: add '--no-gui' option","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:17Z","receivedAt":"2012-03-22T19:52:17Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"This commit teaches difftool to handle the '--no-gui' option. This option\nnegates the existing '--gui' option. The last setting given on the command\nline wins.\n\nThis allows a user to configure \"[alias] mdt = difftool --gui\", but still\nhave the ability to override the setting without error:\n\n$ git mdt --no-gui\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n\nChanges in v6:\n  - This was patch 13/9 in the previous version of the series. It has\n    simply been moved to 2/9.\n\n\n git-difftool.perl   |    7 ++++---\n t/t7800-difftool.sh |   13 +++++++++++++\n 2 files changed, 17 insertions(+), 3 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex f4e28a6..bf51b79 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -22,7 +22,8 @@ sub usage\n {\n \tprint << 'USAGE';\n usage: git difftool [-t|--tool=<tool>]\n-                    [-x|--extcmd=<cmd>] [-g|--gui]\n+                    [-x|--extcmd=<cmd>]\n+                    [-g|--gui] [--no-gui]\n                     [--prompt] [-y|--no-prompt]\n                     ['git diff' options]\n USAGE\n@@ -49,7 +50,7 @@ sub exe\n # parse command-line options. all unrecognized options and arguments\n # are passed through to the 'git diff' command.\n my ($difftool_cmd, $extcmd, $gui, $help, $prompt);\n-GetOptions('g|gui' => \\$gui,\n+GetOptions('g|gui!' => \\$gui,\n \t'h' => \\$help,\n \t'prompt!' => \\$prompt,\n \t'y' => sub { $prompt = 0; },\n@@ -75,7 +76,7 @@ if (defined($extcmd)) {\n \t\tusage();\n \t}\n }\n-if (defined($gui)) {\n+if ($gui) {\n \tmy $guitool = \"\";\n \t$guitool = Git::config('diff.guitool');\n \tif (length($guitool) > 0) {\ndiff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh\nindex 4fb4c93..e716d06 100755\n--- a/t/t7800-difftool.sh\n+++ b/t/t7800-difftool.sh\n@@ -94,6 +94,19 @@ test_expect_success PERL 'difftool honors --gui' '\n \trestore_test_defaults\n '\n \n+test_expect_success PERL 'difftool --gui last setting wins' '\n+\tgit config diff.guitool bogus-tool &&\n+\tgit difftool --no-prompt --gui --no-gui &&\n+\n+\tgit config merge.tool bogus-tool &&\n+\tgit config diff.tool bogus-tool &&\n+\tgit config diff.guitool test-tool &&\n+\tdiff=$(git difftool --no-prompt --no-gui --gui branch) &&\n+\ttest \"$diff\" = \"branch\" &&\n+\n+\trestore_test_defaults\n+'\n+\n test_expect_success PERL 'difftool --gui works without configured diff.guitool' '\n \tgit config diff.tool test-tool &&\n \n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187513","messageId":"1332445944-10944-4-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 3/9 v6] difftool: exit(0) when usage is printed","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:18Z","receivedAt":"2012-03-22T19:52:18Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"Prior to this commit, the script exited with an error whenever the\nusage string was printed, regardless of the reason it was done. In\ncases where usage was printed due to a user request (e.g. '-h'\noption), the script should exit without error (exit 0).\n\nThis commit adds an argument to the usage function that allows the\nexit code to be specified when the function is called.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n git-difftool.perl |    9 +++++----\n 1 file changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex bf51b79..33ae30a 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -20,6 +20,7 @@ use Git;\n \n sub usage\n {\n+\tmy $exitcode = shift;\n \tprint << 'USAGE';\n usage: git difftool [-t|--tool=<tool>]\n                     [-x|--extcmd=<cmd>]\n@@ -27,7 +28,7 @@ usage: git difftool [-t|--tool=<tool>]\n                     [--prompt] [-y|--no-prompt]\n                     ['git diff' options]\n USAGE\n-\texit 1;\n+\texit($exitcode);\n }\n \n sub setup_environment\n@@ -58,14 +59,14 @@ GetOptions('g|gui!' => \\$gui,\n \t'x|extcmd:s' => \\$extcmd);\n \n if (defined($help)) {\n-\tusage();\n+\tusage(0);\n } \n if (defined($difftool_cmd)) {\n \tif (length($difftool_cmd) > 0) {\n \t\t$ENV{GIT_DIFF_TOOL} = $difftool_cmd;\n \t} else {\n \t\tprint \"No <tool> given for --tool=<tool>\\n\";\n-\t\tusage();\n+\t\tusage(1);\n \t}\n }\n if (defined($extcmd)) {\n@@ -73,7 +74,7 @@ if (defined($extcmd)) {\n \t\t$ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;\n \t} else {\n \t\tprint \"No <cmd> given for --extcmd=<cmd>\\n\";\n-\t\tusage();\n+\t\tusage(1);\n \t}\n }\n if ($gui) {\n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187514","messageId":"1332445944-10944-5-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 4/9 v6] difftool: remove explicit change of PATH","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:19Z","receivedAt":"2012-03-22T19:52:19Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"Adding the script directory to PATH is not needed. The script is\nlocated at '$(git --exec-path)', which is already on the PATH.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n git-difftool.perl |    4 ----\n 1 file changed, 4 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 33ae30a..77c9907 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -13,8 +13,6 @@\n use 5.008;\n use strict;\n use warnings;\n-use Cwd qw(abs_path);\n-use File::Basename qw(dirname);\n use Getopt::Long qw(:config pass_through);\n use Git;\n \n@@ -33,8 +31,6 @@ USAGE\n \n sub setup_environment\n {\n-\tmy $DIR = abs_path(dirname($0));\n-\t$ENV{PATH} = \"$DIR:$ENV{PATH}\";\n \t$ENV{GIT_PAGER} = '';\n \t$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n }\n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187515","messageId":"1332445944-10944-6-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 5/9 v6] difftool: stop appending '.exe' to git","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:20Z","receivedAt":"2012-03-22T19:52:20Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"The system call to Git works the same whether or not \".exe\" is\nappended to \"git\". The extra code is not necessary.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n git-difftool.perl |   11 +----------\n 1 file changed, 1 insertion(+), 10 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 77c9907..493260d 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -35,15 +35,6 @@ sub setup_environment\n \t$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n }\n \n-sub exe\n-{\n-\tmy $exe = shift;\n-\tif ($^O eq 'MSWin32' || $^O eq 'msys') {\n-\t\treturn \"$exe.exe\";\n-\t}\n-\treturn $exe;\n-}\n-\n # parse command-line options. all unrecognized options and arguments\n # are passed through to the 'git diff' command.\n my ($difftool_cmd, $extcmd, $gui, $help, $prompt);\n@@ -89,7 +80,7 @@ if (defined($prompt)) {\n }\n \n setup_environment();\n-my @command = (exe('git'), 'diff', @ARGV);\n+my @command = ('git', 'diff', @ARGV);\n \n # ActiveState Perl for Win32 does not implement POSIX semantics of\n # exec* system call. It just spawns the given executable and finishes\n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187516","messageId":"1332445944-10944-7-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 6/9 v6] difftool: eliminate setup_environment function","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:21Z","receivedAt":"2012-03-22T19:52:21Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"Removing this function shortens the code and makes it easier to read.\nNow all environment variables are set as part of procedural operation.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n git-difftool.perl |    9 ++-------\n 1 file changed, 2 insertions(+), 7 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 493260d..d4fe998 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -29,12 +29,6 @@ USAGE\n \texit($exitcode);\n }\n \n-sub setup_environment\n-{\n-\t$ENV{GIT_PAGER} = '';\n-\t$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n-}\n-\n # parse command-line options. all unrecognized options and arguments\n # are passed through to the 'git diff' command.\n my ($difftool_cmd, $extcmd, $gui, $help, $prompt);\n@@ -79,7 +73,8 @@ if (defined($prompt)) {\n \t}\n }\n \n-setup_environment();\n+$ENV{GIT_PAGER} = '';\n+$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n my @command = ('git', 'diff', @ARGV);\n \n # ActiveState Perl for Win32 does not implement POSIX semantics of\n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187517","messageId":"1332445944-10944-8-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 7/9 v6] difftool: replace system call with Git::command_noisy","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:22Z","receivedAt":"2012-03-22T19:52:22Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"The Git.pm module includes functions intended to standardize working\nwith Git repositories in Perl scripts. This commit teaches difftool\nto use Git::command_noisy rather than a system call to run the diff\ncommand.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n git-difftool.perl |   10 +---------\n 1 file changed, 1 insertion(+), 9 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex d4fe998..7366bf5 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -75,12 +75,4 @@ if (defined($prompt)) {\n \n $ENV{GIT_PAGER} = '';\n $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n-my @command = ('git', 'diff', @ARGV);\n-\n-# ActiveState Perl for Win32 does not implement POSIX semantics of\n-# exec* system call. It just spawns the given executable and finishes\n-# the starting program, exiting with code 0.\n-# system will at least catch the errors returned by git diff,\n-# allowing the caller of git difftool better handling of failures.\n-my $rc = system(@command);\n-exit($rc | ($rc >> 8));\n+git_cmd_try { Git::command_noisy(('diff', @ARGV)) } 'exit code %d';\n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187518","messageId":"1332445944-10944-9-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 8/9 v6] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:23Z","receivedAt":"2012-03-22T19:52:23Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"Prior to this commit, the difftool utility could only open files one\nat a time.  The new '--dir-diff' option copies all the modified files\nto a temporary location and runs a directory diff on them.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n\nChanges in v6:\n  - Squashed \"teach --dir-diff to copy modified files back\" into this\n    commit.\n  - Squashed \"add tests for difftool --dir-diff\" into this commit.\n\n\n Documentation/git-difftool.txt |    6 ++\n git-difftool--helper.sh        |   19 +++--\n git-difftool.perl              |  151 +++++++++++++++++++++++++++++++++++++---\n t/t7800-difftool.sh            |   39 +++++++++++\n 4 files changed, 198 insertions(+), 17 deletions(-)\n\ndiff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt\nindex fe38f66..aba5e76 100644\n--- a/Documentation/git-difftool.txt\n+++ b/Documentation/git-difftool.txt\n@@ -19,6 +19,12 @@ linkgit:git-diff[1].\n \n OPTIONS\n -------\n+-d::\n+--dir-diff::\n+\tCopy the modified files to a temporary location and perform\n+\ta directory diff on them. This mode never prompts before\n+\tlaunching the diff tool.\n+\n -y::\n --no-prompt::\n \tDo not prompt before launching a diff tool.\ndiff --git a/git-difftool--helper.sh b/git-difftool--helper.sh\nindex e6558d1..3d0fe0c 100755\n--- a/git-difftool--helper.sh\n+++ b/git-difftool--helper.sh\n@@ -73,9 +73,16 @@ then\n \tfi\n fi\n \n-# Launch the merge tool on each path provided by 'git diff'\n-while test $# -gt 6\n-do\n-\tlaunch_merge_tool \"$1\" \"$2\" \"$5\"\n-\tshift 7\n-done\n+if test -n \"$GIT_DIFFTOOL_DIRDIFF\"\n+then\n+\tLOCAL=\"$1\"\n+\tREMOTE=\"$2\"\n+\trun_merge_tool \"$merge_tool\" false\n+else\n+\t# Launch the merge tool on each path provided by 'git diff'\n+\twhile test $# -gt 6\n+\tdo\n+\t\tlaunch_merge_tool \"$1\" \"$2\" \"$5\"\n+\t\tshift 7\n+\tdone\n+fi\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 7366bf5..0fa131c 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -1,21 +1,29 @@\n #!/usr/bin/env perl\n # Copyright (c) 2009, 2010 David Aguilar\n+# Copyright (c) 2012 Tim Henigan\n #\n # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible\n # git-difftool--helper script.\n #\n # This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.\n-# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, and GIT_DIFF_TOOL\n-# are exported for use by git-difftool--helper.\n+# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, GIT_DIFFTOOL_DIRDIFF,\n+# and GIT_DIFF_TOOL are exported for use by git-difftool--helper.\n #\n # Any arguments that are unknown to this script are forwarded to 'git diff'.\n \n use 5.008;\n use strict;\n use warnings;\n+use File::Basename qw(dirname);\n+use File::Copy;\n+use File::stat;\n+use File::Path qw(mkpath);\n+use File::Temp qw(tempdir);\n use Getopt::Long qw(:config pass_through);\n use Git;\n \n+my @working_tree;\n+\n sub usage\n {\n \tmy $exitcode = shift;\n@@ -24,15 +32,109 @@ usage: git difftool [-t|--tool=<tool>]\n                     [-x|--extcmd=<cmd>]\n                     [-g|--gui] [--no-gui]\n                     [--prompt] [-y|--no-prompt]\n+                    [-d|--dir-diff]\n                     ['git diff' options]\n USAGE\n \texit($exitcode);\n }\n \n+sub setup_dir_diff\n+{\n+\t# Run the diff; exit immediately if no diff found\n+\tmy $repo = Git->repository();\n+\tmy $diffrtn = $repo->command_oneline(['diff', '--raw', '--no-abbrev', '-z', @ARGV]);\n+\texit(0) if (length($diffrtn) == 0);\n+\n+\t# Setup temp directories\n+\tmy $tmpdir = tempdir('git-diffall.XXXXX', CLEANUP => 1, TMPDIR => 1);\n+\tmy $ldir = \"$tmpdir/left\";\n+\tmy $rdir = \"$tmpdir/right\";\n+\tmkpath($ldir) or die $!;\n+\tmkpath($rdir) or die $!;\n+\n+\t# Build index info for left and right sides of the diff\n+\tmy $submodule_mode = \"160000\";\n+\tmy $null_mode = 0 x 6;\n+\tmy $null_sha1 = 0 x 40;\n+\tmy $lindex = \"\";\n+\tmy $rindex = \"\";\n+\tmy %submodule;\n+\tmy @rawdiff = split('\\0', $diffrtn);\n+\n+\tfor (my $i=0; $i<@rawdiff; $i+=2) {\n+\t\tmy ($lmode, $rmode, $lsha1, $rsha1, $status) = split(' ', substr($rawdiff[$i], 1));\n+\t\tmy $path = $rawdiff[$i + 1];\n+\n+\t\tif (($lmode eq $submodule_mode) or ($rmode eq $submodule_mode)) {\n+\t\t\t$submodule{$path}{left} = $lsha1;\n+\t\t\t$submodule{$path}{right} = $rsha1;\n+\t\t\tnext;\n+\t\t}\n+\n+\t\tif ($lmode ne $null_mode) {\n+\t\t\t$lindex .= \"$lmode $lsha1\\t$path\\0\";\n+\t\t}\n+\n+\t\tif ($rmode ne $null_mode) {\n+\t\t\tif ($rsha1 ne $null_sha1) {\n+\t\t\t\t$rindex .= \"$rmode $rsha1\\t$path\\0\";\n+\t\t\t} else {\n+\t\t\t\tpush(@working_tree, $path);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t# Populate the left and right directories based on each index file\n+\tmy ($inpipe, $ctx);\n+\t$ENV{GIT_DIR} = $repo->repo_path();\n+\t$ENV{GIT_INDEX_FILE} = \"$tmpdir/lindex\";\n+\t($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);\n+\tprint($inpipe $lindex);\n+\t$repo->command_close_pipe($inpipe, $ctx);\n+\tsystem(('git', 'checkout-index', '--all', \"--prefix=$ldir/\"));\n+\n+\t$ENV{GIT_INDEX_FILE} = \"$tmpdir/rindex\";\n+\t($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);\n+\tprint($inpipe $rindex);\n+\t$repo->command_close_pipe($inpipe, $ctx);\n+\tsystem(('git', 'checkout-index', '--all', \"--prefix=$rdir/\"));\n+\n+\t# Changes in the working tree need special treatment since they are\n+\t# not part of the index\n+\tmy $workdir = $repo->repo_path() . \"/..\";\n+\tfor (@working_tree) {\n+\t\tmy $dir = dirname($_);\n+\t\tunless (-d \"$rdir/$dir\") {\n+\t\t\tmkpath(\"$rdir/$dir\") or die $!;\n+\t\t}\n+\t\tcopy(\"$workdir/$_\", \"$rdir/$_\") or die $!;\n+\t\tchmod(stat(\"$workdir/$_\")->mode, \"$rdir/$_\") or die $!;\n+\t}\n+\n+\t# Changes to submodules require special treatment. This loop writes a\n+\t# temporary file to both the left and right directories to show the\n+\t# change in the recorded SHA1 for the submodule.\n+\tforeach my $path (keys %submodule) {\n+\t\tif (defined $submodule{$path}{left}) {\n+\t\t\topen(my $fh, \">\", \"$ldir/$path\") or die $!;\n+\t\t\tprint($fh \"Subproject commit $submodule{$path}{left}\");\n+\t\t\tclose($fh);\n+\t\t}\n+\t\tif (defined $submodule{$path}{right}) {\n+\t\t\topen(my $fh, \">\", \"$rdir/$path\") or die $!;\n+\t\t\tprint($fh \"Subproject commit $submodule{$path}{right}\");\n+\t\t\tclose($fh);\n+\t\t}\n+\t}\n+\n+\treturn ($ldir, $rdir);\n+}\n+\n # parse command-line options. all unrecognized options and arguments\n # are passed through to the 'git diff' command.\n-my ($difftool_cmd, $extcmd, $gui, $help, $prompt);\n+my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt);\n GetOptions('g|gui!' => \\$gui,\n+\t'd|dir-diff' => \\$dirdiff,\n \t'h' => \\$help,\n \t'prompt!' => \\$prompt,\n \t'y' => sub { $prompt = 0; },\n@@ -65,14 +167,41 @@ if ($gui) {\n \t\t$ENV{GIT_DIFF_TOOL} = $guitool;\n \t}\n }\n-if (defined($prompt)) {\n-\tif ($prompt) {\n-\t\t$ENV{GIT_DIFFTOOL_PROMPT} = 'true';\n+\n+# In directory diff mode, 'git-difftool--helper' is called once\n+# to compare the a/b directories.  In file diff mode, 'git diff'\n+# will invoke a separate instance of 'git-difftool--helper' for\n+# each file that changed.\n+if (defined($dirdiff)) {\n+\tmy ($a, $b) = setup_dir_diff();\n+\tif (defined($extcmd)) {\n+\t\tsystem(($extcmd, $a, $b));\n \t} else {\n-\t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n+\t\t$ENV{GIT_DIFFTOOL_DIRDIFF} = 'true';\n+\t\tgit_cmd_try {\n+\t\t\tGit::command_noisy(('difftool--helper', $a, $b))\n+\t\t} 'exit code %d';\n+\t}\n+\n+\t# If the diff including working copy files and those\n+\t# files were modified during the diff, then the changes\n+\t# should be copied back to the working tree\n+\tmy $repo = Git->repository();\n+\tmy $workdir = $repo->repo_path() . \"/..\";\n+\tfor (@working_tree) {\n+\t\tcopy(\"$b/$_\", \"$workdir/$_\") or die $!;\n+\t\tchmod(stat(\"$b/$_\")->mode, \"$workdir/$_\") or die $!;\n+\t}\n+} else {\n+\tif (defined($prompt)) {\n+\t\tif ($prompt) {\n+\t\t\t$ENV{GIT_DIFFTOOL_PROMPT} = 'true';\n+\t\t} else {\n+\t\t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n+\t\t}\n \t}\n-}\n \n-$ENV{GIT_PAGER} = '';\n-$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n-git_cmd_try { Git::command_noisy(('diff', @ARGV)) } 'exit code %d';\n+\t$ENV{GIT_PAGER} = '';\n+\t$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n+\tgit_cmd_try { Git::command_noisy(('diff', @ARGV)) } 'exit code %d';\n+}\ndiff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh\nindex e716d06..478c1be 100755\n--- a/t/t7800-difftool.sh\n+++ b/t/t7800-difftool.sh\n@@ -319,4 +319,43 @@ test_expect_success PERL 'say no to the second file' '\n \techo \"$diff\" | stdin_doesnot_contain br2\n '\n \n+test_expect_success PERL 'setup change in subdirectory' '\n+\tgit checkout master &&\n+\tmkdir sub &&\n+\techo master >sub/sub &&\n+\tgit add sub/sub &&\n+\tgit commit -m \"added sub/sub\" &&\n+\techo test >>file &&\n+\techo test >>sub/sub &&\n+\tgit add . &&\n+\tgit commit -m \"modified both\"\n+'\n+\n+test_expect_success PERL 'difftool -d' '\n+\tdiff=$(git difftool -d --extcmd ls branch) &&\n+\techo \"$diff\" | stdin_contains sub &&\n+\techo \"$diff\" | stdin_contains file\n+'\n+\n+test_expect_success PERL 'difftool --dir-diff' '\n+\tdiff=$(git difftool --dir-diff --extcmd ls branch) &&\n+\techo \"$diff\" | stdin_contains sub &&\n+\techo \"$diff\" | stdin_contains file\n+'\n+\n+test_expect_success PERL 'difftool --dir-diff ignores --prompt' '\n+\tdiff=$(git difftool --dir-diff --prompt --extcmd ls branch) &&\n+\techo \"$diff\" | stdin_contains sub &&\n+\techo \"$diff\" | stdin_contains file\n+'\n+\n+test_expect_success PERL 'difftool --dir-diff from subdirectory' '\n+\t(\n+\t\tcd sub &&\n+\t\tdiff=$(git difftool --dir-diff --extcmd ls branch) &&\n+\t\techo \"$diff\" | stdin_contains sub &&\n+\t\techo \"$diff\" | stdin_contains file\n+\t)\n+'\n+\n test_done\n-- \n1.7.10.rc1.36.g15e879\n"},{"id":"187519","messageId":"1332445944-10944-10-git-send-email-tim.henigan@gmail.com","threadId":"30029","inReplyTo":"1332445944-10944-1-git-send-email-tim.henigan@gmail.com","subject":"[PATCH 9/9 v6] difftool: print list of valid tools with '--tool-help'","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-22T19:52:24Z","receivedAt":"2012-03-22T19:52:24Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"Since bc7a96a (mergetool--lib: Refactor tools into separate files,\n2011-08-18), it is possible to add a new diff tool by creating a simple\nscript in the '$(git --exec-path)/mergetools' directory.  Updating the\ndifftool help text is still a manual process, and the documentation can\neasily go out of sync.\n\nTeach the command to read the list of valid tools from the 'mergetools'\ndirectory and print them for the user when the '--tool-help' option is\ngiven.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n\nChanges in v6:\n  - Squashed \"added test for difftool --tool-help\" into this commit.\n\n\n Documentation/git-difftool.txt |   11 ++++++-----\n git-difftool.perl              |   16 +++++++++++++---\n t/t7800-difftool.sh            |    5 +++++\n 3 files changed, 24 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt\nindex aba5e76..31fc2e3 100644\n--- a/Documentation/git-difftool.txt\n+++ b/Documentation/git-difftool.txt\n@@ -36,11 +36,9 @@ OPTIONS\n \n -t <tool>::\n --tool=<tool>::\n-\tUse the diff tool specified by <tool>.\n-\tValid diff tools are:\n-\taraxis, bc3, deltawalker, diffuse, emerge, ecmerge, gvimdiff,\n-\tkdiff3,\tkompare, meld, opendiff, p4merge, tkdiff, vimdiff and\n-\txxdiff.\n+\tUse the diff tool specified by <tool>.  Valid values include\n+\temerge, kompare, meld, and vimdiff. Run `git difftool --tool-help`\n+\tfor the list of valid <tool> settings.\n +\n If a diff tool is not specified, 'git difftool'\n will use the configuration variable `diff.tool`.  If the\n@@ -68,6 +66,9 @@ of the diff post-image.  `$MERGED` is the name of the file which is\n being compared. `$BASE` is provided for compatibility\n with custom merge tool commands and has the same value as `$MERGED`.\n \n+--tool-help::\n+\tPrint a list of diff tools that may be used with `--tool`.\n+\n -x <command>::\n --extcmd=<command>::\n \tSpecify a custom command for viewing diffs.\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 0fa131c..d9be7d6 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -14,7 +14,7 @@\n use 5.008;\n use strict;\n use warnings;\n-use File::Basename qw(dirname);\n+use File::Basename qw(dirname basename);\n use File::Copy;\n use File::stat;\n use File::Path qw(mkpath);\n@@ -28,7 +28,7 @@ sub usage\n {\n \tmy $exitcode = shift;\n \tprint << 'USAGE';\n-usage: git difftool [-t|--tool=<tool>]\n+usage: git difftool [-t|--tool=<tool>] [--tool-help]\n                     [-x|--extcmd=<cmd>]\n                     [-g|--gui] [--no-gui]\n                     [--prompt] [-y|--no-prompt]\n@@ -132,18 +132,28 @@ sub setup_dir_diff\n \n # parse command-line options. all unrecognized options and arguments\n # are passed through to the 'git diff' command.\n-my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt);\n+my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt, $tool_help);\n GetOptions('g|gui!' => \\$gui,\n \t'd|dir-diff' => \\$dirdiff,\n \t'h' => \\$help,\n \t'prompt!' => \\$prompt,\n \t'y' => sub { $prompt = 0; },\n \t't|tool:s' => \\$difftool_cmd,\n+\t'tool-help' => \\$tool_help,\n \t'x|extcmd:s' => \\$extcmd);\n \n if (defined($help)) {\n \tusage(0);\n } \n+if (defined($tool_help)) {\n+\tmy $gitpath = Git::exec_path();\n+\tprint \"'git difftool --tool=<tool>' may be set to one of the following:\\n\";\n+\tfor (glob \"$gitpath/mergetools/*\") {\n+\t\tnext if /defaults$/;\n+\t\tprint \"\\t\" . basename($_) . \"\\n\";\n+\t}\n+\texit(0);\n+}\n if (defined($difftool_cmd)) {\n \tif (length($difftool_cmd) > 0) {\n \t\t$ENV{GIT_DIFF_TOOL} = $difftool_cmd;\ndiff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh\nindex 478c1be..bbe71e5 100755\n--- a/t/t7800-difftool.sh\n+++ b/t/t7800-difftool.sh\n@@ -319,6 +319,11 @@ test_expect_success PERL 'say no to the second file' '\n \techo \"$diff\" | stdin_doesnot_contain br2\n '\n \n+test_expect_success PERL 'difftool --tool-help' '\n+\ttool_help=$(git difftool --tool-help) &&\n+\techo \"$tool_help\" | stdin_contains tool\n+'\n+\n test_expect_success PERL 'setup change in subdirectory' '\n \tgit checkout master &&\n \tmkdir sub &&\n-- \n1.7.10.rc1.36.g15e879\n"}]}