merge-ort: implement merge_incore_recursive()

Implement merge_incore_recursive(), mostly through the use of a new helper function, merge_ort_internal(), which itself is based off merge_recursive_internal() from merge-recursive.c. This drops the number of failures in the testsuite when run under GIT_TEST_MERGE_ALGORITHM=ort from around 1500 to 647. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Dec 16, 2020 at 22:28 UTC 8119214f4e7036cef01a8e13f161ec510b3ff710
2 files changed +98 -2
merge-ort.c
+88 -2
@@ -1356,7 +1356,6 @@ static inline void set_commit_tree(struct commit *c, struct tree *t)
1356 c->maybe_tree = t;
1357 }
1358
1359 -MAYBE_UNUSED
1359 static struct commit *make_virtual_commit(struct repository *repo,
1360 struct tree *tree,
1361 const char *comment)
@@ -1466,6 +1465,89 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt,
1465 }
1466 }
1467
1468 +/*
1469 + * Originally from merge_recursive_internal(); somewhat adapted, though.
1470 + */
1471 +static void merge_ort_internal(struct merge_options *opt,
1472 + struct commit_list *merge_bases,
1473 + struct commit *h1,
1474 + struct commit *h2,
1475 + struct merge_result *result)
1476 +{
1477 + struct commit_list *iter;
1478 + struct commit *merged_merge_bases;
1479 + const char *ancestor_name;
1480 + struct strbuf merge_base_abbrev = STRBUF_INIT;
1481 +
1482 + if (!merge_bases) {
1483 + merge_bases = get_merge_bases(h1, h2);
1484 + /* See merge-ort.h:merge_incore_recursive() declaration NOTE */
1485 + merge_bases = reverse_commit_list(merge_bases);
1486 + }
1487 +
1488 + merged_merge_bases = pop_commit(&merge_bases);
1489 + if (merged_merge_bases == NULL) {
1490 + /* if there is no common ancestor, use an empty tree */
1491 + struct tree *tree;
1492 +
1493 + tree = lookup_tree(opt->repo, opt->repo->hash_algo->empty_tree);
1494 + merged_merge_bases = make_virtual_commit(opt->repo, tree,
1495 + "ancestor");
1496 + ancestor_name = "empty tree";
1497 + } else if (merge_bases) {
1498 + ancestor_name = "merged common ancestors";
1499 + } else {
1500 + strbuf_add_unique_abbrev(&merge_base_abbrev,
1501 + &merged_merge_bases->object.oid,
1502 + DEFAULT_ABBREV);
1503 + ancestor_name = merge_base_abbrev.buf;
1504 + }
1505 +
1506 + for (iter = merge_bases; iter; iter = iter->next) {
1507 + const char *saved_b1, *saved_b2;
1508 + struct commit *prev = merged_merge_bases;
1509 +
1510 + opt->priv->call_depth++;
1511 + /*
1512 + * When the merge fails, the result contains files
1513 + * with conflict markers. The cleanness flag is
1514 + * ignored (unless indicating an error), it was never
1515 + * actually used, as result of merge_trees has always
1516 + * overwritten it: the committed "conflicts" were
1517 + * already resolved.
1518 + */
1519 + saved_b1 = opt->branch1;
1520 + saved_b2 = opt->branch2;
1521 + opt->branch1 = "Temporary merge branch 1";
1522 + opt->branch2 = "Temporary merge branch 2";
1523 + merge_ort_internal(opt, NULL, prev, iter->item, result);
1524 + if (result->clean < 0)
1525 + return;
1526 + opt->branch1 = saved_b1;
1527 + opt->branch2 = saved_b2;
1528 + opt->priv->call_depth--;
1529 +
1530 + merged_merge_bases = make_virtual_commit(opt->repo,
1531 + result->tree,
1532 + "merged tree");
1533 + commit_list_insert(prev, &merged_merge_bases->parents);
1534 + commit_list_insert(iter->item,
1535 + &merged_merge_bases->parents->next);
1536 +
1537 + clear_or_reinit_internal_opts(opt->priv, 1);
1538 + }
1539 +
1540 + opt->ancestor = ancestor_name;
1541 + merge_ort_nonrecursive_internal(opt,
1542 + repo_get_commit_tree(opt->repo,
1543 + merged_merge_bases),
1544 + repo_get_commit_tree(opt->repo, h1),
1545 + repo_get_commit_tree(opt->repo, h2),
1546 + result);
1547 + strbuf_release(&merge_base_abbrev);
1548 + opt->ancestor = NULL; /* avoid accidental re-use of opt->ancestor */
1549 +}
1550 +
1551 void merge_incore_nonrecursive(struct merge_options *opt,
1552 struct tree *merge_base,
1553 struct tree *side1,
@@ -1483,5 +1565,9 @@ void merge_incore_recursive(struct merge_options *opt,
1565 struct commit *side2,
1566 struct merge_result *result)
1567 {
1486 - die("Not yet implemented");
1568 + /* We set the ancestor label based on the merge_bases */
1569 + assert(opt->ancestor == NULL);
1570 +
1571 + merge_start(opt, result);
1572 + merge_ort_internal(opt, merge_bases, side1, side2, result);
1573 }
merge-ort.h
+10
@@ -34,6 +34,16 @@ struct merge_result {
34 /*
35 * rename-detecting three-way merge with recursive ancestor consolidation.
36 * working tree and index are untouched.
37 + *
38 + * merge_bases will be consumed (emptied) so make a copy if you need it.
39 + *
40 + * NOTE: empirically, the recursive algorithm will perform better if you
41 + * pass the merge_bases in the order of oldest commit to the
42 + * newest[1][2].
43 + *
44 + * [1] https://lore.kernel.org/git/nycvar.QRO.7.76.6.1907252055500.21907@tvgsbejvaqbjf.bet/
45 + * [2] commit 8918b0c9c2 ("merge-recur: try to merge older merge bases
46 + * first", 2006-08-09)
47 */
48 void merge_incore_recursive(struct merge_options *opt,
49 struct commit_list *merge_bases,