diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 63a3fc09548abe..22783651b6209f 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -27,6 +27,11 @@ they run `git merge`. To make it easier to adjust such scripts to the updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be set to `no` at the beginning of them. +--cleanup=:: + This option determines how the merge message will be cleaned up + before commiting or being passed on. See linkgit:git-commit[1] for more + details. + --ff:: When the merge resolves as a fast-forward, only update the branch pointer, without creating a merge commit. This is the default diff --git a/builtin/merge.c b/builtin/merge.c index 8f4a5065c209b5..95bfb8c3c486e0 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -36,6 +36,7 @@ #include "packfile.h" #include "tag.h" #include "alias.h" +#include "wt-status.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -96,6 +97,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) { @@ -243,6 +247,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_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")), 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"), @@ -606,6 +611,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")) { @@ -790,23 +797,32 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg) exit(1); } +static const char comment_line_explanation[] = +N_("Lines starting with '%c' will be ignored.\n"); + 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" - "the commit.\n"); + "An empty message aborts 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 (0 < option_edit) { + strbuf_addch(&msg, '\n'); + if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) + wt_status_append_cut_line(&msg); + else + strbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char); + + strbuf_commented_addf(&msg, "\n"); + strbuf_commented_addf(&msg, _(merge_editor_comment)); + } if (signoff) append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0); write_merge_heads(remoteheads); @@ -825,7 +841,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); @@ -873,7 +889,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)) @@ -1402,6 +1417,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (option_edit < 0) option_edit = default_edit_option(); + cleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit); + if (!use_strategies) { if (!remoteheads) ; /* already up-to-date */ diff --git a/builtin/pull.c b/builtin/pull.c index 681c127a07071c..88245bce0ec3fe 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -95,6 +95,7 @@ static char *opt_signoff; static char *opt_squash; static char *opt_commit; static char *opt_edit; +static char *opt_cleanup; static char *opt_ff; static char *opt_verify_signatures; static int opt_autostash = -1; @@ -162,6 +163,9 @@ static struct option pull_options[] = { OPT_PASSTHRU(0, "edit", &opt_edit, NULL, N_("edit message before committing"), PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "cleanup", &opt_cleanup, NULL, + N_("how to strip spaces and #comments from message"), + PARSE_OPT_NOARG), OPT_PASSTHRU(0, "ff", &opt_ff, NULL, N_("allow fast-forward"), PARSE_OPT_NOARG), @@ -625,6 +629,8 @@ static int run_merge(void) argv_array_push(&args, opt_commit); if (opt_edit) argv_array_push(&args, opt_edit); + if (opt_cleanup) + argv_array_push(&args, opt_cleanup); if (opt_ff) argv_array_push(&args, opt_ff); if (opt_verify_signatures) diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh index 89619cf4460828..3fcd58eb2c27a8 100755 --- a/t/t7604-merge-custom-message.sh +++ b/t/t7604-merge-custom-message.sh @@ -47,4 +47,65 @@ test_expect_success 'merge --log appends to custom message' ' test_cmp exp.log actual ' +mesg_with_comment_and_newlines=' +# text + +' + +test_expect_success 'prepare file with comment line and trailing newlines' ' + printf "%s" "$mesg_with_comment_and_newlines" >expect +' + +test_expect_success 'cleanup commit messages (verbatim option)' ' + git reset --hard c1 && + git merge --cleanup=verbatim -F expect c2 && + git cat-file -p HEAD |sed -e "1,/^\$/d" >actual && + test_cmp expect actual +' + +test_expect_success 'cleanup commit messages (whitespace option)' ' + git reset --hard c1 && + { echo;echo "# text";echo; } >text && + echo "# text" >expect && + git merge --cleanup=whitespace -F text c2 && + git cat-file -p HEAD |sed -e "1,/^\$/d">actual && + test_cmp expect actual + +' + +test_expect_success 'cleanup merge messages (scissors option)' ' + git reset --hard c1 && + cat >text <8 ------------------------ +# to be kept, too +# ------------------------ >8 ------------------------ +to be removed +# ------------------------ >8 ------------------------ +to be removed, too +EOF + + cat >expect <8 ------------------------ +# to be kept, too +EOF + git merge --cleanup=scissors -e -F text c2 && + git cat-file -p HEAD |sed -e "1,/^\$/d">actual && + test_cmp expect actual +' + +test_expect_success 'cleanup commit messages (strip option)' ' + git reset --hard c1 && + { echo;echo "# text";echo sample;echo; } >text && + echo sample >expect && + git merge --cleanup=strip -F text c2 && + git cat-file -p HEAD |sed -e "1,/^\$/d">actual && + test_cmp expect actual + +' + test_done diff --git a/wt-status.c b/wt-status.c index 5ffab61015da90..1cf4b8ebe7c07a 100644 --- a/wt-status.c +++ b/wt-status.c @@ -964,13 +964,19 @@ size_t wt_status_locate_end(const char *s, size_t len) return len; } -void wt_status_add_cut_line(FILE *fp) +void wt_status_append_cut_line(struct strbuf *buf) { const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored."); + + strbuf_commented_addf(buf, "%s", cut_line); + strbuf_add_commented_lines(buf, explanation, strlen(explanation)); +} + +void wt_status_add_cut_line(FILE *fp) +{ struct strbuf buf = STRBUF_INIT; - fprintf(fp, "%c %s", comment_line_char, cut_line); - strbuf_add_commented_lines(&buf, explanation, strlen(explanation)); + wt_status_append_cut_line(&buf); fputs(buf.buf, fp); strbuf_release(&buf); } diff --git a/wt-status.h b/wt-status.h index 1673d146fa2f1e..cc5e3625ced6d1 100644 --- a/wt-status.h +++ b/wt-status.h @@ -126,6 +126,7 @@ struct wt_status_state { }; size_t wt_status_locate_end(const char *s, size_t len); +void wt_status_append_cut_line(struct strbuf *buf); void wt_status_add_cut_line(FILE *fp); void wt_status_prepare(struct wt_status *s); void wt_status_print(struct wt_status *s);