Merge branch 'jt/partial-clone-fsck-connectivity'
[alt-git.git] / t / t6036-recursive-corner-cases.sh
blobb5621303d67d47146b4af8ee79fe55c338674071
1 #!/bin/sh
3 test_description='recursive merge corner cases involving criss-cross merges'
5 . ./test-lib.sh
8 # L1 L2
9 # o---o
10 # / \ / \
11 # o X ?
12 # \ / \ /
13 # o---o
14 # R1 R2
17 test_expect_success 'setup basic criss-cross + rename with no modifications' '
18 test_create_repo basic-rename &&
20 cd basic-rename &&
22 ten="0 1 2 3 4 5 6 7 8 9" &&
23 for i in $ten
25 echo line $i in a sample file
26 done >one &&
27 for i in $ten
29 echo line $i in another sample file
30 done >two &&
31 git add one two &&
32 test_tick && git commit -m initial &&
34 git branch L1 &&
35 git checkout -b R1 &&
36 git mv one three &&
37 test_tick && git commit -m R1 &&
39 git checkout L1 &&
40 git mv two three &&
41 test_tick && git commit -m L1 &&
43 git checkout L1^0 &&
44 test_tick && git merge -s ours R1 &&
45 git tag L2 &&
47 git checkout R1^0 &&
48 test_tick && git merge -s ours L1 &&
49 git tag R2
53 test_expect_success 'merge simple rename+criss-cross with no modifications' '
55 cd basic-rename &&
57 git reset --hard &&
58 git checkout L2^0 &&
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 \
70 L2:three R2:three \
71 L2:three R2:three &&
72 git rev-parse >actual \
73 :2:three :3:three &&
74 git hash-object >>actual \
75 three~HEAD three~R2^0
76 test_cmp expect actual
81 # Same as before, but modify L1 slightly:
83 # L1m L2
84 # o---o
85 # / \ / \
86 # o X ?
87 # \ / \ /
88 # o---o
89 # R1 R2
92 test_expect_success 'setup criss-cross + rename merges with basic modification' '
93 test_create_repo rename-modify &&
95 cd rename-modify &&
97 ten="0 1 2 3 4 5 6 7 8 9" &&
98 for i in $ten
100 echo line $i in a sample file
101 done >one &&
102 for i in $ten
104 echo line $i in another sample file
105 done >two &&
106 git add one two &&
107 test_tick && git commit -m initial &&
109 git branch L1 &&
110 git checkout -b R1 &&
111 git mv one three &&
112 echo more >>two &&
113 git add two &&
114 test_tick && git commit -m R1 &&
116 git checkout L1 &&
117 git mv two three &&
118 test_tick && git commit -m L1 &&
120 git checkout L1^0 &&
121 test_tick && git merge -s ours R1 &&
122 git tag L2 &&
124 git checkout R1^0 &&
125 test_tick && git merge -s ours L1 &&
126 git tag R2
130 test_expect_success 'merge criss-cross + rename merges with basic modification' '
132 cd rename-modify &&
134 git checkout L2^0 &&
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 \
146 L2:three R2:three \
147 L2:three R2:three &&
148 git rev-parse >actual \
149 :2:three :3:three &&
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?
167 # B D
168 # o---o
169 # / \ / \
170 # A o X ? F
171 # \ / \ /
172 # o---o
173 # C E
176 test_expect_success 'setup differently handled merges of rename/add conflict' '
177 test_create_repo rename-add &&
179 cd rename-add &&
181 printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
182 git add a &&
183 test_tick && git commit -m A &&
185 git branch B &&
186 git checkout -b C &&
187 echo 10 >>a &&
188 echo "other content" >>new_a &&
189 git add a new_a &&
190 test_tick && git commit -m C &&
192 git checkout B &&
193 git mv a new_a &&
194 test_tick && git commit -m B &&
196 git checkout B^0 &&
197 test_must_fail git merge C &&
198 git clean -f &&
199 test_tick && git commit -m D &&
200 git tag D &&
202 git checkout C^0 &&
203 test_must_fail git merge B &&
204 rm new_a~HEAD new_a &&
205 printf "Incorrectly merged content" >>new_a &&
206 git add -u &&
207 test_tick && git commit -m E &&
208 git tag E
212 test_expect_success 'git detects differently handled merges conflict' '
214 cd rename-add &&
216 git checkout D^0 &&
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 \
228 D:new_a E:new_a &&
229 git rev-parse >actual \
230 :2:new_a :3:new_a &&
231 test_cmp expect actual
233 git cat-file -p B:new_a >ours &&
234 git cat-file -p C:new_a >theirs &&
235 >empty &&
236 test_must_fail git merge-file \
237 -L "Temporary merge branch 2" \
238 -L "" \
239 -L "Temporary merge branch 1" \
240 ours empty theirs &&
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:
250 # B D
251 # o---o
252 # / \ / \
253 # A o X ? F
254 # \ / \ /
255 # o---o
256 # C E
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 &&
269 cd modify-delete &&
271 echo A >file &&
272 git add file &&
273 test_tick &&
274 git commit -m A &&
276 git branch B &&
277 git checkout -b C &&
278 git rm file &&
279 test_tick &&
280 git commit -m C &&
282 git checkout B &&
283 echo B >file &&
284 git add file &&
285 test_tick &&
286 git commit -m B &&
288 git checkout B^0 &&
289 test_must_fail git merge C &&
290 echo B >file &&
291 git add file &&
292 test_tick &&
293 git commit -m D &&
294 git tag D &&
296 git checkout C^0 &&
297 test_must_fail git merge B &&
298 git rm file &&
299 test_tick &&
300 git commit -m E &&
301 git tag E
305 test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
307 cd modify-delete &&
309 git checkout D^0 &&
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 \
321 :1:file :2:file &&
322 test_cmp expect actual
326 test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
328 cd modify-delete &&
330 git reset --hard &&
331 git checkout E^0 &&
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 \
343 :1:file :3:file &&
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'
359 # B D
360 # o---o
361 # / \ / \
362 # A o X ? F
363 # \ / \ /
364 # o---o
365 # C E1 or E2
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 &&
387 cd directory-file &&
389 >ignore-me &&
390 git add ignore-me &&
391 test_tick &&
392 git commit -m A &&
393 git tag A &&
395 git branch B &&
396 git checkout -b C &&
397 mkdir a &&
398 echo 10 >a/file &&
399 git add a/file &&
400 test_tick &&
401 git commit -m C &&
403 git checkout B &&
404 echo 5 >a &&
405 git add a &&
406 test_tick &&
407 git commit -m B &&
409 git checkout B^0 &&
410 test_must_fail git merge C &&
411 git clean -f &&
412 rm -rf a/ &&
413 echo 5 >a &&
414 git add a &&
415 test_tick &&
416 git commit -m D &&
417 git tag D &&
419 git checkout C^0 &&
420 test_must_fail git merge B &&
421 git clean -f &&
422 git rm --cached a &&
423 echo 10 >a/file &&
424 git add a/file &&
425 test_tick &&
426 git commit -m E1 &&
427 git tag E1 &&
429 git checkout C^0 &&
430 test_must_fail git merge B &&
431 git clean -f &&
432 git rm --cached a &&
433 printf "10\n11\n" >a/file &&
434 git add a/file &&
435 test_tick &&
436 git commit -m E2 &&
437 git tag E2
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" &&
445 cd directory-file &&
447 git checkout D^0 &&
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 \
459 A:ignore-me B:a &&
460 git rev-parse >actual \
461 :0:ignore-me :2:a &&
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" &&
470 cd directory-file &&
472 git checkout E1^0 &&
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 \
484 A:ignore-me B:a &&
485 git rev-parse >actual \
486 :0:ignore-me :3:a &&
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" &&
495 cd directory-file &&
497 git checkout D^0 &&
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" &&
522 cd directory-file &&
524 git checkout E2^0 &&
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:
549 # B D
550 # o---o
551 # / \ / \
552 # A o X ? F
553 # \ / \ /
554 # o---o
555 # C E
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 &&
590 git add a &&
591 git commit -m A &&
592 git tag A &&
594 git checkout -b B A &&
595 git mv a b &&
596 echo 7 >>b &&
597 git add -u &&
598 git commit -m B &&
600 git checkout -b C A &&
601 git mv a c &&
602 git commit -m C &&
604 git checkout -q B^0 &&
605 git merge --no-commit -s ours C^0 &&
606 git mv b newname &&
607 git commit -m "Merge commit C^0 into HEAD" &&
608 git tag D &&
610 git checkout -q C^0 &&
611 git merge --no-commit -s ours B^0 &&
612 git mv c newname &&
613 printf "7\n8\n" >>newname &&
614 git add -u &&
615 git commit -m "Merge commit B^0 into HEAD" &&
616 git tag E
620 test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
622 cd rename-squared-squared &&
624 git checkout D^0 &&
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:
642 # B D
643 # o---o
644 # / \ / \
645 # A o X ? F
646 # \ / \ /
647 # o---o
648 # C E
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 &&
667 git add a &&
668 git commit -m A &&
669 git tag A &&
671 git checkout -b B A &&
672 git mv a b &&
673 git commit -m B &&
675 git checkout -b C A &&
676 git mv a c &&
677 printf "2\n3\n4\n5\n6\n7\n" >a &&
678 git add a &&
679 git commit -m C &&
681 git checkout B^0 &&
682 git merge --no-commit -s ours C^0 &&
683 git checkout C -- a c &&
684 mv a old_a &&
685 echo 1 >a &&
686 cat old_a >>a &&
687 rm old_a &&
688 git add -u &&
689 git commit -m "Merge commit C^0 into HEAD" &&
690 git tag D &&
692 git checkout C^0 &&
693 git merge --no-commit -s ours B^0 &&
694 git checkout B -- b &&
695 echo 8 >>a &&
696 git add -u &&
697 git commit -m "Merge commit B^0 into HEAD" &&
698 git tag E
702 test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
704 cd rename-rename-add-source &&
706 git checkout D^0 &&
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 \
719 A:a A:a \
720 correct &&
721 git rev-parse >actual \
722 :0:b :0:c &&
723 git hash-object >>actual \
724 a &&
725 test_cmp expect actual
730 # criss-cross with rename/rename(1to2)/add-dest + simple modify:
732 # B D
733 # o---o
734 # / \ / \
735 # A o X ? F
736 # \ / \ /
737 # o---o
738 # C E
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 &&
754 >a &&
755 git add a &&
756 git commit -m A &&
757 git tag A &&
759 git checkout -b B A &&
760 git mv a b &&
761 printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
762 git add c &&
763 git commit -m B &&
765 git checkout -b C A &&
766 git mv a c &&
767 git commit -m C &&
769 git checkout B^0 &&
770 git merge --no-commit -s ours C^0 &&
771 git mv b a &&
772 git commit -m "D is like B but renames b back to a" &&
773 git tag D &&
775 git checkout B^0 &&
776 git merge --no-commit -s ours C^0 &&
777 git mv b a &&
778 echo 8 >>c &&
779 git add c &&
780 git commit -m "E like D but has mod in c" &&
781 git tag E
785 test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
787 cd rename-rename-add-dest &&
789 git checkout D^0 &&
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 \
801 A:a E:c &&
802 git rev-parse >actual \
803 :0:a :0:c &&
804 test_cmp expect actual
808 test_done