{"thread":{"id":"58350","subject":"[PATCH] t4301: add more interesting merge-tree testcases","startedAt":"2022-08-23T06:48:58Z","lastAt":"2022-08-23T06:48:58Z","messageCount":1,"participants":["Elijah Newren via GitGitGadget"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"461834","messageId":"pull.1332.git.1661237316150.gitgitgadget@gmail.com","threadId":"58350","inReplyTo":null,"subject":"[PATCH] t4301: add more interesting merge-tree testcases","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T06:48:36Z","receivedAt":"2022-08-23T06:48:58Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nThis adds several tests of `merge-tree -z` extended conflict output\nbehavior to the testsuite, including some tests adapted from t6422.\nThese tests mark current behavior, not necessarily optimal behavior.  In\nparticular, some path_msg() calls might want to include additional\npaths.\n\nThese testcases also make something clear about the <Conflicted file>\ninfo section of the output.  That section consists of a sequence of\nlines of the form\n    <mode> <object> <stage> <filename>\nwhere <stage> is always greater than 0 (since each line comes from a\nconflicted file).  The lines correspond to conflicts that would be\nplaced in the index if we were doing a merge in a working tree.  It is\nperhaps natural to assume that for any given line, the <object> and\n<filename> correspond to a single <revision>:<filename> pair from one of\nthe commits being merged (or from the merge base).  This is true for\nsimple conflicts.  However, these testcases make it clear that this is\nnot the case in general.  For example, <object> may be the hash of a\nthree-way content merge of three different files (and with different\nfilenames).\n\nThe tests no longer pass under TEST_PASSES_SANITIZE_LEAK; it appears\nthat doing a directory rename with \"git mv\", among other possible\nproblems, triggers issues.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n    t4301: add more interesting merge-tree testcases\n    \n    Spurred by\n    https://lore.kernel.org/git/pull.1327.git.1660892256.gitgitgadget@gmail.com\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1332%2Fnewren%2Fmerge-tree-testcases-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1332/newren/merge-tree-testcases-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1332\n\n t/t4301-merge-tree-write-tree.sh | 574 ++++++++++++++++++++++++++++++-\n 1 file changed, 573 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh\nindex a243e3c5176..c5fd56df28f 100755\n--- a/t/t4301-merge-tree-write-tree.sh\n+++ b/t/t4301-merge-tree-write-tree.sh\n@@ -2,7 +2,6 @@\n \n test_description='git merge-tree --write-tree'\n \n-TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This test is ort-specific\n@@ -138,6 +137,579 @@ test_expect_success 'test conflict notices and such' '\n \ttest_cmp expect actual\n '\n \n+# directory rename + content conflict\n+#   Commit O: foo, olddir/{a,b,c}\n+#   Commit A: modify foo, newdir/{a,b,c}\n+#   Commit B: modify foo differently & rename foo -> olddir/bar\n+#   Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)\n+\n+test_expect_success 'directory rename + content conflict' '\n+\t# Setup\n+\tgit init dir-rename-and-content &&\n+\t(\n+\t\tcd dir-rename-and-content &&\n+\t\ttest_write_lines 1 2 3 4 5 >foo &&\n+\t\tmkdir olddir &&\n+\t\tfor i in a b c; do echo $i >olddir/$i; done\n+\t\tgit add foo olddir &&\n+\t\tgit commit -m \"original\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\ttest_write_lines 1 2 3 4 5 6 >foo &&\n+\t\tgit add foo &&\n+\t\tgit mv olddir newdir &&\n+\t\tgit commit -m \"Modify foo, rename olddir to newdir\" &&\n+\n+\t\tgit checkout B &&\n+\t\ttest_write_lines 1 2 3 4 5 six >foo &&\n+\t\tgit add foo &&\n+\t\tgit mv foo olddir/bar &&\n+\t\tgit commit -m \"Modify foo & rename foo -> olddir/bar\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd dir-rename-and-content &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z A^0 B^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\t\tanonymize_hash out >actual &&\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t100644 HASH 1Qnewdir/bar\n+\t\t100644 HASH 2Qnewdir/bar\n+\t\t100644 HASH 3Qnewdir/bar\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\tQ2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.\n+\t\tQ1Qnewdir/barQAuto-mergingQAuto-merging newdir/bar\n+\t\tQ1Qnewdir/barQCONFLICT (contents)QCONFLICT (content): Merge conflict in newdir/bar\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+# rename/delete + modify/delete handling\n+#   Commit O: foo\n+#   Commit A: modify foo + rename to bar\n+#   Commit B: delete foo\n+#   Expected: CONFLICT(rename/delete) + CONFLICT(modify/delete)\n+\n+test_expect_success 'rename/delete handling' '\n+\t# Setup\n+\tgit init rename-delete &&\n+\t(\n+\t\tcd rename-delete &&\n+\t\ttest_write_lines 1 2 3 4 5 >foo &&\n+\t\tgit add foo &&\n+\t\tgit commit -m \"original\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\ttest_write_lines 1 2 3 4 5 6 >foo &&\n+\t\tgit add foo &&\n+\t\tgit mv foo bar &&\n+\t\tgit commit -m \"Modify foo, rename to bar\" &&\n+\n+\t\tgit checkout B &&\n+\t\tgit rm foo &&\n+\t\tgit commit -m \"remove foo\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd rename-delete &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z A^0 B^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\t\tanonymize_hash out >actual &&\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t100644 HASH 1Qbar\n+\t\t100644 HASH 2Qbar\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\tQ2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in A^0, but deleted in B^0.\n+\t\tQ1QbarQCONFLICT (modify/delete)QCONFLICT (modify/delete): bar deleted in B^0 and modified in A^0.  Version A^0 of bar left in tree.\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+# rename/add handling\n+#   Commit O: foo\n+#   Commit A: modify foo, add different bar\n+#   Commit B: modify & rename foo->bar\n+#   Expected: CONFLICT(add/add) [via rename collide] for bar\n+\n+test_expect_success 'rename/add handling' '\n+\t# Setup\n+\tgit init rename-add &&\n+\t(\n+\t\tcd rename-add &&\n+\t\ttest_write_lines original 1 2 3 4 5 >foo &&\n+\t\tgit add foo &&\n+\t\tgit commit -m \"original\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\ttest_write_lines 1 2 3 4 5 >foo &&\n+\t\techo \"different file\" >bar &&\n+\t\tgit add foo bar &&\n+\t\tgit commit -m \"Modify foo, add bar\" &&\n+\n+\t\tgit checkout B &&\n+\t\ttest_write_lines original 1 2 3 4 5 6 >foo &&\n+\t\tgit add foo &&\n+\t\tgit mv foo bar &&\n+\t\tgit commit -m \"rename foo to bar\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd rename-add &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z A^0 B^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\n+\t\t#\n+\t\t# First, check that the bar that appears at stage 3 does not\n+\t\t# correspond to an individual blob anywhere in history\n+\t\t#\n+\t\thash=$(cat out | tr \"\\0\" \"\\n\" | head -n 3 | grep 3.bar | cut -f 2 -d \" \") &&\n+\t\tgit rev-list --objects --all >all_blobs &&\n+\t\t! grep $hash all_blobs &&\n+\n+\t\t#\n+\t\t# Second, check anonymized hash output against expectation\n+\t\t#\n+\t\tanonymize_hash out >actual &&\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t100644 HASH 2Qbar\n+\t\t100644 HASH 3Qbar\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\tQ1QbarQAuto-mergingQAuto-merging bar\n+\t\tQ1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar\n+\t\tQ1QfooQAuto-mergingQAuto-merging foo\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+# rename/add, where add is a mode conflict\n+#   Commit O: foo\n+#   Commit A: modify foo, add symlink bar\n+#   Commit B: modify & rename foo->bar\n+#   Expected: CONFLICT(distinct modes) for bar\n+\n+test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '\n+\t# Setup\n+\tgit init rename-add-symlink &&\n+\t(\n+\t\tcd rename-add-symlink &&\n+\t\ttest_write_lines original 1 2 3 4 5 >foo &&\n+\t\tgit add foo &&\n+\t\tgit commit -m \"original\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\ttest_write_lines 1 2 3 4 5 >foo &&\n+\t\tln -s foo bar &&\n+\t\tgit add foo bar &&\n+\t\tgit commit -m \"Modify foo, add symlink bar\" &&\n+\n+\t\tgit checkout B &&\n+\t\ttest_write_lines original 1 2 3 4 5 6 >foo &&\n+\t\tgit add foo &&\n+\t\tgit mv foo bar &&\n+\t\tgit commit -m \"rename foo to bar\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd rename-add-symlink &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z A^0 B^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\n+\t\t#\n+\t\t# First, check that the bar that appears at stage 3 does not\n+\t\t# correspond to an individual blob anywhere in history\n+\t\t#\n+\t\thash=$(cat out | tr \"\\0\" \"\\n\" | head -n 3 | grep 3.bar | cut -f 2 -d \" \") &&\n+\t\tgit rev-list --objects --all >all_blobs &&\n+\t\t! grep $hash all_blobs &&\n+\n+\t\t#\n+\t\t# Second, check anonymized hash output against expectation\n+\t\t#\n+\t\tanonymize_hash out >actual &&\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t120000 HASH 2Qbar\n+\t\t100644 HASH 3Qbar~B^0\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\tQ2QbarQbar~B^0QCONFLICT (distinct modes)QCONFLICT (distinct types): bar had different types on each side; renamed one of them so each can be recorded somewhere.\n+\t\tQ1QfooQAuto-mergingQAuto-merging foo\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+# rename/rename(1to2) + content conflict handling\n+#   Commit O: foo\n+#   Commit A: modify foo & rename to bar\n+#   Commit B: modify foo & rename to baz\n+#   Expected: CONFLICT(rename/rename)\n+\n+test_expect_success 'rename/rename + content conflict' '\n+\t# Setup\n+\tgit init rr-plus-content &&\n+\t(\n+\t\tcd rr-plus-content &&\n+\t\ttest_write_lines 1 2 3 4 5 >foo &&\n+\t\tgit add foo &&\n+\t\tgit commit -m \"original\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\ttest_write_lines 1 2 3 4 5 six >foo &&\n+\t\tgit add foo &&\n+\t\tgit mv foo bar &&\n+\t\tgit commit -m \"Modify foo + rename to bar\" &&\n+\n+\t\tgit checkout B &&\n+\t\ttest_write_lines 1 2 3 4 5 6 >foo &&\n+\t\tgit add foo &&\n+\t\tgit mv foo baz &&\n+\t\tgit commit -m \"Modify foo + rename to baz\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd rr-plus-content &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z A^0 B^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\t\tanonymize_hash out >actual &&\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t100644 HASH 2Qbar\n+\t\t100644 HASH 3Qbaz\n+\t\t100644 HASH 1Qfoo\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\tQ1QfooQAuto-mergingQAuto-merging foo\n+\t\tQ3QfooQbarQbazQCONFLICT (rename/rename)QCONFLICT (rename/rename): foo renamed to bar in A^0 and to baz in B^0.\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+# rename/add/delete\n+#   Commit O: foo\n+#   Commit A: rm foo, add different bar\n+#   Commit B: rename foo->bar\n+#   Expected: CONFLICT (rename/delete), CONFLICT(add/add) [via rename collide]\n+#             for bar\n+\n+test_expect_success 'rename/add/delete conflict' '\n+\t# Setup\n+\tgit init rad &&\n+\t(\n+\t\tcd rad &&\n+\t\techo \"original file\" >foo &&\n+\t\tgit add foo &&\n+\t\tgit commit -m \"original\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit rm foo &&\n+\t\techo \"different file\" >bar &&\n+\t\tgit add bar &&\n+\t\tgit commit -m \"Remove foo, add bar\" &&\n+\n+\t\tgit checkout B &&\n+\t\tgit mv foo bar &&\n+\t\tgit commit -m \"rename foo to bar\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd rad &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z B^0 A^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\t\tanonymize_hash out >actual &&\n+\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t100644 HASH 2Qbar\n+\t\t100644 HASH 3Qbar\n+\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\t2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in B^0, but deleted in A^0.\n+\t\tQ1QbarQAuto-mergingQAuto-merging bar\n+\t\tQ1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+# rename/rename(2to1)/delete/delete\n+#   Commit O: foo, bar\n+#   Commit A: rename foo->baz, rm bar\n+#   Commit B: rename bar->baz, rm foo\n+#   Expected: 2x CONFLICT (rename/delete), CONFLICT (add/add) via colliding\n+#             renames for baz\n+\n+test_expect_success 'rename/rename(2to1)/delete/delete conflict' '\n+\t# Setup\n+\tgit init rrdd &&\n+\t(\n+\t\tcd rrdd &&\n+\t\techo foo >foo &&\n+\t\techo bar >bar &&\n+\t\tgit add foo bar &&\n+\t\tgit commit -m O &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv foo baz &&\n+\t\tgit rm bar &&\n+\t\tgit commit -m \"Rename foo, remove bar\" &&\n+\n+\t\tgit checkout B &&\n+\t\tgit mv bar baz &&\n+\t\tgit rm foo &&\n+\t\tgit commit -m \"Rename bar, remove foo\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd rrdd &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z A^0 B^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\t\tanonymize_hash out >actual &&\n+\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t100644 HASH 2Qbaz\n+\t\t100644 HASH 3Qbaz\n+\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\t2QbazQbarQCONFLICT (rename/delete)QCONFLICT (rename/delete): bar renamed to baz in B^0, but deleted in A^0.\n+\t\tQ2QbazQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to baz in A^0, but deleted in B^0.\n+\t\tQ1QbazQAuto-mergingQAuto-merging baz\n+\t\tQ1QbazQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in baz\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+# mod6: chains of rename/rename(1to2) + add/add via colliding renames\n+#   Commit O: one,      three,       five\n+#   Commit A: one->two, three->four, five->six\n+#   Commit B: one->six, three->two,  five->four\n+#   Expected: three CONFLICT(rename/rename) messages + three CONFLICT(add/add)\n+#             messages; each path in two of the multi-way merged contents\n+#             found in two, four, six\n+\n+test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via colliding renames' '\n+\t# Setup\n+\tgit init mod6 &&\n+\t(\n+\t\tcd mod6 &&\n+\t\ttest_seq 11 19 >one &&\n+\t\ttest_seq 31 39 >three &&\n+\t\ttest_seq 51 59 >five &&\n+\t\tgit add . &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\ttest_seq 10 19 >one &&\n+\t\techo 40        >>three &&\n+\t\tgit add one three &&\n+\t\tgit mv  one   two  &&\n+\t\tgit mv  three four &&\n+\t\tgit mv  five  six  &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\techo 20    >>one       &&\n+\t\techo forty >>three     &&\n+\t\techo 60    >>five      &&\n+\t\tgit add one three five &&\n+\t\tgit mv  one   six  &&\n+\t\tgit mv  three two  &&\n+\t\tgit mv  five  four &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd mod6 &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z A^0 B^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\n+\t\t#\n+\t\t# First, check that some of the hashes that appear as stage\n+\t\t# conflict entries do not appear as individual blobs anywhere\n+\t\t# in history.\n+\t\t#\n+\t\thash1=$(cat out | tr \"\\0\" \"\\n\" | head | grep 2.four | cut -f 2 -d \" \") &&\n+\t\thash2=$(cat out | tr \"\\0\" \"\\n\" | head | grep 3.two | cut -f 2 -d \" \") &&\n+\t\tgit rev-list --objects --all >all_blobs &&\n+\t\t! grep $hash1 all_blobs &&\n+\t\t! grep $hash2 all_blobs &&\n+\n+\t\t#\n+\t\t# Now compare anonymized hash output with expectation\n+\t\t#\n+\t\tanonymize_hash out >actual &&\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t100644 HASH 1Qfive\n+\t\t100644 HASH 2Qfour\n+\t\t100644 HASH 3Qfour\n+\t\t100644 HASH 1Qone\n+\t\t100644 HASH 2Qsix\n+\t\t100644 HASH 3Qsix\n+\t\t100644 HASH 1Qthree\n+\t\t100644 HASH 2Qtwo\n+\t\t100644 HASH 3Qtwo\n+\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\t3QfiveQsixQfourQCONFLICT (rename/rename)QCONFLICT (rename/rename): five renamed to six in A^0 and to four in B^0.\n+\t\tQ1QfourQAuto-mergingQAuto-merging four\n+\t\tQ1QfourQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in four\n+\t\tQ1QoneQAuto-mergingQAuto-merging one\n+\t\tQ3QoneQtwoQsixQCONFLICT (rename/rename)QCONFLICT (rename/rename): one renamed to two in A^0 and to six in B^0.\n+\t\tQ1QsixQAuto-mergingQAuto-merging six\n+\t\tQ1QsixQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in six\n+\t\tQ1QthreeQAuto-mergingQAuto-merging three\n+\t\tQ3QthreeQfourQtwoQCONFLICT (rename/rename)QCONFLICT (rename/rename): three renamed to four in A^0 and to two in B^0.\n+\t\tQ1QtwoQAuto-mergingQAuto-merging two\n+\t\tQ1QtwoQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in two\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+# directory rename + rename/delete + modify/delete + directory/file conflict\n+#   Commit O: foo, olddir/{a,b,c}\n+#   Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file\n+#   Commit B: modify foo & rename foo -> olddir/bar\n+#   Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)\n+\n+test_expect_success 'directory rename + rename/delete + modify/delete + directory/file conflict' '\n+\t# Setup\n+\tgit init 4-stacked-conflict &&\n+\t(\n+\t\tcd 4-stacked-conflict &&\n+\t\ttest_write_lines 1 2 3 4 5 >foo &&\n+\t\tmkdir olddir &&\n+\t\tfor i in a b c; do echo $i >olddir/$i; done\n+\t\tgit add foo olddir &&\n+\t\tgit commit -m \"original\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit rm foo &&\n+\t\tgit mv olddir newdir &&\n+\t\tmkdir newdir/bar &&\n+\t\t>newdir/bar/file &&\n+\t\tgit add newdir/bar/file &&\n+\t\tgit commit -m \"rm foo, olddir/ -> newdir/, + newdir/bar/file\" &&\n+\n+\t\tgit checkout B &&\n+\t\ttest_write_lines 1 2 3 4 5 6 >foo &&\n+\t\tgit add foo &&\n+\t\tgit mv foo olddir/bar &&\n+\t\tgit commit -m \"Modify foo & rename foo -> olddir/bar\"\n+\t) &&\n+\t# Testing\n+\t(\n+\t\tcd 4-stacked-conflict &&\n+\n+\t\ttest_expect_code 1 \\\n+\t\t\tgit merge-tree -z A^0 B^0 >out &&\n+\t\tprintf \"\\\\n\" >>out &&\n+\t\tanonymize_hash out >actual &&\n+\n+\t\tq_to_tab <<-\\EOF | lf_to_nul >expect &&\n+\t\tHASH\n+\t\t100644 HASH 1Qnewdir/bar~B^0\n+\t\t100644 HASH 3Qnewdir/bar~B^0\n+\t\tEOF\n+\n+\t\tq_to_nul <<-EOF >>expect &&\n+\t\tQ2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.\n+\t\tQ2Qnewdir/barQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to newdir/bar in B^0, but deleted in A^0.\n+\t\tQ2Qnewdir/bar~B^0Qnewdir/barQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of newdir/bar from B^0; moving it to newdir/bar~B^0 instead.\n+\t\tQ1Qnewdir/bar~B^0QCONFLICT (modify/delete)QCONFLICT (modify/delete): newdir/bar~B^0 deleted in A^0 and modified in B^0.  Version B^0 of newdir/bar~B^0 left in tree.\n+\t\tQ\n+\t\tEOF\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n for opt in $(git merge-tree --git-completion-helper-all)\n do\n \tif test $opt = \"--trivial-merge\" || test $opt = \"--write-tree\"\n\nbase-commit: 795ea8776befc95ea2becd8020c7a284677b4161\n-- \ngitgitgadget\n"}]}