Git fork
1#!/bin/sh
2
3test_description='git merge-tree --write-tree'
4
5. ./test-lib.sh
6
7test_expect_success setup '
8 test_write_lines 1 2 3 4 5 >numbers &&
9 echo hello >greeting &&
10 echo foo >whatever &&
11 git add numbers greeting whatever &&
12 test_tick &&
13 git commit -m initial &&
14
15 git branch side1 &&
16 git branch side2 &&
17 git branch side3 &&
18 git branch side4 &&
19
20 git checkout side1 &&
21 test_write_lines 1 2 3 4 5 6 >numbers &&
22 echo hi >greeting &&
23 echo bar >whatever &&
24 git add numbers greeting whatever &&
25 test_tick &&
26 git commit -m modify-stuff &&
27
28 git checkout side2 &&
29 test_write_lines 0 1 2 3 4 5 >numbers &&
30 echo yo >greeting &&
31 git rm whatever &&
32 mkdir whatever &&
33 >whatever/empty &&
34 git add numbers greeting whatever/empty &&
35 test_tick &&
36 git commit -m other-modifications &&
37
38 git checkout side3 &&
39 git mv numbers sequence &&
40 test_tick &&
41 git commit -m rename-numbers &&
42
43 git checkout side4 &&
44 test_write_lines 0 1 2 3 4 5 >numbers &&
45 echo yo >greeting &&
46 git add numbers greeting &&
47 test_tick &&
48 git commit -m other-content-modifications &&
49
50 git switch --orphan unrelated &&
51 >something-else &&
52 git add something-else &&
53 test_tick &&
54 git commit -m first-commit
55'
56
57test_expect_success '--quiet on clean merge' '
58 # Get rid of loose objects to start with
59 git gc &&
60 echo "0 objects, 0 kilobytes" >expect &&
61 git count-objects >actual &&
62 test_cmp expect actual &&
63
64 # Ensure merge is successful (exit code of 0)
65 git merge-tree --write-tree --quiet side1 side3 >output &&
66
67 # Ensure there is no output
68 test_must_be_empty output &&
69
70 # Ensure no loose objects written (all new objects written would have
71 # been in "outer layer" of the merge)
72 git count-objects >actual &&
73 test_cmp expect actual
74'
75
76test_expect_success 'Clean merge' '
77 TREE_OID=$(git merge-tree --write-tree side1 side3) &&
78 q_to_tab <<-EOF >expect &&
79 100644 blob $(git rev-parse side1:greeting)Qgreeting
80 100644 blob $(git rev-parse side1:numbers)Qsequence
81 100644 blob $(git rev-parse side1:whatever)Qwhatever
82 EOF
83
84 git ls-tree $TREE_OID >actual &&
85 test_cmp expect actual
86'
87
88# Repeat the previous test, but turn off rename detection
89test_expect_success 'Failed merge without rename detection' '
90 test_must_fail git -c diff.renames=false merge-tree --write-tree side1 side3 >out &&
91 grep "CONFLICT (modify/delete): numbers deleted" out
92'
93
94test_expect_success '--quiet on conflicted merge' '
95 # Get rid of loose objects to start with
96 git gc &&
97 echo "0 objects, 0 kilobytes" >expect &&
98 git count-objects >actual &&
99 test_cmp expect actual &&
100
101 # Ensure merge has conflict
102 test_expect_code 1 git merge-tree --write-tree --quiet side1 side2 >output &&
103
104 # Ensure there is no output
105 test_must_be_empty output &&
106
107 # Ensure no loose objects written (all new objects written would have
108 # been in "outer layer" of the merge)
109 git count-objects >actual &&
110 test_cmp expect actual
111'
112
113test_expect_success 'Content merge and a few conflicts' '
114 git checkout side1^0 &&
115 test_must_fail git merge side2 &&
116 expected_tree=$(git rev-parse AUTO_MERGE) &&
117
118 # We will redo the merge, while we are still in a conflicted state!
119 git ls-files -u >conflicted-file-info &&
120 test_when_finished "git reset --hard" &&
121
122 test_expect_code 1 git merge-tree --write-tree side1 side2 >RESULT &&
123 actual_tree=$(head -n 1 RESULT) &&
124
125 # Due to differences of e.g. "HEAD" vs "side1", the results will not
126 # exactly match. Dig into individual files.
127
128 # Numbers should have three-way merged cleanly
129 test_write_lines 0 1 2 3 4 5 6 >expect &&
130 git show ${actual_tree}:numbers >actual &&
131 test_cmp expect actual &&
132
133 # whatever and whatever~<branch> should have same HASHES
134 git rev-parse ${expected_tree}:whatever ${expected_tree}:whatever~HEAD >expect &&
135 git rev-parse ${actual_tree}:whatever ${actual_tree}:whatever~side1 >actual &&
136 test_cmp expect actual &&
137
138 # greeting should have a merge conflict
139 git show ${expected_tree}:greeting >tmp &&
140 sed -e s/HEAD/side1/ tmp >expect &&
141 git show ${actual_tree}:greeting >actual &&
142 test_cmp expect actual
143'
144
145test_expect_success 'Auto resolve conflicts by "ours" strategy option' '
146 git checkout side1^0 &&
147
148 # make sure merge conflict exists
149 test_must_fail git merge side4 &&
150 git merge --abort &&
151
152 git merge -X ours side4 &&
153 git rev-parse HEAD^{tree} >expected &&
154
155 git merge-tree -X ours side1 side4 >actual &&
156
157 test_cmp expected actual
158'
159
160test_expect_success 'Barf on misspelled option, with exit code other than 0 or 1' '
161 # Mis-spell with single "s" instead of double "s"
162 test_expect_code 129 git merge-tree --write-tree --mesages FOOBAR side1 side2 2>expect &&
163
164 grep "error: unknown option.*mesages" expect
165'
166
167test_expect_success 'Barf on too many arguments' '
168 test_expect_code 129 git merge-tree --write-tree side1 side2 invalid 2>expect &&
169
170 grep "^usage: git merge-tree" expect
171'
172
173anonymize_hash() {
174 sed -e "s/[0-9a-f]\{40,\}/HASH/g" "$@"
175}
176
177test_expect_success 'test conflict notices and such' '
178 test_expect_code 1 git merge-tree --write-tree --name-only side1 side2 >out &&
179 anonymize_hash out >actual &&
180
181 # Expected results:
182 # "greeting" should merge with conflicts
183 # "numbers" should merge cleanly
184 # "whatever" has *both* a modify/delete and a file/directory conflict
185 cat <<-EOF >expect &&
186 HASH
187 greeting
188 whatever~side1
189
190 Auto-merging greeting
191 CONFLICT (content): Merge conflict in greeting
192 Auto-merging numbers
193 CONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
194 CONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1. Version side1 of whatever~side1 left in tree.
195 EOF
196
197 test_cmp expect actual
198'
199
200# directory rename + content conflict
201# Commit O: foo, olddir/{a,b,c}
202# Commit A: modify foo, newdir/{a,b,c}
203# Commit B: modify foo differently & rename foo -> olddir/bar
204# Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
205
206test_expect_success 'directory rename + content conflict' '
207 # Setup
208 git init dir-rename-and-content &&
209 (
210 cd dir-rename-and-content &&
211 test_write_lines 1 2 3 4 5 >foo &&
212 mkdir olddir &&
213 for i in a b c; do echo $i >olddir/$i || exit 1; done &&
214 git add foo olddir &&
215 git commit -m "original" &&
216
217 git branch O &&
218 git branch A &&
219 git branch B &&
220
221 git checkout A &&
222 test_write_lines 1 2 3 4 5 6 >foo &&
223 git add foo &&
224 git mv olddir newdir &&
225 git commit -m "Modify foo, rename olddir to newdir" &&
226
227 git checkout B &&
228 test_write_lines 1 2 3 4 5 six >foo &&
229 git add foo &&
230 git mv foo olddir/bar &&
231 git commit -m "Modify foo & rename foo -> olddir/bar"
232 ) &&
233 # Testing
234 (
235 cd dir-rename-and-content &&
236
237 test_expect_code 1 \
238 git merge-tree -z A^0 B^0 >out &&
239 echo >>out &&
240 anonymize_hash out >actual &&
241 q_to_tab <<-\EOF | lf_to_nul >expect &&
242 HASH
243 100644 HASH 1Qnewdir/bar
244 100644 HASH 2Qnewdir/bar
245 100644 HASH 3Qnewdir/bar
246 EOF
247
248 q_to_nul <<-EOF >>expect &&
249 Q2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.
250 Q1Qnewdir/barQAuto-mergingQAuto-merging newdir/bar
251 Q1Qnewdir/barQCONFLICT (contents)QCONFLICT (content): Merge conflict in newdir/bar
252 Q
253 EOF
254 test_cmp expect actual
255 )
256'
257
258# rename/delete + modify/delete handling
259# Commit O: foo
260# Commit A: modify foo + rename to bar
261# Commit B: delete foo
262# Expected: CONFLICT(rename/delete) + CONFLICT(modify/delete)
263
264test_expect_success 'rename/delete handling' '
265 # Setup
266 git init rename-delete &&
267 (
268 cd rename-delete &&
269 test_write_lines 1 2 3 4 5 >foo &&
270 git add foo &&
271 git commit -m "original" &&
272
273 git branch O &&
274 git branch A &&
275 git branch B &&
276
277 git checkout A &&
278 test_write_lines 1 2 3 4 5 6 >foo &&
279 git add foo &&
280 git mv foo bar &&
281 git commit -m "Modify foo, rename to bar" &&
282
283 git checkout B &&
284 git rm foo &&
285 git commit -m "remove foo"
286 ) &&
287 # Testing
288 (
289 cd rename-delete &&
290
291 test_expect_code 1 \
292 git merge-tree -z A^0 B^0 >out &&
293 echo >>out &&
294 anonymize_hash out >actual &&
295 q_to_tab <<-\EOF | lf_to_nul >expect &&
296 HASH
297 100644 HASH 1Qbar
298 100644 HASH 2Qbar
299 EOF
300
301 q_to_nul <<-EOF >>expect &&
302 Q2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in A^0, but deleted in B^0.
303 Q1QbarQCONFLICT (modify/delete)QCONFLICT (modify/delete): bar deleted in B^0 and modified in A^0. Version A^0 of bar left in tree.
304 Q
305 EOF
306 test_cmp expect actual
307 )
308'
309
310# rename/add handling
311# Commit O: foo
312# Commit A: modify foo, add different bar
313# Commit B: modify & rename foo->bar
314# Expected: CONFLICT(add/add) [via rename collide] for bar
315
316test_expect_success 'rename/add handling' '
317 # Setup
318 git init rename-add &&
319 (
320 cd rename-add &&
321 test_write_lines original 1 2 3 4 5 >foo &&
322 git add foo &&
323 git commit -m "original" &&
324
325 git branch O &&
326 git branch A &&
327 git branch B &&
328
329 git checkout A &&
330 test_write_lines 1 2 3 4 5 >foo &&
331 echo "different file" >bar &&
332 git add foo bar &&
333 git commit -m "Modify foo, add bar" &&
334
335 git checkout B &&
336 test_write_lines original 1 2 3 4 5 6 >foo &&
337 git add foo &&
338 git mv foo bar &&
339 git commit -m "rename foo to bar"
340 ) &&
341 # Testing
342 (
343 cd rename-add &&
344
345 test_expect_code 1 \
346 git merge-tree -z A^0 B^0 >out &&
347 echo >>out &&
348
349 #
350 # First, check that the bar that appears at stage 3 does not
351 # correspond to an individual blob anywhere in history
352 #
353 hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
354 git rev-list --objects --all >all_blobs &&
355 ! grep $hash all_blobs &&
356
357 #
358 # Second, check anonymized hash output against expectation
359 #
360 anonymize_hash out >actual &&
361 q_to_tab <<-\EOF | lf_to_nul >expect &&
362 HASH
363 100644 HASH 2Qbar
364 100644 HASH 3Qbar
365 EOF
366
367 q_to_nul <<-EOF >>expect &&
368 Q1QbarQAuto-mergingQAuto-merging bar
369 Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
370 Q1QfooQAuto-mergingQAuto-merging foo
371 Q
372 EOF
373 test_cmp expect actual
374 )
375'
376
377# rename/add, where add is a mode conflict
378# Commit O: foo
379# Commit A: modify foo, add symlink bar
380# Commit B: modify & rename foo->bar
381# Expected: CONFLICT(distinct modes) for bar
382
383test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '
384 # Setup
385 git init rename-add-symlink &&
386 (
387 cd rename-add-symlink &&
388 test_write_lines original 1 2 3 4 5 >foo &&
389 git add foo &&
390 git commit -m "original" &&
391
392 git branch O &&
393 git branch A &&
394 git branch B &&
395
396 git checkout A &&
397 test_write_lines 1 2 3 4 5 >foo &&
398 ln -s foo bar &&
399 git add foo bar &&
400 git commit -m "Modify foo, add symlink bar" &&
401
402 git checkout B &&
403 test_write_lines original 1 2 3 4 5 6 >foo &&
404 git add foo &&
405 git mv foo bar &&
406 git commit -m "rename foo to bar"
407 ) &&
408 # Testing
409 (
410 cd rename-add-symlink &&
411
412 test_expect_code 1 \
413 git merge-tree -z A^0 B^0 >out &&
414 echo >>out &&
415
416 #
417 # First, check that the bar that appears at stage 3 does not
418 # correspond to an individual blob anywhere in history
419 #
420 hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
421 git rev-list --objects --all >all_blobs &&
422 ! grep $hash all_blobs &&
423
424 #
425 # Second, check anonymized hash output against expectation
426 #
427 anonymize_hash out >actual &&
428 q_to_tab <<-\EOF | lf_to_nul >expect &&
429 HASH
430 120000 HASH 2Qbar
431 100644 HASH 3Qbar~B^0
432 EOF
433
434 q_to_nul <<-EOF >>expect &&
435 Q2QbarQbar~B^0QCONFLICT (distinct modes)QCONFLICT (distinct types): bar had different types on each side; renamed one of them so each can be recorded somewhere.
436 Q1QfooQAuto-mergingQAuto-merging foo
437 Q
438 EOF
439 test_cmp expect actual
440 )
441'
442
443# rename/rename(1to2) + content conflict handling
444# Commit O: foo
445# Commit A: modify foo & rename to bar
446# Commit B: modify foo & rename to baz
447# Expected: CONFLICT(rename/rename)
448
449test_expect_success 'rename/rename + content conflict' '
450 # Setup
451 git init rr-plus-content &&
452 (
453 cd rr-plus-content &&
454 test_write_lines 1 2 3 4 5 >foo &&
455 git add foo &&
456 git commit -m "original" &&
457
458 git branch O &&
459 git branch A &&
460 git branch B &&
461
462 git checkout A &&
463 test_write_lines 1 2 3 4 5 six >foo &&
464 git add foo &&
465 git mv foo bar &&
466 git commit -m "Modify foo + rename to bar" &&
467
468 git checkout B &&
469 test_write_lines 1 2 3 4 5 6 >foo &&
470 git add foo &&
471 git mv foo baz &&
472 git commit -m "Modify foo + rename to baz"
473 ) &&
474 # Testing
475 (
476 cd rr-plus-content &&
477
478 test_expect_code 1 \
479 git merge-tree -z A^0 B^0 >out &&
480 echo >>out &&
481 anonymize_hash out >actual &&
482 q_to_tab <<-\EOF | lf_to_nul >expect &&
483 HASH
484 100644 HASH 2Qbar
485 100644 HASH 3Qbaz
486 100644 HASH 1Qfoo
487 EOF
488
489 q_to_nul <<-EOF >>expect &&
490 Q1QfooQAuto-mergingQAuto-merging foo
491 Q3QfooQbarQbazQCONFLICT (rename/rename)QCONFLICT (rename/rename): foo renamed to bar in A^0 and to baz in B^0.
492 Q
493 EOF
494 test_cmp expect actual
495 )
496'
497
498# rename/add/delete
499# Commit O: foo
500# Commit A: rm foo, add different bar
501# Commit B: rename foo->bar
502# Expected: CONFLICT (rename/delete), CONFLICT(add/add) [via rename collide]
503# for bar
504
505test_expect_success 'rename/add/delete conflict' '
506 # Setup
507 git init rad &&
508 (
509 cd rad &&
510 echo "original file" >foo &&
511 git add foo &&
512 git commit -m "original" &&
513
514 git branch O &&
515 git branch A &&
516 git branch B &&
517
518 git checkout A &&
519 git rm foo &&
520 echo "different file" >bar &&
521 git add bar &&
522 git commit -m "Remove foo, add bar" &&
523
524 git checkout B &&
525 git mv foo bar &&
526 git commit -m "rename foo to bar"
527 ) &&
528 # Testing
529 (
530 cd rad &&
531
532 test_expect_code 1 \
533 git merge-tree -z B^0 A^0 >out &&
534 echo >>out &&
535 anonymize_hash out >actual &&
536
537 q_to_tab <<-\EOF | lf_to_nul >expect &&
538 HASH
539 100644 HASH 2Qbar
540 100644 HASH 3Qbar
541
542 EOF
543
544 q_to_nul <<-EOF >>expect &&
545 2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in B^0, but deleted in A^0.
546 Q1QbarQAuto-mergingQAuto-merging bar
547 Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
548 Q
549 EOF
550 test_cmp expect actual
551 )
552'
553
554# rename/rename(2to1)/delete/delete
555# Commit O: foo, bar
556# Commit A: rename foo->baz, rm bar
557# Commit B: rename bar->baz, rm foo
558# Expected: 2x CONFLICT (rename/delete), CONFLICT (add/add) via colliding
559# renames for baz
560
561test_expect_success 'rename/rename(2to1)/delete/delete conflict' '
562 # Setup
563 git init rrdd &&
564 (
565 cd rrdd &&
566 echo foo >foo &&
567 echo bar >bar &&
568 git add foo bar &&
569 git commit -m O &&
570
571 git branch O &&
572 git branch A &&
573 git branch B &&
574
575 git checkout A &&
576 git mv foo baz &&
577 git rm bar &&
578 git commit -m "Rename foo, remove bar" &&
579
580 git checkout B &&
581 git mv bar baz &&
582 git rm foo &&
583 git commit -m "Rename bar, remove foo"
584 ) &&
585 # Testing
586 (
587 cd rrdd &&
588
589 test_expect_code 1 \
590 git merge-tree -z A^0 B^0 >out &&
591 echo >>out &&
592 anonymize_hash out >actual &&
593
594 q_to_tab <<-\EOF | lf_to_nul >expect &&
595 HASH
596 100644 HASH 2Qbaz
597 100644 HASH 3Qbaz
598
599 EOF
600
601 q_to_nul <<-EOF >>expect &&
602 2QbazQbarQCONFLICT (rename/delete)QCONFLICT (rename/delete): bar renamed to baz in B^0, but deleted in A^0.
603 Q2QbazQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to baz in A^0, but deleted in B^0.
604 Q1QbazQAuto-mergingQAuto-merging baz
605 Q1QbazQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in baz
606 Q
607 EOF
608 test_cmp expect actual
609 )
610'
611
612# mod6: chains of rename/rename(1to2) + add/add via colliding renames
613# Commit O: one, three, five
614# Commit A: one->two, three->four, five->six
615# Commit B: one->six, three->two, five->four
616# Expected: three CONFLICT(rename/rename) messages + three CONFLICT(add/add)
617# messages; each path in two of the multi-way merged contents
618# found in two, four, six
619
620test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via colliding renames' '
621 # Setup
622 git init mod6 &&
623 (
624 cd mod6 &&
625 test_seq 11 19 >one &&
626 test_seq 31 39 >three &&
627 test_seq 51 59 >five &&
628 git add . &&
629 test_tick &&
630 git commit -m "O" &&
631
632 git branch O &&
633 git branch A &&
634 git branch B &&
635
636 git checkout A &&
637 test_seq 10 19 >one &&
638 echo 40 >>three &&
639 git add one three &&
640 git mv one two &&
641 git mv three four &&
642 git mv five six &&
643 test_tick &&
644 git commit -m "A" &&
645
646 git checkout B &&
647 echo 20 >>one &&
648 echo forty >>three &&
649 echo 60 >>five &&
650 git add one three five &&
651 git mv one six &&
652 git mv three two &&
653 git mv five four &&
654 test_tick &&
655 git commit -m "B"
656 ) &&
657 # Testing
658 (
659 cd mod6 &&
660
661 test_expect_code 1 \
662 git merge-tree -z A^0 B^0 >out &&
663 echo >>out &&
664
665 #
666 # First, check that some of the hashes that appear as stage
667 # conflict entries do not appear as individual blobs anywhere
668 # in history.
669 #
670 hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") &&
671 hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") &&
672 git rev-list --objects --all >all_blobs &&
673 ! grep $hash1 all_blobs &&
674 ! grep $hash2 all_blobs &&
675
676 #
677 # Now compare anonymized hash output with expectation
678 #
679 anonymize_hash out >actual &&
680 q_to_tab <<-\EOF | lf_to_nul >expect &&
681 HASH
682 100644 HASH 1Qfive
683 100644 HASH 2Qfour
684 100644 HASH 3Qfour
685 100644 HASH 1Qone
686 100644 HASH 2Qsix
687 100644 HASH 3Qsix
688 100644 HASH 1Qthree
689 100644 HASH 2Qtwo
690 100644 HASH 3Qtwo
691
692 EOF
693
694 q_to_nul <<-EOF >>expect &&
695 3QfiveQsixQfourQCONFLICT (rename/rename)QCONFLICT (rename/rename): five renamed to six in A^0 and to four in B^0.
696 Q1QfourQAuto-mergingQAuto-merging four
697 Q1QfourQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in four
698 Q1QoneQAuto-mergingQAuto-merging one
699 Q3QoneQtwoQsixQCONFLICT (rename/rename)QCONFLICT (rename/rename): one renamed to two in A^0 and to six in B^0.
700 Q1QsixQAuto-mergingQAuto-merging six
701 Q1QsixQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in six
702 Q1QthreeQAuto-mergingQAuto-merging three
703 Q3QthreeQfourQtwoQCONFLICT (rename/rename)QCONFLICT (rename/rename): three renamed to four in A^0 and to two in B^0.
704 Q1QtwoQAuto-mergingQAuto-merging two
705 Q1QtwoQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in two
706 Q
707 EOF
708 test_cmp expect actual
709 )
710'
711
712# directory rename + rename/delete + modify/delete + directory/file conflict
713# Commit O: foo, olddir/{a,b,c}
714# Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file
715# Commit B: modify foo & rename foo -> olddir/bar
716# Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
717
718test_expect_success 'directory rename + rename/delete + modify/delete + directory/file conflict' '
719 # Setup
720 git init 4-stacked-conflict &&
721 (
722 cd 4-stacked-conflict &&
723 test_write_lines 1 2 3 4 5 >foo &&
724 mkdir olddir &&
725 for i in a b c; do echo $i >olddir/$i || exit 1; done &&
726 git add foo olddir &&
727 git commit -m "original" &&
728
729 git branch O &&
730 git branch A &&
731 git branch B &&
732
733 git checkout A &&
734 git rm foo &&
735 git mv olddir newdir &&
736 mkdir newdir/bar &&
737 >newdir/bar/file &&
738 git add newdir/bar/file &&
739 git commit -m "rm foo, olddir/ -> newdir/, + newdir/bar/file" &&
740
741 git checkout B &&
742 test_write_lines 1 2 3 4 5 6 >foo &&
743 git add foo &&
744 git mv foo olddir/bar &&
745 git commit -m "Modify foo & rename foo -> olddir/bar"
746 ) &&
747 # Testing
748 (
749 cd 4-stacked-conflict &&
750
751 test_expect_code 1 \
752 git merge-tree -z A^0 B^0 >out &&
753 echo >>out &&
754 anonymize_hash out >actual &&
755
756 q_to_tab <<-\EOF | lf_to_nul >expect &&
757 HASH
758 100644 HASH 1Qnewdir/bar~B^0
759 100644 HASH 3Qnewdir/bar~B^0
760 EOF
761
762 q_to_nul <<-EOF >>expect &&
763 Q2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.
764 Q2Qnewdir/barQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to newdir/bar in B^0, but deleted in A^0.
765 Q2Qnewdir/bar~B^0Qnewdir/barQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of newdir/bar from B^0; moving it to newdir/bar~B^0 instead.
766 Q1Qnewdir/bar~B^0QCONFLICT (modify/delete)QCONFLICT (modify/delete): newdir/bar~B^0 deleted in A^0 and modified in B^0. Version B^0 of newdir/bar~B^0 left in tree.
767 Q
768 EOF
769 test_cmp expect actual
770 )
771'
772
773for opt in $(git merge-tree --git-completion-helper-all)
774do
775 if test $opt = "--trivial-merge" || test $opt = "--write-tree"
776 then
777 continue
778 fi
779
780 test_expect_success "usage: --trivial-merge is incompatible with $opt" '
781 test_expect_code 128 git merge-tree --trivial-merge $opt side1 side2 side3
782 '
783done
784
785test_expect_success 'Just the conflicted files without the messages' '
786 test_expect_code 1 git merge-tree --write-tree --no-messages --name-only side1 side2 >out &&
787 anonymize_hash out >actual &&
788
789 test_write_lines HASH greeting whatever~side1 >expect &&
790
791 test_cmp expect actual
792'
793
794test_expect_success 'Check conflicted oids and modes without messages' '
795 test_expect_code 1 git merge-tree --write-tree --no-messages side1 side2 >out &&
796 anonymize_hash out >actual &&
797
798 # Compare the basic output format
799 q_to_tab >expect <<-\EOF &&
800 HASH
801 100644 HASH 1Qgreeting
802 100644 HASH 2Qgreeting
803 100644 HASH 3Qgreeting
804 100644 HASH 1Qwhatever~side1
805 100644 HASH 2Qwhatever~side1
806 EOF
807
808 test_cmp expect actual &&
809
810 # Check the actual hashes against the `ls-files -u` output too
811 tail -n +2 out | sed -e s/side1/HEAD/ >actual &&
812 test_cmp conflicted-file-info actual
813'
814
815test_expect_success 'NUL terminated conflicted file "lines"' '
816 git checkout -b tweak1 side1 &&
817 test_write_lines zero 1 2 3 4 5 6 >numbers &&
818 git add numbers &&
819 git mv numbers "Αυτά μου φαίνονται κινέζικα" &&
820 git commit -m "Renamed numbers" &&
821
822 test_expect_code 1 git merge-tree --write-tree -z tweak1 side2 >out &&
823 echo >>out &&
824 anonymize_hash out >actual &&
825
826 # Expected results:
827 # "greeting" should merge with conflicts
828 # "whatever" has *both* a modify/delete and a file/directory conflict
829 # "Αυτά μου φαίνονται κινέζικα" should have a conflict
830 echo HASH | lf_to_nul >expect &&
831
832 q_to_tab <<-EOF | lf_to_nul >>expect &&
833 100644 HASH 1Qgreeting
834 100644 HASH 2Qgreeting
835 100644 HASH 3Qgreeting
836 100644 HASH 1Qwhatever~tweak1
837 100644 HASH 2Qwhatever~tweak1
838 100644 HASH 1QΑυτά μου φαίνονται κινέζικα
839 100644 HASH 2QΑυτά μου φαίνονται κινέζικα
840 100644 HASH 3QΑυτά μου φαίνονται κινέζικα
841
842 EOF
843
844 q_to_nul <<-EOF >>expect &&
845 1QgreetingQAuto-mergingQAuto-merging greeting
846 Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
847 Q2Qwhatever~tweak1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from tweak1; moving it to whatever~tweak1 instead.
848 Q1Qwhatever~tweak1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~tweak1 deleted in side2 and modified in tweak1. Version tweak1 of whatever~tweak1 left in tree.
849 Q1QΑυτά μου φαίνονται κινέζικαQAuto-mergingQAuto-merging Αυτά μου φαίνονται κινέζικα
850 Q1QΑυτά μου φαίνονται κινέζικαQCONFLICT (contents)QCONFLICT (content): Merge conflict in Αυτά μου φαίνονται κινέζικα
851 Q
852 EOF
853
854 test_cmp expect actual
855'
856
857test_expect_success 'error out by default for unrelated histories' '
858 test_expect_code 128 git merge-tree --write-tree side1 unrelated 2>error &&
859
860 grep "refusing to merge unrelated histories" error
861'
862
863test_expect_success 'can override merge of unrelated histories' '
864 git merge-tree --write-tree --allow-unrelated-histories side1 unrelated >tree &&
865 TREE=$(cat tree) &&
866
867 git rev-parse side1:numbers side1:greeting side1:whatever unrelated:something-else >expect &&
868 git rev-parse $TREE:numbers $TREE:greeting $TREE:whatever $TREE:something-else >actual &&
869
870 test_cmp expect actual
871'
872
873test_expect_success SANITY 'merge-ort fails gracefully in a read-only repository' '
874 git init --bare read-only &&
875 git push read-only side1 side2 side3 &&
876 test_when_finished "chmod -R u+w read-only" &&
877 chmod -R a-w read-only &&
878 test_must_fail git -C read-only merge-tree side1 side3 &&
879 test_must_fail git -C read-only merge-tree side1 side2
880'
881
882test_expect_success '--stdin with both a successful and a conflicted merge' '
883 printf "side1 side3\nside1 side2" | git merge-tree --stdin >actual &&
884
885 git checkout side1^0 &&
886 git merge side3 &&
887
888 printf "1\0" >expect &&
889 git rev-parse HEAD^{tree} | lf_to_nul >>expect &&
890 printf "\0" >>expect &&
891
892 git checkout side1^0 &&
893 test_must_fail git merge side2 &&
894 sed s/HEAD/side1/ greeting >tmp &&
895 mv tmp greeting &&
896 git add -u &&
897 git mv whatever~HEAD whatever~side1 &&
898
899 printf "0\0" >>expect &&
900 git write-tree | lf_to_nul >>expect &&
901
902 cat <<-EOF | q_to_tab | lf_to_nul >>expect &&
903 100644 $(git rev-parse side1~1:greeting) 1Qgreeting
904 100644 $(git rev-parse side1:greeting) 2Qgreeting
905 100644 $(git rev-parse side2:greeting) 3Qgreeting
906 100644 $(git rev-parse side1~1:whatever) 1Qwhatever~side1
907 100644 $(git rev-parse side1:whatever) 2Qwhatever~side1
908 EOF
909
910 q_to_nul <<-EOF >>expect &&
911 Q1QgreetingQAuto-mergingQAuto-merging greeting
912 Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
913 Q1QnumbersQAuto-mergingQAuto-merging numbers
914 Q2Qwhatever~side1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
915 Q1Qwhatever~side1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1. Version side1 of whatever~side1 left in tree.
916 EOF
917
918 printf "\0\0" >>expect &&
919
920 test_cmp expect actual
921'
922
923
924test_expect_success '--merge-base is incompatible with --stdin' '
925 test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
926
927 grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect
928'
929
930# specify merge-base as parent of branch2
931# git merge-tree --write-tree --merge-base=c2 c1 c3
932# Commit c1: add file1
933# Commit c2: add file2 after c1
934# Commit c3: add file3 after c2
935# Expected: add file3, and file2 does NOT appear
936
937test_expect_success 'specify merge-base as parent of branch2' '
938 # Setup
939 test_when_finished "rm -rf base-b2-p" &&
940 git init base-b2-p &&
941 test_commit -C base-b2-p c1 file1 &&
942 test_commit -C base-b2-p c2 file2 &&
943 test_commit -C base-b2-p c3 file3 &&
944
945 # Testing
946 TREE_OID=$(git -C base-b2-p merge-tree --write-tree --merge-base=c2 c1 c3) &&
947
948 q_to_tab <<-EOF >expect &&
949 100644 blob $(git -C base-b2-p rev-parse c1:file1)Qfile1
950 100644 blob $(git -C base-b2-p rev-parse c3:file3)Qfile3
951 EOF
952
953 git -C base-b2-p ls-tree $TREE_OID >actual &&
954 test_cmp expect actual
955'
956
957# Since the earlier tests have verified that individual merge-tree calls
958# are doing the right thing, this test case is only used to verify that
959# we can also trigger merges via --stdin, and that when we do we get
960# the same answer as running a bunch of separate merges.
961
962test_expect_success 'check the input format when --stdin is passed' '
963 test_when_finished "rm -rf repo" &&
964 git init repo &&
965 test_commit -C repo c1 &&
966 test_commit -C repo c2 &&
967 test_commit -C repo c3 &&
968 printf "c1 c3\nc2 -- c1 c3\nc2 c3" | git -C repo merge-tree --stdin >actual &&
969
970 printf "1\0" >expect &&
971 git -C repo merge-tree --write-tree -z c1 c3 >>expect &&
972 printf "\0" >>expect &&
973
974 printf "1\0" >>expect &&
975 git -C repo merge-tree --write-tree -z --merge-base=c2 c1 c3 >>expect &&
976 printf "\0" >>expect &&
977
978 printf "1\0" >>expect &&
979 git -C repo merge-tree --write-tree -z c2 c3 >>expect &&
980 printf "\0" >>expect &&
981
982 test_cmp expect actual
983'
984
985test_expect_success '--merge-base with tree OIDs' '
986 git merge-tree --merge-base=side1^ side1 side3 >with-commits &&
987 git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
988 test_cmp with-commits with-trees
989'
990
991test_expect_success 'error out on missing tree objects' '
992 git init --bare missing-tree.git &&
993 git rev-list side3 >list &&
994 git rev-parse side3^: >>list &&
995 git pack-objects missing-tree.git/objects/pack/side3-tree-is-missing <list &&
996 side3=$(git rev-parse side3) &&
997 test_must_fail git --git-dir=missing-tree.git merge-tree $side3^ $side3 >actual 2>err &&
998 test_grep "Could not read $(git rev-parse $side3:)" err &&
999 test_must_be_empty actual
1000'
1001
1002test_expect_success 'error out on missing blob objects' '
1003 echo 1 | git hash-object -w --stdin >blob1 &&
1004 echo 2 | git hash-object -w --stdin >blob2 &&
1005 echo 3 | git hash-object -w --stdin >blob3 &&
1006 printf "100644 blob $(cat blob1)\tblob\n" | git mktree >tree1 &&
1007 printf "100644 blob $(cat blob2)\tblob\n" | git mktree >tree2 &&
1008 printf "100644 blob $(cat blob3)\tblob\n" | git mktree >tree3 &&
1009 git init --bare missing-blob.git &&
1010 cat blob1 blob3 tree1 tree2 tree3 |
1011 git pack-objects missing-blob.git/objects/pack/side1-whatever-is-missing &&
1012 test_must_fail git --git-dir=missing-blob.git >actual 2>err \
1013 merge-tree --merge-base=$(cat tree1) $(cat tree2) $(cat tree3) &&
1014 test_grep "unable to read blob object $(cat blob2)" err &&
1015 test_must_be_empty actual
1016'
1017
1018test_expect_success 'error out on missing commits as well' '
1019 git init --bare missing-commit.git &&
1020 git rev-list --objects side1 side3 >list-including-initial &&
1021 grep -v ^$(git rev-parse side1^) <list-including-initial >list &&
1022 git pack-objects missing-commit.git/objects/pack/missing-initial <list &&
1023 side1=$(git rev-parse side1) &&
1024 side3=$(git rev-parse side3) &&
1025 test_must_fail git --git-dir=missing-commit.git \
1026 merge-tree --allow-unrelated-histories $side1 $side3 >actual &&
1027 test_must_be_empty actual
1028'
1029
1030test_done