git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2] merge-recursive: symlink's descendants not in way

From
JTJonathan Tan <jonathantanmy@google.com>
Date
Sep 18, 2019, 20:27 UTC
Message-ID
<20190918202738.57273-1-jonathantanmy@google.com>
In-Reply-To
<https://public-inbox.org/git/20190917215040.132503-1-jonathantanmy@google.com/>
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 <newren@gmail.com>
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
---
Changes from v1:
- Used has_symlink_leading_path(). This drastically shortens the diff.
- Updated commit message following suggestions from Junio, Szeder Gábor,
  and Elijah Newren.
- Updated test to add prereq and verification that the working tree
  contains what we want.
---
 merge-recursive.c          |  3 ++-
 t/t3030-merge-recursive.sh | 28 ++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/merge-recursive.c b/merge-recursive.c
index 6b812d67e3..22a12cfeba 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 ff641b348a..faa8892741 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" &&
-- 
2.23.0.237.gc6a4ce50a0-goog
Next: Elijah Newren
Message 1 of 6 in “merge-recursive: symlink's descendants not in way”
  1. merge-recursive: symlink's descendants not in wayJonathan Tan, Sep 18, 2019
  2. Elijah NewrenSep 18, 2019
  3. Junio C HamanoSep 20, 2019
  4. Jonathan TanSep 20, 2019
  5. Junio C HamanoSep 20, 2019
  6. Jonathan TanSep 20, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.