diff options
Diffstat (limited to 'merge-ort.c')
| -rw-r--r-- | merge-ort.c | 120 |
1 files changed, 86 insertions, 34 deletions
diff --git a/merge-ort.c b/merge-ort.c index 47b3d1730e..9e85a5e60a 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -39,7 +39,7 @@ #include "mem-pool.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "oid-array.h" #include "path.h" #include "promisor-remote.h" @@ -316,9 +316,14 @@ struct merge_options_internal { * (e.g. "drivers/firmware/raspberrypi.c"). * * store all relevant paths in the repo, both directories and * files (e.g. drivers, drivers/firmware would also be included) - * * these keys serve to intern all the path strings, which allows - * us to do pointer comparison on directory names instead of - * strcmp; we just have to be careful to use the interned strings. + * * these keys serve to intern *all* path strings, which allows us + * to do pointer comparisons on file & directory names instead of + * using strcmp; however, for this pointer-comparison optimization + * to work, any code path that independently computes a path needs + * to check for it existing in this strmap, and if so, point to + * the path in this strmap instead of their computed copy. See + * the "reuse known pointer" comment in + * apply_directory_rename_modifications() for an example. * * The values of paths: * * either a pointer to a merged_info, or a conflict_info struct @@ -2163,7 +2168,7 @@ static int handle_content_merge(struct merge_options *opt, /* * FIXME: If opt->priv->call_depth && !clean, then we really * should not make result->mode match either a->mode or - * b->mode; that causes t6036 "check conflicting mode for + * b->mode; that causes t6416 "check conflicting mode for * regular file" to fail. It would be best to use some other * mode, but we'll confuse all kinds of stuff if we use one * where S_ISREG(result->mode) isn't true, and if we use @@ -2216,8 +2221,8 @@ static int handle_content_merge(struct merge_options *opt, } if (!ret && record_object && - write_object_file(result_buf.ptr, result_buf.size, - OBJ_BLOB, &result->oid)) { + odb_write_object(the_repository->objects, result_buf.ptr, result_buf.size, + OBJ_BLOB, &result->oid)) { path_msg(opt, ERROR_OBJECT_WRITE_FAILED, 0, pathnames[0], pathnames[1], pathnames[2], NULL, _("error: unable to add %s to database"), path); @@ -2313,14 +2318,20 @@ static char *apply_dir_rename(struct strmap_entry *rename_info, return strbuf_detach(&new_path, NULL); } -static int path_in_way(struct strmap *paths, const char *path, unsigned side_mask) +static int path_in_way(struct strmap *paths, + const char *path, + unsigned side_mask, + struct diff_filepair *p) { struct merged_info *mi = strmap_get(paths, path); struct conflict_info *ci; if (!mi) return 0; INITIALIZE_CI(ci, mi); - return mi->clean || (side_mask & (ci->filemask | ci->dirmask)); + return mi->clean || (side_mask & (ci->filemask | ci->dirmask)) + /* See testcases 12[npq] of t6423 for this next condition */ + || ((ci->filemask & 0x01) && + strcmp(p->one->path, path)); } /* @@ -2332,6 +2343,7 @@ static int path_in_way(struct strmap *paths, const char *path, unsigned side_mas static char *handle_path_level_conflicts(struct merge_options *opt, const char *path, unsigned side_index, + struct diff_filepair *p, struct strmap_entry *rename_info, struct strmap *collisions) { @@ -2366,7 +2378,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt, */ if (c_info->reported_already) { clean = 0; - } else if (path_in_way(&opt->priv->paths, new_path, 1 << side_index)) { + } else if (path_in_way(&opt->priv->paths, new_path, 1 << side_index, p)) { c_info->reported_already = 1; strbuf_add_separated_string_list(&collision_paths, ", ", &c_info->source_files); @@ -2520,7 +2532,7 @@ static void compute_collisions(struct strmap *collisions, * happening, and fall back to no-directory-rename detection * behavior for those paths. * - * See testcases 9e and all of section 5 from t6043 for examples. + * See testcases 9e and all of section 5 from t6423 for examples. */ for (i = 0; i < pairs->nr; ++i) { struct strmap_entry *rename_info; @@ -2573,6 +2585,7 @@ static void free_collisions(struct strmap *collisions) static char *check_for_directory_rename(struct merge_options *opt, const char *path, unsigned side_index, + struct diff_filepair *p, struct strmap *dir_renames, struct strmap *dir_rename_exclusions, struct strmap *collisions, @@ -2580,7 +2593,6 @@ static char *check_for_directory_rename(struct merge_options *opt, { char *new_path; struct strmap_entry *rename_info; - struct strmap_entry *otherinfo; const char *new_dir; int other_side = 3 - side_index; @@ -2615,14 +2627,13 @@ static char *check_for_directory_rename(struct merge_options *opt, * to not let Side1 do the rename to dumbdir, since we know that is * the source of one of our directory renames. * - * That's why otherinfo and dir_rename_exclusions is here. + * That's why dir_rename_exclusions is here. * * As it turns out, this also prevents N-way transient rename - * confusion; See testcases 9c and 9d of t6043. + * confusion; See testcases 9c and 9d of t6423. */ new_dir = rename_info->value; /* old_dir = rename_info->key; */ - otherinfo = strmap_get_entry(dir_rename_exclusions, new_dir); - if (otherinfo) { + if (strmap_contains(dir_rename_exclusions, new_dir)) { path_msg(opt, INFO_DIR_RENAME_SKIPPED_DUE_TO_RERENAME, 1, rename_info->key, path, new_dir, NULL, _("WARNING: Avoiding applying %s -> %s rename " @@ -2631,7 +2642,7 @@ static char *check_for_directory_rename(struct merge_options *opt, return NULL; } - new_path = handle_path_level_conflicts(opt, path, side_index, + new_path = handle_path_level_conflicts(opt, path, side_index, p, rename_info, &collisions[side_index]); *clean_merge &= (new_path != NULL); @@ -2876,6 +2887,20 @@ static int process_renames(struct merge_options *opt, } /* + * Directory renames can result in rename-to-self; the code + * below assumes we have A->B with different A & B, and tries + * to move all entries to path B. If A & B are the same path, + * the logic can get confused, so skip further processing when + * A & B are already the same path. + * + * As a reminder, we can avoid strcmp here because all paths + * are interned in opt->priv->paths; see the comment above + * "paths" in struct merge_options_internal. + */ + if (oldpath == newpath) + continue; + + /* * If pair->one->path isn't in opt->priv->paths, that means * that either directory rename detection removed that * path, or a parent directory of oldpath was resolved and @@ -2888,6 +2913,32 @@ static int process_renames(struct merge_options *opt, continue; /* + * Rename caching from a previous commit might give us an + * irrelevant rename for the current commit. + * + * Imagine: + * foo/A -> bar/A + * was a cached rename for the upstream side from the + * previous commit (without the directories being renamed), + * but the next commit being replayed + * * does NOT add or delete files + * * does NOT have directory renames + * * does NOT modify any files under bar/ + * * does NOT modify foo/A + * * DOES modify other files under foo/ (otherwise the + * !oldinfo check above would have already exited for + * us) + * In such a case, our trivial directory resolution will + * have already merged bar/, and our attempt to process + * the cached + * foo/A -> bar/A + * would be counterproductive, and lack the necessary + * information anyway. Skip such renames. + */ + if (!newinfo) + continue; + + /* * diff_filepairs have copies of pathnames, thus we have to * use standard 'strcmp()' (negated) instead of '=='. */ @@ -3413,13 +3464,13 @@ static int collect_renames(struct merge_options *opt, continue; } if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_NONE && - p->status == 'R' && 1) { + p->status == 'R') { possibly_cache_new_pair(renames, p, side_index, NULL); goto skip_directory_renames; } new_path = check_for_directory_rename(opt, p->two->path, - side_index, + side_index, p, dir_renames_for_side, rename_exclusions, collisions, @@ -3629,7 +3680,7 @@ static int read_oid_strbuf(struct merge_options *opt, void *buf; enum object_type type; unsigned long size; - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) { path_msg(opt, ERROR_OBJECT_READ_FAILED, 0, path, NULL, NULL, NULL, @@ -3772,7 +3823,8 @@ static int write_tree(struct object_id *result_oid, } /* Write this object file out, and record in result_oid */ - if (write_object_file(buf.buf, buf.len, OBJ_TREE, result_oid)) + if (odb_write_object(the_repository->objects, buf.buf, + buf.len, OBJ_TREE, result_oid)) ret = -1; strbuf_release(&buf); return ret; @@ -4385,8 +4437,8 @@ static void prefetch_for_content_merges(struct merge_options *opt, if ((ci->filemask & side_mask) && S_ISREG(vi->mode) && - oid_object_info_extended(opt->repo, &vi->oid, NULL, - OBJECT_INFO_FOR_PREFETCH)) + odb_read_object_info_extended(opt->repo->objects, &vi->oid, NULL, + OBJECT_INFO_FOR_PREFETCH)) oid_array_append(&to_fetch, &vi->oid); } } @@ -5092,7 +5144,8 @@ static void merge_check_renames_reusable(struct merge_options *opt, * optimization" comment near that case). * * This could be revisited in the future; see the commit message - * where this comment was added for some possible pointers. + * where this comment was added for some possible pointers, or the + * later commit where this comment was added. */ if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_NONE) { renames->cached_pairs_valid_side = 0; /* neither side valid */ @@ -5353,20 +5406,20 @@ static void merge_recursive_config(struct merge_options *opt, int ui) { char *value = NULL; int renormalize = 0; - git_config_get_int("merge.verbosity", &opt->verbosity); - git_config_get_int("diff.renamelimit", &opt->rename_limit); - git_config_get_int("merge.renamelimit", &opt->rename_limit); - git_config_get_bool("merge.renormalize", &renormalize); + repo_config_get_int(the_repository, "merge.verbosity", &opt->verbosity); + repo_config_get_int(the_repository, "diff.renamelimit", &opt->rename_limit); + repo_config_get_int(the_repository, "merge.renamelimit", &opt->rename_limit); + repo_config_get_bool(the_repository, "merge.renormalize", &renormalize); opt->renormalize = renormalize; - if (!git_config_get_string("diff.renames", &value)) { + if (!repo_config_get_string(the_repository, "diff.renames", &value)) { opt->detect_renames = git_config_rename("diff.renames", value); free(value); } - if (!git_config_get_string("merge.renames", &value)) { + if (!repo_config_get_string(the_repository, "merge.renames", &value)) { opt->detect_renames = git_config_rename("merge.renames", value); free(value); } - if (!git_config_get_string("merge.directoryrenames", &value)) { + if (!repo_config_get_string(the_repository, "merge.directoryrenames", &value)) { int boolval = git_parse_maybe_bool(value); if (0 <= boolval) { opt->detect_directory_renames = boolval ? @@ -5379,7 +5432,7 @@ static void merge_recursive_config(struct merge_options *opt, int ui) free(value); } if (ui) { - if (!git_config_get_string("diff.algorithm", &value)) { + if (!repo_config_get_string(the_repository, "diff.algorithm", &value)) { long diff_algorithm = parse_algorithm_value(value); if (diff_algorithm < 0) die(_("unknown value for config '%s': %s"), "diff.algorithm", value); @@ -5387,7 +5440,7 @@ static void merge_recursive_config(struct merge_options *opt, int ui) free(value); } } - git_config(git_xmerge_config, NULL); + repo_config(the_repository, git_xmerge_config, NULL); } static void init_merge_options(struct merge_options *opt, @@ -5470,7 +5523,6 @@ int parse_merge_opt(struct merge_options *opt, const char *s) if (value < 0) return -1; /* clear out previous settings */ - DIFF_XDL_CLR(opt, NEED_MINIMAL); opt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK; opt->xdl_opts |= value; } |
