Draft release notes to 1.8.0
[gitweb.git] / builtin / merge.c
index 2cef2f6a261377770329fa0fbfc51e5c3dc765c9..0ec8f0d449e505a52166b560f43f30d8c30cfbaf 100644 (file)
@@ -40,9 +40,9 @@ struct strategy {
 };
 
 static const char * const builtin_merge_usage[] = {
-       "git merge [options] [<commit>...]",
-       "git merge [options] <msg> HEAD <commit>",
-       "git merge --abort",
+       N_("git merge [options] [<commit>...]"),
+       N_("git merge [options] <msg> HEAD <commit>"),
+       N_("git merge --abort"),
        NULL
 };
 
@@ -180,39 +180,39 @@ static int option_parse_n(const struct option *opt,
 
 static struct option builtin_merge_options[] = {
        { OPTION_CALLBACK, 'n', NULL, NULL, NULL,
-               "do not show a diffstat at the end of the merge",
+               N_("do not show a diffstat at the end of the merge"),
                PARSE_OPT_NOARG, option_parse_n },
        OPT_BOOLEAN(0, "stat", &show_diffstat,
-               "show a diffstat at the end of the merge"),
-       OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
-       { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
-         "add (at most <n>) entries from shortlog to merge commit message",
+               N_("show a diffstat at the end of the merge")),
+       OPT_BOOLEAN(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
+       { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
+         N_("add (at most <n>) entries from shortlog to merge commit message"),
          PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
        OPT_BOOLEAN(0, "squash", &squash,
-               "create a single commit instead of doing a merge"),
+               N_("create a single commit instead of doing a merge")),
        OPT_BOOLEAN(0, "commit", &option_commit,
-               "perform a commit if the merge succeeds (default)"),
+               N_("perform a commit if the merge succeeds (default)")),
        OPT_BOOL('e', "edit", &option_edit,
-               "edit message before committing"),
+               N_("edit message before committing")),
        OPT_BOOLEAN(0, "ff", &allow_fast_forward,
-               "allow fast-forward (default)"),
+               N_("allow fast-forward (default)")),
        OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
-               "abort if fast-forward is not possible"),
+               N_("abort if fast-forward is not possible")),
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
-       OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
-               "merge strategy to use", option_parse_strategy),
-       OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
-               "option for selected merge strategy", option_parse_x),
-       OPT_CALLBACK('m', "message", &merge_msg, "message",
-               "merge commit message (for a non-fast-forward merge)",
+       OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
+               N_("merge strategy to use"), option_parse_strategy),
+       OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
+               N_("option for selected merge strategy"), option_parse_x),
+       OPT_CALLBACK('m', "message", &merge_msg, N_("message"),
+               N_("merge commit message (for a non-fast-forward merge)"),
                option_parse_message),
        OPT__VERBOSITY(&verbosity),
        OPT_BOOLEAN(0, "abort", &abort_current_merge,
-               "abort the current in-progress merge"),
-       OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
-       { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
-         "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-       OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"),
+               N_("abort the current in-progress merge")),
+       OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
+       { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
+         N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+       OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
        OPT_END()
 };
 
@@ -399,11 +399,12 @@ static void finish(struct commit *head_commit,
        if (new_head && show_diffstat) {
                struct diff_options opts;
                diff_setup(&opts);
+               opts.stat_width = -1; /* use full terminal width */
+               opts.stat_graph_width = -1; /* respect statGraphWidth config */
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
-               if (diff_setup_done(&opts) < 0)
-                       die(_("diff_setup_done failed"));
+               diff_setup_done(&opts);
                diff_tree_sha1(head, new_head, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
@@ -904,12 +905,12 @@ static void prepare_to_commit(struct commit_list *remoteheads)
        write_merge_msg(&msg);
        run_hook(get_index_file(), "prepare-commit-msg",
                 git_path("MERGE_MSG"), "merge", NULL, NULL);
-       if (option_edit) {
+       if (0 < option_edit) {
                if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
                        abort_commit(remoteheads, NULL);
        }
        read_merge_msg(&msg);
-       stripspace(&msg, option_edit);
+       stripspace(&msg, 0 < option_edit);
        if (!msg.len)
                abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
@@ -938,31 +939,22 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 }
 
 static int finish_automerge(struct commit *head,
+                           int head_subsumed,
                            struct commit_list *common,
                            struct commit_list *remoteheads,
                            unsigned char *result_tree,
                            const char *wt_strategy)
 {
-       struct commit_list *parents = NULL, *j;
+       struct commit_list *parents = NULL;
        struct strbuf buf = STRBUF_INIT;
        unsigned char result_commit[20];
 
        free_commit_list(common);
-       if (allow_fast_forward) {
-               parents = remoteheads;
+       parents = remoteheads;
+       if (!head_subsumed || !allow_fast_forward)
                commit_list_insert(head, &parents);
-               parents = reduce_heads(parents);
-       } else {
-               struct commit_list **pptr = &parents;
-
-               pptr = &commit_list_insert(head,
-                               pptr)->next;
-               for (j = remoteheads; j; j = j->next)
-                       pptr = &commit_list_insert(j->item, pptr)->next;
-       }
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
-       free_commit_list(remoteheads);
        if (commit_tree(&merge_msg, result_tree, parents, result_commit,
                        NULL, sign_commit))
                die(_("failed to write commit object"));
@@ -1137,12 +1129,16 @@ static int default_edit_option(void)
                st_stdin.st_mode == st_stdout.st_mode);
 }
 
-static struct commit_list *collect_parents(int argc, const char **argv)
+static struct commit_list *collect_parents(struct commit *head_commit,
+                                          int *head_subsumed,
+                                          int argc, const char **argv)
 {
        int i;
-       struct commit_list *remoteheads = NULL;
+       struct commit_list *remoteheads = NULL, *parents, *next;
        struct commit_list **remotes = &remoteheads;
 
+       if (head_commit)
+               remotes = &commit_list_insert(head_commit, remotes)->next;
        for (i = 0; i < argc; i++) {
                struct commit *commit = get_merge_parent(argv[i]);
                if (!commit)
@@ -1150,6 +1146,20 @@ static struct commit_list *collect_parents(int argc, const char **argv)
                remotes = &commit_list_insert(commit, remotes)->next;
        }
        *remotes = NULL;
+
+       parents = reduce_heads(remoteheads);
+
+       *head_subsumed = 1; /* we will flip this to 0 when we find it */
+       for (remoteheads = NULL, remotes = &remoteheads;
+            parents;
+            parents = next) {
+               struct commit *commit = parents->item;
+               next = parents->next;
+               if (commit == head_commit)
+                       *head_subsumed = 0;
+               else
+                       remotes = &commit_list_insert(commit, remotes)->next;
+       }
        return remoteheads;
 }
 
@@ -1161,7 +1171,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
-       int flag, i, ret = 0;
+       int flag, i, ret = 0, head_subsumed;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
        struct commit_list *common = NULL;
        const char *best_strategy = NULL, *wt_strategy = NULL;
@@ -1270,7 +1280,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                head_arg = argv[1];
                argv += 2;
                argc -= 2;
-               remoteheads = collect_parents(argc, argv);
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
        } else if (!head_commit) {
                struct commit *remote_head;
                /*
@@ -1286,7 +1296,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                if (!allow_fast_forward)
                        die(_("Non-fast-forward commit does not make sense into "
                            "an empty head"));
-               remoteheads = collect_parents(argc, argv);
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
                remote_head = remoteheads->item;
                if (!remote_head)
                        die(_("%s - not something we can merge"), argv[0]);
@@ -1305,7 +1315,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * the standard merge summary message to be appended
                 * to the given message.
                 */
-               remoteheads = collect_parents(argc, argv);
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
                for (p = remoteheads; p; p = p->next)
                        merge_name(merge_remote_util(p->item)->name, &merge_names);
 
@@ -1340,18 +1350,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                if (!fast_forward_only &&
                    merge_remote_util(commit) &&
                    merge_remote_util(commit)->obj &&
-                   merge_remote_util(commit)->obj->type == OBJ_TAG) {
-                       if (option_edit < 0)
-                               option_edit = default_edit_option();
+                   merge_remote_util(commit)->obj->type == OBJ_TAG)
                        allow_fast_forward = 0;
-               }
        }
 
        if (option_edit < 0)
-               option_edit = 0;
+               option_edit = default_edit_option();
 
        if (!use_strategies) {
-               if (!remoteheads->next)
+               if (!remoteheads)
+                       ; /* already up-to-date */
+               else if (!remoteheads->next)
                        add_strategies(pull_twohead, DEFAULT_TWOHEAD);
                else
                        add_strategies(pull_octopus, DEFAULT_OCTOPUS);
@@ -1364,7 +1373,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        allow_trivial = 0;
        }
 
-       if (!remoteheads->next)
+       if (!remoteheads)
+               ; /* already up-to-date */
+       else if (!remoteheads->next)
                common = get_merge_bases(head_commit, remoteheads->item, 1);
        else {
                struct commit_list *list = remoteheads;
@@ -1376,10 +1387,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
                   NULL, 0, DIE_ON_ERR);
 
-       if (!common)
+       if (remoteheads && !common)
                ; /* No common ancestors found. We need a real merge. */
-       else if (!remoteheads->next && !common->next &&
-                       common->item == remoteheads->item) {
+       else if (!remoteheads ||
+                (!remoteheads->next && !common->next &&
+                 common->item == remoteheads->item)) {
                /*
                 * If head can reach all the merge then we are up to date.
                 * but first the most common case of merging one remote.
@@ -1434,7 +1446,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                refresh_cache(REFRESH_QUIET);
                if (allow_trivial && !fast_forward_only) {
                        /* See if it is really trivial. */
-                       git_committer_info(IDENT_ERROR_ON_NO_NAME);
+                       git_committer_info(IDENT_STRICT);
                        printf(_("Trying really trivial in-index merge...\n"));
                        if (!read_tree_trivial(common->item->object.sha1,
                                               head_commit->object.sha1,
@@ -1477,7 +1489,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                die(_("Not possible to fast-forward, aborting."));
 
        /* We are going to make a new commit. */
-       git_committer_info(IDENT_ERROR_ON_NO_NAME);
+       git_committer_info(IDENT_STRICT);
 
        /*
         * At this point, we need a real merge.  No matter what strategy
@@ -1553,7 +1565,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * auto resolved the merge cleanly.
         */
        if (automerge_was_ok) {
-               ret = finish_automerge(head_commit, common, remoteheads,
+               ret = finish_automerge(head_commit, head_subsumed,
+                                      common, remoteheads,
                                       result_tree, wt_strategy);
                goto done;
        }