Merge branch 'ma/sequencer-do-reset-saner-loop-termination'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Nov 2018 13:37:21 +0000 (22:37 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Nov 2018 13:37:21 +0000 (22:37 +0900)
Code readability fix.

* ma/sequencer-do-reset-saner-loop-termination:
sequencer: break out of loop explicitly

1  2 
sequencer.c
diff --combined sequencer.c
index 9e1ab3a2a7e3f65cc444cded7f3330bb27be2db8,11c5a65a3209febd42e74d7e1a1070bda36fa663..405d5ef86bfd1bfa5e01c63d7f1afe88e7132feb
@@@ -30,8 -30,6 +30,8 @@@
  #include "oidset.h"
  #include "commit-slab.h"
  #include "alias.h"
 +#include "commit-reach.h"
 +#include "rebase-interactive.h"
  
  #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
  
@@@ -54,10 -52,7 +54,10 @@@ static GIT_PATH_FUNC(rebase_path, "reba
   * the lines are processed, they are removed from the front of this
   * file and written to the tail of 'done'.
   */
 -static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
 +GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
 +static GIT_PATH_FUNC(rebase_path_todo_backup,
 +                   "rebase-merge/git-rebase-todo.backup")
 +
  /*
   * The rebase command lines that have already been processed. A line
   * is moved here when it is first handled, before any associated user
@@@ -145,7 -140,7 +145,7 @@@ static GIT_PATH_FUNC(rebase_path_refs_t
  
  /*
   * The following files are written by git-rebase just after parsing the
 - * command-line (and are only consumed, not modified, by the sequencer).
 + * command-line.
   */
  static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
  static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
@@@ -157,7 -152,6 +157,7 @@@ static GIT_PATH_FUNC(rebase_path_autost
  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_quiet, "rebase-merge/quiet")
  
  static int git_sequencer_config(const char *k, const char *v, void *cb)
  {
@@@ -231,16 -225,13 +231,16 @@@ static const char *get_todo_path(const 
   * Returns 3 when sob exists within conforming footer as last entry
   */
  static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
 -      int ignore_footer)
 +      size_t ignore_footer)
  {
 +      struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
        struct trailer_info info;
 -      int i;
 +      size_t i;
        int found_sob = 0, found_sob_last = 0;
  
 -      trailer_info_get(&info, sb->buf);
 +      opts.no_divider = 1;
 +
 +      trailer_info_get(&info, sb->buf, &opts);
  
        if (info.trailer_start == info.trailer_end)
                return 0;
@@@ -382,8 -373,8 +382,8 @@@ static void print_advice(int show_hint
        }
  }
  
 -static int write_message(const void *buf, size_t len, const char *filename,
 -                       int append_eol)
 +int write_message(const void *buf, size_t len, const char *filename,
 +                int append_eol)
  {
        struct lock_file msg_file = LOCK_INIT;
  
@@@ -479,8 -470,8 +479,8 @@@ static int fast_forward_to(const struc
        struct strbuf sb = STRBUF_INIT;
        struct strbuf err = STRBUF_INIT;
  
 -      read_cache();
 -      if (checkout_fast_forward(from, to, 1))
 +      read_index(&the_index);
 +      if (checkout_fast_forward(the_repository, from, to, 1))
                return -1; /* the callee should have complained already */
  
        strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts)));
@@@ -619,7 -610,7 +619,7 @@@ static int is_index_unchanged(void
        if (!(cache_tree_oid = get_cache_tree_oid()))
                return -1;
  
 -      return !oidcmp(cache_tree_oid, get_commit_tree_oid(head_commit));
 +      return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
  }
  
  static int write_author_script(const char *message)
@@@ -809,23 -800,6 +809,23 @@@ N_("you have staged changes in your wor
  #define VERIFY_MSG  (1<<4)
  #define CREATE_ROOT_COMMIT (1<<5)
  
 +static int run_command_silent_on_success(struct child_process *cmd)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      int rc;
 +
 +      cmd->stdout_to_stderr = 1;
 +      rc = pipe_command(cmd,
 +                        NULL, 0,
 +                        NULL, 0,
 +                        &buf, 0);
 +
 +      if (rc)
 +              fputs(buf.buf, stderr);
 +      strbuf_release(&buf);
 +      return rc;
 +}
 +
  /*
   * If we are cherry-pick, and if the merge did not result in
   * hand-editing, we will hit this commit and inherit the original
@@@ -887,11 -861,18 +887,11 @@@ static int run_git_commit(const char *d
  
        cmd.git_cmd = 1;
  
 -      if (is_rebase_i(opts)) {
 -              if (!(flags & EDIT_MSG)) {
 -                      cmd.stdout_to_stderr = 1;
 -                      cmd.err = -1;
 -              }
 -
 -              if (read_env_script(&cmd.env_array)) {
 -                      const char *gpg_opt = gpg_sign_opt_quoted(opts);
 +      if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) {
 +              const char *gpg_opt = gpg_sign_opt_quoted(opts);
  
 -                      return error(_(staged_changes_advice),
 -                                   gpg_opt, gpg_opt);
 -              }
 +              return error(_(staged_changes_advice),
 +                           gpg_opt, gpg_opt);
        }
  
        argv_array_push(&cmd.args, "commit");
        if ((flags & ALLOW_EMPTY))
                argv_array_push(&cmd.args, "--allow-empty");
  
 -      if (opts->allow_empty_message)
 +      if (!(flags & EDIT_MSG))
                argv_array_push(&cmd.args, "--allow-empty-message");
  
 -      if (cmd.err == -1) {
 -              /* hide stderr on success */
 -              struct strbuf buf = STRBUF_INIT;
 -              int rc = pipe_command(&cmd,
 -                                    NULL, 0,
 -                                    /* stdout is already redirected */
 -                                    NULL, 0,
 -                                    &buf, 0);
 -              if (rc)
 -                      fputs(buf.buf, stderr);
 -              strbuf_release(&buf);
 -              return rc;
 -      }
 -
 -      return run_command(&cmd);
 +      if (is_rebase_i(opts) && !(flags & EDIT_MSG))
 +              return run_command_silent_on_success(&cmd);
 +      else
 +              return run_command(&cmd);
  }
  
  static int rest_is_empty(const struct strbuf *sb, int start)
@@@ -1180,7 -1172,7 +1180,7 @@@ void print_commit_summary(const char *p
        strbuf_release(&author_ident);
        strbuf_release(&committer_ident);
  
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
  
        rev.diff = 1;
@@@ -1225,7 -1217,7 +1225,7 @@@ static int parse_head(struct commit **h
                current_head = lookup_commit_reference(the_repository, &oid);
                if (!current_head)
                        return error(_("could not parse HEAD"));
 -              if (oidcmp(&oid, &current_head->object.oid)) {
 +              if (!oideq(&oid, &current_head->object.oid)) {
                        warning(_("HEAD %s is not a commit!"),
                                oid_to_hex(&oid));
                }
@@@ -1295,9 -1287,9 +1295,9 @@@ static int try_to_commit(struct strbuf 
                goto out;
        }
  
 -      if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
 -                                            get_commit_tree_oid(current_head) :
 -                                            the_hash_algo->empty_tree, &tree)) {
 +      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 (cleanup != COMMIT_MSG_CLEANUP_NONE)
                strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
 -      if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
 +      if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
                res = 1; /* run 'git commit' to display error message */
                goto out;
        }
@@@ -1402,7 -1394,7 +1402,7 @@@ static int is_original_commit_empty(str
                ptree_oid = the_hash_algo->empty_tree; /* commit is root */
        }
  
 -      return !oidcmp(ptree_oid, get_commit_tree_oid(commit));
 +      return oideq(ptree_oid, get_commit_tree_oid(commit));
  }
  
  /*
@@@ -1458,7 -1450,6 +1458,7 @@@ enum todo_command 
        TODO_SQUASH,
        /* commands that do something else than handling a single commit */
        TODO_EXEC,
 +      TODO_BREAK,
        TODO_LABEL,
        TODO_RESET,
        TODO_MERGE,
@@@ -1480,7 -1471,6 +1480,7 @@@ static struct 
        { 'f', "fixup" },
        { 's', "squash" },
        { 'x', "exec" },
 +      { 'b', "break" },
        { 'l', "label" },
        { 't', "reset" },
        { 'm', "merge" },
@@@ -1684,7 -1674,7 +1684,7 @@@ static int do_pick_commit(enum todo_com
                unborn = get_oid("HEAD", &head);
                /* Do we want to generate a root commit? */
                if (is_pick_or_similar(command) && opts->have_squash_onto &&
 -                  !oidcmp(&head, &opts->squash_onto)) {
 +                  oideq(&head, &opts->squash_onto)) {
                        if (is_fixup(command))
                                return error(_("cannot fixup root commit"));
                        flags |= CREATE_ROOT_COMMIT;
                        oid_to_hex(&commit->object.oid));
  
        if (opts->allow_ff && !is_fixup(command) &&
 -          ((parent && !oidcmp(&parent->object.oid, &head)) ||
 +          ((parent && oideq(&parent->object.oid, &head)) ||
             (!parent && unborn))) {
                if (is_rebase_i(opts))
                        write_author_script(msg.message);
  
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
 -              res |= try_merge_command(opts->strategy,
 +              res |= try_merge_command(the_repository, opts->strategy,
                                         opts->xopts_nr, (const char **)opts->xopts,
                                        common, oid_to_hex(&head), remotes);
                free_commit_list(common);
                      : _("could not apply %s... %s"),
                      short_commit_name(commit), msg.subject);
                print_advice(res == 1, opts);
 -              rerere(opts->allow_rerere_auto);
 +              repo_rerere(the_repository, opts->allow_rerere_auto);
                goto leave;
        }
  
@@@ -1919,7 -1909,7 +1919,7 @@@ static int read_and_refresh_cache(struc
  {
        struct lock_file index_lock = LOCK_INIT;
        int index_fd = hold_locked_index(&index_lock, 0);
 -      if (read_index_preload(&the_index, NULL) < 0) {
 +      if (read_index_preload(&the_index, NULL, 0) < 0) {
                rollback_lock_file(&index_lock);
                return error(_("git %s: failed to read the index"),
                        _(action_name(opts)));
@@@ -1994,8 -1984,7 +1994,8 @@@ static int parse_insn_line(struct todo_
                if (skip_prefix(bol, todo_command_info[i].str, &bol)) {
                        item->command = i;
                        break;
 -              } else if (bol[1] == ' ' && *bol == todo_command_info[i].c) {
 +              } else if ((bol + 1 == eol || bol[1] == ' ') &&
 +                         *bol == todo_command_info[i].c) {
                        bol++;
                        item->command = i;
                        break;
        padding = strspn(bol, " \t");
        bol += padding;
  
 -      if (item->command == TODO_NOOP) {
 +      if (item->command == TODO_NOOP || item->command == TODO_BREAK) {
                if (bol != eol)
                        return error(_("%s does not accept arguments: '%s'"),
                                     command_to_string(item->command), bol);
@@@ -2251,14 -2240,21 +2251,14 @@@ static int populate_opts_cb(const char 
        return 0;
  }
  
 -static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
 +void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
  {
        int i;
 -      char *strategy_opts_string;
 -
 -      strbuf_reset(buf);
 -      if (!read_oneliner(buf, rebase_path_strategy(), 0))
 -              return;
 -      opts->strategy = strbuf_detach(buf, NULL);
 -      if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
 -              return;
 +      char *strategy_opts_string = raw_opts;
  
 -      strategy_opts_string = buf->buf;
        if (*strategy_opts_string == ' ')
                strategy_opts_string++;
 +
        opts->xopts_nr = split_cmdline(strategy_opts_string,
                                       (const char ***)&opts->xopts);
        for (i = 0; i < opts->xopts_nr; i++) {
        }
  }
  
 +static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
 +{
 +      strbuf_reset(buf);
 +      if (!read_oneliner(buf, rebase_path_strategy(), 0))
 +              return;
 +      opts->strategy = strbuf_detach(buf, NULL);
 +      if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
 +              return;
 +
 +      parse_strategy_opts(opts, buf->buf);
 +}
 +
  static int read_populate_opts(struct replay_opts *opts)
  {
        if (is_rebase_i(opts)) {
        return 0;
  }
  
 +static void write_strategy_opts(struct replay_opts *opts)
 +{
 +      int i;
 +      struct strbuf buf = STRBUF_INIT;
 +
 +      for (i = 0; i < opts->xopts_nr; ++i)
 +              strbuf_addf(&buf, " --%s", opts->xopts[i]);
 +
 +      write_file(rebase_path_strategy_opts(), "%s\n", buf.buf);
 +      strbuf_release(&buf);
 +}
 +
 +int write_basic_state(struct replay_opts *opts, const char *head_name,
 +                    const char *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)
 +              write_file(rebase_path_onto(), "%s\n", onto);
 +      if (orig_head)
 +              write_file(rebase_path_orig_head(), "%s\n", orig_head);
 +
 +      if (quiet)
 +              write_file(rebase_path_quiet(), "%s\n", quiet);
 +      else
 +              write_file(rebase_path_quiet(), "\n");
 +
 +      if (opts->verbose)
 +              write_file(rebase_path_verbose(), "%s", "");
 +      if (opts->strategy)
 +              write_file(rebase_path_strategy(), "%s\n", opts->strategy);
 +      if (opts->xopts_nr > 0)
 +              write_strategy_opts(opts);
 +
 +      if (opts->allow_rerere_auto == RERERE_AUTOUPDATE)
 +              write_file(rebase_path_allow_rerere_autoupdate(), "--rerere-autoupdate\n");
 +      else if (opts->allow_rerere_auto == RERERE_NOAUTOUPDATE)
 +              write_file(rebase_path_allow_rerere_autoupdate(), "--no-rerere-autoupdate\n");
 +
 +      if (opts->gpg_sign)
 +              write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
 +      if (opts->signoff)
 +              write_file(rebase_path_signoff(), "--signoff\n");
 +
 +      return 0;
 +}
 +
  static int walk_revs_populate_todo(struct todo_list *todo_list,
                                struct replay_opts *opts)
  {
@@@ -2487,7 -2422,7 +2487,7 @@@ static int rollback_is_safe(void
        if (get_oid("HEAD", &actual_head))
                oidclr(&actual_head);
  
 -      return !oidcmp(&actual_head, &expected_head);
 +      return oideq(&actual_head, &expected_head);
  }
  
  static int reset_for_rollback(const struct object_id *oid)
@@@ -2660,7 -2595,7 +2660,7 @@@ static int make_patch(struct commit *co
  
        strbuf_addf(&buf, "%s/patch", get_dir(opts));
        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
 -      init_revisions(&log_tree_opt, NULL);
 +      repo_init_revisions(the_repository, &log_tree_opt, NULL);
        log_tree_opt.abbrev = 0;
        log_tree_opt.diff = 1;
        log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
@@@ -2890,7 -2825,7 +2890,7 @@@ static int do_reset(const char *name, i
        struct tree_desc desc;
        struct tree *tree;
        struct unpack_trees_options unpack_tree_opts;
-       int ret = 0, i;
+       int ret = 0;
  
        if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
                }
                oidcpy(&oid, &opts->squash_onto);
        } else {
+               int i;
                /* Determine the length of the label */
                for (i = 0; i < len; i++)
                        if (isspace(name[i]))
-                               len = i;
+                               break;
+               len = i;
  
                strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
                if (get_oid(ref_name.buf, &oid) &&
@@@ -3048,7 -2986,7 +3051,7 @@@ static int do_merge(struct commit *comm
        }
  
        if (opts->have_squash_onto &&
 -          !oidcmp(&head_commit->object.oid, &opts->squash_onto)) {
 +          oideq(&head_commit->object.oid, &opts->squash_onto)) {
                /*
                 * When the user tells us to "merge" something into a
                 * "[new root]", let's simply fast-forward to the merge head.
         * commit, we cannot fast-forward.
         */
        can_fast_forward = opts->allow_ff && commit && commit->parents &&
 -              !oidcmp(&commit->parents->item->object.oid,
 -                      &head_commit->object.oid);
 +              oideq(&commit->parents->item->object.oid,
 +                    &head_commit->object.oid);
  
        /*
         * If any merge head is different from the original one, we cannot
                struct commit_list *p = commit->parents->next;
  
                for (j = to_merge; j && p; j = j->next, p = p->next)
 -                      if (oidcmp(&j->item->object.oid,
 +                      if (!oideq(&j->item->object.oid,
                                   &p->item->object.oid)) {
                                can_fast_forward = 0;
                                break;
        write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
  
        bases = get_merge_bases(head_commit, merge_commit);
 -      if (bases && !oidcmp(&merge_commit->object.oid,
 -                           &bases->item->object.oid)) {
 +      if (bases && oideq(&merge_commit->object.oid,
 +                         &bases->item->object.oid)) {
                ret = 0;
                /* skip merging an ancestor of HEAD */
                goto leave_merge;
  
        rollback_lock_file(&lock);
        if (ret)
 -              rerere(opts->allow_rerere_auto);
 +              repo_rerere(the_repository, opts->allow_rerere_auto);
        else
                /*
                 * In case of problems, we now want to return a positive
@@@ -3347,73 -3285,6 +3350,73 @@@ static const char *reflog_message(struc
        return buf.buf;
  }
  
 +static int run_git_checkout(struct replay_opts *opts, const char *commit,
 +                          const char *action)
 +{
 +      struct child_process cmd = CHILD_PROCESS_INIT;
 +
 +      cmd.git_cmd = 1;
 +
 +      argv_array_push(&cmd.args, "checkout");
 +      argv_array_push(&cmd.args, commit);
 +      argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
 +
 +      if (opts->verbose)
 +              return run_command(&cmd);
 +      else
 +              return run_command_silent_on_success(&cmd);
 +}
 +
 +int prepare_branch_to_be_rebased(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(opts, commit, action))
 +                      return error(_("could not checkout %s"), commit);
 +      }
 +
 +      return 0;
 +}
 +
 +static int checkout_onto(struct replay_opts *opts,
 +                       const char *onto_name, const char *onto,
 +                       const char *orig_head)
 +{
 +      struct object_id oid;
 +      const char *action = reflog_message(opts, "start", "checkout %s", onto_name);
 +
 +      if (get_oid(orig_head, &oid))
 +              return error(_("%s: not a valid OID"), orig_head);
 +
 +      if (run_git_checkout(opts, onto, action)) {
 +              apply_autostash(opts);
 +              sequencer_remove_state(opts);
 +              return error(_("could not detach HEAD"));
 +      }
 +
 +      return update_ref(NULL, "ORIG_HEAD", &oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR);
 +}
 +
 +static int stopped_at_head(void)
 +{
 +      struct object_id head;
 +      struct commit *commit;
 +      struct commit_message message;
 +
 +      if (get_oid("HEAD", &head) ||
 +          !(commit = lookup_commit(the_repository, &head)) ||
 +          parse_commit(commit) || get_message(commit, &message))
 +              fprintf(stderr, _("Stopped at HEAD\n"));
 +      else {
 +              fprintf(stderr, _("Stopped at %s\n"), message.label);
 +              free_message(commit, &message);
 +      }
 +      return 0;
 +
 +}
 +
  static const char rescheduled_advice[] =
  N_("Could not execute the todo command\n"
  "\n"
@@@ -3460,9 -3331,6 +3463,9 @@@ static int pick_commits(struct todo_lis
                        unlink(rebase_path_stopped_sha());
                        unlink(rebase_path_amend());
                        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 +
 +                      if (item->command == TODO_BREAK)
 +                              return stopped_at_head();
                }
                if (item->command <= TODO_SQUASH) {
                        if (is_rebase_i(opts))
                                 */
                                if (item->command == TODO_REWORD &&
                                    !get_oid("HEAD", &oid) &&
 -                                  (!oidcmp(&item->commit->object.oid, &oid) ||
 +                                  (oideq(&item->commit->object.oid, &oid) ||
                                     (opts->have_squash_onto &&
 -                                    !oidcmp(&opts->squash_onto, &oid))))
 +                                    oideq(&opts->squash_onto, &oid))))
                                        to_amend = 1;
  
                                return res | error_with_patch(item->commit,
@@@ -3641,7 -3509,7 +3644,7 @@@ cleanup_head_ref
                        struct object_id orig, head;
  
                        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
 -                      init_revisions(&log_tree_opt, NULL);
 +                      repo_init_revisions(the_repository, &log_tree_opt, NULL);
                        log_tree_opt.diff = 1;
                        log_tree_opt.diffopt.output_format =
                                DIFF_FORMAT_DIFFSTAT;
@@@ -3730,7 -3598,7 +3733,7 @@@ static int commit_staged_changes(struc
                if (get_oid_hex(rev.buf, &to_amend))
                        return error(_("invalid contents: '%s'"),
                                rebase_path_amend());
 -              if (!is_clean && oidcmp(&head, &to_amend))
 +              if (!is_clean && !oideq(&head, &to_amend))
                        return error(_("\nYou have uncommitted changes in your "
                                       "working tree. Please, commit them\n"
                                       "first and then run 'git rebase "
                 * the commit message and if there was a squash, let the user
                 * edit it.
                 */
 -              if (is_clean && !oidcmp(&head, &to_amend) &&
 -                  opts->current_fixup_count > 0 &&
 -                  file_exists(rebase_path_stopped_sha())) {
 +              if (!is_clean || !opts->current_fixup_count)
 +                      ; /* this is not the final fixup */
 +              else if (!oideq(&head, &to_amend) ||
 +                       !file_exists(rebase_path_stopped_sha())) {
 +                      /* was a final fixup or squash done manually? */
 +                      if (!is_fixup(peek_command(todo_list, 0))) {
 +                              unlink(rebase_path_fixup_msg());
 +                              unlink(rebase_path_squash_msg());
 +                              unlink(rebase_path_current_fixups());
 +                              strbuf_reset(&opts->current_fixups);
 +                              opts->current_fixup_count = 0;
 +                      }
 +              } else {
 +                      /* we are in a fixup/squash chain */
                        const char *p = opts->current_fixups.buf;
                        int len = opts->current_fixups.len;
  
@@@ -3974,7 -3831,7 +3977,7 @@@ int sequencer_pick_revisions(struct rep
        return res;
  }
  
 -void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
 +void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
  {
        unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
        struct strbuf sob = STRBUF_INIT;
@@@ -4277,7 -4134,9 +4280,7 @@@ static int make_script_with_merges(stru
                        struct object_id *oid = &parent->item->object.oid;
                        if (!oidset_contains(&interesting, oid))
                                continue;
 -                      if (!oidset_contains(&child_seen, oid))
 -                              oidset_insert(&child_seen, oid);
 -                      else
 +                      if (oidset_insert(&child_seen, oid))
                                label_oid(oid, "branch-point", &state);
                }
  
@@@ -4385,7 -4244,7 +4388,7 @@@ int sequencer_make_script(FILE *out, in
        const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
        int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
  
 -      init_revisions(&revs, NULL);
 +      repo_init_revisions(the_repository, &revs, NULL);
        revs.verbose_header = 1;
        if (!rebase_merges)
                revs.max_parents = 1;
@@@ -4551,20 -4410,24 +4554,20 @@@ int transform_todos(unsigned flags
        return i;
  }
  
 -enum check_level {
 -      CHECK_IGNORE = 0, CHECK_WARN, CHECK_ERROR
 -};
 -
 -static enum check_level get_missing_commit_check_level(void)
 +enum missing_commit_check_level get_missing_commit_check_level(void)
  {
        const char *value;
  
        if (git_config_get_value("rebase.missingcommitscheck", &value) ||
                        !strcasecmp("ignore", value))
 -              return CHECK_IGNORE;
 +              return MISSING_COMMIT_CHECK_IGNORE;
        if (!strcasecmp("warn", value))
 -              return CHECK_WARN;
 +              return MISSING_COMMIT_CHECK_WARN;
        if (!strcasecmp("error", value))
 -              return CHECK_ERROR;
 +              return MISSING_COMMIT_CHECK_ERROR;
        warning(_("unrecognized setting %s for option "
                  "rebase.missingCommitsCheck. Ignoring."), value);
 -      return CHECK_IGNORE;
 +      return MISSING_COMMIT_CHECK_IGNORE;
  }
  
  define_commit_slab(commit_seen, unsigned char);
   */
  int check_todo_list(void)
  {
 -      enum check_level check_level = get_missing_commit_check_level();
 +      enum missing_commit_check_level check_level = get_missing_commit_check_level();
        struct strbuf todo_file = STRBUF_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
        struct strbuf missing = STRBUF_INIT;
        advise_to_edit_todo = res =
                parse_insn_buffer(todo_list.buf.buf, &todo_list);
  
 -      if (res || check_level == CHECK_IGNORE)
 +      if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
                goto leave_check;
  
        /* Mark the commits in git-rebase-todo as seen */
        if (!missing.len)
                goto leave_check;
  
 -      if (check_level == CHECK_ERROR)
 +      if (check_level == MISSING_COMMIT_CHECK_ERROR)
                advise_to_edit_todo = res = 1;
  
        fprintf(stderr,
@@@ -4674,17 -4537,17 +4677,17 @@@ static int rewrite_file(const char *pat
  }
  
  /* skip picking commits whose parents are unchanged */
 -int skip_unnecessary_picks(void)
 +static int skip_unnecessary_picks(struct object_id *output_oid)
  {
        const char *todo_file = rebase_path_todo();
        struct strbuf buf = STRBUF_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
 -      struct object_id onto_oid, *oid = &onto_oid, *parent_oid;
 +      struct object_id *parent_oid;
        int fd, i;
  
        if (!read_oneliner(&buf, rebase_path_onto(), 0))
                return error(_("could not read 'onto'"));
 -      if (get_oid(buf.buf, &onto_oid)) {
 +      if (get_oid(buf.buf, output_oid)) {
                strbuf_release(&buf);
                return error(_("need a HEAD to fixup"));
        }
                if (item->commit->parents->next)
                        break; /* merge commit */
                parent_oid = &item->commit->parents->item->object.oid;
 -              if (hashcmp(parent_oid->hash, oid->hash))
 +              if (!oideq(parent_oid, output_oid))
                        break;
 -              oid = &item->commit->object.oid;
 +              oidcpy(output_oid, &item->commit->object.oid);
        }
        if (i > 0) {
                int offset = get_item_line_offset(&todo_list, i);
  
                todo_list.current = i;
                if (is_fixup(peek_command(&todo_list, 0)))
 -                      record_in_rewritten(oid, peek_command(&todo_list, 0));
 +                      record_in_rewritten(output_oid, peek_command(&todo_list, 0));
        }
  
        todo_list_release(&todo_list);
 -      printf("%s\n", oid_to_hex(oid));
  
        return 0;
  }
  
 +int complete_action(struct replay_opts *opts, unsigned flags,
 +                  const char *shortrevisions, const char *onto_name,
 +                  const char *onto, const char *orig_head, const char *cmd,
 +                  unsigned autosquash)
 +{
 +      const char *shortonto, *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      struct strbuf *buf = &(todo_list.buf);
 +      struct object_id oid;
 +      struct stat st;
 +
 +      get_oid(onto, &oid);
 +      shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
 +
 +      if (!lstat(todo_file, &st) && st.st_size == 0 &&
 +          write_message("noop\n", 5, todo_file, 0))
 +              return -1;
 +
 +      if (autosquash && rearrange_squash())
 +              return -1;
 +
 +      if (cmd && *cmd)
 +              sequencer_add_exec_commands(cmd);
 +
 +      if (strbuf_read_file(buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +
 +      if (parse_insn_buffer(buf->buf, &todo_list)) {
 +              todo_list_release(&todo_list);
 +              return error(_("unusable todo list: '%s'"), todo_file);
 +      }
 +
 +      if (count_commands(&todo_list) == 0) {
 +              apply_autostash(opts);
 +              sequencer_remove_state(opts);
 +              todo_list_release(&todo_list);
 +
 +              return error(_("nothing to do"));
 +      }
 +
 +      strbuf_addch(buf, '\n');
 +      strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
 +                                    "Rebase %s onto %s (%d commands)",
 +                                    count_commands(&todo_list)),
 +                            shortrevisions, shortonto, count_commands(&todo_list));
 +      append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
 +
 +      if (write_message(buf->buf, buf->len, todo_file, 0)) {
 +              todo_list_release(&todo_list);
 +              return -1;
 +      }
 +
 +      if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
 +              return error(_("could not copy '%s' to '%s'."), todo_file,
 +                           rebase_path_todo_backup());
 +
 +      if (transform_todos(flags | TODO_LIST_SHORTEN_IDS))
 +              return error(_("could not transform the todo list"));
 +
 +      strbuf_reset(buf);
 +
 +      if (launch_sequence_editor(todo_file, buf, NULL)) {
 +              apply_autostash(opts);
 +              sequencer_remove_state(opts);
 +              todo_list_release(&todo_list);
 +
 +              return -1;
 +      }
 +
 +      strbuf_stripspace(buf, 1);
 +      if (buf->len == 0) {
 +              apply_autostash(opts);
 +              sequencer_remove_state(opts);
 +              todo_list_release(&todo_list);
 +
 +              return error(_("nothing to do"));
 +      }
 +
 +      todo_list_release(&todo_list);
 +
 +      if (check_todo_list()) {
 +              checkout_onto(opts, onto_name, onto, orig_head);
 +              return -1;
 +      }
 +
 +      if (transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS)))
 +              return error(_("could not transform the todo list"));
 +
 +      if (opts->allow_ff && skip_unnecessary_picks(&oid))
 +              return error(_("could not skip unnecessary pick commands"));
 +
 +      if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
 +              return -1;
 +;
 +      if (require_clean_work_tree("rebase", "", 1, 1))
 +              return -1;
 +
 +      return sequencer_continue(opts);
 +}
 +
  struct subject2item_entry {
        struct hashmap_entry entry;
        int i;