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,