Merge branch 'js/rebase-recreate-merge' into next
authorJunio C Hamano <gitster@pobox.com>
Thu, 15 Mar 2018 22:10:57 +0000 (15:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 15 Mar 2018 22:10:57 +0000 (15:10 -0700)
"git rebase" learned "--recreate-merges" to transplant the whole
topology of commit graph elsewhere.

* js/rebase-recreate-merge:
rebase -i: introduce --recreate-merges=[no-]rebase-cousins
pull: accept --rebase=recreate to recreate the branch topology
sequencer: handle post-rewrite for merge commands
sequencer: make refs generated by the `label` command worktree-local
rebase: introduce the --recreate-merges option
rebase-helper --make-script: introduce a flag to recreate merges
sequencer: fast-forward merge commits, if possible
sequencer: introduce the `merge` command
sequencer: introduce new commands to reset the revision
git-rebase--interactive: clarify arguments
sequencer: make rearrange_squash() a bit more obvious
sequencer: avoid using errno clobbered by rollback_lock_file()

1  2 
Documentation/config.txt
Documentation/git-rebase.txt
builtin/pull.c
builtin/rebase--helper.c
builtin/remote.c
contrib/completion/git-completion.bash
git-rebase--interactive.sh
git-rebase.sh
sequencer.c
sequencer.h
diff --combined Documentation/config.txt
index ce9102cea83766be0ae8d2ed2867fd4c587d7a95,da41ab246dc62358b8250a06e55175c06cb92479..7b4a2a8980fe7633d0118b9bba59d87bd5549442
@@@ -1058,6 -1058,10 +1058,10 @@@ branch.<name>.rebase:
        "git pull" is run. See "pull.rebase" for doing this in a non
        branch-specific manner.
  +
+ When recreate, also pass `--recreate-merges` along to 'git rebase'
+ so that locally committed merge commits will not be flattened
+ by running 'git pull'.
+ +
  When preserve, also pass `--preserve-merges` along to 'git rebase'
  so that locally committed merge commits will not be flattened
  by running 'git pull'.
@@@ -1398,16 -1402,7 +1402,16 @@@ fetch.unpackLimit:
  
  fetch.prune::
        If true, fetch will automatically behave as if the `--prune`
 -      option was given on the command line.  See also `remote.<name>.prune`.
 +      option was given on the command line.  See also `remote.<name>.prune`
 +      and the PRUNING section of linkgit:git-fetch[1].
 +
 +fetch.pruneTags::
 +      If true, fetch will automatically behave as if the
 +      `refs/tags/*:refs/tags/*` refspec was provided when pruning,
 +      if not set already. This allows for setting both this option
 +      and `fetch.prune` to maintain a 1=1 mapping to upstream
 +      refs. See also `remote.<name>.pruneTags` and the PRUNING
 +      section of linkgit:git-fetch[1].
  
  fetch.output::
        Control how ref update status is printed. Valid values are
@@@ -2616,6 -2611,10 +2620,10 @@@ pull.rebase:
        pull" is run. See "branch.<name>.rebase" for setting this on a
        per-branch basis.
  +
+ When recreate, also pass `--recreate-merges` along to 'git rebase'
+ so that locally committed merge commits will not be flattened
+ by running 'git pull'.
+ +
  When preserve, also pass `--preserve-merges` along to 'git rebase'
  so that locally committed merge commits will not be flattened
  by running 'git pull'.
@@@ -2954,15 -2953,6 +2962,15 @@@ remote.<name>.prune:
        remote (as if the `--prune` option was given on the command line).
        Overrides `fetch.prune` settings, if any.
  
 +remote.<name>.pruneTags::
 +      When set to true, fetching from this remote by default will also
 +      remove any local tags that no longer exist on the remote if pruning
 +      is activated in general via `remote.<name>.prune`, `fetch.prune` or
 +      `--prune`. Overrides `fetch.pruneTags` settings, if any.
 ++
 +See also `remote.<name>.prune` and the PRUNING section of
 +linkgit:git-fetch[1].
 +
  remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
@@@ -3228,8 -3218,7 +3236,8 @@@ submodule.active:
  
  submodule.recurse::
        Specifies if commands recurse into submodules by default. This
 -      applies to all commands that have a `--recurse-submodules` option.
 +      applies to all commands that have a `--recurse-submodules` option,
 +      except `clone`.
        Defaults to false.
  
  submodule.fetchJobs::
@@@ -3362,10 -3351,6 +3370,10 @@@ uploadpack.packObjectsHook:
        was run. I.e., `upload-pack` will feed input intended for
        `pack-objects` to the hook, and expects a completed packfile on
        stdout.
 +
 +uploadpack.allowFilter::
 +      If this option is set, `upload-pack` will advertise partial
 +      clone and partial fetch object filtering.
  +
  Note that this configuration variable is ignored if it is seen in the
  repository-level config (this is a safety measure against fetching from
index 3277ca143273e01f5f4973ed351c8a5cb4b8e0fa,0e6d020d92493e8033638d6de49fc6d32359d014..2b85416f969aee96bf42a7233549a993f1974798
@@@ -12,7 -12,7 +12,7 @@@ SYNOPSI
        [<upstream> [<branch>]]
  'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
        --root [<branch>]
 -'git rebase' --continue | --skip | --abort | --quit | --edit-todo
 +'git rebase' --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch
  
  DESCRIPTION
  -----------
@@@ -244,22 -244,12 +244,22 @@@ leave out at most one of A and B, in wh
        Keep the commits that do not change anything from its
        parents in the result.
  
 +--allow-empty-message::
 +      By default, rebasing commits with an empty message will fail.
 +      This option overrides that behavior, allowing commits with empty
 +      messages to be rebased.
 +
  --skip::
        Restart the rebasing process by skipping the current patch.
  
  --edit-todo::
        Edit the todo list during an interactive rebase.
  
 +--show-current-patch::
 +      Show the current patch in an interactive rebase or when rebase
 +      is stopped because of conflicts. This is the equivalent of
 +      `git show REBASE_HEAD`.
 +
  -m::
  --merge::
        Use merging strategies to rebase.  When the recursive (default) merge
@@@ -378,6 -368,17 +378,17 @@@ The commit list format can be changed b
  rebase.instructionFormat.  A customized instruction format will automatically
  have the long commit hash prepended to the format.
  
+ --recreate-merges[=(rebase-cousins|no-rebase-cousins)]::
+       Recreate merge commits instead of flattening the history by replaying
+       merges. Merge conflict resolutions or manual amendments to merge
+       commits are not recreated automatically, but have to be recreated
+       manually.
+ +
+ By default, or when `no-rebase-cousins` was specified, commits which do not
+ have `<upstream>` as direct ancestor keep their original branch point.
+ If the `rebase-cousins` mode is turned on, such commits are rebased onto
+ `<upstream>` (or `<onto>`, if specified).
  -p::
  --preserve-merges::
        Recreate merge commits instead of flattening the history by replaying
@@@ -780,7 -781,8 +791,8 @@@ BUG
  The todo list presented by `--preserve-merges --interactive` does not
  represent the topology of the revision graph.  Editing commits and
  rewording their commit messages should work fine, but attempts to
- reorder commits tend to produce counterintuitive results.
+ reorder commits tend to produce counterintuitive results. Use
+ --recreate-merges for a more faithful representation.
  
  For example, an attempt to rearrange
  ------------
diff --combined builtin/pull.c
index e32d6cd5b4c999bc45b961c1387af066c72a823a,e33c84e034595e0401b9d5b2952f82ff3ce8d02e..3d1cc60eed6c21d3c3ba5bb5c138a1923539de4b
@@@ -27,14 -27,16 +27,16 @@@ enum rebase_type 
        REBASE_FALSE = 0,
        REBASE_TRUE,
        REBASE_PRESERVE,
+       REBASE_RECREATE,
        REBASE_INTERACTIVE
  };
  
  /**
   * Parses the value of --rebase. If value is a false value, returns
   * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
-  * "preserve", returns REBASE_PRESERVE. If value is a invalid value, dies with
-  * a fatal error if fatal is true, otherwise returns REBASE_INVALID.
+  * "recreate", returns REBASE_RECREATE. If value is "preserve", returns
+  * REBASE_PRESERVE. If value is a invalid value, dies with a fatal error if
+  * fatal is true, otherwise returns REBASE_INVALID.
   */
  static enum rebase_type parse_config_rebase(const char *key, const char *value,
                int fatal)
@@@ -47,6 -49,8 +49,8 @@@
                return REBASE_TRUE;
        else if (!strcmp(value, "preserve"))
                return REBASE_PRESERVE;
+       else if (!strcmp(value, "recreate"))
+               return REBASE_RECREATE;
        else if (!strcmp(value, "interactive"))
                return REBASE_INTERACTIVE;
  
@@@ -130,7 -134,7 +134,7 @@@ static struct option pull_options[] = 
        /* Options passed to git-merge or git-rebase */
        OPT_GROUP(N_("Options related to merging")),
        { OPTION_CALLBACK, 'r', "rebase", &opt_rebase,
-         "false|true|preserve|interactive",
+         "false|true|recreate|preserve|interactive",
          N_("incorporate changes by rebasing rather than merging"),
          PARSE_OPT_OPTARG, parse_opt_rebase },
        OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
        OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"),
                N_("path to upload pack on remote end"),
                0),
 -      OPT__FORCE(&opt_force, N_("force overwrite of local branch")),
 +      OPT__FORCE(&opt_force, N_("force overwrite of local branch"), 0),
        OPT_PASSTHRU('t', "tags", &opt_tags, NULL,
                N_("fetch all tags and associated objects"),
                PARSE_OPT_NOARG),
@@@ -574,7 -578,6 +578,7 @@@ static int rebase_submodules(void
        cp.no_stdin = 1;
        argv_array_pushl(&cp.args, "submodule", "update",
                                   "--recursive", "--rebase", NULL);
 +      argv_push_verbosity(&cp.args);
  
        return run_command(&cp);
  }
@@@ -587,7 -590,6 +591,7 @@@ static int update_submodules(void
        cp.no_stdin = 1;
        argv_array_pushl(&cp.args, "submodule", "update",
                                   "--recursive", "--checkout", NULL);
 +      argv_push_verbosity(&cp.args);
  
        return run_command(&cp);
  }
@@@ -800,7 -802,9 +804,9 @@@ static int run_rebase(const struct obje
        argv_push_verbosity(&args);
  
        /* Options passed to git-rebase */
-       if (opt_rebase == REBASE_PRESERVE)
+       if (opt_rebase == REBASE_RECREATE)
+               argv_array_push(&args, "--recreate-merges");
+       else if (opt_rebase == REBASE_PRESERVE)
                argv_array_push(&args, "--preserve-merges");
        else if (opt_rebase == REBASE_INTERACTIVE)
                argv_array_push(&args, "--interactive");
diff --combined builtin/rebase--helper.c
index ad074705bb51d1de4221b3c5dfaa7229903c0ef0,cea99cb32354a6e19223c0a86a9ec319d838b0b2..5d1f12de57bc7bf50960047c4a0a81a4798d0498
@@@ -12,8 -12,8 +12,8 @@@ static const char * const builtin_rebas
  int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
  {
        struct replay_opts opts = REPLAY_OPTS_INIT;
-       unsigned flags = 0, keep_empty = 0;
-       int abbreviate_commands = 0;
+       unsigned flags = 0, keep_empty = 0, recreate_merges = 0;
+       int abbreviate_commands = 0, rebase_cousins = -1;
        enum {
                CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
                CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
        struct option options[] = {
                OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
                OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
 +              OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
 +                      N_("allow commits with empty messages")),
+               OPT_BOOL(0, "recreate-merges", &recreate_merges, N_("recreate merge commits")),
+               OPT_BOOL(0, "rebase-cousins", &rebase_cousins,
+                        N_("keep original branch points of cousins")),
                OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
                                CONTINUE),
                OPT_CMDMODE(0, "abort", &command, N_("abort rebase"),
@@@ -45,7 -46,7 +48,7 @@@
                OPT_END()
        };
  
 -      git_config(git_default_config, NULL);
 +      sequencer_init_config(&opts);
        git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
  
        opts.action = REPLAY_INTERACTIVE_REBASE;
  
        flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
        flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
+       flags |= recreate_merges ? TODO_LIST_RECREATE_MERGES : 0;
+       flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
        flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
  
+       if (rebase_cousins >= 0 && !recreate_merges)
+               warning(_("--[no-]rebase-cousins has no effect without "
+                         "--recreate-merges"));
        if (command == CONTINUE && argc == 1)
                return !!sequencer_continue(&opts);
        if (command == ABORT && argc == 1)
diff --combined builtin/remote.c
index 805ffc05cdb80e4a69de4134e757f9c71e8033dc,b7d0f7ce5964b475f51a4b2a14620971150ffc5d..210890c8a8ea77d31e70e14f0551f338d6828e50
@@@ -168,7 -168,7 +168,7 @@@ static int add(int argc, const char **a
                OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
                { OPTION_CALLBACK, 0, "mirror", &mirror, N_("push|fetch"),
                        N_("set up remote as a mirror to push to or fetch from"),
 -                      PARSE_OPT_OPTARG, parse_mirror_opt },
 +                      PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt },
                OPT_END()
        };
  
@@@ -306,6 -306,8 +306,8 @@@ static int config_read_branches(const c
                                info->rebase = v;
                        else if (!strcmp(value, "preserve"))
                                info->rebase = NORMAL_REBASE;
+                       else if (!strcmp(value, "recreate"))
+                               info->rebase = NORMAL_REBASE;
                        else if (!strcmp(value, "interactive"))
                                info->rebase = INTERACTIVE_REBASE;
                }
@@@ -322,7 -324,7 +324,7 @@@ static void read_branches(void
  
  struct ref_states {
        struct remote *remote;
 -      struct string_list new, stale, tracked, heads, push;
 +      struct string_list new_refs, stale, tracked, heads, push;
        int queried;
  };
  
@@@ -337,12 -339,12 +339,12 @@@ static int get_ref_states(const struct 
                        die(_("Could not get fetch map for refspec %s"),
                                states->remote->fetch_refspec[i]);
  
 -      states->new.strdup_strings = 1;
 +      states->new_refs.strdup_strings = 1;
        states->tracked.strdup_strings = 1;
        states->stale.strdup_strings = 1;
        for (ref = fetch_map; ref; ref = ref->next) {
                if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
 -                      string_list_append(&states->new, abbrev_branch(ref->name));
 +                      string_list_append(&states->new_refs, abbrev_branch(ref->name));
                else
                        string_list_append(&states->tracked, abbrev_branch(ref->name));
        }
        free_refs(stale_refs);
        free_refs(fetch_map);
  
 -      string_list_sort(&states->new);
 +      string_list_sort(&states->new_refs);
        string_list_sort(&states->tracked);
        string_list_sort(&states->stale);
  
@@@ -546,8 -548,8 +548,8 @@@ static int add_branch_for_removal(cons
  }
  
  struct rename_info {
 -      const char *old;
 -      const char *new;
 +      const char *old_name;
 +      const char *new_name;
        struct string_list *remote_branches;
  };
  
@@@ -560,7 -562,7 +562,7 @@@ static int read_remote_branches(const c
        int flag;
        const char *symref;
  
 -      strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
 +      strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
        if (starts_with(refname, buf.buf)) {
                item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
@@@ -615,36 -617,36 +617,36 @@@ static int mv(int argc, const char **ar
        if (argc != 3)
                usage_with_options(builtin_remote_rename_usage, options);
  
 -      rename.old = argv[1];
 -      rename.new = argv[2];
 +      rename.old_name = argv[1];
 +      rename.new_name = argv[2];
        rename.remote_branches = &remote_branches;
  
 -      oldremote = remote_get(rename.old);
 +      oldremote = remote_get(rename.old_name);
        if (!remote_is_configured(oldremote, 1))
 -              die(_("No such remote: %s"), rename.old);
 +              die(_("No such remote: %s"), rename.old_name);
  
 -      if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
 +      if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
  
 -      newremote = remote_get(rename.new);
 +      newremote = remote_get(rename.new_name);
        if (remote_is_configured(newremote, 1))
 -              die(_("remote %s already exists."), rename.new);
 +              die(_("remote %s already exists."), rename.new_name);
  
 -      strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
 +      strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name);
        if (!valid_fetch_refspec(buf.buf))
 -              die(_("'%s' is not a valid remote name"), rename.new);
 +              die(_("'%s' is not a valid remote name"), rename.new_name);
  
        strbuf_reset(&buf);
 -      strbuf_addf(&buf, "remote.%s", rename.old);
 -      strbuf_addf(&buf2, "remote.%s", rename.new);
 +      strbuf_addf(&buf, "remote.%s", rename.old_name);
 +      strbuf_addf(&buf2, "remote.%s", rename.new_name);
        if (git_config_rename_section(buf.buf, buf2.buf) < 1)
                return error(_("Could not rename config section '%s' to '%s'"),
                                buf.buf, buf2.buf);
  
        strbuf_reset(&buf);
 -      strbuf_addf(&buf, "remote.%s.fetch", rename.new);
 +      strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
        git_config_set_multivar(buf.buf, NULL, NULL, 1);
 -      strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
 +      strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
        for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
                char *ptr;
  
                        refspec_updated = 1;
                        strbuf_splice(&buf2,
                                      ptr-buf2.buf + strlen(":refs/remotes/"),
 -                                    strlen(rename.old), rename.new,
 -                                    strlen(rename.new));
 +                                    strlen(rename.old_name), rename.new_name,
 +                                    strlen(rename.new_name));
                } else
                        warning(_("Not updating non-default fetch refspec\n"
                                  "\t%s\n"
        for (i = 0; i < branch_list.nr; i++) {
                struct string_list_item *item = branch_list.items + i;
                struct branch_info *info = item->util;
 -              if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
 +              if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) {
                        strbuf_reset(&buf);
                        strbuf_addf(&buf, "branch.%s.remote", item->string);
 -                      git_config_set(buf.buf, rename.new);
 +                      git_config_set(buf.buf, rename.new_name);
                }
        }
  
                        continue;
                strbuf_reset(&buf);
                strbuf_addstr(&buf, item->string);
 -              strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
 -                              rename.new, strlen(rename.new));
 +              strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
 +                              rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf2);
                strbuf_addf(&buf2, "remote: renamed %s to %s",
                                item->string, buf.buf);
                        continue;
                strbuf_reset(&buf);
                strbuf_addstr(&buf, item->string);
 -              strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
 -                              rename.new, strlen(rename.new));
 +              strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
 +                              rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf2);
                strbuf_addstr(&buf2, item->util);
 -              strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
 -                              rename.new, strlen(rename.new));
 +              strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name),
 +                              rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf3);
                strbuf_addf(&buf3, "remote: renamed %s to %s",
                                item->string, buf.buf);
@@@ -822,7 -824,7 +824,7 @@@ static void clear_push_info(void *util
  
  static void free_remote_ref_states(struct ref_states *states)
  {
 -      string_list_clear(&states->new, 0);
 +      string_list_clear(&states->new_refs, 0);
        string_list_clear(&states->stale, 1);
        string_list_clear(&states->tracked, 0);
        string_list_clear(&states->heads, 0);
@@@ -907,7 -909,7 +909,7 @@@ static int show_remote_info_item(struc
        if (states->queried) {
                const char *fmt = "%s";
                const char *arg = "";
 -              if (string_list_has_string(&states->new, name)) {
 +              if (string_list_has_string(&states->new_refs, name)) {
                        fmt = _(" new (next fetch will store in remotes/%s)");
                        arg = states->remote->name;
                } else if (string_list_has_string(&states->tracked, name))
@@@ -1176,7 -1178,7 +1178,7 @@@ static int show(int argc, const char **
  
                /* remote branch info */
                info.width = 0;
 -              for_each_string_list(&states.new, add_remote_to_show_info, &info);
 +              for_each_string_list(&states.new_refs, add_remote_to_show_info, &info);
                for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
                for_each_string_list(&states.stale, add_remote_to_show_info, &info);
                if (info.list->nr)
index 6da95b8095d9a18694f6a3cda139fe4ba832f4ee,6f98c96fee90598f481e6f61046146f41be75eda..d345d1fe0c3957c2744989c6758c79cffe0f52cb
@@@ -280,39 -280,6 +280,39 @@@ __gitcomp (
        esac
  }
  
 +# This function is equivalent to
 +#
 +#    __gitcomp "$(git xxx --git-completion-helper) ..."
 +#
 +# except that the output is cached. Accept 1-3 arguments:
 +# 1: the git command to execute, this is also the cache key
 +# 2: extra options to be added on top (e.g. negative forms)
 +# 3: options to be excluded
 +__gitcomp_builtin ()
 +{
 +      # spaces must be replaced with underscore for multi-word
 +      # commands, e.g. "git remote add" becomes remote_add.
 +      local cmd="$1"
 +      local incl="$2"
 +      local excl="$3"
 +
 +      local var=__gitcomp_builtin_"${cmd/-/_}"
 +      local options
 +      eval "options=\$$var"
 +
 +      if [ -z "$options" ]; then
 +              # leading and trailing spaces are significant to make
 +              # option removal work correctly.
 +              options=" $(__git ${cmd/_/ } --git-completion-helper) $incl "
 +              for i in $excl; do
 +                      options="${options/ $i / }"
 +              done
 +              eval "$var=\"$options\""
 +      fi
 +
 +      __gitcomp "$options"
 +}
 +
  # Variation of __gitcomp_nl () that appends to the existing list of
  # completion candidates, COMPREPLY.
  __gitcomp_nl_append ()
@@@ -472,7 -439,7 +472,7 @@@ __git_refs (
                        track=""
                        ;;
                *)
 -                      for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
 +                      for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do
                                case "$i" in
                                $match*)
                                        if [ -e "$dir/$i" ]; then
@@@ -627,7 -594,7 +627,7 @@@ __git_is_configured_remote (
  
  __git_list_merge_strategies ()
  {
 -      git merge -s help 2>&1 |
 +      LANG=C LC_ALL=C git merge -s help 2>&1 |
        sed -n -e '/[Aa]vailable strategies are: /,/^$/{
                s/\.$//
                s/.*://
@@@ -1105,13 -1072,12 +1105,13 @@@ __git_count_arguments (
  }
  
  __git_whitespacelist="nowarn warn error error-all fix"
 +__git_am_inprogress_options="--skip --continue --resolved --abort --quit --show-current-patch"
  
  _git_am ()
  {
        __git_find_repo_path
        if [ -d "$__git_repo_path"/rebase-apply ]; then
 -              __gitcomp "--skip --continue --resolved --abort"
 +              __gitcomp "$__git_am_inprogress_options"
                return
        fi
        case "$cur" in
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --3way --committer-date-is-author-date --ignore-date
 -                      --ignore-whitespace --ignore-space-change
 -                      --interactive --keep --no-utf8 --signoff --utf8
 -                      --whitespace= --scissors
 -                      "
 +              __gitcomp_builtin am "--no-utf8" \
 +                      "$__git_am_inprogress_options"
                return
        esac
  }
@@@ -1134,7 -1104,14 +1134,7 @@@ _git_apply (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --stat --numstat --summary --check --index
 -                      --cached --index-info --reverse --reject --unidiff-zero
 -                      --apply --no-add --exclude=
 -                      --ignore-whitespace --ignore-space-change
 -                      --whitespace= --inaccurate-eof --verbose
 -                      --recount --directory=
 -                      "
 +              __gitcomp_builtin apply
                return
        esac
  }
@@@ -1143,7 -1120,10 +1143,7 @@@ _git_add (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --interactive --refresh --patch --update --dry-run
 -                      --ignore-errors --intent-to-add --force --edit --chmod=
 -                      "
 +              __gitcomp_builtin add
                return
        esac
  
@@@ -1220,8 -1200,12 +1220,8 @@@ _git_branch (
                __git_complete_refs --cur="${cur##--set-upstream-to=}"
                ;;
        --*)
 -              __gitcomp "
 -                      --color --no-color --verbose --abbrev= --no-abbrev
 -                      --track --no-track --contains --no-contains --merged --no-merged
 -                      --set-upstream-to= --edit-description --list
 -                      --unset-upstream --delete --move --copy --remotes
 -                      --column --no-column --sort= --points-at
 +              __gitcomp_builtin branch "--no-color --no-abbrev
 +                      --no-track --no-column
                        "
                ;;
        *)
@@@ -1263,7 -1247,11 +1263,7 @@@ _git_checkout (
                __gitcomp "diff3 merge" "" "${cur##--conflict=}"
                ;;
        --*)
 -              __gitcomp "
 -                      --quiet --ours --theirs --track --no-track --merge
 -                      --conflict= --orphan --patch --detach --ignore-skip-worktree-bits
 -                      --recurse-submodules --no-recurse-submodules
 -                      "
 +              __gitcomp_builtin checkout "--no-track --no-recurse-submodules"
                ;;
        *)
                # check if --track, --no-track, or --no-guess was specified
@@@ -1283,19 -1271,16 +1283,19 @@@ _git_cherry (
        __git_complete_refs
  }
  
 +__git_cherry_pick_inprogress_options="--continue --quit --abort"
 +
  _git_cherry_pick ()
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then
 -              __gitcomp "--continue --quit --abort"
 +              __gitcomp "$__git_cherry_pick_inprogress_options"
                return
        fi
        case "$cur" in
        --*)
 -              __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
 +              __gitcomp_builtin cherry-pick "" \
 +                      "$__git_cherry_pick_inprogress_options"
                ;;
        *)
                __git_complete_refs
@@@ -1307,7 -1292,7 +1307,7 @@@ _git_clean (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--dry-run --quiet"
 +              __gitcomp_builtin clean
                return
                ;;
        esac
@@@ -1320,7 -1305,26 +1320,7 @@@ _git_clone (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --local
 -                      --no-hardlinks
 -                      --shared
 -                      --reference
 -                      --quiet
 -                      --no-checkout
 -                      --bare
 -                      --mirror
 -                      --origin
 -                      --upload-pack
 -                      --template=
 -                      --depth
 -                      --single-branch
 -                      --no-tags
 -                      --branch
 -                      --recurse-submodules
 -                      --no-single-branch
 -                      --shallow-submodules
 -                      "
 +              __gitcomp_builtin clone "--no-single-branch"
                return
                ;;
        esac
@@@ -1353,7 -1357,16 +1353,7 @@@ _git_commit (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --all --author= --signoff --verify --no-verify
 -                      --edit --no-edit
 -                      --amend --include --only --interactive
 -                      --dry-run --reuse-message= --reedit-message=
 -                      --reset-author --file= --message= --template=
 -                      --cleanup= --untracked-files --untracked-files=
 -                      --verbose --quiet --fixup= --squash=
 -                      --patch --short --date --allow-empty
 -                      "
 +              __gitcomp_builtin commit "--no-edit --verify"
                return
        esac
  
@@@ -1369,7 -1382,11 +1369,7 @@@ _git_describe (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --all --tags --contains --abbrev= --candidates=
 -                      --exact-match --debug --long --match --always --first-parent
 -                      --exclude --dirty --broken
 -                      "
 +              __gitcomp_builtin describe
                return
        esac
        __git_complete_refs
@@@ -1394,7 -1411,7 +1394,7 @@@ __git_diff_common_options="--stat --num
                        --dirstat --dirstat= --dirstat-by-file
                        --dirstat-by-file= --cumulative
                        --diff-algorithm=
 -                      --submodule --submodule=
 +                      --submodule --submodule= --ignore-submodules
  "
  
  _git_diff ()
@@@ -1435,11 -1452,11 +1435,11 @@@ _git_difftool (
                return
                ;;
        --*)
 -              __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
 -                      --base --ours --theirs
 -                      --no-renames --diff-filter= --find-copies-harder
 -                      --relative --ignore-submodules
 -                      --tool="
 +              __gitcomp_builtin difftool "$__git_diff_common_options
 +                                      --base --cached --ours --theirs
 +                                      --pickaxe-all --pickaxe-regex
 +                                      --relative --staged
 +                                      "
                return
                ;;
        esac
  
  __git_fetch_recurse_submodules="yes on-demand no"
  
 -__git_fetch_options="
 -      --quiet --verbose --append --upload-pack --force --keep --depth=
 -      --tags --no-tags --all --prune --dry-run --recurse-submodules=
 -      --unshallow --update-shallow
 -"
 -
  _git_fetch ()
  {
        case "$cur" in
                return
                ;;
        --*)
 -              __gitcomp "$__git_fetch_options"
 +              __gitcomp_builtin fetch "--no-tags"
                return
                ;;
        esac
@@@ -1493,7 -1516,10 +1493,7 @@@ _git_fsck (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --tags --root --unreachable --cache --no-reflogs --full
 -                      --strict --verbose --lost-found --name-objects
 -                      "
 +              __gitcomp_builtin fsck "--no-reflogs"
                return
                ;;
        esac
@@@ -1503,7 -1529,7 +1503,7 @@@ _git_gc (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--prune --aggressive"
 +              __gitcomp_builtin gc
                return
                ;;
        esac
@@@ -1559,7 -1585,21 +1559,7 @@@ _git_grep (
  
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --cached
 -                      --text --ignore-case --word-regexp --invert-match
 -                      --full-name --line-number
 -                      --extended-regexp --basic-regexp --fixed-strings
 -                      --perl-regexp
 -                      --threads
 -                      --files-with-matches --name-only
 -                      --files-without-match
 -                      --max-depth
 -                      --count
 -                      --and --or --not --all-match
 -                      --break --heading --show-function --function-context
 -                      --untracked --no-index
 -                      "
 +              __gitcomp_builtin grep
                return
                ;;
        esac
@@@ -1577,7 -1617,7 +1577,7 @@@ _git_help (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--all --guides --info --man --web"
 +              __gitcomp_builtin help
                return
                ;;
        esac
@@@ -1600,7 -1640,7 +1600,7 @@@ _git_init (
                return
                ;;
        --*)
 -              __gitcomp "--quiet --bare --template= --shared --shared="
 +              __gitcomp_builtin init
                return
                ;;
        esac
@@@ -1610,7 -1650,13 +1610,7 @@@ _git_ls_files (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--cached --deleted --modified --others --ignored
 -                      --stage --directory --no-empty-directory --unmerged
 -                      --killed --exclude= --exclude-from=
 -                      --exclude-per-directory= --exclude-standard
 -                      --error-unmatch --with-tree= --full-name
 -                      --abbrev --ignored --exclude-per-directory
 -                      "
 +              __gitcomp_builtin ls-files "--no-empty-directory"
                return
                ;;
        esac
@@@ -1624,7 -1670,7 +1624,7 @@@ _git_ls_remote (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--heads --tags --refs --get-url --symref"
 +              __gitcomp_builtin ls-remote
                return
                ;;
        esac
@@@ -1748,18 -1794,22 +1748,18 @@@ _git_log (
        __git_complete_revlist
  }
  
 -# Common merge options shared by git-merge(1) and git-pull(1).
 -__git_merge_options="
 -      --no-commit --no-stat --log --no-log --squash --strategy
 -      --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
 -      --verify-signatures --no-verify-signatures --gpg-sign
 -      --quiet --verbose --progress --no-progress
 -"
 -
  _git_merge ()
  {
        __git_complete_strategy && return
  
        case "$cur" in
        --*)
 -              __gitcomp "$__git_merge_options
 -                      --rerere-autoupdate --no-rerere-autoupdate --abort --continue"
 +              __gitcomp_builtin merge "--no-rerere-autoupdate
 +                              --no-commit --no-edit --no-ff
 +                              --no-log --no-progress
 +                              --no-squash --no-stat
 +                              --no-verify-signatures
 +                              "
                return
        esac
        __git_complete_refs
@@@ -1783,7 -1833,7 +1783,7 @@@ _git_merge_base (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--octopus --independent --is-ancestor --fork-point"
 +              __gitcomp_builtin merge-base
                return
                ;;
        esac
@@@ -1794,7 -1844,7 +1794,7 @@@ _git_mv (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--dry-run"
 +              __gitcomp_builtin mv
                return
                ;;
        esac
  
  _git_name_rev ()
  {
 -      __gitcomp "--tags --all --stdin"
 +      __gitcomp_builtin name-rev
  }
  
  _git_notes ()
  {
 -      local subcommands='add append copy edit list prune remove show'
 +      local subcommands='add append copy edit get-ref list merge prune remove show'
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
  
        case "$subcommand,$cur" in
        ,--*)
 -              __gitcomp '--ref'
 +              __gitcomp_builtin notes
                ;;
        ,*)
                case "$prev" in
                        ;;
                esac
                ;;
 -      add,--reuse-message=*|append,--reuse-message=*|\
 -      add,--reedit-message=*|append,--reedit-message=*)
 +      *,--reuse-message=*|*,--reedit-message=*)
                __git_complete_refs --cur="${cur#*=}"
                ;;
 -      add,--*|append,--*)
 -              __gitcomp '--file= --message= --reedit-message=
 -                              --reuse-message='
 +      *,--*)
 +              __gitcomp_builtin notes_$subcommand
                ;;
 -      copy,--*)
 -              __gitcomp '--stdin'
 -              ;;
 -      prune,--*)
 -              __gitcomp '--dry-run --verbose'
 -              ;;
 -      prune,*)
 +      prune,*|get-ref,*)
 +              # this command does not take a ref, do not complete it
                ;;
        *)
                case "$prev" in
@@@ -1863,11 -1920,12 +1863,11 @@@ _git_pull (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --rebase --no-rebase
 -                      --autostash --no-autostash
 -                      $__git_merge_options
 -                      $__git_fetch_options
 -              "
 +              __gitcomp_builtin pull "--no-autostash --no-commit --no-edit
 +                                      --no-ff --no-log --no-progress --no-rebase
 +                                      --no-squash --no-stat --no-tags
 +                                      --no-verify-signatures"
 +
                return
                ;;
        esac
@@@ -1918,7 -1976,12 +1918,7 @@@ _git_push (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --all --mirror --tags --dry-run --force --verbose
 -                      --quiet --prune --delete --follow-tags
 -                      --receive-pack= --repo= --set-upstream
 -                      --force-with-lease --force-with-lease= --recurse-submodules=
 -              "
 +              __gitcomp_builtin push
                return
                ;;
        esac
@@@ -1929,11 -1992,11 +1929,11 @@@ _git_rebase (
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then
 -              __gitcomp "--continue --skip --abort --quit --edit-todo"
 +              __gitcomp "--continue --skip --abort --quit --edit-todo --show-current-patch"
                return
        elif [ -d "$__git_repo_path"/rebase-apply ] || \
             [ -d "$__git_repo_path"/rebase-merge ]; then
 -              __gitcomp "--continue --skip --abort --quit"
 +              __gitcomp "--continue --skip --abort --quit --show-current-patch"
                return
        fi
        __git_complete_strategy && return
        --*)
                __gitcomp "
                        --onto --merge --strategy --interactive
-                       --preserve-merges --stat --no-stat
+                       --recreate-merges --preserve-merges --stat --no-stat
                        --committer-date-is-author-date --ignore-date
                        --ignore-whitespace --whitespace=
                        --autosquash --no-autosquash
                        --autostash --no-autostash
                        --verify --no-verify
                        --keep-empty --root --force-rebase --no-ff
 +                      --rerere-autoupdate
                        --exec
                        "
  
@@@ -2019,7 -2081,7 +2019,7 @@@ _git_send_email (
                        --compose --confirm= --dry-run --envelope-sender
                        --from --identity
                        --in-reply-to --no-chain-reply-to --no-signed-off-by-cc
 -                      --no-suppress-from --no-thread --quiet
 +                      --no-suppress-from --no-thread --quiet --reply-to
                        --signed-off-by-cc --smtp-pass --smtp-server
                        --smtp-server-port --smtp-encryption= --smtp-user
                        --subject --suppress-cc= --suppress-from --thread --to
@@@ -2057,7 -2119,11 +2057,7 @@@ _git_status (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --short --branch --porcelain --long --verbose
 -                      --untracked-files= --ignore-submodules= --ignored
 -                      --column= --no-column
 -                      "
 +              __gitcomp_builtin status "--no-column"
                return
                ;;
        esac
@@@ -2116,7 -2182,7 +2116,7 @@@ _git_config (
                return
                ;;
        branch.*.rebase)
-               __gitcomp "false true preserve interactive"
+               __gitcomp "false true recreate preserve interactive"
                return
                ;;
        remote.pushdefault)
        esac
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --system --global --local --file=
 -                      --list --replace-all
 -                      --get --get-all --get-regexp
 -                      --add --unset --unset-all
 -                      --remove-section --rename-section
 -                      --name-only
 -                      "
 +              __gitcomp_builtin config
                return
                ;;
        branch.*.*)
@@@ -2599,7 -2672,7 +2599,7 @@@ _git_remote (
        if [ -z "$subcommand" ]; then
                case "$cur" in
                --*)
 -                      __gitcomp "--verbose"
 +                      __gitcomp_builtin remote
                        ;;
                *)
                        __gitcomp "$subcommands"
  
        case "$subcommand,$cur" in
        add,--*)
 -              __gitcomp "--track --master --fetch --tags --no-tags --mirror="
 +              __gitcomp_builtin remote_add "--no-tags"
                ;;
        add,*)
                ;;
        set-head,--*)
 -              __gitcomp "--auto --delete"
 +              __gitcomp_builtin remote_set-head
                ;;
        set-branches,--*)
 -              __gitcomp "--add"
 +              __gitcomp_builtin remote_set-branches
                ;;
        set-head,*|set-branches,*)
                __git_complete_remote_or_refspec
                ;;
        update,--*)
 -              __gitcomp "--prune"
 +              __gitcomp_builtin remote_update
                ;;
        update,*)
                __gitcomp "$(__git_get_config_variables "remotes")"
                ;;
        set-url,--*)
 -              __gitcomp "--push --add --delete"
 +              __gitcomp_builtin remote_set-url
                ;;
        get-url,--*)
 -              __gitcomp "--push --all"
 +              __gitcomp_builtin remote_get-url
                ;;
        prune,--*)
 -              __gitcomp "--dry-run"
 +              __gitcomp_builtin remote_prune
                ;;
        *)
                __gitcomp_nl "$(__git_remotes)"
@@@ -2648,7 -2721,7 +2648,7 @@@ _git_replace (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--edit --graft --format= --list --delete"
 +              __gitcomp_builtin replace
                return
                ;;
        esac
@@@ -2672,26 -2745,26 +2672,26 @@@ _git_reset (
  
        case "$cur" in
        --*)
 -              __gitcomp "--merge --mixed --hard --soft --patch --keep"
 +              __gitcomp_builtin reset
                return
                ;;
        esac
        __git_complete_refs
  }
  
 +__git_revert_inprogress_options="--continue --quit --abort"
 +
  _git_revert ()
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/REVERT_HEAD ]; then
 -              __gitcomp "--continue --quit --abort"
 +              __gitcomp "$__git_revert_inprogress_options"
                return
        fi
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --edit --mainline --no-edit --no-commit --signoff
 -                      --strategy= --strategy-option=
 -                      "
 +              __gitcomp_builtin revert "--no-edit" \
 +                      "$__git_revert_inprogress_options"
                return
                ;;
        esac
@@@ -2702,7 -2775,7 +2702,7 @@@ _git_rm (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--cached --dry-run --ignore-unmatch --quiet"
 +              __gitcomp_builtin rm
                return
                ;;
        esac
@@@ -2760,7 -2833,12 +2760,7 @@@ _git_show_branch (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --all --remotes --topo-order --date-order --current --more=
 -                      --list --independent --merge-base --no-name
 -                      --color --no-color
 -                      --sha1-name --sparse --topics --reflog
 -                      "
 +              __gitcomp_builtin show-branch "--no-color"
                return
                ;;
        esac
@@@ -2993,7 -3071,11 +2993,7 @@@ _git_tag (
  
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --list --delete --verify --annotate --message --file
 -                      --sign --cleanup --local-user --force --column --sort=
 -                      --contains --no-contains --points-at --merged --no-merged --create-reflog
 -                      "
 +              __gitcomp_builtin tag
                ;;
        esac
  }
@@@ -3005,26 -3087,23 +3005,26 @@@ _git_whatchanged (
  
  _git_worktree ()
  {
 -      local subcommands="add list lock prune unlock"
 +      local subcommands="add list lock move prune remove unlock"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
        else
                case "$subcommand,$cur" in
                add,--*)
 -                      __gitcomp "--detach"
 +                      __gitcomp_builtin worktree_add
                        ;;
                list,--*)
 -                      __gitcomp "--porcelain"
 +                      __gitcomp_builtin worktree_list
                        ;;
                lock,--*)
 -                      __gitcomp "--reason"
 +                      __gitcomp_builtin worktree_lock
                        ;;
                prune,--*)
 -                      __gitcomp "--dry-run --expire --verbose"
 +                      __gitcomp_builtin worktree_prune
 +                      ;;
 +              remove,--*)
 +                      __gitcomp "--force"
                        ;;
                *)
                        ;;
index 331c8dfeac3cac2fd2d9d9287d5c487669fe80a0,e199fe1cca569fa03a7afc90c40342580169788c..47a9cb81b80f7eae7799f3771019ee5c6d759f0f
@@@ -155,13 -155,19 +155,19 @@@ reschedule_last_action () 
  append_todo_help () {
        gettext "
  Commands:
- p, pick = use commit
- r, reword = use commit, but edit the commit message
- e, edit = use commit, but stop for amending
- s, squash = use commit, but meld into previous commit
- f, fixup = like \"squash\", but discard this commit's log message
- x, exec = run command (the rest of the line) using shell
- d, drop = remove commit
+ p, pick <commit> = use commit
+ r, reword <commit> = use commit, but edit the commit message
+ e, edit <commit> = use commit, but stop for amending
+ s, squash <commit> = use commit, but meld into previous commit
+ f, fixup <commit> = like \"squash\", but discard this commit's log message
+ x, exec <commit> = run command (the rest of the line) using shell
+ d, drop <commit> = remove commit
+ l, label <label> = label current HEAD with a name
+ t, reset <label> = reset HEAD to a label
+ m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
+ .       create a merge commit using the original merge commit's
+ .       message (or the oneline, if no original merge commit was
+ .       specified). Use -c <commit> to reword the commit message.
  
  These lines can be re-ordered; they are executed from top to bottom.
  " | git stripspace --comment-lines >>"$todo"
@@@ -199,14 -205,12 +205,14 @@@ make_patch () 
  
  die_with_patch () {
        echo "$1" > "$state_dir"/stopped-sha
 +      git update-ref REBASE_HEAD "$1"
        make_patch "$1"
        die "$2"
  }
  
  exit_with_patch () {
        echo "$1" > "$state_dir"/stopped-sha
 +      git update-ref REBASE_HEAD "$1"
        make_patch $1
        git rev-parse --verify HEAD > "$amend"
        gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
@@@ -283,7 -287,7 +289,7 @@@ pick_one () 
  
        test -d "$rewritten" &&
                pick_one_preserving_merges "$@" && return
 -      output eval git cherry-pick $allow_rerere_autoupdate \
 +      output eval git cherry-pick $allow_rerere_autoupdate $allow_empty_message \
                        ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
                        "$strategy_args" $empty_args $ff "$@"
  
@@@ -398,7 -402,7 +404,7 @@@ pick_one_preserving_merges () 
                                        --sq-quote "$gpg_sign_opt")} \
                                $allow_rerere_autoupdate "$merge_args" \
                                "$strategy_args" \
 -                              -m $(git rev-parse --sq-quote "$msg_content") \
 +                              -m "$(git rev-parse --sq-quote "$msg_content")" \
                                "$new_parents"
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
                        ;;
                *)
                        output eval git cherry-pick $allow_rerere_autoupdate \
 +                              $allow_empty_message \
                                ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
                                "$strategy_args" "$@" ||
                                die_with_patch $sha1 "$(eval_gettext "Could not pick \$sha1")"
@@@ -562,8 -565,7 +568,8 @@@ do_next () 
  
                mark_action_done
                do_pick $sha1 "$rest"
 -              git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} || {
 +              git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} \
 +                      $allow_empty_message || {
                        warn "$(eval_gettext "\
  Could not amend commit after successfully picking \$sha1... \$rest
  This is most likely due to an empty commit message, or the pre-commit hook
@@@ -611,7 -613,7 +617,7 @@@ you are able to reword the commit.")
                        # This is an intermediate commit; its message will only be
                        # used in case of trouble.  So use the long version:
                        do_with_author output git commit --amend --no-verify -F "$squash_msg" \
 -                              ${gpg_sign_opt:+"$gpg_sign_opt"} ||
 +                              ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
                                die_failed_squash $sha1 "$rest"
                        ;;
                *)
                        if test -f "$fixup_msg"
                        then
                                do_with_author git commit --amend --no-verify -F "$fixup_msg" \
 -                                      ${gpg_sign_opt:+"$gpg_sign_opt"} ||
 +                                      ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
                                        die_failed_squash $sha1 "$rest"
                        else
                                cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
                                rm -f "$GIT_DIR"/MERGE_MSG
                                do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e \
 -                                      ${gpg_sign_opt:+"$gpg_sign_opt"} ||
 +                                      ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
                                        die_failed_squash $sha1 "$rest"
                        fi
                        rm -f "$squash_msg" "$fixup_msg"
@@@ -758,8 -760,7 +764,8 @@@ case "$action" i
  continue)
        if test ! -d "$rewritten"
        then
 -              exec git rebase--helper ${force_rebase:+--no-ff} --continue
 +              exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
 +                      --continue
        fi
        # do we have anything to commit?
        if git diff-index --cached --quiet HEAD --
@@@ -799,11 -800,11 +805,11 @@@ In both cases, once you're done, contin
  You have uncommitted changes in your working tree. Please commit them
  first and then run 'git rebase --continue' again.")"
                        do_with_author git commit --amend --no-verify -F "$msg" -e \
 -                              ${gpg_sign_opt:+"$gpg_sign_opt"} ||
 +                              ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
                                die "$(gettext "Could not commit staged changes.")"
                else
                        do_with_author git commit --no-verify -F "$msg" -e \
 -                              ${gpg_sign_opt:+"$gpg_sign_opt"} ||
 +                              ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
                                die "$(gettext "Could not commit staged changes.")"
                fi
        fi
@@@ -822,8 -823,7 +828,8 @@@ skip
  
        if test ! -d "$rewritten"
        then
 -              exec git rebase--helper ${force_rebase:+--no-ff} --continue
 +              exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
 +                      --continue
        fi
        do_rest
        return 0
@@@ -846,9 -846,6 +852,9 @@@ To continue rebase after editing, run
  
        exit
        ;;
 +show-current-patch)
 +      exec git show REBASE_HEAD --
 +      ;;
  esac
  
  comment_for_reflog start
@@@ -864,7 -861,6 +870,7 @@@ f
  
  orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
  mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
 +rm -f "$(git rev-parse --git-path REBASE_HEAD)"
  
  : > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
  write_basic_state
@@@ -906,6 -902,8 +912,8 @@@ f
  if test t != "$preserve_merges"
  then
        git rebase--helper --make-script ${keep_empty:+--keep-empty} \
+               ${recreate_merges:+--recreate-merges} \
+               ${rebase_cousins:+--rebase-cousins} \
                $revisions ${restrict_revision+^$restrict_revision} >"$todo" ||
        die "$(gettext "Could not generate todo list")"
  else
@@@ -1026,8 -1024,7 +1034,8 @@@ checkout_ont
  if test -z "$rebase_root" && test ! -d "$rewritten"
  then
        require_clean_work_tree "rebase"
 -      exec git rebase--helper ${force_rebase:+--no-ff} --continue
 +      exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
 +              --continue
  fi
  do_rest
  
diff --combined git-rebase.sh
index a1f6e5de6a3ed1fe9a6217a136611682f3db6582,58d778a2da05e3590d618417e2108dc05395321d..b24e4d5d87640232f69198228da6037a49aebfce
@@@ -17,6 -17,7 +17,7 @@@ q,quiet!           be quiet. implies --
  autostash          automatically stash/stash pop before and after
  fork-point         use 'merge-base --fork-point' to refine upstream
  onto=!             rebase onto given branch instead of upstream
+ recreate-merges?   try to recreate merges instead of skipping them
  p,preserve-merges! try to recreate merges instead of ignoring them
  s,strategy=!       use the given merge strategy
  no-ff!             cherry-pick all commits, even if unchanged
@@@ -24,7 -25,6 +25,7 @@@ m,merge!           use merging strategi
  i,interactive!     let the user edit the list of commits to rebase
  x,exec=!           add exec lines after each commit of the editable list
  k,keep-empty     preserve empty commits during rebase
 +allow-empty-message allow rebasing commits with empty messages
  f,force-rebase!    force rebase even if branch is up to date
  X,strategy-option=! pass the argument through to the merge strategy
  stat!              display a diffstat of what changed upstream
@@@ -46,7 -46,6 +47,7 @@@ abort!             abort and check out 
  skip!              skip current patch and continue
  edit-todo!         edit the todo list during an interactive rebase
  quit!              abort but keep HEAD where it is
 +show-current-patch! show the patch file being applied or merged
  "
  . git-sh-setup
  set_reflog_action rebase
@@@ -88,10 -87,11 +89,12 @@@ type
  state_dir=
  # One of {'', continue, skip, abort}, as parsed from command line
  action=
+ recreate_merges=
+ rebase_cousins=
  preserve_merges=
  autosquash=
  keep_empty=
 +allow_empty_message=
  test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
  case "$(git config --bool commit.gpgsign)" in
  true) gpg_sign_opt=-S ;;
@@@ -184,7 -184,6 +187,7 @@@ You can run "git stash pop" or "git sta
  }
  
  finish_rebase () {
 +      rm -f "$(git rev-parse --git-path REBASE_HEAD)"
        apply_autostash &&
        { git gc --auto || true; } &&
        rm -rf "$state_dir"
@@@ -249,7 -248,7 +252,7 @@@ d
        --verify)
                ok_to_skip_pre_rebase=
                ;;
 -      --continue|--skip|--abort|--quit|--edit-todo)
 +      --continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
                test $total_argc -eq 2 || usage
                action=${1##--}
                ;;
        --keep-empty)
                keep_empty=yes
                ;;
 +      --allow-empty-message)
 +              allow_empty_message=--allow-empty-message
 +              ;;
+       --recreate-merges)
+               recreate_merges=t
+               test -z "$interactive_rebase" && interactive_rebase=implied
+               ;;
+       --recreate-merges=*)
+               recreate_merges=t
+               case "${1#*=}" in
+               rebase-cousins) rebase_cousins=t;;
+               no-rebase-cousins) rebase_cousins=;;
+               *) die "Unknown mode: $1";;
+               esac
+               test -z "$interactive_rebase" && interactive_rebase=implied
+               ;;
        --preserve-merges)
                preserve_merges=t
                test -z "$interactive_rebase" && interactive_rebase=implied
@@@ -419,10 -428,6 +435,10 @@@ quit
  edit-todo)
        run_specific_rebase
        ;;
 +show-current-patch)
 +      run_specific_rebase
 +      die "BUG: run_specific_rebase is not supposed to return here"
 +      ;;
  esac
  
  # Make sure no rebase is in progress
diff --combined sequencer.c
index f9d1001dee9ad10e243aaeafc46fbdd13597fce7,d8cc63dbe4da9553ae96b42f244c3c043bf56dc7..d34ffd392c07e38882dcf9145421bb0766e753cc
@@@ -1,10 -1,10 +1,10 @@@
  #include "cache.h"
  #include "config.h"
  #include "lockfile.h"
 -#include "sequencer.h"
  #include "dir.h"
  #include "object.h"
  #include "commit.h"
 +#include "sequencer.h"
  #include "tag.h"
  #include "run-command.h"
  #include "exec_cmd.h"
  #include "log-tree.h"
  #include "wt-status.h"
  #include "hashmap.h"
 +#include "notes-utils.h"
 +#include "sigchain.h"
+ #include "unpack-trees.h"
+ #include "worktree.h"
+ #include "oidmap.h"
+ #include "oidset.h"
  
  #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
  
  const char sign_off_header[] = "Signed-off-by: ";
  static const char cherry_picked_prefix[] = "(cherry picked from commit ";
  
 +GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
 +
  GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
  
  static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
@@@ -120,6 -120,13 +124,13 @@@ static GIT_PATH_FUNC(rebase_path_stoppe
  static GIT_PATH_FUNC(rebase_path_rewritten_list, "rebase-merge/rewritten-list")
  static GIT_PATH_FUNC(rebase_path_rewritten_pending,
        "rebase-merge/rewritten-pending")
+ /*
+  * The path of the file listing refs that need to be deleted after the rebase
+  * finishes. This is used by the `label` command to record the need for cleanup.
+  */
+ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete")
  /*
   * The following files are written by git-rebase just after parsing the
   * command-line (and are only consumed, not modified, by the sequencer).
@@@ -134,51 -141,6 +145,51 @@@ static GIT_PATH_FUNC(rebase_path_strate
  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 int git_sequencer_config(const char *k, const char *v, void *cb)
 +{
 +      struct replay_opts *opts = cb;
 +      int status;
 +
 +      if (!strcmp(k, "commit.cleanup")) {
 +              const char *s;
 +
 +              status = git_config_string(&s, k, v);
 +              if (status)
 +                      return status;
 +
 +              if (!strcmp(s, "verbatim"))
 +                      opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
 +              else if (!strcmp(s, "whitespace"))
 +                      opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
 +              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
 +                      warning(_("invalid commit message cleanup mode '%s'"),
 +                                s);
 +
 +              return status;
 +      }
 +
 +      if (!strcmp(k, "commit.gpgsign")) {
 +              opts->gpg_sign = git_config_bool(k, v) ? xstrdup("") : NULL;
 +              return 0;
 +      }
 +
 +      status = git_gpg_config(k, v, NULL);
 +      if (status)
 +              return status;
 +
 +      return git_diff_basic_config(k, v, NULL);
 +}
 +
 +void sequencer_init_config(struct replay_opts *opts)
 +{
 +      opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
 +      git_config(git_sequencer_config, opts);
 +}
 +
  static inline int is_rebase_i(const struct replay_opts *opts)
  {
        return opts->action == REPLAY_INTERACTIVE_REBASE;
@@@ -244,18 -206,33 +255,33 @@@ static const char *gpg_sign_opt_quoted(
  
  int sequencer_remove_state(struct replay_opts *opts)
  {
-       struct strbuf dir = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT;
        int i;
  
+       if (strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
+               char *p = buf.buf;
+               while (*p) {
+                       char *eol = strchr(p, '\n');
+                       if (eol)
+                               *eol = '\0';
+                       if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0)
+                               warning(_("could not delete '%s'"), p);
+                       if (!eol)
+                               break;
+                       p = eol + 1;
+               }
+       }
        free(opts->gpg_sign);
        free(opts->strategy);
        for (i = 0; i < opts->xopts_nr; i++)
                free(opts->xopts[i]);
        free(opts->xopts);
  
-       strbuf_addstr(&dir, get_dir(opts));
-       remove_dir_recursively(&dir, 0);
-       strbuf_release(&dir);
+       strbuf_reset(&buf);
+       strbuf_addstr(&buf, get_dir(opts));
+       remove_dir_recursively(&buf, 0);
+       strbuf_release(&buf);
  
        return 0;
  }
@@@ -339,21 -316,25 +365,23 @@@ static void print_advice(int show_hint
  static int write_message(const void *buf, size_t len, const char *filename,
                         int append_eol)
  {
 -      static struct lock_file msg_file;
 +      struct lock_file msg_file = LOCK_INIT;
  
        int msg_fd = hold_lock_file_for_update(&msg_file, filename, 0);
        if (msg_fd < 0)
                return error_errno(_("could not lock '%s'"), filename);
        if (write_in_full(msg_fd, buf, len) < 0) {
+               error_errno(_("could not write to '%s'"), filename);
                rollback_lock_file(&msg_file);
-               return error_errno(_("could not write to '%s'"), filename);
+               return -1;
        }
        if (append_eol && write(msg_fd, "\n", 1) < 0) {
+               error_errno(_("could not write eol to '%s'"), filename);
                rollback_lock_file(&msg_file);
-               return error_errno(_("could not write eol to '%s'"), filename);
+               return -1;
        }
 -      if (commit_lock_file(&msg_file) < 0) {
 -              rollback_lock_file(&msg_file);
 -              return error(_("failed to finalize '%s'."), filename);
 -      }
 +      if (commit_lock_file(&msg_file) < 0)
 +              return error(_("failed to finalize '%s'"), filename);
  
        return 0;
  }
@@@ -483,7 -464,7 +511,7 @@@ static int do_recursive_merge(struct co
        struct tree *result, *next_tree, *base_tree, *head_tree;
        int clean;
        char **xopt;
 -      static struct lock_file index_lock;
 +      struct lock_file index_lock = LOCK_INIT;
  
        if (hold_locked_index(&index_lock, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
                fputs(o.obuf.buf, stdout);
        strbuf_release(&o.obuf);
        diff_warn_rename_limit("merge.renamelimit", o.needed_rename_limit, 0);
 -      if (clean < 0)
 +      if (clean < 0) {
 +              rollback_lock_file(&index_lock);
                return clean;
 +      }
  
 -      if (active_cache_changed &&
 -          write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &index_lock,
 +                             COMMIT_LOCK | SKIP_IF_UNCHANGED))
                /*
                 * TRANSLATORS: %s will be "revert", "cherry-pick" or
                 * "rebase -i".
                 */
                return error(_("%s: Unable to write new index file"),
                        _(action_name(opts)));
 -      rollback_lock_file(&index_lock);
 -
 -      if (opts->signoff)
 -              append_signoff(msgbuf, 0, 0);
  
        if (!clean)
                append_conflicts_hint(msgbuf);
@@@ -641,18 -624,6 +669,18 @@@ static int read_env_script(struct argv_
        return 0;
  }
  
 +static char *get_author(const char *message)
 +{
 +      size_t len;
 +      const char *a;
 +
 +      a = find_commit_header(message, "author", &len);
 +      if (a)
 +              return xmemdupz(a, len);
 +
 +      return NULL;
 +}
 +
  static const char staged_changes_advice[] =
  N_("you have staged changes in your working tree\n"
  "If these changes are meant to be squashed into the previous commit, run:\n"
@@@ -715,6 -686,8 +743,6 @@@ static int run_git_commit(const char *d
                argv_array_push(&cmd.args, "--amend");
        if (opts->gpg_sign)
                argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
 -      if (opts->signoff)
 -              argv_array_push(&cmd.args, "-s");
        if (defmsg)
                argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
        if ((flags & CLEANUP_MSG))
        return run_command(&cmd);
  }
  
 +static int rest_is_empty(const struct strbuf *sb, int start)
 +{
 +      int i, eol;
 +      const char *nl;
 +
 +      /* Check if the rest is just whitespace and Signed-off-by's. */
 +      for (i = start; i < sb->len; i++) {
 +              nl = memchr(sb->buf + i, '\n', sb->len - i);
 +              if (nl)
 +                      eol = nl - sb->buf;
 +              else
 +                      eol = sb->len;
 +
 +              if (strlen(sign_off_header) <= eol - i &&
 +                  starts_with(sb->buf + i, sign_off_header)) {
 +                      i = eol;
 +                      continue;
 +              }
 +              while (i < eol)
 +                      if (!isspace(sb->buf[i++]))
 +                              return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +/*
 + * Find out if the message in the strbuf contains only whitespace and
 + * Signed-off-by lines.
 + */
 +int message_is_empty(const struct strbuf *sb,
 +                   enum commit_msg_cleanup_mode cleanup_mode)
 +{
 +      if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
 +              return 0;
 +      return rest_is_empty(sb, 0);
 +}
 +
 +/*
 + * See if the user edited the message in the editor or left what
 + * was in the template intact
 + */
 +int template_untouched(const struct strbuf *sb, const char *template_file,
 +                     enum commit_msg_cleanup_mode cleanup_mode)
 +{
 +      struct strbuf tmpl = STRBUF_INIT;
 +      const char *start;
 +
 +      if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
 +              return 0;
 +
 +      if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
 +              return 0;
 +
 +      strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 +      if (!skip_prefix(sb->buf, tmpl.buf, &start))
 +              start = sb->buf;
 +      strbuf_release(&tmpl);
 +      return rest_is_empty(sb, start - sb->buf);
 +}
 +
 +int update_head_with_reflog(const struct commit *old_head,
 +                          const struct object_id *new_head,
 +                          const char *action, const struct strbuf *msg,
 +                          struct strbuf *err)
 +{
 +      struct ref_transaction *transaction;
 +      struct strbuf sb = STRBUF_INIT;
 +      const char *nl;
 +      int ret = 0;
 +
 +      if (action) {
 +              strbuf_addstr(&sb, action);
 +              strbuf_addstr(&sb, ": ");
 +      }
 +
 +      nl = strchr(msg->buf, '\n');
 +      if (nl) {
 +              strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
 +      } else {
 +              strbuf_addbuf(&sb, msg);
 +              strbuf_addch(&sb, '\n');
 +      }
 +
 +      transaction = ref_transaction_begin(err);
 +      if (!transaction ||
 +          ref_transaction_update(transaction, "HEAD", new_head,
 +                                 old_head ? &old_head->object.oid : &null_oid,
 +                                 0, sb.buf, err) ||
 +          ref_transaction_commit(transaction, err)) {
 +              ret = -1;
 +      }
 +      ref_transaction_free(transaction);
 +      strbuf_release(&sb);
 +
 +      return ret;
 +}
 +
 +static int run_rewrite_hook(const struct object_id *oldoid,
 +                          const struct object_id *newoid)
 +{
 +      struct child_process proc = CHILD_PROCESS_INIT;
 +      const char *argv[3];
 +      int code;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      argv[0] = find_hook("post-rewrite");
 +      if (!argv[0])
 +              return 0;
 +
 +      argv[1] = "amend";
 +      argv[2] = NULL;
 +
 +      proc.argv = argv;
 +      proc.in = -1;
 +      proc.stdout_to_stderr = 1;
 +
 +      code = start_command(&proc);
 +      if (code)
 +              return code;
 +      strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
 +      sigchain_push(SIGPIPE, SIG_IGN);
 +      write_in_full(proc.in, sb.buf, sb.len);
 +      close(proc.in);
 +      strbuf_release(&sb);
 +      sigchain_pop(SIGPIPE);
 +      return finish_command(&proc);
 +}
 +
 +void commit_post_rewrite(const struct commit *old_head,
 +                       const struct object_id *new_head)
 +{
 +      struct notes_rewrite_cfg *cfg;
 +
 +      cfg = init_copy_notes_for_rewrite("amend");
 +      if (cfg) {
 +              /* we are amending, so old_head is not NULL */
 +              copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
 +              finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
 +      }
 +      run_rewrite_hook(&old_head->object.oid, new_head);
 +}
 +
 +static int run_prepare_commit_msg_hook(struct strbuf *msg, const char *commit)
 +{
 +      struct argv_array hook_env = ARGV_ARRAY_INIT;
 +      int ret;
 +      const char *name;
 +
 +      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", get_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)
 +              ret = error(_("'prepare-commit-msg' hook failed"));
 +      argv_array_clear(&hook_env);
 +
 +      return ret;
 +}
 +
 +static const char implicit_ident_advice_noconfig[] =
 +N_("Your name and email address were configured automatically based\n"
 +"on your username and hostname. Please check that they are accurate.\n"
 +"You can suppress this message by setting them explicitly. Run the\n"
 +"following command and follow the instructions in your editor to edit\n"
 +"your configuration file:\n"
 +"\n"
 +"    git config --global --edit\n"
 +"\n"
 +"After doing this, you may fix the identity used for this commit with:\n"
 +"\n"
 +"    git commit --amend --reset-author\n");
 +
 +static const char implicit_ident_advice_config[] =
 +N_("Your name and email address were configured automatically based\n"
 +"on your username and hostname. Please check that they are accurate.\n"
 +"You can suppress this message by setting them explicitly:\n"
 +"\n"
 +"    git config --global user.name \"Your Name\"\n"
 +"    git config --global user.email you@example.com\n"
 +"\n"
 +"After doing this, you may fix the identity used for this commit with:\n"
 +"\n"
 +"    git commit --amend --reset-author\n");
 +
 +static const char *implicit_ident_advice(void)
 +{
 +      char *user_config = expand_user_path("~/.gitconfig", 0);
 +      char *xdg_config = xdg_config_home("config");
 +      int config_exists = file_exists(user_config) || file_exists(xdg_config);
 +
 +      free(user_config);
 +      free(xdg_config);
 +
 +      if (config_exists)
 +              return _(implicit_ident_advice_config);
 +      else
 +              return _(implicit_ident_advice_noconfig);
 +
 +}
 +
 +void print_commit_summary(const char *prefix, const struct object_id *oid,
 +                        unsigned int flags)
 +{
 +      struct rev_info rev;
 +      struct commit *commit;
 +      struct strbuf format = STRBUF_INIT;
 +      const char *head;
 +      struct pretty_print_context pctx = {0};
 +      struct strbuf author_ident = STRBUF_INIT;
 +      struct strbuf committer_ident = STRBUF_INIT;
 +
 +      commit = lookup_commit(oid);
 +      if (!commit)
 +              die(_("couldn't look up newly created commit"));
 +      if (parse_commit(commit))
 +              die(_("could not parse newly created commit"));
 +
 +      strbuf_addstr(&format, "format:%h] %s");
 +
 +      format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
 +      format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
 +      if (strbuf_cmp(&author_ident, &committer_ident)) {
 +              strbuf_addstr(&format, "\n Author: ");
 +              strbuf_addbuf_percentquote(&format, &author_ident);
 +      }
 +      if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
 +              struct strbuf date = STRBUF_INIT;
 +
 +              format_commit_message(commit, "%ad", &date, &pctx);
 +              strbuf_addstr(&format, "\n Date: ");
 +              strbuf_addbuf_percentquote(&format, &date);
 +              strbuf_release(&date);
 +      }
 +      if (!committer_ident_sufficiently_given()) {
 +              strbuf_addstr(&format, "\n Committer: ");
 +              strbuf_addbuf_percentquote(&format, &committer_ident);
 +              if (advice_implicit_identity) {
 +                      strbuf_addch(&format, '\n');
 +                      strbuf_addstr(&format, implicit_ident_advice());
 +              }
 +      }
 +      strbuf_release(&author_ident);
 +      strbuf_release(&committer_ident);
 +
 +      init_revisions(&rev, prefix);
 +      setup_revisions(0, NULL, &rev, NULL);
 +
 +      rev.diff = 1;
 +      rev.diffopt.output_format =
 +              DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
 +
 +      rev.verbose_header = 1;
 +      rev.show_root_diff = 1;
 +      get_commit_format(format.buf, &rev);
 +      rev.always_show_header = 0;
 +      rev.diffopt.detect_rename = DIFF_DETECT_RENAME;
 +      rev.diffopt.break_opt = 0;
 +      diff_setup_done(&rev.diffopt);
 +
 +      head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
 +      if (!head)
 +              die_errno(_("unable to resolve HEAD after creating commit"));
 +      if (!strcmp(head, "HEAD"))
 +              head = _("detached HEAD");
 +      else
 +              skip_prefix(head, "refs/heads/", &head);
 +      printf("[%s%s ", head, (flags & SUMMARY_INITIAL_COMMIT) ?
 +                                              _(" (root-commit)") : "");
 +
 +      if (!log_tree_commit(&rev, commit)) {
 +              rev.always_show_header = 1;
 +              rev.use_terminator = 1;
 +              log_tree_commit(&rev, commit);
 +      }
 +
 +      strbuf_release(&format);
 +}
 +
 +static int parse_head(struct commit **head)
 +{
 +      struct commit *current_head;
 +      struct object_id oid;
 +
 +      if (get_oid("HEAD", &oid)) {
 +              current_head = NULL;
 +      } else {
 +              current_head = lookup_commit_reference(&oid);
 +              if (!current_head)
 +                      return error(_("could not parse HEAD"));
 +              if (oidcmp(&oid, &current_head->object.oid)) {
 +                      warning(_("HEAD %s is not a commit!"),
 +                              oid_to_hex(&oid));
 +              }
 +              if (parse_commit(current_head))
 +                      return error(_("could not parse HEAD commit"));
 +      }
 +      *head = current_head;
 +
 +      return 0;
 +}
 +
 +/*
 + * Try to commit without forking 'git commit'. In some cases we need
 + * to run 'git commit' to display an error message
 + *
 + * Returns:
 + *  -1 - error unable to commit
 + *   0 - success
 + *   1 - run 'git commit'
 + */
 +static int try_to_commit(struct strbuf *msg, const char *author,
 +                       struct replay_opts *opts, unsigned int flags,
 +                       struct object_id *oid)
 +{
 +      struct object_id tree;
 +      struct commit *current_head;
 +      struct commit_list *parents = NULL;
 +      struct commit_extra_header *extra = NULL;
 +      struct strbuf err = STRBUF_INIT;
 +      struct strbuf commit_msg = STRBUF_INIT;
 +      char *amend_author = NULL;
 +      const char *hook_commit = NULL;
 +      enum commit_msg_cleanup_mode cleanup;
 +      int res = 0;
 +
 +      if (parse_head(&current_head))
 +              return -1;
 +
 +      if (flags & AMEND_MSG) {
 +              const char *exclude_gpgsig[] = { "gpgsig", NULL };
 +              const char *out_enc = get_commit_output_encoding();
 +              const char *message = logmsg_reencode(current_head, NULL,
 +                                                    out_enc);
 +
 +              if (!msg) {
 +                      const char *orig_message = NULL;
 +
 +                      find_commit_subject(message, &orig_message);
 +                      msg = &commit_msg;
 +                      strbuf_addstr(msg, orig_message);
 +                      hook_commit = "HEAD";
 +              }
 +              author = amend_author = get_author(message);
 +              unuse_commit_buffer(current_head, message);
 +              if (!author) {
 +                      res = error(_("unable to parse commit author"));
 +                      goto out;
 +              }
 +              parents = copy_commit_list(current_head->parents);
 +              extra = read_commit_extra_headers(current_head, exclude_gpgsig);
 +      } else if (current_head) {
 +              commit_list_insert(current_head, &parents);
 +      }
 +
 +      if (write_cache_as_tree(tree.hash, 0, NULL)) {
 +              res = error(_("git write-tree failed to write a tree"));
 +              goto out;
 +      }
 +
 +      if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
 +                                            &current_head->tree->object.oid :
 +                                            &empty_tree_oid, &tree)) {
 +              res = 1; /* run 'git commit' to display error message */
 +              goto out;
 +      }
 +
 +      if (find_hook("prepare-commit-msg")) {
 +              res = run_prepare_commit_msg_hook(msg, hook_commit);
 +              if (res)
 +                      goto out;
 +              if (strbuf_read_file(&commit_msg, git_path_commit_editmsg(),
 +                                   2048) < 0) {
 +                      res = error_errno(_("unable to read commit message "
 +                                            "from '%s'"),
 +                                          git_path_commit_editmsg());
 +                      goto out;
 +              }
 +              msg = &commit_msg;
 +      }
 +
 +      cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
 +                                        opts->default_msg_cleanup;
 +
 +      if (cleanup != COMMIT_MSG_CLEANUP_NONE)
 +              strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
 +      if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
 +              res = 1; /* run 'git commit' to display error message */
 +              goto out;
 +      }
 +
 +      if (commit_tree_extended(msg->buf, msg->len, &tree, parents,
 +                               oid, author, opts->gpg_sign, extra)) {
 +              res = error(_("failed to write commit object"));
 +              goto out;
 +      }
 +
 +      if (update_head_with_reflog(current_head, oid,
 +                                  getenv("GIT_REFLOG_ACTION"), msg, &err)) {
 +              res = error("%s", err.buf);
 +              goto out;
 +      }
 +
 +      if (flags & AMEND_MSG)
 +              commit_post_rewrite(current_head, oid);
 +
 +out:
 +      free_commit_extra_headers(extra);
 +      strbuf_release(&err);
 +      strbuf_release(&commit_msg);
 +      free(amend_author);
 +
 +      return res;
 +}
 +
 +static int do_commit(const char *msg_file, const char *author,
 +                   struct replay_opts *opts, unsigned int flags)
 +{
 +      int res = 1;
 +
 +      if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {
 +              struct object_id oid;
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
 +                      return error_errno(_("unable to read commit message "
 +                                           "from '%s'"),
 +                                         msg_file);
 +
 +              res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
 +                                  &oid);
 +              strbuf_release(&sb);
 +              if (!res) {
 +                      unlink(git_path_cherry_pick_head());
 +                      unlink(git_path_merge_msg());
 +                      if (!is_rebase_i(opts))
 +                              print_commit_summary(NULL, &oid,
 +                                              SUMMARY_SHOW_AUTHOR_DATE);
 +                      return res;
 +              }
 +      }
 +      if (res == 1)
 +              return run_git_commit(msg_file, opts, flags);
 +
 +      return res;
 +}
 +
  static int is_original_commit_empty(struct commit *commit)
  {
        const struct object_id *ptree_oid;
@@@ -1277,6 -795,10 +1305,10 @@@ enum todo_command 
        TODO_SQUASH,
        /* commands that do something else than handling a single commit */
        TODO_EXEC,
+       TODO_LABEL,
+       TODO_RESET,
+       TODO_MERGE,
+       TODO_MERGE_AND_EDIT,
        /* commands that do nothing but are counted for reporting progress */
        TODO_NOOP,
        TODO_DROP,
@@@ -1295,6 -817,10 +1327,10 @@@ static struct 
        { 'f', "fixup" },
        { 's', "squash" },
        { 'x', "exec" },
+       { 'l', "label" },
+       { 't', "reset" },
+       { 'm', "merge" },
+       { 0, "merge" }, /* MERGE_AND_EDIT */
        { 0,   "noop" },
        { 'd', "drop" },
        { 0,   NULL }
@@@ -1462,7 -988,6 +1498,7 @@@ static int do_pick_commit(enum todo_com
        struct object_id head;
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
 +      char *author = NULL;
        struct commit_message msg = { NULL, NULL, NULL, NULL };
        struct strbuf msgbuf = STRBUF_INIT;
        int res, unborn = 0, allow;
                        strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
                        strbuf_addstr(&msgbuf, ")\n");
                }
 +              if (!is_fixup(command))
 +                      author = get_author(msg.message);
        }
  
        if (command == TODO_REWORD)
                }
        }
  
 +      if (opts->signoff)
 +              append_signoff(&msgbuf, 0, 0);
 +
        if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
                res = -1;
        else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
                goto leave;
        } else if (allow)
                flags |= ALLOW_EMPTY;
 -      if (!opts->no_commit)
 +      if (!opts->no_commit) {
  fast_forward_edit:
 -              res = run_git_commit(msg_file, opts, flags);
 +              if (author || command == TODO_REVERT || (flags & AMEND_MSG))
 +                      res = do_commit(msg_file, author, opts, flags);
 +              else
 +                      res = error(_("unable to parse commit author"));
 +      }
  
        if (!res && final_fixup) {
                unlink(rebase_path_fixup_msg());
  
  leave:
        free_message(commit, &msg);
 +      free(author);
        update_abort_safety_file();
  
        return res;
@@@ -1704,7 -1219,7 +1740,7 @@@ static int prepare_revs(struct replay_o
  
  static int read_and_refresh_cache(struct replay_opts *opts)
  {
 -      static struct lock_file index_lock;
 +      struct lock_file index_lock = LOCK_INIT;
        int index_fd = hold_locked_index(&index_lock, 0);
        if (read_index_preload(&the_index, NULL) < 0) {
                rollback_lock_file(&index_lock);
                        _(action_name(opts)));
        }
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
 -      if (the_index.cache_changed && index_fd >= 0) {
 -              if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
 +      if (index_fd >= 0) {
 +              if (write_locked_index(&the_index, &index_lock,
 +                                     COMMIT_LOCK | SKIP_IF_UNCHANGED)) {
                        return error(_("git %s: failed to refresh the index"),
                                _(action_name(opts)));
                }
        }
 -      rollback_lock_file(&index_lock);
        return 0;
  }
  
@@@ -1800,13 -1315,29 +1836,29 @@@ static int parse_insn_line(struct todo_
                return error(_("missing arguments for %s"),
                             command_to_string(item->command));
  
-       if (item->command == TODO_EXEC) {
+       if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
+           item->command == TODO_RESET) {
                item->commit = NULL;
                item->arg = bol;
                item->arg_len = (int)(eol - bol);
                return 0;
        }
  
+       if (item->command == TODO_MERGE) {
+               if (skip_prefix(bol, "-C", &bol))
+                       bol += strspn(bol, " \t");
+               else if (skip_prefix(bol, "-c", &bol)) {
+                       bol += strspn(bol, " \t");
+                       item->command = TODO_MERGE_AND_EDIT;
+               } else {
+                       item->command = TODO_MERGE_AND_EDIT;
+                       item->commit = NULL;
+                       item->arg = bol;
+                       item->arg_len = (int)(eol - bol);
+                       return 0;
+               }
+       }
        end_of_object_name = (char *) bol + strcspn(bol, " \t\n");
        saved = *end_of_object_name;
        *end_of_object_name = '\0';
@@@ -1868,31 -1399,22 +1920,31 @@@ static int count_commands(struct todo_l
        return count;
  }
  
 +static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path)
 +{
 +      int fd;
 +      ssize_t len;
 +
 +      fd = open(path, O_RDONLY);
 +      if (fd < 0)
 +              return error_errno(_("could not open '%s'"), path);
 +      len = strbuf_read(sb, fd, 0);
 +      close(fd);
 +      if (len < 0)
 +              return error(_("could not read '%s'."), path);
 +      return len;
 +}
 +
  static int read_populate_todo(struct todo_list *todo_list,
                        struct replay_opts *opts)
  {
        struct stat st;
        const char *todo_file = get_todo_path(opts);
 -      int fd, res;
 +      int res;
  
        strbuf_reset(&todo_list->buf);
 -      fd = open(todo_file, O_RDONLY);
 -      if (fd < 0)
 -              return error_errno(_("could not open '%s'"), todo_file);
 -      if (strbuf_read(&todo_list->buf, fd, 0) < 0) {
 -              close(fd);
 -              return error(_("could not read '%s'."), todo_file);
 -      }
 -      close(fd);
 +      if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
 +              return -1;
  
        res = stat(todo_file, &st);
        if (res)
@@@ -2107,24 -1629,29 +2159,24 @@@ static int create_seq_dir(void
  
  static int save_head(const char *head)
  {
 -      static struct lock_file head_lock;
 +      struct lock_file head_lock = LOCK_INIT;
        struct strbuf buf = STRBUF_INIT;
        int fd;
        ssize_t written;
  
        fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
 -      if (fd < 0) {
 -              error_errno(_("could not lock HEAD"));
 -              rollback_lock_file(&head_lock);
 -              return -1;
 -      }
 +      if (fd < 0)
 +              return error_errno(_("could not lock HEAD"));
        strbuf_addf(&buf, "%s\n", head);
        written = write_in_full(fd, buf.buf, buf.len);
        strbuf_release(&buf);
        if (written < 0) {
+               error_errno(_("could not write to '%s'"), git_path_head_file());
                rollback_lock_file(&head_lock);
-               return error_errno(_("could not write to '%s'"),
-                                  git_path_head_file());
+               return -1;
        }
 -      if (commit_lock_file(&head_lock) < 0) {
 -              rollback_lock_file(&head_lock);
 -              return error(_("failed to finalize '%s'."), git_path_head_file());
 -      }
 +      if (commit_lock_file(&head_lock) < 0)
 +              return error(_("failed to finalize '%s'"), git_path_head_file());
        return 0;
  }
  
@@@ -2228,7 -1755,7 +2280,7 @@@ fail
  
  static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
  {
 -      static struct lock_file todo_lock;
 +      struct lock_file todo_lock = LOCK_INIT;
        const char *todo_path = get_todo_path(opts);
        int next = todo_list->current, offset, fd;
  
                        todo_list->buf.len - offset) < 0)
                return error_errno(_("could not write to '%s'"), todo_path);
        if (commit_lock_file(&todo_lock) < 0)
 -              return error(_("failed to finalize '%s'."), todo_path);
 +              return error(_("failed to finalize '%s'"), todo_path);
  
        if (is_rebase_i(opts)) {
                const char *done_path = rebase_path_done();
@@@ -2318,9 -1845,6 +2370,9 @@@ static int make_patch(struct commit *co
        p = short_commit_name(commit);
        if (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)
                return -1;
 +      if (update_ref("rebase", "REBASE_HEAD", &commit->object.oid,
 +                     NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
 +              res |= error(_("could not update %s"), "REBASE_HEAD");
  
        strbuf_addf(&buf, "%s/patch", get_dir(opts));
        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
@@@ -2448,6 -1972,304 +2500,304 @@@ static int do_exec(const char *command_
        return status;
  }
  
+ static int safe_append(const char *filename, const char *fmt, ...)
+ {
+       va_list ap;
+       struct lock_file lock = LOCK_INIT;
+       int fd = hold_lock_file_for_update(&lock, filename,
+                                          LOCK_REPORT_ON_ERROR);
+       struct strbuf buf = STRBUF_INIT;
+       if (fd < 0)
+               return -1;
+       if (strbuf_read_file(&buf, filename, 0) < 0 && errno != ENOENT)
+               return error_errno(_("could not read '%s'"), filename);
+       strbuf_complete(&buf, '\n');
+       va_start(ap, fmt);
+       strbuf_vaddf(&buf, fmt, ap);
+       va_end(ap);
+       if (write_in_full(fd, buf.buf, buf.len) < 0) {
+               error_errno(_("could not write to '%s'"), filename);
+               strbuf_release(&buf);
+               rollback_lock_file(&lock);
+               return -1;
+       }
+       if (commit_lock_file(&lock) < 0) {
+               strbuf_release(&buf);
+               rollback_lock_file(&lock);
+               return error(_("failed to finalize '%s'"), filename);
+       }
+       strbuf_release(&buf);
+       return 0;
+ }
+ static int do_label(const char *name, int len)
+ {
+       struct ref_store *refs = get_main_ref_store();
+       struct ref_transaction *transaction;
+       struct strbuf ref_name = STRBUF_INIT, err = STRBUF_INIT;
+       struct strbuf msg = STRBUF_INIT;
+       int ret = 0;
+       struct object_id head_oid;
+       if (len == 1 && *name == '#')
+               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);
+       transaction = ref_store_transaction_begin(refs, &err);
+       if (!transaction) {
+               error("%s", err.buf);
+               ret = -1;
+       } else if (get_oid("HEAD", &head_oid)) {
+               error(_("could not read HEAD"));
+               ret = -1;
+       } else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
+                                         NULL, 0, msg.buf, &err) < 0 ||
+                  ref_transaction_commit(transaction, &err)) {
+               error("%s", err.buf);
+               ret = -1;
+       }
+       ref_transaction_free(transaction);
+       strbuf_release(&err);
+       strbuf_release(&msg);
+       if (!ret)
+               ret = safe_append(rebase_path_refs_to_delete(),
+                                 "%s\n", ref_name.buf);
+       strbuf_release(&ref_name);
+       return ret;
+ }
+ static int do_reset(const char *name, int len, struct replay_opts *opts)
+ {
+       struct strbuf ref_name = STRBUF_INIT;
+       struct object_id oid;
+       struct lock_file lock = LOCK_INIT;
+       struct tree_desc desc;
+       struct tree *tree;
+       struct unpack_trees_options unpack_tree_opts;
+       int ret = 0, i;
+       if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
+               return -1;
+       /* Determine the length of the label */
+       for (i = 0; i < len; i++)
+               if (isspace(name[i]))
+                       len = i;
+       strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
+       if (get_oid(ref_name.buf, &oid) &&
+           get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) {
+               error(_("could not read '%s'"), ref_name.buf);
+               rollback_lock_file(&lock);
+               strbuf_release(&ref_name);
+               return -1;
+       }
+       memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
+       unpack_tree_opts.head_idx = 1;
+       unpack_tree_opts.src_index = &the_index;
+       unpack_tree_opts.dst_index = &the_index;
+       unpack_tree_opts.fn = oneway_merge;
+       unpack_tree_opts.merge = 1;
+       unpack_tree_opts.update = 1;
+       unpack_tree_opts.reset = 1;
+       if (read_cache_unmerged()) {
+               rollback_lock_file(&lock);
+               strbuf_release(&ref_name);
+               return error_resolve_conflict(_(action_name(opts)));
+       }
+       if (!fill_tree_descriptor(&desc, &oid)) {
+               error(_("failed to find tree of %s"), oid_to_hex(&oid));
+               rollback_lock_file(&lock);
+               free((void *)desc.buffer);
+               strbuf_release(&ref_name);
+               return -1;
+       }
+       if (unpack_trees(1, &desc, &unpack_tree_opts)) {
+               rollback_lock_file(&lock);
+               free((void *)desc.buffer);
+               strbuf_release(&ref_name);
+               return -1;
+       }
+       tree = parse_tree_indirect(&oid);
+       prime_cache_tree(&the_index, tree);
+       if (write_locked_index(&the_index, &lock, COMMIT_LOCK) < 0)
+               ret = error(_("could not write index"));
+       free((void *)desc.buffer);
+       if (!ret) {
+               struct strbuf msg = STRBUF_INIT;
+               strbuf_addf(&msg, "(rebase -i) reset '%.*s'", len, name);
+               ret = update_ref(msg.buf, "HEAD", &oid, NULL, 0,
+                                UPDATE_REFS_MSG_ON_ERR);
+               strbuf_release(&msg);
+       }
+       strbuf_release(&ref_name);
+       return ret;
+ }
+ static int do_merge(struct commit *commit, const char *arg, int arg_len,
+                   int run_commit_flags, struct replay_opts *opts)
+ {
+       int merge_arg_len;
+       struct strbuf ref_name = STRBUF_INIT;
+       struct commit *head_commit, *merge_commit, *i;
+       struct commit_list *common, *j, *reversed = NULL;
+       struct merge_options o;
+       int can_fast_forward, ret;
+       static struct lock_file lock;
+       for (merge_arg_len = 0; merge_arg_len < arg_len; merge_arg_len++)
+               if (isspace(arg[merge_arg_len]))
+                       break;
+       if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
+               return -1;
+       head_commit = lookup_commit_reference_by_name("HEAD");
+       if (!head_commit) {
+               rollback_lock_file(&lock);
+               return error(_("cannot merge without a current revision"));
+       }
+       if (commit) {
+               const char *message = get_commit_buffer(commit, NULL);
+               const char *body;
+               int len;
+               if (!message) {
+                       rollback_lock_file(&lock);
+                       return error(_("could not get commit message of '%s'"),
+                                    oid_to_hex(&commit->object.oid));
+               }
+               write_author_script(message);
+               find_commit_subject(message, &body);
+               len = strlen(body);
+               if (write_message(body, len, git_path_merge_msg(), 0) < 0) {
+                       error_errno(_("could not write '%s'"),
+                                   git_path_merge_msg());
+                       unuse_commit_buffer(commit, message);
+                       rollback_lock_file(&lock);
+                       return -1;
+               }
+               unuse_commit_buffer(commit, message);
+       } else {
+               const char *p = arg + merge_arg_len;
+               struct strbuf buf = STRBUF_INIT;
+               int len;
+               strbuf_addf(&buf, "author %s", git_author_info(0));
+               write_author_script(buf.buf);
+               strbuf_reset(&buf);
+               p += strspn(p, " \t");
+               if (*p == '#' && isspace(p[1]))
+                       p += 1 + strspn(p + 1, " \t");
+               if (*p)
+                       len = strlen(p);
+               else {
+                       strbuf_addf(&buf, "Merge branch '%.*s'",
+                                   merge_arg_len, arg);
+                       p = buf.buf;
+                       len = buf.len;
+               }
+               if (write_message(p, len, git_path_merge_msg(), 0) < 0) {
+                       error_errno(_("could not write '%s'"),
+                                   git_path_merge_msg());
+                       strbuf_release(&buf);
+                       rollback_lock_file(&lock);
+                       return -1;
+               }
+               strbuf_release(&buf);
+       }
+       /*
+        * If HEAD is not identical to the parent of the original merge commit,
+        * we cannot fast-forward.
+        */
+       can_fast_forward = opts->allow_ff && commit && commit->parents &&
+               !oidcmp(&commit->parents->item->object.oid,
+                       &head_commit->object.oid);
+       strbuf_addf(&ref_name, "refs/rewritten/%.*s", merge_arg_len, arg);
+       merge_commit = lookup_commit_reference_by_name(ref_name.buf);
+       if (!merge_commit) {
+               /* fall back to non-rewritten ref or commit */
+               strbuf_splice(&ref_name, 0, strlen("refs/rewritten/"), "", 0);
+               merge_commit = lookup_commit_reference_by_name(ref_name.buf);
+       }
+       if (!merge_commit) {
+               error(_("could not resolve '%s'"), ref_name.buf);
+               strbuf_release(&ref_name);
+               rollback_lock_file(&lock);
+               return -1;
+       }
+       if (can_fast_forward && commit->parents->next &&
+           !commit->parents->next->next &&
+           !oidcmp(&commit->parents->next->item->object.oid,
+                   &merge_commit->object.oid)) {
+               strbuf_release(&ref_name);
+               rollback_lock_file(&lock);
+               return fast_forward_to(&commit->object.oid,
+                                      &head_commit->object.oid, 0, opts);
+       }
+       write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
+                     git_path_merge_head(), 0);
+       write_message("no-ff", 5, git_path_merge_mode(), 0);
+       common = get_merge_bases(head_commit, merge_commit);
+       for (j = common; j; j = j->next)
+               commit_list_insert(j->item, &reversed);
+       free_commit_list(common);
+       read_cache();
+       init_merge_options(&o);
+       o.branch1 = "HEAD";
+       o.branch2 = ref_name.buf;
+       o.buffer_output = 2;
+       ret = merge_recursive(&o, head_commit, merge_commit, reversed, &i);
+       if (ret <= 0)
+               fputs(o.obuf.buf, stdout);
+       strbuf_release(&o.obuf);
+       if (ret < 0) {
+               strbuf_release(&ref_name);
+               rollback_lock_file(&lock);
+               return error(_("conflicts while merging '%.*s'"),
+                            merge_arg_len, arg);
+       }
+       if (active_cache_changed &&
+           write_locked_index(&the_index, &lock, COMMIT_LOCK)) {
+               strbuf_release(&ref_name);
+               return error(_("merge: Unable to write new index file"));
+       }
+       rollback_lock_file(&lock);
+       ret = run_git_commit(git_path_merge_msg(), opts, run_commit_flags);
+       strbuf_release(&ref_name);
+       return ret;
+ }
  static int is_final_fixup(struct todo_list *todo_list)
  {
        int i = todo_list->current;
@@@ -2572,7 -2394,6 +2922,7 @@@ static int pick_commits(struct todo_lis
                        unlink(rebase_path_author_script());
                        unlink(rebase_path_stopped_sha());
                        unlink(rebase_path_amend());
 +                      delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
                }
                if (item->command <= TODO_SQUASH) {
                        if (is_rebase_i(opts))
                                /* `current` will be incremented below */
                                todo_list->current = -1;
                        }
+               } else if (item->command == TODO_LABEL)
+                       res = do_label(item->arg, item->arg_len);
+               else if (item->command == TODO_RESET)
+                       res = do_reset(item->arg, item->arg_len, opts);
+               else if (item->command == TODO_MERGE ||
+                        item->command == TODO_MERGE_AND_EDIT) {
+                       res = do_merge(item->commit, item->arg, item->arg_len,
+                                      item->command == TODO_MERGE_AND_EDIT ?
+                                      EDIT_MSG | VERIFY_MSG : 0, opts);
+                       if (item->commit)
+                               record_in_rewritten(&item->commit->object.oid,
+                                                   peek_command(todo_list, 1));
                } else if (!is_noop(item->command))
                        return error(_("unknown command %d"), item->command);
  
@@@ -2878,7 -2711,7 +3240,7 @@@ int sequencer_pick_revisions(struct rep
                        if (!lookup_commit_reference_gently(&oid, 1)) {
                                enum object_type type = sha1_object_info(oid.hash, NULL);
                                return error(_("%s: can't cherry-pick a %s"),
 -                                      name, typename(type));
 +                                      name, type_name(type));
                        }
                } else
                        return error(_("%s: bad revision"), name);
@@@ -2986,6 -2819,345 +3348,345 @@@ void append_signoff(struct strbuf *msgb
        strbuf_release(&sob);
  }
  
+ struct labels_entry {
+       struct hashmap_entry entry;
+       char label[FLEX_ARRAY];
+ };
+ static int labels_cmp(const void *fndata, const struct labels_entry *a,
+                     const struct labels_entry *b, const void *key)
+ {
+       return key ? strcmp(a->label, key) : strcmp(a->label, b->label);
+ }
+ struct string_entry {
+       struct oidmap_entry entry;
+       char string[FLEX_ARRAY];
+ };
+ struct label_state {
+       struct oidmap commit2label;
+       struct hashmap labels;
+       struct strbuf buf;
+ };
+ static const char *label_oid(struct object_id *oid, const char *label,
+                            struct label_state *state)
+ {
+       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);
+       if (string_entry)
+               return string_entry->string;
+       /*
+        * For "uninteresting" commits, i.e. commits that are not to be
+        * rebased, and which can therefore not be labeled, we use a unique
+        * abbreviation of the commit name. This is slightly more complicated
+        * than calling find_unique_abbrev() because we also need to make
+        * sure that the abbreviation does not conflict with any other
+        * label.
+        *
+        * We disallow "interesting" commits to be labeled by a string that
+        * is a valid full-length hash, to ensure that we always can find an
+        * abbreviation for any uninteresting commit's names that does not
+        * clash with any other label.
+        */
+       if (!label) {
+               char *p;
+               strbuf_reset(&state->buf);
+               strbuf_grow(&state->buf, GIT_SHA1_HEXSZ);
+               label = p = state->buf.buf;
+               find_unique_abbrev_r(p, oid->hash, default_abbrev);
+               /*
+                * We may need to extend the abbreviated hash so that there is
+                * no conflicting label.
+                */
+               if (hashmap_get_from_hash(&state->labels, strihash(p), p)) {
+                       size_t i = strlen(p) + 1;
+                       oid_to_hex_r(p, oid);
+                       for (; i < GIT_SHA1_HEXSZ; i++) {
+                               char save = p[i];
+                               p[i] = '\0';
+                               if (!hashmap_get_from_hash(&state->labels,
+                                                          strihash(p), p))
+                                       break;
+                               p[i] = save;
+                       }
+               }
+       } else if (((len = strlen(label)) == GIT_SHA1_RAWSZ &&
+                   !get_oid_hex(label, &dummy)) ||
+                  (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.
+                */
+               struct strbuf *buf = &state->buf;
+               strbuf_reset(buf);
+               strbuf_add(buf, label, 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;
+               }
+               label = buf->buf;
+       }
+       FLEX_ALLOC_STR(labels_entry, label, label);
+       hashmap_entry_init(labels_entry, strihash(label));
+       hashmap_add(&state->labels, labels_entry);
+       FLEX_ALLOC_STR(string_entry, string, label);
+       oidcpy(&string_entry->entry.oid, oid);
+       oidmap_put(&state->commit2label, string_entry);
+       return string_entry->string;
+ }
+ static int make_script_with_merges(struct pretty_print_context *pp,
+                                  struct rev_info *revs, FILE *out,
+                                  unsigned flags)
+ {
+       int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
+       int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
+       struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
+       struct strbuf label = STRBUF_INIT;
+       struct commit_list *commits = NULL, **tail = &commits, *iter;
+       struct commit_list *tips = NULL, **tips_tail = &tips;
+       struct commit *commit;
+       struct oidmap commit2todo = OIDMAP_INIT;
+       struct string_entry *entry;
+       struct oidset interesting = OIDSET_INIT, child_seen = OIDSET_INIT,
+               shown = OIDSET_INIT;
+       struct label_state state = { OIDMAP_INIT, { NULL }, STRBUF_INIT };
+       int abbr = flags & TODO_LIST_ABBREVIATE_CMDS;
+       const char *cmd_pick = abbr ? "p" : "pick",
+               *cmd_label = abbr ? "l" : "label",
+               *cmd_reset = abbr ? "t" : "reset",
+               *cmd_merge = abbr ? "m" : "merge";
+       oidmap_init(&commit2todo, 0);
+       oidmap_init(&state.commit2label, 0);
+       hashmap_init(&state.labels, (hashmap_cmp_fn) labels_cmp, NULL, 0);
+       strbuf_init(&state.buf, 32);
+       if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
+               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);
+       }
+       /*
+        * First phase:
+        * - get onelines for all commits
+        * - gather all branch tips (i.e. 2nd or later parents of merges)
+        * - label all branch tips
+        */
+       while ((commit = get_revision(revs))) {
+               struct commit_list *to_merge;
+               int is_octopus;
+               const char *p1, *p2;
+               struct object_id *oid;
+               tail = &commit_list_insert(commit, tail)->next;
+               oidset_insert(&interesting, &commit->object.oid);
+               if ((commit->object.flags & PATCHSAME))
+                       continue;
+               strbuf_reset(&oneline);
+               pretty_print_commit(pp, commit, &oneline);
+               to_merge = commit->parents ? commit->parents->next : NULL;
+               if (!to_merge) {
+                       /* non-merge commit: easy case */
+                       strbuf_reset(&buf);
+                       if (!keep_empty && is_original_commit_empty(commit))
+                               strbuf_addf(&buf, "%c ", comment_line_char);
+                       strbuf_addf(&buf, "%s %s %s", cmd_pick,
+                                   oid_to_hex(&commit->object.oid),
+                                   oneline.buf);
+                       FLEX_ALLOC_STR(entry, string, buf.buf);
+                       oidcpy(&entry->entry.oid, &commit->object.oid);
+                       oidmap_put(&commit2todo, entry);
+                       continue;
+               }
+               is_octopus = to_merge && to_merge->next;
+               if (is_octopus)
+                       BUG("Octopus merges not yet supported");
+               /* Create a label */
+               strbuf_reset(&label);
+               if (skip_prefix(oneline.buf, "Merge ", &p1) &&
+                   (p1 = strchr(p1, '\'')) &&
+                   (p2 = strchr(++p1, '\'')))
+                       strbuf_add(&label, p1, p2 - p1);
+               else if (skip_prefix(oneline.buf, "Merge pull request ",
+                                    &p1) &&
+                        (p1 = strstr(p1, " from ")))
+                       strbuf_addstr(&label, p1 + strlen(" from "));
+               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));
+               /* label the tip of merged branch */
+               oid = &to_merge->item->object.oid;
+               strbuf_addch(&buf, ' ');
+               if (!oidset_contains(&interesting, oid))
+                       strbuf_addstr(&buf, label_oid(oid, NULL, &state));
+               else {
+                       tips_tail = &commit_list_insert(to_merge->item,
+                                                       tips_tail)->next;
+                       strbuf_addstr(&buf, label_oid(oid, label.buf, &state));
+               }
+               strbuf_addf(&buf, " # %s", oneline.buf);
+               FLEX_ALLOC_STR(entry, string, buf.buf);
+               oidcpy(&entry->entry.oid, &commit->object.oid);
+               oidmap_put(&commit2todo, entry);
+       }
+       /*
+        * Second phase:
+        * - label branch points
+        * - add HEAD to the branch tips
+        */
+       for (iter = commits; iter; iter = iter->next) {
+               struct commit_list *parent = iter->item->parents;
+               for (; parent; parent = parent->next) {
+                       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
+                               label_oid(oid, "branch-point", &state);
+               }
+               /* Add HEAD as implict "tip of branch" */
+               if (!iter->next)
+                       tips_tail = &commit_list_insert(iter->item,
+                                                       tips_tail)->next;
+       }
+       /*
+        * Third phase: output the todo list. This is a bit tricky, as we
+        * want to avoid jumping back and forth between revisions. To
+        * accomplish that goal, we walk backwards from the branch tips,
+        * gathering commits not yet shown, reversing the list on the fly,
+        * then outputting that list (labeling revisions as needed).
+        */
+       fprintf(out, "%s onto\n", cmd_label);
+       for (iter = tips; iter; iter = iter->next) {
+               struct commit_list *list = NULL, *iter2;
+               commit = iter->item;
+               if (oidset_contains(&shown, &commit->object.oid))
+                       continue;
+               entry = oidmap_get(&state.commit2label, &commit->object.oid);
+               if (entry)
+                       fprintf(out, "\n# Branch %s\n", entry->string);
+               else
+                       fprintf(out, "\n");
+               while (oidset_contains(&interesting, &commit->object.oid) &&
+                      !oidset_contains(&shown, &commit->object.oid)) {
+                       commit_list_insert(commit, &list);
+                       if (!commit->parents) {
+                               commit = NULL;
+                               break;
+                       }
+                       commit = commit->parents->item;
+               }
+               if (!commit)
+                       fprintf(out, "%s onto\n", cmd_reset);
+               else {
+                       const char *to = NULL;
+                       entry = oidmap_get(&state.commit2label,
+                                          &commit->object.oid);
+                       if (entry)
+                               to = entry->string;
+                       else if (!rebase_cousins)
+                               to = label_oid(&commit->object.oid, NULL,
+                                              &state);
+                       if (!to || !strcmp(to, "onto"))
+                               fprintf(out, "%s onto\n", cmd_reset);
+                       else {
+                               strbuf_reset(&oneline);
+                               pretty_print_commit(pp, commit, &oneline);
+                               fprintf(out, "%s %s # %s\n",
+                                       cmd_reset, to, oneline.buf);
+                       }
+               }
+               for (iter2 = list; iter2; iter2 = iter2->next) {
+                       struct object_id *oid = &iter2->item->object.oid;
+                       entry = oidmap_get(&commit2todo, oid);
+                       /* only show if not already upstream */
+                       if (entry)
+                               fprintf(out, "%s\n", entry->string);
+                       entry = oidmap_get(&state.commit2label, oid);
+                       if (entry)
+                               fprintf(out, "%s %s\n",
+                                       cmd_label, entry->string);
+                       oidset_insert(&shown, oid);
+               }
+               free_commit_list(list);
+       }
+       free_commit_list(commits);
+       free_commit_list(tips);
+       strbuf_release(&label);
+       strbuf_release(&oneline);
+       strbuf_release(&buf);
+       oidmap_free(&commit2todo, 1);
+       oidmap_free(&state.commit2label, 1);
+       hashmap_free(&state.labels, 1);
+       strbuf_release(&state.buf);
+       return 0;
+ }
  int sequencer_make_script(FILE *out, int argc, const char **argv,
                          unsigned flags)
  {
        struct commit *commit;
        int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
        const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
+       int recreate_merges = flags & TODO_LIST_RECREATE_MERGES;
  
        init_revisions(&revs, NULL);
        revs.verbose_header = 1;
-       revs.max_parents = 1;
-       revs.cherry_pick = 1;
+       if (recreate_merges)
+               revs.cherry_mark = 1;
+       else {
+               revs.max_parents = 1;
+               revs.cherry_pick = 1;
+       }
        revs.limited = 1;
        revs.reverse = 1;
        revs.right_only = 1;
        if (prepare_revision_walk(&revs) < 0)
                return error(_("make_script: error preparing revisions"));
  
+       if (recreate_merges)
+               return make_script_with_merges(&pp, &revs, out, flags);
        while ((commit = get_revision(&revs))) {
                strbuf_reset(&buf);
                if (!keep_empty && is_original_commit_empty(commit))
@@@ -3113,8 -3293,14 +3822,14 @@@ int transform_todos(unsigned flags
                                          short_commit_name(item->commit) :
                                          oid_to_hex(&item->commit->object.oid);
  
+                       if (item->command == TODO_MERGE)
+                               strbuf_addstr(&buf, " -C");
+                       else if (item->command == TODO_MERGE_AND_EDIT)
+                               strbuf_addstr(&buf, " -c");
                        strbuf_addf(&buf, " %s", oid);
                }
                /* add all the rest */
                if (!item->arg_len)
                        strbuf_addch(&buf, '\n');
@@@ -3159,13 -3345,20 +3874,13 @@@ int check_todo_list(void
        struct strbuf todo_file = STRBUF_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
        struct strbuf missing = STRBUF_INIT;
 -      int advise_to_edit_todo = 0, res = 0, fd, i;
 +      int advise_to_edit_todo = 0, res = 0, i;
  
        strbuf_addstr(&todo_file, rebase_path_todo());
 -      fd = open(todo_file.buf, O_RDONLY);
 -      if (fd < 0) {
 -              res = error_errno(_("could not open '%s'"), todo_file.buf);
 -              goto leave_check;
 -      }
 -      if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
 -              close(fd);
 -              res = error(_("could not read '%s'."), todo_file.buf);
 +      if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
 +              res = -1;
                goto leave_check;
        }
 -      close(fd);
        advise_to_edit_todo = res =
                parse_insn_buffer(todo_list.buf.buf, &todo_list);
  
  
        todo_list_release(&todo_list);
        strbuf_addstr(&todo_file, ".backup");
 -      fd = open(todo_file.buf, O_RDONLY);
 -      if (fd < 0) {
 -              res = error_errno(_("could not open '%s'"), todo_file.buf);
 -              goto leave_check;
 -      }
 -      if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
 -              close(fd);
 -              res = error(_("could not read '%s'."), todo_file.buf);
 +      if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
 +              res = -1;
                goto leave_check;
        }
 -      close(fd);
        strbuf_release(&todo_file);
        res = !!parse_insn_buffer(todo_list.buf.buf, &todo_list);
  
@@@ -3265,8 -3465,15 +3980,8 @@@ int skip_unnecessary_picks(void
        }
        strbuf_release(&buf);
  
 -      fd = open(todo_file, O_RDONLY);
 -      if (fd < 0) {
 -              return error_errno(_("could not open '%s'"), todo_file);
 -      }
 -      if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
 -              close(fd);
 -              return error(_("could not read '%s'."), todo_file);
 -      }
 -      close(fd);
 +      if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
 +              return -1;
        if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {
                todo_list_release(&todo_list);
                return -1;
@@@ -3357,11 -3564,17 +4072,11 @@@ int rearrange_squash(void
        const char *todo_file = rebase_path_todo();
        struct todo_list todo_list = TODO_LIST_INIT;
        struct hashmap subject2item;
 -      int res = 0, rearranged = 0, *next, *tail, fd, i;
 +      int res = 0, rearranged = 0, *next, *tail, i;
        char **subjects;
  
 -      fd = open(todo_file, O_RDONLY);
 -      if (fd < 0)
 -              return error_errno(_("could not open '%s'"), todo_file);
 -      if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
 -              close(fd);
 -              return error(_("could not read '%s'."), todo_file);
 -      }
 -      close(fd);
 +      if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
 +              return -1;
        if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {
                todo_list_release(&todo_list);
                return -1;
                struct subject2item_entry *entry;
  
                next[i] = tail[i] = -1;
-               if (item->command >= TODO_EXEC) {
+               if (!item->commit || item->command == TODO_DROP) {
                        subjects[i] = NULL;
                        continue;
                }
diff --combined sequencer.h
index e45b178dfc41d723bf186f20674c4515d7c7fa00,deebc6e325823021a5cf02cf0d05b80bafe07655..739dd0fa92b263f7c05f9c1d41f840b2f9e41dcd
@@@ -1,7 -1,6 +1,7 @@@
  #ifndef SEQUENCER_H
  #define SEQUENCER_H
  
 +const char *git_path_commit_editmsg(void);
  const char *git_path_seq_dir(void);
  
  #define APPEND_SIGNOFF_DEDUP (1u << 0)
@@@ -12,13 -11,6 +12,13 @@@ enum replay_action 
        REPLAY_INTERACTIVE_REBASE
  };
  
 +enum commit_msg_cleanup_mode {
 +      COMMIT_MSG_CLEANUP_SPACE,
 +      COMMIT_MSG_CLEANUP_NONE,
 +      COMMIT_MSG_CLEANUP_SCISSORS,
 +      COMMIT_MSG_CLEANUP_ALL
 +};
 +
  struct replay_opts {
        enum replay_action action;
  
@@@ -37,7 -29,6 +37,7 @@@
        int mainline;
  
        char *gpg_sign;
 +      enum commit_msg_cleanup_mode default_msg_cleanup;
  
        /* Merge strategy */
        char *strategy;
@@@ -49,8 -40,6 +49,8 @@@
  };
  #define REPLAY_OPTS_INIT { -1 }
  
 +/* Call this to setup defaults before parsing command line options */
 +void sequencer_init_config(struct replay_opts *opts);
  int sequencer_pick_revisions(struct replay_opts *opts);
  int sequencer_continue(struct replay_opts *opts);
  int sequencer_rollback(struct replay_opts *opts);
@@@ -59,6 -48,13 +59,13 @@@ int sequencer_remove_state(struct repla
  #define TODO_LIST_KEEP_EMPTY (1U << 0)
  #define TODO_LIST_SHORTEN_IDS (1U << 1)
  #define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
+ #define TODO_LIST_RECREATE_MERGES (1U << 3)
+ /*
+  * When recreating merges, commits that do have the base commit as ancestor
+  * ("cousins") are *not* rebased onto the new base by default. If those
+  * commits should be rebased onto the new base, this flag needs to be passed.
+  */
+ #define TODO_LIST_REBASE_COUSINS (1U << 4)
  int sequencer_make_script(FILE *out, int argc, const char **argv,
                          unsigned flags);
  
@@@ -72,19 -68,5 +79,19 @@@ extern const char sign_off_header[]
  
  void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
  void append_conflicts_hint(struct strbuf *msgbuf);
 +int message_is_empty(const struct strbuf *sb,
 +                   enum commit_msg_cleanup_mode cleanup_mode);
 +int template_untouched(const struct strbuf *sb, const char *template_file,
 +                     enum commit_msg_cleanup_mode cleanup_mode);
 +int update_head_with_reflog(const struct commit *old_head,
 +                          const struct object_id *new_head,
 +                          const char *action, const struct strbuf *msg,
 +                          struct strbuf *err);
 +void commit_post_rewrite(const struct commit *current_head,
 +                       const struct object_id *new_head);
  
 +#define SUMMARY_INITIAL_COMMIT   (1 << 0)
 +#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
 +void print_commit_summary(const char *prefix, const struct object_id *oid,
 +                        unsigned int flags);
  #endif