Merge branch 'pw/rebase-abort-clean-rewritten' into maint
[gitweb.git] / sequencer.c
index 258e5831565cb0782344eeffd2aa71eb161317df..1d206fd22456cce12f7496321df8a47fcb94db9e 100644 (file)
@@ -171,17 +171,22 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)
                if (status)
                        return status;
 
-               if (!strcmp(s, "verbatim"))
+               if (!strcmp(s, "verbatim")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
-               else if (!strcmp(s, "whitespace"))
+                       opts->explicit_cleanup = 1;
+               } else if (!strcmp(s, "whitespace")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
-               else if (!strcmp(s, "strip"))
+                       opts->explicit_cleanup = 1;
+               } else if (!strcmp(s, "strip")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
-               else if (!strcmp(s, "scissors"))
-                       opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
-               else
+                       opts->explicit_cleanup = 1;
+               } else if (!strcmp(s, "scissors")) {
+                       opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
+                       opts->explicit_cleanup = 1;
+               } else {
                        warning(_("invalid commit message cleanup mode '%s'"),
                                  s);
+               }
 
                free((char *)s);
                return status;
@@ -513,11 +518,54 @@ static int fast_forward_to(struct repository *r,
        return 0;
 }
 
+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
+       int use_editor)
+{
+       if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
+               return use_editor ? COMMIT_MSG_CLEANUP_ALL :
+                                   COMMIT_MSG_CLEANUP_SPACE;
+       else if (!strcmp(cleanup_arg, "verbatim"))
+               return COMMIT_MSG_CLEANUP_NONE;
+       else if (!strcmp(cleanup_arg, "whitespace"))
+               return COMMIT_MSG_CLEANUP_SPACE;
+       else if (!strcmp(cleanup_arg, "strip"))
+               return COMMIT_MSG_CLEANUP_ALL;
+       else if (!strcmp(cleanup_arg, "scissors"))
+               return use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+                                   COMMIT_MSG_CLEANUP_SPACE;
+       else
+               die(_("Invalid cleanup mode %s"), cleanup_arg);
+}
+
+/*
+ * NB using int rather than enum cleanup_mode to stop clang's
+ * -Wtautological-constant-out-of-range-compare complaining that the comparison
+ * is always true.
+ */
+static const char *describe_cleanup_mode(int cleanup_mode)
+{
+       static const char *modes[] = { "whitespace",
+                                      "verbatim",
+                                      "scissors",
+                                      "strip" };
+
+       if (cleanup_mode < ARRAY_SIZE(modes))
+               return modes[cleanup_mode];
+
+       BUG("invalid cleanup_mode provided (%d)", cleanup_mode);
+}
+
 void append_conflicts_hint(struct index_state *istate,
-                          struct strbuf *msgbuf)
+       struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)
 {
        int i;
 
+       if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+               strbuf_addch(msgbuf, '\n');
+               wt_status_append_cut_line(msgbuf);
+               strbuf_addch(msgbuf, comment_line_char);
+       }
+
        strbuf_addch(msgbuf, '\n');
        strbuf_commented_addf(msgbuf, "Conflicts:\n");
        for (i = 0; i < istate->cache_nr;) {
@@ -585,7 +633,8 @@ static int do_recursive_merge(struct repository *r,
                        _(action_name(opts)));
 
        if (!clean)
-               append_conflicts_hint(r->index, msgbuf);
+               append_conflicts_hint(r->index, msgbuf,
+                                     opts->default_msg_cleanup);
 
        return !clean;
 }
@@ -904,7 +953,6 @@ static int run_git_commit(struct repository *r,
                          unsigned int flags)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
-       const char *value;
 
        if ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) {
                struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;
@@ -974,7 +1022,7 @@ static int run_git_commit(struct repository *r,
                argv_array_push(&cmd.args, "-e");
        else if (!(flags & CLEANUP_MSG) &&
                 !opts->signoff && !opts->record_origin &&
-                git_config_get_value("commit.cleanup", &value))
+                !opts->explicit_cleanup)
                argv_array_push(&cmd.args, "--cleanup=verbatim");
 
        if ((flags & ALLOW_EMPTY))
@@ -1015,6 +1063,16 @@ static int rest_is_empty(const struct strbuf *sb, int start)
        return 1;
 }
 
+void cleanup_message(struct strbuf *msgbuf,
+       enum commit_msg_cleanup_mode cleanup_mode, int verbose)
+{
+       if (verbose || /* Truncate the message just before the diff, if any. */
+           cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+               strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
+       if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+               strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+}
+
 /*
  * Find out if the message in the strbuf contains only whitespace and
  * Signed-off-by lines.
@@ -1385,8 +1443,13 @@ static int try_to_commit(struct repository *r,
                msg = &commit_msg;
        }
 
-       cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
-                                         opts->default_msg_cleanup;
+       if (flags & CLEANUP_MSG)
+               cleanup = COMMIT_MSG_CLEANUP_ALL;
+       else if ((opts->signoff || opts->record_origin) &&
+                !opts->explicit_cleanup)
+               cleanup = COMMIT_MSG_CLEANUP_SPACE;
+       else
+               cleanup = opts->default_msg_cleanup;
 
        if (cleanup != COMMIT_MSG_CLEANUP_NONE)
                strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
@@ -2101,12 +2164,48 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        item->arg_len = (int)(eol - bol);
 
        if (status < 0)
-               return -1;
+               return error(_("could not parse '%.*s'"),
+                            (int)(end_of_object_name - bol), bol);
 
        item->commit = lookup_commit_reference(r, &commit_oid);
        return !item->commit;
 }
 
+int sequencer_get_last_command(struct repository *r, enum replay_action *action)
+{
+       struct todo_item item;
+       char *eol;
+       const char *todo_file;
+       struct strbuf buf = STRBUF_INIT;
+       int ret = -1;
+
+       todo_file = git_path_todo_file();
+       if (strbuf_read_file(&buf, todo_file, 0) < 0) {
+               if (errno == ENOENT)
+                       return -1;
+               else
+                       return error_errno("unable to open '%s'", todo_file);
+       }
+       eol = strchrnul(buf.buf, '\n');
+       if (buf.buf != eol && eol[-1] == '\r')
+               eol--; /* strip Carriage Return */
+       if (parse_insn_line(r, &item, buf.buf, buf.buf, eol))
+               goto fail;
+       if (item.command == TODO_PICK)
+               *action = REPLAY_PICK;
+       else if (item.command == TODO_REVERT)
+               *action = REPLAY_REVERT;
+       else
+               goto fail;
+
+       ret = 0;
+
+ fail:
+       strbuf_release(&buf);
+
+       return ret;
+}
+
 int todo_list_parse_insn_buffer(struct repository *r, char *buf,
                                struct todo_list *todo_list)
 {
@@ -2190,6 +2289,57 @@ static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path)
        return len;
 }
 
+static int have_finished_the_last_pick(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       const char *eol;
+       const char *todo_path = git_path_todo_file();
+       int ret = 0;
+
+       if (strbuf_read_file(&buf, todo_path, 0) < 0) {
+               if (errno == ENOENT) {
+                       return 0;
+               } else {
+                       error_errno("unable to open '%s'", todo_path);
+                       return 0;
+               }
+       }
+       /* If there is only one line then we are done */
+       eol = strchr(buf.buf, '\n');
+       if (!eol || !eol[1])
+               ret = 1;
+
+       strbuf_release(&buf);
+
+       return ret;
+}
+
+void sequencer_post_commit_cleanup(struct repository *r)
+{
+       struct replay_opts opts = REPLAY_OPTS_INIT;
+       int need_cleanup = 0;
+
+       if (file_exists(git_path_cherry_pick_head(r))) {
+               unlink(git_path_cherry_pick_head(r));
+               opts.action = REPLAY_PICK;
+               need_cleanup = 1;
+       }
+
+       if (file_exists(git_path_revert_head(r))) {
+               unlink(git_path_revert_head(r));
+               opts.action = REPLAY_REVERT;
+               need_cleanup = 1;
+       }
+
+       if (!need_cleanup)
+               return;
+
+       if (!have_finished_the_last_pick())
+               return;
+
+       sequencer_remove_state(&opts);
+}
+
 static int read_populate_todo(struct repository *r,
                              struct todo_list *todo_list,
                              struct replay_opts *opts)
@@ -2277,6 +2427,15 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
                opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
        else if (!strcmp(key, "options.edit"))
                opts->edit = git_config_bool_or_int(key, value, &error_flag);
+       else if (!strcmp(key, "options.allow-empty"))
+               opts->allow_empty =
+                       git_config_bool_or_int(key, value, &error_flag);
+       else if (!strcmp(key, "options.allow-empty-message"))
+               opts->allow_empty_message =
+                       git_config_bool_or_int(key, value, &error_flag);
+       else if (!strcmp(key, "options.keep-redundant-commits"))
+               opts->keep_redundant_commits =
+                       git_config_bool_or_int(key, value, &error_flag);
        else if (!strcmp(key, "options.signoff"))
                opts->signoff = git_config_bool_or_int(key, value, &error_flag);
        else if (!strcmp(key, "options.record-origin"))
@@ -2296,7 +2455,10 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
                opts->allow_rerere_auto =
                        git_config_bool_or_int(key, value, &error_flag) ?
                                RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
-       else
+       else if (!strcmp(key, "options.default-msg-cleanup")) {
+               opts->explicit_cleanup = 1;
+               opts->default_msg_cleanup = get_cleanup_mode(value, 1);
+       } else
                return error(_("invalid key: %s"), key);
 
        if (!error_flag)
@@ -2671,36 +2833,59 @@ static int save_opts(struct replay_opts *opts)
        int res = 0;
 
        if (opts->no_commit)
-               res |= git_config_set_in_file_gently(opts_file, "options.no-commit", "true");
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.no-commit", "true");
        if (opts->edit)
-               res |= git_config_set_in_file_gently(opts_file, "options.edit", "true");
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.edit", "true");
+       if (opts->allow_empty)
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.allow-empty", "true");
+       if (opts->allow_empty_message)
+               res |= git_config_set_in_file_gently(opts_file,
+                               "options.allow-empty-message", "true");
+       if (opts->keep_redundant_commits)
+               res |= git_config_set_in_file_gently(opts_file,
+                               "options.keep-redundant-commits", "true");
        if (opts->signoff)
-               res |= git_config_set_in_file_gently(opts_file, "options.signoff", "true");
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.signoff", "true");
        if (opts->record_origin)
-               res |= git_config_set_in_file_gently(opts_file, "options.record-origin", "true");
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.record-origin", "true");
        if (opts->allow_ff)
-               res |= git_config_set_in_file_gently(opts_file, "options.allow-ff", "true");
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.allow-ff", "true");
        if (opts->mainline) {
                struct strbuf buf = STRBUF_INIT;
                strbuf_addf(&buf, "%d", opts->mainline);
-               res |= git_config_set_in_file_gently(opts_file, "options.mainline", buf.buf);
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.mainline", buf.buf);
                strbuf_release(&buf);
        }
        if (opts->strategy)
-               res |= git_config_set_in_file_gently(opts_file, "options.strategy", opts->strategy);
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.strategy", opts->strategy);
        if (opts->gpg_sign)
-               res |= git_config_set_in_file_gently(opts_file, "options.gpg-sign", opts->gpg_sign);
+               res |= git_config_set_in_file_gently(opts_file,
+                                       "options.gpg-sign", opts->gpg_sign);
        if (opts->xopts) {
                int i;
                for (i = 0; i < opts->xopts_nr; i++)
                        res |= git_config_set_multivar_in_file_gently(opts_file,
-                                                       "options.strategy-option",
-                                                       opts->xopts[i], "^$", 0);
+                                       "options.strategy-option",
+                                       opts->xopts[i], "^$", 0);
        }
        if (opts->allow_rerere_auto)
-               res |= git_config_set_in_file_gently(opts_file, "options.allow-rerere-auto",
-                                                    opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
-                                                    "true" : "false");
+               res |= git_config_set_in_file_gently(opts_file,
+                               "options.allow-rerere-auto",
+                               opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
+                               "true" : "false");
+
+       if (opts->explicit_cleanup)
+               res |= git_config_set_in_file_gently(opts_file,
+                               "options.default-msg-cleanup",
+                               describe_cleanup_mode(opts->default_msg_cleanup));
        return res;
 }
 
@@ -3549,8 +3734,11 @@ static int pick_commits(struct repository *r,
                        unlink(git_path_merge_head(the_repository));
                        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 
-                       if (item->command == TODO_BREAK)
+                       if (item->command == TODO_BREAK) {
+                               if (!opts->verbose)
+                                       term_clear_line();
                                return stopped_at_head(r);
+                       }
                }
                if (item->command <= TODO_SQUASH) {
                        if (is_rebase_i(opts))
@@ -3572,11 +3760,14 @@ static int pick_commits(struct repository *r,
                        }
                        if (item->command == TODO_EDIT) {
                                struct commit *commit = item->commit;
-                               if (!res)
+                               if (!res) {
+                                       if (!opts->verbose)
+                                               term_clear_line();
                                        fprintf(stderr,
                                                _("Stopped at %s...  %.*s\n"),
                                                short_commit_name(commit),
                                                item->arg_len, arg);
+                               }
                                return error_with_patch(r, commit,
                                        arg, item->arg_len, opts, res, !res);
                        }
@@ -3614,11 +3805,12 @@ static int pick_commits(struct repository *r,
                        int saved = *end_of_arg;
                        struct stat st;
 
+                       if (!opts->verbose)
+                               term_clear_line();
                        *end_of_arg = '\0';
                        res = do_exec(r, arg);
                        *end_of_arg = saved;
 
-                       /* Reread the todo file if it has changed. */
                        if (res) {
                                if (opts->reschedule_failed_exec)
                                        reschedule = 1;
@@ -3626,6 +3818,7 @@ static int pick_commits(struct repository *r,
                                res = error_errno(_("could not stat '%s'"),
                                                  get_todo_path(opts));
                        else if (match_stat_data(&todo_list->stat, &st)) {
+                               /* Reread the todo file if it has changed. */
                                todo_list_release(todo_list);
                                if (read_populate_todo(r, todo_list, opts))
                                        res = -1; /* message was printed */
@@ -3772,10 +3965,13 @@ static int pick_commits(struct repository *r,
                }
                apply_autostash(opts);
 
-               if (!opts->quiet)
+               if (!opts->quiet) {
+                       if (!opts->verbose)
+                               term_clear_line();
                        fprintf(stderr,
                                "Successfully rebased and updated %s.\n",
                                head_ref.buf);
+               }
 
                strbuf_release(&buf);
                strbuf_release(&head_ref);