{"thread":{"id":"45271","subject":"[PATCH 1/2] t6042: Add failing test for rename/rename/delete/delete.","startedAt":"2017-03-03T11:42:50Z","lastAt":"2017-03-03T11:43:34Z","messageCount":2,"participants":["Nicolas Cavallari"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"313161","messageId":"20170303113317.18575-1-nicolas.cavallari@green-communications.fr","threadId":"45271","inReplyTo":null,"subject":"[PATCH 1/2] t6042: Add failing test for rename/rename/delete/delete.","fromName":"Nicolas Cavallari","fromEmail":"nicolas.cavallari@green-communications.fr","sentAt":"2017-03-03T11:33:16Z","receivedAt":"2017-03-03T11:42:50Z","isPatch":true,"sender":{"key":"nicolas.cavallari@green-communications.fr","avatar":null},"body":"Each side of a rename 2to1 could also be deleted by the other side.\nThe code does not expect this.  While it luckily works for files,\nit fails for symlinks as follow:\n\nCONFLICT (rename/rename): Rename a->c in HEAD. Rename b->c in C^0\nRenaming a to c~HEAD and b to c~C^0 instead\nerror: cannot read object 0000000000000000000000000000000000000000 'c~C^0'\n\nSigned-off-by: Nicolas Cavallari <nicolas.cavallari@green-communications.fr>\n---\n t/t6042-merge-rename-corner-cases.sh | 38 ++++++++++++++++++++++++++++++++++++\n 1 file changed, 38 insertions(+)\n\ndiff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh\nindex 411550d2b..ea4e14cbd 100755\n--- a/t/t6042-merge-rename-corner-cases.sh\n+++ b/t/t6042-merge-rename-corner-cases.sh\n@@ -575,4 +575,42 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting\n \ttest ! -f c\n '\n \n+# Testcase setup for rename/rename/delete/delete (2in1, deleted):\n+#   Commit A: new symlinks: a->alice b->bob\n+#   Commit B: rename a->c, delete b\n+#   Commit C: rename b->c, delete a\n+#\n+# We should not fail completely.\n+\n+test_expect_success 'setup rename/rename/delete/delete conflict' '\n+\tgit rm -rf . &&\n+\tgit clean -fdqx &&\n+\trm -rf .git &&\n+\tgit init &&\n+\n+\tln -s alice a &&\n+\tln -s bob b &&\n+\tgit add a b &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\n+\tgit checkout -b B A &&\n+\tgit mv a c &&\n+\tgit rm b &&\n+\tgit commit -m B &&\n+\n+\tgit checkout -b C A &&\n+\tgit mv b c &&\n+\tgit rm a &&\n+\tgit commit -m C\n+'\n+\n+test_expect_failure 'rename/rename/delete/delete leaves at least one file' '\n+\tgit checkout B^0 &&\n+\ttest_must_fail git merge -s recursive C^0 &&\n+\n+\ttest -L \"c~HEAD\" &&\n+\ttest -L \"c~C^0\"\n+'\n+\n test_done\n-- \n2.11.0\n\n"},{"id":"313162","messageId":"20170303113317.18575-2-nicolas.cavallari@green-communications.fr","threadId":"45271","inReplyTo":"20170303113317.18575-1-nicolas.cavallari@green-communications.fr","subject":"[PATCH/RFC 2/2] merge-recursive: Handle rename/rename/delete/delete conflicts.","fromName":"Nicolas Cavallari","fromEmail":"nicolas.cavallari@green-communications.fr","sentAt":"2017-03-03T11:33:17Z","receivedAt":"2017-03-03T11:43:34Z","isPatch":true,"sender":{"key":"nicolas.cavallari@green-communications.fr","avatar":null},"body":"On a rename 2to1 conflict, where both a and b are renamed to c,\nthe other branch may have deleted a or b.  This currently luckily\nworks for files but fails for symlinks.\n\nFix conflict_rename_rename_2to1 to not merge_file_special_markers()\nwith diff_filespecs where mode == 0.  The alternative would be to make\nmerge_file_1() handle it.\n\nMark the corresponding testcase in t6042 as fixed.\n\nSigned-off-by: Nicolas Cavallari <nicolas.cavallari@green-communications.fr>\n---\n merge-recursive.c                    | 28 ++++++++++++++++++++++------\n t/t6042-merge-rename-corner-cases.sh |  2 +-\n 2 files changed, 23 insertions(+), 7 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b7ff1ada3..0e5a3e3ed 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1305,13 +1305,29 @@ static int conflict_rename_rename_2to1(struct merge_options *o,\n \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n-\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n-\t\t\t\t       o->branch1, c1->path,\n-\t\t\t\t       o->branch2, ci->ren1_other.path, &mfi_c1) ||\n-\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n-\t\t\t\t       o->branch1, ci->ren2_other.path,\n-\t\t\t\t       o->branch2, c2->path, &mfi_c2))\n+\t// The other branch may have deleted a, as indicated by mode == 0.\n+\tif (ci->ren1_other.mode == 0) {\n+\t\tmfi_c1.clean = 0;\n+\t\tmfi_c1.merge = 0;\n+\t\tmfi_c1.mode = c1->mode;\n+\t\toidcpy(&mfi_c1.oid, &c1->oid);\n+\t} else if (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n+\t\t\t\t\t      o->branch1, c1->path,\n+\t\t\t\t\t      o->branch2, ci->ren1_other.path,\n+\t\t\t\t\t      &mfi_c1)) {\n \t\treturn -1;\n+\t}\n+\n+\tif (ci->ren2_other.mode == 0) {\n+\t\tmfi_c2.clean = 0;\n+\t\tmfi_c2.merge = 0;\n+\t\tmfi_c2.mode = c2->mode;\n+\t\toidcpy(&mfi_c2.oid, &c2->oid);\n+\t} else if (merge_file_special_markers(o, b, &ci->ren2_other, c2,\n+\t\t\t\t\t      o->branch1, ci->ren2_other.path,\n+\t\t\t\t\t      o->branch2, c2->path, &mfi_c2)) {\n+\t\treturn -1;\n+\t}\n \n \tif (o->call_depth) {\n \t\t/*\ndiff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh\nindex ea4e14cbd..34b16d5d7 100755\n--- a/t/t6042-merge-rename-corner-cases.sh\n+++ b/t/t6042-merge-rename-corner-cases.sh\n@@ -605,7 +605,7 @@ test_expect_success 'setup rename/rename/delete/delete conflict' '\n \tgit commit -m C\n '\n \n-test_expect_failure 'rename/rename/delete/delete leaves at least one file' '\n+test_expect_success 'rename/rename/delete/delete leaves at least one file' '\n \tgit checkout B^0 &&\n \ttest_must_fail git merge -s recursive C^0 &&\n \n-- \n2.11.0\n\n"}]}