diff options
Diffstat (limited to 'git-difftool.perl')
-rwxr-xr-x | git-difftool.perl | 147 |
1 files changed, 63 insertions, 84 deletions
diff --git a/git-difftool.perl b/git-difftool.perl index ebd13baa6e..df59bdfe97 100755 --- a/git-difftool.perl +++ b/git-difftool.perl @@ -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 { @@ -37,14 +38,6 @@ USAGE exit($exitcode); } -sub find_worktree -{ - # Git->repository->wc_path() does not honor changes to the working - # tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree' - # config variable. - return Git::command_oneline('rev-parse', '--show-toplevel'); -} - sub print_tool_help { # See the comment at the bottom of file_diff() for the reason behind @@ -67,14 +60,14 @@ sub exit_cleanup sub use_wt_file { - my ($repo, $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 = $repo->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); } @@ -83,20 +76,17 @@ 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/; + my @gitargs = ('--git-dir', $repo_path, '--work-tree', $worktree); + my @refreshargs = ( + @gitargs, 'update-index', + '--really-refresh', '-q', '--unmerged'); try { Git::command_oneline(@refreshargs); } catch Git::Error::Command with {}; - my $line = Git::command_oneline(@gitargs); + my @diffargs = (@gitargs, 'diff-files', '--name-only', '-z'); + my $line = Git::command_oneline(@diffargs); my @files; if (defined $line) { @files = split('\0', $line); @@ -105,28 +95,23 @@ sub changed_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 { - my ($repo, $workdir, $symlinks) = @_; - - # Run the diff; exit immediately if no diff found - # 'Repository' and 'WorkingCopy' must be explicitly set to insure that - # if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used - # by Git->repository->command*. - my $repo_path = $repo->repo_path(); - my %repo_args = (Repository => $repo_path, WorkingCopy => $workdir); - my $diffrepo = Git->repository(%repo_args); - + my ($worktree, $symlinks) = @_; my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV); - my $diffrtn = $diffrepo->command_oneline(@gitargs); + 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'; @@ -137,14 +122,14 @@ sub setup_dir_diff 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 @@ -176,12 +161,12 @@ EOF if ($lmode eq $symlink_mode) { $symlink{$src_path}{left} = - $diffrepo->command_oneline('show', "$lsha1"); + Git::command_oneline('show', $lsha1); } if ($rmode eq $symlink_mode) { $symlink{$dst_path}{right} = - $diffrepo->command_oneline('show', "$rsha1"); + Git::command_oneline('show', $rsha1); } if ($lmode ne $null_mode and $status !~ /^C/) { @@ -189,14 +174,14 @@ 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($repo, $workdir, - $dst_path, $rsha1); + my ($use, $wt_sha1) = + 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"; @@ -204,6 +189,10 @@ EOF } } + # 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"; @@ -211,64 +200,52 @@ EOF mkpath($ldir) or exit_cleanup($tmpdir, 1); mkpath($rdir) or exit_cleanup($tmpdir, 1); - # If $GIT_DIR is not set prior to calling 'git update-index' and - # 'git checkout-index', then those commands will fail if difftool - # is called from a directory other than the repo root. - my $must_unset_git_dir = 0; - if (not defined($ENV{GIT_DIR})) { - $must_unset_git_dir = 1; - $ENV{GIT_DIR} = $repo_path; - } - # Populate the left and right directories based on each index file my ($inpipe, $ctx); $ENV{GIT_INDEX_FILE} = "$tmpdir/lindex"; ($inpipe, $ctx) = - $repo->command_input_pipe(qw(update-index -z --index-info)); + Git::command_input_pipe('update-index', '-z', '--index-info'); print($inpipe $lindex); - $repo->command_close_pipe($inpipe, $ctx); + Git::command_close_pipe($inpipe, $ctx); my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/"); exit_cleanup($tmpdir, $rc) if $rc != 0; $ENV{GIT_INDEX_FILE} = "$tmpdir/rindex"; ($inpipe, $ctx) = - $repo->command_input_pipe(qw(update-index -z --index-info)); + Git::command_input_pipe('update-index', '-z', '--index-info'); print($inpipe $rindex); - $repo->command_close_pipe($inpipe, $ctx); + Git::command_close_pipe($inpipe, $ctx); $rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/"); 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)); + Git::command_input_pipe('update-index', '--info-only', '-z', '--index-info'); print($inpipe $wtindex); - $repo->command_close_pipe($inpipe, $ctx); + Git::command_close_pipe($inpipe, $ctx); # If $GIT_DIR was explicitly set just for the update/checkout # commands, then it should be unset before continuing. - delete($ENV{GIT_DIR}) if ($must_unset_git_dir); 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); } @@ -306,7 +283,7 @@ EOF exit_cleanup($tmpdir, 1) if not $ok; } - return ($ldir, $rdir, $tmpdir, @working_tree); + return ($ldir, $rdir, $tmpdir, @files); } sub write_to_file @@ -370,7 +347,7 @@ sub main 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); } } @@ -378,7 +355,7 @@ sub main 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); } } @@ -415,9 +392,10 @@ sub dir_diff my $rc; my $error = 0; my $repo = Git->repository(); - my $workdir = find_worktree(); - my ($a, $b, $tmpdir, @worktree) = - setup_dir_diff($repo, $workdir, $symlinks); + my $repo_path = $repo->repo_path(); + 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); @@ -438,37 +416,38 @@ 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->repo_path(), - "$tmpdir/wtindex", "$workdir"); - %tmp_modified = changed_files($repo->repo_path(), - "$tmpdir/wtindex", "$b"); + %wt_modified = changed_files( + $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 .= "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"), $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); |