3 test_description
='git merge-tree --write-tree'
7 # This test is ort-specific
8 if test "$GIT_TEST_MERGE_ALGORITHM" != "ort"
10 skip_all
="GIT_TEST_MERGE_ALGORITHM != ort"
14 test_expect_success setup
'
15 test_write_lines 1 2 3 4 5 >numbers &&
16 echo hello >greeting &&
18 git add numbers greeting whatever &&
20 git commit -m initial &&
28 test_write_lines 1 2 3 4 5 6 >numbers &&
31 git add numbers greeting whatever &&
33 git commit -m modify-stuff &&
36 test_write_lines 0 1 2 3 4 5 >numbers &&
41 git add numbers greeting whatever/empty &&
43 git commit -m other-modifications &&
46 git mv numbers sequence &&
48 git commit -m rename-numbers &&
51 test_write_lines 0 1 2 3 4 5 >numbers &&
53 git add numbers greeting &&
55 git commit -m other-content-modifications &&
57 git switch --orphan unrelated &&
59 git add something-else &&
61 git commit -m first-commit
64 test_expect_success
'Clean merge' '
65 TREE_OID=$(git merge-tree --write-tree side1 side3) &&
66 q_to_tab <<-EOF >expect &&
67 100644 blob $(git rev-parse side1:greeting)Qgreeting
68 100644 blob $(git rev-parse side1:numbers)Qsequence
69 100644 blob $(git rev-parse side1:whatever)Qwhatever
72 git ls-tree $TREE_OID >actual &&
73 test_cmp expect actual
76 test_expect_success
'Content merge and a few conflicts' '
77 git checkout side1^0 &&
78 test_must_fail git merge side2 &&
79 expected_tree=$(git rev-parse AUTO_MERGE) &&
81 # We will redo the merge, while we are still in a conflicted state!
82 git ls-files -u >conflicted-file-info &&
83 test_when_finished "git reset --hard" &&
85 test_expect_code 1 git merge-tree --write-tree side1 side2 >RESULT &&
86 actual_tree=$(head -n 1 RESULT) &&
88 # Due to differences of e.g. "HEAD" vs "side1", the results will not
89 # exactly match. Dig into individual files.
91 # Numbers should have three-way merged cleanly
92 test_write_lines 0 1 2 3 4 5 6 >expect &&
93 git show ${actual_tree}:numbers >actual &&
94 test_cmp expect actual &&
96 # whatever and whatever~<branch> should have same HASHES
97 git rev-parse ${expected_tree}:whatever ${expected_tree}:whatever~HEAD >expect &&
98 git rev-parse ${actual_tree}:whatever ${actual_tree}:whatever~side1 >actual &&
99 test_cmp expect actual &&
101 # greeting should have a merge conflict
102 git show ${expected_tree}:greeting >tmp &&
103 sed -e s/HEAD/side1/ tmp >expect &&
104 git show ${actual_tree}:greeting >actual &&
105 test_cmp expect actual
108 test_expect_success
'Auto resolve conflicts by "ours" strategy option' '
109 git checkout side1^0 &&
111 # make sure merge conflict exists
112 test_must_fail git merge side4 &&
115 git merge -X ours side4 &&
116 git rev-parse HEAD^{tree} >expected &&
118 git merge-tree -X ours side1 side4 >actual &&
120 test_cmp expected actual
123 test_expect_success
'Barf on misspelled option, with exit code other than 0 or 1' '
124 # Mis-spell with single "s" instead of double "s"
125 test_expect_code 129 git merge-tree --write-tree --mesages FOOBAR side1 side2 2>expect &&
127 grep "error: unknown option.*mesages" expect
130 test_expect_success
'Barf on too many arguments' '
131 test_expect_code 129 git merge-tree --write-tree side1 side2 invalid 2>expect &&
133 grep "^usage: git merge-tree" expect
137 sed -e "s/[0-9a-f]\{40,\}/HASH/g" "$@"
140 test_expect_success
'test conflict notices and such' '
141 test_expect_code 1 git merge-tree --write-tree --name-only side1 side2 >out &&
142 anonymize_hash out >actual &&
145 # "greeting" should merge with conflicts
146 # "numbers" should merge cleanly
147 # "whatever" has *both* a modify/delete and a file/directory conflict
148 cat <<-EOF >expect &&
153 Auto-merging greeting
154 CONFLICT (content): Merge conflict in greeting
156 CONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
157 CONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1. Version side1 of whatever~side1 left in tree.
160 test_cmp expect actual
163 # directory rename + content conflict
164 # Commit O: foo, olddir/{a,b,c}
165 # Commit A: modify foo, newdir/{a,b,c}
166 # Commit B: modify foo differently & rename foo -> olddir/bar
167 # Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
169 test_expect_success
'directory rename + content conflict' '
171 git init dir-rename-and-content &&
173 cd dir-rename-and-content &&
174 test_write_lines 1 2 3 4 5 >foo &&
176 for i in a b c; do echo $i >olddir/$i || exit 1; done &&
177 git add foo olddir &&
178 git commit -m "original" &&
185 test_write_lines 1 2 3 4 5 6 >foo &&
187 git mv olddir newdir &&
188 git commit -m "Modify foo, rename olddir to newdir" &&
191 test_write_lines 1 2 3 4 5 six >foo &&
193 git mv foo olddir/bar &&
194 git commit -m "Modify foo & rename foo -> olddir/bar"
198 cd dir-rename-and-content &&
201 git merge-tree -z A^0 B^0 >out &&
203 anonymize_hash out >actual &&
204 q_to_tab <<-\EOF | lf_to_nul >expect &&
206 100644 HASH 1Qnewdir/bar
207 100644 HASH 2Qnewdir/bar
208 100644 HASH 3Qnewdir/bar
211 q_to_nul <<-EOF >>expect &&
212 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.
213 Q1Qnewdir/barQAuto-mergingQAuto-merging newdir/bar
214 Q1Qnewdir/barQCONFLICT (contents)QCONFLICT (content): Merge conflict in newdir/bar
217 test_cmp expect actual
221 # rename/delete + modify/delete handling
223 # Commit A: modify foo + rename to bar
224 # Commit B: delete foo
225 # Expected: CONFLICT(rename/delete) + CONFLICT(modify/delete)
227 test_expect_success
'rename/delete handling' '
229 git init rename-delete &&
232 test_write_lines 1 2 3 4 5 >foo &&
234 git commit -m "original" &&
241 test_write_lines 1 2 3 4 5 6 >foo &&
244 git commit -m "Modify foo, rename to bar" &&
248 git commit -m "remove foo"
255 git merge-tree -z A^0 B^0 >out &&
257 anonymize_hash out >actual &&
258 q_to_tab <<-\EOF | lf_to_nul >expect &&
264 q_to_nul <<-EOF >>expect &&
265 Q2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in A^0, but deleted in B^0.
266 Q1QbarQCONFLICT (modify/delete)QCONFLICT (modify/delete): bar deleted in B^0 and modified in A^0. Version A^0 of bar left in tree.
269 test_cmp expect actual
273 # rename/add handling
275 # Commit A: modify foo, add different bar
276 # Commit B: modify & rename foo->bar
277 # Expected: CONFLICT(add/add) [via rename collide] for bar
279 test_expect_success
'rename/add handling' '
281 git init rename-add &&
284 test_write_lines original 1 2 3 4 5 >foo &&
286 git commit -m "original" &&
293 test_write_lines 1 2 3 4 5 >foo &&
294 echo "different file" >bar &&
296 git commit -m "Modify foo, add bar" &&
299 test_write_lines original 1 2 3 4 5 6 >foo &&
302 git commit -m "rename foo to bar"
309 git merge-tree -z A^0 B^0 >out &&
313 # First, check that the bar that appears at stage 3 does not
314 # correspond to an individual blob anywhere in history
316 hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
317 git rev-list --objects --all >all_blobs &&
318 ! grep $hash all_blobs &&
321 # Second, check anonymized hash output against expectation
323 anonymize_hash out >actual &&
324 q_to_tab <<-\EOF | lf_to_nul >expect &&
330 q_to_nul <<-EOF >>expect &&
331 Q1QbarQAuto-mergingQAuto-merging bar
332 Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
333 Q1QfooQAuto-mergingQAuto-merging foo
336 test_cmp expect actual
340 # rename/add, where add is a mode conflict
342 # Commit A: modify foo, add symlink bar
343 # Commit B: modify & rename foo->bar
344 # Expected: CONFLICT(distinct modes) for bar
346 test_expect_success SYMLINKS
'rename/add, where add is a mode conflict' '
348 git init rename-add-symlink &&
350 cd rename-add-symlink &&
351 test_write_lines original 1 2 3 4 5 >foo &&
353 git commit -m "original" &&
360 test_write_lines 1 2 3 4 5 >foo &&
363 git commit -m "Modify foo, add symlink bar" &&
366 test_write_lines original 1 2 3 4 5 6 >foo &&
369 git commit -m "rename foo to bar"
373 cd rename-add-symlink &&
376 git merge-tree -z A^0 B^0 >out &&
380 # First, check that the bar that appears at stage 3 does not
381 # correspond to an individual blob anywhere in history
383 hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
384 git rev-list --objects --all >all_blobs &&
385 ! grep $hash all_blobs &&
388 # Second, check anonymized hash output against expectation
390 anonymize_hash out >actual &&
391 q_to_tab <<-\EOF | lf_to_nul >expect &&
394 100644 HASH 3Qbar~B^0
397 q_to_nul <<-EOF >>expect &&
398 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.
399 Q1QfooQAuto-mergingQAuto-merging foo
402 test_cmp expect actual
406 # rename/rename(1to2) + content conflict handling
408 # Commit A: modify foo & rename to bar
409 # Commit B: modify foo & rename to baz
410 # Expected: CONFLICT(rename/rename)
412 test_expect_success
'rename/rename + content conflict' '
414 git init rr-plus-content &&
416 cd rr-plus-content &&
417 test_write_lines 1 2 3 4 5 >foo &&
419 git commit -m "original" &&
426 test_write_lines 1 2 3 4 5 six >foo &&
429 git commit -m "Modify foo + rename to bar" &&
432 test_write_lines 1 2 3 4 5 6 >foo &&
435 git commit -m "Modify foo + rename to baz"
439 cd rr-plus-content &&
442 git merge-tree -z A^0 B^0 >out &&
444 anonymize_hash out >actual &&
445 q_to_tab <<-\EOF | lf_to_nul >expect &&
452 q_to_nul <<-EOF >>expect &&
453 Q1QfooQAuto-mergingQAuto-merging foo
454 Q3QfooQbarQbazQCONFLICT (rename/rename)QCONFLICT (rename/rename): foo renamed to bar in A^0 and to baz in B^0.
457 test_cmp expect actual
463 # Commit A: rm foo, add different bar
464 # Commit B: rename foo->bar
465 # Expected: CONFLICT (rename/delete), CONFLICT(add/add) [via rename collide]
468 test_expect_success
'rename/add/delete conflict' '
473 echo "original file" >foo &&
475 git commit -m "original" &&
483 echo "different file" >bar &&
485 git commit -m "Remove foo, add bar" &&
489 git commit -m "rename foo to bar"
496 git merge-tree -z B^0 A^0 >out &&
498 anonymize_hash out >actual &&
500 q_to_tab <<-\EOF | lf_to_nul >expect &&
507 q_to_nul <<-EOF >>expect &&
508 2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in B^0, but deleted in A^0.
509 Q1QbarQAuto-mergingQAuto-merging bar
510 Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
513 test_cmp expect actual
517 # rename/rename(2to1)/delete/delete
519 # Commit A: rename foo->baz, rm bar
520 # Commit B: rename bar->baz, rm foo
521 # Expected: 2x CONFLICT (rename/delete), CONFLICT (add/add) via colliding
524 test_expect_success
'rename/rename(2to1)/delete/delete conflict' '
541 git commit -m "Rename foo, remove bar" &&
546 git commit -m "Rename bar, remove foo"
553 git merge-tree -z A^0 B^0 >out &&
555 anonymize_hash out >actual &&
557 q_to_tab <<-\EOF | lf_to_nul >expect &&
564 q_to_nul <<-EOF >>expect &&
565 2QbazQbarQCONFLICT (rename/delete)QCONFLICT (rename/delete): bar renamed to baz in B^0, but deleted in A^0.
566 Q2QbazQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to baz in A^0, but deleted in B^0.
567 Q1QbazQAuto-mergingQAuto-merging baz
568 Q1QbazQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in baz
571 test_cmp expect actual
575 # mod6: chains of rename/rename(1to2) + add/add via colliding renames
576 # Commit O: one, three, five
577 # Commit A: one->two, three->four, five->six
578 # Commit B: one->six, three->two, five->four
579 # Expected: three CONFLICT(rename/rename) messages + three CONFLICT(add/add)
580 # messages; each path in two of the multi-way merged contents
581 # found in two, four, six
583 test_expect_success
'mod6: chains of rename/rename(1to2) and add/add via colliding renames' '
588 test_seq 11 19 >one &&
589 test_seq 31 39 >three &&
590 test_seq 51 59 >five &&
600 test_seq 10 19 >one &&
611 echo forty >>three &&
613 git add one three five &&
625 git merge-tree -z A^0 B^0 >out &&
629 # First, check that some of the hashes that appear as stage
630 # conflict entries do not appear as individual blobs anywhere
633 hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") &&
634 hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") &&
635 git rev-list --objects --all >all_blobs &&
636 ! grep $hash1 all_blobs &&
637 ! grep $hash2 all_blobs &&
640 # Now compare anonymized hash output with expectation
642 anonymize_hash out >actual &&
643 q_to_tab <<-\EOF | lf_to_nul >expect &&
657 q_to_nul <<-EOF >>expect &&
658 3QfiveQsixQfourQCONFLICT (rename/rename)QCONFLICT (rename/rename): five renamed to six in A^0 and to four in B^0.
659 Q1QfourQAuto-mergingQAuto-merging four
660 Q1QfourQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in four
661 Q1QoneQAuto-mergingQAuto-merging one
662 Q3QoneQtwoQsixQCONFLICT (rename/rename)QCONFLICT (rename/rename): one renamed to two in A^0 and to six in B^0.
663 Q1QsixQAuto-mergingQAuto-merging six
664 Q1QsixQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in six
665 Q1QthreeQAuto-mergingQAuto-merging three
666 Q3QthreeQfourQtwoQCONFLICT (rename/rename)QCONFLICT (rename/rename): three renamed to four in A^0 and to two in B^0.
667 Q1QtwoQAuto-mergingQAuto-merging two
668 Q1QtwoQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in two
671 test_cmp expect actual
675 # directory rename + rename/delete + modify/delete + directory/file conflict
676 # Commit O: foo, olddir/{a,b,c}
677 # Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file
678 # Commit B: modify foo & rename foo -> olddir/bar
679 # Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
681 test_expect_success
'directory rename + rename/delete + modify/delete + directory/file conflict' '
683 git init 4-stacked-conflict &&
685 cd 4-stacked-conflict &&
686 test_write_lines 1 2 3 4 5 >foo &&
688 for i in a b c; do echo $i >olddir/$i || exit 1; done &&
689 git add foo olddir &&
690 git commit -m "original" &&
698 git mv olddir newdir &&
701 git add newdir/bar/file &&
702 git commit -m "rm foo, olddir/ -> newdir/, + newdir/bar/file" &&
705 test_write_lines 1 2 3 4 5 6 >foo &&
707 git mv foo olddir/bar &&
708 git commit -m "Modify foo & rename foo -> olddir/bar"
712 cd 4-stacked-conflict &&
715 git merge-tree -z A^0 B^0 >out &&
717 anonymize_hash out >actual &&
719 q_to_tab <<-\EOF | lf_to_nul >expect &&
721 100644 HASH 1Qnewdir/bar~B^0
722 100644 HASH 3Qnewdir/bar~B^0
725 q_to_nul <<-EOF >>expect &&
726 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.
727 Q2Qnewdir/barQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to newdir/bar in B^0, but deleted in A^0.
728 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.
729 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.
732 test_cmp expect actual
736 for opt
in $
(git merge-tree
--git-completion-helper-all)
738 if test $opt = "--trivial-merge" ||
test $opt = "--write-tree"
743 test_expect_success
"usage: --trivial-merge is incompatible with $opt" '
744 test_expect_code 128 git merge-tree --trivial-merge $opt side1 side2 side3
748 test_expect_success
'Just the conflicted files without the messages' '
749 test_expect_code 1 git merge-tree --write-tree --no-messages --name-only side1 side2 >out &&
750 anonymize_hash out >actual &&
752 test_write_lines HASH greeting whatever~side1 >expect &&
754 test_cmp expect actual
757 test_expect_success
'Check conflicted oids and modes without messages' '
758 test_expect_code 1 git merge-tree --write-tree --no-messages side1 side2 >out &&
759 anonymize_hash out >actual &&
761 # Compare the basic output format
762 q_to_tab >expect <<-\EOF &&
764 100644 HASH 1Qgreeting
765 100644 HASH 2Qgreeting
766 100644 HASH 3Qgreeting
767 100644 HASH 1Qwhatever~side1
768 100644 HASH 2Qwhatever~side1
771 test_cmp expect actual &&
773 # Check the actual hashes against the `ls-files -u` output too
774 tail -n +2 out | sed -e s/side1/HEAD/ >actual &&
775 test_cmp conflicted-file-info actual
778 test_expect_success
'NUL terminated conflicted file "lines"' '
779 git checkout -b tweak1 side1 &&
780 test_write_lines zero 1 2 3 4 5 6 >numbers &&
782 git mv numbers "Αυτά μου φαίνονται κινέζικα" &&
783 git commit -m "Renamed numbers" &&
785 test_expect_code 1 git merge-tree --write-tree -z tweak1 side2 >out &&
787 anonymize_hash out >actual &&
790 # "greeting" should merge with conflicts
791 # "whatever" has *both* a modify/delete and a file/directory conflict
792 # "Αυτά μου φαίνονται κινέζικα" should have a conflict
793 echo HASH | lf_to_nul >expect &&
795 q_to_tab <<-EOF | lf_to_nul >>expect &&
796 100644 HASH 1Qgreeting
797 100644 HASH 2Qgreeting
798 100644 HASH 3Qgreeting
799 100644 HASH 1Qwhatever~tweak1
800 100644 HASH 2Qwhatever~tweak1
801 100644 HASH 1QΑυτά μου φαίνονται κινέζικα
802 100644 HASH 2QΑυτά μου φαίνονται κινέζικα
803 100644 HASH 3QΑυτά μου φαίνονται κινέζικα
807 q_to_nul <<-EOF >>expect &&
808 1QgreetingQAuto-mergingQAuto-merging greeting
809 Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
810 Q2Qwhatever~tweak1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from tweak1; moving it to whatever~tweak1 instead.
811 Q1Qwhatever~tweak1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~tweak1 deleted in side2 and modified in tweak1. Version tweak1 of whatever~tweak1 left in tree.
812 Q1QΑυτά μου φαίνονται κινέζικαQAuto-mergingQAuto-merging Αυτά μου φαίνονται κινέζικα
813 Q1QΑυτά μου φαίνονται κινέζικαQCONFLICT (contents)QCONFLICT (content): Merge conflict in Αυτά μου φαίνονται κινέζικα
817 test_cmp expect actual
820 test_expect_success
'error out by default for unrelated histories' '
821 test_expect_code 128 git merge-tree --write-tree side1 unrelated 2>error &&
823 grep "refusing to merge unrelated histories" error
826 test_expect_success
'can override merge of unrelated histories' '
827 git merge-tree --write-tree --allow-unrelated-histories side1 unrelated >tree &&
830 git rev-parse side1:numbers side1:greeting side1:whatever unrelated:something-else >expect &&
831 git rev-parse $TREE:numbers $TREE:greeting $TREE:whatever $TREE:something-else >actual &&
833 test_cmp expect actual
836 test_expect_success SANITY
'merge-ort fails gracefully in a read-only repository' '
837 git init --bare read-only &&
838 git push read-only side1 side2 side3 &&
839 test_when_finished "chmod -R u+w read-only" &&
840 chmod -R a-w read-only &&
841 test_must_fail git -C read-only merge-tree side1 side3 &&
842 test_must_fail git -C read-only merge-tree side1 side2
845 test_expect_success
'--stdin with both a successful and a conflicted merge' '
846 printf "side1 side3\nside1 side2" | git merge-tree --stdin >actual &&
848 git checkout side1^0 &&
851 printf "1\0" >expect &&
852 git rev-parse HEAD^{tree} | lf_to_nul >>expect &&
853 printf "\0" >>expect &&
855 git checkout side1^0 &&
856 test_must_fail git merge side2 &&
857 sed s/HEAD/side1/ greeting >tmp &&
860 git mv whatever~HEAD whatever~side1 &&
862 printf "0\0" >>expect &&
863 git write-tree | lf_to_nul >>expect &&
865 cat <<-EOF | q_to_tab | lf_to_nul >>expect &&
866 100644 $(git rev-parse side1~1:greeting) 1Qgreeting
867 100644 $(git rev-parse side1:greeting) 2Qgreeting
868 100644 $(git rev-parse side2:greeting) 3Qgreeting
869 100644 $(git rev-parse side1~1:whatever) 1Qwhatever~side1
870 100644 $(git rev-parse side1:whatever) 2Qwhatever~side1
873 q_to_nul <<-EOF >>expect &&
874 Q1QgreetingQAuto-mergingQAuto-merging greeting
875 Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
876 Q1QnumbersQAuto-mergingQAuto-merging numbers
877 Q2Qwhatever~side1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
878 Q1Qwhatever~side1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1. Version side1 of whatever~side1 left in tree.
881 printf "\0\0" >>expect &&
883 test_cmp expect actual
887 test_expect_success
'--merge-base is incompatible with --stdin' '
888 test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
890 grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect
893 # specify merge-base as parent of branch2
894 # git merge-tree --write-tree --merge-base=c2 c1 c3
895 # Commit c1: add file1
896 # Commit c2: add file2 after c1
897 # Commit c3: add file3 after c2
898 # Expected: add file3, and file2 does NOT appear
900 test_expect_success
'specify merge-base as parent of branch2' '
902 test_when_finished "rm -rf base-b2-p" &&
903 git init base-b2-p &&
904 test_commit -C base-b2-p c1 file1 &&
905 test_commit -C base-b2-p c2 file2 &&
906 test_commit -C base-b2-p c3 file3 &&
909 TREE_OID=$(git -C base-b2-p merge-tree --write-tree --merge-base=c2 c1 c3) &&
911 q_to_tab <<-EOF >expect &&
912 100644 blob $(git -C base-b2-p rev-parse c1:file1)Qfile1
913 100644 blob $(git -C base-b2-p rev-parse c3:file3)Qfile3
916 git -C base-b2-p ls-tree $TREE_OID >actual &&
917 test_cmp expect actual
920 # Since the earlier tests have verified that individual merge-tree calls
921 # are doing the right thing, this test case is only used to verify that
922 # we can also trigger merges via --stdin, and that when we do we get
923 # the same answer as running a bunch of separate merges.
925 test_expect_success
'check the input format when --stdin is passed' '
926 test_when_finished "rm -rf repo" &&
928 test_commit -C repo c1 &&
929 test_commit -C repo c2 &&
930 test_commit -C repo c3 &&
931 printf "c1 c3\nc2 -- c1 c3\nc2 c3" | git -C repo merge-tree --stdin >actual &&
933 printf "1\0" >expect &&
934 git -C repo merge-tree --write-tree -z c1 c3 >>expect &&
935 printf "\0" >>expect &&
937 printf "1\0" >>expect &&
938 git -C repo merge-tree --write-tree -z --merge-base=c2 c1 c3 >>expect &&
939 printf "\0" >>expect &&
941 printf "1\0" >>expect &&
942 git -C repo merge-tree --write-tree -z c2 c3 >>expect &&
943 printf "\0" >>expect &&
945 test_cmp expect actual
948 test_expect_success
'--merge-base with tree OIDs' '
949 git merge-tree --merge-base=side1^ side1 side3 >with-commits &&
950 git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
951 test_cmp with-commits with-trees
954 test_expect_success
'error out on missing tree objects' '
955 git init --bare missing-tree.git &&
956 git rev-list side3 >list &&
957 git rev-parse side3^: >>list &&
958 git pack-objects missing-tree.git/objects/pack/side3-tree-is-missing <list &&
959 side3=$(git rev-parse side3) &&
960 test_must_fail git --git-dir=missing-tree.git merge-tree $side3^ $side3 >actual 2>err &&
961 test_grep "Could not read $(git rev-parse $side3:)" err &&
962 test_must_be_empty actual
965 test_expect_success
'error out on missing blob objects' '
966 echo 1 | git hash-object -w --stdin >blob1 &&
967 echo 2 | git hash-object -w --stdin >blob2 &&
968 echo 3 | git hash-object -w --stdin >blob3 &&
969 printf "100644 blob $(cat blob1)\tblob\n" | git mktree >tree1 &&
970 printf "100644 blob $(cat blob2)\tblob\n" | git mktree >tree2 &&
971 printf "100644 blob $(cat blob3)\tblob\n" | git mktree >tree3 &&
972 git init --bare missing-blob.git &&
973 cat blob1 blob3 tree1 tree2 tree3 |
974 git pack-objects missing-blob.git/objects/pack/side1-whatever-is-missing &&
975 test_must_fail git --git-dir=missing-blob.git >actual 2>err \
976 merge-tree --merge-base=$(cat tree1) $(cat tree2) $(cat tree3) &&
977 test_grep "unable to read blob object $(cat blob2)" err &&
978 test_must_be_empty actual
981 test_expect_success
'error out on missing commits as well' '
982 git init --bare missing-commit.git &&
983 git rev-list --objects side1 side3 >list-including-initial &&
984 grep -v ^$(git rev-parse side1^) <list-including-initial >list &&
985 git pack-objects missing-commit.git/objects/pack/missing-initial <list &&
986 side1=$(git rev-parse side1) &&
987 side3=$(git rev-parse side3) &&
988 test_must_fail git --git-dir=missing-commit.git \
989 merge-tree --allow-unrelated-histories $side1 $side3 >actual &&
990 test_must_be_empty actual