diff options
Diffstat (limited to 'merge-recursive.c')
-rw-r--r-- | merge-recursive.c | 180 |
1 files changed, 130 insertions, 50 deletions
diff --git a/merge-recursive.c b/merge-recursive.c index d945779..8ff29ed 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -3,14 +3,10 @@ * Fredrik Kuivinen. * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006 */ -#include "cache.h" +#include "git-compat-util.h" #include "merge-recursive.h" -#include "advice.h" #include "alloc.h" -#include "attr.h" -#include "blob.h" -#include "builtin.h" #include "cache-tree.h" #include "commit.h" #include "commit-reach.h" @@ -18,14 +14,22 @@ #include "diff.h" #include "diffcore.h" #include "dir.h" -#include "ll-merge.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" +#include "merge-ll.h" #include "lockfile.h" -#include "object-store.h" +#include "match-trees.h" +#include "name-hash.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" +#include "path.h" #include "repository.h" #include "revision.h" +#include "sparse-index.h" #include "string-list.h" -#include "submodule-config.h" -#include "submodule.h" +#include "symlinks.h" #include "tag.h" #include "tree-walk.h" #include "unpack-trees.h" @@ -45,7 +49,7 @@ struct path_hashmap_entry { char path[FLEX_ARRAY]; }; -static int path_hashmap_cmp(const void *cmp_data, +static int path_hashmap_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *keydata) @@ -82,17 +86,17 @@ static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap, { struct dir_rename_entry key; - if (dir == NULL) + if (!dir) return NULL; hashmap_entry_init(&key.ent, strhash(dir)); key.dir = dir; return hashmap_get_entry(hashmap, &key, ent, NULL); } -static int dir_rename_cmp(const void *unused_cmp_data, +static int dir_rename_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, - const void *unused_keydata) + const void *keydata UNUSED) { const struct dir_rename_entry *e1, *e2; @@ -134,10 +138,10 @@ static struct collision_entry *collision_find_entry(struct hashmap *hashmap, return hashmap_get_entry(hashmap, &key, ent, NULL); } -static int collision_cmp(const void *unused_cmp_data, +static int collision_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, - const void *unused_keydata) + const void *keydata UNUSED) { const struct collision_entry *e1, *e2; @@ -401,8 +405,9 @@ static inline int merge_detect_rename(struct merge_options *opt) static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) { - parse_tree(tree); - init_tree_desc(desc, tree->buffer, tree->size); + if (parse_tree(tree) < 0) + exit(128); + init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size); } static int unpack_trees_start(struct merge_options *opt, @@ -412,7 +417,7 @@ static int unpack_trees_start(struct merge_options *opt, { int rc; struct tree_desc t[3]; - struct index_state tmp_index = { NULL }; + struct index_state tmp_index = INDEX_STATE_INIT(opt->repo); memset(&opt->priv->unpack_opts, 0, sizeof(opt->priv->unpack_opts)); if (opt->priv->call_depth) @@ -456,7 +461,7 @@ static void unpack_trees_finish(struct merge_options *opt) clear_unpack_trees_porcelain(&opt->priv->unpack_opts); } -static int save_files_dirs(const struct object_id *oid, +static int save_files_dirs(const struct object_id *oid UNUSED, struct strbuf *base, const char *path, unsigned int mode, void *context) { @@ -522,10 +527,10 @@ static struct stage_data *insert_stage_data(struct repository *r, */ static struct string_list *get_unmerged(struct index_state *istate) { - struct string_list *unmerged = xcalloc(1, sizeof(struct string_list)); + struct string_list *unmerged = xmalloc(sizeof(struct string_list)); int i; - unmerged->strdup_strings = 1; + string_list_init_dup(unmerged); /* TODO: audit for interaction with sparse-index. */ ensure_full_index(istate); @@ -951,7 +956,8 @@ static int update_file_flags(struct merge_options *opt, goto update_index; } - buf = read_object_file(&contents->oid, &type, &size); + buf = repo_read_object_file(the_repository, &contents->oid, + &type, &size); if (!buf) { ret = err(opt, _("cannot read object %s '%s'"), oid_to_hex(&contents->oid), path); @@ -1042,13 +1048,14 @@ static int merge_3way(struct merge_options *opt, const int extra_marker_size) { mmfile_t orig, src1, src2; - struct ll_merge_options ll_opts = {0}; + struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT; char *base, *name1, *name2; - int merge_status; + enum ll_merge_result merge_status; ll_opts.renormalize = opt->renormalize; ll_opts.extra_marker_size = extra_marker_size; ll_opts.xdl_opts = opt->xdl_opts; + ll_opts.conflict_style = opt->conflict_style; if (opt->priv->call_depth) { ll_opts.virtual_ancestor = 1; @@ -1090,6 +1097,9 @@ static int merge_3way(struct merge_options *opt, merge_status = ll_merge(result_buf, a->path, &orig, base, &src1, name1, &src2, name2, opt->repo->index, &ll_opts); + if (merge_status == LL_MERGE_BINARY_CONFLICT) + warning("Cannot merge binary files: %s (%s vs. %s)", + a->path, name1, name2); free(base); free(name1); @@ -1131,7 +1141,13 @@ static int find_first_merges(struct repository *repo, die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { struct object *o = &(commit->object); - if (repo_in_merge_bases(repo, b, commit)) + int ret = repo_in_merge_bases(repo, b, commit); + if (ret < 0) { + object_array_clear(&merges); + release_revisions(&revs); + return ret; + } + if (ret) add_object_array(o, NULL, &merges); } reset_revision_walk(); @@ -1146,9 +1162,17 @@ static int find_first_merges(struct repository *repo, contains_another = 0; for (j = 0; j < merges.nr; j++) { struct commit *m2 = (struct commit *) merges.objects[j].item; - if (i != j && repo_in_merge_bases(repo, m2, m1)) { - contains_another = 1; - break; + if (i != j) { + int ret = repo_in_merge_bases(repo, m2, m1); + if (ret < 0) { + object_array_clear(&merges); + release_revisions(&revs); + return ret; + } + if (ret > 0) { + contains_another = 1; + break; + } } } @@ -1157,6 +1181,7 @@ static int find_first_merges(struct repository *repo, } object_array_clear(&merges); + release_revisions(&revs); return result->nr; } @@ -1183,7 +1208,7 @@ static int merge_submodule(struct merge_options *opt, const struct object_id *b) { struct repository subrepo; - int ret = 0; + int ret = 0, ret2; struct commit *commit_base, *commit_a, *commit_b; int parent_count; struct object_array merges; @@ -1220,14 +1245,32 @@ static int merge_submodule(struct merge_options *opt, } /* check whether both changes are forward */ - if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) || - !repo_in_merge_bases(&subrepo, commit_base, commit_b)) { + ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a); + if (ret2 < 0) { + output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + goto cleanup; + } + if (ret2 > 0) + ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_b); + if (ret2 < 0) { + output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + goto cleanup; + } + if (!ret2) { output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path); goto cleanup; } /* Case #1: a is contained in b or vice versa */ - if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) { + ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b); + if (ret2 < 0) { + output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + goto cleanup; + } + if (ret2) { oidcpy(result, b); if (show(opt, 3)) { output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); @@ -1240,7 +1283,13 @@ static int merge_submodule(struct merge_options *opt, ret = 1; goto cleanup; } - if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) { + ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a); + if (ret2 < 0) { + output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + goto cleanup; + } + if (ret2) { oidcpy(result, a); if (show(opt, 3)) { output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); @@ -1269,6 +1318,10 @@ static int merge_submodule(struct merge_options *opt, parent_count = find_first_merges(&subrepo, &merges, path, commit_a, commit_b); switch (parent_count) { + case -1: + output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + break; case 0: output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path); break; @@ -1369,12 +1422,12 @@ static int merge_mode_and_contents(struct merge_options *opt, extra_marker_size); if ((merge_status < 0) || !result_buf.ptr) - ret = err(opt, _("Failed to execute internal merge")); + ret = err(opt, _("failed to execute internal merge")); if (!ret && write_object_file(result_buf.ptr, result_buf.size, - blob_type, &result->blob.oid)) - ret = err(opt, _("Unable to add %s to database"), + OBJ_BLOB, &result->blob.oid)) + ret = err(opt, _("unable to add %s to database"), a->path); free(result_buf.ptr); @@ -1383,11 +1436,14 @@ static int merge_mode_and_contents(struct merge_options *opt, /* FIXME: bug, what if modes didn't match? */ result->clean = (merge_status == 0); } else if (S_ISGITLINK(a->mode)) { - result->clean = merge_submodule(opt, &result->blob.oid, - o->path, - &o->oid, - &a->oid, - &b->oid); + int clean = merge_submodule(opt, &result->blob.oid, + o->path, + &o->oid, + &a->oid, + &b->oid); + if (clean < 0) + return -1; + result->clean = clean; } else if (S_ISLNK(a->mode)) { switch (opt->recursive_variant) { case MERGE_VARIANT_NORMAL: @@ -1987,14 +2043,14 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path, * renamed means the root directory can never be renamed -- because * the root directory always exists). */ - if (end_of_old == NULL) + if (!end_of_old) return; /* Note: *old_dir and *new_dir are still NULL */ /* * If new_path contains no directory (end_of_new is NULL), then we * have a rename of old_path's directory to the root directory. */ - if (end_of_new == NULL) { + if (!end_of_new) { *old_dir = xstrndup(old_path, end_of_old - old_path); *new_dir = xstrdup(""); return; @@ -2096,7 +2152,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt, if (!new_path) { /* This should only happen when entry->non_unique_new_dir set */ if (!entry->non_unique_new_dir) - BUG("entry->non_unqiue_dir not set and !new_path"); + BUG("entry->non_unique_new_dir not set and !new_path"); output(opt, 1, _("CONFLICT (directory rename split): " "Unclear where to place %s because directory " "%s was renamed to multiple other directories, " @@ -2113,7 +2169,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt, * to ensure that's the case. */ collision_ent = collision_find_entry(collisions, new_path); - if (collision_ent == NULL) + if (!collision_ent) BUG("collision_ent is NULL"); /* @@ -2993,7 +3049,7 @@ static void final_cleanup_rename(struct string_list *rename) const struct rename *re; int i; - if (rename == NULL) + if (!rename) return; for (i = 0; i < rename->nr; i++) { @@ -3017,7 +3073,7 @@ static int read_oid_strbuf(struct merge_options *opt, void *buf; enum object_type type; unsigned long size; - buf = read_object_file(oid, &type, &size); + buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) return err(opt, _("cannot read object %s"), oid_to_hex(oid)); if (type != OBJ_BLOB) { @@ -3588,7 +3644,9 @@ static int merge_recursive_internal(struct merge_options *opt, } if (!merge_bases) { - merge_bases = get_merge_bases(h1, h2); + if (repo_get_merge_bases(the_repository, h1, h2, + &merge_bases) < 0) + return -1; merge_bases = reverse_commit_list(merge_bases); } @@ -3602,7 +3660,7 @@ static int merge_recursive_internal(struct merge_options *opt, } merged_merge_bases = pop_commit(&merge_bases); - if (merged_merge_bases == NULL) { + if (!merged_merge_bases) { /* if there is no common ancestor, use an empty tree */ struct tree *tree; @@ -3711,6 +3769,10 @@ static int merge_start(struct merge_options *opt, struct tree *head) assert(opt->priv == NULL); + /* Not supported; option specific to merge-ort */ + assert(!opt->record_conflict_msgs_as_headers); + assert(!opt->msg_header_prefix); + /* Sanity check on repo state; index must match head */ if (repo_index_has_changes(opt->repo, head, &sb)) { err(opt, _("Your local changes to the following files would be overwritten by merge:\n %s"), @@ -3789,7 +3851,7 @@ static struct commit *get_ref(struct repository *repo, return make_virtual_commit(repo, (struct tree*)object, name); if (object->type != OBJ_COMMIT) return NULL; - if (parse_commit((struct commit *)object)) + if (repo_parse_commit(repo, (struct commit *)object)) return NULL; return (struct commit *)object; } @@ -3886,6 +3948,8 @@ void init_merge_options(struct merge_options *opt, opt->renormalize = 0; + opt->conflict_style = -1; + merge_recursive_config(opt); merge_verbosity = getenv("GIT_MERGE_VERBOSITY"); if (merge_verbosity) @@ -3894,6 +3958,22 @@ void init_merge_options(struct merge_options *opt, opt->buffer_output = 0; } +/* + * For now, members of merge_options do not need deep copying, but + * it may change in the future, in which case we would need to update + * this, and also make a matching change to clear_merge_options() to + * release the resources held by a copied instance. + */ +void copy_merge_options(struct merge_options *dst, struct merge_options *src) +{ + *dst = *src; +} + +void clear_merge_options(struct merge_options *opt UNUSED) +{ + ; /* no-op as our copy is shallow right now */ +} + int parse_merge_opt(struct merge_options *opt, const char *s) { const char *arg; |