difftool --dir-diff: symlink all files matching the working tree
authorJohn Keeping <john@keeping.me.uk>
Thu, 14 Mar 2013 20:19:41 +0000 (20:19 +0000)
committerJunio C Hamano <gitster@pobox.com>
Thu, 14 Mar 2013 21:33:06 +0000 (14:33 -0700)
Some users like to edit files in their diff tool when using "git
difftool --dir-diff --symlink" to compare against the working tree but
difftool currently only created symlinks when a file contains unstaged
changes.

Change this behaviour so that symlinks are created whenever the
right-hand side of the comparison has the same SHA1 as the file in the
working tree.

Note that textconv filters are handled in the same way as by git-diff
and if a clean filter is not the inverse of its smudge filter we already
get a null SHA1 from "diff --raw" and will symlink the file without
going through the new hash-object based check.

Signed-off-by: John Keeping <john@keeping.me.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-difftool.txt
git-difftool.perl
t/t7800-difftool.sh
index e575fea1ad77eef80cd0460b66b7925a08a65f0d..8361e6e4e3d2d4829d3ce3c6f3ec4a2ac974e86e 100644 (file)
@@ -72,7 +72,9 @@ with custom merge tool commands and has the same value as `$MERGED`.
 --symlinks::
 --no-symlinks::
        'git difftool''s default behavior is create symlinks to the
-       working tree when run in `--dir-diff` mode.
+       working tree when run in `--dir-diff` mode and the right-hand
+       side of the comparison yields the same content as the file in
+       the working tree.
 +
 Specifying `--no-symlinks` instructs 'git difftool' to create copies
 instead.  `--no-symlinks` is the default on Windows.
index 3cab257595a5f4dd50c474733a39f76bc897eb4e..c433e86f09e9c10e2bf94d7e5cc61364a33aab2b 100755 (executable)
@@ -83,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) = @_;
@@ -159,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";
                        }
                }
        }
index eb1d3f85b59b2e74231f0285fc4e90d95d0658fa..db3d3d6bd1d8a979ee757bb7faca0adec853485b 100755 (executable)
@@ -370,6 +370,28 @@ test_expect_success PERL 'difftool --dir-diff' '
        echo "$diff" | stdin_contains file
 '
 
+write_script .git/CHECK_SYMLINKS <<\EOF
+for f in file file2 sub/sub
+do
+       echo "$f"
+       readlink "$2/$f"
+done >actual
+EOF
+
+test_expect_success PERL,SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' '
+       cat >expect <<-EOF &&
+       file
+       $(pwd)/file
+       file2
+       $(pwd)/file2
+       sub/sub
+       $(pwd)/sub/sub
+       EOF
+       git difftool --dir-diff --symlink \
+               --extcmd "./.git/CHECK_SYMLINKS" branch HEAD &&
+       test_cmp actual expect
+'
+
 test_expect_success PERL 'difftool --dir-diff ignores --prompt' '
        diff=$(git difftool --dir-diff --prompt --extcmd ls branch) &&
        echo "$diff" | stdin_contains sub &&