diff options
Diffstat (limited to 'builtin/merge.c')
-rw-r--r-- | builtin/merge.c | 102 |
1 files changed, 75 insertions, 27 deletions
diff --git a/builtin/merge.c b/builtin/merge.c index e47d77baee..6e99aead46 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -37,7 +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) @@ -57,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; @@ -72,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; @@ -98,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) { @@ -113,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"); @@ -246,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"), @@ -262,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, @@ -279,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; @@ -380,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) @@ -609,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")) { @@ -797,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 *); @@ -806,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); @@ -829,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); @@ -858,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; } @@ -877,7 +896,6 @@ static int finish_automerge(struct commit *head, 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)) @@ -885,7 +903,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; } @@ -898,7 +916,15 @@ static int suggest_conflicts(void) filename = git_path_merge_msg(the_repository); fp = xfopen(filename, "a"); - append_conflicts_hint(&the_index, &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); @@ -1259,6 +1285,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}; @@ -1298,15 +1334,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); @@ -1383,9 +1434,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 */ @@ -1463,7 +1511,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) ; |