Skip to content

Commit 8119214

Browse files
newrengitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 43e9c4e commit 8119214

File tree

2 files changed

+98
-2
lines changed

2 files changed

+98
-2
lines changed

merge-ort.c

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,7 +1356,6 @@ static inline void set_commit_tree(struct commit *c, struct tree *t)
13561356
c->maybe_tree = t;
13571357
}
13581358

1359-
MAYBE_UNUSED
13601359
static struct commit *make_virtual_commit(struct repository *repo,
13611360
struct tree *tree,
13621361
const char *comment)
@@ -1466,6 +1465,89 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt,
14661465
}
14671466
}
14681467

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+
14691551
void merge_incore_nonrecursive(struct merge_options *opt,
14701552
struct tree *merge_base,
14711553
struct tree *side1,
@@ -1483,5 +1565,9 @@ void merge_incore_recursive(struct merge_options *opt,
14831565
struct commit *side2,
14841566
struct merge_result *result)
14851567
{
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);
14871573
}

merge-ort.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ struct merge_result {
3434
/*
3535
* rename-detecting three-way merge with recursive ancestor consolidation.
3636
* 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/[email protected]/
45+
* [2] commit 8918b0c9c2 ("merge-recur: try to merge older merge bases
46+
* first", 2006-08-09)
3747
*/
3848
void merge_incore_recursive(struct merge_options *opt,
3949
struct commit_list *merge_bases,

0 commit comments

Comments
 (0)