Skip to content

Commit

Permalink
Merge branch 'jk/difftool-in-subdir' into maint
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Jan 17, 2017
2 parents aa83f7a + ce69269 commit a558332
Showing 1 changed file with 29 additions and 24 deletions.
53 changes: 29 additions & 24 deletions git-difftool.perl
Expand Up @@ -59,14 +59,14 @@ sub exit_cleanup

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);
}
Expand Down Expand Up @@ -100,11 +100,17 @@ sub changed_files

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';
Expand All @@ -115,7 +121,7 @@ sub setup_dir_diff
my $wtindex = '';
my %submodule;
my %symlink;
my @working_tree = ();
my @files = ();
my %working_tree_dups = ();
my @rawdiff = split('\0', $diffrtn);

Expand Down Expand Up @@ -167,14 +173,14 @@ sub setup_dir_diff
}

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";
Expand All @@ -184,7 +190,7 @@ sub setup_dir_diff

# Go to the root of the worktree so that the left index files
# are properly setup -- the index is toplevel-relative.
chdir($workdir);
chdir($worktree);

# Setup temp directories
my $tmpdir = tempdir('git-difftool.XXXXX', CLEANUP => 0, TMPDIR => 1);
Expand Down Expand Up @@ -224,23 +230,21 @@ sub setup_dir_diff
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);
}
Expand Down Expand Up @@ -278,7 +282,7 @@ sub setup_dir_diff
exit_cleanup($tmpdir, 1) if not $ok;
}

return ($ldir, $rdir, $tmpdir, @working_tree);
return ($ldir, $rdir, $tmpdir, @files);
}

sub write_to_file
Expand Down Expand Up @@ -388,8 +392,9 @@ sub dir_diff
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);
Expand All @@ -410,31 +415,31 @@ sub dir_diff
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 .= "'$workdir/$file' and '$b/$file'.\n";
$errmsg .= "'$worktree/$file' and '$b/$file'.\n";
$errmsg .= "warning: Working tree file has been left.\n";
$errmsg .= "warning:\n";
warn $errmsg;
$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);
}
}
Expand Down

0 comments on commit a558332

Please sign in to comment.