e5f167a06eeb4c14d779021e003f3833c227b73d
1#!/bin/sh
2
3test_description='recursive merge corner cases involving criss-cross merges'
4
5. ./test-lib.sh
6
7get_clean_checkout () {
8 git reset --hard &&
9 git clean -fdqx &&
10 git checkout "$1"
11}
12
13#
14# L1 L2
15# o---o
16# / \ / \
17# o X ?
18# \ / \ /
19# o---o
20# R1 R2
21#
22
23test_expect_success 'setup basic criss-cross + rename with no modifications' '
24 test_create_repo basic-rename &&
25 (
26 cd basic-rename &&
27
28 ten="0 1 2 3 4 5 6 7 8 9" &&
29 for i in $ten
30 do
31 echo line $i in a sample file
32 done >one &&
33 for i in $ten
34 do
35 echo line $i in another sample file
36 done >two &&
37 git add one two &&
38 test_tick && git commit -m initial &&
39
40 git branch L1 &&
41 git checkout -b R1 &&
42 git mv one three &&
43 test_tick && git commit -m R1 &&
44
45 git checkout L1 &&
46 git mv two three &&
47 test_tick && git commit -m L1 &&
48
49 git checkout L1^0 &&
50 test_tick && git merge -s ours R1 &&
51 git tag L2 &&
52
53 git checkout R1^0 &&
54 test_tick && git merge -s ours L1 &&
55 git tag R2
56 )
57'
58
59test_expect_success 'merge simple rename+criss-cross with no modifications' '
60 (
61 cd basic-rename &&
62
63 git reset --hard &&
64 git checkout L2^0 &&
65
66 test_must_fail git merge -s recursive R2^0 &&
67
68 git ls-files -s >out &&
69 test_line_count = 2 out &&
70 git ls-files -u >out &&
71 test_line_count = 2 out &&
72 git ls-files -o >out &&
73 test_line_count = 3 out &&
74
75 git rev-parse >expect \
76 L2:three R2:three \
77 L2:three R2:three &&
78 git rev-parse >actual \
79 :2:three :3:three &&
80 git hash-object >>actual \
81 three~HEAD three~R2^0
82 test_cmp expect actual
83 )
84'
85
86#
87# Same as before, but modify L1 slightly:
88#
89# L1m L2
90# o---o
91# / \ / \
92# o X ?
93# \ / \ /
94# o---o
95# R1 R2
96#
97
98test_expect_success 'setup criss-cross + rename merges with basic modification' '
99 test_create_repo rename-modify &&
100 (
101 cd rename-modify &&
102
103 ten="0 1 2 3 4 5 6 7 8 9" &&
104 for i in $ten
105 do
106 echo line $i in a sample file
107 done >one &&
108 for i in $ten
109 do
110 echo line $i in another sample file
111 done >two &&
112 git add one two &&
113 test_tick && git commit -m initial &&
114
115 git branch L1 &&
116 git checkout -b R1 &&
117 git mv one three &&
118 echo more >>two &&
119 git add two &&
120 test_tick && git commit -m R1 &&
121
122 git checkout L1 &&
123 git mv two three &&
124 test_tick && git commit -m L1 &&
125
126 git checkout L1^0 &&
127 test_tick && git merge -s ours R1 &&
128 git tag L2 &&
129
130 git checkout R1^0 &&
131 test_tick && git merge -s ours L1 &&
132 git tag R2
133 )
134'
135
136test_expect_success 'merge criss-cross + rename merges with basic modification' '
137 (
138 cd rename-modify &&
139
140 git checkout L2^0 &&
141
142 test_must_fail git merge -s recursive R2^0 &&
143
144 git ls-files -s >out &&
145 test_line_count = 2 out &&
146 git ls-files -u >out &&
147 test_line_count = 2 out &&
148 git ls-files -o >out &&
149 test_line_count = 3 out &&
150
151 git rev-parse >expect \
152 L2:three R2:three \
153 L2:three R2:three &&
154 git rev-parse >actual \
155 :2:three :3:three &&
156 git hash-object >>actual \
157 three~HEAD three~R2^0
158 test_cmp expect actual
159 )
160'
161
162#
163# For the next test, we start with three commits in two lines of development
164# which setup a rename/add conflict:
165# Commit A: File 'a' exists
166# Commit B: Rename 'a' -> 'new_a'
167# Commit C: Modify 'a', create different 'new_a'
168# Later, two different people merge and resolve differently:
169# Commit D: Merge B & C, ignoring separately created 'new_a'
170# Commit E: Merge B & C making use of some piece of secondary 'new_a'
171# Finally, someone goes to merge D & E. Does git detect the conflict?
172#
173# B D
174# o---o
175# / \ / \
176# A o X ? F
177# \ / \ /
178# o---o
179# C E
180#
181
182test_expect_success 'setup differently handled merges of rename/add conflict' '
183 test_create_repo rename-add &&
184 (
185 cd rename-add &&
186
187 printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
188 git add a &&
189 test_tick && git commit -m A &&
190
191 git branch B &&
192 git checkout -b C &&
193 echo 10 >>a &&
194 echo "other content" >>new_a &&
195 git add a new_a &&
196 test_tick && git commit -m C &&
197
198 git checkout B &&
199 git mv a new_a &&
200 test_tick && git commit -m B &&
201
202 git checkout B^0 &&
203 test_must_fail git merge C &&
204 git clean -f &&
205 test_tick && git commit -m D &&
206 git tag D &&
207
208 git checkout C^0 &&
209 test_must_fail git merge B &&
210 rm new_a~HEAD new_a &&
211 printf "Incorrectly merged content" >>new_a &&
212 git add -u &&
213 test_tick && git commit -m E &&
214 git tag E
215 )
216'
217
218test_expect_success 'git detects differently handled merges conflict' '
219 (
220 cd rename-add &&
221
222 git checkout D^0 &&
223
224 test_must_fail git merge -s recursive E^0 &&
225
226 git ls-files -s >out &&
227 test_line_count = 3 out &&
228 git ls-files -u >out &&
229 test_line_count = 3 out &&
230 git ls-files -o >out &&
231 test_line_count = 1 out &&
232
233 git rev-parse >expect \
234 D:new_a E:new_a &&
235 git rev-parse >actual \
236 :2:new_a :3:new_a &&
237 test_cmp expect actual
238
239 git cat-file -p B:new_a >ours &&
240 git cat-file -p C:new_a >theirs &&
241 >empty &&
242 test_must_fail git merge-file \
243 -L "Temporary merge branch 2" \
244 -L "" \
245 -L "Temporary merge branch 1" \
246 ours empty theirs &&
247 sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
248 git cat-file -p :1:new_a >actual &&
249 test_cmp expect actual
250 )
251'
252
253#
254# criss-cross + modify/delete:
255#
256# B D
257# o---o
258# / \ / \
259# A o X ? F
260# \ / \ /
261# o---o
262# C E
263#
264# Commit A: file with contents 'A\n'
265# Commit B: file with contents 'B\n'
266# Commit C: file not present
267# Commit D: file with contents 'B\n'
268# Commit E: file not present
269#
270# Merging commits D & E should result in modify/delete conflict.
271
272test_expect_success 'setup criss-cross + modify/delete resolved differently' '
273 test_create_repo modify-delete &&
274 (
275 cd modify-delete &&
276
277 echo A >file &&
278 git add file &&
279 test_tick &&
280 git commit -m A &&
281
282 git branch B &&
283 git checkout -b C &&
284 git rm file &&
285 test_tick &&
286 git commit -m C &&
287
288 git checkout B &&
289 echo B >file &&
290 git add file &&
291 test_tick &&
292 git commit -m B &&
293
294 git checkout B^0 &&
295 test_must_fail git merge C &&
296 echo B >file &&
297 git add file &&
298 test_tick &&
299 git commit -m D &&
300 git tag D &&
301
302 git checkout C^0 &&
303 test_must_fail git merge B &&
304 git rm file &&
305 test_tick &&
306 git commit -m E &&
307 git tag E
308 )
309'
310
311test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
312 (
313 cd modify-delete &&
314
315 git checkout D^0 &&
316
317 test_must_fail git merge -s recursive E^0 &&
318
319 git ls-files -s >out &&
320 test_line_count = 2 out &&
321 git ls-files -u >out &&
322 test_line_count = 2 out &&
323
324 git rev-parse >expect \
325 master:file B:file &&
326 git rev-parse >actual \
327 :1:file :2:file &&
328 test_cmp expect actual
329 )
330'
331
332test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
333 (
334 cd modify-delete &&
335
336 git reset --hard &&
337 git checkout E^0 &&
338
339 test_must_fail git merge -s recursive D^0 &&
340
341 git ls-files -s >out &&
342 test_line_count = 2 out &&
343 git ls-files -u >out &&
344 test_line_count = 2 out &&
345
346 git rev-parse >expect \
347 master:file B:file &&
348 git rev-parse >actual \
349 :1:file :3:file &&
350 test_cmp expect actual
351 )
352'
353
354#
355# criss-cross + d/f conflict via add/add:
356# Commit A: Neither file 'a' nor directory 'a/' exists.
357# Commit B: Introduce 'a'
358# Commit C: Introduce 'a/file'
359# Commit D: Merge B & C, keeping 'a' and deleting 'a/'
360#
361# Two different later cases:
362# Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
363# Commit E2: Merge B & C, deleting 'a' but keeping a slightly modified 'a/file'
364#
365# B D
366# o---o
367# / \ / \
368# A o X ? F
369# \ / \ /
370# o---o
371# C E1 or E2
372#
373# Merging D & E1 requires we first create a virtual merge base X from
374# merging A & B in memory. Now, if X could keep both 'a' and 'a/file' in
375# the index, then the merge of D & E1 could be resolved cleanly with both
376# 'a' and 'a/file' removed. Since git does not currently allow creating
377# such a tree, the best we can do is have X contain both 'a~<unique>' and
378# 'a/file' resulting in the merge of D and E1 having a rename/delete
379# conflict for 'a'. (Although this merge appears to be unsolvable with git
380# currently, git could do a lot better than it currently does with these
381# d/f conflicts, which is the purpose of this test.)
382#
383# Merge of D & E2 has similar issues for path 'a', but should always result
384# in a modify/delete conflict for path 'a/file'.
385#
386# We run each merge in both directions, to check for directional issues
387# with D/F conflict handling.
388#
389
390test_expect_success 'setup differently handled merges of directory/file conflict' '
391 test_create_repo directory-file &&
392 (
393 cd directory-file &&
394
395 >ignore-me &&
396 git add ignore-me &&
397 test_tick &&
398 git commit -m A &&
399 git tag A &&
400
401 git branch B &&
402 git checkout -b C &&
403 mkdir a &&
404 echo 10 >a/file &&
405 git add a/file &&
406 test_tick &&
407 git commit -m C &&
408
409 git checkout B &&
410 echo 5 >a &&
411 git add a &&
412 test_tick &&
413 git commit -m B &&
414
415 git checkout B^0 &&
416 test_must_fail git merge C &&
417 git clean -f &&
418 rm -rf a/ &&
419 echo 5 >a &&
420 git add a &&
421 test_tick &&
422 git commit -m D &&
423 git tag D &&
424
425 git checkout C^0 &&
426 test_must_fail git merge B &&
427 git clean -f &&
428 git rm --cached a &&
429 echo 10 >a/file &&
430 git add a/file &&
431 test_tick &&
432 git commit -m E1 &&
433 git tag E1 &&
434
435 git checkout C^0 &&
436 test_must_fail git merge B &&
437 git clean -f &&
438 git rm --cached a &&
439 printf "10\n11\n" >a/file &&
440 git add a/file &&
441 test_tick &&
442 git commit -m E2 &&
443 git tag E2
444 )
445'
446
447test_expect_success 'merge of D & E1 fails but has appropriate contents' '
448 (
449 cd directory-file &&
450
451 get_clean_checkout D^0 &&
452
453 test_must_fail git merge -s recursive E1^0 &&
454
455 git ls-files -s >out &&
456 test_line_count = 2 out &&
457 git ls-files -u >out &&
458 test_line_count = 1 out &&
459 git ls-files -o >out &&
460 test_line_count = 1 out &&
461
462 git rev-parse >expect \
463 A:ignore-me B:a &&
464 git rev-parse >actual \
465 :0:ignore-me :2:a &&
466 test_cmp expect actual
467 )
468'
469
470test_expect_success 'merge of E1 & D fails but has appropriate contents' '
471 (
472 cd directory-file &&
473
474 get_clean_checkout E1^0 &&
475
476 test_must_fail git merge -s recursive D^0 &&
477
478 git ls-files -s >out &&
479 test_line_count = 2 out &&
480 git ls-files -u >out &&
481 test_line_count = 1 out &&
482 git ls-files -o >out &&
483 test_line_count = 1 out &&
484
485 git rev-parse >expect \
486 A:ignore-me B:a &&
487 git rev-parse >actual \
488 :0:ignore-me :3:a &&
489 test_cmp expect actual
490 )
491'
492
493test_expect_success 'merge of D & E2 fails but has appropriate contents' '
494 (
495 cd directory-file &&
496
497 get_clean_checkout D^0 &&
498
499 test_must_fail git merge -s recursive E2^0 &&
500
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 &&
507
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
513
514 test_path_is_file a~HEAD
515 )
516'
517
518test_expect_success 'merge of E2 & D fails but has appropriate contents' '
519 (
520 cd directory-file &&
521
522 get_clean_checkout E2^0 &&
523
524 test_must_fail git merge -s recursive D^0 &&
525
526 git ls-files -s >out &&
527 test_line_count = 4 out &&
528 git ls-files -u >out &&
529 test_line_count = 3 out &&
530 git ls-files -o >out &&
531 test_line_count = 2 out &&
532
533 git rev-parse >expect \
534 B:a E2:a/file c:a/file A:ignore-me &&
535 git rev-parse >actual \
536 :3:a :2:a/file :1:a/file :0:ignore-me &&
537 test_cmp expect actual
538
539 test_path_is_file a~D^0
540 )
541'
542
543#
544# criss-cross with rename/rename(1to2)/modify followed by
545# rename/rename(2to1)/modify:
546#
547# B D
548# o---o
549# / \ / \
550# A o X ? F
551# \ / \ /
552# o---o
553# C E
554#
555# Commit A: new file: a
556# Commit B: rename a->b, modifying by adding a line
557# Commit C: rename a->c
558# Commit D: merge B&C, resolving conflict by keeping contents in newname
559# Commit E: merge B&C, resolving conflict similar to D but adding another line
560#
561# There is a conflict merging B & C, but one of filename not of file
562# content. Whoever created D and E chose specific resolutions for that
563# conflict resolution. Now, since: (1) there is no content conflict
564# merging B & C, (2) D does not modify that merged content further, and (3)
565# both D & E resolve the name conflict in the same way, the modification to
566# newname in E should not cause any conflicts when it is merged with D.
567# (Note that this can be accomplished by having the virtual merge base have
568# the merged contents of b and c stored in a file named a, which seems like
569# the most logical choice anyway.)
570#
571# Comment from Junio: I do not necessarily agree with the choice "a", but
572# it feels sound to say "B and C do not agree what the final pathname
573# should be, but we know this content was derived from the common A:a so we
574# use one path whose name is arbitrary in the virtual merge base X between
575# D and E" and then further let the rename detection to notice that that
576# arbitrary path gets renamed between X-D to "newname" and X-E also to
577# "newname" to resolve it as both sides renaming it to the same new
578# name. It is akin to what we do at the content level, i.e. "B and C do not
579# agree what the final contents should be, so we leave the conflict marker
580# but that may cancel out at the final merge stage".
581
582test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
583 test_create_repo rename-squared-squared &&
584 (
585 cd rename-squared-squared &&
586
587 printf "1\n2\n3\n4\n5\n6\n" >a &&
588 git add a &&
589 git commit -m A &&
590 git tag A &&
591
592 git checkout -b B A &&
593 git mv a b &&
594 echo 7 >>b &&
595 git add -u &&
596 git commit -m B &&
597
598 git checkout -b C A &&
599 git mv a c &&
600 git commit -m C &&
601
602 git checkout -q B^0 &&
603 git merge --no-commit -s ours C^0 &&
604 git mv b newname &&
605 git commit -m "Merge commit C^0 into HEAD" &&
606 git tag D &&
607
608 git checkout -q C^0 &&
609 git merge --no-commit -s ours B^0 &&
610 git mv c newname &&
611 printf "7\n8\n" >>newname &&
612 git add -u &&
613 git commit -m "Merge commit B^0 into HEAD" &&
614 git tag E
615 )
616'
617
618test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
619 (
620 cd rename-squared-squared &&
621
622 git checkout D^0 &&
623
624 git merge -s recursive E^0 &&
625
626 git ls-files -s >out &&
627 test_line_count = 1 out &&
628 git ls-files -u >out &&
629 test_line_count = 0 out &&
630 git ls-files -o >out &&
631 test_line_count = 1 out &&
632
633 test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
634 )
635'
636
637#
638# criss-cross with rename/rename(1to2)/add-source + resolvable modify/modify:
639#
640# B D
641# o---o
642# / \ / \
643# A o X ? F
644# \ / \ /
645# o---o
646# C E
647#
648# Commit A: new file: a
649# Commit B: rename a->b
650# Commit C: rename a->c, add different a
651# Commit D: merge B&C, keeping b&c and (new) a modified at beginning
652# Commit E: merge B&C, keeping b&c and (new) a modified at end
653#
654# Merging commits D & E should result in no conflict; doing so correctly
655# requires getting the virtual merge base (from merging B&C) right, handling
656# renaming carefully (both in the virtual merge base and later), and getting
657# content merge handled.
658
659test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
660 test_create_repo rename-rename-add-source &&
661 (
662 cd rename-rename-add-source &&
663
664 printf "lots\nof\nwords\nand\ncontent\n" >a &&
665 git add a &&
666 git commit -m A &&
667 git tag A &&
668
669 git checkout -b B A &&
670 git mv a b &&
671 git commit -m B &&
672
673 git checkout -b C A &&
674 git mv a c &&
675 printf "2\n3\n4\n5\n6\n7\n" >a &&
676 git add a &&
677 git commit -m C &&
678
679 git checkout B^0 &&
680 git merge --no-commit -s ours C^0 &&
681 git checkout C -- a c &&
682 mv a old_a &&
683 echo 1 >a &&
684 cat old_a >>a &&
685 rm old_a &&
686 git add -u &&
687 git commit -m "Merge commit C^0 into HEAD" &&
688 git tag D &&
689
690 git checkout C^0 &&
691 git merge --no-commit -s ours B^0 &&
692 git checkout B -- b &&
693 echo 8 >>a &&
694 git add -u &&
695 git commit -m "Merge commit B^0 into HEAD" &&
696 git tag E
697 )
698'
699
700test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
701 (
702 cd rename-rename-add-source &&
703
704 git checkout D^0 &&
705
706 git merge -s recursive E^0 &&
707
708 git ls-files -s >out &&
709 test_line_count = 3 out &&
710 git ls-files -u >out &&
711 test_line_count = 0 out &&
712 git ls-files -o >out &&
713 test_line_count = 1 out &&
714
715 printf "1\n2\n3\n4\n5\n6\n7\n8\n" >correct &&
716 git rev-parse >expect \
717 A:a A:a \
718 correct &&
719 git rev-parse >actual \
720 :0:b :0:c &&
721 git hash-object >>actual \
722 a &&
723 test_cmp expect actual
724 )
725'
726
727#
728# criss-cross with rename/rename(1to2)/add-dest + simple modify:
729#
730# B D
731# o---o
732# / \ / \
733# A o X ? F
734# \ / \ /
735# o---o
736# C E
737#
738# Commit A: new file: a
739# Commit B: rename a->b, add c
740# Commit C: rename a->c
741# Commit D: merge B&C, keeping A:a and B:c
742# Commit E: merge B&C, keeping A:a and slightly modified c from B
743#
744# Merging commits D & E should result in no conflict. The virtual merge
745# base of B & C needs to not delete B:c for that to work, though...
746
747test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
748 test_create_repo rename-rename-add-dest &&
749 (
750 cd rename-rename-add-dest &&
751
752 >a &&
753 git add a &&
754 git commit -m A &&
755 git tag A &&
756
757 git checkout -b B A &&
758 git mv a b &&
759 printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
760 git add c &&
761 git commit -m B &&
762
763 git checkout -b C A &&
764 git mv a c &&
765 git commit -m C &&
766
767 git checkout B^0 &&
768 git merge --no-commit -s ours C^0 &&
769 git mv b a &&
770 git commit -m "D is like B but renames b back to a" &&
771 git tag D &&
772
773 git checkout B^0 &&
774 git merge --no-commit -s ours C^0 &&
775 git mv b a &&
776 echo 8 >>c &&
777 git add c &&
778 git commit -m "E like D but has mod in c" &&
779 git tag E
780 )
781'
782
783test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
784 (
785 cd rename-rename-add-dest &&
786
787 git checkout D^0 &&
788
789 git merge -s recursive E^0 &&
790
791 git ls-files -s >out &&
792 test_line_count = 2 out &&
793 git ls-files -u >out &&
794 test_line_count = 0 out &&
795 git ls-files -o >out &&
796 test_line_count = 1 out &&
797
798 git rev-parse >expect \
799 A:a E:c &&
800 git rev-parse >actual \
801 :0:a :0:c &&
802 test_cmp expect actual
803 )
804'
805
806test_done