builtin/merge-recursive: switch to using merge_ort_generic()

Switch from merge-recursive to merge-ort. Adjust the following testcases due to the switch: * t6430: most of the test differences here were due to improved D/F conflict handling explained in more detail in ef527787089c (merge tests: expect improved directory/file conflict handling in ort, 2020-10-26). These changes weren't made to this test back in that commit simply because I had been looking at `git merge` rather than `git merge-recursive`. The final test in this testsuite, though, was expunged because it was looking for specific output, and the calls to output_commit_title() were discarded from merge_ort_internal() in its adaptation from merge_recursive_internal(); see 8119214f4e70 (merge-ort: implement merge_incore_recursive(), 2020-12-16). * t6434: This test is built entirely around rename/delete conflicts, which had a suboptimal handling under merge-recursive. As explained in more detail in commits 1f3c9ba707 ("t6425: be more flexible with rename/delete conflict messages", 2020-08-10) and 727c75b23f ("t6404, t6423: expect improved rename/delete handling in ort backend", 2020-10-26), rename/delete conflicts should each have two entries in the index rather than just one. Adjust the expectations for all the tests in this testcase to see the two entries per rename/delete conflict. * t6424: merge-recursive had a special check-if-toplevel-trees-match check that it ran at the beginning on both the merge-base and the other side being merged in. In such a case, it exited early and printed an "Already up to date." message. merge-ort got rid of this, and instead checks the merge base tree matching the other side throughout the tree instead of just at the toplevel, allowing it to avoid recursing into various subtrees. As part of that, it got rid of the specialty toplevel message. That message hasn't been missed for years from `git merge`, so I don't think it is necessary to keep it just for `git merge-recursive`, especially since the latter is rarely used. (git itself only references it in the testsuite, whereas it used to power one of the three rebase backends that existed once upon a time.) Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Apr 8, 2025 at 15:48 UTC 77c029493a671d9ee200bda42da0a3c04cdf110f
4 files changed +22 -45
builtin/merge-recursive.c
+2 -2
@@ -3,7 +3,7 @@
3 #include "advice.h"
4 #include "gettext.h"
5 #include "hash.h"
6 -#include "merge-recursive.h"
6 +#include "merge-ort-wrappers.h"
7 #include "object-name.h"
8
9 static const char builtin_merge_recursive_usage[] =
@@ -89,7 +89,7 @@ int cmd_merge_recursive(int argc,
89 if (o.verbosity >= 3)
90 printf(_("Merging %s with %s\n"), o.branch1, o.branch2);
91
92 - failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
92 + failed = merge_ort_generic(&o, &h1, &h2, bases_count, bases, &result);
93
94 free(better1);
95 free(better2);
t/t6424-merge-unrelated-index-changes.sh
+3 -1
@@ -176,9 +176,11 @@ test_expect_success 'merge-recursive, when index==head but head!=HEAD' '
176 # Make index match B
177 git diff C B -- | git apply --cached &&
178 test_when_finished "git clean -fd" && # Do not leave untracked around
179 + git write-tree >index-before &&
180 # Merge B & F, with B as "head"
181 git merge-recursive A -- B F > out &&
181 - test_grep "Already up to date" out
182 + git write-tree >index-after &&
183 + test_cmp index-before index-after
184 '
185
186 test_expect_success 'recursive, when file has staged changes not matching HEAD nor what a merge would give' '
t/t6430-merge-recursive.sh
+8 -35
@@ -373,9 +373,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
373
374 git ls-files -s >actual &&
375 (
376 - echo "100644 $o0 1 a" &&
377 - echo "100644 $o1 2 a" &&
376 echo "100644 $o4 0 a/c" &&
377 + echo "100644 $o0 1 a~$c1" &&
378 + echo "100644 $o1 2 a~$c1" &&
379 echo "100644 $o0 0 b" &&
380 echo "100644 $o0 0 c" &&
381 echo "100644 $o1 0 d/e"
@@ -397,9 +397,9 @@ test_expect_success 'merge-recursive d/f conflict result the other way' '
397
398 git ls-files -s >actual &&
399 (
400 - echo "100644 $o0 1 a" &&
401 - echo "100644 $o1 3 a" &&
400 echo "100644 $o4 0 a/c" &&
401 + echo "100644 $o0 1 a~$c1" &&
402 + echo "100644 $o1 3 a~$c1" &&
403 echo "100644 $o0 0 b" &&
404 echo "100644 $o0 0 c" &&
405 echo "100644 $o1 0 d/e"
@@ -424,9 +424,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
424 echo "100644 $o1 0 a" &&
425 echo "100644 $o0 0 b" &&
426 echo "100644 $o0 0 c" &&
427 - echo "100644 $o6 3 d" &&
427 echo "100644 $o0 1 d/e" &&
429 - echo "100644 $o1 2 d/e"
428 + echo "100644 $o1 2 d/e" &&
429 + echo "100644 $o6 3 d~$c6"
430 ) >expected &&
431 test_cmp expected actual
432
@@ -448,9 +448,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
448 echo "100644 $o1 0 a" &&
449 echo "100644 $o0 0 b" &&
450 echo "100644 $o0 0 c" &&
451 - echo "100644 $o6 2 d" &&
451 echo "100644 $o0 1 d/e" &&
453 - echo "100644 $o1 3 d/e"
452 + echo "100644 $o1 3 d/e" &&
453 + echo "100644 $o6 2 d~$c6"
454 ) >expected &&
455 test_cmp expected actual
456
@@ -696,33 +696,6 @@ test_expect_success 'merging with triple rename across D/F conflict' '
696 git merge other
697 '
698
699 -test_expect_success 'merge-recursive remembers the names of all base trees' '
700 - git reset --hard HEAD &&
701 -
702 - # make the index match $c1 so that merge-recursive below does not
703 - # fail early
704 - git diff --binary HEAD $c1 -- | git apply --cached &&
705 -
706 - # more trees than static slots used by oid_to_hex()
707 - for commit in $c0 $c2 $c4 $c5 $c6 $c7
708 - do
709 - git rev-parse "$commit^{tree}" || return 1
710 - done >trees &&
711 -
712 - # ignore the return code; it only fails because the input is weird...
713 - test_must_fail git -c merge.verbosity=5 merge-recursive $(cat trees) -- $c1 $c3 >out &&
714 -
715 - # ...but make sure it fails in the expected way
716 - test_grep CONFLICT.*rename/rename out &&
717 -
718 - # merge-recursive prints in reverse order, but we do not care
719 - sort <trees >expect &&
720 - sed -n "s/^virtual //p" out | sort >actual &&
721 - test_cmp expect actual &&
722 -
723 - git clean -fd
724 -'
725 -
699 test_expect_success 'merge-recursive internal merge resolves to the sameness' '
700 git reset --hard HEAD &&
701
t/t6434-merge-recursive-rename-options.sh
+9 -7
@@ -34,7 +34,9 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
34 get_expected_stages () {
35 git checkout rename -- $1-new &&
36 git ls-files --stage $1-new >expected-stages-undetected-$1 &&
37 - sed "s/ 0 / 2 /" <expected-stages-undetected-$1 \
37 + git ls-tree HEAD^ $1-old >tmp &&
38 + git ls-tree HEAD $1-new >>tmp &&
39 + cat tmp | awk '{print $1 " " $3 " " NR "\t" '$1'"-new"}' \
40 >expected-stages-detected-$1 &&
41 git read-tree -u --reset HEAD
42 }
@@ -51,11 +53,11 @@ rename_undetected () {
53
54 check_common () {
55 git ls-files --stage >stages-actual &&
54 - test_line_count = 4 stages-actual
56 + test_line_count = $1 stages-actual
57 }
58
59 check_threshold_0 () {
58 - check_common &&
60 + check_common 8 &&
61 rename_detected 0 &&
62 rename_detected 1 &&
63 rename_detected 2 &&
@@ -63,7 +65,7 @@ check_threshold_0 () {
65 }
66
67 check_threshold_1 () {
66 - check_common &&
68 + check_common 7 &&
69 rename_undetected 0 &&
70 rename_detected 1 &&
71 rename_detected 2 &&
@@ -71,7 +73,7 @@ check_threshold_1 () {
73 }
74
75 check_threshold_2 () {
74 - check_common &&
76 + check_common 6 &&
77 rename_undetected 0 &&
78 rename_undetected 1 &&
79 rename_detected 2 &&
@@ -79,7 +81,7 @@ check_threshold_2 () {
81 }
82
83 check_exact_renames () {
82 - check_common &&
84 + check_common 5 &&
85 rename_undetected 0 &&
86 rename_undetected 1 &&
87 rename_undetected 2 &&
@@ -87,7 +89,7 @@ check_exact_renames () {
89 }
90
91 check_no_renames () {
90 - check_common &&
92 + check_common 4 &&
93 rename_undetected 0 &&
94 rename_undetected 1 &&
95 rename_undetected 2 &&