Skip to content

Commit 5bafb35

Browse files
davvidgitster
authored andcommitted
difftool: fix symlink-file writing in dir-diff mode
The difftool dir-diff mode handles symlinks by replacing them with their readlink(2) values. This allows diff tools to see changes to symlinks as if they were regular text diffs with the old and new path values. This is analogous to what "git diff" displays when symlinks change. The temporary diff directories that are created initially contain symlinks because they get checked-out using a temporary index that retains the original symlinks as checked-in to the repository. A bug was introduced when difftool was rewritten in C that made difftool write the readlink(2) contents into the pointed-to file rather than the symlink itself. The write was going through the symlink and writing to its target rather than writing to the symlink path itself. Replace symlinks with raw text files by unlinking the symlink path before writing the readlink(2) content into them. When 18ec800 (difftool: handle modified symlinks in dir-diff mode, 2017-03-15) added handling for modified symlinks this bug got recorded in the test suite. The tests included the pointed-to symlink target paths. These paths were being reported because difftool was erroneously writing to them, but they should have never been reported nor written. Correct the modified-symlinks test cases by removing the target files from the expected output. Add a test to ensure that symlinks are written with the readlink(2) values and that the target files contain their original content. Reported-by: Alan Blotz <[email protected]> Helped-by: Đoàn Trần Công Danh <[email protected]> Signed-off-by: David Aguilar <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ebf3c04 commit 5bafb35

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

builtin/difftool.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,11 +562,13 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
562562
if (*entry->left) {
563563
add_path(&ldir, ldir_len, entry->path);
564564
ensure_leading_directories(ldir.buf);
565+
unlink(ldir.buf);
565566
write_file(ldir.buf, "%s", entry->left);
566567
}
567568
if (*entry->right) {
568569
add_path(&rdir, rdir_len, entry->path);
569570
ensure_leading_directories(rdir.buf);
571+
unlink(rdir.buf);
570572
write_file(rdir.buf, "%s", entry->right);
571573
}
572574
}

t/t7800-difftool.sh

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,6 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
674674
rm c &&
675675
ln -s d c &&
676676
cat >expect <<-EOF &&
677-
b
678677
c
679678
680679
c
@@ -710,7 +709,6 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
710709
# Deleted symlinks
711710
rm -f c &&
712711
cat >expect <<-EOF &&
713-
b
714712
c
715713
716714
EOF
@@ -723,6 +721,71 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
723721
test_cmp expect actual
724722
'
725723

724+
test_expect_success SYMLINKS 'difftool --dir-diff writes symlinks as raw text' '
725+
# Start out on a branch called "branch-init".
726+
git init -b branch-init symlink-files &&
727+
(
728+
cd symlink-files &&
729+
# This test ensures that symlinks are written as raw text.
730+
# The "cat" tools output link and file contents.
731+
git config difftool.cat-left-link.cmd "cat \"\$LOCAL/link\"" &&
732+
git config difftool.cat-left-a.cmd "cat \"\$LOCAL/file-a\"" &&
733+
git config difftool.cat-right-link.cmd "cat \"\$REMOTE/link\"" &&
734+
git config difftool.cat-right-b.cmd "cat \"\$REMOTE/file-b\"" &&
735+
736+
# Record the empty initial state so that we can come back here
737+
# later and not have to consider the any cases where difftool
738+
# will create symlinks back into the worktree.
739+
test_tick &&
740+
git commit --allow-empty -m init &&
741+
742+
# Create a file called "file-a" with a symlink pointing to it.
743+
git switch -c branch-a &&
744+
echo a >file-a &&
745+
ln -s file-a link &&
746+
git add file-a link &&
747+
test_tick &&
748+
git commit -m link-to-file-a &&
749+
750+
# Create a file called "file-b" and point the symlink to it.
751+
git switch -c branch-b &&
752+
echo b >file-b &&
753+
rm link &&
754+
ln -s file-b link &&
755+
git add file-b link &&
756+
git rm file-a &&
757+
test_tick &&
758+
git commit -m link-to-file-b &&
759+
760+
# Checkout the initial branch so that the --symlinks behavior is
761+
# not activated. The two directories should be completely
762+
# independent with no symlinks pointing back here.
763+
git switch branch-init &&
764+
765+
# The left link must be "file-a" and "file-a" must contain "a".
766+
echo file-a >expect &&
767+
git difftool --symlinks --dir-diff --tool cat-left-link \
768+
branch-a branch-b >actual &&
769+
test_cmp expect actual &&
770+
771+
echo a >expect &&
772+
git difftool --symlinks --dir-diff --tool cat-left-a \
773+
branch-a branch-b >actual &&
774+
test_cmp expect actual &&
775+
776+
# The right link must be "file-b" and "file-b" must contain "b".
777+
echo file-b >expect &&
778+
git difftool --symlinks --dir-diff --tool cat-right-link \
779+
branch-a branch-b >actual &&
780+
test_cmp expect actual &&
781+
782+
echo b >expect &&
783+
git difftool --symlinks --dir-diff --tool cat-right-b \
784+
branch-a branch-b >actual &&
785+
test_cmp expect actual
786+
)
787+
'
788+
726789
test_expect_success 'add -N and difftool -d' '
727790
test_when_finished git reset --hard &&
728791

0 commit comments

Comments
 (0)