Raw
1 #!/bin/sh
2
3 test_description="recursive merge corner cases w/ renames but not criss-crosses"
4 # t6036 has corner cases that involve both criss-cross merges and renames
5
6 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
7 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
8
9 . ./test-lib.sh
10
11 test_setup_rename_delete_untracked () {
12 git init rename-delete-untracked &&
13 (
14 cd rename-delete-untracked &&
15
16 echo "A pretty inscription" >ring &&
17 git add ring &&
18 test_tick &&
19 git commit -m beginning &&
20
21 git branch people &&
22 git checkout -b rename-the-ring &&
23 git mv ring one-ring-to-rule-them-all &&
24 test_tick &&
25 git commit -m fullname &&
26
27 git checkout people &&
28 git rm ring &&
29 echo gollum >owner &&
30 git add owner &&
31 test_tick &&
32 git commit -m track-people-instead-of-objects &&
33 echo "Myyy PRECIOUSSS" >ring
34 )
35 }
36
37 test_expect_success "Does git preserve Gollum's precious artifact?" '
38 test_setup_rename_delete_untracked &&
39 (
40 cd rename-delete-untracked &&
41
42 test_must_fail git merge -s recursive rename-the-ring &&
43
44 # Make sure git did not delete an untracked file
45 test_path_is_file ring
46 )
47 '
48
49 # Testcase setup for rename/modify/add-source:
50 # Commit A: new file: a
51 # Commit B: modify a slightly
52 # Commit C: rename a->b, add completely different a
53 #
54 # We should be able to merge B & C cleanly
55
56 test_setup_rename_modify_add_source () {
57 git init rename-modify-add-source &&
58 (
59 cd rename-modify-add-source &&
60
61 printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
62 git add a &&
63 git commit -m A &&
64 git tag A &&
65
66 git checkout -b B A &&
67 echo 8 >>a &&
68 git add a &&
69 git commit -m B &&
70
71 git checkout -b C A &&
72 git mv a b &&
73 echo something completely different >a &&
74 git add a &&
75 git commit -m C
76 )
77 }
78
79 test_expect_failure 'rename/modify/add-source conflict resolvable' '
80 test_setup_rename_modify_add_source &&
81 (
82 cd rename-modify-add-source &&
83
84 git checkout B^0 &&
85
86 git merge -s recursive C^0 &&
87
88 git rev-parse >expect \
89 B:a C:a &&
90 git rev-parse >actual \
91 b c &&
92 test_cmp expect actual
93 )
94 '
95
96 test_setup_break_detection_1 () {
97 git init break-detection-1 &&
98 (
99 cd break-detection-1 &&
100
101 printf "1\n2\n3\n4\n5\n" >a &&
102 echo foo >b &&
103 git add a b &&
104 git commit -m A &&
105 git tag A &&
106
107 git checkout -b B A &&
108 git mv a c &&
109 echo "Completely different content" >a &&
110 git add a &&
111 git commit -m B &&
112
113 git checkout -b C A &&
114 echo 6 >>a &&
115 git add a &&
116 git commit -m C
117 )
118 }
119
120 test_expect_failure 'conflict caused if rename not detected' '
121 test_setup_break_detection_1 &&
122 (
123 cd break-detection-1 &&
124
125 git checkout -q C^0 &&
126 git merge -s recursive B^0 &&
127
128 git ls-files -s >out &&
129 test_line_count = 3 out &&
130 git ls-files -u >out &&
131 test_line_count = 0 out &&
132 git ls-files -o >out &&
133 test_line_count = 1 out &&
134
135 test_line_count = 6 c &&
136 git rev-parse >expect \
137 B:a A:b &&
138 git rev-parse >actual \
139 :0:a :0:b &&
140 test_cmp expect actual
141 )
142 '
143
144 test_setup_break_detection_2 () {
145 git init break-detection-2 &&
146 (
147 cd break-detection-2 &&
148
149 printf "1\n2\n3\n4\n5\n" >a &&
150 echo foo >b &&
151 git add a b &&
152 git commit -m A &&
153 git tag A &&
154
155 git checkout -b D A &&
156 echo 7 >>a &&
157 git add a &&
158 git mv a c &&
159 echo "Completely different content" >a &&
160 git add a &&
161 git commit -m D &&
162
163 git checkout -b E A &&
164 git rm a &&
165 echo "Completely different content" >>a &&
166 git add a &&
167 git commit -m E
168 )
169 }
170
171 test_expect_failure 'missed conflict if rename not detected' '
172 test_setup_break_detection_2 &&
173 (
174 cd break-detection-2 &&
175
176 git checkout -q E^0 &&
177 test_must_fail git merge -s recursive D^0
178 )
179 '
180
181 # Tests for undetected rename/add-source causing a file to erroneously be
182 # deleted (and for mishandled rename/rename(1to1) causing the same issue).
183 #
184 # This test uses a rename/rename(1to1)+add-source conflict (1to1 means the
185 # same file is renamed on both sides to the same thing; it should trigger
186 # the 1to2 logic, which it would do if the add-source didn't cause issues
187 # for git's rename detection):
188 # Commit A: new file: a
189 # Commit B: rename a->b
190 # Commit C: rename a->b, add unrelated a
191
192 test_setup_break_detection_3 () {
193 git init break-detection-3 &&
194 (
195 cd break-detection-3 &&
196
197 printf "1\n2\n3\n4\n5\n" >a &&
198 git add a &&
199 git commit -m A &&
200 git tag A &&
201
202 git checkout -b B A &&
203 git mv a b &&
204 git commit -m B &&
205
206 git checkout -b C A &&
207 git mv a b &&
208 echo foobar >a &&
209 git add a &&
210 git commit -m C
211 )
212 }
213
214 test_expect_failure 'detect rename/add-source and preserve all data' '
215 test_setup_break_detection_3 &&
216 (
217 cd break-detection-3 &&
218
219 git checkout B^0 &&
220
221 git merge -s recursive C^0 &&
222
223 git ls-files -s >out &&
224 test_line_count = 2 out &&
225 git ls-files -u >out &&
226 test_line_count = 2 out &&
227 git ls-files -o >out &&
228 test_line_count = 1 out &&
229
230 test_path_is_file a &&
231 test_path_is_file b &&
232
233 git rev-parse >expect \
234 A:a C:a &&
235 git rev-parse >actual \
236 :0:b :0:a &&
237 test_cmp expect actual
238 )
239 '
240
241 test_expect_failure 'detect rename/add-source and preserve all data, merge other way' '
242 test_setup_break_detection_3 &&
243 (
244 cd break-detection-3 &&
245
246 git checkout C^0 &&
247
248 git merge -s recursive B^0 &&
249
250 git ls-files -s >out &&
251 test_line_count = 2 out &&
252 git ls-files -u >out &&
253 test_line_count = 2 out &&
254 git ls-files -o >out &&
255 test_line_count = 1 out &&
256
257 test_path_is_file a &&
258 test_path_is_file b &&
259
260 git rev-parse >expect \
261 A:a C:a &&
262 git rev-parse >actual \
263 :0:b :0:a &&
264 test_cmp expect actual
265 )
266 '
267
268 test_setup_rename_directory () {
269 git init rename-directory-$1 &&
270 (
271 cd rename-directory-$1 &&
272
273 printf "1\n2\n3\n4\n5\n6\n" >file &&
274 git add file &&
275 test_tick &&
276 git commit -m base &&
277 git tag base &&
278
279 git checkout -b right &&
280 echo 7 >>file &&
281 mkdir newfile &&
282 echo junk >newfile/realfile &&
283 git add file newfile/realfile &&
284 test_tick &&
285 git commit -m right &&
286
287 git checkout -b left-conflict base &&
288 echo 8 >>file &&
289 git add file &&
290 git mv file newfile &&
291 test_tick &&
292 git commit -m left &&
293
294 git checkout -b left-clean base &&
295 echo 0 >newfile &&
296 cat file >>newfile &&
297 git add newfile &&
298 git rm file &&
299 test_tick &&
300 git commit -m left
301 )
302 }
303
304 test_expect_success 'rename/directory conflict + clean content merge' '
305 test_setup_rename_directory 1a &&
306 (
307 cd rename-directory-1a &&
308
309 git checkout left-clean^0 &&
310
311 test_must_fail git merge -s recursive right^0 &&
312
313 git ls-files -s >out &&
314 test_line_count = 2 out &&
315 git ls-files -u >out &&
316 test_line_count = 1 out &&
317 git ls-files -o >out &&
318 test_line_count = 1 out &&
319
320 echo 0 >expect &&
321 git cat-file -p base:file >>expect &&
322 echo 7 >>expect &&
323 test_cmp expect newfile~HEAD &&
324
325 test_path_is_file newfile/realfile &&
326 test_path_is_file newfile~HEAD
327 )
328 '
329
330 test_expect_success 'rename/directory conflict + content merge conflict' '
331 test_setup_rename_directory 1b &&
332 (
333 cd rename-directory-1b &&
334
335 git reset --hard &&
336 git clean -fdqx &&
337
338 git checkout left-conflict^0 &&
339
340 test_must_fail git merge -s recursive right^0 &&
341
342 git ls-files -s >out &&
343 test_line_count = 4 out &&
344 git ls-files -u >out &&
345 test_line_count = 3 out &&
346 git ls-files -o >out &&
347 test_line_count = 1 out &&
348
349 git cat-file -p left-conflict:newfile >left &&
350 git cat-file -p base:file >base &&
351 git cat-file -p right:file >right &&
352 test_must_fail git merge-file \
353 -L "HEAD:newfile" \
354 -L "" \
355 -L "right^0:file" \
356 left base right &&
357 test_cmp left newfile~HEAD &&
358
359 git rev-parse >expect \
360 base:file left-conflict:newfile right:file &&
361 git rev-parse >actual \
362 :1:newfile~HEAD :2:newfile~HEAD :3:newfile~HEAD &&
363 test_cmp expect actual &&
364
365 test_path_is_file newfile/realfile &&
366 test_path_is_file newfile~HEAD
367 )
368 '
369
370 test_setup_rename_directory_2 () {
371 git init rename-directory-2 &&
372 (
373 cd rename-directory-2 &&
374
375 mkdir sub &&
376 printf "1\n2\n3\n4\n5\n6\n" >sub/file &&
377 git add sub/file &&
378 test_tick &&
379 git commit -m base &&
380 git tag base &&
381
382 git checkout -b right &&
383 echo 7 >>sub/file &&
384 git add sub/file &&
385 test_tick &&
386 git commit -m right &&
387
388 git checkout -b left base &&
389 echo 0 >newfile &&
390 cat sub/file >>newfile &&
391 git rm sub/file &&
392 mv newfile sub &&
393 git add sub &&
394 test_tick &&
395 git commit -m left
396 )
397 }
398
399 test_expect_success 'disappearing dir in rename/directory conflict handled' '
400 test_setup_rename_directory_2 &&
401 (
402 cd rename-directory-2 &&
403
404 git checkout left^0 &&
405
406 git merge -s recursive right^0 &&
407
408 git ls-files -s >out &&
409 test_line_count = 1 out &&
410 git ls-files -u >out &&
411 test_line_count = 0 out &&
412 git ls-files -o >out &&
413 test_line_count = 1 out &&
414
415 echo 0 >expect &&
416 git cat-file -p base:sub/file >>expect &&
417 echo 7 >>expect &&
418 test_cmp expect sub &&
419
420 test_path_is_file sub
421 )
422 '
423
424 # Test for basic rename/add-dest conflict, with rename needing content merge:
425 # Commit O: a
426 # Commit A: rename a->b, modifying b too
427 # Commit B: modify a, add different b
428
429 test_setup_rename_with_content_merge_and_add () {
430 git init rename-with-content-merge-and-add-$1 &&
431 (
432 cd rename-with-content-merge-and-add-$1 &&
433
434 test_seq 1 5 >a &&
435 git add a &&
436 git commit -m O &&
437 git tag O &&
438
439 git checkout -b A O &&
440 git mv a b &&
441 test_seq 0 5 >b &&
442 git add b &&
443 git commit -m A &&
444
445 git checkout -b B O &&
446 echo 6 >>a &&
447 echo hello world >b &&
448 git add a b &&
449 git commit -m B
450 )
451 }
452
453 test_expect_success 'handle rename-with-content-merge vs. add' '
454 test_setup_rename_with_content_merge_and_add AB &&
455 (
456 cd rename-with-content-merge-and-add-AB &&
457
458 git checkout A^0 &&
459
460 test_must_fail git merge -s recursive B^0 >out &&
461 test_grep "CONFLICT (.*/add)" out &&
462
463 git ls-files -s >out &&
464 test_line_count = 2 out &&
465 git ls-files -u >out &&
466 test_line_count = 2 out &&
467 # Also, make sure both unmerged entries are for "b"
468 git ls-files -u b >out &&
469 test_line_count = 2 out &&
470 git ls-files -o >out &&
471 test_line_count = 1 out &&
472
473 test_path_is_missing a &&
474 test_path_is_file b &&
475
476 test_seq 0 6 >tmp &&
477 git hash-object tmp >expect &&
478 git rev-parse B:b >>expect &&
479 git rev-parse >actual \
480 :2:b :3:b &&
481 test_cmp expect actual &&
482
483 # Test that the two-way merge in b is as expected
484 git cat-file -p :2:b >>ours &&
485 git cat-file -p :3:b >>theirs &&
486 >empty &&
487 test_must_fail git merge-file \
488 -L "HEAD" \
489 -L "" \
490 -L "B^0" \
491 ours empty theirs &&
492 test_cmp ours b
493 )
494 '
495
496 test_expect_success 'handle rename-with-content-merge vs. add, merge other way' '
497 test_setup_rename_with_content_merge_and_add BA &&
498 (
499 cd rename-with-content-merge-and-add-BA &&
500
501 git reset --hard &&
502 git clean -fdx &&
503
504 git checkout B^0 &&
505
506 test_must_fail git merge -s recursive A^0 >out &&
507 test_grep "CONFLICT (.*/add)" out &&
508
509 git ls-files -s >out &&
510 test_line_count = 2 out &&
511 git ls-files -u >out &&
512 test_line_count = 2 out &&
513 # Also, make sure both unmerged entries are for "b"
514 git ls-files -u b >out &&
515 test_line_count = 2 out &&
516 git ls-files -o >out &&
517 test_line_count = 1 out &&
518
519 test_path_is_missing a &&
520 test_path_is_file b &&
521
522 test_seq 0 6 >tmp &&
523 git rev-parse B:b >expect &&
524 git hash-object tmp >>expect &&
525 git rev-parse >actual \
526 :2:b :3:b &&
527 test_cmp expect actual &&
528
529 # Test that the two-way merge in b is as expected
530 git cat-file -p :2:b >>ours &&
531 git cat-file -p :3:b >>theirs &&
532 >empty &&
533 test_must_fail git merge-file \
534 -L "HEAD" \
535 -L "" \
536 -L "A^0" \
537 ours empty theirs &&
538 test_cmp ours b
539 )
540 '
541
542 # Test for all kinds of things that can go wrong with rename/rename (2to1):
543 # Commit A: new files: a & b
544 # Commit B: rename a->c, modify b
545 # Commit C: rename b->c, modify a
546 #
547 # Merging of B & C should NOT be clean. Questions:
548 # * Both a & b should be removed by the merge; are they?
549 # * The two c's should contain modifications to a & b; do they?
550 # * The index should contain two files, both for c; does it?
551 # * The working copy should have two files, both of form c~<unique>; does it?
552 # * Nothing else should be present. Is anything?
553
554 test_setup_rename_rename_2to1 () {
555 git init rename-rename-2to1 &&
556 (
557 cd rename-rename-2to1 &&
558
559 printf "1\n2\n3\n4\n5\n" >a &&
560 printf "5\n4\n3\n2\n1\n" >b &&
561 git add a b &&
562 git commit -m A &&
563 git tag A &&
564
565 git checkout -b B A &&
566 git mv a c &&
567 echo 0 >>b &&
568 git add b &&
569 git commit -m B &&
570
571 git checkout -b C A &&
572 git mv b c &&
573 echo 6 >>a &&
574 git add a &&
575 git commit -m C
576 )
577 }
578
579 test_expect_success 'handle rename/rename (2to1) conflict correctly' '
580 test_setup_rename_rename_2to1 &&
581 (
582 cd rename-rename-2to1 &&
583
584 git checkout B^0 &&
585
586 test_must_fail git merge -s recursive C^0 >out &&
587 test_grep "CONFLICT (\(.*\)/\1)" out &&
588
589 git ls-files -s >out &&
590 test_line_count = 2 out &&
591 git ls-files -u >out &&
592 test_line_count = 2 out &&
593 git ls-files -u c >out &&
594 test_line_count = 2 out &&
595 git ls-files -o >out &&
596 test_line_count = 1 out &&
597
598 test_path_is_missing a &&
599 test_path_is_missing b &&
600
601 git rev-parse >expect \
602 C:a B:b &&
603 git rev-parse >actual \
604 :2:c :3:c &&
605 test_cmp expect actual &&
606
607 # Test that the two-way merge in new_a is as expected
608 git cat-file -p :2:c >>ours &&
609 git cat-file -p :3:c >>theirs &&
610 >empty &&
611 test_must_fail git merge-file \
612 -L "HEAD" \
613 -L "" \
614 -L "C^0" \
615 ours empty theirs &&
616 git hash-object c >actual &&
617 git hash-object ours >expect &&
618 test_cmp expect actual
619 )
620 '
621
622 # Testcase setup for simple rename/rename (1to2) conflict:
623 # Commit A: new file: a
624 # Commit B: rename a->b
625 # Commit C: rename a->c
626 test_setup_rename_rename_1to2 () {
627 git init rename-rename-1to2 &&
628 (
629 cd rename-rename-1to2 &&
630
631 echo stuff >a &&
632 git add a &&
633 test_tick &&
634 git commit -m A &&
635 git tag A &&
636
637 git checkout -b B A &&
638 git mv a b &&
639 test_tick &&
640 git commit -m B &&
641
642 git checkout -b C A &&
643 git mv a c &&
644 test_tick &&
645 git commit -m C
646 )
647 }
648
649 test_expect_success 'merge has correct working tree contents' '
650 test_setup_rename_rename_1to2 &&
651 (
652 cd rename-rename-1to2 &&
653
654 git checkout C^0 &&
655
656 test_must_fail git merge -s recursive B^0 &&
657
658 git ls-files -s >out &&
659 test_line_count = 3 out &&
660 git ls-files -u >out &&
661 test_line_count = 3 out &&
662 git ls-files -o >out &&
663 test_line_count = 1 out &&
664
665 test_path_is_missing a &&
666 git rev-parse >expect \
667 A:a A:a A:a \
668 A:a A:a &&
669 git rev-parse >actual \
670 :1:a :3:b :2:c &&
671 git hash-object >>actual \
672 b c &&
673 test_cmp expect actual
674 )
675 '
676
677 # Testcase setup for rename/rename(1to2)/add-source conflict:
678 # Commit A: new file: a
679 # Commit B: rename a->b
680 # Commit C: rename a->c, add completely different a
681 #
682 # Merging of B & C should NOT be clean; there's a rename/rename conflict
683
684 test_setup_rename_rename_1to2_add_source_1 () {
685 git init rename-rename-1to2-add-source-1 &&
686 (
687 cd rename-rename-1to2-add-source-1 &&
688
689 printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
690 git add a &&
691 git commit -m A &&
692 git tag A &&
693
694 git checkout -b B A &&
695 git mv a b &&
696 git commit -m B &&
697
698 git checkout -b C A &&
699 git mv a c &&
700 echo something completely different >a &&
701 git add a &&
702 git commit -m C
703 )
704 }
705
706 test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge' '
707 test_setup_rename_rename_1to2_add_source_1 &&
708 (
709 cd rename-rename-1to2-add-source-1 &&
710
711 git checkout B^0 &&
712
713 test_must_fail git merge -s recursive C^0 &&
714
715 git ls-files -s >out &&
716 test_line_count = 4 out &&
717 git ls-files -o >out &&
718 test_line_count = 1 out &&
719
720 git rev-parse >expect \
721 C:a A:a B:b C:C &&
722 git rev-parse >actual \
723 :3:a :1:a :2:b :3:c &&
724 test_cmp expect actual &&
725
726 test_path_is_file a &&
727 test_path_is_file b &&
728 test_path_is_file c
729 )
730 '
731
732 test_setup_rename_rename_1to2_add_source_2 () {
733 git init rename-rename-1to2-add-source-2 &&
734 (
735 cd rename-rename-1to2-add-source-2 &&
736
737 >a &&
738 git add a &&
739 test_tick &&
740 git commit -m base &&
741 git tag A &&
742
743 git checkout -b B A &&
744 git mv a b &&
745 test_tick &&
746 git commit -m one &&
747
748 git checkout -b C A &&
749 git mv a b &&
750 echo important-info >a &&
751 git add a &&
752 test_tick &&
753 git commit -m two
754 )
755 }
756
757 test_expect_failure 'rename/rename/add-source still tracks new a file' '
758 test_setup_rename_rename_1to2_add_source_2 &&
759 (
760 cd rename-rename-1to2-add-source-2 &&
761
762 git checkout C^0 &&
763 git merge -s recursive B^0 &&
764
765 git ls-files -s >out &&
766 test_line_count = 2 out &&
767 git ls-files -o >out &&
768 test_line_count = 1 out &&
769
770 git rev-parse >expect \
771 C:a A:a &&
772 git rev-parse >actual \
773 :0:a :0:b &&
774 test_cmp expect actual
775 )
776 '
777
778 test_setup_rename_rename_1to2_add_dest () {
779 git init rename-rename-1to2-add-dest &&
780 (
781 cd rename-rename-1to2-add-dest &&
782
783 echo stuff >a &&
784 git add a &&
785 test_tick &&
786 git commit -m base &&
787 git tag A &&
788
789 git checkout -b B A &&
790 git mv a b &&
791 echo precious-data >c &&
792 git add c &&
793 test_tick &&
794 git commit -m one &&
795
796 git checkout -b C A &&
797 git mv a c &&
798 echo important-info >b &&
799 git add b &&
800 test_tick &&
801 git commit -m two
802 )
803 }
804
805 test_expect_success 'rename/rename/add-dest merge still knows about conflicting file versions' '
806 test_setup_rename_rename_1to2_add_dest &&
807 (
808 cd rename-rename-1to2-add-dest &&
809
810 git checkout C^0 &&
811 test_must_fail git merge -s recursive B^0 &&
812
813 git ls-files -s >out &&
814 test_line_count = 5 out &&
815 git ls-files -u b >out &&
816 test_line_count = 2 out &&
817 git ls-files -u c >out &&
818 test_line_count = 2 out &&
819 git ls-files -o >out &&
820 test_line_count = 1 out &&
821
822 git rev-parse >expect \
823 A:a C:b B:b C:c B:c &&
824 git rev-parse >actual \
825 :1:a :2:b :3:b :2:c :3:c &&
826 test_cmp expect actual &&
827
828 # Record some contents for re-doing merges
829 git cat-file -p A:a >stuff &&
830 git cat-file -p C:b >important_info &&
831 git cat-file -p B:c >precious_data &&
832 >empty &&
833
834 # Test the merge in b
835 test_must_fail git merge-file \
836 -L "HEAD" \
837 -L "" \
838 -L "B^0" \
839 important_info empty stuff &&
840 test_cmp important_info b &&
841
842 # Test the merge in c
843 test_must_fail git merge-file \
844 -L "HEAD" \
845 -L "" \
846 -L "B^0" \
847 stuff empty precious_data &&
848 test_cmp stuff c
849 )
850 '
851
852 # Testcase rad, rename/add/delete
853 # Commit O: foo
854 # Commit A: rm foo, add different bar
855 # Commit B: rename foo->bar
856 # Expected: CONFLICT (rename/add/delete), two-way merged bar
857
858 test_setup_rad () {
859 git init rad &&
860 (
861 cd rad &&
862 echo "original file" >foo &&
863 git add foo &&
864 git commit -m "original" &&
865
866 git branch O &&
867 git branch A &&
868 git branch B &&
869
870 git checkout A &&
871 git rm foo &&
872 echo "different file" >bar &&
873 git add bar &&
874 git commit -m "Remove foo, add bar" &&
875
876 git checkout B &&
877 git mv foo bar &&
878 git commit -m "rename foo to bar"
879 )
880 }
881
882 test_expect_success 'rad-check: rename/add/delete conflict' '
883 test_setup_rad &&
884 (
885 cd rad &&
886
887 git checkout B^0 &&
888 test_must_fail git merge -s recursive A^0 >out 2>err &&
889
890 # Instead of requiring the output to contain one combined line
891 # CONFLICT (rename/add/delete)
892 # or perhaps two lines:
893 # CONFLICT (rename/add): new file collides with rename target
894 # CONFLICT (rename/delete): rename source removed on other side
895 # and instead of requiring "rename/add" instead of "add/add",
896 # be flexible in the type of console output message(s) reported
897 # for this particular case; we will be more stringent about the
898 # contents of the index and working directory.
899 test_grep "CONFLICT (.*/add)" out &&
900 test_grep "CONFLICT (rename.*/delete)" out &&
901 test_must_be_empty err &&
902
903 git ls-files -s >file_count &&
904 test_line_count = 2 file_count &&
905 git ls-files -u >file_count &&
906 test_line_count = 2 file_count &&
907 git ls-files -o >file_count &&
908 test_line_count = 3 file_count &&
909
910 git rev-parse >actual \
911 :2:bar :3:bar &&
912 git rev-parse >expect \
913 B:bar A:bar &&
914
915 test_path_is_missing foo &&
916 # bar should have two-way merged contents of the different
917 # versions of bar; check that content from both sides is
918 # present.
919 test_grep original bar &&
920 test_grep different bar
921 )
922 '
923
924 # Testcase rrdd, rename/rename(2to1)/delete/delete
925 # Commit O: foo, bar
926 # Commit A: rename foo->baz, rm bar
927 # Commit B: rename bar->baz, rm foo
928 # Expected: CONFLICT (rename/rename/delete/delete), two-way merged baz
929
930 test_setup_rrdd () {
931 git init rrdd &&
932 (
933 cd rrdd &&
934 echo foo >foo &&
935 echo bar >bar &&
936 git add foo bar &&
937 git commit -m O &&
938
939 git branch O &&
940 git branch A &&
941 git branch B &&
942
943 git checkout A &&
944 git mv foo baz &&
945 git rm bar &&
946 git commit -m "Rename foo, remove bar" &&
947
948 git checkout B &&
949 git mv bar baz &&
950 git rm foo &&
951 git commit -m "Rename bar, remove foo"
952 )
953 }
954
955 test_expect_success 'rrdd-check: rename/rename(2to1)/delete/delete conflict' '
956 test_setup_rrdd &&
957 (
958 cd rrdd &&
959
960 git checkout A^0 &&
961 test_must_fail git merge -s recursive B^0 >out 2>err &&
962
963 # Instead of requiring the output to contain one combined line
964 # CONFLICT (rename/rename/delete/delete)
965 # or perhaps two lines:
966 # CONFLICT (rename/rename): ...
967 # CONFLICT (rename/delete): info about pair 1
968 # CONFLICT (rename/delete): info about pair 2
969 # and instead of requiring "rename/rename" instead of "add/add",
970 # be flexible in the type of console output message(s) reported
971 # for this particular case; we will be more stringent about the
972 # contents of the index and working directory.
973 test_grep "CONFLICT (\(.*\)/\1)" out &&
974 test_grep "CONFLICT (rename.*delete)" out &&
975 test_must_be_empty err &&
976
977 git ls-files -s >file_count &&
978 test_line_count = 2 file_count &&
979 git ls-files -u >file_count &&
980 test_line_count = 2 file_count &&
981 git ls-files -o >file_count &&
982 test_line_count = 3 file_count &&
983
984 git rev-parse >actual \
985 :2:baz :3:baz &&
986 git rev-parse >expect \
987 O:foo O:bar &&
988
989 test_path_is_missing foo &&
990 test_path_is_missing bar &&
991 # baz should have two-way merged contents of the original
992 # contents of foo and bar; check that content from both sides
993 # is present.
994 test_grep foo baz &&
995 test_grep bar baz
996 )
997 '
998
999 # Testcase mod6, chains of rename/rename(1to2) and rename/rename(2to1)
1000 # Commit O: one, three, five
1001 # Commit A: one->two, three->four, five->six
1002 # Commit B: one->six, three->two, five->four
1003 # Expected: six CONFLICT(rename/rename) messages, each path in two of the
1004 # multi-way merged contents found in two, four, six
1005
1006 test_setup_mod6 () {
1007 git init mod6 &&
1008 (
1009 cd mod6 &&
1010 test_seq 11 19 >one &&
1011 test_seq 31 39 >three &&
1012 test_seq 51 59 >five &&
1013 git add . &&
1014 test_tick &&
1015 git commit -m "O" &&
1016
1017 git branch O &&
1018 git branch A &&
1019 git branch B &&
1020
1021 git checkout A &&
1022 test_seq 10 19 >one &&
1023 echo 40 >>three &&
1024 git add one three &&
1025 git mv one two &&
1026 git mv three four &&
1027 git mv five six &&
1028 test_tick &&
1029 git commit -m "A" &&
1030
1031 git checkout B &&
1032 echo 20 >>one &&
1033 echo forty >>three &&
1034 echo 60 >>five &&
1035 git add one three five &&
1036 git mv one six &&
1037 git mv three two &&
1038 git mv five four &&
1039 test_tick &&
1040 git commit -m "B"
1041 )
1042 }
1043
1044 test_expect_success 'mod6-check: chains of rename/rename(1to2) and rename/rename(2to1)' '
1045 test_setup_mod6 &&
1046 (
1047 cd mod6 &&
1048
1049 git checkout A^0 &&
1050
1051 test_must_fail git merge -s recursive B^0 >out 2>err &&
1052
1053 test_grep "CONFLICT (rename/rename)" out &&
1054 test_must_be_empty err &&
1055
1056 git ls-files -s >file_count &&
1057 test_line_count = 9 file_count &&
1058 git ls-files -u >file_count &&
1059 test_line_count = 9 file_count &&
1060 git ls-files -o >file_count &&
1061 test_line_count = 3 file_count &&
1062
1063 test_seq 10 20 >merged-one &&
1064 test_seq 51 60 >merged-five &&
1065 # Determine what the merge of three would give us.
1066 test_seq 31 39 >three-base &&
1067 test_seq 31 40 >three-side-A &&
1068 test_seq 31 39 >three-side-B &&
1069 echo forty >>three-side-B &&
1070 test_must_fail git merge-file \
1071 -L "HEAD:four" \
1072 -L "" \
1073 -L "B^0:two" \
1074 three-side-A three-base three-side-B &&
1075 sed -e "s/^\([<=>]\)/\1\1/" three-side-A >merged-three &&
1076
1077 # Verify the index is as expected
1078 git rev-parse >actual \
1079 :2:two :3:two \
1080 :2:four :3:four \
1081 :2:six :3:six &&
1082 git hash-object >expect \
1083 merged-one merged-three \
1084 merged-three merged-five \
1085 merged-five merged-one &&
1086 test_cmp expect actual &&
1087
1088 git cat-file -p :2:two >expect &&
1089 git cat-file -p :3:two >other &&
1090 >empty &&
1091 test_must_fail git merge-file \
1092 -L "HEAD" -L "" -L "B^0" \
1093 expect empty other &&
1094 test_cmp expect two &&
1095
1096 git cat-file -p :2:four >expect &&
1097 git cat-file -p :3:four >other &&
1098 test_must_fail git merge-file \
1099 -L "HEAD" -L "" -L "B^0" \
1100 expect empty other &&
1101 test_cmp expect four &&
1102
1103 git cat-file -p :2:six >expect &&
1104 git cat-file -p :3:six >other &&
1105 test_must_fail git merge-file \
1106 -L "HEAD" -L "" -L "B^0" \
1107 expect empty other &&
1108 test_cmp expect six
1109 )
1110 '
1111
1112 test_conflicts_with_adds_and_renames() {
1113 sideL=$1
1114 sideR=$2
1115
1116 # Setup:
1117 # L
1118 # / \
1119 # main ?
1120 # \ /
1121 # R
1122 #
1123 # Where:
1124 # Both L and R have files named 'three' which collide. Each of
1125 # the colliding files could have been involved in a rename, in
1126 # which case there was a file named 'one' or 'two' that was
1127 # modified on the opposite side of history and renamed into the
1128 # collision on this side of history.
1129 #
1130 # Questions:
1131 # 1) The index should contain both a stage 2 and stage 3 entry
1132 # for the colliding file. Does it?
1133 # 2) When renames are involved, the content merges are clean, so
1134 # the index should reflect the content merges, not merely the
1135 # version of the colliding file from the prior commit. Does
1136 # it?
1137 # 3) There should be a file in the worktree named 'three'
1138 # containing the two-way merged contents of the content-merged
1139 # versions of 'three' from each of the two colliding
1140 # files. Is it present?
1141 # 4) There should not be any three~* files in the working
1142 # tree
1143 test_setup_collision_conflict () {
1144 git init simple_${sideL}_${sideR} &&
1145 (
1146 cd simple_${sideL}_${sideR} &&
1147
1148 # Create some related files now
1149 test_seq -f "Random base content line %d" 1 10 >file_v1 &&
1150 cp file_v1 file_v2 &&
1151 echo modification >>file_v2 &&
1152
1153 cp file_v1 file_v3 &&
1154 echo more stuff >>file_v3 &&
1155 cp file_v3 file_v4 &&
1156 echo yet more stuff >>file_v4 &&
1157
1158 # Use a tag to record both these files for simple
1159 # access, and clean out these untracked files
1160 git tag file_v1 $(git hash-object -w file_v1) &&
1161 git tag file_v2 $(git hash-object -w file_v2) &&
1162 git tag file_v3 $(git hash-object -w file_v3) &&
1163 git tag file_v4 $(git hash-object -w file_v4) &&
1164 git clean -f &&
1165
1166 # Setup original commit (or merge-base), consisting of
1167 # files named "one" and "two" if renames were involved.
1168 touch irrelevant_file &&
1169 git add irrelevant_file &&
1170 if [ $sideL = "rename" ]
1171 then
1172 git show file_v1 >one &&
1173 git add one
1174 fi &&
1175 if [ $sideR = "rename" ]
1176 then
1177 git show file_v3 >two &&
1178 git add two
1179 fi &&
1180 test_tick && git commit -m initial &&
1181
1182 git branch L &&
1183 git branch R &&
1184
1185 # Handle the left side
1186 git checkout L &&
1187 if [ $sideL = "rename" ]
1188 then
1189 git mv one three
1190 else
1191 git show file_v2 >three &&
1192 git add three
1193 fi &&
1194 if [ $sideR = "rename" ]
1195 then
1196 git show file_v4 >two &&
1197 git add two
1198 fi &&
1199 test_tick && git commit -m L &&
1200
1201 # Handle the right side
1202 git checkout R &&
1203 if [ $sideL = "rename" ]
1204 then
1205 git show file_v2 >one &&
1206 git add one
1207 fi &&
1208 if [ $sideR = "rename" ]
1209 then
1210 git mv two three
1211 else
1212 git show file_v4 >three &&
1213 git add three
1214 fi &&
1215 test_tick && git commit -m R
1216 )
1217 }
1218
1219 test_expect_success "check simple $sideL/$sideR conflict" '
1220 test_setup_collision_conflict &&
1221 (
1222 cd simple_${sideL}_${sideR} &&
1223
1224 git checkout L^0 &&
1225
1226 # Merge must fail; there is a conflict
1227 test_must_fail git merge -s recursive R^0 &&
1228
1229 # Make sure the index has the right number of entries
1230 git ls-files -s >out &&
1231 test_line_count = 3 out &&
1232 git ls-files -u >out &&
1233 test_line_count = 2 out &&
1234 # Ensure we have the correct number of untracked files
1235 git ls-files -o >out &&
1236 test_line_count = 1 out &&
1237
1238 # Nothing should have touched irrelevant_file
1239 git rev-parse >actual \
1240 :0:irrelevant_file \
1241 :2:three \
1242 :3:three &&
1243 git rev-parse >expected \
1244 main:irrelevant_file \
1245 file_v2 \
1246 file_v4 &&
1247 test_cmp expected actual &&
1248
1249 # Make sure we have the correct merged contents for
1250 # three
1251 git show file_v1 >expected &&
1252 cat <<-\EOF >>expected &&
1253 <<<<<<< HEAD
1254 modification
1255 =======
1256 more stuff
1257 yet more stuff
1258 >>>>>>> R^0
1259 EOF
1260
1261 test_cmp expected three
1262 )
1263 '
1264 }
1265
1266 test_conflicts_with_adds_and_renames rename rename
1267 test_conflicts_with_adds_and_renames rename add
1268 test_conflicts_with_adds_and_renames add rename
1269 test_conflicts_with_adds_and_renames add add
1270
1271 # Setup:
1272 # L
1273 # / \
1274 # main ?
1275 # \ /
1276 # R
1277 #
1278 # Where:
1279 # main has two files, named 'one' and 'two'.
1280 # branches L and R both modify 'one', in conflicting ways.
1281 # branches L and R both modify 'two', in conflicting ways.
1282 # branch L also renames 'one' to 'three'.
1283 # branch R also renames 'two' to 'three'.
1284 #
1285 # So, we have four different conflicting files that all end up at path
1286 # 'three'.
1287 test_setup_nested_conflicts_from_rename_rename () {
1288 git init nested_conflicts_from_rename_rename &&
1289 (
1290 cd nested_conflicts_from_rename_rename &&
1291
1292 # Create some related files now
1293 test_seq -f "Random base content line %d" 1 10 >file_v1 &&
1294
1295 cp file_v1 file_v2 &&
1296 cp file_v1 file_v3 &&
1297 cp file_v1 file_v4 &&
1298 cp file_v1 file_v5 &&
1299 cp file_v1 file_v6 &&
1300
1301 echo one >>file_v1 &&
1302 echo uno >>file_v2 &&
1303 echo eins >>file_v3 &&
1304
1305 echo two >>file_v4 &&
1306 echo dos >>file_v5 &&
1307 echo zwei >>file_v6 &&
1308
1309 # Setup original commit (or merge-base), consisting of
1310 # files named "one" and "two".
1311 mv file_v1 one &&
1312 mv file_v4 two &&
1313 git add one two &&
1314 test_tick && git commit -m english &&
1315
1316 git branch L &&
1317 git branch R &&
1318
1319 # Handle the left side
1320 git checkout L &&
1321 git rm one two &&
1322 mv -f file_v2 three &&
1323 mv -f file_v5 two &&
1324 git add two three &&
1325 test_tick && git commit -m spanish &&
1326
1327 # Handle the right side
1328 git checkout R &&
1329 git rm one two &&
1330 mv -f file_v3 one &&
1331 mv -f file_v6 three &&
1332 git add one three &&
1333 test_tick && git commit -m german
1334 )
1335 }
1336
1337 test_expect_success 'check nested conflicts from rename/rename(2to1)' '
1338 test_setup_nested_conflicts_from_rename_rename &&
1339 (
1340 cd nested_conflicts_from_rename_rename &&
1341
1342 git checkout L^0 &&
1343
1344 # Merge must fail; there is a conflict
1345 test_must_fail git merge -s recursive R^0 &&
1346
1347 # Make sure the index has the right number of entries
1348 git ls-files -s >out &&
1349 test_line_count = 2 out &&
1350 git ls-files -u >out &&
1351 test_line_count = 2 out &&
1352 # Ensure we have the correct number of untracked files
1353 git ls-files -o >out &&
1354 test_line_count = 1 out &&
1355
1356 # Compare :2:three to expected values
1357 git cat-file -p main:one >base &&
1358 git cat-file -p L:three >ours &&
1359 git cat-file -p R:one >theirs &&
1360 test_must_fail git merge-file \
1361 -L "HEAD:three" -L "" -L "R^0:one" \
1362 ours base theirs &&
1363 sed -e "s/^\([<=>]\)/\1\1/" ours >L-three &&
1364 git cat-file -p :2:three >expect &&
1365 test_cmp expect L-three &&
1366
1367 # Compare :2:three to expected values
1368 git cat-file -p main:two >base &&
1369 git cat-file -p L:two >ours &&
1370 git cat-file -p R:three >theirs &&
1371 test_must_fail git merge-file \
1372 -L "HEAD:two" -L "" -L "R^0:three" \
1373 ours base theirs &&
1374 sed -e "s/^\([<=>]\)/\1\1/" ours >R-three &&
1375 git cat-file -p :3:three >expect &&
1376 test_cmp expect R-three &&
1377
1378 # Compare three to expected contents
1379 >empty &&
1380 test_must_fail git merge-file \
1381 -L "HEAD" -L "" -L "R^0" \
1382 L-three empty R-three &&
1383 test_cmp three L-three
1384 )
1385 '
1386
1387 # Testcase rename/rename(1to2) of a binary file
1388 # Commit O: orig
1389 # Commit A: orig-A
1390 # Commit B: orig-B
1391 # Expected: CONFLICT(rename/rename) message, three unstaged entries in the
1392 # index, and contents of orig-[AB] at path orig-[AB]
1393 test_setup_rename_rename_1_to_2_binary () {
1394 git init rename_rename_1_to_2_binary &&
1395 (
1396 cd rename_rename_1_to_2_binary &&
1397
1398 echo '* binary' >.gitattributes &&
1399 git add .gitattributes &&
1400
1401 test_seq 1 10 >orig &&
1402 git add orig &&
1403 git commit -m orig &&
1404
1405 git branch A &&
1406 git branch B &&
1407
1408 git checkout A &&
1409 git mv orig orig-A &&
1410 test_seq 1 11 >orig-A &&
1411 git add orig-A &&
1412 git commit -m orig-A &&
1413
1414 git checkout B &&
1415 git mv orig orig-B &&
1416 test_seq 0 10 >orig-B &&
1417 git add orig-B &&
1418 git commit -m orig-B
1419
1420 )
1421 }
1422
1423 test_expect_success 'rename/rename(1to2) with a binary file' '
1424 test_setup_rename_rename_1_to_2_binary &&
1425 (
1426 cd rename_rename_1_to_2_binary &&
1427
1428 git checkout A^0 &&
1429
1430 test_must_fail git merge -s recursive B^0 &&
1431
1432 # Make sure the index has the right number of entries
1433 git ls-files -s >actual &&
1434 test_line_count = 4 actual &&
1435
1436 git rev-parse A:orig-A B:orig-B >expect &&
1437 git hash-object orig-A orig-B >actual &&
1438 test_cmp expect actual
1439 )
1440 '
1441
1442 # Testcase preliminary submodule/directory conflict and submodule rename
1443 # Commit O: <empty, or additional irrelevant stuff>
1444 # Commit A1: introduce "folder" (as a tree)
1445 # Commit B1: introduce "folder" (as a submodule)
1446 # Commit A2: merge B1 into A1, but keep folder as a tree
1447 # Commit B2: merge A1 into B1, but keep folder as a submodule
1448 # Merge A2 & B2
1449 test_setup_submodule_directory_preliminary_conflict () {
1450 git init submodule_directory_preliminary_conflict &&
1451 (
1452 cd submodule_directory_preliminary_conflict &&
1453
1454 # Trying to do the A2 and B2 merges above is slightly more
1455 # challenging with a local submodule (because checking out
1456 # another commit has the submodule in the way). Instead,
1457 # first create the commits with the wrong parents but right
1458 # trees, in the order A1, A2, B1, B2...
1459 #
1460 # Then go back and create new A2 & B2 with the correct
1461 # parents and the same trees.
1462
1463 git commit --allow-empty -m orig &&
1464
1465 git branch A &&
1466 git branch B &&
1467
1468 git checkout B &&
1469 mkdir folder &&
1470 echo A>folder/A &&
1471 echo B>folder/B &&
1472 echo C>folder/C &&
1473 echo D>folder/D &&
1474 echo E>folder/E &&
1475 git add folder &&
1476 git commit -m B1 &&
1477
1478 git commit --allow-empty -m B2 &&
1479
1480 git checkout A &&
1481 git init folder &&
1482 (
1483 cd folder &&
1484 >Z &&
1485 >Y &&
1486 git add Z Y &&
1487 git commit -m "original submodule commit"
1488 ) &&
1489 git add folder &&
1490 git commit -m A1 &&
1491
1492 git commit --allow-empty -m A2 &&
1493
1494 NewA2=$(git commit-tree -p A^ -p B^ -m "Merge B into A" A^{tree}) &&
1495 NewB2=$(git commit-tree -p B^ -p A^ -m "Merge A into B" B^{tree}) &&
1496 git update-ref refs/heads/A $NewA2 &&
1497 git update-ref refs/heads/B $NewB2
1498 )
1499 }
1500
1501 test_expect_success 'submodule/directory preliminary conflict' '
1502 test_setup_submodule_directory_preliminary_conflict &&
1503 (
1504 cd submodule_directory_preliminary_conflict &&
1505
1506 git checkout A^0 &&
1507
1508 test_expect_code 1 git merge B^0 &&
1509
1510 # Make sure the index has the right number of entries
1511 git ls-files -s >actual &&
1512 test_line_count = 2 actual &&
1513
1514 # The "folder" as directory should have been resolved away
1515 # as part of the merge. The "folder" as submodule got
1516 # renamed to "folder~Temporary merge branch 2" in the
1517 # virtual merge base, resulting in a
1518 # "folder~Temporary merge branch 2" -> "folder"
1519 # rename in the outermerge for the submodule, which then
1520 # becomes part of a rename/delete conflict (because "folder"
1521 # as a submodule was deleted in A2).
1522 submod=$(git rev-parse A:folder) &&
1523 printf "160000 $submod 1\tfolder\n160000 $submod 2\tfolder\n" >expect &&
1524 test_cmp expect actual
1525 )
1526 '
1527
1528 # Testcase: submodule/directory conflict with duplicate tree entries
1529 # One side has a path as a gitlink (submodule). The other side replaces
1530 # the gitlink with a directory. A third-party tool creates a tree on the
1531 # submodule side that has *both* a gitlink and a tree entry for the same
1532 # path (adding a file inside the submodule path ignoring that there's a
1533 # gitlink there). collect_merge_info_callback() should detect the
1534 # duplicate and abort rather than silently corrupting its bookkeeping.
1535
1536 test_expect_success 'duplicate tree entries trigger an error' '
1537 test_when_finished "rm -rf duplicate-entry" &&
1538 git init duplicate-entry &&
1539 (
1540 cd duplicate-entry &&
1541
1542 # Base commit: "docs" is a gitlink (submodule)
1543 empty_tree=$(git mktree </dev/null) &&
1544 fake_commit=$(git commit-tree $empty_tree </dev/null) &&
1545 git update-index --add --cacheinfo 160000,$fake_commit,docs &&
1546 echo base >file.txt &&
1547 git add file.txt &&
1548 git commit -m base &&
1549
1550 # side1: remove the gitlink, replace with a directory
1551 git checkout -b side1 &&
1552 git rm --cached docs &&
1553 mkdir -p docs &&
1554 echo hello >docs/requirements.txt &&
1555 git add docs/requirements.txt &&
1556 git commit -m "side1: submodule to directory" &&
1557
1558 # side2: keep the gitlink but craft a tree that also
1559 # contains a tree entry for "docs" (simulating a tool
1560 # that adds files inside a submodule path without
1561 # removing the gitlink first).
1562 git checkout main &&
1563 git checkout -b side2 &&
1564 blob_oid=$(echo world | git hash-object -w --stdin) &&
1565 docs_tree=$(printf "100644 blob %s\trequirements.txt\n" \
1566 "$blob_oid" | git mktree) &&
1567 cur_tree=$(git rev-parse HEAD^{tree}) &&
1568 git cat-file -p $cur_tree >tree-listing &&
1569 printf "040000 tree %s\tdocs\n" "$docs_tree" >>tree-listing &&
1570 new_tree=$(git mktree <tree-listing) &&
1571 side2_commit=$(git commit-tree $new_tree -p HEAD \
1572 -m "side2: add file alongside submodule") &&
1573 git update-ref refs/heads/side2 $side2_commit &&
1574
1575 # Merging must detect the duplicate and abort
1576 git checkout side1 &&
1577 test_must_fail git merge side2 2>err &&
1578 test_grep "duplicate entries" err
1579 )
1580 '
1581
1582 test_done