Git fork
1#!/bin/sh
2
3test_description='recursive merge corner cases involving criss-cross merges'
4
5GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
8. ./test-lib.sh
9
10#
11# L1 L2
12# o---o
13# / \ / \
14# o X ?
15# \ / \ /
16# o---o
17# R1 R2
18#
19
20test_expect_success 'setup basic criss-cross + rename with no modifications' '
21 git init basic-rename &&
22 (
23 cd basic-rename &&
24
25 ten="0 1 2 3 4 5 6 7 8 9" &&
26 printf "line %d in a sample file\n" $ten >one &&
27 printf "line %d in another sample file\n" $ten >two &&
28 git add one two &&
29 test_tick && git commit -m initial &&
30
31 git branch L1 &&
32 git checkout -b R1 &&
33 git mv one three &&
34 test_tick && git commit -m R1 &&
35
36 git checkout L1 &&
37 git mv two three &&
38 test_tick && git commit -m L1 &&
39
40 git checkout L1^0 &&
41 test_tick && git merge -s ours R1 &&
42 git tag L2 &&
43
44 git checkout R1^0 &&
45 test_tick && git merge -s ours L1 &&
46 git tag R2
47 )
48'
49
50test_expect_success 'merge simple rename+criss-cross with no modifications' '
51 (
52 cd basic-rename &&
53
54 git reset --hard &&
55 git checkout L2^0 &&
56
57 test_must_fail git merge -s recursive R2^0 &&
58
59 git ls-files -s >out &&
60 test_line_count = 5 out &&
61 git ls-files -u >out &&
62 test_line_count = 3 out &&
63 git ls-files -o >out &&
64 test_line_count = 1 out &&
65
66 git rev-parse >expect \
67 L2:three R2:three &&
68 git rev-parse >actual \
69 :2:three :3:three &&
70 test_cmp expect actual
71 )
72'
73
74#
75# Same as before, but modify L1 slightly:
76#
77# L1m L2
78# o---o
79# / \ / \
80# o X ?
81# \ / \ /
82# o---o
83# R1 R2
84#
85
86test_expect_success 'setup criss-cross + rename merges with basic modification' '
87 git init rename-modify &&
88 (
89 cd rename-modify &&
90
91 ten="0 1 2 3 4 5 6 7 8 9" &&
92 printf "line %d in a sample file\n" $ten >one &&
93 printf "line %d in another sample file\n" $ten >two &&
94 git add one two &&
95 test_tick && git commit -m initial &&
96
97 git branch L1 &&
98 git checkout -b R1 &&
99 git mv one three &&
100 echo more >>two &&
101 git add two &&
102 test_tick && git commit -m R1 &&
103
104 git checkout L1 &&
105 git mv two three &&
106 test_tick && git commit -m L1 &&
107
108 git checkout L1^0 &&
109 test_tick && git merge -s ours R1 &&
110 git tag L2 &&
111
112 git checkout R1^0 &&
113 test_tick && git merge -s ours L1 &&
114 git tag R2
115 )
116'
117
118test_expect_success 'merge criss-cross + rename merges with basic modification' '
119 (
120 cd rename-modify &&
121
122 git checkout L2^0 &&
123
124 test_must_fail git merge -s recursive R2^0 &&
125
126 git ls-files -s >out &&
127 test_line_count = 5 out &&
128 git ls-files -u >out &&
129 test_line_count = 3 out &&
130 git ls-files -o >out &&
131 test_line_count = 1 out &&
132
133 git rev-parse >expect \
134 L2:three R2:three &&
135 git rev-parse >actual \
136 :2:three :3:three &&
137 test_cmp expect actual
138 )
139'
140
141#
142# For the next test, we start with three commits in two lines of development
143# which setup a rename/add conflict:
144# Commit A: File 'a' exists
145# Commit B: Rename 'a' -> 'new_a'
146# Commit C: Modify 'a', create different 'new_a'
147# Later, two different people merge and resolve differently:
148# Commit D: Merge B & C, ignoring separately created 'new_a'
149# Commit E: Merge B & C making use of some piece of secondary 'new_a'
150# Finally, someone goes to merge D & E. Does git detect the conflict?
151#
152# B D
153# o---o
154# / \ / \
155# A o X ? F
156# \ / \ /
157# o---o
158# C E
159#
160
161test_expect_success 'setup differently handled merges of rename/add conflict' '
162 git init rename-add &&
163 (
164 cd rename-add &&
165
166 printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
167 git add a &&
168 test_tick && git commit -m A &&
169
170 git branch B &&
171 git checkout -b C &&
172 echo 10 >>a &&
173 test_write_lines 0 1 2 3 4 5 6 7 foobar >new_a &&
174 git add a new_a &&
175 test_tick && git commit -m C &&
176
177 git checkout B &&
178 git mv a new_a &&
179 test_tick && git commit -m B &&
180
181 git checkout B^0 &&
182 test_must_fail git merge C &&
183 git show :2:new_a >new_a &&
184 git add new_a &&
185 test_tick && git commit -m D &&
186 git tag D &&
187
188 git checkout C^0 &&
189 test_must_fail git merge B &&
190 test_write_lines 0 1 2 3 4 5 6 7 bad_merge >new_a &&
191 git add -u &&
192 test_tick && git commit -m E &&
193 git tag E
194 )
195'
196
197test_expect_success 'git detects differently handled merges conflict' '
198 (
199 cd rename-add &&
200
201 git checkout D^0 &&
202
203 test_must_fail git merge -s recursive E^0 &&
204
205 git ls-files -s >out &&
206 test_line_count = 3 out &&
207 git ls-files -u >out &&
208 test_line_count = 3 out &&
209 git ls-files -o >out &&
210 test_line_count = 1 out &&
211
212 git cat-file -p C:new_a >ours &&
213 git cat-file -p C:a >theirs &&
214 >empty &&
215 test_must_fail git merge-file \
216 -L "Temporary merge branch 1" \
217 -L "" \
218 -L "Temporary merge branch 2" \
219 ours empty theirs &&
220 sed -e "s/^\([<=>]\)/\1\1\1/" ours >ours-tweaked &&
221 git hash-object ours-tweaked >expect &&
222 git rev-parse >>expect \
223 D:new_a E:new_a &&
224 git rev-parse >actual \
225 :1:new_a :2:new_a :3:new_a &&
226 test_cmp expect actual &&
227
228 # Test that the two-way merge in new_a is as expected
229 git cat-file -p D:new_a >ours &&
230 git cat-file -p E:new_a >theirs &&
231 >empty &&
232 test_must_fail git merge-file \
233 -L "HEAD" \
234 -L "" \
235 -L "E^0" \
236 ours empty theirs &&
237 sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
238 git hash-object new_a >actual &&
239 git hash-object ours >expect &&
240 test_cmp expect actual
241 )
242'
243
244# Repeat the above testcase with precisely the same setup, other than with
245# the two merge bases having different orderings of commit timestamps so
246# that they are reversed in the order they are provided to merge-recursive,
247# so that we can improve code coverage.
248test_expect_success 'git detects differently handled merges conflict, swapped' '
249 (
250 cd rename-add &&
251
252 # Difference #1: Do cleanup from previous testrun
253 git reset --hard &&
254 git clean -fdqx &&
255
256 # Difference #2: Change commit timestamps
257 btime=$(git log --no-walk --date=raw --format=%cd B | awk "{print \$1}") &&
258 ctime=$(git log --no-walk --date=raw --format=%cd C | awk "{print \$1}") &&
259 newctime=$(($btime+1)) &&
260 git fast-export --no-data --all | sed -e s/$ctime/$newctime/ | git fast-import --force --quiet &&
261 # End of most differences; rest is copy-paste of last test,
262 # other than swapping C:a and C:new_a due to order switch
263
264 git checkout D^0 &&
265 test_must_fail git merge -s recursive E^0 &&
266
267 git ls-files -s >out &&
268 test_line_count = 3 out &&
269 git ls-files -u >out &&
270 test_line_count = 3 out &&
271 git ls-files -o >out &&
272 test_line_count = 1 out &&
273
274 git cat-file -p C:a >ours &&
275 git cat-file -p C:new_a >theirs &&
276 >empty &&
277 test_must_fail git merge-file \
278 -L "Temporary merge branch 1" \
279 -L "" \
280 -L "Temporary merge branch 2" \
281 ours empty theirs &&
282 sed -e "s/^\([<=>]\)/\1\1\1/" ours >ours-tweaked &&
283 git hash-object ours-tweaked >expect &&
284 git rev-parse >>expect \
285 D:new_a E:new_a &&
286 git rev-parse >actual \
287 :1:new_a :2:new_a :3:new_a &&
288 test_cmp expect actual &&
289
290 # Test that the two-way merge in new_a is as expected
291 git cat-file -p D:new_a >ours &&
292 git cat-file -p E:new_a >theirs &&
293 >empty &&
294 test_must_fail git merge-file \
295 -L "HEAD" \
296 -L "" \
297 -L "E^0" \
298 ours empty theirs &&
299 sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
300 git hash-object new_a >actual &&
301 git hash-object ours >expect &&
302 test_cmp expect actual
303 )
304'
305
306#
307# criss-cross + modify/delete:
308#
309# B D
310# o---o
311# / \ / \
312# A o X ? F
313# \ / \ /
314# o---o
315# C E
316#
317# Commit A: file with contents 'A\n'
318# Commit B: file with contents 'B\n'
319# Commit C: file not present
320# Commit D: file with contents 'B\n'
321# Commit E: file not present
322#
323# Merging commits D & E should result in modify/delete conflict.
324
325test_expect_success 'setup criss-cross + modify/delete resolved differently' '
326 git init modify-delete &&
327 (
328 cd modify-delete &&
329
330 echo A >file &&
331 git add file &&
332 test_tick &&
333 git commit -m A &&
334
335 git branch B &&
336 git checkout -b C &&
337 git rm file &&
338 test_tick &&
339 git commit -m C &&
340
341 git checkout B &&
342 echo B >file &&
343 git add file &&
344 test_tick &&
345 git commit -m B &&
346
347 git checkout B^0 &&
348 test_must_fail git merge C &&
349 echo B >file &&
350 git add file &&
351 test_tick &&
352 git commit -m D &&
353 git tag D &&
354
355 git checkout C^0 &&
356 test_must_fail git merge B &&
357 git rm file &&
358 test_tick &&
359 git commit -m E &&
360 git tag E
361 )
362'
363
364test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
365 (
366 cd modify-delete &&
367
368 git checkout D^0 &&
369
370 test_must_fail git merge -s recursive E^0 &&
371
372 git ls-files -s >out &&
373 test_line_count = 2 out &&
374 git ls-files -u >out &&
375 test_line_count = 2 out &&
376
377 git rev-parse >expect \
378 main:file B:file &&
379 git rev-parse >actual \
380 :1:file :2:file &&
381 test_cmp expect actual
382 )
383'
384
385test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
386 (
387 cd modify-delete &&
388
389 git reset --hard &&
390 git checkout E^0 &&
391
392 test_must_fail git merge -s recursive D^0 &&
393
394 git ls-files -s >out &&
395 test_line_count = 2 out &&
396 git ls-files -u >out &&
397 test_line_count = 2 out &&
398
399 git rev-parse >expect \
400 main:file B:file &&
401 git rev-parse >actual \
402 :1:file :3:file &&
403 test_cmp expect actual
404 )
405'
406
407# SORRY FOR THE SUPER LONG DESCRIPTION, BUT THIS NEXT ONE IS HAIRY
408#
409# criss-cross + d/f conflict via add/add:
410# Commit A: Neither file 'a' nor directory 'a/' exists.
411# Commit B: Introduce 'a'
412# Commit C: Introduce 'a/file'
413# Commit D1: Merge B & C, keeping 'a' and deleting 'a/'
414# Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
415#
416# B D1 or D2
417# o---o
418# / \ / \
419# A o X ? F
420# \ / \ /
421# o---o
422# C E1 or E2 or E3
423#
424# I'll describe D2, E2, & E3 (which are alternatives for D1 & E1) more below...
425#
426# Merging D1 & E1 requires we first create a virtual merge base X from
427# merging A & B in memory. There are several possibilities for the merge-base:
428# 1: Keep both 'a' and 'a/file' (assuming crazy filesystem allowing a tree
429# with a directory and file at same path): results in merge of D1 & E1
430# being clean with both files deleted. Bad (no conflict detected).
431# 2: Keep 'a' but not 'a/file': Merging D1 & E1 is clean and matches E1. Bad.
432# 3: Keep 'a/file' but not 'a': Merging D1 & E1 is clean and matches D1. Bad.
433# 4: Keep neither file: Merging D1 & E1 reports the D/F add/add conflict.
434#
435# So 4 sounds good for this case, but if we were to merge D1 & E3, where E3
436# is defined as:
437# Commit E3: Merge B & C, keeping modified a, and deleting a/
438# then we'd get an add/add conflict for 'a', which seems suboptimal. A little
439# creativity leads us to an alternate choice:
440# 5: Keep 'a' as 'a~$UNIQUE' and a/file; results:
441# Merge D1 & E1: rename/delete conflict for 'a'; a/file silently deleted
442# Merge D1 & E3 is clean, as expected.
443#
444# So choice 5 at least provides some kind of conflict for the original case,
445# and can merge cleanly as expected with D1 and E3. It also made things just
446# slightly funny for merging D1 and E4, where E4 is defined as:
447# Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
448# in this case, we'll get a rename/rename(1to2) conflict because a~$UNIQUE
449# gets renamed to 'a' in D1 and to 'a2' in E4. But that's better than having
450# two files (both 'a' and 'a2') sitting around without the user being notified
451# that we could detect they were related and need to be merged. Also, choice
452# 5 makes the handling of 'a/file' seem suboptimal. What if we were to merge
453# D2 and E4, where D2 is:
454# Commit D2: Merge B & C, renaming 'a'->'a2', keeping 'a/file'
455# This would result in a clean merge with 'a2' having three-way merged
456# contents (good), and deleting 'a/' (bad) -- it doesn't detect the
457# conflict in how the different sides treated a/file differently.
458# Continuing down the creative route:
459# 6: Keep 'a' as 'a~$UNIQUE1' and keep 'a/' as 'a~$UNIQUE2/'; results:
460# Merge D1 & E1: rename/delete conflict for 'a' and each path under 'a/'.
461# Merge D1 & E3: clean, as expected.
462# Merge D1 & E4: rename/rename(1to2) conflict on 'a' vs 'a2'.
463# Merge D2 & E4: clean for 'a2', rename/delete for a/file
464#
465# Choice 6 could cause rename detection to take longer (providing more targets
466# that need to be searched). Also, the conflict message for each path under
467# 'a/' might be annoying unless we can detect it at the directory level, print
468# it once, and then suppress it for individual filepaths underneath.
469#
470#
471# As of time of writing, git uses choice 5. Directory rename detection and
472# rename detection performance improvements might make choice 6 a desirable
473# improvement. But we can at least document where we fall short for now...
474#
475#
476# Historically, this testcase also used:
477# Commit E2: Merge B & C, deleting 'a' but keeping slightly modified 'a/file'
478# The merge of D1 & E2 is very similar to D1 & E1 -- it has similar issues for
479# path 'a', but should always result in a modify/delete conflict for path
480# 'a/file'. These tests ran the two merges
481# D1 & E1
482# D1 & E2
483# in both directions, to check for directional issues with D/F conflict
484# handling. Later we added
485# D1 & E3
486# D1 & E4
487# D2 & E4
488# for good measure, though we only ran those one way because we had pretty
489# good confidence in merge-recursive's directional handling of D/F issues.
490#
491# Just to summarize all the intermediate merge commits:
492# Commit D1: Merge B & C, keeping a and deleting a/
493# Commit D2: Merge B & C, renaming a->a2, keeping a/file
494# Commit E1: Merge B & C, deleting a but keeping a/file
495# Commit E2: Merge B & C, deleting a but keeping slightly modified a/file
496# Commit E3: Merge B & C, keeping modified a, and deleting a/
497# Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
498#
499
500test_expect_success 'setup differently handled merges of directory/file conflict' '
501 git init directory-file &&
502 (
503 cd directory-file &&
504
505 >ignore-me &&
506 git add ignore-me &&
507 test_tick &&
508 git commit -m A &&
509 git tag A &&
510
511 git branch B &&
512 git checkout -b C &&
513 mkdir a &&
514 test_write_lines a b c d e f g >a/file &&
515 git add a/file &&
516 test_tick &&
517 git commit -m C &&
518
519 git checkout B &&
520 test_write_lines 1 2 3 4 5 6 7 >a &&
521 git add a &&
522 test_tick &&
523 git commit -m B &&
524
525 git checkout B^0 &&
526 git merge -s ours -m D1 C^0 &&
527 git tag D1 &&
528
529 git checkout B^0 &&
530 test_must_fail git merge C^0 &&
531 git rm -rf a/ &&
532 git rm a~HEAD &&
533 git cat-file -p B:a >a2 &&
534 git add a2 &&
535 git commit -m D2 &&
536 git tag D2 &&
537
538 git checkout C^0 &&
539 git merge -s ours -m E1 B^0 &&
540 git tag E1 &&
541
542 git checkout C^0 &&
543 git merge -s ours -m E2 B^0 &&
544 test_write_lines a b c d e f g h >a/file &&
545 git add a/file &&
546 git commit --amend -C HEAD &&
547 git tag E2 &&
548
549 git checkout C^0 &&
550 test_must_fail git merge B^0 &&
551 git rm a~B^0 &&
552 git rm -rf a/ &&
553 test_write_lines 1 2 3 4 5 6 7 8 >a &&
554 git add a &&
555 git commit -m E3 &&
556 git tag E3 &&
557
558 git checkout C^0 &&
559 test_must_fail git merge B^0 &&
560 git rm -rf a/ &&
561 git rm a~B^0 &&
562 test_write_lines 1 2 3 4 5 6 7 8 >a2 &&
563 git add a2 &&
564 git commit -m E4 &&
565 git tag E4
566 )
567'
568
569test_expect_success 'merge of D1 & E1 fails but has appropriate contents' '
570 test_when_finished "git -C directory-file reset --hard" &&
571 test_when_finished "git -C directory-file clean -fdqx" &&
572 (
573 cd directory-file &&
574
575 git checkout D1^0 &&
576
577 test_must_fail git merge -s recursive E1^0 &&
578
579 git ls-files -s >out &&
580 test_line_count = 3 out &&
581 git ls-files -u >out &&
582 test_line_count = 2 out &&
583 git ls-files -o >out &&
584 test_line_count = 1 out &&
585
586 git rev-parse >expect A:ignore-me B:a D1:a &&
587 git rev-parse >actual :0:ignore-me :1:a :2:a &&
588 test_cmp expect actual
589 )
590'
591
592test_expect_success 'merge of E1 & D1 fails but has appropriate contents' '
593 test_when_finished "git -C directory-file reset --hard" &&
594 test_when_finished "git -C directory-file clean -fdqx" &&
595 (
596 cd directory-file &&
597
598 git checkout E1^0 &&
599
600 test_must_fail git merge -s recursive D1^0 &&
601
602 git ls-files -s >out &&
603 test_line_count = 3 out &&
604 git ls-files -u >out &&
605 test_line_count = 2 out &&
606 git ls-files -o >out &&
607 test_line_count = 1 out &&
608
609 git rev-parse >expect \
610 A:ignore-me B:a D1:a &&
611 git rev-parse >actual \
612 :0:ignore-me :1:a :3:a &&
613 test_cmp expect actual
614 )
615'
616
617test_expect_success 'merge of D1 & E2 fails but has appropriate contents' '
618 test_when_finished "git -C directory-file reset --hard" &&
619 test_when_finished "git -C directory-file clean -fdqx" &&
620 (
621 cd directory-file &&
622
623 git checkout D1^0 &&
624
625 test_must_fail git merge -s recursive E2^0 &&
626
627 git ls-files -s >out &&
628 test_line_count = 5 out &&
629 git ls-files -u >out &&
630 test_line_count = 4 out &&
631 git ls-files -o >out &&
632 test_line_count = 1 out &&
633
634 git rev-parse >expect \
635 B:a D1:a E2:a/file C:a/file A:ignore-me &&
636 git rev-parse >actual \
637 :1:a~HEAD :2:a~HEAD :3:a/file :1:a/file :0:ignore-me &&
638 test_cmp expect actual &&
639
640 test_path_is_file a~HEAD
641 )
642'
643
644test_expect_success 'merge of E2 & D1 fails but has appropriate contents' '
645 test_when_finished "git -C directory-file reset --hard" &&
646 test_when_finished "git -C directory-file clean -fdqx" &&
647 (
648 cd directory-file &&
649
650 git checkout E2^0 &&
651
652 test_must_fail git merge -s recursive D1^0 &&
653
654 git ls-files -s >out &&
655 test_line_count = 5 out &&
656 git ls-files -u >out &&
657 test_line_count = 4 out &&
658 git ls-files -o >out &&
659 test_line_count = 1 out &&
660
661 git rev-parse >expect \
662 B:a D1:a E2:a/file C:a/file A:ignore-me &&
663 git rev-parse >actual \
664 :1:a~D1^0 :3:a~D1^0 :2:a/file :1:a/file :0:ignore-me &&
665 test_cmp expect actual &&
666
667 test_path_is_file a~D1^0
668 )
669'
670
671test_expect_success 'merge of D1 & E3 succeeds' '
672 test_when_finished "git -C directory-file reset --hard" &&
673 test_when_finished "git -C directory-file clean -fdqx" &&
674 (
675 cd directory-file &&
676
677 git checkout D1^0 &&
678
679 git merge -s recursive E3^0 &&
680
681 git ls-files -s >out &&
682 test_line_count = 2 out &&
683 git ls-files -u >out &&
684 test_line_count = 0 out &&
685 git ls-files -o >out &&
686 test_line_count = 1 out &&
687
688 git rev-parse >expect \
689 A:ignore-me E3:a &&
690 git rev-parse >actual \
691 :0:ignore-me :0:a &&
692 test_cmp expect actual
693 )
694'
695
696test_expect_success 'merge of D1 & E4 puts merge of a and a2 in both a and a2' '
697 test_when_finished "git -C directory-file reset --hard" &&
698 test_when_finished "git -C directory-file clean -fdqx" &&
699 (
700 cd directory-file &&
701
702 git checkout D1^0 &&
703
704 test_must_fail git merge -s recursive E4^0 &&
705
706 git ls-files -s >out &&
707 test_line_count = 4 out &&
708 git ls-files -u >out &&
709 test_line_count = 3 out &&
710 git ls-files -o >out &&
711 test_line_count = 1 out &&
712
713 git rev-parse >expect \
714 A:ignore-me B:a E4:a2 E4:a2 &&
715 git rev-parse >actual \
716 :0:ignore-me :1:a~Temporary\ merge\ branch\ 2 :2:a :3:a2 &&
717 test_cmp expect actual
718 )
719'
720
721test_expect_failure 'merge of D2 & E4 merges a2s & reports conflict for a/file' '
722 test_when_finished "git -C directory-file reset --hard" &&
723 test_when_finished "git -C directory-file clean -fdqx" &&
724 (
725 cd directory-file &&
726
727 git checkout D2^0 &&
728
729 test_must_fail git merge -s recursive E4^0 &&
730
731 git ls-files -s >out &&
732 test_line_count = 3 out &&
733 git ls-files -u >out &&
734 test_line_count = 1 out &&
735 git ls-files -o >out &&
736 test_line_count = 1 out &&
737
738 git rev-parse >expect \
739 A:ignore-me E4:a2 D2:a/file &&
740 git rev-parse >actual \
741 :0:ignore-me :0:a2 :2:a/file &&
742 test_cmp expect actual
743 )
744'
745
746#
747# criss-cross with rename/rename(1to2)/modify followed by
748# rename/rename(2to1)/modify:
749#
750# B D
751# o---o
752# / \ / \
753# A o X ? F
754# \ / \ /
755# o---o
756# C E
757#
758# Commit A: new file: a
759# Commit B: rename a->b, modifying by adding a line
760# Commit C: rename a->c
761# Commit D: merge B&C, resolving conflict by keeping contents in newname
762# Commit E: merge B&C, resolving conflict similar to D but adding another line
763#
764# There is a conflict merging B & C, but one of filename not of file
765# content. Whoever created D and E chose specific resolutions for that
766# conflict resolution. Now, since: (1) there is no content conflict
767# merging B & C, (2) D does not modify that merged content further, and (3)
768# both D & E resolve the name conflict in the same way, the modification to
769# newname in E should not cause any conflicts when it is merged with D.
770# (Note that this can be accomplished by having the virtual merge base have
771# the merged contents of b and c stored in a file named a, which seems like
772# the most logical choice anyway.)
773#
774# Comment from Junio: I do not necessarily agree with the choice "a", but
775# it feels sound to say "B and C do not agree what the final pathname
776# should be, but we know this content was derived from the common A:a so we
777# use one path whose name is arbitrary in the virtual merge base X between
778# D and E" and then further let the rename detection to notice that that
779# arbitrary path gets renamed between X-D to "newname" and X-E also to
780# "newname" to resolve it as both sides renaming it to the same new
781# name. It is akin to what we do at the content level, i.e. "B and C do not
782# agree what the final contents should be, so we leave the conflict marker
783# but that may cancel out at the final merge stage".
784
785test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
786 git init rename-squared-squared &&
787 (
788 cd rename-squared-squared &&
789
790 printf "1\n2\n3\n4\n5\n6\n" >a &&
791 git add a &&
792 git commit -m A &&
793 git tag A &&
794
795 git checkout -b B A &&
796 git mv a b &&
797 echo 7 >>b &&
798 git add -u &&
799 git commit -m B &&
800
801 git checkout -b C A &&
802 git mv a c &&
803 git commit -m C &&
804
805 git checkout -q B^0 &&
806 git merge --no-commit -s ours C^0 &&
807 git mv b newname &&
808 git commit -m "Merge commit C^0 into HEAD" &&
809 git tag D &&
810
811 git checkout -q C^0 &&
812 git merge --no-commit -s ours B^0 &&
813 git mv c newname &&
814 printf "7\n8\n" >>newname &&
815 git add -u &&
816 git commit -m "Merge commit B^0 into HEAD" &&
817 git tag E
818 )
819'
820
821test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
822 (
823 cd rename-squared-squared &&
824
825 git checkout D^0 &&
826
827 git merge -s recursive E^0 &&
828
829 git ls-files -s >out &&
830 test_line_count = 1 out &&
831 git ls-files -u >out &&
832 test_line_count = 0 out &&
833 git ls-files -o >out &&
834 test_line_count = 1 out &&
835
836 test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
837 )
838'
839
840#
841# criss-cross with rename/rename(1to2)/add-source + resolvable modify/modify:
842#
843# B D
844# o---o
845# / \ / \
846# A o X ? F
847# \ / \ /
848# o---o
849# C E
850#
851# Commit A: new file: a
852# Commit B: rename a->b
853# Commit C: rename a->c, add different a
854# Commit D: merge B&C, keeping b&c and (new) a modified at beginning
855# Commit E: merge B&C, keeping b&c and (new) a modified at end
856#
857# Merging commits D & E should result in no conflict; doing so correctly
858# requires getting the virtual merge base (from merging B&C) right, handling
859# renaming carefully (both in the virtual merge base and later), and getting
860# content merge handled.
861
862test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
863 git init rename-rename-add-source &&
864 (
865 cd rename-rename-add-source &&
866
867 printf "lots\nof\nwords\nand\ncontent\n" >a &&
868 git add a &&
869 git commit -m A &&
870 git tag A &&
871
872 git checkout -b B A &&
873 git mv a b &&
874 git commit -m B &&
875
876 git checkout -b C A &&
877 git mv a c &&
878 printf "2\n3\n4\n5\n6\n7\n" >a &&
879 git add a &&
880 git commit -m C &&
881
882 git checkout B^0 &&
883 git merge --no-commit -s ours C^0 &&
884 git checkout C -- a c &&
885 mv a old_a &&
886 echo 1 >a &&
887 cat old_a >>a &&
888 rm old_a &&
889 git add -u &&
890 git commit -m "Merge commit C^0 into HEAD" &&
891 git tag D &&
892
893 git checkout C^0 &&
894 git merge --no-commit -s ours B^0 &&
895 git checkout B -- b &&
896 echo 8 >>a &&
897 git add -u &&
898 git commit -m "Merge commit B^0 into HEAD" &&
899 git tag E
900 )
901'
902
903test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
904 (
905 cd rename-rename-add-source &&
906
907 git checkout D^0 &&
908
909 git merge -s recursive E^0 &&
910
911 git ls-files -s >out &&
912 test_line_count = 3 out &&
913 git ls-files -u >out &&
914 test_line_count = 0 out &&
915 git ls-files -o >out &&
916 test_line_count = 1 out &&
917
918 printf "1\n2\n3\n4\n5\n6\n7\n8\n" >correct &&
919 git rev-parse >expect \
920 A:a A:a \
921 correct &&
922 git rev-parse >actual \
923 :0:b :0:c &&
924 git hash-object >>actual \
925 a &&
926 test_cmp expect actual
927 )
928'
929
930#
931# criss-cross with rename/rename(1to2)/add-dest + simple modify:
932#
933# B D
934# o---o
935# / \ / \
936# A o X ? F
937# \ / \ /
938# o---o
939# C E
940#
941# Commit A: new file: a
942# Commit B: rename a->b, add c
943# Commit C: rename a->c
944# Commit D: merge B&C, keeping A:a and B:c
945# Commit E: merge B&C, keeping A:a and slightly modified c from B
946#
947# Merging commits D & E should result in no conflict. The virtual merge
948# base of B & C needs to not delete B:c for that to work, though...
949
950test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
951 git init rename-rename-add-dest &&
952 (
953 cd rename-rename-add-dest &&
954
955 >a &&
956 git add a &&
957 git commit -m A &&
958 git tag A &&
959
960 git checkout -b B A &&
961 git mv a b &&
962 printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
963 git add c &&
964 git commit -m B &&
965
966 git checkout -b C A &&
967 git mv a c &&
968 git commit -m C &&
969
970 git checkout B^0 &&
971 git merge --no-commit -s ours C^0 &&
972 git mv b a &&
973 git commit -m "D is like B but renames b back to a" &&
974 git tag D &&
975
976 git checkout B^0 &&
977 git merge --no-commit -s ours C^0 &&
978 git mv b a &&
979 echo 8 >>c &&
980 git add c &&
981 git commit -m "E like D but has mod in c" &&
982 git tag E
983 )
984'
985
986test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
987 (
988 cd rename-rename-add-dest &&
989
990 git checkout D^0 &&
991
992 git merge -s recursive E^0 &&
993
994 git ls-files -s >out &&
995 test_line_count = 2 out &&
996 git ls-files -u >out &&
997 test_line_count = 0 out &&
998 git ls-files -o >out &&
999 test_line_count = 1 out &&
1000
1001 git rev-parse >expect \
1002 A:a E:c &&
1003 git rev-parse >actual \
1004 :0:a :0:c &&
1005 test_cmp expect actual
1006 )
1007'
1008
1009#
1010# criss-cross with modify/modify on a symlink:
1011#
1012# B D
1013# o---o
1014# / \ / \
1015# A o X ? F
1016# \ / \ /
1017# o---o
1018# C E
1019#
1020# Commit A: simple simlink fickle->lagoon
1021# Commit B: redirect fickle->disneyland
1022# Commit C: redirect fickle->home
1023# Commit D: merge B&C, resolving in favor of B
1024# Commit E: merge B&C, resolving in favor of C
1025#
1026# This is an obvious modify/modify conflict for the symlink 'fickle'. Can
1027# git detect it?
1028
1029test_expect_success 'setup symlink modify/modify' '
1030 git init symlink-modify-modify &&
1031 (
1032 cd symlink-modify-modify &&
1033
1034 test_ln_s_add lagoon fickle &&
1035 git commit -m A &&
1036 git tag A &&
1037
1038 git checkout -b B A &&
1039 git rm fickle &&
1040 test_ln_s_add disneyland fickle &&
1041 git commit -m B &&
1042
1043 git checkout -b C A &&
1044 git rm fickle &&
1045 test_ln_s_add home fickle &&
1046 git add fickle &&
1047 git commit -m C &&
1048
1049 git checkout -q B^0 &&
1050 git merge -s ours -m D C^0 &&
1051 git tag D &&
1052
1053 git checkout -q C^0 &&
1054 git merge -s ours -m E B^0 &&
1055 git tag E
1056 )
1057'
1058
1059test_expect_success 'check symlink modify/modify' '
1060 (
1061 cd symlink-modify-modify &&
1062
1063 git checkout D^0 &&
1064
1065 test_must_fail git merge -s recursive E^0 &&
1066
1067 git ls-files -s >out &&
1068 test_line_count = 3 out &&
1069 git ls-files -u >out &&
1070 test_line_count = 3 out &&
1071 git ls-files -o >out &&
1072 test_line_count = 1 out
1073 )
1074'
1075
1076#
1077# criss-cross with add/add of a symlink:
1078#
1079# B D
1080# o---o
1081# / \ / \
1082# A o X ? F
1083# \ / \ /
1084# o---o
1085# C E
1086#
1087# Commit A: No symlink or path exists yet
1088# Commit B: set up symlink: fickle->disneyland
1089# Commit C: set up symlink: fickle->home
1090# Commit D: merge B&C, resolving in favor of B
1091# Commit E: merge B&C, resolving in favor of C
1092#
1093# This is an obvious add/add conflict for the symlink 'fickle'. Can
1094# git detect it?
1095
1096test_expect_success 'setup symlink add/add' '
1097 git init symlink-add-add &&
1098 (
1099 cd symlink-add-add &&
1100
1101 touch ignoreme &&
1102 git add ignoreme &&
1103 git commit -m A &&
1104 git tag A &&
1105
1106 git checkout -b B A &&
1107 test_ln_s_add disneyland fickle &&
1108 git commit -m B &&
1109
1110 git checkout -b C A &&
1111 test_ln_s_add home fickle &&
1112 git add fickle &&
1113 git commit -m C &&
1114
1115 git checkout -q B^0 &&
1116 git merge -s ours -m D C^0 &&
1117 git tag D &&
1118
1119 git checkout -q C^0 &&
1120 git merge -s ours -m E B^0 &&
1121 git tag E
1122 )
1123'
1124
1125test_expect_success 'check symlink add/add' '
1126 (
1127 cd symlink-add-add &&
1128
1129 git checkout D^0 &&
1130
1131 test_must_fail git merge -s recursive E^0 &&
1132
1133 git ls-files -s >out &&
1134 test_line_count = 3 out &&
1135 git ls-files -u >out &&
1136 test_line_count = 2 out &&
1137 git ls-files -o >out &&
1138 test_line_count = 1 out
1139 )
1140'
1141
1142#
1143# criss-cross with modify/modify on a submodule:
1144#
1145# B D
1146# o---o
1147# / \ / \
1148# A o X ? F
1149# \ / \ /
1150# o---o
1151# C E
1152#
1153# Commit A: simple submodule repo
1154# Commit B: update repo
1155# Commit C: update repo differently
1156# Commit D: merge B&C, resolving in favor of B
1157# Commit E: merge B&C, resolving in favor of C
1158#
1159# This is an obvious modify/modify conflict for the submodule 'repo'. Can
1160# git detect it?
1161
1162test_expect_success 'setup submodule modify/modify' '
1163 git init submodule-modify-modify &&
1164 (
1165 cd submodule-modify-modify &&
1166
1167 git init submod &&
1168 (
1169 cd submod &&
1170 touch file-A &&
1171 git add file-A &&
1172 git commit -m A &&
1173 git tag A &&
1174
1175 git checkout -b B A &&
1176 touch file-B &&
1177 git add file-B &&
1178 git commit -m B &&
1179 git tag B &&
1180
1181 git checkout -b C A &&
1182 touch file-C &&
1183 git add file-C &&
1184 git commit -m C &&
1185 git tag C
1186 ) &&
1187
1188 git -C submod reset --hard A &&
1189 git add submod &&
1190 git commit -m A &&
1191 git tag A &&
1192
1193 git checkout -b B A &&
1194 git -C submod reset --hard B &&
1195 git add submod &&
1196 git commit -m B &&
1197
1198 git checkout -b C A &&
1199 git -C submod reset --hard C &&
1200 git add submod &&
1201 git commit -m C &&
1202
1203 git checkout -q B^0 &&
1204 git merge -s ours -m D C^0 &&
1205 git tag D &&
1206
1207 git checkout -q C^0 &&
1208 git merge -s ours -m E B^0 &&
1209 git tag E
1210 )
1211'
1212
1213test_expect_success 'check submodule modify/modify' '
1214 (
1215 cd submodule-modify-modify &&
1216
1217 git checkout D^0 &&
1218
1219 test_must_fail git merge -s recursive E^0 &&
1220
1221 git ls-files -s >out &&
1222 test_line_count = 3 out &&
1223 git ls-files -u >out &&
1224 test_line_count = 3 out &&
1225 git ls-files -o >out &&
1226 test_line_count = 1 out
1227 )
1228'
1229
1230#
1231# criss-cross with add/add on a submodule:
1232#
1233# B D
1234# o---o
1235# / \ / \
1236# A o X ? F
1237# \ / \ /
1238# o---o
1239# C E
1240#
1241# Commit A: nothing of note
1242# Commit B: introduce submodule repo
1243# Commit C: introduce submodule repo at different commit
1244# Commit D: merge B&C, resolving in favor of B
1245# Commit E: merge B&C, resolving in favor of C
1246#
1247# This is an obvious add/add conflict for the submodule 'repo'. Can
1248# git detect it?
1249
1250test_expect_success 'setup submodule add/add' '
1251 git init submodule-add-add &&
1252 (
1253 cd submodule-add-add &&
1254
1255 git init submod &&
1256 (
1257 cd submod &&
1258 touch file-A &&
1259 git add file-A &&
1260 git commit -m A &&
1261 git tag A &&
1262
1263 git checkout -b B A &&
1264 touch file-B &&
1265 git add file-B &&
1266 git commit -m B &&
1267 git tag B &&
1268
1269 git checkout -b C A &&
1270 touch file-C &&
1271 git add file-C &&
1272 git commit -m C &&
1273 git tag C
1274 ) &&
1275
1276 touch irrelevant-file &&
1277 git add irrelevant-file &&
1278 git commit -m A &&
1279 git tag A &&
1280
1281 git checkout -b B A &&
1282 git -C submod reset --hard B &&
1283 git add submod &&
1284 git commit -m B &&
1285
1286 git checkout -b C A &&
1287 git -C submod reset --hard C &&
1288 git add submod &&
1289 git commit -m C &&
1290
1291 git checkout -q B^0 &&
1292 git merge -s ours -m D C^0 &&
1293 git tag D &&
1294
1295 git checkout -q C^0 &&
1296 git merge -s ours -m E B^0 &&
1297 git tag E
1298 )
1299'
1300
1301test_expect_success 'check submodule add/add' '
1302 (
1303 cd submodule-add-add &&
1304
1305 git checkout D^0 &&
1306
1307 test_must_fail git merge -s recursive E^0 &&
1308
1309 git ls-files -s >out &&
1310 test_line_count = 3 out &&
1311 git ls-files -u >out &&
1312 test_line_count = 2 out &&
1313 git ls-files -o >out &&
1314 test_line_count = 1 out
1315 )
1316'
1317
1318#
1319# criss-cross with conflicting entry types:
1320#
1321# B D
1322# o---o
1323# / \ / \
1324# A o X ? F
1325# \ / \ /
1326# o---o
1327# C E
1328#
1329# Commit A: nothing of note
1330# Commit B: introduce submodule 'path'
1331# Commit C: introduce symlink 'path'
1332# Commit D: merge B&C, resolving in favor of B
1333# Commit E: merge B&C, resolving in favor of C
1334#
1335# This is an obvious add/add conflict for 'path'. Can git detect it?
1336
1337test_expect_success 'setup conflicting entry types (submodule vs symlink)' '
1338 git init submodule-symlink-add-add &&
1339 (
1340 cd submodule-symlink-add-add &&
1341
1342 git init path &&
1343 (
1344 cd path &&
1345 touch file-B &&
1346 git add file-B &&
1347 git commit -m B &&
1348 git tag B
1349 ) &&
1350
1351 touch irrelevant-file &&
1352 git add irrelevant-file &&
1353 git commit -m A &&
1354 git tag A &&
1355
1356 git checkout -b B A &&
1357 git -C path reset --hard B &&
1358 git add path &&
1359 git commit -m B &&
1360
1361 git checkout -b C A &&
1362 rm -rf path/ &&
1363 test_ln_s_add irrelevant-file path &&
1364 git commit -m C &&
1365
1366 git checkout -q B^0 &&
1367 git merge -s ours -m D C^0 &&
1368 git tag D &&
1369
1370 git checkout -q C^0 &&
1371 git merge -s ours -m E B^0 &&
1372 git tag E
1373 )
1374'
1375
1376test_expect_success 'check conflicting entry types (submodule vs symlink)' '
1377 (
1378 cd submodule-symlink-add-add &&
1379
1380 git checkout D^0 &&
1381
1382 test_must_fail git merge -s recursive E^0 &&
1383
1384 git ls-files -s >out &&
1385 test_line_count = 3 out &&
1386 git ls-files -u >out &&
1387 test_line_count = 2 out &&
1388 git ls-files -o >out &&
1389 test_line_count = 1 out
1390 )
1391'
1392
1393#
1394# criss-cross with regular files that have conflicting modes:
1395#
1396# B D
1397# o---o
1398# / \ / \
1399# A o X ? F
1400# \ / \ /
1401# o---o
1402# C E
1403#
1404# Commit A: nothing of note
1405# Commit B: introduce file source_me.bash, not executable
1406# Commit C: introduce file source_me.bash, executable
1407# Commit D: merge B&C, resolving in favor of B
1408# Commit E: merge B&C, resolving in favor of C
1409#
1410# This is an obvious add/add mode conflict. Can git detect it?
1411
1412test_expect_success 'setup conflicting modes for regular file' '
1413 git init regular-file-mode-conflict &&
1414 (
1415 cd regular-file-mode-conflict &&
1416
1417 touch irrelevant-file &&
1418 git add irrelevant-file &&
1419 git commit -m A &&
1420 git tag A &&
1421
1422 git checkout -b B A &&
1423 echo "command_to_run" >source_me.bash &&
1424 git add source_me.bash &&
1425 git commit -m B &&
1426
1427 git checkout -b C A &&
1428 echo "command_to_run" >source_me.bash &&
1429 git add source_me.bash &&
1430 test_chmod +x source_me.bash &&
1431 git commit -m C &&
1432
1433 git checkout -q B^0 &&
1434 git merge -s ours -m D C^0 &&
1435 git tag D &&
1436
1437 git checkout -q C^0 &&
1438 git merge -s ours -m E B^0 &&
1439 git tag E
1440 )
1441'
1442
1443test_expect_failure 'check conflicting modes for regular file' '
1444 (
1445 cd regular-file-mode-conflict &&
1446
1447 git checkout D^0 &&
1448
1449 test_must_fail git merge -s recursive E^0 &&
1450
1451 git ls-files -s >out &&
1452 test_line_count = 3 out &&
1453 git ls-files -u >out &&
1454 test_line_count = 2 out &&
1455 git ls-files -o >out &&
1456 test_line_count = 1 out
1457 )
1458'
1459
1460# Setup:
1461# L1---L2
1462# / \ / \
1463# main X ?
1464# \ / \ /
1465# R1---R2
1466#
1467# Where:
1468# main has two files, named 'b' and 'a'
1469# branches L1 and R1 both modify each of the two files in conflicting ways
1470#
1471# L2 is a merge of R1 into L1; more on it later.
1472# R2 is a merge of L1 into R1; more on it later.
1473#
1474# X is an auto-generated merge-base used when merging L2 and R2.
1475# since X is a merge of L1 and R1, it has conflicting versions of each file
1476#
1477# More about L2 and R2:
1478# - both resolve the conflicts in 'b' and 'a' differently
1479# - L2 renames 'b' to 'm'
1480# - R2 renames 'a' to 'm'
1481#
1482# In the end, in file 'm' we have four different conflicting files (from
1483# two versions of 'b' and two of 'a'). In addition, if
1484# merge.conflictstyle is diff3, then the base version also has
1485# conflict markers of its own, leading to a total of three levels of
1486# conflict markers. This is a pretty weird corner case, but we just want
1487# to ensure that we handle it as well as practical.
1488
1489test_expect_success 'setup nested conflicts' '
1490 git init nested_conflicts &&
1491 (
1492 cd nested_conflicts &&
1493
1494 # Create some related files now
1495 printf "Random base content line %d\n" $(test_seq 1 10) >initial &&
1496
1497 cp initial b_L1 &&
1498 cp initial b_R1 &&
1499 cp initial b_L2 &&
1500 cp initial b_R2 &&
1501 cp initial a_L1 &&
1502 cp initial a_R1 &&
1503 cp initial a_L2 &&
1504 cp initial a_R2 &&
1505
1506 test_write_lines b b_L1 >>b_L1 &&
1507 test_write_lines b b_R1 >>b_R1 &&
1508 test_write_lines b b_L2 >>b_L2 &&
1509 test_write_lines b b_R2 >>b_R2 &&
1510 test_write_lines a a_L1 >>a_L1 &&
1511 test_write_lines a a_R1 >>a_R1 &&
1512 test_write_lines a a_L2 >>a_L2 &&
1513 test_write_lines a a_R2 >>a_R2 &&
1514
1515 # Setup original commit (or merge-base), consisting of
1516 # files named "b" and "a"
1517 cp initial b &&
1518 cp initial a &&
1519 echo b >>b &&
1520 echo a >>a &&
1521 git add b a &&
1522 test_tick && git commit -m initial &&
1523
1524 git branch L &&
1525 git branch R &&
1526
1527 # Handle the left side
1528 git checkout L &&
1529 mv -f b_L1 b &&
1530 mv -f a_L1 a &&
1531 git add b a &&
1532 test_tick && git commit -m "version L1 of files" &&
1533 git tag L1 &&
1534
1535 # Handle the right side
1536 git checkout R &&
1537 mv -f b_R1 b &&
1538 mv -f a_R1 a &&
1539 git add b a &&
1540 test_tick && git commit -m "version R1 of files" &&
1541 git tag R1 &&
1542
1543 # Create first merge on left side
1544 git checkout L &&
1545 test_must_fail git merge R1 &&
1546 mv -f b_L2 b &&
1547 mv -f a_L2 a &&
1548 git add b a &&
1549 git mv b m &&
1550 test_tick && git commit -m "left merge, rename b->m" &&
1551 git tag L2 &&
1552
1553 # Create first merge on right side
1554 git checkout R &&
1555 test_must_fail git merge L1 &&
1556 mv -f b_R2 b &&
1557 mv -f a_R2 a &&
1558 git add b a &&
1559 git mv a m &&
1560 test_tick && git commit -m "right merge, rename a->m" &&
1561 git tag R2
1562 )
1563'
1564
1565test_expect_success 'check nested conflicts' '
1566 (
1567 cd nested_conflicts &&
1568
1569 git clean -f &&
1570 MAIN=$(git rev-parse --short main) &&
1571 git checkout L2^0 &&
1572
1573 # Merge must fail; there is a conflict
1574 test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R2^0 &&
1575
1576 # Make sure the index has the right number of entries
1577 git ls-files -s >out &&
1578 test_line_count = 2 out &&
1579 git ls-files -u >out &&
1580 test_line_count = 2 out &&
1581 # Ensure we have the correct number of untracked files
1582 git ls-files -o >out &&
1583 test_line_count = 1 out &&
1584
1585 # Create a and b from virtual merge base X
1586 git cat-file -p main:a >base &&
1587 git cat-file -p L1:a >ours &&
1588 git cat-file -p R1:a >theirs &&
1589 test_must_fail git merge-file --diff3 \
1590 -L "Temporary merge branch 1" \
1591 -L "$MAIN" \
1592 -L "Temporary merge branch 2" \
1593 ours \
1594 base \
1595 theirs &&
1596 sed -e "s/^\([<|=>]\)/\1\1/" ours >vmb_a &&
1597
1598 git cat-file -p main:b >base &&
1599 git cat-file -p L1:b >ours &&
1600 git cat-file -p R1:b >theirs &&
1601 test_must_fail git merge-file --diff3 \
1602 -L "Temporary merge branch 1" \
1603 -L "$MAIN" \
1604 -L "Temporary merge branch 2" \
1605 ours \
1606 base \
1607 theirs &&
1608 sed -e "s/^\([<|=>]\)/\1\1/" ours >vmb_b &&
1609
1610 # Compare :2:m to expected values
1611 git cat-file -p L2:m >ours &&
1612 git cat-file -p R2:b >theirs &&
1613 test_must_fail git merge-file --diff3 \
1614 -L "HEAD:m" \
1615 -L "merged common ancestors:b" \
1616 -L "R2^0:b" \
1617 ours \
1618 vmb_b \
1619 theirs &&
1620 sed -e "s/^\([<|=>]\)/\1\1/" ours >m_stage_2 &&
1621 git cat-file -p :2:m >actual &&
1622 test_cmp m_stage_2 actual &&
1623
1624 # Compare :3:m to expected values
1625 git cat-file -p L2:a >ours &&
1626 git cat-file -p R2:m >theirs &&
1627 test_must_fail git merge-file --diff3 \
1628 -L "HEAD:a" \
1629 -L "merged common ancestors:a" \
1630 -L "R2^0:m" \
1631 ours \
1632 vmb_a \
1633 theirs &&
1634 sed -e "s/^\([<|=>]\)/\1\1/" ours >m_stage_3 &&
1635 git cat-file -p :3:m >actual &&
1636 test_cmp m_stage_3 actual &&
1637
1638 # Compare m to expected contents
1639 >empty &&
1640 cp m_stage_2 expected_final_m &&
1641 test_must_fail git merge-file --diff3 \
1642 -L "HEAD" \
1643 -L "merged common ancestors" \
1644 -L "R2^0" \
1645 expected_final_m \
1646 empty \
1647 m_stage_3 &&
1648 test_cmp expected_final_m m
1649 )
1650'
1651
1652# Setup:
1653# L1---L2---L3
1654# / \ / \ / \
1655# main X1 X2 ?
1656# \ / \ / \ /
1657# R1---R2---R3
1658#
1659# Where:
1660# main has one file named 'content'
1661# branches L1 and R1 both modify each of the two files in conflicting ways
1662#
1663# L<n> (n>1) is a merge of R<n-1> into L<n-1>
1664# R<n> (n>1) is a merge of L<n-1> into R<n-1>
1665# L<n> and R<n> resolve the conflicts differently.
1666#
1667# X<n> is an auto-generated merge-base used when merging L<n+1> and R<n+1>.
1668# By construction, X1 has conflict markers due to conflicting versions.
1669# X2, due to using merge.conflictstyle=3, has nested conflict markers.
1670#
1671# So, merging R3 into L3 using merge.conflictstyle=3 should show the
1672# nested conflict markers from X2 in the base version -- that means we
1673# have three levels of conflict markers. Can we distinguish all three?
1674
1675test_expect_success 'setup virtual merge base with nested conflicts' '
1676 git init virtual_merge_base_has_nested_conflicts &&
1677 (
1678 cd virtual_merge_base_has_nested_conflicts &&
1679
1680 # Create some related files now
1681 printf "Random base content line %d\n" $(test_seq 1 10) >content &&
1682
1683 # Setup original commit
1684 git add content &&
1685 test_tick && git commit -m initial &&
1686
1687 git branch L &&
1688 git branch R &&
1689
1690 # Create L1
1691 git checkout L &&
1692 echo left >>content &&
1693 git add content &&
1694 test_tick && git commit -m "version L1 of content" &&
1695 git tag L1 &&
1696
1697 # Create R1
1698 git checkout R &&
1699 echo right >>content &&
1700 git add content &&
1701 test_tick && git commit -m "version R1 of content" &&
1702 git tag R1 &&
1703
1704 # Create L2
1705 git checkout L &&
1706 test_must_fail git -c merge.conflictstyle=diff3 merge R1 &&
1707 git checkout L1 content &&
1708 test_tick && git commit -m "version L2 of content" &&
1709 git tag L2 &&
1710
1711 # Create R2
1712 git checkout R &&
1713 test_must_fail git -c merge.conflictstyle=diff3 merge L1 &&
1714 git checkout R1 content &&
1715 test_tick && git commit -m "version R2 of content" &&
1716 git tag R2 &&
1717
1718 # Create L3
1719 git checkout L &&
1720 test_must_fail git -c merge.conflictstyle=diff3 merge R2 &&
1721 git checkout L1 content &&
1722 test_tick && git commit -m "version L3 of content" &&
1723 git tag L3 &&
1724
1725 # Create R3
1726 git checkout R &&
1727 test_must_fail git -c merge.conflictstyle=diff3 merge L2 &&
1728 git checkout R1 content &&
1729 test_tick && git commit -m "version R3 of content" &&
1730 git tag R3
1731 )
1732'
1733
1734test_expect_success 'check virtual merge base with nested conflicts' '
1735 (
1736 cd virtual_merge_base_has_nested_conflicts &&
1737
1738 MAIN=$(git rev-parse --short main) &&
1739 git checkout L3^0 &&
1740
1741 # Merge must fail; there is a conflict
1742 test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R3^0 &&
1743
1744 # Make sure the index has the right number of entries
1745 git ls-files -s >out &&
1746 test_line_count = 3 out &&
1747 git ls-files -u >out &&
1748 test_line_count = 3 out &&
1749 # Ensure we have the correct number of untracked files
1750 git ls-files -o >out &&
1751 test_line_count = 1 out &&
1752
1753 # Compare :[23]:content to expected values
1754 git rev-parse L1:content R1:content >expect &&
1755 git rev-parse :2:content :3:content >actual &&
1756 test_cmp expect actual &&
1757
1758 # Imitate X1 merge base, except without long enough conflict
1759 # markers because a subsequent sed will modify them. Put
1760 # result into vmb.
1761 git cat-file -p main:content >base &&
1762 git cat-file -p L:content >left &&
1763 git cat-file -p R:content >right &&
1764 cp left merged-once &&
1765 test_must_fail git merge-file --diff3 \
1766 -L "Temporary merge branch 1" \
1767 -L "$MAIN" \
1768 -L "Temporary merge branch 2" \
1769 merged-once \
1770 base \
1771 right &&
1772 sed -e "s/^\([<|=>]\)/\1\1\1/" merged-once >vmb &&
1773
1774 # Imitate X2 merge base, overwriting vmb. Note that we
1775 # extend both sets of conflict markers to make them longer
1776 # with the sed command.
1777 cp left merged-twice &&
1778 test_must_fail git merge-file --diff3 \
1779 -L "Temporary merge branch 1" \
1780 -L "merged common ancestors" \
1781 -L "Temporary merge branch 2" \
1782 merged-twice \
1783 vmb \
1784 right &&
1785 sed -e "s/^\([<|=>]\)/\1\1\1/" merged-twice >vmb &&
1786
1787 # Compare :1:content to expected value
1788 git cat-file -p :1:content >actual &&
1789 test_cmp vmb actual &&
1790
1791 # Determine expected content in final outer merge, compare to
1792 # what the merge generated.
1793 cp -f left expect &&
1794 test_must_fail git merge-file --diff3 \
1795 -L "HEAD" -L "merged common ancestors" -L "R3^0" \
1796 expect vmb right &&
1797 test_cmp expect content
1798 )
1799'
1800
1801test_done