From 83e3ad3b126125d23eea10283d85682a1a680daa Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Wed, 18 Sep 2019 13:27:38 -0700 Subject: merge-recursive: symlink's descendants not in way When the working tree has: - bar (directory) - bar/file (file) - foo (symlink to .) (note that lstat() for "foo/bar" would tell us that it is a directory) and the user merges a commit that deletes the foo symlink and instead contains: - bar (directory, as above) - bar/file (file, as above) - foo (directory) - foo/bar (file) the merge should happen without requiring user intervention. However, this does not happen. This is because dir_in_way(), when checking the working tree, thinks that "foo/bar" is a directory. But a symlink should be treated much the same as a file: since dir_in_way() is only checking to see if there is a directory in the way, we don't want symlinks in leading paths to sometimes cause dir_in_way() to return true. Teach dir_in_way() to also check for symlinks in leading paths before reporting whether a directory is in the way. Helped-by: Elijah Newren Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano diff --git a/merge-recursive.c b/merge-recursive.c index 6b812d6..22a12cf 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -764,7 +764,8 @@ static int dir_in_way(struct index_state *istate, const char *path, strbuf_release(&dirpath); return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode) && - !(empty_ok && is_empty_dir(path)); + !(empty_ok && is_empty_dir(path)) && + !has_symlink_leading_path(path, strlen(path)); } /* diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh index ff641b3..faa8892 100755 --- a/t/t3030-merge-recursive.sh +++ b/t/t3030-merge-recursive.sh @@ -452,6 +452,34 @@ test_expect_success 'merge-recursive d/f conflict result' ' ' +test_expect_success SYMLINKS 'dir in working tree with symlink ancestor does not produce d/f conflict' ' + git init sym && + ( + cd sym && + ln -s . foo && + mkdir bar && + >bar/file && + git add foo bar/file && + git commit -m "foo symlink" && + + git checkout -b branch1 && + git commit --allow-empty -m "empty commit" && + + git checkout master && + git rm foo && + mkdir foo && + >foo/bar && + git add foo/bar && + git commit -m "replace foo symlink with real foo dir and foo/bar file" && + + git checkout branch1 && + + git cherry-pick master && + test_path_is_dir foo && + test_path_is_file foo/bar + ) +' + test_expect_success 'reset and 3-way merge' ' git reset --hard "$c2" && -- cgit v0.10.2-6-g49f6