@ -13,9 +13,9 @@
use 5.008;
use 5.008;
use strict;
use strict;
use warnings;
use warnings;
use Error qw(:try);
use File::Basename qw(dirname);
use File::Basename qw(dirname);
use File::Copy;
use File::Copy;
use File::Compare;
use File::Find;
use File::Find;
use File::stat;
use File::stat;
use File::Path qw(mkpath rmtree);
use File::Path qw(mkpath rmtree);
@ -88,14 +88,45 @@ sub use_wt_file
my ($repo, $workdir, $file, $sha1, $symlinks) = @_;
my ($repo, $workdir, $file, $sha1, $symlinks) = @_;
my $null_sha1 = '0' x 40;
my $null_sha1 = '0' x 40;
if ($sha1 eq $null_sha1) {
if ($sha1 ne $null_sha1 and not $symlinks) {
return 1;
} elsif (not $symlinks) {
return 0;
return 0;
}
}
my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
return $sha1 eq $wt_sha1;
my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1);
return ($use, $wt_sha1);
}
sub changed_files
{
my ($repo_path, $index, $worktree) = @_;
$ENV{GIT_INDEX_FILE} = $index;
$ENV{GIT_WORK_TREE} = $worktree;
my $must_unset_git_dir = 0;
if (not defined($ENV{GIT_DIR})) {
$must_unset_git_dir = 1;
$ENV{GIT_DIR} = $repo_path;
}
my @refreshargs = qw/update-index --really-refresh -q --unmerged/;
my @gitargs = qw/diff-files --name-only -z/;
try {
Git::command_oneline(@refreshargs);
} catch Git::Error::Command with {};
my $line = Git::command_oneline(@gitargs);
my @files;
if (defined $line) {
@files = split('\0', $line);
} else {
@files = ();
}
delete($ENV{GIT_INDEX_FILE});
delete($ENV{GIT_WORK_TREE});
delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
return map { $_ => 1 } @files;
}
}
sub setup_dir_diff
sub setup_dir_diff
@ -121,6 +152,7 @@ sub setup_dir_diff
my $null_sha1 = '0' x 40;
my $null_sha1 = '0' x 40;
my $lindex = '';
my $lindex = '';
my $rindex = '';
my $rindex = '';
my $wtindex = '';
my %submodule;
my %submodule;
my %symlink;
my %symlink;
my @working_tree = ();
my @working_tree = ();
@ -174,8 +206,12 @@ EOF
}
}
if ($rmode ne $null_mode) {
if ($rmode ne $null_mode) {
if (use_wt_file($repo, $workdir, $dst_path, $rsha1, $symlinks)) {
my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
push(@working_tree, $dst_path);
$dst_path, $rsha1,
$symlinks);
if ($use) {
push @working_tree, $dst_path;
$wtindex .= "$rmode $wt_sha1\t$dst_path\0";
} else {
} else {
$rindex .= "$rmode $rsha1\t$dst_path\0";
$rindex .= "$rmode $rsha1\t$dst_path\0";
}
}
@ -218,6 +254,12 @@ EOF
$rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
$rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
exit_cleanup($tmpdir, $rc) if $rc != 0;
exit_cleanup($tmpdir, $rc) if $rc != 0;
$ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex";
($inpipe, $ctx) =
$repo->command_input_pipe(qw(update-index --info-only -z --index-info));
print($inpipe $wtindex);
$repo->command_close_pipe($inpipe, $ctx);
# If $GIT_DIR was explicitly set just for the update/checkout
# If $GIT_DIR was explicitly set just for the update/checkout
# commands, then it should be unset before continuing.
# commands, then it should be unset before continuing.
delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
@ -390,19 +432,34 @@ sub dir_diff
# should be copied back to the working tree.
# should be copied back to the working tree.
# Do not copy back files when symlinks are used and the
# Do not copy back files when symlinks are used and the
# external tool did not replace the original link with a file.
# external tool did not replace the original link with a file.
#
# These hashes are loaded lazily since they aren't needed
# in the common case of --symlinks and the difftool updating
# files through the symlink.
my %wt_modified;
my %tmp_modified;
my $indices_loaded = 0;
for my $file (@worktree) {
for my $file (@worktree) {
next if $symlinks && -l "$b/$file";
next if $symlinks && -l "$b/$file";
next if ! -f "$b/$file";
next if ! -f "$b/$file";
my $diff = compare("$b/$file", "$workdir/$file");
if (!$indices_loaded) {
if ($diff == 0) {
%wt_modified = changed_files($repo->repo_path(),
next;
"$tmpdir/wtindex", "$workdir");
} elsif ($diff == -1) {
%tmp_modified = changed_files($repo->repo_path(),
my $errmsg = "warning: Could not compare ";
"$tmpdir/wtindex", "$b");
$errmsg += "'$b/$file' with '$workdir/$file'\n";
$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 .= "warning: Working tree file has been left.\n";
$errmsg .= "warning:\n";
warn $errmsg;
warn $errmsg;
$error = 1;
$error = 1;
} elsif ($diff == 1) {
} elsif (exists $tmp_modified{$file}) {
my $mode = stat("$b/$file")->mode;
my $mode = stat("$b/$file")->mode;
copy("$b/$file", "$workdir/$file") or
copy("$b/$file", "$workdir/$file") or
exit_cleanup($tmpdir, 1);
exit_cleanup($tmpdir, 1);