difftool --dir-diff: symlink all files matching the working tree
[gitweb.git] / git-difftool.perl
index edd0493a08b501d60c04044e79a9e0b096d77953..c433e86f09e9c10e2bf94d7e5cc61364a33aab2b 100755 (executable)
@@ -59,57 +59,16 @@ sub find_worktree
        return $worktree;
 }
 
-sub filter_tool_scripts
-{
-       my ($tools) = @_;
-       if (-d $_) {
-               if ($_ ne ".") {
-                       # Ignore files in subdirectories
-                       $File::Find::prune = 1;
-               }
-       } else {
-               if ((-f $_) && ($_ ne "defaults")) {
-                       push(@$tools, $_);
-               }
-       }
-}
-
 sub print_tool_help
 {
-       my ($cmd, @found, @notfound, @tools);
-       my $gitpath = Git::exec_path();
-
-       find(sub { filter_tool_scripts(\@tools) }, "$gitpath/mergetools");
-
-       foreach my $tool (@tools) {
-               $cmd  = "TOOL_MODE=diff";
-               $cmd .= ' && . "$(git --exec-path)/git-mergetool--lib"';
-               $cmd .= " && get_merge_tool_path $tool >/dev/null 2>&1";
-               $cmd .= " && can_diff >/dev/null 2>&1";
-               if (system('sh', '-c', $cmd) == 0) {
-                       push(@found, $tool);
-               } else {
-                       push(@notfound, $tool);
-               }
-       }
-
-       print << 'EOF';
-'git difftool --tool=<tool>' may be set to one of the following:
-EOF
-       print "\t$_\n" for (sort(@found));
-
-       print << 'EOF';
-
-The following tools are valid, but not currently available:
-EOF
-       print "\t$_\n" for (sort(@notfound));
+       my $cmd = 'TOOL_MODE=diff';
+       $cmd .= ' && . "$(git --exec-path)/git-mergetool--lib"';
+       $cmd .= ' && show_tool_help';
 
-       print << 'EOF';
-
-NOTE: Some of the tools listed above only work in a windowed
-environment. If run in a terminal-only session, they will fail.
-EOF
-       exit(0);
+       # See the comment at the bottom of file_diff() for the reason behind
+       # using system() followed by exit() instead of exec().
+       my $rc = system('sh', '-c', $cmd);
+       exit($rc | ($rc >> 8));
 }
 
 sub exit_cleanup
@@ -124,6 +83,21 @@ sub exit_cleanup
        exit($status | ($status >> 8));
 }
 
+sub use_wt_file
+{
+       my ($repo, $workdir, $file, $sha1, $symlinks) = @_;
+       my $null_sha1 = '0' x 40;
+
+       if ($sha1 eq $null_sha1) {
+               return 1;
+       } elsif (not $symlinks) {
+               return 0;
+       }
+
+       my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
+       return $sha1 eq $wt_sha1;
+}
+
 sub setup_dir_diff
 {
        my ($repo, $workdir, $symlinks) = @_;
@@ -200,10 +174,10 @@ sub setup_dir_diff
                }
 
                if ($rmode ne $null_mode) {
-                       if ($rsha1 ne $null_sha1) {
-                               $rindex .= "$rmode $rsha1\t$dst_path\0";
-                       } else {
+                       if (use_wt_file($repo, $workdir, $dst_path, $rsha1, $symlinks)) {
                                push(@working_tree, $dst_path);
+                       } else {
+                               $rindex .= "$rmode $rsha1\t$dst_path\0";
                        }
                }
        }
@@ -250,7 +224,9 @@ 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
+       # 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) {
                my $dir = dirname($file);
                unless (-d "$rdir/$dir") {