merge-ort: defer recursing into directories when merge base is matched

When one side of history matches the merge base (including when the merge base has no entry for the given directory), have collect_merge_info_callback() defer recursing into the directory. To ensure those entries are eventually handled, add a call to handled_deferred_entries() in collect_merge_info() after traverse_trees() returns. Note that the condition in collect_merge_info_callback() may look more complicated than necessary at first glance; renames->trivial_merges_okay[side] is always true until handle_deferred_entries() is called, and possible_trivial_merges[side] is always empty right now (and in the future won't be filled until handle_deferred_entries() is called). However, when handle_deferred_entries() calls traverse_trees() for the relevant deferred directories, those traverse_trees() calls will once again end up in collect_merge_info_callback() for all the entries under those subdirectories. The extra conditions are there for such deferred cases and will be used more as we do more with those variables. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Jul 16, 2021 at 05:22 UTC 5e1ca57a7bbf19ac6c92a4e032ca05345dc622bd
1 file changed +31 -2
merge-ort.c
+31 -2
@@ -1147,8 +1147,36 @@ static int collect_merge_info_callback(int n,
1147 struct tree_desc t[3];
1148 void *buf[3] = {NULL, NULL, NULL};
1149 const char *original_dir_name;
1150 - int i, ret;
1150 + int i, ret, side;
1151
1152 + /*
1153 + * Check for whether we can avoid recursing due to one side
1154 + * matching the merge base. The side that does NOT match is
1155 + * the one that might have a rename destination we need.
1156 + */
1157 + assert(!side1_matches_mbase || !side2_matches_mbase);
1158 + side = side1_matches_mbase ? MERGE_SIDE2 :
1159 + side2_matches_mbase ? MERGE_SIDE1 : MERGE_BASE;
1160 + if (filemask == 0 && (dirmask == 2 || dirmask == 4)) {
1161 + /*
1162 + * Also defer recursing into new directories; set up a
1163 + * few variables to let us do so.
1164 + */
1165 + ci->match_mask = (7 - dirmask);
1166 + side = dirmask / 2;
1167 + }
1168 + if (renames->dir_rename_mask != 0x07 &&
1169 + side != MERGE_BASE &&
1170 + renames->deferred[side].trivial_merges_okay &&
1171 + !strset_contains(&renames->deferred[side].target_dirs,
1172 + pi.string)) {
1173 + strintmap_set(&renames->deferred[side].possible_trivial_merges,
1174 + pi.string, renames->dir_rename_mask);
1175 + renames->dir_rename_mask = prev_dir_rename_mask;
1176 + return mask;
1177 + }
1178 +
1179 + /* We need to recurse */
1180 ci->match_mask &= filemask;
1181 newinfo = *info;
1182 newinfo.prev = info;
@@ -1202,7 +1230,6 @@ static int collect_merge_info_callback(int n,
1230 return mask;
1231 }
1232
1205 -MAYBE_UNUSED
1233 static int handle_deferred_entries(struct merge_options *opt,
1234 struct traverse_info *info)
1235 {
@@ -1291,6 +1318,8 @@ static int collect_merge_info(struct merge_options *opt,
1318
1319 trace2_region_enter("merge", "traverse_trees", opt->repo);
1320 ret = traverse_trees(NULL, 3, t, &info);
1321 + if (ret == 0)
1322 + ret = handle_deferred_entries(opt, &info);
1323 trace2_region_leave("merge", "traverse_trees", opt->repo);
1324
1325 return ret;