am: improve author-script error reporting
[gitweb.git] / builtin / merge.c
index ee050a47f34d7394d048f955baabb37a9e716ef8..e331ca6d481005f6122fa6fda6075728f893632d 100644 (file)
@@ -14,6 +14,7 @@
 #include "run-command.h"
 #include "diff.h"
 #include "refs.h"
+#include "refspec.h"
 #include "commit.h"
 #include "diffcore.h"
 #include "revision.h"
@@ -34,6 +35,8 @@
 #include "string-list.h"
 #include "packfile.h"
 #include "tag.h"
+#include "alias.h"
+#include "commit-reach.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -109,6 +112,35 @@ static int option_parse_message(const struct option *opt,
        return 0;
 }
 
+static int option_read_message(struct parse_opt_ctx_t *ctx,
+                              const struct option *opt, int unset)
+{
+       struct strbuf *buf = opt->value;
+       const char *arg;
+
+       if (unset)
+               BUG("-F cannot be negated");
+
+       if (ctx->opt) {
+               arg = ctx->opt;
+               ctx->opt = NULL;
+       } else if (ctx->argc > 1) {
+               ctx->argc--;
+               arg = *++ctx->argv;
+       } else
+               return opterror(opt, "requires a value", 0);
+
+       if (buf->len)
+               strbuf_addch(buf, '\n');
+       if (ctx->prefix && !is_absolute_path(arg))
+               arg = prefix_filename(ctx->prefix, arg);
+       if (strbuf_read_file(buf, arg, 0) < 0)
+               return error(_("could not read file '%s'"), arg);
+       have_message = 1;
+
+       return 0;
+}
+
 static struct strategy *get_strategy(const char *name)
 {
        int i;
@@ -213,9 +245,9 @@ static struct option builtin_merge_options[] = {
        OPT_BOOL('e', "edit", &option_edit,
                N_("edit message before committing")),
        OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
-       { OPTION_SET_INT, 0, "ff-only", &fast_forward, NULL,
-               N_("abort if fast-forward is not possible"),
-               PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, FF_ONLY },
+       OPT_SET_INT_F(0, "ff-only", &fast_forward,
+                     N_("abort if fast-forward is not possible"),
+                     FF_ONLY, PARSE_OPT_NONEG),
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_BOOL(0, "verify-signatures", &verify_signatures,
                N_("verify that the named commit has a valid GPG signature")),
@@ -226,6 +258,9 @@ static struct option builtin_merge_options[] = {
        OPT_CALLBACK('m', "message", &merge_msg, N_("message"),
                N_("merge commit message (for a non-fast-forward merge)"),
                option_parse_message),
+       { OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
+               N_("read message from file"), PARSE_OPT_NONEG,
+               (parse_opt_cb *) option_read_message },
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
@@ -245,9 +280,9 @@ static struct option builtin_merge_options[] = {
 /* Cleans up metadata that is uninteresting after a succeeded merge. */
 static void drop_save(void)
 {
-       unlink(git_path_merge_head());
-       unlink(git_path_merge_msg());
-       unlink(git_path_merge_mode());
+       unlink(git_path_merge_head(the_repository));
+       unlink(git_path_merge_msg(the_repository));
+       unlink(git_path_merge_mode(the_repository));
 }
 
 static int save_state(struct object_id *stash)
@@ -280,7 +315,7 @@ static int save_state(struct object_id *stash)
        return rc;
 }
 
-static void read_empty(unsigned const char *sha1, int verbose)
+static void read_empty(const struct object_id *oid, int verbose)
 {
        int i = 0;
        const char *args[7];
@@ -290,15 +325,15 @@ static void read_empty(unsigned const char *sha1, int verbose)
                args[i++] = "-v";
        args[i++] = "-m";
        args[i++] = "-u";
-       args[i++] = EMPTY_TREE_SHA1_HEX;
-       args[i++] = sha1_to_hex(sha1);
+       args[i++] = empty_tree_oid_hex();
+       args[i++] = oid_to_hex(oid);
        args[i] = NULL;
 
        if (run_command_v_opt(args, RUN_GIT_CMD))
                die(_("read-tree failed"));
 }
 
-static void reset_hard(unsigned const char *sha1, int verbose)
+static void reset_hard(const struct object_id *oid, int verbose)
 {
        int i = 0;
        const char *args[6];
@@ -308,7 +343,7 @@ static void reset_hard(unsigned const char *sha1, int verbose)
                args[i++] = "-v";
        args[i++] = "--reset";
        args[i++] = "-u";
-       args[i++] = sha1_to_hex(sha1);
+       args[i++] = oid_to_hex(oid);
        args[i] = NULL;
 
        if (run_command_v_opt(args, RUN_GIT_CMD))
@@ -324,7 +359,7 @@ static void restore_state(const struct object_id *head,
        if (is_null_oid(stash))
                return;
 
-       reset_hard(head->hash, 1);
+       reset_hard(head, 1);
 
        args[2] = oid_to_hex(stash);
 
@@ -380,7 +415,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
                        oid_to_hex(&commit->object.oid));
                pretty_print_commit(&ctx, commit, &out);
        }
-       write_file_buf(git_path_squash_msg(), out.buf, out.len);
+       write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
        strbuf_release(&out);
 }
 
@@ -412,7 +447,7 @@ static void finish(struct commit *head_commit,
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
                         */
-                       close_all_packs();
+                       close_all_packs(the_repository->objects);
                        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
                }
        }
@@ -443,6 +478,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
        struct object_id branch_head;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf bname = STRBUF_INIT;
+       struct merge_remote_desc *desc;
        const char *ptr;
        char *found_ref;
        int len, early;
@@ -515,16 +551,13 @@ static void merge_name(const char *remote, struct strbuf *msg)
                strbuf_release(&truname);
        }
 
-       if (remote_head->util) {
-               struct merge_remote_desc *desc;
-               desc = merge_remote_util(remote_head);
-               if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
-                       strbuf_addf(msg, "%s\t\t%s '%s'\n",
-                                   oid_to_hex(&desc->obj->oid),
-                                   type_name(desc->obj->type),
-                                   remote);
-                       goto cleanup;
-               }
+       desc = merge_remote_util(remote_head);
+       if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
+               strbuf_addf(msg, "%s\t\t%s '%s'\n",
+                           oid_to_hex(&desc->obj->oid),
+                           type_name(desc->obj->type),
+                           remote);
+               goto cleanup;
        }
 
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
@@ -639,7 +672,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 
 static void write_tree_trivial(struct object_id *oid)
 {
-       if (write_cache_as_tree(oid->hash, 0, NULL))
+       if (write_cache_as_tree(oid, 0, NULL))
                die(_("git write-tree failed to write a tree"));
 }
 
@@ -647,7 +680,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                              struct commit_list *remoteheads,
                              struct commit *head)
 {
-       static struct lock_file lock;
+       struct lock_file lock = LOCK_INIT;
        const char *head_arg = "HEAD";
 
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
@@ -693,7 +726,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                        exit(128);
                if (write_locked_index(&the_index, &lock,
                                       COMMIT_LOCK | SKIP_IF_UNCHANGED))
-                       die (_("unable to write %s"), get_index_file());
+                       die(_("unable to write %s"), get_index_file());
                return clean ? 0 : 1;
        } else {
                return try_merge_command(strategy, xopts_nr, xopts,
@@ -741,7 +774,7 @@ static void add_strategies(const char *string, unsigned attr)
 
 static void read_merge_msg(struct strbuf *msg)
 {
-       const char *filename = git_path_merge_msg();
+       const char *filename = git_path_merge_msg(the_repository);
        strbuf_reset(msg);
        if (strbuf_read_file(msg, filename, 0) < 0)
                die_errno(_("Could not read from '%s'"), filename);
@@ -778,18 +811,18 @@ static void prepare_to_commit(struct commit_list *remoteheads)
        if (signoff)
                append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
        write_merge_heads(remoteheads);
-       write_file_buf(git_path_merge_msg(), msg.buf, msg.len);
+       write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len);
        if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
-                           git_path_merge_msg(), "merge", NULL))
+                           git_path_merge_msg(the_repository), "merge", NULL))
                abort_commit(remoteheads, NULL);
        if (0 < option_edit) {
-               if (launch_editor(git_path_merge_msg(), NULL, NULL))
+               if (launch_editor(git_path_merge_msg(the_repository), NULL, NULL))
                        abort_commit(remoteheads, NULL);
        }
 
        if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
                                          "commit-msg",
-                                         git_path_merge_msg(), NULL))
+                                         git_path_merge_msg(the_repository), NULL))
                abort_commit(remoteheads, NULL);
 
        read_merge_msg(&msg);
@@ -805,7 +838,7 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 {
        struct object_id result_tree, result_commit;
        struct commit_list *parents, **pptr = &parents;
-       static struct lock_file lock;
+       struct lock_file lock = LOCK_INIT;
 
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
@@ -859,7 +892,7 @@ static int suggest_conflicts(void)
        FILE *fp;
        struct strbuf msgbuf = STRBUF_INIT;
 
-       filename = git_path_merge_msg();
+       filename = git_path_merge_msg(the_repository);
        fp = xfopen(filename, "a");
 
        append_conflicts_hint(&msgbuf);
@@ -932,19 +965,22 @@ static void write_merge_heads(struct commit_list *remoteheads)
        for (j = remoteheads; j; j = j->next) {
                struct object_id *oid;
                struct commit *c = j->item;
-               if (c->util && merge_remote_util(c)->obj) {
-                       oid = &merge_remote_util(c)->obj->oid;
+               struct merge_remote_desc *desc;
+
+               desc = merge_remote_util(c);
+               if (desc && desc->obj) {
+                       oid = &desc->obj->oid;
                } else {
                        oid = &c->object.oid;
                }
                strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
        }
-       write_file_buf(git_path_merge_head(), buf.buf, buf.len);
+       write_file_buf(git_path_merge_head(the_repository), buf.buf, buf.len);
 
        strbuf_reset(&buf);
        if (fast_forward == FF_NO)
                strbuf_addstr(&buf, "no-ff");
-       write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
+       write_file_buf(git_path_merge_mode(the_repository), buf.buf, buf.len);
        strbuf_release(&buf);
 }
 
@@ -952,7 +988,8 @@ static void write_merge_state(struct commit_list *remoteheads)
 {
        write_merge_heads(remoteheads);
        strbuf_addch(&merge_msg, '\n');
-       write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
+       write_file_buf(git_path_merge_msg(the_repository), merge_msg.buf,
+                      merge_msg.len);
 }
 
 static int default_edit_option(void)
@@ -1031,11 +1068,12 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
        const char *filename;
        int fd, pos, npos;
        struct strbuf fetch_head_file = STRBUF_INIT;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        if (!merge_names)
                merge_names = &fetch_head_file;
 
-       filename = git_path_fetch_head();
+       filename = git_path_fetch_head(the_repository);
        fd = open(filename, O_RDONLY);
        if (fd < 0)
                die_errno(_("could not open '%s' for reading"), filename);
@@ -1056,16 +1094,16 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
                else
                        npos = merge_names->len;
 
-               if (npos - pos < GIT_SHA1_HEXSZ + 2 ||
+               if (npos - pos < hexsz + 2 ||
                    get_oid_hex(merge_names->buf + pos, &oid))
                        commit = NULL; /* bad */
-               else if (memcmp(merge_names->buf + pos + GIT_SHA1_HEXSZ, "\t\t", 2))
+               else if (memcmp(merge_names->buf + pos + hexsz, "\t\t", 2))
                        continue; /* not-for-merge */
                else {
-                       char saved = merge_names->buf[pos + GIT_SHA1_HEXSZ];
-                       merge_names->buf[pos + GIT_SHA1_HEXSZ] = '\0';
+                       char saved = merge_names->buf[pos + hexsz];
+                       merge_names->buf[pos + hexsz] = '\0';
                        commit = get_merge_parent(merge_names->buf + pos);
-                       merge_names->buf[pos + GIT_SHA1_HEXSZ] = saved;
+                       merge_names->buf[pos + hexsz] = saved;
                }
                if (!commit) {
                        if (ptr)
@@ -1152,7 +1190,7 @@ static int merging_a_throwaway_tag(struct commit *commit)
        tag_ref = xstrfmt("refs/tags/%s",
                          ((struct tag *)merge_remote_util(commit)->obj)->tag);
        if (!read_ref(tag_ref, &oid) &&
-           !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))
+           oideq(&oid, &merge_remote_util(commit)->obj->oid))
                is_throwaway_tag = 0;
        else
                is_throwaway_tag = 1;
@@ -1183,14 +1221,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
        if (branch)
                skip_prefix(branch, "refs/heads/", &branch);
+
+       init_diff_ui_defaults();
+       git_config(git_merge_config, NULL);
+
        if (!branch || is_null_oid(&head_oid))
                head_commit = NULL;
        else
                head_commit = lookup_commit_or_die(&head_oid, "HEAD");
 
-       init_diff_ui_defaults();
-       git_config(git_merge_config, NULL);
-
        if (branch_mergeoptions)
                parse_branch_merge_options(branch_mergeoptions);
        argc = parse_options(argc, argv, prefix, builtin_merge_options,
@@ -1209,7 +1248,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        usage_msg_opt(_("--abort expects no arguments"),
                              builtin_merge_usage, builtin_merge_options);
 
-               if (!file_exists(git_path_merge_head()))
+               if (!file_exists(git_path_merge_head(the_repository)))
                        die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
                /* Invoke 'git reset --merge' */
@@ -1225,7 +1264,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        usage_msg_opt(_("--continue expects no arguments"),
                              builtin_merge_usage, builtin_merge_options);
 
-               if (!file_exists(git_path_merge_head()))
+               if (!file_exists(git_path_merge_head(the_repository)))
                        die(_("There is no merge in progress (MERGE_HEAD missing)."));
 
                /* Invoke 'git commit' */
@@ -1236,7 +1275,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (read_cache_unmerged())
                die_resolve_conflict("merge");
 
-       if (file_exists(git_path_merge_head())) {
+       if (file_exists(git_path_merge_head(the_repository))) {
                /*
                 * There is no unmerged entry, don't advise 'git
                 * add/rm <file>', just 'git commit'.
@@ -1247,7 +1286,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                else
                        die(_("You have not concluded your merge (MERGE_HEAD exists)."));
        }
-       if (file_exists(git_path_cherry_pick_head())) {
+       if (file_exists(git_path_cherry_pick_head(the_repository))) {
                if (advice_resolve_conflict)
                        die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
                            "Please, commit your changes before you merge."));
@@ -1297,7 +1336,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                if (remoteheads->next)
                        die(_("Can merge only exactly one commit into empty head"));
                remote_head_oid = &remoteheads->item->object.oid;
-               read_empty(remote_head_oid->hash, 0);
+               read_empty(remote_head_oid, 0);
                update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
                goto done;
@@ -1324,7 +1363,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
                        check_commit_signature(commit, &signature_check);
 
-                       find_unique_abbrev_r(hex, commit->object.oid.hash, DEFAULT_ABBREV);
+                       find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
                        switch (signature_check.result) {
                        case 'G':
                                break;
@@ -1410,16 +1449,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                goto done;
        } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
-                       !oidcmp(&common->item->object.oid, &head_commit->object.oid)) {
+                       oideq(&common->item->object.oid, &head_commit->object.oid)) {
                /* Again the most common case of merging one remote. */
                struct strbuf msg = STRBUF_INIT;
                struct commit *commit;
 
                if (verbosity >= 0) {
                        printf(_("Updating %s..%s\n"),
-                              find_unique_abbrev(head_commit->object.oid.hash,
+                              find_unique_abbrev(&head_commit->object.oid,
                                                  DEFAULT_ABBREV),
-                              find_unique_abbrev(remoteheads->item->object.oid.hash,
+                              find_unique_abbrev(&remoteheads->item->object.oid,
                                                  DEFAULT_ABBREV));
                }
                strbuf_addstr(&msg, "Fast-forward");
@@ -1483,7 +1522,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                         * HEAD^^" would be missed.
                         */
                        common_one = get_merge_bases(head_commit, j->item);
-                       if (oidcmp(&common_one->item->object.oid, &j->item->object.oid)) {
+                       if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) {
                                up_to_date = 0;
                                break;
                        }