Web lists-archives.com

[PATCH 08/30] directory rename detection: files/directories in the way of some renames




Signed-off-by: Elijah Newren <newren@xxxxxxxxx>
---
 t/t6043-merge-rename-directories.sh | 303 ++++++++++++++++++++++++++++++++++++
 1 file changed, 303 insertions(+)

diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh
index ec054b210a..d15153c652 100755
--- a/t/t6043-merge-rename-directories.sh
+++ b/t/t6043-merge-rename-directories.sh
@@ -750,4 +750,307 @@ test_expect_success '4a-check: Directory split, with original directory still pr
 #   detection.)  But, sadly, see testcase 8b.
 ###########################################################################
 
+
+###########################################################################
+# SECTION 5: Files/directories in the way of subset of to-be-renamed paths
+#
+# Implicitly renaming files due to a detected directory rename could run
+# into problems if there are files or directories in the way of the paths
+# we want to rename.  Explore such cases in this section.
+###########################################################################
+
+# Testcase 5a, Merge directories, other side adds files to original and target
+#   Commit A: z/{b,c},       y/d
+#   Commit B: z/{b,c,e_1,f}, y/{d,e_2}
+#   Commit C: y/{b,c,d}
+#   Expected: z/e_1, y/{b,c,d,e_2,f} + CONFLICT warning
+#   NOTE: While directory rename detection is active here causing z/f to
+#         become y/f, we did not apply this for z/e_1 because that would
+#         give us an add/add conflict for y/e_1 vs y/e_2.  This problem with
+#         this add/add, is that both versions of y/e are from the same side
+#         of history, giving us no way to represent this conflict in the
+#         index.
+
+test_expect_success '5a-setup: Merge directories, other side adds files to original and target' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	mkdir z &&
+	echo b >z/b &&
+	echo c >z/c &&
+	mkdir y &&
+	echo d >y/d &&
+	git add z y &&
+	test_tick &&
+	git commit -m "A" &&
+
+	git branch A &&
+	git branch B &&
+	git branch C &&
+
+	git checkout B &&
+	echo e1 >z/e &&
+	echo f >z/f &&
+	echo e2 >y/e &&
+	git add z/e z/f y/e &&
+	test_tick &&
+	git commit -m "B" &&
+
+	git checkout C &&
+	git mv z/b y/ &&
+	git mv z/c y/ &&
+	rmdir z &&
+	test_tick &&
+	git commit -m "C"
+'
+
+test_expect_failure '5a-check: Merge directories, other side adds files to original and target' '
+	git checkout B^0 &&
+
+	test_must_fail git merge -s recursive C^0 >out &&
+
+	test 6 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :0:y/b) = $(git rev-parse A:z/b) &&
+	test $(git rev-parse :0:y/c) = $(git rev-parse A:z/c) &&
+	test $(git rev-parse :0:y/d) = $(git rev-parse A:y/d) &&
+
+	test $(git rev-parse :0:y/e) = $(git rev-parse B:y/e) &&
+	test $(git rev-parse :0:z/e) = $(git rev-parse B:z/e) &&
+
+	test $(git rev-parse :0:y/f) = $(git rev-parse B:z/f) &&
+
+	test_i18ngrep "CONFLICT.*implicit dir rename" out
+'
+
+# Testcase 5b, Rename/delete in order to get add/add/add conflict
+#   (Related to testcase 8d; these may appear slightly inconsistent to users;
+#    Also related to testcases 7d and 7e)
+#   Commit A: z/{b,c,d_1}
+#   Commit B: y/{b,c,d_2}
+#   Commit C: z/{b,c,d_1,e}, y/d_3
+#   Expected: y/{b,c,e}, CONFLICT(add/add: y/d_2 vs. y/d_3)
+#   NOTE: If z/d_1 in commit C were to be involved in dir rename detection, as
+#         we normaly would since z/ is being renamed to y/, then this would be
+#         a rename/delete (z/d_1 -> y/d_1 vs. deleted) AND an add/add/add
+#         conflict of y/d_1 vs. y/d_2 vs. y/d_3.  Add/add/add is not
+#         representable in the index, so the existence of y/d_3 needs to
+#         cause us to bail on directory rename detection for that path, falling
+#         back to git behavior without the directory rename detection.
+
+test_expect_success '5b-setup: Rename/delete in order to get add/add/add conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	mkdir z &&
+	echo b >z/b &&
+	echo c >z/c &&
+	echo d1 >z/d &&
+	git add z &&
+	test_tick &&
+	git commit -m "A" &&
+
+	git branch A &&
+	git branch B &&
+	git branch C &&
+
+	git checkout B &&
+	git rm z/d &&
+	git mv z y &&
+	echo d2 >y/d &&
+	git add y/d &&
+	test_tick &&
+	git commit -m "B" &&
+
+	git checkout C &&
+	mkdir y &&
+	echo d3 >y/d &&
+	echo e >z/e &&
+	git add y/d z/e &&
+	test_tick &&
+	git commit -m "C"
+'
+
+test_expect_failure '5b-check: Rename/delete in order to get add/add/add conflict' '
+	git checkout B^0 &&
+
+	test_must_fail git merge -s recursive C^0 >out &&
+	test_i18ngrep "CONFLICT (add/add).* y/d" out &&
+
+	test 5 -eq $(git ls-files -s | wc -l) &&
+	test 2 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :0:y/b) = $(git rev-parse A:z/b) &&
+	test $(git rev-parse :0:y/c) = $(git rev-parse A:z/c) &&
+	test $(git rev-parse :0:y/e) = $(git rev-parse C:z/e) &&
+
+	test_must_fail git rev-parse :1:y/d &&
+	test $(git rev-parse :2:y/d) = $(git rev-parse B:y/d) &&
+	test $(git rev-parse :3:y/d) = $(git rev-parse C:y/d) &&
+	test -f y/d
+'
+
+# Testcase 5c, Transitive rename would cause rename/rename/rename/add/add/add
+#   (Directory rename detection would result in transitive rename vs.
+#    rename/rename(1to2) and turn it into a rename/rename(1to3).  Further,
+#    rename paths conflict with separate adds on the other side)
+#   (Related to testcases 3b and 7c)
+#   Commit A: z/{b,c}, x/d_1
+#   Commit B: y/{b,c,d_2}, w/d_1
+#   Commit C: z/{b,c,d_1,e}, w/d_3, y/d_4
+#   Expected: A mess, but only a rename/rename(1to2)/add/add mess.  Use the
+#             presence of y/d_4 in C to avoid doing transitive rename of
+#             x/d_1 -> z/d_1 -> y/d_1, so that the only paths we have at
+#             y/d are y/d_2 and y/d_4.  We still do the move from z/e to y/e,
+#             though, because it doesn't have anything in the way.
+
+test_expect_success '5c-setup: Transitive rename would cause rename/rename/rename/add/add/add' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	mkdir z &&
+	echo b >z/b &&
+	echo c >z/c &&
+	mkdir x &&
+	echo d1 >x/d &&
+	git add z x &&
+	test_tick &&
+	git commit -m "A" &&
+
+	git branch A &&
+	git branch B &&
+	git branch C &&
+
+	git checkout B &&
+	git mv z y &&
+	echo d2 >y/d &&
+	git add y/d &&
+	git mv x w &&
+	test_tick &&
+	git commit -m "B" &&
+
+	git checkout C &&
+	git mv x/d z/ &&
+	mkdir w &&
+	mkdir y &&
+	echo d3 >w/d &&
+	echo d4 >y/d &&
+	echo e >z/e &&
+	git add w/ y/ z/e &&
+	test_tick &&
+	git commit -m "C"
+'
+
+test_expect_failure '5c-check: Transitive rename would cause rename/rename/rename/add/add/add' '
+	git checkout B^0 &&
+
+	test_must_fail git merge -s recursive C^0 >out &&
+	test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
+	test_i18ngrep "CONFLICT (add/add).* y/d" out &&
+
+	test 9 -eq $(git ls-files -s | wc -l) &&
+	test 6 -eq $(git ls-files -u | wc -l) &&
+	test 3 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :0:y/b) = $(git rev-parse A:z/b) &&
+	test $(git rev-parse :0:y/c) = $(git rev-parse A:z/c) &&
+	test $(git rev-parse :0:y/e) = $(git rev-parse C:z/e) &&
+
+	test_must_fail git rev-parse :1:y/d &&
+	test $(git rev-parse :2:w/d) = $(git rev-parse A:x/d) &&
+	test $(git rev-parse :3:w/d) = $(git rev-parse C:w/d) &&
+	test $(git rev-parse :1:x/d) = $(git rev-parse A:x/d) &&
+	test $(git rev-parse :2:y/d) = $(git rev-parse B:y/d) &&
+	test $(git rev-parse :3:y/d) = $(git rev-parse C:y/d) &&
+	test $(git rev-parse :3:z/d) = $(git rev-parse A:x/d) &&
+
+	test $(git hash-object w/d~HEAD) = $(git rev-parse A:x/d) &&
+	test $(git hash-object w/d~C^0) = $(git rev-parse C:w/d) &&
+	test ! -f x/d &&
+	test -f y/d &&
+	grep -q "<<<<" y/d &&  # conflict markers should be present
+	test $(git hash-object z/d) = $(git rev-parse A:x/d)
+'
+
+# Testcase 5d, Directory/file/file conflict due to directory rename
+#   Commit A: z/{b,c}
+#   Commit B: y/{b,c,d_1}
+#   Commit C: z/{b,c,d_2,f}, y/d/e
+#   Expected: y/{b,c,d/e,f}, z/d_2, CONFLICT(file/directory), y/d_1~HEAD
+#   Note: The fact that y/d/ exists in C makes us bail on directory rename
+#         detection for z/d_2, but that doesn't prevent us from applying the
+#         directory rename detection for z/f -> y/f.
+
+test_expect_success '5d-setup: Directory/file/file conflict due to directory rename' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	mkdir z &&
+	echo b >z/b &&
+	echo c >z/c &&
+	git add z &&
+	test_tick &&
+	git commit -m "A" &&
+
+	git branch A &&
+	git branch B &&
+	git branch C &&
+
+	git checkout B &&
+	git mv z y &&
+	echo d1 >y/d &&
+	git add y/d &&
+	test_tick &&
+	git commit -m "B" &&
+
+	git checkout C &&
+	mkdir -p y/d &&
+	echo e >y/d/e &&
+	echo d2 >z/d &&
+	echo f >z/f &&
+	git add y/d/e z/d z/f &&
+	test_tick &&
+	git commit -m "C"
+'
+
+test_expect_failure '5d-check: Directory/file/file conflict due to directory rename' '
+	git checkout B^0 &&
+
+	test_must_fail git merge -s recursive C^0 >out &&
+	test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
+
+	test 6 -eq $(git ls-files -s | wc -l) &&
+	test 1 -eq $(git ls-files -u | wc -l) &&
+	test 2 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :0:y/b) = $(git rev-parse A:z/b) &&
+	test $(git rev-parse :0:y/c) = $(git rev-parse A:z/c) &&
+	test $(git rev-parse :0:z/d) = $(git rev-parse C:z/d) &&
+	test $(git rev-parse :0:y/f) = $(git rev-parse C:z/f) &&
+
+	test $(git rev-parse :2:y/d) = $(git rev-parse B:y/d) &&
+	test $(git rev-parse :0:y/d/e) = $(git rev-parse C:y/d/e) &&
+
+	test $(git hash-object y/d~HEAD) = $(git rev-parse B:y/d)
+'
+
+###########################################################################
+# Rules suggested by section 5:
+#
+#   If a subset of to-be-renamed files have a file or directory in the way,
+#   "turn off" the directory rename for those specific sub-paths, falling
+#   back to old handling.  But, sadly, see testcases 8a and 8b.
+###########################################################################
+
 test_done
-- 
2.15.0.5.g9567be9905