Merge branch 'nd/switch-and-restore'
[gitweb.git] / builtin / merge.c
index 8f4a5065c209b5b50e02271b48277fff90306338..e2ccbc44e204173b09f5ad4b704a31a9e8643bb6 100644 (file)
@@ -6,6 +6,7 @@
  * Based on git-merge.sh by Junio C Hamano.
  */
 
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
 #include "config.h"
 #include "parse-options.h"
@@ -36,6 +37,9 @@
 #include "packfile.h"
 #include "tag.h"
 #include "alias.h"
+#include "branch.h"
+#include "commit-reach.h"
+#include "wt-status.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -55,7 +59,7 @@ static const char * const builtin_merge_usage[] = {
 };
 
 static int show_diffstat = 1, shortlog_len = -1, squash;
-static int option_commit = 1;
+static int option_commit = -1;
 static int option_edit = -1;
 static int allow_trivial = 1, have_message, verify_signatures;
 static int overwrite_ignore = 1;
@@ -70,6 +74,7 @@ static int option_renormalize;
 static int verbosity;
 static int allow_rerere_auto;
 static int abort_current_merge;
+static int quit_current_merge;
 static int continue_current_merge;
 static int allow_unrelated_histories;
 static int show_progress = -1;
@@ -96,6 +101,9 @@ enum ff_type {
 
 static enum ff_type fast_forward = FF_ALLOW;
 
+static const char *cleanup_arg;
+static enum commit_msg_cleanup_mode cleanup_mode;
+
 static int option_parse_message(const struct option *opt,
                                const char *arg, int unset)
 {
@@ -111,12 +119,15 @@ 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)
+static enum parse_opt_result option_read_message(struct parse_opt_ctx_t *ctx,
+                                                const struct option *opt,
+                                                const char *arg_not_used,
+                                                int unset)
 {
        struct strbuf *buf = opt->value;
        const char *arg;
 
+       BUG_ON_OPT_ARG(arg_not_used);
        if (unset)
                BUG("-F cannot be negated");
 
@@ -127,7 +138,7 @@ static int option_read_message(struct parse_opt_ctx_t *ctx,
                ctx->argc--;
                arg = *++ctx->argv;
        } else
-               return opterror(opt, "requires a value", 0);
+               return error(_("option `%s' requires a value"), opt->long_name);
 
        if (buf->len)
                strbuf_addch(buf, '\n');
@@ -223,6 +234,7 @@ static int option_parse_x(const struct option *opt,
 static int option_parse_n(const struct option *opt,
                          const char *arg, int unset)
 {
+       BUG_ON_OPT_ARG(arg);
        show_diffstat = unset;
        return 0;
 }
@@ -243,6 +255,7 @@ static struct option builtin_merge_options[] = {
                N_("perform a commit if the merge succeeds (default)")),
        OPT_BOOL('e', "edit", &option_edit,
                N_("edit message before committing")),
+       OPT_CLEANUP(&cleanup_arg),
        OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
        OPT_SET_INT_F(0, "ff-only", &fast_forward,
                      N_("abort if fast-forward is not possible"),
@@ -259,10 +272,12 @@ static struct option builtin_merge_options[] = {
                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 },
+               NULL, 0, option_read_message },
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
+       OPT_BOOL(0, "quit", &quit_current_merge,
+               N_("--abort but leave index and working tree alone")),
        OPT_BOOL(0, "continue", &continue_current_merge,
                N_("continue the current in-progress merge")),
        OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
@@ -276,14 +291,6 @@ static struct option builtin_merge_options[] = {
        OPT_END()
 };
 
-/* Cleans up metadata that is uninteresting after a succeeded merge. */
-static void drop_save(void)
-{
-       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)
 {
        int len;
@@ -377,7 +384,7 @@ static void finish_up_to_date(const char *msg)
 {
        if (verbosity >= 0)
                printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg);
-       drop_save();
+       remove_merge_branch_state(the_repository);
 }
 
 static void squash_message(struct commit *commit, struct commit_list *remoteheads)
@@ -389,7 +396,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 
        printf(_("Squash commit -- not updating HEAD\n"));
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        rev.ignore_merges = 1;
        rev.commit_format = CMIT_FMT_MEDIUM;
 
@@ -446,13 +453,13 @@ static void finish(struct commit *head_commit,
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
                         */
-                       close_all_packs(the_repository->objects);
+                       close_object_store(the_repository->objects);
                        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
                }
        }
        if (new_head && show_diffstat) {
                struct diff_options opts;
-               diff_setup(&opts);
+               repo_diff_setup(the_repository, &opts);
                opts.stat_width = -1; /* use full terminal width */
                opts.stat_graph_width = -1; /* respect statGraphWidth config */
                opts.output_format |=
@@ -576,7 +583,7 @@ static void parse_branch_merge_options(char *bmo)
        argc = split_cmdline(bmo, &argv);
        if (argc < 0)
                die(_("Bad branch.%s.mergeoptions string: %s"), branch,
-                   split_cmdline_strerror(argc));
+                   _(split_cmdline_strerror(argc)));
        REALLOC_ARRAY(argv, argc + 2);
        MOVE_ARRAY(argv + 1, argv, argc + 1);
        argc++;
@@ -606,6 +613,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                return git_config_string(&pull_twohead, k, v);
        else if (!strcmp(k, "pull.octopus"))
                return git_config_string(&pull_octopus, k, v);
+       else if (!strcmp(k, "commit.cleanup"))
+               return git_config_string(&cleanup_arg, k, v);
        else if (!strcmp(k, "merge.renormalize"))
                option_renormalize = git_config_bool(k, v);
        else if (!strcmp(k, "merge.ff")) {
@@ -700,7 +709,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                        return 2;
                }
 
-               init_merge_options(&o);
+               init_merge_options(&o, the_repository);
                if (!strcmp(strategy, "subtree"))
                        o.subtree_shift = "";
 
@@ -728,8 +737,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                        die(_("unable to write %s"), get_index_file());
                return clean ? 0 : 1;
        } else {
-               return try_merge_command(strategy, xopts_nr, xopts,
-                                               common, head_arg, remoteheads);
+               return try_merge_command(the_repository,
+                                        strategy, xopts_nr, xopts,
+                                        common, head_arg, remoteheads);
        }
 }
 
@@ -793,8 +803,13 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)
 static const char merge_editor_comment[] =
 N_("Please enter a commit message to explain why this merge is necessary,\n"
    "especially if it merges an updated upstream into a topic branch.\n"
-   "\n"
-   "Lines starting with '%c' will be ignored, and an empty message aborts\n"
+   "\n");
+
+static const char scissors_editor_comment[] =
+N_("An empty message aborts the commit.\n");
+
+static const char no_scissors_editor_comment[] =
+N_("Lines starting with '%c' will be ignored, and an empty message aborts\n"
    "the commit.\n");
 
 static void write_merge_heads(struct commit_list *);
@@ -802,11 +817,19 @@ static void prepare_to_commit(struct commit_list *remoteheads)
 {
        struct strbuf msg = STRBUF_INIT;
        strbuf_addbuf(&msg, &merge_msg);
-       strbuf_addch(&msg, '\n');
        if (squash)
                BUG("the control must not reach here under --squash");
-       if (0 < option_edit)
-               strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
+       if (0 < option_edit) {
+               strbuf_addch(&msg, '\n');
+               if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+                       wt_status_append_cut_line(&msg);
+                       strbuf_commented_addf(&msg, "\n");
+               }
+               strbuf_commented_addf(&msg, _(merge_editor_comment));
+               strbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?
+                       scissors_editor_comment :
+                       no_scissors_editor_comment), comment_line_char);
+       }
        if (signoff)
                append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
        write_merge_heads(remoteheads);
@@ -825,7 +848,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                abort_commit(remoteheads, NULL);
 
        read_merge_msg(&msg);
-       strbuf_stripspace(&msg, 0 < option_edit);
+       cleanup_message(&msg, cleanup_mode, 0);
        if (!msg.len)
                abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
@@ -854,7 +877,7 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
                        &result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, &result_commit, "In-index merge");
-       drop_save();
+       remove_merge_branch_state(the_repository);
        return 0;
 }
 
@@ -869,11 +892,11 @@ static int finish_automerge(struct commit *head,
        struct strbuf buf = STRBUF_INIT;
        struct object_id result_commit;
 
+       write_tree_trivial(result_tree);
        free_commit_list(common);
        parents = remoteheads;
        if (!head_subsumed || fast_forward == FF_NO)
                commit_list_insert(head, &parents);
-       strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
        if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
                        &result_commit, NULL, sign_commit))
@@ -881,7 +904,7 @@ static int finish_automerge(struct commit *head,
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
        finish(head, remoteheads, &result_commit, buf.buf);
        strbuf_release(&buf);
-       drop_save();
+       remove_merge_branch_state(the_repository);
        return 0;
 }
 
@@ -894,11 +917,19 @@ static int suggest_conflicts(void)
        filename = git_path_merge_msg(the_repository);
        fp = xfopen(filename, "a");
 
-       append_conflicts_hint(&msgbuf);
+       /*
+        * We can't use cleanup_mode because if we're not using the editor,
+        * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even
+        * though the message is meant to be processed later by git-commit.
+        * Thus, we will get the cleanup mode which is returned when we _are_
+        * using an editor.
+        */
+       append_conflicts_hint(&the_index, &msgbuf,
+                             get_cleanup_mode(cleanup_arg, 1));
        fputs(msgbuf.buf, fp);
        strbuf_release(&msgbuf);
        fclose(fp);
-       rerere(allow_rerere_auto);
+       repo_rerere(the_repository, allow_rerere_auto);
        printf(_("Automatic merge failed; "
                        "fix conflicts and then commit the result.\n"));
        return 1;
@@ -910,7 +941,7 @@ static int evaluate_result(void)
        struct rev_info rev;
 
        /* Check how many files differ. */
-       init_revisions(&rev, "");
+       repo_init_revisions(the_repository, &rev, "");
        setup_revisions(0, NULL, &rev, NULL);
        rev.diffopt.output_format |=
                DIFF_FORMAT_CALLBACK;
@@ -1189,7 +1220,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;
@@ -1255,6 +1286,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                goto done;
        }
 
+       if (quit_current_merge) {
+               if (orig_argc != 2)
+                       usage_msg_opt(_("--quit expects no arguments"),
+                                     builtin_merge_usage,
+                                     builtin_merge_options);
+
+               remove_merge_branch_state(the_repository);
+               goto done;
+       }
+
        if (continue_current_merge) {
                int nargc = 1;
                const char *nargv[] = {"commit", NULL};
@@ -1294,15 +1335,30 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        }
        resolve_undo_clear();
 
+       if (option_edit < 0)
+               option_edit = default_edit_option();
+
+       cleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);
+
        if (verbosity < 0)
                show_diffstat = 0;
 
        if (squash) {
                if (fast_forward == FF_NO)
                        die(_("You cannot combine --squash with --no-ff."));
+               if (option_commit > 0)
+                       die(_("You cannot combine --squash with --commit."));
+               /*
+                * squash can now silently disable option_commit - this is not
+                * a problem as it is only overriding the default, not a user
+                * supplied option.
+                */
                option_commit = 0;
        }
 
+       if (option_commit < 0)
+               option_commit = 1;
+
        if (!argc) {
                if (default_to_upstream)
                        argc = setup_with_upstream(&argv);
@@ -1334,6 +1390,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        die(_("%s - not something we can merge"), argv[0]);
                if (remoteheads->next)
                        die(_("Can merge only exactly one commit into empty head"));
+
+               if (verify_signatures)
+                       verify_merge_signature(remoteheads->item, verbosity);
+
                remote_head_oid = &remoteheads->item->object.oid;
                read_empty(remote_head_oid, 0);
                update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
@@ -1355,31 +1415,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
        if (verify_signatures) {
                for (p = remoteheads; p; p = p->next) {
-                       struct commit *commit = p->item;
-                       char hex[GIT_MAX_HEXSZ + 1];
-                       struct signature_check signature_check;
-                       memset(&signature_check, 0, sizeof(signature_check));
-
-                       check_commit_signature(commit, &signature_check);
-
-                       find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
-                       switch (signature_check.result) {
-                       case 'G':
-                               break;
-                       case 'U':
-                               die(_("Commit %s has an untrusted GPG signature, "
-                                     "allegedly by %s."), hex, signature_check.signer);
-                       case 'B':
-                               die(_("Commit %s has a bad GPG signature "
-                                     "allegedly by %s."), hex, signature_check.signer);
-                       default: /* 'N' */
-                               die(_("Commit %s does not have a GPG signature."), hex);
-                       }
-                       if (verbosity >= 0 && signature_check.result == 'G')
-                               printf(_("Commit %s has a good GPG signature by %s\n"),
-                                      hex, signature_check.signer);
-
-                       signature_check_clear(&signature_check);
+                       verify_merge_signature(p->item, verbosity);
                }
        }
 
@@ -1399,9 +1435,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        fast_forward = FF_NO;
        }
 
-       if (option_edit < 0)
-               option_edit = default_edit_option();
-
        if (!use_strategies) {
                if (!remoteheads)
                        ; /* already up-to-date */
@@ -1448,7 +1481,7 @@ 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;
@@ -1470,7 +1503,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               if (checkout_fast_forward(&head_commit->object.oid,
+               if (checkout_fast_forward(the_repository,
+                                         &head_commit->object.oid,
                                          &commit->object.oid,
                                          overwrite_ignore)) {
                        ret = 1;
@@ -1478,7 +1512,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
 
                finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
-               drop_save();
+               remove_merge_branch_state(the_repository);
                goto done;
        } else if (!remoteheads->next && common->next)
                ;
@@ -1521,7 +1555,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;
                        }
@@ -1553,8 +1587,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
            save_state(&stash))
                oidclr(&stash);
 
-       for (i = 0; i < use_strategies_nr; i++) {
-               int ret;
+       for (i = 0; !merge_was_ok && i < use_strategies_nr; i++) {
+               int ret, cnt;
                if (i) {
                        printf(_("Rewinding the tree to pristine...\n"));
                        restore_state(&head_commit->object.oid, &stash);
@@ -1571,40 +1605,26 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                ret = try_merge_strategy(use_strategies[i]->name,
                                         common, remoteheads,
                                         head_commit);
-               if (!option_commit && !ret) {
-                       merge_was_ok = 1;
-                       /*
-                        * This is necessary here just to avoid writing
-                        * the tree, but later we will *not* exit with
-                        * status code 1 because merge_was_ok is set.
-                        */
-                       ret = 1;
-               }
-
-               if (ret) {
-                       /*
-                        * The backend exits with 1 when conflicts are
-                        * left to be resolved, with 2 when it does not
-                        * handle the given merge at all.
-                        */
-                       if (ret == 1) {
-                               int cnt = evaluate_result();
-
-                               if (best_cnt <= 0 || cnt <= best_cnt) {
-                                       best_strategy = use_strategies[i]->name;
-                                       best_cnt = cnt;
+               /*
+                * The backend exits with 1 when conflicts are
+                * left to be resolved, with 2 when it does not
+                * handle the given merge at all.
+                */
+               if (ret < 2) {
+                       if (!ret) {
+                               if (option_commit) {
+                                       /* Automerge succeeded. */
+                                       automerge_was_ok = 1;
+                                       break;
                                }
+                               merge_was_ok = 1;
+                       }
+                       cnt = evaluate_result();
+                       if (best_cnt <= 0 || cnt <= best_cnt) {
+                               best_strategy = use_strategies[i]->name;
+                               best_cnt = cnt;
                        }
-                       if (merge_was_ok)
-                               break;
-                       else
-                               continue;
                }
-
-               /* Automerge succeeded. */
-               write_tree_trivial(&result_tree);
-               automerge_was_ok = 1;
-               break;
        }
 
        /*