merge-ort: avoid repeating fill_tree_descriptor() on the same tree

Three-way merges, by their nature, are going to often have two or more trees match at a given subdirectory. We can avoid calling fill_tree_descriptor() on the same tree by checking when these trees match. Noting when various oids match will also be useful in other calculations and optimizations as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Dec 13, 2020 at 08:04 UTC 885f0063e9962068d381ca5256ca49070bb2480c
1 file changed +22 -4
merge-ort.c
+22 -4
@@ -223,6 +223,15 @@ static int collect_merge_info_callback(int n,
223 unsigned mbase_null = !(mask & 1);
224 unsigned side1_null = !(mask & 2);
225 unsigned side2_null = !(mask & 4);
226 + unsigned side1_matches_mbase = (!side1_null && !mbase_null &&
227 + names[0].mode == names[1].mode &&
228 + oideq(&names[0].oid, &names[1].oid));
229 + unsigned side2_matches_mbase = (!side2_null && !mbase_null &&
230 + names[0].mode == names[2].mode &&
231 + oideq(&names[0].oid, &names[2].oid));
232 + unsigned sides_match = (!side1_null && !side2_null &&
233 + names[1].mode == names[2].mode &&
234 + oideq(&names[1].oid, &names[2].oid));
235
236 /* n = 3 is a fundamental assumption. */
237 if (n != 3)
@@ -275,10 +284,19 @@ static int collect_merge_info_callback(int n,
284 newinfo.pathlen = st_add3(newinfo.pathlen, p->pathlen, 1);
285
286 for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
278 - const struct object_id *oid = NULL;
279 - if (dirmask & 1)
280 - oid = &names[i].oid;
281 - buf[i] = fill_tree_descriptor(opt->repo, t + i, oid);
287 + if (i == 1 && side1_matches_mbase)
288 + t[1] = t[0];
289 + else if (i == 2 && side2_matches_mbase)
290 + t[2] = t[0];
291 + else if (i == 2 && sides_match)
292 + t[2] = t[1];
293 + else {
294 + const struct object_id *oid = NULL;
295 + if (dirmask & 1)
296 + oid = &names[i].oid;
297 + buf[i] = fill_tree_descriptor(opt->repo,
298 + t + i, oid);
299 + }
300 dirmask >>= 1;
301 }
302