implement fetching of moved submodules
[gitweb.git] / builtin / merge.c
index 703827f00668ca0df807f35e300f0927d00075cd..ab5ffe85e8f5d7a359004f2bbb52a5778d0b91b4 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include "cache.h"
+#include "config.h"
 #include "parse-options.h"
 #include "builtin.h"
 #include "lockfile.h"
@@ -31,6 +32,7 @@
 #include "gpg-interface.h"
 #include "sequencer.h"
 #include "string-list.h"
+#include "packfile.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -69,7 +71,9 @@ static int continue_current_merge;
 static int allow_unrelated_histories;
 static int show_progress = -1;
 static int default_to_upstream = 1;
+static int signoff;
 static const char *sign_commit;
+static int verify_msg = 1;
 
 static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -232,6 +236,8 @@ static struct option builtin_merge_options[] = {
        { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
        OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
+       OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
+       OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")),
        OPT_END()
 };
 
@@ -249,6 +255,7 @@ static int save_state(struct object_id *stash)
        struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf buffer = STRBUF_INIT;
        const char *argv[] = {"stash", "create", NULL};
+       int rc = -1;
 
        cp.argv = argv;
        cp.out = -1;
@@ -262,11 +269,14 @@ static int save_state(struct object_id *stash)
        if (finish_command(&cp) || len < 0)
                die(_("stash failed"));
        else if (!len)          /* no changes */
-               return -1;
+               goto out;
        strbuf_setlen(&buffer, buffer.len-1);
        if (get_oid(buffer.buf, stash))
                die(_("not a valid object: %s"), buffer.buf);
-       return 0;
+       rc = 0;
+out:
+       strbuf_release(&buffer);
+       return rc;
 }
 
 static void read_empty(unsigned const char *sha1, int verbose)
@@ -415,7 +425,7 @@ static void finish(struct commit *head_commit,
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
                diff_setup_done(&opts);
-               diff_tree_sha1(head->hash, new_head->hash, "", &opts);
+               diff_tree_oid(head, new_head, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
        }
@@ -536,7 +546,7 @@ static void parse_branch_merge_options(char *bmo)
                die(_("Bad branch.%s.mergeoptions string: %s"), branch,
                    split_cmdline_strerror(argc));
        REALLOC_ARRAY(argv, argc + 2);
-       memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+       MOVE_ARRAY(argv + 1, argv, argc + 1);
        argc++;
        argv[0] = "branch.*.mergeoptions";
        parse_options(argc, argv, NULL, builtin_merge_options,
@@ -565,7 +575,7 @@ static int git_merge_config(const char *k, const char *v, void *cb)
        else if (!strcmp(k, "merge.renormalize"))
                option_renormalize = git_config_bool(k, v);
        else if (!strcmp(k, "merge.ff")) {
-               int boolval = git_config_maybe_bool(k, v);
+               int boolval = git_parse_maybe_bool(v);
                if (0 <= boolval) {
                        fast_forward = boolval ? FF_ALLOW : FF_NO;
                } else if (v && !strcmp(v, "only")) {
@@ -605,13 +615,13 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
        opts.verbose_update = 1;
        opts.trivial_merges_only = 1;
        opts.merge = 1;
-       trees[nr_trees] = parse_tree_indirect(common->hash);
+       trees[nr_trees] = parse_tree_indirect(common);
        if (!trees[nr_trees++])
                return -1;
-       trees[nr_trees] = parse_tree_indirect(head->hash);
+       trees[nr_trees] = parse_tree_indirect(head);
        if (!trees[nr_trees++])
                return -1;
-       trees[nr_trees] = parse_tree_indirect(one->hash);
+       trees[nr_trees] = parse_tree_indirect(one);
        if (!trees[nr_trees++])
                return -1;
        opts.fn = threeway_merge;
@@ -755,13 +765,19 @@ N_("Please enter a commit message to explain why this merge is necessary,\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 *);
 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 (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);
        if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
                            git_path_merge_msg(), "merge", NULL))
@@ -770,6 +786,12 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                if (launch_editor(git_path_merge_msg(), 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))
+               abort_commit(remoteheads, NULL);
+
        read_merge_msg(&msg);
        strbuf_stripspace(&msg, 0 < option_edit);
        if (!msg.len)
@@ -839,9 +861,7 @@ static int suggest_conflicts(void)
        struct strbuf msgbuf = STRBUF_INIT;
 
        filename = git_path_merge_msg();
-       fp = fopen(filename, "a");
-       if (!fp)
-               die_errno(_("Could not open '%s' for writing"), filename);
+       fp = xfopen(filename, "a");
 
        append_conflicts_hint(&msgbuf);
        fputs(msgbuf.buf, fp);
@@ -905,7 +925,7 @@ static int setup_with_upstream(const char ***argv)
        return i;
 }
 
-static void write_merge_state(struct commit_list *remoteheads)
+static void write_merge_heads(struct commit_list *remoteheads)
 {
        struct commit_list *j;
        struct strbuf buf = STRBUF_INIT;
@@ -921,13 +941,19 @@ static void write_merge_state(struct commit_list *remoteheads)
                strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
        }
        write_file_buf(git_path_merge_head(), buf.buf, buf.len);
-       strbuf_addch(&merge_msg, '\n');
-       write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.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);
+       strbuf_release(&buf);
+}
+
+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);
 }
 
 static int default_edit_option(void)
@@ -941,7 +967,7 @@ static int default_edit_option(void)
                return 0;
 
        if (e) {
-               int v = git_config_maybe_bool(name, e);
+               int v = git_parse_maybe_bool(e);
                if (v < 0)
                        die(_("Bad value '%s' in environment '%s'"), e, name);
                return v;
@@ -1118,12 +1144,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * current branch.
         */
        branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
-       if (branch && starts_with(branch, "refs/heads/"))
-               branch += 11;
+       if (branch)
+               skip_prefix(branch, "refs/heads/", &branch);
        if (!branch || is_null_oid(&head_oid))
                head_commit = NULL;
        else
-               head_commit = lookup_commit_or_die(head_oid.hash, "HEAD");
+               head_commit = lookup_commit_or_die(&head_oid, "HEAD");
 
        init_diff_ui_defaults();
        git_config(git_merge_config, NULL);
@@ -1346,7 +1372,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * If head can reach all the merge then we are up to date.
                 * but first the most common case of merging one remote.
                 */
-               finish_up_to_date(_("Already up-to-date."));
+               finish_up_to_date(_("Already up to date."));
                goto done;
        } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
@@ -1372,8 +1398,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               if (checkout_fast_forward(head_commit->object.oid.hash,
-                                         commit->object.oid.hash,
+               if (checkout_fast_forward(&head_commit->object.oid,
+                                         &commit->object.oid,
                                          overwrite_ignore)) {
                        ret = 1;
                        goto done;
@@ -1429,7 +1455,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        }
                }
                if (up_to_date) {
-                       finish_up_to_date(_("Already up-to-date. Yeeah!"));
+                       finish_up_to_date(_("Already up to date. Yeeah!"));
                        goto done;
                }
        }