Merge branch 'jk/difftool-in-subdir'
authorJunio C Hamano <gitster@pobox.com>
Tue, 27 Dec 2016 08:11:43 +0000 (00:11 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 27 Dec 2016 08:11:43 +0000 (00:11 -0800)
Even though an fix was attempted in Git 2.9.3 days, but running
"git difftool --dir-diff" from a subdirectory never worked. This
has been fixed.

* jk/difftool-in-subdir:
difftool: rename variables for consistency
difftool: chdir as early as possible
difftool: sanitize $workdir as early as possible
difftool: fix dir-diff index creation when in a subdirectory

1  2 
git-difftool.perl
diff --combined git-difftool.perl
index 674828de3920634505291eee03f8ea908430d938,4e4f5d8138a68e52e5ec0b787c1d7a09ba3e6658..df59bdfe97786b5d9f48777cfad80545ff6bbdc2
@@@ -22,7 -22,6 +22,7 @@@ use File::Path qw(mkpath rmtree)
  use File::Temp qw(tempdir);
  use Getopt::Long qw(:config pass_through);
  use Git;
 +use Git::I18N;
  
  sub usage
  {
@@@ -60,14 -59,14 +60,14 @@@ sub exit_cleanu
  
  sub use_wt_file
  {
-       my ($workdir, $file, $sha1) = @_;
+       my ($file, $sha1) = @_;
        my $null_sha1 = '0' x 40;
  
-       if (-l "$workdir/$file" || ! -e _) {
+       if (-l $file || ! -e _) {
                return (0, $null_sha1);
        }
  
-       my $wt_sha1 = Git::command_oneline('hash-object', "$workdir/$file");
+       my $wt_sha1 = Git::command_oneline('hash-object', $file);
        my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1);
        return ($use, $wt_sha1);
  }
@@@ -101,11 -100,17 +101,17 @@@ sub changed_file
  
  sub setup_dir_diff
  {
-       my ($workdir, $symlinks) = @_;
+       my ($worktree, $symlinks) = @_;
        my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV);
        my $diffrtn = Git::command_oneline(@gitargs);
        exit(0) unless defined($diffrtn);
  
+       # Go to the root of the worktree now that we've captured the list of
+       # changed files.  The paths returned by diff --raw are relative to the
+       # top-level of the repository, but we defer changing directories so
+       # that @ARGV can perform pathspec limiting in the current directory.
+       chdir($worktree);
        # Build index info for left and right sides of the diff
        my $submodule_mode = '160000';
        my $symlink_mode = '120000';
        my $wtindex = '';
        my %submodule;
        my %symlink;
-       my @working_tree = ();
+       my @files = ();
        my %working_tree_dups = ();
        my @rawdiff = split('\0', $diffrtn);
  
        my $i = 0;
        while ($i < $#rawdiff) {
                if ($rawdiff[$i] =~ /^::/) {
 -                      warn << 'EOF';
 +                      warn __ <<'EOF';
  Combined diff formats ('-c' and '--cc') are not supported in
  directory diff mode ('-d' and '--dir-diff').
  EOF
                }
  
                if ($rmode ne $null_mode) {
-                       # Avoid duplicate working_tree entries
+                       # Avoid duplicate entries
                        if ($working_tree_dups{$dst_path}++) {
                                next;
                        }
                        my ($use, $wt_sha1) =
-                               use_wt_file($workdir, $dst_path, $rsha1);
+                               use_wt_file($dst_path, $rsha1);
                        if ($use) {
-                               push @working_tree, $dst_path;
+                               push @files, $dst_path;
                                $wtindex .= "$rmode $wt_sha1\t$dst_path\0";
                        } else {
                                $rindex .= "$rmode $rsha1\t$dst_path\0";
                }
        }
  
-       chdir($workdir);
 +      # Go to the root of the worktree so that the left index files
 +      # are properly setup -- the index is toplevel-relative.
++      chdir($worktree);
 +
        # Setup temp directories
        my $tmpdir = tempdir('git-difftool.XXXXX', CLEANUP => 0, TMPDIR => 1);
        my $ldir = "$tmpdir/left";
        delete($ENV{GIT_INDEX_FILE});
  
        # Changes in the working tree need special treatment since they are
-       # not part of the index. Remove any trailing slash from $workdir
-       # before starting to avoid double slashes in symlink targets.
-       $workdir =~ s|/$||;
-       for my $file (@working_tree) {
+       # not part of the index.
+       for my $file (@files) {
                my $dir = dirname($file);
                unless (-d "$rdir/$dir") {
                        mkpath("$rdir/$dir") or
                        exit_cleanup($tmpdir, 1);
                }
                if ($symlinks) {
-                       symlink("$workdir/$file", "$rdir/$file") or
+                       symlink("$worktree/$file", "$rdir/$file") or
                        exit_cleanup($tmpdir, 1);
                } else {
-                       copy("$workdir/$file", "$rdir/$file") or
+                       copy($file, "$rdir/$file") or
                        exit_cleanup($tmpdir, 1);
  
-                       my $mode = stat("$workdir/$file")->mode;
+                       my $mode = stat($file)->mode;
                        chmod($mode, "$rdir/$file") or
                        exit_cleanup($tmpdir, 1);
                }
                exit_cleanup($tmpdir, 1) if not $ok;
        }
  
-       return ($ldir, $rdir, $tmpdir, @working_tree);
+       return ($ldir, $rdir, $tmpdir, @files);
  }
  
  sub write_to_file
@@@ -343,7 -342,7 +347,7 @@@ sub mai
                if (length($opts{difftool_cmd}) > 0) {
                        $ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
                } else {
 -                      print "No <tool> given for --tool=<tool>\n";
 +                      print __("No <tool> given for --tool=<tool>\n");
                        usage(1);
                }
        }
                if (length($opts{extcmd}) > 0) {
                        $ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
                } else {
 -                      print "No <cmd> given for --extcmd=<cmd>\n";
 +                      print __("No <cmd> given for --extcmd=<cmd>\n");
                        usage(1);
                }
        }
@@@ -389,8 -388,9 +393,9 @@@ sub dir_dif
        my $error = 0;
        my $repo = Git->repository();
        my $repo_path = $repo->repo_path();
-       my $workdir = $repo->wc_path();
-       my ($a, $b, $tmpdir, @worktree) = setup_dir_diff($workdir, $symlinks);
+       my $worktree = $repo->wc_path();
+       $worktree =~ s|/$||; # Avoid double slashes in symlink targets
+       my ($a, $b, $tmpdir, @files) = setup_dir_diff($worktree, $symlinks);
  
        if (defined($extcmd)) {
                $rc = system($extcmd, $a, $b);
        my %tmp_modified;
        my $indices_loaded = 0;
  
-       for my $file (@worktree) {
+       for my $file (@files) {
                next if $symlinks && -l "$b/$file";
                next if ! -f "$b/$file";
  
                if (!$indices_loaded) {
                        %wt_modified = changed_files(
-                               $repo_path, "$tmpdir/wtindex", $workdir);
+                               $repo_path, "$tmpdir/wtindex", $worktree);
                        %tmp_modified = changed_files(
                                $repo_path, "$tmpdir/wtindex", $b);
                        $indices_loaded = 1;
                }
  
                if (exists $wt_modified{$file} and exists $tmp_modified{$file}) {
 -                      my $errmsg = "warning: Both files modified: ";
 -                      $errmsg .= "'$worktree/$file' and '$b/$file'.\n";
 -                      $errmsg .= "warning: Working tree file has been left.\n";
 -                      $errmsg .= "warning:\n";
 -                      warn $errmsg;
 +                      warn sprintf(__(
 +                              "warning: Both files modified:\n" .
 +                              "'%s/%s' and '%s/%s'.\n" .
 +                              "warning: Working tree file has been left.\n" .
-                               "warning:\n"), $workdir, $file, $b, $file);
++                              "warning:\n"), $worktree, $file, $b, $file);
                        $error = 1;
                } elsif (exists $tmp_modified{$file}) {
                        my $mode = stat("$b/$file")->mode;
-                       copy("$b/$file", "$workdir/$file") or
+                       copy("$b/$file", $file) or
                        exit_cleanup($tmpdir, 1);
  
-                       chmod($mode, "$workdir/$file") or
+                       chmod($mode, $file) or
                        exit_cleanup($tmpdir, 1);
                }
        }
        if ($error) {
 -              warn "warning: Temporary files exist in '$tmpdir'.\n";
 -              warn "warning: You may want to cleanup or recover these.\n";
 +              warn sprintf(__(
 +                      "warning: Temporary files exist in '%s'.\n" .
 +                      "warning: You may want to cleanup or recover these.\n"), $tmpdir);
                exit(1);
        } else {
                exit_cleanup($tmpdir, $rc);