From: Junio C Hamano Date: Wed, 30 May 2018 12:51:29 +0000 (+0900) Subject: Merge branch 'ma/unpack-trees-free-msgs' X-Git-Tag: v2.18.0-rc0~2 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/e47dbece39712567a36a096010c5c1223119f7e3?ds=inline;hp=-c Merge branch 'ma/unpack-trees-free-msgs' Leak plugging. * ma/unpack-trees-free-msgs: unpack_trees_options: free messages when done argv-array: return the pushed string from argv_push*() merge-recursive: provide pair of `unpack_trees_{start,finish}()` merge: setup `opts` later in `checkout_fast_forward()` --- e47dbece39712567a36a096010c5c1223119f7e3 diff --combined argv-array.c index cb5bcd2c06,449dfc105a..f352ea9357 --- a/argv-array.c +++ b/argv-array.c @@@ -21,12 -21,13 +21,13 @@@ static void argv_array_push_nodup(struc array->argv[array->argc] = NULL; } - void argv_array_push(struct argv_array *array, const char *value) + const char *argv_array_push(struct argv_array *array, const char *value) { argv_array_push_nodup(array, xstrdup(value)); + return array->argv[array->argc - 1]; } - void argv_array_pushf(struct argv_array *array, const char *fmt, ...) + const char *argv_array_pushf(struct argv_array *array, const char *fmt, ...) { va_list ap; struct strbuf v = STRBUF_INIT; @@@ -36,6 -37,7 +37,7 @@@ va_end(ap); argv_array_push_nodup(array, strbuf_detach(&v, NULL)); + return array->argv[array->argc - 1]; } void argv_array_pushl(struct argv_array *array, ...) @@@ -64,26 -66,6 +66,26 @@@ void argv_array_pop(struct argv_array * array->argc--; } +void argv_array_split(struct argv_array *array, const char *to_split) +{ + while (isspace(*to_split)) + to_split++; + for (;;) { + const char *p = to_split; + + if (!*p) + break; + + while (*p && !isspace(*p)) + p++; + argv_array_push_nodup(array, xstrndup(to_split, p - to_split)); + + while (isspace(*p)) + p++; + to_split = p; + } +} + void argv_array_clear(struct argv_array *array) { if (array->argv != empty_argv) { diff --combined argv-array.h index 750c30d2f2,715c93b246..a39ba43f57 --- a/argv-array.h +++ b/argv-array.h @@@ -12,15 -12,13 +12,15 @@@ struct argv_array #define ARGV_ARRAY_INIT { empty_argv, 0, 0 } void argv_array_init(struct argv_array *); - void argv_array_push(struct argv_array *, const char *); + const char *argv_array_push(struct argv_array *, const char *); __attribute__((format (printf,2,3))) - void argv_array_pushf(struct argv_array *, const char *fmt, ...); + const char *argv_array_pushf(struct argv_array *, const char *fmt, ...); LAST_ARG_MUST_BE_NULL void argv_array_pushl(struct argv_array *, ...); void argv_array_pushv(struct argv_array *, const char **); void argv_array_pop(struct argv_array *); +/* Splits by whitespace; does not handle quoted arguments! */ +void argv_array_split(struct argv_array *, const char *); void argv_array_clear(struct argv_array *); const char **argv_array_detach(struct argv_array *); diff --combined builtin/checkout.c index 2b3b768eff,5cebe170fc..2e1d2376d2 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@@ -484,8 -484,7 +484,8 @@@ static int merge_working_tree(const str resolve_undo_clear(); if (opts->force) { - ret = reset_tree(new_branch_info->commit->tree, opts, 1, writeout_error); + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 1, writeout_error); if (ret) return ret; } else { @@@ -527,6 -526,7 +527,7 @@@ init_tree_desc(&trees[1], tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); + clear_unpack_trees_porcelain(&topts); if (ret == -1) { /* * Unpack couldn't do a trivial merge; either @@@ -571,23 -571,18 +572,23 @@@ o.verbosity = 0; work = write_tree_from_memory(&o); - ret = reset_tree(new_branch_info->commit->tree, opts, 1, + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 1, writeout_error); if (ret) return ret; o.ancestor = old_branch_info->name; o.branch1 = new_branch_info->name; o.branch2 = "local"; - ret = merge_trees(&o, new_branch_info->commit->tree, work, - old_branch_info->commit->tree, &result); + ret = merge_trees(&o, + get_commit_tree(new_branch_info->commit), + work, + get_commit_tree(old_branch_info->commit), + &result); if (ret < 0) exit(128); - ret = reset_tree(new_branch_info->commit->tree, opts, 0, + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 0, writeout_error); strbuf_release(&o.obuf); if (ret) @@@ -1008,7 -1003,7 +1009,7 @@@ static int parse_branchname_arg(int arg *source_tree = parse_tree_indirect(rev); } else { parse_commit_or_die(new_branch_info->commit); - *source_tree = new_branch_info->commit->tree; + *source_tree = get_commit_tree(new_branch_info->commit); } if (!*source_tree) /* case (1): want a tree */ diff --combined merge-recursive.c index cf5341828f,338f63a952..ac27abbd4c --- a/merge-recursive.c +++ b/merge-recursive.c @@@ -23,7 -23,6 +23,7 @@@ #include "merge-recursive.h" #include "dir.h" #include "submodule.h" +#include "revision.h" struct path_hashmap_entry { struct hashmap_entry e; @@@ -163,7 -162,7 +163,7 @@@ static struct commit *make_virtual_comm struct commit *commit = alloc_commit_node(); set_merge_remote_desc(commit, comment, (struct object *)commit); - commit->tree = tree; + commit->maybe_tree = tree; commit->object.parsed = 1; return commit; } @@@ -338,10 -337,10 +338,10 @@@ static void init_tree_desc_from_tree(st init_tree_desc(desc, tree->buffer, tree->size); } - static int git_merge_trees(struct merge_options *o, - struct tree *common, - struct tree *head, - struct tree *merge) + static int unpack_trees_start(struct merge_options *o, + struct tree *common, + struct tree *head, + struct tree *merge) { int rc; struct tree_desc t[3]; @@@ -380,6 -379,12 +380,12 @@@ return rc; } + static void unpack_trees_finish(struct merge_options *o) + { + discard_index(&o->orig_index); + clear_unpack_trees_porcelain(&o->unpack_opts); + } + struct tree *write_tree_from_memory(struct merge_options *o) { struct tree *result = NULL; @@@ -393,7 -398,7 +399,7 @@@ fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce), (int)ce_namelen(ce), ce->name); } - die("BUG: unmerged index entries in merge-recursive.c"); + BUG("unmerged index entries in merge-recursive.c"); } if (!active_cache_tree) @@@ -1083,185 -1088,6 +1089,185 @@@ static int merge_3way(struct merge_opti return merge_status; } +static int find_first_merges(struct object_array *result, const char *path, + struct commit *a, struct commit *b) +{ + int i, j; + struct object_array merges = OBJECT_ARRAY_INIT; + struct commit *commit; + int contains_another; + + char merged_revision[42]; + const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path", + "--all", merged_revision, NULL }; + struct rev_info revs; + struct setup_revision_opt rev_opts; + + memset(result, 0, sizeof(struct object_array)); + memset(&rev_opts, 0, sizeof(rev_opts)); + + /* get all revisions that merge commit a */ + xsnprintf(merged_revision, sizeof(merged_revision), "^%s", + oid_to_hex(&a->object.oid)); + init_revisions(&revs, NULL); + rev_opts.submodule = path; + /* FIXME: can't handle linked worktrees in submodules yet */ + revs.single_worktree = path != NULL; + setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts); + + /* save all revisions from the above list that contain b */ + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); + while ((commit = get_revision(&revs)) != NULL) { + struct object *o = &(commit->object); + if (in_merge_bases(b, commit)) + add_object_array(o, NULL, &merges); + } + reset_revision_walk(); + + /* Now we've got all merges that contain a and b. Prune all + * merges that contain another found merge and save them in + * result. + */ + for (i = 0; i < merges.nr; i++) { + struct commit *m1 = (struct commit *) merges.objects[i].item; + + contains_another = 0; + for (j = 0; j < merges.nr; j++) { + struct commit *m2 = (struct commit *) merges.objects[j].item; + if (i != j && in_merge_bases(m2, m1)) { + contains_another = 1; + break; + } + } + + if (!contains_another) + add_object_array(merges.objects[i].item, NULL, result); + } + + object_array_clear(&merges); + return result->nr; +} + +static void print_commit(struct commit *commit) +{ + struct strbuf sb = STRBUF_INIT; + struct pretty_print_context ctx = {0}; + ctx.date_mode.type = DATE_NORMAL; + format_commit_message(commit, " %h: %m %s", &sb, &ctx); + fprintf(stderr, "%s\n", sb.buf); + strbuf_release(&sb); +} + +static int merge_submodule(struct merge_options *o, + struct object_id *result, const char *path, + const struct object_id *base, const struct object_id *a, + const struct object_id *b) +{ + struct commit *commit_base, *commit_a, *commit_b; + int parent_count; + struct object_array merges; + + int i; + int search = !o->call_depth; + + /* store a in result in case we fail */ + oidcpy(result, a); + + /* we can not handle deletion conflicts */ + if (is_null_oid(base)) + return 0; + if (is_null_oid(a)) + return 0; + if (is_null_oid(b)) + return 0; + + if (add_submodule_odb(path)) { + output(o, 1, _("Failed to merge submodule %s (not checked out)"), path); + return 0; + } + + if (!(commit_base = lookup_commit_reference(base)) || + !(commit_a = lookup_commit_reference(a)) || + !(commit_b = lookup_commit_reference(b))) { + output(o, 1, _("Failed to merge submodule %s (commits not present)"), path); + return 0; + } + + /* check whether both changes are forward */ + if (!in_merge_bases(commit_base, commit_a) || + !in_merge_bases(commit_base, commit_b)) { + output(o, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path); + return 0; + } + + /* Case #1: a is contained in b or vice versa */ + if (in_merge_bases(commit_a, commit_b)) { + oidcpy(result, b); + if (show(o, 3)) { + output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path); + output_commit_title(o, commit_b); + } else if (show(o, 2)) + output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(b)); + else + ; /* no output */ + + return 1; + } + if (in_merge_bases(commit_b, commit_a)) { + oidcpy(result, a); + if (show(o, 3)) { + output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path); + output_commit_title(o, commit_a); + } else if (show(o, 2)) + output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(a)); + else + ; /* no output */ + + return 1; + } + + /* + * Case #2: There are one or more merges that contain a and b in + * the submodule. If there is only one, then present it as a + * suggestion to the user, but leave it marked unmerged so the + * user needs to confirm the resolution. + */ + + /* Skip the search if makes no sense to the calling context. */ + if (!search) + return 0; + + /* find commit which merges them */ + parent_count = find_first_merges(&merges, path, commit_a, commit_b); + switch (parent_count) { + case 0: + output(o, 1, _("Failed to merge submodule %s (merge following commits not found)"), path); + break; + + case 1: + output(o, 1, _("Failed to merge submodule %s (not fast-forward)"), path); + output(o, 2, _("Found a possible merge resolution for the submodule:\n")); + print_commit((struct commit *) merges.objects[0].item); + output(o, 2, _( + "If this is correct simply add it to the index " + "for example\n" + "by using:\n\n" + " git update-index --cacheinfo 160000 %s \"%s\"\n\n" + "which will accept this suggestion.\n"), + oid_to_hex(&merges.objects[0].item->oid), path); + break; + + default: + output(o, 1, _("Failed to merge submodule %s (multiple merges found)"), path); + for (i = 0; i < merges.nr; i++) + print_commit((struct commit *) merges.objects[i].item); + } + + object_array_clear(&merges); + return 0; +} + static int merge_file_1(struct merge_options *o, const struct diff_filespec *one, const struct diff_filespec *a, @@@ -1325,11 -1151,12 +1331,11 @@@ return ret; result->clean = (merge_status == 0); } else if (S_ISGITLINK(a->mode)) { - result->clean = merge_submodule(&result->oid, + result->clean = merge_submodule(o, &result->oid, one->path, &one->oid, &a->oid, - &b->oid, - !o->call_depth); + &b->oid); } else if (S_ISLNK(a->mode)) { switch (o->recursive_variant) { case MERGE_RECURSIVE_NORMAL: @@@ -1345,7 -1172,7 +1351,7 @@@ break; } } else - die("BUG: unsupported object type in the tree"); + BUG("unsupported object type in the tree"); } if (result->merge) @@@ -2598,7 -2425,7 +2604,7 @@@ static int process_renames(struct merge const char *ren2_dst = ren2->pair->two->path; enum rename_type rename_type; if (strcmp(ren1_src, ren2_src) != 0) - die("BUG: ren1_src != ren2_src"); + BUG("ren1_src != ren2_src"); ren2->dst_entry->processed = 1; ren2->processed = 1; if (strcmp(ren1_dst, ren2_dst) != 0) { @@@ -2632,7 -2459,7 +2638,7 @@@ ren2 = lookup->util; ren2_dst = ren2->pair->two->path; if (strcmp(ren1_dst, ren2_dst) != 0) - die("BUG: ren1_dst != ren2_dst"); + BUG("ren1_dst != ren2_dst"); clean_merge = 0; ren2->processed = 1; @@@ -3236,7 -3063,7 +3242,7 @@@ static int process_entry(struct merge_o */ remove_file(o, 1, path, !a_mode); } else - die("BUG: fatal merge failure, shouldn't happen."); + BUG("fatal merge failure, shouldn't happen."); return clean_merge; } @@@ -3267,13 -3094,14 +3273,14 @@@ int merge_trees(struct merge_options *o return 1; } - code = git_merge_trees(o, common, head, merge); + code = unpack_trees_start(o, common, head, merge); if (code != 0) { if (show(o, 4) || o->call_depth) err(o, _("merging of trees %s and %s failed"), oid_to_hex(&head->object.oid), oid_to_hex(&merge->object.oid)); + unpack_trees_finish(o); return -1; } @@@ -3314,7 -3142,7 +3321,7 @@@ for (i = 0; i < entries->nr; i++) { struct stage_data *e = entries->items[i].util; if (!e->processed) - die("BUG: unprocessed path??? %s", + BUG("unprocessed path??? %s", entries->items[i].string); } @@@ -3326,20 -3154,15 +3333,15 @@@ cleanup hashmap_free(&o->current_file_dir_set, 1); - if (clean < 0) + if (clean < 0) { + unpack_trees_finish(o); return clean; + } } else clean = 1; - /* Free the extra index left from git_merge_trees() */ - /* - * FIXME: Need to also free data allocated by - * setup_unpack_trees_porcelain() tucked away in o->unpack_opts.msgs, - * but the problem is that only half of it refers to dynamically - * allocated data, while the other half points at static strings. - */ - discard_index(&o->orig_index); + unpack_trees_finish(o); if (o->call_depth && !(*result = write_tree_from_memory(o))) return -1; @@@ -3434,8 -3257,7 +3436,8 @@@ int merge_recursive(struct merge_option read_cache(); o->ancestor = "merged common ancestors"; - clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree, + clean = merge_trees(o, get_commit_tree(h1), get_commit_tree(h2), + get_commit_tree(merged_common_ancestors), &mrtree); if (clean < 0) { flush_output(o); diff --combined merge.c index 5186cb6156,b433291d0c..0783858739 --- a/merge.c +++ b/merge.c @@@ -11,7 -11,10 +11,7 @@@ static const char *merge_argument(struct commit *commit) { - if (commit) - return oid_to_hex(&commit->object.oid); - else - return EMPTY_TREE_SHA1_HEX; + return oid_to_hex(commit ? &commit->object.oid : the_hash_algo->empty_tree); } int index_has_changes(struct strbuf *sb) @@@ -91,8 -94,24 +91,24 @@@ int checkout_fast_forward(const struct return -1; memset(&trees, 0, sizeof(trees)); - memset(&opts, 0, sizeof(opts)); memset(&t, 0, sizeof(t)); + + trees[nr_trees] = parse_tree_indirect(head); + if (!trees[nr_trees++]) { + rollback_lock_file(&lock_file); + return -1; + } + trees[nr_trees] = parse_tree_indirect(remote); + if (!trees[nr_trees++]) { + rollback_lock_file(&lock_file); + return -1; + } + for (i = 0; i < nr_trees; i++) { + parse_tree(trees[i]); + init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); + } + + memset(&opts, 0, sizeof(opts)); if (overwrite_ignore) { memset(&dir, 0, sizeof(dir)); dir.flags |= DIR_SHOW_IGNORED; @@@ -109,24 -128,13 +125,13 @@@ opts.fn = twoway_merge; setup_unpack_trees_porcelain(&opts, "merge"); - trees[nr_trees] = parse_tree_indirect(head); - if (!trees[nr_trees++]) { - rollback_lock_file(&lock_file); - return -1; - } - trees[nr_trees] = parse_tree_indirect(remote); - if (!trees[nr_trees++]) { - rollback_lock_file(&lock_file); - return -1; - } - for (i = 0; i < nr_trees; i++) { - parse_tree(trees[i]); - init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); - } if (unpack_trees(nr_trees, t, &opts)) { rollback_lock_file(&lock_file); + clear_unpack_trees_porcelain(&opts); return -1; } + clear_unpack_trees_porcelain(&opts); + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) return error(_("unable to write new index file")); return 0; diff --combined unpack-trees.c index d17f726e75,73a6dc1701..3a85a02a77 --- a/unpack-trees.c +++ b/unpack-trees.c @@@ -1,5 -1,6 +1,6 @@@ #define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" + #include "argv-array.h" #include "repository.h" #include "config.h" #include "dir.h" @@@ -103,6 -104,8 +104,8 @@@ void setup_unpack_trees_porcelain(struc const char **msgs = opts->msgs; const char *msg; + argv_array_init(&opts->msgs_to_free); + if (!strcmp(cmd, "checkout")) msg = advice_commit_before_merge ? _("Your local changes to the following files would be overwritten by checkout:\n%%s" @@@ -119,7 -122,7 +122,7 @@@ "Please commit your changes or stash them before you %s.") : _("Your local changes to the following files would be overwritten by %s:\n%%s"); msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] = - xstrfmt(msg, cmd, cmd); + argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); msgs[ERROR_NOT_UPTODATE_DIR] = _("Updating the following directories would lose untracked files in them:\n%s"); @@@ -139,7 -142,8 +142,8 @@@ ? _("The following untracked working tree files would be removed by %s:\n%%s" "Please move or remove them before you %s.") : _("The following untracked working tree files would be removed by %s:\n%%s"); - msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, cmd, cmd); + msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = + argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); if (!strcmp(cmd, "checkout")) msg = advice_commit_before_merge @@@ -156,7 -160,8 +160,8 @@@ ? _("The following untracked working tree files would be overwritten by %s:\n%%s" "Please move or remove them before you %s.") : _("The following untracked working tree files would be overwritten by %s:\n%%s"); - msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, cmd, cmd); + msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = + argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); /* * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we @@@ -179,6 -184,12 +184,12 @@@ opts->unpack_rejects[i].strdup_strings = 1; } + void clear_unpack_trees_porcelain(struct unpack_trees_options *opts) + { + argv_array_clear(&opts->msgs_to_free); + memset(opts->msgs, 0, sizeof(opts->msgs)); + } + static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce, unsigned int set, unsigned int clear) { @@@ -290,7 -301,7 +301,7 @@@ static void load_gitmodules_file(struc if (!state && ce->ce_flags & CE_WT_REMOVE) { repo_read_gitmodules(the_repository); } else if (state && (ce->ce_flags & CE_UPDATE)) { - submodule_free(); + submodule_free(the_repository); checkout_entry(ce, state, NULL); repo_read_gitmodules(the_repository); } @@@ -398,7 -409,7 +409,7 @@@ static int check_updates(struct unpack_ if (ce->ce_flags & CE_UPDATE) { if (ce->ce_flags & CE_WT_REMOVE) - die("BUG: both update and delete flags are set on %s", + BUG("both update and delete flags are set on %s", ce->name); display_progress(progress, ++cnt); ce->ce_flags &= ~CE_UPDATE; @@@ -1284,21 -1295,10 +1295,21 @@@ int unpack_trees(unsigned len, struct t o->result.timestamp.sec = o->src_index->timestamp.sec; o->result.timestamp.nsec = o->src_index->timestamp.nsec; o->result.version = o->src_index->version; - o->result.split_index = o->src_index->split_index; - if (o->result.split_index) + if (!o->src_index->split_index) { + o->result.split_index = NULL; + } else if (o->src_index == o->dst_index) { + /* + * o->dst_index (and thus o->src_index) will be discarded + * and overwritten with o->result at the end of this function, + * so just use src_index's split_index to avoid having to + * create a new one. + */ + o->result.split_index = o->src_index->split_index; o->result.split_index->refcount++; - hashcpy(o->result.sha1, o->src_index->sha1); + } else { + o->result.split_index = init_split_index(&o->result); + } + oidcpy(&o->result.oid, &o->src_index->oid); o->merge_size = len; mark_all_ce_unused(o->src_index); @@@ -1412,6 -1412,7 +1423,6 @@@ } } - o->src_index = NULL; ret = check_updates(o) ? (-2) : 0; if (o->dst_index) { if (!ret) { @@@ -1422,13 -1423,12 +1433,13 @@@ WRITE_TREE_SILENT | WRITE_TREE_REPAIR); } - move_index_extensions(&o->result, o->dst_index); + move_index_extensions(&o->result, o->src_index); discard_index(o->dst_index); *o->dst_index = o->result; } else { discard_index(&o->result); } + o->src_index = NULL; done: clear_exclude_list(&el);