{"thread":{"id":"30223","subject":"[PATCH 8/9 v12] difftool: teach difftool to handle directory diffs","startedAt":"2012-04-12T13:18:46Z","lastAt":"2012-04-13T16:31:21Z","messageCount":6,"participants":["Tim Henigan","Junio C Hamano","David Aguilar"],"isPatch":true,"patchVersion":12,"patchTotal":9},"messages":[{"id":"189099","messageId":"1334236726-18393-1-git-send-email-tim.henigan@gmail.com","threadId":"30223","inReplyTo":null,"subject":"[PATCH 8/9 v12] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-04-12T13:18:46Z","receivedAt":"2012-04-12T13:18:46Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"When 'difftool' is called to compare a range of commits that modify\nmore than one file, it opens a separate instance of the diff tool for\neach file that changed.\n\nThe new '--dir-diff' option copies all the modified files to a temporary\nlocation and runs a directory diff on them in a single instance of the\ndiff tool.\n\nSigned-off-by: Tim Henigan <tim.henigan@gmail.com>\n---\n\nThis replaces 5038aa2 on the 'th/difftool-diffall' branch.\n\nChanges in v12:\n\n  - Updates based on patch review by David Aguilar [1]\n      * Reword script header comment to no longer enumerate each\n        exported var.\n      * Changed shebang line to #!/usr/bin/perl.\n      * Changed $null_mode to use string form of \"0\" x 6.\n      * Changed $null_sha1 to use string form of \"0\" x 40.\n      * Changed for(@rawdiff) loop to use $#rawdiff.\n      * Dropped extra parens from all 'system' calls.\n      * The return value of all 'system' calls are now checked.\n      * Changed 'for (@working_tree)' to 'for my $file (@working_tree)'.\n      * Changed 'foreach my $path' to 'for my $path'.\n      * All calls to 'defined()' now use consistent style.\n      * Comment about ActiveState Perl 'exec' is now retained.\n\n  - Changes to allow 'difftool' to be called with $GIT_DIR and\n    $GIT_WORK_TREE overridden at the command-line (or via core.worktree):\n      * Changed 'git diff --raw' to be called using backticks to insure\n        that env vars set by the user on the command-line (i.e. GIT_DIR\n        and GIT_WORK_TREE) are used.\n      * Added comments indicating why $GIT_DIR is explicitly set prior\n        to calling 'git update-index' and 'git checkout-index'.\n      * If $GIT_DIR is set by the script, it is now unset after being used.\n      * $GIT_INDEX_FILE is now always unset after being used.\n      * Added helper function (find_worktree) to work around issues with\n        Git->repository->wc_path(). See the comment in the script for\n        details.\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/194721/focus=195014\n\n\n Documentation/git-difftool.txt |    6 ++\n git-difftool--helper.sh        |   19 ++--\n git-difftool.perl              |  216 ++++++++++++++++++++++++++++++++++++----\n t/t7800-difftool.sh            |   39 ++++++++\n 4 files changed, 254 insertions(+), 26 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 aba3d2f..85405bf 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -1,21 +1,29 @@\n-#!/usr/bin/env perl\n+#!/usr/bin/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+# The GIT_DIFF* variables 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+my $rc;\n+\n sub usage\n {\n \tmy $exitcode = shift;\n@@ -24,15 +32,158 @@ 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 find_worktree\n+{\n+\t# Git->repository->wc_path() does not honor changes to the working\n+\t# tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'\n+\t# config variable.\n+\tmy $worktree;\n+\tmy $env_worktree = $ENV{GIT_WORK_TREE};\n+\tmy $core_worktree = Git::config('core.worktree');\n+\n+\tif (length($env_worktree) > 0) {\n+\t\t$worktree = $env_worktree;\n+\t} elsif (length($core_worktree) > 0) {\n+\t\t$worktree = $core_worktree;\n+\t} else {\n+\t\tmy $repo = Git->repository();\n+\t\t$worktree = $repo->wc_path();\n+\t}\n+\n+\treturn $worktree;\n+}\n+\n+sub setup_dir_diff\n+{\n+\t# Run the diff; exit immediately if no diff found\n+\tmy $cmd = \"git diff --raw --no-abbrev -z \" . join(\" \", @ARGV);\n+\tmy $diffrtn = `$cmd` or die $!;\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 $repo = Git->repository();\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\tif ($lsha1 ne $rsha1) {\n+\t\t\t\t$submodule{$path}{right} = $rsha1;\n+\t\t\t} else {\n+\t\t\t\t$submodule{$path}{right} = \"$rsha1-dirty\";\n+\t\t\t}\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# If $GIT_DIR is not set prior to calling 'git update-index' and\n+\t# 'git checkout-index', then those commands will fail if difftool\n+\t# is called from a directory other than the repo root.\n+\tmy $must_unset_git_dir = 0;\n+\tif (not defined($ENV{GIT_DIR})) {\n+\t\t$must_unset_git_dir = 1;\n+\t\t$ENV{GIT_DIR} = $repo->repo_path();\n+\t}\n+\n+\t# Populate the left and right directories based on each index file\n+\tmy ($inpipe, $ctx);\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+\t$rc = system('git', 'checkout-index', '--all', \"--prefix=$ldir/\");\n+\texit($rc | ($rc >> 8)) if ($rc != 0);\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+\t$rc = system('git', 'checkout-index', '--all', \"--prefix=$rdir/\");\n+\texit($rc | ($rc >> 8)) if ($rc != 0);\n+\n+\t# If $GIT_DIR was explicitly set just for the update/checkout\n+\t# commands, then it should be unset before continuing.\n+\tdelete($ENV{GIT_DIR}) if ($must_unset_git_dir);\n+\tdelete($ENV{GIT_INDEX_FILE});\n+\n+\t# Changes in the working tree need special treatment since they are\n+\t# not part of the index\n+\tmy $workdir = find_worktree();\n+\tfor my $file (@working_tree) {\n+\t\tmy $dir = dirname($file);\n+\t\tunless (-d \"$rdir/$dir\") {\n+\t\t\tmkpath(\"$rdir/$dir\") or die $!;\n+\t\t}\n+\t\tcopy(\"$workdir/$file\", \"$rdir/$file\") or die $!;\n+\t\tchmod(stat(\"$workdir/$file\")->mode, \"$rdir/$file\") 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+\tfor my $path (keys %submodule) {\n+\t\tif (defined($submodule{$path}{left})) {\n+\t\t\tmy $dir = dirname($path);\n+\t\t\tunless (-d \"$ldir/$dir\") {\n+\t\t\t\tmkpath(\"$ldir/$dir\") or die $!;\n+\t\t\t}\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\tmy $dir = dirname($path);\n+\t\t\tunless (-d \"$rdir/$dir\") {\n+\t\t\t\tmkpath(\"$rdir/$dir\") or die $!;\n+\t\t\t}\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,22 +216,47 @@ 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\t$rc = system($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\t$rc = system('git', 'difftool--helper', $a, $b);\n \t}\n-}\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+\texit($rc | ($rc >> 8)) if ($rc != 0);\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 $workdir = find_worktree();\n+\tfor my $file (@working_tree) {\n+\t\tcopy(\"$b/$file\", \"$workdir/$file\") or die $!;\n+\t\tchmod(stat(\"$b/$file\")->mode, \"$workdir/$file\") 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+\t$ENV{GIT_PAGER} = '';\n+\t$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';\n+\n+\t# ActiveState Perl for Win32 does not implement POSIX semantics of\n+\t# exec* system call. It just spawns the given executable and finishes\n+\t# the starting program, exiting with code 0.\n+\t# system will at least catch the errors returned by git diff,\n+\t# allowing the caller of git difftool better handling of failures.\n+\tmy $rc = system('git', 'diff', @ARGV);\n+\texit($rc | ($rc >> 8));\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.26.g40aa3\n"},{"id":"189126","messageId":"7viph46c1t.fsf@alter.siamese.dyndns.org","threadId":"30223","inReplyTo":"1334236726-18393-1-git-send-email-tim.henigan@gmail.com","subject":"Re: [PATCH 8/9 v12] difftool: teach difftool to handle directory diffs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-04-12T18:27:10Z","receivedAt":"2012-04-12T18:27:10Z","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> +sub setup_dir_diff\n> +{\n> +\t# Run the diff; exit immediately if no diff found\n> +\tmy $cmd = \"git diff --raw --no-abbrev -z \" . join(\" \", @ARGV);\n> +\tmy $diffrtn = `$cmd` or die $!;\n\nWhy this change?  It looks like a rather unpleasant regression compared to\nthe previous one that used \"command_oneline\", so that each element of\n@ARGV was given as a single argument to the command.  Now, you are\nsplitting any argument that has $IFS in it.\n"},{"id":"189128","messageId":"CAFouetg6Ot8sKiNi45A0QRv6YYdL3Mwrb3tkVkajQQuZukSp3g@mail.gmail.com","threadId":"30223","inReplyTo":"7viph46c1t.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 8/9 v12] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-04-12T19:10:45Z","receivedAt":"2012-04-12T19:10:45Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"On Thu, Apr 12, 2012 at 2:27 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Tim Henigan <tim.henigan@gmail.com> writes:\n>\n>> +sub setup_dir_diff\n>> +{\n>> +     # Run the diff; exit immediately if no diff found\n>> +     my $cmd = \"git diff --raw --no-abbrev -z \" . join(\" \", @ARGV);\n>> +     my $diffrtn = `$cmd` or die $!;\n>\n> Why this change?  It looks like a rather unpleasant regression compared to\n> the previous one that used \"command_oneline\", so that each element of\n> @ARGV was given as a single argument to the command.  Now, you are\n> splitting any argument that has $IFS in it.\n\nThe 'Git->repository->command*' functions all override the\n$GIT_WORK_TREE set by the user on the command line with 'pwd'.  They\nalso override the 'core.worktree' setting, if it is used.\n\nSo without this change, the following fails:\n\n  $ GIT_DIR=<dir> GIT_WORK_TREE=<dir> git difftool -d\n\nWould the following change be better?\n\n-     my $cmd = \"git diff --raw --no-abbrev -z \" . join(\" \", @ARGV);\n+     my $cmd = \"git diff --raw --no-abbrev -z @ARGV\";\n"},{"id":"189132","messageId":"7v62d4692l.fsf@alter.siamese.dyndns.org","threadId":"30223","inReplyTo":"CAFouetg6Ot8sKiNi45A0QRv6YYdL3Mwrb3tkVkajQQuZukSp3g@mail.gmail.com","subject":"Re: [PATCH 8/9 v12] difftool: teach difftool to handle directory diffs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-04-12T19:31:30Z","receivedAt":"2012-04-12T19:31:30Z","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> Would the following change be better?\n>\n> -     my $cmd = \"git diff --raw --no-abbrev -z \" . join(\" \", @ARGV);\n> +     my $cmd = \"git diff --raw --no-abbrev -z @ARGV\";\n\nThey look exactly the same to me.\n"},{"id":"189170","messageId":"CAJDDKr4UbN3izmqa89CCH7OY4c9LOH0S2EbdUCxHK3G6GHd4xQ@mail.gmail.com","threadId":"30223","inReplyTo":"7v62d4692l.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 8/9 v12] difftool: teach difftool to handle directory diffs","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-04-13T02:53:27Z","receivedAt":"2012-04-13T02:53:27Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On Thu, Apr 12, 2012 at 12:31 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Tim Henigan <tim.henigan@gmail.com> writes:\n>\n>> Would the following change be better?\n>>\n>> -     my $cmd = \"git diff --raw --no-abbrev -z \" . join(\" \", @ARGV);\n>> +     my $cmd = \"git diff --raw --no-abbrev -z @ARGV\";\n>\n> They look exactly the same to me.\n\nThe issue is that this does not properly handle shell characters, IFS, etc.\n\nI think a simple solution would be to use git diff --exit-code --quiet here.\n\ne.g.\n\n    my $rc = system('git', 'diff', '--exit-code', '--quiet', @ARGV);\n\nThat avoids the shell and properly handles shell meta-characters, IFS, etc.\n-- \nDavid\n"},{"id":"189197","messageId":"CAFouetim-sUQ53dKWF6rVKH9Gx-SVuZmzpJFHosQSSrw+wX71w@mail.gmail.com","threadId":"30223","inReplyTo":"CAJDDKr4UbN3izmqa89CCH7OY4c9LOH0S2EbdUCxHK3G6GHd4xQ@mail.gmail.com","subject":"Re: [PATCH 8/9 v12] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-04-13T16:31:21Z","receivedAt":"2012-04-13T16:31:21Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"On Thu, Apr 12, 2012 at 10:53 PM, David Aguilar <davvid@gmail.com> wrote:\n>\n> The issue is that this does not properly handle shell characters, IFS, etc.\n>\n> I think a simple solution would be to use git diff --exit-code --quiet here.\n>\n> e.g.\n>\n>    my $rc = system('git', 'diff', '--exit-code', '--quiet', @ARGV);\n>\n> That avoids the shell and properly handles shell meta-characters, IFS, etc.\n\nThe problem is that I need the output of 'git diff' to drive the rest\nof the script.  Getting STDOUT from a system() call is difficult, so I\nswitched to backticks.  Obviously I missed the regression.\n\nThe best solution seems to be to find a way to make\n'Git->repository->command*' to work with $GIT_DIR and $GIT_WORK_TREE.\nI will send v13 with this change soon.\n"}]}