{"thread":{"id":"29969","subject":"[PATCH 7/9] difftool: teach difftool to handle directory diffs","startedAt":"2012-03-17T01:59:34Z","lastAt":"2012-03-20T20:17:51Z","messageCount":8,"participants":["Tim Henigan","David Aguilar","Jakub Narebski","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"187123","messageId":"1331949574-15192-1-git-send-email-tim.henigan@gmail.com","threadId":"29969","inReplyTo":null,"subject":"[PATCH 7/9] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-17T01:59:34Z","receivedAt":"2012-03-17T01:59:34Z","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\nI'm not sure what conventions are used to decide when new copyright\nauthors are added.  Given the amount of change to this script, it\nseems appropriate to add me to the list. However, if that is change\nis outside norms, I have no problem taking it out.\n\n\n Documentation/git-difftool.txt |    6 ++\n git-difftool--helper.sh        |   20 ++++--\n git-difftool.perl              |  142 ++++++++++++++++++++++++++++++++++++----\n 3 files changed, 150 insertions(+), 18 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..bd0556f 100755\n--- a/git-difftool--helper.sh\n+++ b/git-difftool--helper.sh\n@@ -6,6 +6,7 @@\n # Copyright (c) 2009, 2010 David Aguilar\n \n TOOL_MODE=diff\n+SUBDIRECTORY_OK=1\n . git-mergetool--lib\n \n # difftool.prompt controls the default prompt/no-prompt behavior\n@@ -73,9 +74,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 8498089..0262c7a 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -1,18 +1,24 @@\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@@ -22,15 +28,109 @@ sub usage\n \tprint << 'USAGE';\n usage: git difftool [-t|--tool=<tool>] [-x|--extcmd=<cmd>]\n                     [-y|--no-prompt]   [-g|--gui]\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('/tmp/git-diffall.XXXXX', CLEANUP => 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 @working_tree;\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_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$repo->command_oneline([\"checkout-index\", \"-a\", \"--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+\t$repo->command_oneline([\"checkout-index\", \"-a\", \"--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->wc_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(SUBMOD, \">$ldir/$path\") or die $!;\n+\t\t\tprint(SUBMOD \"Subproject commit $submodule{$path}{left}\");\n+\t\t\tclose(SUBMOD);\n+\t\t}\n+\t\tif (defined $submodule{$path}{right}) {\n+\t\t\topen(SUBMOD, \">$rdir/$path\") or die $!;\n+\t\t\tprint(SUBMOD \"Subproject commit $submodule{$path}{right}\");\n+\t\t\tclose(SUBMOD);\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, $no_prompt, $prompt);\n+my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $no_prompt, $prompt);\n GetOptions('g|gui' => \\$gui,\n+\t'd|dir-diff' => \\$dirdiff,\n \t'h' => \\$help,\n \t'prompt' => \\$prompt,\n \t't|tool:s' => \\$difftool_cmd,\n@@ -63,13 +163,31 @@ if (defined($gui)) {\n \t\t$ENV{GIT_DIFF_TOOL} = $guitool;\n \t}\n }\n-if (defined($prompt)) {\n-\t$ENV{GIT_DIFFTOOL_PROMPT} = 'true';\n-}\n-elsif (defined($no_prompt)) {\n-\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\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+# 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_DIRDIFF} = 'true';\n+\t\tsystem(\"git difftool--helper $a $b\");\n+\t}\n+\t# TODO: 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+} else {\n+\tif (defined($prompt)) {\n+\t\t$ENV{GIT_DIFFTOOL_PROMPT} = 'true';\n+\t}\n+\telsif (defined($no_prompt)) {\n+\t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n+\t}\n+\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+}\n-- \n1.7.9.1.290.gbd444\n"},{"id":"187129","messageId":"CAJDDKr4mc7OwUzE9d51KkgMaMauVJx_T=rdmXnhEc_a0hc0LGQ@mail.gmail.com","threadId":"29969","inReplyTo":"1331949574-15192-1-git-send-email-tim.henigan@gmail.com","subject":"Re: [PATCH 7/9] difftool: teach difftool to handle directory diffs","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-03-17T03:08:55Z","receivedAt":"2012-03-17T03:08:55Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On Fri, Mar 16, 2012 at 6:59 PM, Tim Henigan <tim.henigan@gmail.com> wrote:\n> Prior to this commit, the difftool utility could only open files one\n> at a time.  The new '--dir-diff' option copies all the modified files\n> to a temporary location and runs a directory diff on them.\n>\n> Signed-off-by: Tim Henigan <tim.henigan@gmail.com>\n> ---\n>\n>  Documentation/git-difftool.txt |    6 ++\n>  git-difftool--helper.sh        |   20 ++++--\n>  git-difftool.perl              |  142 ++++++++++++++++++++++++++++++++++++----\n>  3 files changed, 150 insertions(+), 18 deletions(-)\n>\n> diff --git a/git-difftool.perl b/git-difftool.perl\n> index 8498089..0262c7a 100755\n> --- a/git-difftool.perl\n> +++ b/git-difftool.perl\n> @@ -22,15 +28,109 @@ sub usage\n>        print << 'USAGE';\n>  usage: git difftool [-t|--tool=<tool>] [-x|--extcmd=<cmd>]\n>                     [-y|--no-prompt]   [-g|--gui]\n> +                    [-d|--dir-diff]\n>                     ['git diff' options]\n>  USAGE\n>        exit($exitcode);\n>  }\n>\n> +sub setup_dir_diff\n> +{\n> +       # Run the diff; exit immediately if no diff found\n> +       my $repo = Git->repository();\n> +       my $diffrtn = $repo->command_oneline(['diff', '--raw', '--no-abbrev', '-z', @ARGV]);\n> +       exit(0) if (length($diffrtn) == 0);\n> +\n> +       # Setup temp directories\n> +       my $tmpdir = tempdir('/tmp/git-diffall.XXXXX', CLEANUP => 1);\n\nIs it okay to hardcode /tmp here, or should we instead do something like this?:\n\n    my $tmp = $ENV{TMPDIR} || '/tmp';\n-- \nDavid\n"},{"id":"187153","messageId":"CAFouetgFwaksCpggMER28i9UhEfpWhRcx5To8ck0Bsv+mbirTg@mail.gmail.com","threadId":"29969","inReplyTo":"CAJDDKr4mc7OwUzE9d51KkgMaMauVJx_T=rdmXnhEc_a0hc0LGQ@mail.gmail.com","subject":"Re: [PATCH 7/9] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-17T14:18:58Z","receivedAt":"2012-03-17T14:18:58Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"On Fri, Mar 16, 2012 at 11:08 PM, David Aguilar <davvid@gmail.com> wrote:\n> On Fri, Mar 16, 2012 at 6:59 PM, Tim Henigan <tim.henigan@gmail.com> wrote:\n\n>> +       # Setup temp directories\n>> +       my $tmpdir = tempdir('/tmp/git-diffall.XXXXX', CLEANUP => 1);\n>\n> Is it okay to hardcode /tmp here, or should we instead do something like this?:\n>\n>    my $tmp = $ENV{TMPDIR} || '/tmp';\n\nThis could be done better.  Based on the documentation [1], it appears\nthat the proper way to do this is:\n\n    my $tmpdir = tempdir('git-diffall.XXXXX', CLEANUP => 1, TMPDIR => 1);\n\nAdding 'TMPDIR => 1' is the equivalent of commanding the tmp dir to be\ncreated in 'File::Spec->tmpdir' [2].\n\nI will try this out and send a v2 patch.\n\n[1]: http://perldoc.perl.org/File/Temp.html\n[2]: http://perldoc.perl.org/File/Spec.html\n"},{"id":"187156","messageId":"m3fwd7l2mu.fsf@localhost.localdomain","threadId":"29969","inReplyTo":"1331949574-15192-1-git-send-email-tim.henigan@gmail.com","subject":"Re: [PATCH 7/9] difftool: teach difftool to handle directory diffs","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2012-03-17T14:32:12Z","receivedAt":"2012-03-17T14:32:12Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Tim Henigan <tim.henigan@gmail.com> writes:\n\n> +\tforeach my $path (keys %submodule) {\n> +\t\tif (defined $submodule{$path}{left}) {\n> +\t\t\topen(SUBMOD, \">$ldir/$path\") or die $!;\n> +\t\t\tprint(SUBMOD \"Subproject commit $submodule{$path}{left}\");\n> +\t\t\tclose(SUBMOD);\n> +\t\t}\n> +\t\tif (defined $submodule{$path}{right}) {\n> +\t\t\topen(SUBMOD, \">$rdir/$path\") or die $!;\n> +\t\t\tprint(SUBMOD \"Subproject commit $submodule{$path}{right}\");\n> +\t\t\tclose(SUBMOD);\n\nCould you please use modern Perl, and use lexical filehandles instead\nof globs, and 3-arg version of 'open', i.e.\n\n  +\t\t\topen my $submod_fh, \">\", \"$ldir/$path\" or die $!;\n  +\t\t\tprint $submod_fh \"Subproject commit $submodule{$path}{left}\";\n  +\t\t\tclose $submod_fh;\n\n\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_DIRDIFF} = 'true';\n> +\t\tsystem(\"git difftool--helper $a $b\");\n> +\t}\n> +\t# TODO: 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> +} else {\n> +\tif (defined($prompt)) {\n> +\t\t$ENV{GIT_DIFFTOOL_PROMPT} = 'true';\n> +\t}\n> +\telsif (defined($no_prompt)) {\n> +\t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n> +\t}\n> +\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> +}\n\nWhy you use 'system' (and not in list form which does not require\nescaping shell characters) instead of git_cmd_try for first \n\"git difftool--helper\" invocation?\n\nIs $extcmd and resultof setup_dir_diff() to be treated as shell\nsnippet, and used in string form of 'system' without escaping shell\nmetacharacters (like ' ' in filename)?\n\n-- \nJakub Narebski\n"},{"id":"187179","messageId":"CAFouetg1JEBsO4SyHxpaphQZQtmbo8ugbHoUWnhSXku4DWuvVA@mail.gmail.com","threadId":"29969","inReplyTo":"m3fwd7l2mu.fsf@localhost.localdomain","subject":"Re: [PATCH 7/9] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-18T00:58:12Z","receivedAt":"2012-03-18T00:58:12Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"On Sat, Mar 17, 2012 at 10:32 AM, Jakub Narebski <jnareb@gmail.com> wrote:\n> Tim Henigan <tim.henigan@gmail.com> writes:\n>\n>> +     foreach my $path (keys %submodule) {\n>> +             if (defined $submodule{$path}{left}) {\n>> +                     open(SUBMOD, \">$ldir/$path\") or die $!;\n>> +                     print(SUBMOD \"Subproject commit $submodule{$path}{left}\");\n>> +                     close(SUBMOD);\n>> +             }\n>> +             if (defined $submodule{$path}{right}) {\n>> +                     open(SUBMOD, \">$rdir/$path\") or die $!;\n>> +                     print(SUBMOD \"Subproject commit $submodule{$path}{right}\");\n>> +                     close(SUBMOD);\n>\n> Could you please use modern Perl, and use lexical filehandles instead\n> of globs, and 3-arg version of 'open', i.e.\n>\n>  +                     open my $submod_fh, \">\", \"$ldir/$path\" or die $!;\n>  +                     print $submod_fh \"Subproject commit $submodule{$path}{left}\";\n>  +                     close $submod_fh;\n\nI will make this change in v2.  Thank you for the review and the reminder.\n\n\n>> +if (defined($dirdiff)) {\n>> +     my ($a, $b) = setup_dir_diff();\n>> +     if (defined($extcmd)) {\n>> +             system(\"$extcmd $a $b\");\n>> +     } else {\n>> +             $ENV{GIT_DIFFTOOL_DIRDIFF} = 'true';\n>> +             system(\"git difftool--helper $a $b\");\n>> +     }\n>\n> Why you use 'system' (and not in list form which does not require\n> escaping shell characters) instead of git_cmd_try for first\n> \"git difftool--helper\" invocation?\n\nThere is no good reason to use 'system' here.  I will change this to\n'Git::command_noisy' in v2.\n\n\n> Is $extcmd and resultof setup_dir_diff() to be treated as shell\n> snippet, and used in string form of 'system' without escaping shell\n> metacharacters (like ' ' in filename)?\n\nIn v2, I will change to the list form of 'system' for the call to\n$extcmd. I think I made some bad assumptions about the contents of\n$extcmd, $a and $b because of my local test setup.  However, changing\nto list form should correct the oversight.\n"},{"id":"187341","messageId":"CAFouetiSyTFAR=CJBuYEcpUDq=f2jVaOMnsThgEP-zSbTH1H_g@mail.gmail.com","threadId":"29969","inReplyTo":"1331949574-15192-1-git-send-email-tim.henigan@gmail.com","subject":"Re: [PATCH 7/9] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-20T17:43:52Z","receivedAt":"2012-03-20T17:43:52Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"I have run into a problem with the way that the tmp directories are\npopulated using 'git update-index' and 'git checkout-index'.  I would\nappreciate any feedback that may help me understand what I am doing\nwrong.\n\nThis is in regard to the following section of code:\n\nOn Fri, Mar 16, 2012 at 9:59 PM, Tim Henigan <tim.henigan@gmail.com> wrote:\n>\n> +sub setup_dir_diff\n> +{\n> +       # Run the diff; exit immediately if no diff found\n> +       my $repo = Git->repository();\n> +       my $diffrtn = $repo->command_oneline(['diff', '--raw', '--no-abbrev', '-z', @ARGV]);\n> +       exit(0) if (length($diffrtn) == 0);\n\n<snip>\n\n> +       my @rawdiff = split('\\0', $diffrtn);\n> +\n> +       for (my $i=0; $i<@rawdiff; $i+=2) {\n> +               my ($lmode, $rmode, $lsha1, $rsha1, $status) = split(' ', substr($rawdiff[$i], 1));\n> +               my $path = $rawdiff[$i + 1];\n> +\n> +               if (($lmode eq $submodule_mode) or ($rmode eq $submodule_mode)) {\n> +                       $submodule{$path}{left} = $lsha1;\n> +                       $submodule{$path}{right} = $rsha1;\n> +                       next;\n> +               }\n> +\n> +               if ($lmode ne $null_mode) {\n> +                       $lindex .= \"$lmode $lsha1\\t$path\\0\";\n> +               }\n> +\n> +               if ($rmode ne $null_mode) {\n> +                       if ($rsha1 ne $null_sha1) {\n> +                               $rindex .= \"$rmode $rsha1\\t$path\\0\";\n> +                       } else {\n> +                               push(@working_tree, $path);\n> +                       }\n> +               }\n> +       }\n> +\n> +       # Populate the left and right directories based on each index file\n> +       my ($inpipe, $ctx);\n> +       $ENV{GIT_INDEX_FILE} = \"$tmpdir/lindex\";\n> +       ($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);\n> +       print($inpipe $lindex);\n> +       $repo->command_close_pipe($inpipe, $ctx);\n> +       $repo->command_oneline([\"checkout-index\", \"-a\", \"--prefix=$ldir/\"]);\n\nWhen run from the root directory of a Git repository, the 'difftool\n--dir-diff' command operates as expected:\n  - Temporary index files for each side of the diff are created using\n'git update-index'.\n  - 'git checkout-index --all --prefix=$tmp' is used to populate the\ntmp directory based on the index file.\n\nHowever, when 'difftool --dir-diff' is run from a subdirectory of the\nGit repository, there are problems.\n  - The temporary index file generated by 'git update-index' appears\nto be correct.\n  - 'git checkout-index --all --prefix=$tmp' command does not work\n(output tmp dir is empty).\n\nI have tried some combinations of setting $ENV{GIT_DIR} and\n$ENV{GIT_WORK_TREE}, but it has not fixed the problem.  Is there\nanything obvious that I missed?\n"},{"id":"187352","messageId":"7vlimv5del.fsf@alter.siamese.dyndns.org","threadId":"29969","inReplyTo":"CAFouetiSyTFAR=CJBuYEcpUDq=f2jVaOMnsThgEP-zSbTH1H_g@mail.gmail.com","subject":"Re: [PATCH 7/9] difftool: teach difftool to handle directory diffs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-03-20T18:35:14Z","receivedAt":"2012-03-20T18:35:14Z","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> However, when 'difftool --dir-diff' is run from a subdirectory of the\n> Git repository, there are problems.\n>   - The temporary index file generated by 'git update-index' appears\n> to be correct.\n>   - 'git checkout-index --all --prefix=$tmp' command does not work\n> (output tmp dir is empty).\n\nHrmph.\n\n$ rm -fr /tmp/xyz\n$ cd Documentation\n$ git checkout-index --all --prefix=/tmp/xyz/\n$ ls -F /tmp/xyz\nDocumentation/\n$ rm -fr /tmp/xyz\n$ GIT_DIR=$(pwd)/../.git GIT_WORK_TREE=$(pwd) \\\n  git checkout-index --all --prefix=/tmp/xyz/\n$ ls -FC /tmp/xyz | head -2 \nabspath.c                   git-merge-octopus.sh*       reflog-walk.c\naclocal.m4                  git-merge-one-file.sh*      reflog-walk.h\n\nBe sure to have the trailing slash in $tmp.\n"},{"id":"187356","messageId":"CAFouethvGQD3RR_Lcu6GFA3pRS7_7nQC77g7coxOBDBP4ATP6A@mail.gmail.com","threadId":"29969","inReplyTo":"7vlimv5del.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 7/9] difftool: teach difftool to handle directory diffs","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-03-20T20:17:51Z","receivedAt":"2012-03-20T20:17:51Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"On Tue, Mar 20, 2012 at 2:35 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Tim Henigan <tim.henigan@gmail.com> writes:\n>\n>> However, when 'difftool --dir-diff' is run from a subdirectory of the\n>> Git repository, there are problems.\n>>   - The temporary index file generated by 'git update-index' appears\n>> to be correct.\n>>   - 'git checkout-index --all --prefix=$tmp' command does not work\n>> (output tmp dir is empty).\n>\n> Hrmph.\n>\n> $ rm -fr /tmp/xyz\n> $ cd Documentation\n> $ git checkout-index --all --prefix=/tmp/xyz/\n> $ ls -F /tmp/xyz\n> Documentation/\n> $ rm -fr /tmp/xyz\n> $ GIT_DIR=$(pwd)/../.git GIT_WORK_TREE=$(pwd) \\\n>  git checkout-index --all --prefix=/tmp/xyz/\n> $ ls -FC /tmp/xyz | head -2\n> abspath.c                   git-merge-octopus.sh*       reflog-walk.c\n> aclocal.m4                  git-merge-one-file.sh*      reflog-walk.h\n>\n> Be sure to have the trailing slash in $tmp.\n\nIt turns out that 'Git::command_oneline()' does not honor environment\nvariables set prior to calling the function.\n\nThis change fixed the problem:\n\n- $repo->command_oneline([\"checkout-index\", \"-a\", \"--prefix=$ldir/\"]);\n+ $ENV{GIT_DIR} = $repo->repo_path();\n+ system(('git', 'checkout-index', '-a', \"--prefix=$ldir/\"));\n\nI'm still testing, but will submit an updated patch soon.\n"}]}