3 test_description='recursive merge corner cases involving criss-cross merges'
17 test_expect_success 'setup basic criss-cross + rename with no modifications' '
18 test_create_repo basic-rename &&
22 ten="0 1 2 3 4 5 6 7 8 9" &&
25 echo line $i in a sample file
29 echo line $i in another sample file
32 test_tick && git commit -m initial &&
37 test_tick && git commit -m R1 &&
41 test_tick && git commit -m L1 &&
44 test_tick && git merge -s ours R1 &&
48 test_tick && git merge -s ours L1 &&
53 test_expect_success 'merge simple rename+criss-cross with no modifications' '
60 test_must_fail git merge -s recursive R2^0 &&
62 git ls-files -s >out &&
63 test_line_count = 2 out &&
64 git ls-files -u >out &&
65 test_line_count = 2 out &&
66 git ls-files -o >out &&
67 test_line_count = 3 out &&
69 git rev-parse >expect \
72 git rev-parse >actual \
74 git hash-object >>actual \
76 test_cmp expect actual
81 # Same as before, but modify L1 slightly:
92 test_expect_success 'setup criss-cross + rename merges with basic modification' '
93 test_create_repo rename-modify &&
97 ten="0 1 2 3 4 5 6 7 8 9" &&
100 echo line $i in a sample file
104 echo line $i in another sample file
107 test_tick && git commit -m initial &&
110 git checkout -b R1 &&
114 test_tick && git commit -m R1 &&
118 test_tick && git commit -m L1 &&
121 test_tick && git merge -s ours R1 &&
125 test_tick && git merge -s ours L1 &&
130 test_expect_success 'merge criss-cross + rename merges with basic modification' '
136 test_must_fail git merge -s recursive R2^0 &&
138 git ls-files -s >out &&
139 test_line_count = 2 out &&
140 git ls-files -u >out &&
141 test_line_count = 2 out &&
142 git ls-files -o >out &&
143 test_line_count = 3 out &&
145 git rev-parse >expect \
148 git rev-parse >actual \
150 git hash-object >>actual \
151 three~HEAD three~R2^0
152 test_cmp expect actual
157 # For the next test, we start with three commits in two lines of development
158 # which setup a rename/add conflict:
159 # Commit A: File 'a' exists
160 # Commit B: Rename 'a' -> 'new_a'
161 # Commit C: Modify 'a', create different 'new_a'
162 # Later, two different people merge and resolve differently:
163 # Commit D: Merge B & C, ignoring separately created 'new_a'
164 # Commit E: Merge B & C making use of some piece of secondary 'new_a'
165 # Finally, someone goes to merge D & E. Does git detect the conflict?
176 test_expect_success 'setup differently handled merges of rename/add conflict' '
177 test_create_repo rename-add &&
181 printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
183 test_tick && git commit -m A &&
188 echo "other content" >>new_a &&
190 test_tick && git commit -m C &&
194 test_tick && git commit -m B &&
197 test_must_fail git merge C &&
199 test_tick && git commit -m D &&
203 test_must_fail git merge B &&
204 rm new_a~HEAD new_a &&
205 printf "Incorrectly merged content" >>new_a &&
207 test_tick && git commit -m E &&
212 test_expect_success 'git detects differently handled merges conflict' '
218 test_must_fail git merge -s recursive E^0 &&
220 git ls-files -s >out &&
221 test_line_count = 3 out &&
222 git ls-files -u >out &&
223 test_line_count = 3 out &&
224 git ls-files -o >out &&
225 test_line_count = 1 out &&
227 git rev-parse >expect \
229 git rev-parse >actual \
231 test_cmp expect actual
233 git cat-file -p B:new_a >ours &&
234 git cat-file -p C:new_a >theirs &&
236 test_must_fail git merge-file \
237 -L "Temporary merge branch 2" \
239 -L "Temporary merge branch 1" \
241 sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
242 git cat-file -p :1:new_a >actual &&
243 test_cmp expect actual
248 # criss-cross + modify/delete:
258 # Commit A: file with contents 'A\n'
259 # Commit B: file with contents 'B\n'
260 # Commit C: file not present
261 # Commit D: file with contents 'B\n'
262 # Commit E: file not present
264 # Merging commits D & E should result in modify/delete conflict.
266 test_expect_success 'setup criss-cross + modify/delete resolved differently' '
267 test_create_repo modify-delete &&
289 test_must_fail git merge C &&
297 test_must_fail git merge B &&
305 test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
311 test_must_fail git merge -s recursive E^0 &&
313 git ls-files -s >out &&
314 test_line_count = 2 out &&
315 git ls-files -u >out &&
316 test_line_count = 2 out &&
318 git rev-parse >expect \
319 master:file B:file &&
320 git rev-parse >actual \
322 test_cmp expect actual
326 test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
333 test_must_fail git merge -s recursive D^0 &&
335 git ls-files -s >out &&
336 test_line_count = 2 out &&
337 git ls-files -u >out &&
338 test_line_count = 2 out &&
340 git rev-parse >expect \
341 master:file B:file &&
342 git rev-parse >actual \
344 test_cmp expect actual
349 # criss-cross + d/f conflict via add/add:
350 # Commit A: Neither file 'a' nor directory 'a/' exists.
351 # Commit B: Introduce 'a'
352 # Commit C: Introduce 'a/file'
353 # Commit D: Merge B & C, keeping 'a' and deleting 'a/'
355 # Two different later cases:
356 # Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
357 # Commit E2: Merge B & C, deleting 'a' but keeping a slightly modified 'a/file'
367 # Merging D & E1 requires we first create a virtual merge base X from
368 # merging A & B in memory. Now, if X could keep both 'a' and 'a/file' in
369 # the index, then the merge of D & E1 could be resolved cleanly with both
370 # 'a' and 'a/file' removed. Since git does not currently allow creating
371 # such a tree, the best we can do is have X contain both 'a~<unique>' and
372 # 'a/file' resulting in the merge of D and E1 having a rename/delete
373 # conflict for 'a'. (Although this merge appears to be unsolvable with git
374 # currently, git could do a lot better than it currently does with these
375 # d/f conflicts, which is the purpose of this test.)
377 # Merge of D & E2 has similar issues for path 'a', but should always result
378 # in a modify/delete conflict for path 'a/file'.
380 # We run each merge in both directions, to check for directional issues
381 # with D/F conflict handling.
384 test_expect_success 'setup differently handled merges of directory/file conflict' '
385 test_create_repo directory-file &&
410 test_must_fail git merge C &&
420 test_must_fail git merge B &&
430 test_must_fail git merge B &&
433 printf "10\n11\n" >a/file &&
441 test_expect_success 'merge of D & E1 fails but has appropriate contents' '
442 test_when_finished "git -C directory-file reset --hard" &&
443 test_when_finished "git -C directory-file clean -fdqx" &&
449 test_must_fail git merge -s recursive E1^0 &&
451 git ls-files -s >out &&
452 test_line_count = 2 out &&
453 git ls-files -u >out &&
454 test_line_count = 1 out &&
455 git ls-files -o >out &&
456 test_line_count = 1 out &&
458 git rev-parse >expect \
460 git rev-parse >actual \
462 test_cmp expect actual
466 test_expect_success 'merge of E1 & D fails but has appropriate contents' '
467 test_when_finished "git -C directory-file reset --hard" &&
468 test_when_finished "git -C directory-file clean -fdqx" &&
474 test_must_fail git merge -s recursive D^0 &&
476 git ls-files -s >out &&
477 test_line_count = 2 out &&
478 git ls-files -u >out &&
479 test_line_count = 1 out &&
480 git ls-files -o >out &&
481 test_line_count = 1 out &&
483 git rev-parse >expect \
485 git rev-parse >actual \
487 test_cmp expect actual
491 test_expect_success 'merge of D & E2 fails but has appropriate contents' '
492 test_when_finished "git -C directory-file reset --hard" &&
493 test_when_finished "git -C directory-file clean -fdqx" &&
499 test_must_fail git merge -s recursive E2^0 &&
501 git ls-files -s >out &&
502 test_line_count = 4 out &&
503 git ls-files -u >out &&
504 test_line_count = 3 out &&
505 git ls-files -o >out &&
506 test_line_count = 2 out &&
508 git rev-parse >expect \
509 B:a E2:a/file c:a/file A:ignore-me &&
510 git rev-parse >actual \
511 :2:a :3:a/file :1:a/file :0:ignore-me &&
512 test_cmp expect actual
514 test_path_is_file a~HEAD
518 test_expect_success 'merge of E2 & D fails but has appropriate contents' '
519 test_when_finished "git -C directory-file reset --hard" &&
520 test_when_finished "git -C directory-file clean -fdqx" &&
526 test_must_fail git merge -s recursive D^0 &&
528 git ls-files -s >out &&
529 test_line_count = 4 out &&
530 git ls-files -u >out &&
531 test_line_count = 3 out &&
532 git ls-files -o >out &&
533 test_line_count = 2 out &&
535 git rev-parse >expect \
536 B:a E2:a/file c:a/file A:ignore-me &&
537 git rev-parse >actual \
538 :3:a :2:a/file :1:a/file :0:ignore-me &&
539 test_cmp expect actual
541 test_path_is_file a~D^0
546 # criss-cross with rename/rename(1to2)/modify followed by
547 # rename/rename(2to1)/modify:
557 # Commit A: new file: a
558 # Commit B: rename a->b, modifying by adding a line
559 # Commit C: rename a->c
560 # Commit D: merge B&C, resolving conflict by keeping contents in newname
561 # Commit E: merge B&C, resolving conflict similar to D but adding another line
563 # There is a conflict merging B & C, but one of filename not of file
564 # content. Whoever created D and E chose specific resolutions for that
565 # conflict resolution. Now, since: (1) there is no content conflict
566 # merging B & C, (2) D does not modify that merged content further, and (3)
567 # both D & E resolve the name conflict in the same way, the modification to
568 # newname in E should not cause any conflicts when it is merged with D.
569 # (Note that this can be accomplished by having the virtual merge base have
570 # the merged contents of b and c stored in a file named a, which seems like
571 # the most logical choice anyway.)
573 # Comment from Junio: I do not necessarily agree with the choice "a", but
574 # it feels sound to say "B and C do not agree what the final pathname
575 # should be, but we know this content was derived from the common A:a so we
576 # use one path whose name is arbitrary in the virtual merge base X between
577 # D and E" and then further let the rename detection to notice that that
578 # arbitrary path gets renamed between X-D to "newname" and X-E also to
579 # "newname" to resolve it as both sides renaming it to the same new
580 # name. It is akin to what we do at the content level, i.e. "B and C do not
581 # agree what the final contents should be, so we leave the conflict marker
582 # but that may cancel out at the final merge stage".
584 test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
585 test_create_repo rename-squared-squared &&
587 cd rename-squared-squared &&
589 printf "1\n2\n3\n4\n5\n6\n" >a &&
594 git checkout -b B A &&
600 git checkout -b C A &&
604 git checkout -q B^0 &&
605 git merge --no-commit -s ours C^0 &&
607 git commit -m "Merge commit C^0 into HEAD" &&
610 git checkout -q C^0 &&
611 git merge --no-commit -s ours B^0 &&
613 printf "7\n8\n" >>newname &&
615 git commit -m "Merge commit B^0 into HEAD" &&
620 test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
622 cd rename-squared-squared &&
626 git merge -s recursive E^0 &&
628 git ls-files -s >out &&
629 test_line_count = 1 out &&
630 git ls-files -u >out &&
631 test_line_count = 0 out &&
632 git ls-files -o >out &&
633 test_line_count = 1 out &&
635 test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
640 # criss-cross with rename/rename(1to2)/add-source + resolvable modify/modify:
650 # Commit A: new file: a
651 # Commit B: rename a->b
652 # Commit C: rename a->c, add different a
653 # Commit D: merge B&C, keeping b&c and (new) a modified at beginning
654 # Commit E: merge B&C, keeping b&c and (new) a modified at end
656 # Merging commits D & E should result in no conflict; doing so correctly
657 # requires getting the virtual merge base (from merging B&C) right, handling
658 # renaming carefully (both in the virtual merge base and later), and getting
659 # content merge handled.
661 test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
662 test_create_repo rename-rename-add-source &&
664 cd rename-rename-add-source &&
666 printf "lots\nof\nwords\nand\ncontent\n" >a &&
671 git checkout -b B A &&
675 git checkout -b C A &&
677 printf "2\n3\n4\n5\n6\n7\n" >a &&
682 git merge --no-commit -s ours C^0 &&
683 git checkout C -- a c &&
689 git commit -m "Merge commit C^0 into HEAD" &&
693 git merge --no-commit -s ours B^0 &&
694 git checkout B -- b &&
697 git commit -m "Merge commit B^0 into HEAD" &&
702 test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
704 cd rename-rename-add-source &&
708 git merge -s recursive E^0 &&
710 git ls-files -s >out &&
711 test_line_count = 3 out &&
712 git ls-files -u >out &&
713 test_line_count = 0 out &&
714 git ls-files -o >out &&
715 test_line_count = 1 out &&
717 printf "1\n2\n3\n4\n5\n6\n7\n8\n" >correct &&
718 git rev-parse >expect \
721 git rev-parse >actual \
723 git hash-object >>actual \
725 test_cmp expect actual
730 # criss-cross with rename/rename(1to2)/add-dest + simple modify:
740 # Commit A: new file: a
741 # Commit B: rename a->b, add c
742 # Commit C: rename a->c
743 # Commit D: merge B&C, keeping A:a and B:c
744 # Commit E: merge B&C, keeping A:a and slightly modified c from B
746 # Merging commits D & E should result in no conflict. The virtual merge
747 # base of B & C needs to not delete B:c for that to work, though...
749 test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
750 test_create_repo rename-rename-add-dest &&
752 cd rename-rename-add-dest &&
759 git checkout -b B A &&
761 printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
765 git checkout -b C A &&
770 git merge --no-commit -s ours C^0 &&
772 git commit -m "D is like B but renames b back to a" &&
776 git merge --no-commit -s ours C^0 &&
780 git commit -m "E like D but has mod in c" &&
785 test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
787 cd rename-rename-add-dest &&
791 git merge -s recursive E^0 &&
793 git ls-files -s >out &&
794 test_line_count = 2 out &&
795 git ls-files -u >out &&
796 test_line_count = 0 out &&
797 git ls-files -o >out &&
798 test_line_count = 1 out &&
800 git rev-parse >expect \
802 git rev-parse >actual \
804 test_cmp expect actual