@@ -1223,6 +1223,18 @@ static int collect_merge_info_callback(int n,
return mask;
}
+static void resolve_trivial_directory_merge(struct conflict_info *ci, int side)
+{
+ VERIFY_CI(ci);
+ assert((side == 1 && ci->match_mask == 5) ||
+ (side == 2 && ci->match_mask == 3));
+ oidcpy(&ci->merged.result.oid, &ci->stages[side].oid);
+ ci->merged.result.mode = ci->stages[side].mode;
+ ci->merged.is_null = is_null_oid(&ci->stages[side].oid);
+ ci->match_mask = 0;
+ ci->merged.clean = 1; /* (ci->filemask == 0); */
+}
+
static int handle_deferred_entries(struct merge_options *opt,
struct traverse_info *info)
{
@@ -1232,9 +1244,71 @@ static int handle_deferred_entries(struct merge_options *opt,
int side, ret = 0;
for (side = MERGE_SIDE1; side <= MERGE_SIDE2; side++) {
- renames->trivial_merges_okay[side] = 0;
- strintmap_for_each_entry(&renames->possible_trivial_merges[side],
- &iter, entry) {
+ unsigned optimization_okay = 1;
+ struct strintmap copy;
+
+ /* Loop over the set of paths we need to know rename info for */
+ strset_for_each_entry(&renames->relevant_sources[side],
+ &iter, entry) {
+ char *rename_target, *dir, *dir_marker;
+ struct strmap_entry *e;
+
+ /*
+ * if we don't know delete/rename info for this path,
+ * then we need to recurse into all trees to get all
+ * adds to make sure we have it.
+ */
+ if (strset_contains(&renames->cached_irrelevant[side],
+ entry->key))
+ continue;
+ e = strmap_get_entry(&renames->cached_pairs[side],
+ entry->key);
+ if (!e) {
+ optimization_okay = 0;
+ break;
+ }
+
+ /* If this is a delete, we have enough info already */
+ rename_target = e->value;
+ if (!rename_target)
+ continue;
+
+ /* If we already walked the rename target, we're good */
+ if (strmap_contains(&opt->priv->paths, rename_target))
+ continue;
+
+ /*
+ * Otherwise, we need to get a list of directories that
+ * will need to be recursed into to get this
+ * rename_target.
+ */
+ dir = xstrdup(rename_target);
+ while ((dir_marker = strrchr(dir, '/'))) {
+ *dir_marker = '\0';
+ if (strset_contains(&renames->target_dirs[side],
+ dir))
+ break;
+ strset_add(&renames->target_dirs[side], dir);
+ }
+ free(dir);
+ }
+ renames->trivial_merges_okay[side] = optimization_okay;
+ /*
+ * We need to recurse into any directories in
+ * possible_trivial_merges[side] found in target_dirs[side].
+ * But when we recurse, we may need to queue up some of the
+ * subdirectories for possible_trivial_merges[side]. Since
+ * we can't safely iterate through a hashmap while also adding
+ * entries, move the entries into 'copy', iterate over 'copy',
+ * and then we'll also iterate anything added into
+ * possible_trivial_merges[side] once this loop is done.
+ */
+ copy = renames->possible_trivial_merges[side];
+ strintmap_init_with_options(&renames->possible_trivial_merges[side],
+ 0,
+ NULL,
+ 0);
+ strintmap_for_each_entry(©, &iter, entry) {
const char *path = entry->key;
unsigned dir_rename_mask = (intptr_t)entry->value;
struct conflict_info *ci;
@@ -1247,6 +1321,13 @@ static int handle_deferred_entries(struct merge_options *opt,
VERIFY_CI(ci);
dirmask = ci->dirmask;
+ if (optimization_okay &&
+ !strset_contains(&renames->target_dirs[side],
+ path)) {
+ resolve_trivial_directory_merge(ci, side);
+ continue;
+ }
+
info->name = path;
info->namelen = strlen(path);
info->pathlen = info->namelen + 1;
@@ -1282,6 +1363,20 @@ static int handle_deferred_entries(struct merge_options *opt,
if (ret < 0)
return ret;
}
+ strintmap_clear(©);
+ strintmap_for_each_entry(&renames->possible_trivial_merges[side],
+ &iter, entry) {
+ const char *path = entry->key;
+ struct conflict_info *ci;
+
+ ci = strmap_get(&opt->priv->paths, path);
+ VERIFY_CI(ci);
+
+ assert(renames->trivial_merges_okay[side] &&
+ !strset_contains(&renames->target_dirs[side],
+ path));
+ resolve_trivial_directory_merge(ci, side);
+ }
}
return ret;
}