t6423: test directory renames causing rename-to-self

Directory rename detection can cause transitive renames, e.g. if the two different sides of history each do one half of: A/file -> B/file B/ -> C/ then directory rename detection transitively renames to give us C/file. Since the default for merge.directoryRenames is conflict, this results in an error message saying it is unclear whether the file should be placed at B/file or C/file. What if C/ is A/, though? In such a case, the transitive rename would give us A/file, the original name we started with. Logically, having an error message with B/file vs. A/file should be fine, as should leaving the file where it started. But the logic in both merge-recursive and merge-ort did not handle a case of a filename being renamed to itself correctly; merge-recursive had two bugs, and merge-ort had one. Add some testcases covering such a scenario. Based-on-testcase-by: Anders Kaseorg <andersk@mit.edu> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Jun 30, 2021 at 17:29 UTC 806f83287f8d6c0568beed7ca5d603f936d53c40
1 file changed +175
t/t6423-merge-rename-directories.sh
+175
@@ -4966,6 +4966,181 @@ test_expect_success '12g: Testcase with two kinds of "relevant" renames' '
4966 )
4967 '
4968
4969 +# Testcase 12i, Directory rename causes rename-to-self
4970 +# Commit O: source/{subdir/foo, bar, baz_1}
4971 +# Commit A: source/{foo, bar, baz_1}
4972 +# Commit B: source/{subdir/{foo, bar}, baz_2}
4973 +# Expected: source/{foo, bar, baz_2}, with conflicts on
4974 +# source/bar vs. source/subdir/bar
4975 +
4976 +test_setup_12i () {
4977 + test_create_repo 12i &&
4978 + (
4979 + cd 12i &&
4980 +
4981 + mkdir -p source/subdir &&
4982 + echo foo >source/subdir/foo &&
4983 + echo bar >source/bar &&
4984 + echo baz >source/baz &&
4985 + git add source &&
4986 + git commit -m orig &&
4987 +
4988 + git branch O &&
4989 + git branch A &&
4990 + git branch B &&
4991 +
4992 + git switch A &&
4993 + git mv source/subdir/foo source/foo &&
4994 + git commit -m A &&
4995 +
4996 + git switch B &&
4997 + git mv source/bar source/subdir/bar &&
4998 + echo more baz >>source/baz &&
4999 + git commit -m B
5000 + )
5001 +}
5002 +
5003 +test_expect_merge_algorithm failure failure '12i: Directory rename causes rename-to-self' '
5004 + test_setup_12i &&
5005 + (
5006 + cd 12i &&
5007 +
5008 + git checkout A^0 &&
5009 +
5010 + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
5011 +
5012 + test_path_is_missing source/subdir &&
5013 + test_path_is_file source/bar &&
5014 + test_path_is_file source/baz &&
5015 +
5016 + git ls-files | uniq >tracked &&
5017 + test_line_count = 3 tracked &&
5018 +
5019 + git status --porcelain -uno >actual &&
5020 + cat >expect <<-\EOF &&
5021 + UU source/bar
5022 + M source/baz
5023 + EOF
5024 + test_cmp expect actual
5025 + )
5026 +'
5027 +
5028 +# Testcase 12j, Directory rename to root causes rename-to-self
5029 +# Commit O: {subdir/foo, bar, baz_1}
5030 +# Commit A: {foo, bar, baz_1}
5031 +# Commit B: {subdir/{foo, bar}, baz_2}
5032 +# Expected: {foo, bar, baz_2}, with conflicts on bar vs. subdir/bar
5033 +
5034 +test_setup_12j () {
5035 + test_create_repo 12j &&
5036 + (
5037 + cd 12j &&
5038 +
5039 + mkdir -p subdir &&
5040 + echo foo >subdir/foo &&
5041 + echo bar >bar &&
5042 + echo baz >baz &&
5043 + git add . &&
5044 + git commit -m orig &&
5045 +
5046 + git branch O &&
5047 + git branch A &&
5048 + git branch B &&
5049 +
5050 + git switch A &&
5051 + git mv subdir/foo foo &&
5052 + git commit -m A &&
5053 +
5054 + git switch B &&
5055 + git mv bar subdir/bar &&
5056 + echo more baz >>baz &&
5057 + git commit -m B
5058 + )
5059 +}
5060 +
5061 +test_expect_merge_algorithm failure failure '12j: Directory rename to root causes rename-to-self' '
5062 + test_setup_12j &&
5063 + (
5064 + cd 12j &&
5065 +
5066 + git checkout A^0 &&
5067 +
5068 + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
5069 +
5070 + test_path_is_missing subdir &&
5071 + test_path_is_file bar &&
5072 + test_path_is_file baz &&
5073 +
5074 + git ls-files | uniq >tracked &&
5075 + test_line_count = 3 tracked &&
5076 +
5077 + git status --porcelain -uno >actual &&
5078 + cat >expect <<-\EOF &&
5079 + UU bar
5080 + M baz
5081 + EOF
5082 + test_cmp expect actual
5083 + )
5084 +'
5085 +
5086 +# Testcase 12k, Directory rename with sibling causes rename-to-self
5087 +# Commit O: dirB/foo, dirA/{bar, baz_1}
5088 +# Commit A: dirA/{foo, bar, baz_1}
5089 +# Commit B: dirB/{foo, bar}, dirA/baz_2
5090 +# Expected: dirA/{foo, bar, baz_2}, with conflicts on dirA/bar vs. dirB/bar
5091 +
5092 +test_setup_12k () {
5093 + test_create_repo 12k &&
5094 + (
5095 + cd 12k &&
5096 +
5097 + mkdir dirA dirB &&
5098 + echo foo >dirB/foo &&
5099 + echo bar >dirA/bar &&
5100 + echo baz >dirA/baz &&
5101 + git add . &&
5102 + git commit -m orig &&
5103 +
5104 + git branch O &&
5105 + git branch A &&
5106 + git branch B &&
5107 +
5108 + git switch A &&
5109 + git mv dirB/* dirA/ &&
5110 + git commit -m A &&
5111 +
5112 + git switch B &&
5113 + git mv dirA/bar dirB/bar &&
5114 + echo more baz >>dirA/baz &&
5115 + git commit -m B
5116 + )
5117 +}
5118 +
5119 +test_expect_merge_algorithm failure failure '12k: Directory rename with sibling causes rename-to-self' '
5120 + test_setup_12k &&
5121 + (
5122 + cd 12k &&
5123 +
5124 + git checkout A^0 &&
5125 +
5126 + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
5127 +
5128 + test_path_is_missing dirB &&
5129 + test_path_is_file dirA/bar &&
5130 + test_path_is_file dirA/baz &&
5131 +
5132 + git ls-files | uniq >tracked &&
5133 + test_line_count = 3 tracked &&
5134 +
5135 + git status --porcelain -uno >actual &&
5136 + cat >expect <<-\EOF &&
5137 + UU dirA/bar
5138 + M dirA/baz
5139 + EOF
5140 + test_cmp expect actual
5141 + )
5142 +'
5143 +
5144 ###########################################################################
5145 # SECTION 13: Checking informational and conflict messages
5146 #