]> git.ipfire.org Git - thirdparty/git.git/blobdiff - sequencer.c
credential: correct order of parameters for credential_match
[thirdparty/git.git] / sequencer.c
index 9d5964fd81fe09985c9283c49ad83f98b055d661..e528225e787f4e26de604807efc798e8b967010e 100644 (file)
@@ -57,6 +57,8 @@ static GIT_PATH_FUNC(rebase_path, "rebase-merge")
 GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
 GIT_PATH_FUNC(rebase_path_todo_backup, "rebase-merge/git-rebase-todo.backup")
 
+GIT_PATH_FUNC(rebase_path_dropped, "rebase-merge/dropped")
+
 /*
  * The rebase command lines that have already been processed. A line
  * is moved here when it is first handled, before any associated user
@@ -131,7 +133,7 @@ static GIT_PATH_FUNC(rebase_path_rewritten_pending,
        "rebase-merge/rewritten-pending")
 
 /*
- * The path of the file containig the OID of the "squash onto" commit, i.e.
+ * The path of the file containing the OID of the "squash onto" commit, i.e.
  * the dummy commit used for `reset [new root]`.
  */
 static GIT_PATH_FUNC(rebase_path_squash_onto, "rebase-merge/squash-onto")
@@ -158,6 +160,8 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
 static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
 static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
 static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec")
+static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, "rebase-merge/drop_redundant_commits")
+static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, "rebase-merge/keep_redundant_commits")
 
 static int git_sequencer_config(const char *k, const char *v, void *cb)
 {
@@ -288,7 +292,7 @@ int sequencer_remove_state(struct replay_opts *opts)
                        char *eol = strchr(p, '\n');
                        if (eol)
                                *eol = '\0';
-                       if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
+                       if (delete_ref("(rebase) cleanup", p, NULL, 0) < 0) {
                                warning(_("could not delete '%s'"), p);
                                ret = -1;
                        }
@@ -322,7 +326,7 @@ static const char *action_name(const struct replay_opts *opts)
        case REPLAY_PICK:
                return N_("cherry-pick");
        case REPLAY_INTERACTIVE_REBASE:
-               return N_("rebase -i");
+               return N_("rebase");
        }
        die(_("unknown action: %d"), opts->action);
 }
@@ -588,7 +592,7 @@ static int do_recursive_merge(struct repository *r,
        struct merge_options o;
        struct tree *next_tree, *base_tree, *head_tree;
        int clean;
-       char **xopt;
+       int i;
        struct lock_file index_lock = LOCK_INIT;
 
        if (repo_hold_locked_index(r, &index_lock, LOCK_REPORT_ON_ERROR) < 0)
@@ -608,8 +612,8 @@ static int do_recursive_merge(struct repository *r,
        next_tree = next ? get_commit_tree(next) : empty_tree(r);
        base_tree = base ? get_commit_tree(base) : empty_tree(r);
 
-       for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
-               parse_merge_opt(&o, *xopt);
+       for (i = 0; i < opts->xopts_nr; i++)
+               parse_merge_opt(&o, opts->xopts[i]);
 
        clean = merge_trees(&o,
                            head_tree,
@@ -626,7 +630,7 @@ static int do_recursive_merge(struct repository *r,
                               COMMIT_LOCK | SKIP_IF_UNCHANGED))
                /*
                 * TRANSLATORS: %s will be "revert", "cherry-pick" or
-                * "rebase -i".
+                * "rebase".
                 */
                return error(_("%s: Unable to write new index file"),
                        _(action_name(opts)));
@@ -1126,25 +1130,22 @@ static int run_prepare_commit_msg_hook(struct repository *r,
                                       struct strbuf *msg,
                                       const char *commit)
 {
-       struct argv_array hook_env = ARGV_ARRAY_INIT;
-       int ret;
-       const char *name;
+       int ret = 0;
+       const char *name, *arg1 = NULL, *arg2 = NULL;
 
        name = git_path_commit_editmsg();
        if (write_message(msg->buf, msg->len, name, 0))
                return -1;
 
-       argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", r->index_file);
-       argv_array_push(&hook_env, "GIT_EDITOR=:");
-       if (commit)
-               ret = run_hook_le(hook_env.argv, "prepare-commit-msg", name,
-                                 "commit", commit, NULL);
-       else
-               ret = run_hook_le(hook_env.argv, "prepare-commit-msg", name,
-                                 "message", NULL);
-       if (ret)
+       if (commit) {
+               arg1 = "commit";
+               arg2 = commit;
+       } else {
+               arg1 = "message";
+       }
+       if (run_commit_hook(0, r->index_file, "prepare-commit-msg", name,
+                           arg1, arg2, NULL))
                ret = error(_("'prepare-commit-msg' hook failed"));
-       argv_array_clear(&hook_env);
 
        return ret;
 }
@@ -1353,11 +1354,27 @@ static int try_to_commit(struct repository *r,
                goto out;
        }
 
-       if (!(flags & ALLOW_EMPTY) && oideq(current_head ?
-                                           get_commit_tree_oid(current_head) :
-                                           the_hash_algo->empty_tree, &tree)) {
-               res = 1; /* run 'git commit' to display error message */
-               goto out;
+       if (!(flags & ALLOW_EMPTY)) {
+               struct commit *first_parent = current_head;
+
+               if (flags & AMEND_MSG) {
+                       if (current_head->parents) {
+                               first_parent = current_head->parents->item;
+                               if (repo_parse_commit(r, first_parent)) {
+                                       res = error(_("could not parse HEAD commit"));
+                                       goto out;
+                               }
+                       } else {
+                               first_parent = NULL;
+                       }
+               }
+               if (oideq(first_parent
+                         ? get_commit_tree_oid(first_parent)
+                         : the_hash_algo->empty_tree,
+                         &tree)) {
+                       res = 1; /* run 'git commit' to display error message */
+                       goto out;
+               }
        }
 
        if (find_hook("prepare-commit-msg")) {
@@ -1403,6 +1420,7 @@ static int try_to_commit(struct repository *r,
                goto out;
        }
 
+       run_commit_hook(0, r->index_file, "post-commit", NULL);
        if (flags & AMEND_MSG)
                commit_post_rewrite(r, current_head, oid);
 
@@ -1469,23 +1487,30 @@ static int is_original_commit_empty(struct commit *commit)
 }
 
 /*
- * Do we run "git commit" with "--allow-empty"?
+ * Should empty commits be allowed?  Return status:
+ *    <0: Error in is_index_unchanged(r) or is_original_commit_empty(commit)
+ *     0: Halt on empty commit
+ *     1: Allow empty commit
+ *     2: Drop empty commit
  */
 static int allow_empty(struct repository *r,
                       struct replay_opts *opts,
                       struct commit *commit)
 {
-       int index_unchanged, empty_commit;
+       int index_unchanged, originally_empty;
 
        /*
-        * Three cases:
+        * Four cases:
         *
         * (1) we do not allow empty at all and error out.
         *
-        * (2) we allow ones that were initially empty, but
-        * forbid the ones that become empty;
+        * (2) we allow ones that were initially empty, and
+        *     just drop the ones that become empty
+        *
+        * (3) we allow ones that were initially empty, but
+        *     halt for the ones that become empty;
         *
-        * (3) we allow both.
+        * (4) we allow both.
         */
        if (!opts->allow_empty)
                return 0; /* let "git commit" barf as necessary */
@@ -1499,13 +1524,15 @@ static int allow_empty(struct repository *r,
        if (opts->keep_redundant_commits)
                return 1;
 
-       empty_commit = is_original_commit_empty(commit);
-       if (empty_commit < 0)
-               return empty_commit;
-       if (!empty_commit)
-               return 0;
-       else
+       originally_empty = is_original_commit_empty(commit);
+       if (originally_empty < 0)
+               return originally_empty;
+       if (originally_empty)
                return 1;
+       else if (opts->drop_redundant_commits)
+               return 2;
+       else
+               return 0;
 }
 
 static struct {
@@ -1576,6 +1603,7 @@ static int update_squash_messages(struct repository *r,
        struct strbuf buf = STRBUF_INIT;
        int res;
        const char *message, *body;
+       const char *encoding = get_commit_output_encoding();
 
        if (opts->current_fixup_count > 0) {
                struct strbuf header = STRBUF_INIT;
@@ -1602,7 +1630,7 @@ static int update_squash_messages(struct repository *r,
                        return error(_("need a HEAD to fixup"));
                if (!(head_commit = lookup_commit_reference(r, &head)))
                        return error(_("could not read HEAD"));
-               if (!(head_message = get_commit_buffer(head_commit, NULL)))
+               if (!(head_message = logmsg_reencode(head_commit, NULL, encoding)))
                        return error(_("could not read HEAD's commit message"));
 
                find_commit_subject(head_message, &body);
@@ -1623,7 +1651,7 @@ static int update_squash_messages(struct repository *r,
                unuse_commit_buffer(head_commit, head_message);
        }
 
-       if (!(message = get_commit_buffer(commit, NULL)))
+       if (!(message = logmsg_reencode(commit, NULL, encoding)))
                return error(_("could not read commit message of %s"),
                             oid_to_hex(&commit->object.oid));
        find_commit_subject(message, &body);
@@ -1715,7 +1743,7 @@ static int do_pick_commit(struct repository *r,
        char *author = NULL;
        struct commit_message msg = { NULL, NULL, NULL, NULL };
        struct strbuf msgbuf = STRBUF_INIT;
-       int res, unborn = 0, reword = 0, allow;
+       int res, unborn = 0, reword = 0, allow, drop_commit;
 
        if (opts->no_commit) {
                /*
@@ -1920,13 +1948,22 @@ static int do_pick_commit(struct repository *r,
                goto leave;
        }
 
+       drop_commit = 0;
        allow = allow_empty(r, opts, commit);
        if (allow < 0) {
                res = allow;
                goto leave;
-       } else if (allow)
+       } else if (allow == 1) {
                flags |= ALLOW_EMPTY;
-       if (!opts->no_commit) {
+       } else if (allow == 2) {
+               drop_commit = 1;
+               unlink(git_path_cherry_pick_head(r));
+               unlink(git_path_merge_msg(r));
+               fprintf(stderr,
+                       _("dropping %s %s -- patch contents already upstream\n"),
+                       oid_to_hex(&commit->object.oid), msg.subject);
+       } /* else allow == 0 and there's nothing special to do */
+       if (!opts->no_commit && !drop_commit) {
                if (author || command == TODO_REVERT || (flags & AMEND_MSG))
                        res = do_commit(r, msg_file, author, opts, flags);
                else
@@ -2008,6 +2045,7 @@ void todo_list_release(struct todo_list *todo_list)
 static struct todo_item *append_new_todo(struct todo_list *todo_list)
 {
        ALLOC_GROW(todo_list->items, todo_list->nr + 1, todo_list->alloc);
+       todo_list->total_nr++;
        return todo_list->items + todo_list->nr++;
 }
 
@@ -2102,6 +2140,8 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        saved = *end_of_object_name;
        *end_of_object_name = '\0';
        status = get_oid(bol, &commit_oid);
+       if (status < 0)
+               error(_("could not parse '%s'"), bol); /* return later */
        *end_of_object_name = saved;
 
        bol = end_of_object_name + strspn(end_of_object_name, " \t");
@@ -2109,11 +2149,10 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        item->arg_len = (int)(eol - bol);
 
        if (status < 0)
-               return error(_("could not parse '%.*s'"),
-                            (int)(end_of_object_name - bol), bol);
+               return status;
 
        item->commit = lookup_commit_reference(r, &commit_oid);
-       return !item->commit;
+       return item->commit ? 0 : -1;
 }
 
 int sequencer_get_last_command(struct repository *r, enum replay_action *action)
@@ -2279,6 +2318,16 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
        sequencer_remove_state(&opts);
 }
 
+static void todo_list_write_total_nr(struct todo_list *todo_list)
+{
+       FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
+
+       if (f) {
+               fprintf(f, "%d\n", todo_list->total_nr);
+               fclose(f);
+       }
+}
+
 static int read_populate_todo(struct repository *r,
                              struct todo_list *todo_list,
                              struct replay_opts *opts)
@@ -2324,7 +2373,6 @@ static int read_populate_todo(struct repository *r,
 
        if (is_rebase_i(opts)) {
                struct todo_list done = TODO_LIST_INIT;
-               FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
 
                if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
                    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
@@ -2336,10 +2384,7 @@ static int read_populate_todo(struct repository *r,
                        + count_commands(todo_list);
                todo_list_release(&done);
 
-               if (f) {
-                       fprintf(f, "%d\n", todo_list->total_nr);
-                       fclose(f);
-               }
+               todo_list_write_total_nr(todo_list);
        }
 
        return 0;
@@ -2473,6 +2518,12 @@ static int read_populate_opts(struct replay_opts *opts)
                if (file_exists(rebase_path_reschedule_failed_exec()))
                        opts->reschedule_failed_exec = 1;
 
+               if (file_exists(rebase_path_drop_redundant_commits()))
+                       opts->drop_redundant_commits = 1;
+
+               if (file_exists(rebase_path_keep_redundant_commits()))
+                       opts->keep_redundant_commits = 1;
+
                read_strategy_opts(opts, &buf);
                strbuf_release(&buf);
 
@@ -2524,8 +2575,6 @@ static void write_strategy_opts(struct replay_opts *opts)
 int write_basic_state(struct replay_opts *opts, const char *head_name,
                      struct commit *onto, const char *orig_head)
 {
-       const char *quiet = getenv("GIT_QUIET");
-
        if (head_name)
                write_file(rebase_path_head_name(), "%s\n", head_name);
        if (onto)
@@ -2534,8 +2583,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
        if (orig_head)
                write_file(rebase_path_orig_head(), "%s\n", orig_head);
 
-       if (quiet)
-               write_file(rebase_path_quiet(), "%s\n", quiet);
+       if (opts->quiet)
+               write_file(rebase_path_quiet(), "%s", "");
        if (opts->verbose)
                write_file(rebase_path_verbose(), "%s", "");
        if (opts->strategy)
@@ -2552,6 +2601,10 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
                write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
        if (opts->signoff)
                write_file(rebase_path_signoff(), "--signoff\n");
+       if (opts->drop_redundant_commits)
+               write_file(rebase_path_drop_redundant_commits(), "%s", "");
+       if (opts->keep_redundant_commits)
+               write_file(rebase_path_keep_redundant_commits(), "%s", "");
        if (opts->reschedule_failed_exec)
                write_file(rebase_path_reschedule_failed_exec(), "%s", "");
 
@@ -2564,14 +2617,17 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
        enum todo_command command = opts->action == REPLAY_PICK ?
                TODO_PICK : TODO_REVERT;
        const char *command_string = todo_command_info[command].str;
+       const char *encoding;
        struct commit *commit;
 
        if (prepare_revs(opts))
                return -1;
 
+       encoding = get_log_output_encoding();
+
        while ((commit = get_revision(opts->revs))) {
                struct todo_item *item = append_new_todo(todo_list);
-               const char *commit_buffer = get_commit_buffer(commit, NULL);
+               const char *commit_buffer = logmsg_reencode(commit, NULL, encoding);
                const char *subject;
                int subject_len;
 
@@ -2968,7 +3024,8 @@ static int make_patch(struct repository *r,
 
        strbuf_addf(&buf, "%s/message", get_dir(opts));
        if (!file_exists(buf.buf)) {
-               const char *commit_buffer = get_commit_buffer(commit, NULL);
+               const char *encoding = get_commit_output_encoding();
+               const char *commit_buffer = logmsg_reencode(commit, NULL, encoding);
                find_commit_subject(commit_buffer, &subject);
                res |= write_message(subject, strlen(subject), buf.buf, 1);
                unuse_commit_buffer(commit, commit_buffer);
@@ -3147,7 +3204,7 @@ static int do_label(struct repository *r, const char *name, int len)
                return error(_("illegal label name: '%.*s'"), len, name);
 
        strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
-       strbuf_addf(&msg, "rebase -i (label) '%.*s'", len, name);
+       strbuf_addf(&msg, "rebase (label) '%.*s'", len, name);
 
        transaction = ref_store_transaction_begin(refs, &err);
        if (!transaction) {
@@ -3370,7 +3427,8 @@ static int do_merge(struct repository *r,
        }
 
        if (commit) {
-               const char *message = get_commit_buffer(commit, NULL);
+               const char *encoding = get_commit_output_encoding();
+               const char *message = logmsg_reencode(commit, NULL, encoding);
                const char *body;
                int len;
 
@@ -3688,20 +3746,6 @@ static int run_git_checkout(struct repository *r, struct replay_opts *opts,
        return ret;
 }
 
-int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
-                                const char *commit)
-{
-       const char *action;
-
-       if (commit && *commit) {
-               action = reflog_message(opts, "start", "checkout %s", commit);
-               if (run_git_checkout(r, opts, commit, action))
-                       return error(_("could not checkout %s"), commit);
-       }
-
-       return 0;
-}
-
 static int checkout_onto(struct repository *r, struct replay_opts *opts,
                         const char *onto_name, const struct object_id *onto,
                         const char *orig_head)
@@ -3910,7 +3954,7 @@ static int pick_commits(struct repository *r,
                                                        item->commit,
                                                        arg, item->arg_len,
                                                        opts, res, 0);
-               } else if (check_todo && !res) {
+               } else if (is_rebase_i(opts) && check_todo && !res) {
                        struct stat st;
 
                        if (stat(get_todo_path(opts), &st)) {
@@ -4151,9 +4195,10 @@ static int commit_staged_changes(struct repository *r,
                                 */
                                struct commit *commit;
                                const char *path = rebase_path_squash_msg();
+                               const char *encoding = get_commit_output_encoding();
 
                                if (parse_head(r, &commit) ||
-                                   !(p = get_commit_buffer(commit, NULL)) ||
+                                   !(p = logmsg_reencode(commit, NULL, encoding)) ||
                                    write_message(p, strlen(p), path, 0)) {
                                        unuse_commit_buffer(commit, p);
                                        return error(_("could not write file: "
@@ -4210,8 +4255,18 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
        if (is_rebase_i(opts)) {
                if ((res = read_populate_todo(r, &todo_list, opts)))
                        goto release_todo_list;
-               if (commit_staged_changes(r, opts, &todo_list))
-                       return -1;
+
+               if (file_exists(rebase_path_dropped())) {
+                       if ((res = todo_list_check_against_backup(r, &todo_list)))
+                               goto release_todo_list;
+
+                       unlink(rebase_path_dropped());
+               }
+
+               if (commit_staged_changes(r, opts, &todo_list)) {
+                       res = -1;
+                       goto release_todo_list;
+               }
        } else if (!file_exists(get_todo_path(opts)))
                return continue_single_pick(r);
        else if ((res = read_populate_todo(r, &todo_list, opts)))
@@ -4425,7 +4480,6 @@ static const char *label_oid(struct object_id *oid, const char *label,
        struct labels_entry *labels_entry;
        struct string_entry *string_entry;
        struct object_id dummy;
-       size_t len;
        int i;
 
        string_entry = oidmap_get(&state->commit2label, oid);
@@ -4445,10 +4499,10 @@ static const char *label_oid(struct object_id *oid, const char *label,
         * abbreviation for any uninteresting commit's names that does not
         * clash with any other label.
         */
+       strbuf_reset(&state->buf);
        if (!label) {
                char *p;
 
-               strbuf_reset(&state->buf);
                strbuf_grow(&state->buf, GIT_MAX_HEXSZ);
                label = p = state->buf.buf;
 
@@ -4471,32 +4525,55 @@ static const char *label_oid(struct object_id *oid, const char *label,
                                p[i] = save;
                        }
                }
-       } else if (((len = strlen(label)) == the_hash_algo->hexsz &&
-                   !get_oid_hex(label, &dummy)) ||
-                  (len == 1 && *label == '#') ||
-                  hashmap_get_from_hash(&state->labels,
-                                        strihash(label), label)) {
+       } else {
+               struct strbuf *buf = &state->buf;
+
                /*
-                * If the label already exists, or if the label is a valid full
-                * OID, or the label is a '#' (which we use as a separator
-                * between merge heads and oneline), we append a dash and a
-                * number to make it unique.
+                * Sanitize labels by replacing non-alpha-numeric characters
+                * (including white-space ones) by dashes, as they might be
+                * illegal in file names (and hence in ref names).
+                *
+                * Note that we retain non-ASCII UTF-8 characters (identified
+                * via the most significant bit). They should be all acceptable
+                * in file names. We do not validate the UTF-8 here, that's not
+                * the job of this function.
                 */
-               struct strbuf *buf = &state->buf;
+               for (; *label; label++)
+                       if ((*label & 0x80) || isalnum(*label))
+                               strbuf_addch(buf, *label);
+                       /* avoid leading dash and double-dashes */
+                       else if (buf->len && buf->buf[buf->len - 1] != '-')
+                               strbuf_addch(buf, '-');
+               if (!buf->len) {
+                       strbuf_addstr(buf, "rev-");
+                       strbuf_add_unique_abbrev(buf, oid, default_abbrev);
+               }
+               label = buf->buf;
 
-               strbuf_reset(buf);
-               strbuf_add(buf, label, len);
+               if ((buf->len == the_hash_algo->hexsz &&
+                    !get_oid_hex(label, &dummy)) ||
+                   (buf->len == 1 && *label == '#') ||
+                   hashmap_get_from_hash(&state->labels,
+                                         strihash(label), label)) {
+                       /*
+                        * If the label already exists, or if the label is a
+                        * valid full OID, or the label is a '#' (which we use
+                        * as a separator between merge heads and oneline), we
+                        * append a dash and a number to make it unique.
+                        */
+                       size_t len = buf->len;
 
-               for (i = 2; ; i++) {
-                       strbuf_setlen(buf, len);
-                       strbuf_addf(buf, "-%d", i);
-                       if (!hashmap_get_from_hash(&state->labels,
-                                                  strihash(buf->buf),
-                                                  buf->buf))
-                               break;
-               }
+                       for (i = 2; ; i++) {
+                               strbuf_setlen(buf, len);
+                               strbuf_addf(buf, "-%d", i);
+                               if (!hashmap_get_from_hash(&state->labels,
+                                                          strihash(buf->buf),
+                                                          buf->buf))
+                                       break;
+                       }
 
-               label = buf->buf;
+                       label = buf->buf;
+               }
        }
 
        FLEX_ALLOC_STR(labels_entry, label, label);
@@ -4514,7 +4591,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                                   struct rev_info *revs, struct strbuf *out,
                                   unsigned flags)
 {
-       int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
        int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
        int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
        struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
@@ -4540,10 +4616,15 @@ static int make_script_with_merges(struct pretty_print_context *pp,
        strbuf_init(&state.buf, 32);
 
        if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
+               struct labels_entry *onto_label_entry;
                struct object_id *oid = &revs->cmdline.rev[0].item->oid;
                FLEX_ALLOC_STR(entry, string, "onto");
                oidcpy(&entry->entry.oid, oid);
                oidmap_put(&state.commit2label, entry);
+
+               FLEX_ALLOC_STR(onto_label_entry, label, "onto");
+               hashmap_entry_init(&onto_label_entry->entry, strihash("onto"));
+               hashmap_add(&state.labels, &onto_label_entry->entry);
        }
 
        /*
@@ -4572,8 +4653,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                if (!to_merge) {
                        /* non-merge commit: easy case */
                        strbuf_reset(&buf);
-                       if (!keep_empty && is_empty)
-                               strbuf_addf(&buf, "%c ", comment_line_char);
                        strbuf_addf(&buf, "%s %s %s", cmd_pick,
                                    oid_to_hex(&commit->object.oid),
                                    oneline.buf);
@@ -4598,10 +4677,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                else
                        strbuf_addbuf(&label, &oneline);
 
-               for (p1 = label.buf; *p1; p1++)
-                       if (isspace(*p1))
-                               *(char *)p1 = '-';
-
                strbuf_reset(&buf);
                strbuf_addf(&buf, "%s -C %s",
                            cmd_merge, oid_to_hex(&commit->object.oid));
@@ -4644,7 +4719,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                                label_oid(oid, "branch-point", &state);
                }
 
-               /* Add HEAD as implict "tip of branch" */
+               /* Add HEAD as implicit "tip of branch" */
                if (!iter->next)
                        tips_tail = &commit_list_insert(iter->item,
                                                        tips_tail)->next;
@@ -4744,7 +4819,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
        struct pretty_print_context pp = {0};
        struct rev_info revs;
        struct commit *commit;
-       int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
        const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
        int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 
@@ -4780,12 +4854,10 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
                return make_script_with_merges(&pp, &revs, out, flags);
 
        while ((commit = get_revision(&revs))) {
-               int is_empty  = is_original_commit_empty(commit);
+               int is_empty = is_original_commit_empty(commit);
 
                if (!is_empty && (commit->object.flags & PATCHSAME))
                        continue;
-               if (!keep_empty && is_empty)
-                       strbuf_addf(out, "%c ", comment_line_char);
                strbuf_addf(out, "%s %s ", insn,
                            oid_to_hex(&commit->object.oid));
                pretty_print_commit(&pp, commit, out);
@@ -4826,7 +4898,7 @@ void todo_list_add_exec_commands(struct todo_list *todo_list,
         * are considered part of the pick, so we insert the commands *after*
         * those chains if there are any.
         *
-        * As we insert the exec commands immediatly after rearranging
+        * As we insert the exec commands immediately after rearranging
         * any fixups and before the user edits the list, a fixup chain
         * can never contain comments (any comments are empty picks that
         * have been commented out because the user did not specify
@@ -4922,7 +4994,7 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
 
        todo_list_to_strbuf(r, todo_list, &buf, num, flags);
        if (flags & TODO_LIST_APPEND_TODO_HELP)
-               append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
+               append_todo_help(count_commands(todo_list),
                                 shortrevisions, shortonto, &buf);
 
        res = write_message(buf.buf, buf.len, file, 0);
@@ -4931,41 +5003,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
        return res;
 }
 
-static const char edit_todo_list_advice[] =
-N_("You can fix this with 'git rebase --edit-todo' "
-"and then run 'git rebase --continue'.\n"
-"Or you can abort the rebase with 'git rebase"
-" --abort'.\n");
-
-int check_todo_list_from_file(struct repository *r)
-{
-       struct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;
-       int res = 0;
-
-       if (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {
-               res = -1;
-               goto out;
-       }
-
-       if (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {
-               res = -1;
-               goto out;
-       }
-
-       res = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);
-       if (!res)
-               res = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);
-       if (!res)
-               res = todo_list_check(&old_todo, &new_todo);
-       if (res)
-               fprintf(stderr, _(edit_todo_list_advice));
-out:
-       todo_list_release(&old_todo);
-       todo_list_release(&new_todo);
-
-       return res;
-}
-
 /* skip picking commits whose parents are unchanged */
 static int skip_unnecessary_picks(struct repository *r,
                                  struct todo_list *todo_list,
@@ -5005,6 +5042,7 @@ static int skip_unnecessary_picks(struct repository *r,
                MOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);
                todo_list->nr -= i;
                todo_list->current = 0;
+               todo_list->done_nr += i;
 
                if (is_fixup(peek_command(todo_list, 0)))
                        record_in_rewritten(base_oid, peek_command(todo_list, 0));
@@ -5021,7 +5059,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 {
        const char *shortonto, *todo_file = rebase_path_todo();
        struct todo_list new_todo = TODO_LIST_INIT;
-       struct strbuf *buf = &todo_list->buf;
+       struct strbuf *buf = &todo_list->buf, buf2 = STRBUF_INIT;
        struct object_id oid = onto->object.oid;
        int res;
 
@@ -5062,17 +5100,22 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
                todo_list_release(&new_todo);
 
                return error(_("nothing to do"));
-       }
-
-       if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||
-           todo_list_check(todo_list, &new_todo)) {
-               fprintf(stderr, _(edit_todo_list_advice));
+       } else if (res == -4) {
                checkout_onto(r, opts, onto_name, &onto->object.oid, orig_head);
                todo_list_release(&new_todo);
 
                return -1;
        }
 
+       /* Expand the commit IDs */
+       todo_list_to_strbuf(r, &new_todo, &buf2, -1, 0);
+       strbuf_swap(&new_todo.buf, &buf2);
+       strbuf_release(&buf2);
+       new_todo.total_nr -= new_todo.nr;
+       if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
+               BUG("invalid todo list after expanding IDs:\n%s",
+                   new_todo.buf.buf);
+
        if (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {
                todo_list_release(&new_todo);
                return error(_("could not skip unnecessary pick commands"));
@@ -5084,15 +5127,21 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
                return error_errno(_("could not write '%s'"), todo_file);
        }
 
-       todo_list_release(&new_todo);
+       res = -1;
 
        if (checkout_onto(r, opts, onto_name, &oid, orig_head))
-               return -1;
+               goto cleanup;
 
        if (require_clean_work_tree(r, "rebase", "", 1, 1))
-               return -1;
+               goto cleanup;
 
-       return sequencer_continue(r, opts);
+       todo_list_write_total_nr(&new_todo);
+       res = pick_commits(r, &new_todo, opts);
+
+cleanup:
+       todo_list_release(&new_todo);
+
+       return res;
 }
 
 struct subject2item_entry {
@@ -5169,7 +5218,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
                *commit_todo_item_at(&commit_todo, item->commit) = item;
 
                parse_commit(item->commit);
-               commit_buffer = get_commit_buffer(item->commit, NULL);
+               commit_buffer = logmsg_reencode(item->commit, NULL, "UTF-8");
                find_commit_subject(commit_buffer, &subject);
                format_subject(&buf, subject, " ");
                subject = subjects[i] = strbuf_detach(&buf, &subject_len);