Merge branch 'nd/switch-and-restore'
authorJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:44 +0000 (15:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:44 +0000 (15:25 -0700)
Two new commands "git switch" and "git restore" are introduced to
split "checking out a branch to work on advancing its history" and
"checking out paths out of the index and/or a tree-ish to work on
advancing the current history" out of the single "git checkout"
command.

* nd/switch-and-restore: (46 commits)
completion: disable dwim on "git switch -d"
switch: allow to switch in the middle of bisect
t2027: use test_must_be_empty
Declare both git-switch and git-restore experimental
help: move git-diff and git-reset to different groups
doc: promote "git restore"
user-manual.txt: prefer 'merge --abort' over 'reset --hard'
completion: support restore
t: add tests for restore
restore: support --patch
restore: replace --force with --ignore-unmerged
restore: default to --source=HEAD when only --staged is specified
restore: reject invalid combinations with --staged
restore: add --worktree and --staged
checkout: factor out worktree checkout code
restore: disable overlay mode by default
restore: make pathspec mandatory
restore: take tree-ish from --source option instead
checkout: split part of it to new command 'restore'
doc: promote "git switch"
...

41 files changed:
1  2 
.gitignore
Documentation/config/advice.txt
Documentation/config/branch.txt
Documentation/config/diff.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-clean.txt
Documentation/git-format-patch.txt
Documentation/git-merge.txt
Documentation/git-rebase.txt
Documentation/git-rerere.txt
Documentation/git-reset.txt
Documentation/git-revert.txt
Documentation/git-stash.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/revisions.txt
Makefile
advice.c
branch.c
branch.h
builtin.h
builtin/am.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/rebase.c
builtin/reset.c
builtin/revert.c
contrib/completion/git-completion.bash
git-add--interactive.perl
git.c
parse-options-cb.c
parse-options.h
sequencer.c
sequencer.h
sha1-name.c
t/t7512-status-help.sh
unpack-trees.c
wt-status.c
diff --combined .gitignore
index 4470d7cfc0ae72991116f8d1b65fb03e35e88bdf,fb377106be89bfb514b1b4a33b7e678ba1fb5411..40326140c740b57971ba89c5324aaa053d84f8cd
@@@ -82,7 -82,7 +82,7 @@@
  /git-init-db
  /git-interpret-trailers
  /git-instaweb
 -/git-legacy-rebase
 +/git-legacy-stash
  /git-log
  /git-ls-files
  /git-ls-remote
  /git-range-diff
  /git-read-tree
  /git-rebase
 -/git-rebase--am
 -/git-rebase--common
 -/git-rebase--interactive
  /git-rebase--preserve-merges
  /git-receive-pack
  /git-reflog
  /git-remote-ftps
  /git-remote-fd
  /git-remote-ext
 -/git-remote-testgit
  /git-remote-testpy
  /git-remote-testsvn
  /git-repack
  /git-request-pull
  /git-rerere
  /git-reset
+ /git-restore
  /git-rev-list
  /git-rev-parse
  /git-revert
  /git-submodule
  /git-submodule--helper
  /git-svn
+ /git-switch
  /git-symbolic-ref
  /git-tag
  /git-unpack-file
index ec4f6ae6585bac4b107134e52f544d53c24cdf86,239d4795065dd8973e191be5a65bb111e9f4d532..d5fd05ce816e1ad724f0084112392a259a6f225e
@@@ -42,7 -42,8 +42,8 @@@ advice.*:
                state in the output of linkgit:git-status[1], in
                the template shown when writing commit messages in
                linkgit:git-commit[1], and in the help message shown
-               by linkgit:git-checkout[1] when switching branch.
+               by linkgit:git-switch[1] or
+               linkgit:git-checkout[1] when switching branch.
        statusUoption::
                Advise to consider using the `-u` option to linkgit:git-status[1]
                when the command takes more than 2 seconds to enumerate untracked
                your information is guessed from the system username and
                domain name.
        detachedHead::
-               Advice shown when you used linkgit:git-checkout[1] to
-               move to the detach HEAD state, to instruct how to create
-               a local branch after the fact.
+               Advice shown when you used
+               linkgit:git-switch[1] or linkgit:git-checkout[1]
+               to move to the detach HEAD state, to instruct how to
+               create a local branch after the fact.
        checkoutAmbiguousRemoteBranchName::
                Advice shown when the argument to
-               linkgit:git-checkout[1] ambiguously resolves to a
+               linkgit:git-checkout[1] and linkgit:git-switch[1]
+               ambiguously resolves to a
                remote tracking branch on more than one remote in
                situations where an unambiguous argument would have
                otherwise caused a remote-tracking branch to be
@@@ -90,6 -93,4 +93,6 @@@
        waitingForEditor::
                Print a message to the terminal whenever Git is waiting for
                editor input from the user.
 +      nestedTag::
 +              Advice shown if a user attempts to recursively tag a tag object.
  --
index 8f4b3faadd47b6c655f403528615ae4f0e0df9f6,8050466159f75569bcb6799ed9fb842d792739ad..a592d522a744f99cd2c8f94400bca1203bfe2886
@@@ -1,5 -1,5 +1,5 @@@
  branch.autoSetupMerge::
-       Tells 'git branch' and 'git checkout' to set up new branches
+       Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
        so that linkgit:git-pull[1] will appropriately merge from the
        starting point branch. Note that even if this option is not set,
        this behavior can be chosen per-branch using the `--track`
@@@ -11,7 -11,7 +11,7 @@@
        branch. This option defaults to true.
  
  branch.autoSetupRebase::
-       When a new branch is created with 'git branch' or 'git checkout'
+       When a new branch is created with 'git branch', 'git switch' or 'git checkout'
        that tracks another branch, this variable tells Git to set
        up pull to rebase instead of merge (see "branch.<name>.rebase").
        When `never`, rebase is never automatically set to true.
@@@ -85,9 -85,9 +85,9 @@@ When `merges`, pass the `--rebase-merge
  so that the local merge commits are included in the rebase (see
  linkgit:git-rebase[1] for details).
  +
 -When preserve, also pass `--preserve-merges` along to 'git rebase'
 -so that locally committed merge commits will not be flattened
 -by running 'git pull'.
 +When `preserve` (deprecated in favor of `merges`), also pass
 +`--preserve-merges` along to 'git rebase' so that locally committed merge
 +commits will not be flattened by running 'git pull'.
  +
  When the value is `interactive`, the rebase is run in interactive mode.
  +
index 2c4c9ba27aa48a2eea4951c8a4cffc4f718a75ce,b3b304ee120a9122a699267ffac79b3f956e61c6..5afb5a2cbc69b763263e3e265343884c0ff64dda
@@@ -10,7 -10,7 +10,7 @@@ diff.autoRefreshIndex:
  
  diff.dirstat::
        A comma separated list of `--dirstat` parameters specifying the
 -      default behavior of the `--dirstat` option to linkgit:git-diff[1]`
 +      default behavior of the `--dirstat` option to linkgit:git-diff[1]
        and friends. The defaults can be overridden on the command line
        (using `--dirstat=<param1,param2,...>`). The fallback defaults
        (when not changed by `diff.dirstat`) are `changes,noncumulative,3`.
@@@ -73,12 -73,13 +73,13 @@@ diff.external:
        environment variable.  The command is called with parameters
        as described under "git Diffs" in linkgit:git[1].  Note: if
        you want to use an external diff program only on a subset of
 -      your files, you might want to use linkgit:gitattributes[5] instead.
 +      your files, you might want to use linkgit:gitattributes[5] instead.
  
  diff.ignoreSubmodules::
        Sets the default value of --ignore-submodules. Note that this
        affects only 'git diff' Porcelain, and not lower level 'diff'
-       commands such as 'git diff-files'. 'git checkout' also honors
+       commands such as 'git diff-files'. 'git checkout'
+       and 'git switch' also honor
        this setting when reporting uncommitted changes. Setting it to
        'all' disables the submodule summary normally shown by 'git commit'
        and 'git status' when `status.submoduleSummary` is set unless it is
index 16e14c62827f66afca2690a3be2cf1b69c69afd3,1e2d89b174e57e0920fc22435341a8f5cff1f6c3..135206ff4aba651f9f40f307fdabaec91ae86555
@@@ -8,14 -8,12 +8,14 @@@ git-branch - List, create, or delete br
  SYNOPSIS
  --------
  [verse]
 -'git branch' [--color[=<when>] | --no-color] [-r | -a]
 -      [--list] [--show-current] [-v [--abbrev=<length> | --no-abbrev]]
 +'git branch' [--color[=<when>] | --no-color] [--show-current]
 +      [-v [--abbrev=<length> | --no-abbrev]]
        [--column[=<options>] | --no-column] [--sort=<key>]
        [(--merged | --no-merged) [<commit>]]
        [--contains [<commit]] [--no-contains [<commit>]]
 -      [--points-at <object>] [--format=<format>] [<pattern>...]
 +      [--points-at <object>] [--format=<format>]
 +      [(-r | --remotes) | (-a | --all)]
 +      [--list] [<pattern>...]
  'git branch' [--track | --no-track] [-f] <branchname> [<start-point>]
  'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
  'git branch' --unset-upstream [<branchname>]
@@@ -28,19 -26,13 +28,19 @@@ DESCRIPTIO
  -----------
  
  If `--list` is given, or if there are no non-option arguments, existing
 -branches are listed; the current branch will be highlighted with an
 -asterisk.  Option `-r` causes the remote-tracking branches to be listed,
 -and option `-a` shows both local and remote branches. If a `<pattern>`
 +branches are listed; the current branch will be highlighted in green and
 +marked with an asterisk.  Any branches checked out in linked worktrees will
 +be highlighted in cyan and marked with a plus sign. Option `-r` causes the
 +remote-tracking branches to be listed,
 +and option `-a` shows both local and remote branches.
 +
 +If a `<pattern>`
  is given, it is used as a shell wildcard to restrict the output to
  matching branches. If multiple patterns are given, a branch is shown if
 -it matches any of the patterns.  Note that when providing a
 -`<pattern>`, you must use `--list`; otherwise the command is interpreted
 +it matches any of the patterns.
 +
 +Note that when providing a
 +`<pattern>`, you must use `--list`; otherwise the command may be interpreted
  as branch creation.
  
  With `--contains`, shows only the branches that contain the named commit
@@@ -53,14 -45,10 +53,14 @@@ argument is missing it defaults to `HEA
  branch).
  
  The command's second form creates a new branch head named <branchname>
 -which points to the current `HEAD`, or <start-point> if given.
 +which points to the current `HEAD`, or <start-point> if given. As a
 +special case, for <start-point>, you may use `"A...B"` as a shortcut for
 +the merge base of `A` and `B` if there is exactly one merge base. You
 +can leave out at most one of `A` and `B`, in which case it defaults to
 +`HEAD`.
  
  Note that this will create the new branch, but it will not switch the
- working tree to it; use "git checkout <newbranch>" to switch to the
+ working tree to it; use "git switch <newbranch>" to switch to the
  new branch.
  
  When a local branch is started off a remote-tracking branch, Git sets up the
@@@ -161,12 -149,10 +161,12 @@@ This option is only applicable in non-v
  -r::
  --remotes::
        List or delete (if used with -d) the remote-tracking branches.
 +      Combine with `--list` to match the optional pattern(s).
  
  -a::
  --all::
        List both remote-tracking branches and local branches.
 +      Combine with `--list` to match optional pattern(s).
  
  -l::
  --list::
        When in list mode,
        show sha1 and commit subject line for each head, along with
        relationship to upstream branch (if any). If given twice, print
 -      the name of the upstream branch, as well (see also `git remote
 -      show <remote>`).
 +      the path of the linked worktree (if any) and the name of the upstream
 +      branch, as well (see also `git remote show <remote>`).  Note that the
 +      current worktree's HEAD will not have its path printed (it will always
 +      be your current directory).
  
  -q::
  --quiet::
  +
  This behavior is the default when the start point is a remote-tracking branch.
  Set the branch.autoSetupMerge configuration variable to `false` if you
- want `git checkout` and `git branch` to always behave as if `--no-track`
+ want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
  were given. Set it to `always` if you want this behavior when the
  start-point is either a local or remote-tracking branch.
  
@@@ -313,7 -297,7 +313,7 @@@ Start development from a known tag:
  $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
  $ cd my2.6
  $ git branch my2.6.14 v2.6.14   <1>
- $ git checkout my2.6.14
+ $ git switch my2.6.14
  ------------
  +
  <1> This step and the next one could be combined into a single step with
@@@ -334,25 -318,13 +334,25 @@@ $ git branch -D tes
  <2> Delete the "test" branch even if the "master" branch (or whichever branch
      is currently checked out) does not have all commits from the test branch.
  
 +Listing branches from a specific remote::
 ++
 +------------
 +$ git branch -r -l '<remote>/<pattern>'                 <1>
 +$ git for-each-ref 'refs/remotes/<remote>/<pattern>'    <2>
 +------------
 ++
 +<1> Using `-a` would conflate <remote> with any local branches you happen to
 +    have been prefixed with the same <remote> pattern.
 +<2> `for-each-ref` can take a wide range of options. See linkgit:git-for-each-ref[1]
 +
 +Patterns will normally need quoting.
  
  NOTES
  -----
  
- If you are creating a branch that you want to checkout immediately, it is
- easier to use the git checkout command with its `-b` option to create
a branch and check it out with a single command.
+ If you are creating a branch that you want to switch to immediately,
+ it is easier to use the "git switch" command with its `-c` option to
do the same thing with a single command.
  
  The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
  serve four related but different purposes:
index 964f912d29ee92d55a3c98d40cc41941e7db743a,a294652dd6753297d3e2f9647b88c5d0f2c247e4..cf3cac0a2b518ec902453d7e65b6b7d403a99ef7
@@@ -23,31 -23,22 +23,22 @@@ or the specified tree.  If no paths ar
  also update `HEAD` to set the specified branch as the current
  branch.
  
- 'git checkout' <branch>::
-       To prepare for working on <branch>, switch to it by updating
+ 'git checkout' [<branch>]::
+       To prepare for working on `<branch>`, switch to it by updating
        the index and the files in the working tree, and by pointing
-       HEAD at the branch. Local modifications to the files in the
+       `HEAD` at the branch. Local modifications to the files in the
        working tree are kept, so that they can be committed to the
-       <branch>.
+       `<branch>`.
  +
- If <branch> is not found but there does exist a tracking branch in
- exactly one remote (call it <remote>) with a matching name, treat as
- equivalent to
+ If `<branch>` is not found but there does exist a tracking branch in
+ exactly one remote (call it `<remote>`) with a matching name and
`--no-guess` is not specified, treat as equivalent to
  +
  ------------
  $ git checkout -b <branch> --track <remote>/<branch>
  ------------
  +
- If the branch exists in multiple remotes and one of them is named by
- the `checkout.defaultRemote` configuration variable, we'll use that
- one for the purposes of disambiguation, even if the `<branch>` isn't
- unique across all remotes. Set it to
- e.g. `checkout.defaultRemote=origin` to always checkout remote
- branches from there if `<branch>` is ambiguous but exists on the
- 'origin' remote. See also `checkout.defaultRemote` in
- linkgit:git-config[1].
- +
- You could omit <branch>, in which case the command degenerates to
+ You could omit `<branch>`, in which case the command degenerates to
  "check out the current branch", which is a glorified no-op with
  rather expensive side-effects to show only the tracking information,
  if exists, for the current branch.
@@@ -61,7 -52,7 +52,7 @@@
        `--track` without `-b` implies branch creation; see the
        description of `--track` below.
  +
- If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+ If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it
  is reset. This is the transactional equivalent of
  +
  ------------
@@@ -75,25 -66,25 +66,25 @@@ successful
  'git checkout' --detach [<branch>]::
  'git checkout' [--detach] <commit>::
  
-       Prepare to work on top of <commit>, by detaching HEAD at it
+       Prepare to work on top of `<commit>`, by detaching `HEAD` at it
        (see "DETACHED HEAD" section), and updating the index and the
        files in the working tree.  Local modifications to the files
        in the working tree are kept, so that the resulting working
        tree will be the state recorded in the commit plus the local
        modifications.
  +
- When the <commit> argument is a branch name, the `--detach` option can
- be used to detach HEAD at the tip of the branch (`git checkout
- <branch>` would check out that branch without detaching HEAD).
+ When the `<commit>` argument is a branch name, the `--detach` option can
+ be used to detach `HEAD` at the tip of the branch (`git checkout
+ <branch>` would check out that branch without detaching `HEAD`).
  +
- Omitting <branch> detaches HEAD at the tip of the current branch.
+ Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
  
  'git checkout' [<tree-ish>] [--] <pathspec>...::
  
        Overwrite paths in the working tree by replacing with the
-       contents in the index or in the <tree-ish> (most often a
-       commit).  When a <tree-ish> is given, the paths that
-       match the <pathspec> are updated both in the index and in
+       contents in the index or in the `<tree-ish>` (most often a
+       commit).  When a `<tree-ish>` is given, the paths that
+       match the `<pathspec>` are updated both in the index and in
        the working tree.
  +
  The index may contain unmerged entries because of a previous failed merge.
@@@ -118,7 -109,8 +109,8 @@@ OPTION
  --quiet::
        Quiet, suppress feedback messages.
  
- --[no-]progress::
+ --progress::
+ --no-progress::
        Progress status is reported on the standard error stream
        by default when it is attached to a terminal, unless `--quiet`
        is specified. This flag enables progress reporting even if not
  -f::
  --force::
        When switching branches, proceed even if the index or the
-       working tree differs from HEAD.  This is used to throw away
+       working tree differs from `HEAD`.  This is used to throw away
        local changes.
  +
  When checking out paths from the index, do not fail upon unmerged
@@@ -154,12 -146,12 +146,12 @@@ on your side branch as `theirs` (i.e. "
  of it").
  
  -b <new_branch>::
-       Create a new branch named <new_branch> and start it at
-       <start_point>; see linkgit:git-branch[1] for details.
+       Create a new branch named `<new_branch>` and start it at
+       `<start_point>`; see linkgit:git-branch[1] for details.
  
  -B <new_branch>::
-       Creates the branch <new_branch> and start it at <start_point>;
-       if it already exists, then reset it to <start_point>. This is
+       Creates the branch `<new_branch>` and start it at `<start_point>`;
+       if it already exists, then reset it to `<start_point>`. This is
        equivalent to running "git branch" with "-f"; see
        linkgit:git-branch[1] for details.
  
@@@ -172,15 -164,36 +164,36 @@@ If no `-b` option is given, the name o
  derived from the remote-tracking branch, by looking at the local part of
  the refspec configured for the corresponding remote, and then stripping
  the initial part up to the "*".
- This would tell us to use "hack" as the local branch when branching
- off of "origin/hack" (or "remotes/origin/hack", or even
"refs/remotes/origin/hack").  If the given name has no slash, or the above
+ This would tell us to use `hack` as the local branch when branching
+ off of `origin/hack` (or `remotes/origin/hack`, or even
`refs/remotes/origin/hack`).  If the given name has no slash, or the above
  guessing results in an empty name, the guessing is aborted.  You can
  explicitly give a name with `-b` in such a case.
  
  --no-track::
        Do not set up "upstream" configuration, even if the
-       branch.autoSetupMerge configuration variable is true.
+       `branch.autoSetupMerge` configuration variable is true.
+ --guess::
+ --no-guess::
+       If `<branch>` is not found but there does exist a tracking
+       branch in exactly one remote (call it `<remote>`) with a
+       matching name, treat as equivalent to
+ +
+ ------------
+ $ git checkout -b <branch> --track <remote>/<branch>
+ ------------
+ +
+ If the branch exists in multiple remotes and one of them is named by
+ the `checkout.defaultRemote` configuration variable, we'll use that
+ one for the purposes of disambiguation, even if the `<branch>` isn't
+ unique across all remotes. Set it to
+ e.g. `checkout.defaultRemote=origin` to always checkout remote
+ branches from there if `<branch>` is ambiguous but exists on the
+ 'origin' remote. See also `checkout.defaultRemote` in
+ linkgit:git-config[1].
+ +
+ Use `--no-guess` to disable this.
  
  -l::
        Create the new branch's reflog; see linkgit:git-branch[1] for
  --detach::
        Rather than checking out a branch to work on it, check out a
        commit for inspection and discardable experiments.
-       This is the default behavior of "git checkout <commit>" when
-       <commit> is not a branch name.  See the "DETACHED HEAD" section
+       This is the default behavior of `git checkout <commit>` when
+       `<commit>` is not a branch name.  See the "DETACHED HEAD" section
        below for details.
  
  --orphan <new_branch>::
-       Create a new 'orphan' branch, named <new_branch>, started from
-       <start_point> and switch to it.  The first commit made on this
+       Create a new 'orphan' branch, named `<new_branch>`, started from
+       `<start_point>` and switch to it.  The first commit made on this
        new branch will have no parents and it will be the root of a new
        history totally disconnected from all the other branches and
        commits.
  +
  The index and the working tree are adjusted as if you had previously run
"git checkout <start_point>".  This allows you to start a new history
- that records a set of paths similar to <start_point> by easily running
"git commit -a" to make the root commit.
`git checkout <start_point>`.  This allows you to start a new history
+ that records a set of paths similar to `<start_point>` by easily running
`git commit -a` to make the root commit.
  +
  This can be useful when you want to publish the tree from a commit
  without exposing its full history. You might want to do this to publish
@@@ -212,17 -225,17 +225,17 @@@ whose full history contains proprietar
  code.
  +
  If you want to start a disconnected history that records a set of paths
- that is totally different from the one of <start_point>, then you should
+ that is totally different from the one of `<start_point>`, then you should
  clear the index and the working tree right after creating the orphan
- branch by running "git rm -rf ." from the top level of the working tree.
+ branch by running `git rm -rf .` from the top level of the working tree.
  Afterwards you will be ready to prepare your new files, repopulating the
  working tree, by copying them from elsewhere, extracting a tarball, etc.
  
  --ignore-skip-worktree-bits::
        In sparse checkout mode, `git checkout -- <paths>` would
-       update only entries matched by <paths> and sparse patterns
-       in $GIT_DIR/info/sparse-checkout. This option ignores
-       the sparse patterns and adds back any files in <paths>.
+       update only entries matched by `<paths>` and sparse patterns
+       in `$GIT_DIR/info/sparse-checkout`. This option ignores
+       the sparse patterns and adds back any files in `<paths>`.
  
  -m::
  --merge::
@@@ -242,29 -255,27 +255,29 @@@ should result in deletion of the path)
  +
  When checking out paths from the index, this option lets you recreate
  the conflicted merge in the specified paths.
 ++
 +When switching branches with `--merge`, staged changes may be lost.
  
  --conflict=<style>::
-       The same as --merge option above, but changes the way the
+       The same as `--merge` option above, but changes the way the
        conflicting hunks are presented, overriding the
-       merge.conflictStyle configuration variable.  Possible values are
+       `merge.conflictStyle` configuration variable.  Possible values are
        "merge" (default) and "diff3" (in addition to what is shown by
        "merge" style, shows the original contents).
  
  -p::
  --patch::
        Interactively select hunks in the difference between the
-       <tree-ish> (or the index, if unspecified) and the working
+       `<tree-ish>` (or the index, if unspecified) and the working
        tree.  The chosen hunks are then applied in reverse to the
-       working tree (and if a <tree-ish> was specified, the index).
+       working tree (and if a `<tree-ish>` was specified, the index).
  +
  This means that you can use `git checkout -p` to selectively discard
  edits from your current working tree. See the ``Interactive Mode''
  section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
  +
  Note that this option uses the no overlay mode by default (see also
- `--[no-]overlay`), and currently doesn't support overlay mode.
+ `--overlay`), and currently doesn't support overlay mode.
  
  --ignore-other-worktrees::
        `git checkout` refuses when the wanted ref is already checked
        out anyway. In other words, the ref can be held by more than one
        worktree.
  
- --[no-]recurse-submodules::
-       Using --recurse-submodules will update the content of all initialized
+ --overwrite-ignore::
+ --no-overwrite-ignore::
+       Silently overwrite ignored files when switching branches. This
+       is the default behavior. Use `--no-overwrite-ignore` to abort
+       the operation when the new branch contains ignored files.
+ --recurse-submodules::
+ --no-recurse-submodules::
+       Using `--recurse-submodules` will update the content of all initialized
        submodules according to the commit recorded in the superproject. If
        local modifications in a submodule would be overwritten the checkout
-       will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+       will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
        is used, the work trees of submodules will not be updated.
-       Just like linkgit:git-submodule[1], this will detach the
-       submodules HEAD.
- --no-guess::
-       Do not attempt to create a branch if a remote tracking branch
-       of the same name exists.
+       Just like linkgit:git-submodule[1], this will detach `HEAD` of the
+       submodule.
  
- --[no-]overlay::
+ --overlay::
+ --no-overlay::
        In the default overlay mode, `git checkout` never
        removes files from the index or the working tree.  When
        specifying `--no-overlay`, files that appear in the index and
-       working tree, but not in <tree-ish> are removed, to make them
-       match <tree-ish> exactly.
+       working tree, but not in `<tree-ish>` are removed, to make them
+       match `<tree-ish>` exactly.
  
  <branch>::
        Branch to checkout; if it refers to a branch (i.e., a name that,
        when prepended with "refs/heads/", is a valid ref), then that
        branch is checked out. Otherwise, if it refers to a valid
-       commit, your HEAD becomes "detached" and you are no longer on
+       commit, your `HEAD` becomes "detached" and you are no longer on
        any branch (see below for details).
  +
- You can use the `"@{-N}"` syntax to refer to the N-th last
+ You can use the `@{-N}` syntax to refer to the N-th last
  branch/commit checked out using "git checkout" operation. You may
- also specify `-` which is synonymous to `"@{-1}"`.
+ also specify `-` which is synonymous to `@{-1}`.
  +
- As a special case, you may use `"A...B"` as a shortcut for the
+ As a special case, you may use `A...B` as a shortcut for the
  merge base of `A` and `B` if there is exactly one merge base. You can
  leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
  
  
  <start_point>::
        The name of a commit at which to start the new branch; see
-       linkgit:git-branch[1] for details. Defaults to HEAD.
+       linkgit:git-branch[1] for details. Defaults to `HEAD`.
 ++
 +As a special case, you may use `"A...B"` as a shortcut for the
 +merge base of `A` and `B` if there is exactly one merge base. You can
 +leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
  
  <tree-ish>::
        Tree to checkout from (when paths are given). If not specified,
  
  DETACHED HEAD
  -------------
HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each
`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each
  branch refers to a specific commit. Let's look at a repo with three
- commits, one of them tagged, and with branch 'master' checked out:
+ commits, one of them tagged, and with branch `master` checked out:
  
  ------------
             HEAD (refers to branch 'master')
@@@ -341,10 -352,10 +358,10 @@@ a---b---c  branch 'master' (refers to c
  ------------
  
  When a commit is created in this state, the branch is updated to refer to
- the new commit. Specifically, 'git commit' creates a new commit 'd', whose
- parent is commit 'c', and then updates branch 'master' to refer to new
- commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers
- to commit 'd':
+ the new commit. Specifically, 'git commit' creates a new commit `d`, whose
+ parent is commit `c`, and then updates branch `master` to refer to new
+ commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers
+ to commit `d`:
  
  ------------
  $ edit; git add; git commit
@@@ -361,7 -372,7 +378,7 @@@ a---b---c---d  branch 'master' (refers 
  It is sometimes useful to be able to checkout a commit that is not at
  the tip of any named branch, or even to create a new commit that is not
  referenced by a named branch. Let's look at what happens when we
- checkout commit 'b' (here we show two ways this may be done):
+ checkout commit `b` (here we show two ways this may be done):
  
  ------------
  $ git checkout v2.0  # or
@@@ -376,9 -387,9 +393,9 @@@ a---b---c---d  branch 'master' (refers 
    tag 'v2.0' (refers to commit 'b')
  ------------
  
- Notice that regardless of which checkout command we use, HEAD now refers
- directly to commit 'b'. This is known as being in detached HEAD state.
- It means simply that HEAD refers to a specific commit, as opposed to
+ Notice that regardless of which checkout command we use, `HEAD` now refers
+ directly to commit `b`. This is known as being in detached `HEAD` state.
+ It means simply that `HEAD` refers to a specific commit, as opposed to
  referring to a named branch. Let's see what happens when we create a commit:
  
  ------------
@@@ -395,7 -406,7 +412,7 @@@ a---b---c---d  branch 'master' (refers 
    tag 'v2.0' (refers to commit 'b')
  ------------
  
- There is now a new commit 'e', but it is referenced only by HEAD. We can
+ There is now a new commit `e`, but it is referenced only by `HEAD`. We can
  of course add yet another commit in this state:
  
  ------------
@@@ -413,7 -424,7 +430,7 @@@ a---b---c---d  branch 'master' (refers 
  ------------
  
  In fact, we can perform all the normal Git operations. But, let's look
- at what happens when we then checkout master:
+ at what happens when we then checkout `master`:
  
  ------------
  $ git checkout master
@@@ -428,9 -439,9 +445,9 @@@ a---b---c---d  branch 'master' (refers 
  ------------
  
  It is important to realize that at this point nothing refers to commit
'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted
`f`. Eventually commit `f` (and by extension commit `e`) will be deleted
  by the routine Git garbage collection process, unless we create a reference
- before that happens. If we have not yet moved away from commit 'f',
+ before that happens. If we have not yet moved away from commit `f`,
  any of these will create a reference to it:
  
  ------------
@@@ -439,19 -450,19 +456,19 @@@ $ git branch foo        <2
  $ git tag foo           <3>
  ------------
  
- <1> creates a new branch 'foo', which refers to commit 'f', and then
-     updates HEAD to refer to branch 'foo'. In other words, we'll no longer
-     be in detached HEAD state after this command.
+ <1> creates a new branch `foo`, which refers to commit `f`, and then
+     updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
+     be in detached `HEAD` state after this command.
  
- <2> similarly creates a new branch 'foo', which refers to commit 'f',
-     but leaves HEAD detached.
+ <2> similarly creates a new branch `foo`, which refers to commit `f`,
+     but leaves `HEAD` detached.
  
- <3> creates a new tag 'foo', which refers to commit 'f',
-     leaving HEAD detached.
+ <3> creates a new tag `foo`, which refers to commit `f`,
+     leaving `HEAD` detached.
  
- If we have moved away from commit 'f', then we must first recover its object
+ If we have moved away from commit `f`, then we must first recover its object
  name (typically by using git reflog), and then we can create a reference to
- it. For example, to see the last two commits to which HEAD referred, we
+ it. For example, to see the last two commits to which `HEAD` referred, we
  can use either of these commands:
  
  ------------
@@@ -462,12 -473,12 +479,12 @@@ $ git log -g -2 HEA
  ARGUMENT DISAMBIGUATION
  -----------------------
  
- When there is only one argument given and it is not `--` (e.g. "git
- checkout abc"), and when the argument is both a valid `<tree-ish>`
- (e.g. a branch "abc" exists) and a valid `<pathspec>` (e.g. a file
+ When there is only one argument given and it is not `--` (e.g. `git
+ checkout abc`), and when the argument is both a valid `<tree-ish>`
+ (e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file
  or a directory whose name is "abc" exists), Git would usually ask
  you to disambiguate.  Because checking out a branch is so common an
- operation, however, "git checkout abc" takes "abc" as a `<tree-ish>`
+ operation, however, `git checkout abc` takes "abc" as a `<tree-ish>`
  in such a situation.  Use `git checkout -- <pathspec>` if you want
  to checkout these paths out of the index.
  
@@@ -475,7 -486,7 +492,7 @@@ EXAMPLE
  --------
  
  . The following sequence checks out the `master` branch, reverts
-   the `Makefile` to two revisions back, deletes hello.c by
+   the `Makefile` to two revisions back, deletes `hello.c` by
    mistake, and gets it back from the index.
  +
  ------------
@@@ -487,7 -498,7 +504,7 @@@ $ git checkout hello.c            <3
  +
  <1> switch branch
  <2> take a file out of another commit
- <3> restore hello.c from the index
+ <3> restore `hello.c` from the index
  +
  If you want to check out _all_ C source files out of the index,
  you can say
@@@ -516,7 -527,7 +533,7 @@@ $ git checkout -- hello.
  $ git checkout mytopic
  ------------
  +
- However, your "wrong" branch and correct "mytopic" branch may
+ However, your "wrong" branch and correct `mytopic` branch may
  differ in files that you have modified locally, in which case
  the above checkout would fail like this:
  +
@@@ -557,6 -568,11 +574,11 @@@ $ edit frot
  $ git add frotz
  ------------
  
+ SEE ALSO
+ --------
+ linkgit:git-switch[1],
+ linkgit:git-restore[1]
  GIT
  ---
  Part of the linkgit:git[1] suite
index db876f7dde9237c47d1083d1649f8f8b077b9c82,8a31ccffea751cf64a049ce00605ba90def5a067..0028ff12d1dadb2abc7a50816ae6fa5807c82f46
@@@ -55,15 -55,16 +55,15 @@@ OPTION
  
  -e <pattern>::
  --exclude=<pattern>::
 -      In addition to those found in .gitignore (per directory) and
 -      $GIT_DIR/info/exclude, also consider these patterns to be in the
 -      set of the ignore rules in effect.
 +      Use the given exclude pattern in addition to the standard ignore rules
 +      (see linkgit:gitignore[5]).
  
  -x::
 -      Don't use the standard ignore rules read from .gitignore (per
 -      directory) and $GIT_DIR/info/exclude, but do still use the ignore
 -      rules given with `-e` options.  This allows removing all untracked
 +      Don't use the standard ignore rules (see linkgit:gitignore[5]), but
 +      still use the ignore rules given with `-e` options from the command
 +      line.  This allows removing all untracked
        files, including build products.  This can be used (possibly in
-       conjunction with 'git reset') to create a pristine
+       conjunction with 'git restore' or 'git reset') to create a pristine
        working directory to test a clean build.
  
  -X::
index 9ce5b8aaee345f07d3bc9eb3eb0c7513c6989900,01bfcecf690e857c5a3b14ef3be1f9307efe825d..b9b97e63aec5e73e7c5607c19661639a256dc1ed
@@@ -22,8 -22,7 +22,8 @@@ SYNOPSI
                   [--rfc] [--subject-prefix=Subject-Prefix]
                   [(--reroll-count|-v) <n>]
                   [--to=<email>] [--cc=<email>]
 -                 [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
 +                 [--[no-]cover-letter] [--quiet]
 +                 [--no-notes | --notes[=<ref>]]
                   [--interdiff=<previous>]
                   [--range-diff=<previous> [--creation-factor=<percent>]]
                   [--progress]
@@@ -264,7 -263,6 +264,7 @@@ material (this may change in the future
        for details.
  
  --notes[=<ref>]::
 +--no-notes::
        Append the notes (see linkgit:git-notes[1]) for the commit
        after the three-dash line.
  +
@@@ -275,9 -273,6 +275,9 @@@ these explanations after `format-patch
  keeping them as Git notes allows them to be maintained between versions
  of the patch series (but see the discussion of the `notes.rewrite`
  configuration options in linkgit:git-notes[1] to use this workflow).
 ++
 +The default is `--no-notes`, unless the `format.notes` configuration is
 +set.
  
  --[no-]signature=<signature>::
        Add a signature to each message produced. Per RFC 3676 the signature
@@@ -426,8 -421,8 +426,8 @@@ One way to test if your MUA is set up c
  * Apply it:
  
      $ git fetch <project> master:test-apply
-     $ git checkout test-apply
-     $ git reset --hard
+     $ git switch test-apply
+     $ git restore --source=HEAD --staged --worktree :/
      $ git am a.patch
  
  If it does not apply correctly, there can be various reasons.
index 3ff0393507c5ab25f06195b1fdca4bd7bd59770a,6a9163d8fe1129e2e07fd70c2e7d45eb21c122c4..01fd52dc7063802226bf4f7205a2a1aab697bc67
@@@ -13,7 -13,8 +13,7 @@@ SYNOPSI
        [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
        [--[no-]allow-unrelated-histories]
        [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
 -'git merge' --abort
 -'git merge' --continue
 +'git merge' (--continue | --abort | --quit)
  
  DESCRIPTION
  -----------
@@@ -82,11 -83,15 +82,16 @@@ invocations. The automated message can 
  If `--log` is specified, a shortlog of the commits being merged
  will be appended to the specified message.
  
 ---[no-]rerere-autoupdate::
 +--rerere-autoupdate::
 +--no-rerere-autoupdate::
        Allow the rerere mechanism to update the index with the
        result of auto-conflict resolution if possible.
  
+ --overwrite-ignore::
+ --no-overwrite-ignore::
+       Silently overwrite ignored files from the merge result. This
+       is the default behavior. Use `--no-overwrite-ignore` to abort.
  --abort::
        Abort the current conflict resolution process, and
        try to reconstruct the pre-merge state.
@@@ -99,10 -104,6 +104,10 @@@ commit or stash your changes before run
  'git merge --abort' is equivalent to 'git reset --merge' when
  `MERGE_HEAD` is present.
  
 +--quit::
 +      Forget about the current merge in progress. Leave the index
 +      and the working tree as-is.
 +
  --continue::
        After a 'git merge' stops due to conflicts you can conclude the
        merge by running 'git merge --continue' (see "HOW TO RESOLVE
index 057c21d8ee5de7757c788e9a51abe1e7d2504a04,c742b73a003ab63a7914649632cd1b94ec20af1d..6156609cf7149ccf5c1f79df2d9807cdbcc609ba
@@@ -12,12 -12,12 +12,12 @@@ SYNOPSI
        [<upstream> [<branch>]]
  'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
        --root [<branch>]
 -'git rebase' --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch
 +'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
  
  DESCRIPTION
  -----------
  If <branch> is specified, 'git rebase' will perform an automatic
- `git checkout <branch>` before doing anything else.  Otherwise
+ `git switch <branch>` before doing anything else.  Otherwise
  it remains on the current branch.
  
  If <upstream> is not specified, the upstream configured in
@@@ -300,11 -300,6 +300,11 @@@ See also INCOMPATIBLE OPTIONS below
  +
  See also INCOMPATIBLE OPTIONS below.
  
 +--rerere-autoupdate::
 +--no-rerere-autoupdate::
 +      Allow the rerere mechanism to update the index with the
 +      result of auto-conflict resolution if possible.
 +
  -S[<keyid>]::
  --gpg-sign[=<keyid>]::
        GPG-sign commits. The `keyid` argument is optional and
@@@ -420,9 -415,9 +420,9 @@@ i.e. commits that would be excluded by 
  the `rebase-cousins` mode is turned on, such commits are instead rebased
  onto `<upstream>` (or `<onto>`, if specified).
  +
 -The `--rebase-merges` mode is similar in spirit to `--preserve-merges`, but
 -in contrast to that option works well in interactive rebases: commits can be
 -reordered, inserted and dropped at will.
 +The `--rebase-merges` mode is similar in spirit to the deprecated
 +`--preserve-merges`, but in contrast to that option works well in interactive
 +rebases: commits can be reordered, inserted and dropped at will.
  +
  It is currently only possible to recreate the merge commits using the
  `recursive` merge strategy; Different merge strategies can be used only via
@@@ -432,10 -427,9 +432,10 @@@ See also REBASING MERGES and INCOMPATIB
  
  -p::
  --preserve-merges::
 -      Recreate merge commits instead of flattening the history by replaying
 -      commits a merge commit introduces. Merge conflict resolutions or manual
 -      amendments to merge commits are not preserved.
 +      [DEPRECATED: use `--rebase-merges` instead] Recreate merge commits
 +      instead of flattening the history by replaying commits a merge commit
 +      introduces. Merge conflict resolutions or manual amendments to merge
 +      commits are not preserved.
  +
  This uses the `--interactive` machinery internally, but combining it
  with the `--interactive` option explicitly is generally not a good
@@@ -675,8 -669,7 +675,8 @@@ $ git rebase -i HEAD~
  
  And move the first patch to the end of the list.
  
 -You might want to preserve merges, if you have a history like this:
 +You might want to recreate merge commits, e.g. if you have a history
 +like this:
  
  ------------------
             X
@@@ -690,7 -683,7 +690,7 @@@ Suppose you want to rebase the side bra
  sure that the current HEAD is "B", and call
  
  -----------------------------
 -$ git rebase -i -p --onto Q O
 +$ git rebase -i -r --onto Q O
  -----------------------------
  
  Reordering and editing commits usually creates untested intermediate
@@@ -1027,11 -1020,11 +1027,11 @@@ merge cmak
  
  BUGS
  ----
 -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. Use
 -`--rebase-merges` in such scenarios instead.
 +The todo list presented by the deprecated `--preserve-merges --interactive`
 +does not represent the topology of the revision graph (use `--rebase-merges`
 +instead).  Editing commits and rewording their commit messages should work
 +fine, but attempts to reorder commits tend to produce counterintuitive results.
 +Use `--rebase-merges` in such scenarios instead.
  
  For example, an attempt to rearrange
  ------------
index 95763d7581579f5e20d063e5b6cba20f8a6d59cf,fe4434ad9df93eb0a9a60c1fb0128949b29ec9ab..4cfc883378082673e57c4d14fc27764e6223c0c4
@@@ -24,7 -24,7 +24,7 @@@ on the initial manual merge, and applyi
  hand resolutions to their corresponding automerge results.
  
  [NOTE]
 -You need to set the configuration variable rerere.enabled in order to
 +You need to set the configuration variable `rerere.enabled` in order to
  enable this command.
  
  
@@@ -91,7 -91,7 +91,7 @@@ For such a test, you need to merge mast
  One way to do it is to pull master into the topic branch:
  
  ------------
-       $ git checkout topic
+       $ git switch topic
        $ git merge master
  
                o---*---o---+ topic
@@@ -113,10 -113,10 +113,10 @@@ the upstream might have been advanced s
  in which case the final commit graph would look like this:
  
  ------------
-       $ git checkout topic
+       $ git switch topic
        $ git merge master
        $ ... work on both topic and master branches
-       $ git checkout master
+       $ git switch master
        $ git merge topic
  
                o---*---o---+---o---o topic
@@@ -136,11 -136,11 +136,11 @@@ merges, you could blow away the test me
  top of the tip before the test merge:
  
  ------------
-       $ git checkout topic
+       $ git switch topic
        $ git merge master
        $ git reset --hard HEAD^ ;# rewind the test merge
        $ ... work on both topic and master branches
-       $ git checkout master
+       $ git switch master
        $ git merge topic
  
                o---*---o-------o---o topic
index 26e746c53f25ce833e7dace94b37b178aca48f4e,633d71d36af2562e57dd913951226356f54ca80a..97e0544d9e1e171d60a4d413b60df88884ed233f
@@@ -25,12 -25,13 +25,13 @@@ The `<tree-ish>`/`<commit>` defaults t
        the current branch.)
  +
  This means that `git reset <paths>` is the opposite of `git add
- <paths>`.
+ <paths>`. This command is equivalent to
+ `git restore [--source=<tree-ish>] --staged <paths>...`.
  +
  After running `git reset <paths>` to update the index entry, you can
- use linkgit:git-checkout[1] to check the contents out of the index to
- the working tree.
Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
+ use linkgit:git-restore[1] to check the contents out of the index to
+ the working tree. Alternatively, using linkgit:git-restore[1]
and specifying a commit with `--source`, you
  can copy the contents of a path out of a commit to the index and to the
  working tree in one go.
  
@@@ -86,8 -87,8 +87,8 @@@ but carries forward unmerged index entr
        changes, reset is aborted.
  --
  
- If you want to undo a commit other than the latest on a branch,
linkgit:git-revert[1] is your friend.
+ See "Reset, restore and revert" in linkgit:git[1] for the differences
between the three commands.
  
  
  OPTIONS
@@@ -149,9 -150,9 +150,9 @@@ See also the `--amend` option to linkgi
  Undo a commit, making it a topic branch::
  +
  ------------
- $ git branch topic/wip     <1>
- $ git reset --hard HEAD~3  <2>
- $ git checkout topic/wip   <3>
+ $ git branch topic/wip          <1>
+ $ git reset --hard HEAD~3       <2>
+ $ git switch topic/wip          <3>
  ------------
  +
  <1> You have made some commits, but realize they were premature
@@@ -232,13 -233,13 +233,13 @@@ working tree are not in any shape to b
  need to get to the other branch for a quick bugfix.
  +
  ------------
- $ git checkout feature ;# you were working in "feature" branch and
- $ work work work       ;# got interrupted
+ $ git switch feature  ;# you were working in "feature" branch and
+ $ work work work      ;# got interrupted
  $ git commit -a -m "snapshot WIP"                 <1>
- $ git checkout master
+ $ git switch master
  $ fix fix fix
  $ git commit ;# commit with real log
- $ git checkout feature
+ $ git switch feature
  $ git reset --soft HEAD^ ;# go back to WIP state  <2>
  $ git reset                                       <3>
  ------------
@@@ -279,18 -280,18 +280,18 @@@ reset it while keeping the changes in y
  +
  ------------
  $ git tag start
- $ git checkout -b branch1
+ $ git switch -c branch1
  $ edit
  $ git commit ...                            <1>
  $ edit
- $ git checkout -b branch2                   <2>
+ $ git switch -c branch2                     <2>
  $ git reset --keep start                    <3>
  ------------
  +
  <1> This commits your first edits in `branch1`.
  <2> In the ideal world, you could have realized that the earlier
      commit did not belong to the new topic when you created and switched
-     to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is
+     to `branch2` (i.e. `git switch -c branch2 start`), but nobody is
      perfect.
  <3> But you can use `reset --keep` to remove the unwanted commit after
      you switched to `branch2`.
@@@ -428,8 -429,8 +429,8 @@@ working index HEAD target         worki
  
  `reset --merge` is meant to be used when resetting out of a conflicted
  merge. Any mergy operation guarantees that the working tree file that is
 -involved in the merge does not have local change wrt the index before
 -it starts, and that it writes the result out to the working tree. So if
 +involved in the merge does not have a local change with respect to the index
 +before it starts, and that it writes the result out to the working tree. So if
  we see some difference between the index and the target and also
  between the index and the working tree, then it means that we are not
  resetting out from a state that a mergy operation left after failing
index 0c82ca5bc0e5a380e8fc441fbb34e12c85cffcab,9aadc36881beae56b11202450947bcf28f94f3f6..fae4d66547fb900d79d2bafad52cf0aba9a51158
@@@ -26,10 -26,13 +26,13 @@@ effect of some earlier commits (often o
  throw away all uncommitted changes in your working directory, you
  should see linkgit:git-reset[1], particularly the `--hard` option.  If
  you want to extract specific files as they were in another commit, you
- should see linkgit:git-checkout[1], specifically the `git checkout
<commit> -- <filename>` syntax.  Take care with these alternatives as
+ should see linkgit:git-restore[1], specifically the `--source`
option. Take care with these alternatives as
  both will discard uncommitted changes in your working directory.
  
+ See "Reset, restore and revert" in linkgit:git[1] for the differences
+ between the three commands.
  OPTIONS
  -------
  <commit>...::
@@@ -66,13 -69,6 +69,13 @@@ more details
        With this option, 'git revert' will not start the commit
        message editor.
  
 +--cleanup=<mode>::
 +      This option determines how the commit message will be cleaned up before
 +      being passed on to the commit machinery. See linkgit:git-commit[1] for more
 +      details. In particular, if the '<mode>' is given a value of `scissors`,
 +      scissors will be appended to `MERGE_MSG` before being passed on in the case
 +      of a conflict.
 +
  -n::
  --no-commit::
        Usually the command automatically creates some commits with
@@@ -108,11 -104,6 +111,11 @@@ effect to your index in a row
        Pass the merge strategy-specific option through to the
        merge strategy.  See linkgit:git-merge[1] for details.
  
 +--rerere-autoupdate::
 +--no-rerere-autoupdate::
 +      Allow the rerere mechanism to update the index with the
 +      result of auto-conflict resolution if possible.
 +
  SEQUENCER SUBCOMMANDS
  ---------------------
  include::sequencer.txt[]
index e31ea7d3037d55207132fc6ab07b52b7710199af,ebb6282db3ef7a8a96681dd21cd8a02ccd3a0cdd..8fbe12c66c823bce8756eaf3c836d5e8f0f7a340
@@@ -9,7 -9,7 +9,7 @@@ SYNOPSI
  --------
  [verse]
  'git stash' list [<options>]
 -'git stash' show [<stash>]
 +'git stash' show [<options>] [<stash>]
  'git stash' drop [-q|--quiet] [<stash>]
  'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
  'git stash' branch <branchname> [<stash>]
@@@ -106,7 -106,7 +106,7 @@@ stash@{1}: On master: 9cc0589... Add gi
  The command takes options applicable to the 'git log'
  command to control what is shown and how. See linkgit:git-log[1].
  
 -show [<stash>]::
 +show [<options>] [<stash>]::
  
        Show the changes recorded in the stash entry as a diff between the
        stashed contents and the commit back when the stash entry was first
@@@ -235,12 -235,12 +235,12 @@@ return to your original branch to make 
  +
  ----------------------------------------------------------------
  # ... hack hack hack ...
- $ git checkout -b my_wip
+ $ git switch -c my_wip
  $ git commit -a -m "WIP"
- $ git checkout master
+ $ git switch master
  $ edit emergency fix
  $ git commit -a -m "Fix in a hurry"
- $ git checkout my_wip
+ $ git switch my_wip
  $ git reset --soft HEAD^
  # ... continue hacking ...
  ----------------------------------------------------------------
@@@ -293,7 -293,8 +293,8 @@@ SEE ALS
  linkgit:git-checkout[1],
  linkgit:git-commit[1],
  linkgit:git-reflog[1],
- linkgit:git-reset[1]
+ linkgit:git-reset[1],
+ linkgit:git-switch[1]
  
  GIT
  ---
diff --combined Documentation/git.txt
index f9b09db89b83b9be12c4fdce856b4be822db23f0,fbed007354d38990b48e3cca54c4bbc6a5278fdc..e095514ace357b8e76ab0d9ea58b2a6c0e3dcd5b
@@@ -33,8 -33,7 +33,8 @@@ individual Git commands with "git help 
  manual page gives you an overview of the command-line command syntax.
  
  A formatted and hyperlinked copy of the latest Git documentation
 -can be viewed at `https://git.github.io/htmldocs/git.html`.
 +can be viewed at https://git.github.io/htmldocs/git.html
 +or https://git-scm.com/docs.
  
  
  OPTIONS
@@@ -211,6 -210,26 +211,26 @@@ people via patch over e-mail
  
  include::cmds-foreignscminterface.txt[]
  
+ Reset, restore and revert
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ There are three commands with similar names: `git reset`,
+ `git restore` and `git revert`.
+ * linkgit:git-revert[1] is about making a new commit that reverts the
+   changes made by other commits.
+ * linkgit:git-restore[1] is about restoring files in the working tree
+   from either the index or another commit. This command does not
+   update your branch. The command can also be used to restore files in
+   the index from another commit.
+ * linkgit:git-reset[1] is about updating your branch, moving the tip
+   in order to add or remove commits from the branch. This operation
+   changes the commit history.
+ +
+ `git reset` can also be used to restore the index, overlapping with
+ `git restore`.
  
  Low-level commands (plumbing)
  -----------------------------
@@@ -537,6 -556,7 +557,6 @@@ othe
        The command-line parameters passed to the configured command are
        determined by the ssh variant.  See `ssh.variant` option in
        linkgit:git-config[1] for details.
 -
  +
  `$GIT_SSH_COMMAND` takes precedence over `$GIT_SSH`, and is interpreted
  by the shell, which allows additional arguments to be included.
@@@ -661,54 -681,6 +681,54 @@@ of clones and fetches
        When a curl trace is enabled (see `GIT_TRACE_CURL` above), do not dump
        data (that is, only dump info lines and headers).
  
 +`GIT_TRACE2`::
 +      Enables more detailed trace messages from the "trace2" library.
 +      Output from `GIT_TRACE2` is a simple text-based format for human
 +      readability.
 ++
 +If this variable is set to "1", "2" or "true" (comparison
 +is case insensitive), trace messages will be printed to
 +stderr.
 ++
 +If the variable is set to an integer value greater than 2
 +and lower than 10 (strictly) then Git will interpret this
 +value as an open file descriptor and will try to write the
 +trace messages into this file descriptor.
 ++
 +Alternatively, if the variable is set to an absolute path
 +(starting with a '/' character), Git will interpret this
 +as a file path and will try to append the trace messages
 +to it.  If the path already exists and is a directory, the
 +trace messages will be written to files (one per process)
 +in that directory, named according to the last component
 +of the SID and an optional counter (to avoid filename
 +collisions).
 ++
 +In addition, if the variable is set to
 +`af_unix:[<socket_type>:]<absolute-pathname>`, Git will try
 +to open the path as a Unix Domain Socket.  The socket type
 +can be either `stream` or `dgram`.
 ++
 +Unsetting the variable, or setting it to empty, "0" or
 +"false" (case insensitive) disables trace messages.
 ++
 +See link:technical/api-trace2.html[Trace2 documentation]
 +for full details.
 +
 +
 +`GIT_TRACE2_EVENT`::
 +      This setting writes a JSON-based format that is suited for machine
 +      interpretation.
 +      See `GIT_TRACE2` for available trace output options and
 +      link:technical/api-trace2.html[Trace2 documentation] for full details.
 +
 +`GIT_TRACE2_PERF`::
 +      In addition to the text-based messages available in `GIT_TRACE2`, this
 +      setting writes a column-based format for understanding nesting
 +      regions.
 +      See `GIT_TRACE2` for available trace output options and
 +      link:technical/api-trace2.html[Trace2 documentation] for full details.
 +
  `GIT_REDACT_COOKIES`::
        This can be set to a comma-separated list of strings. When a curl trace
        is enabled (see `GIT_TRACE_CURL` above), whenever a "Cookies:" header
index 2796dfc83bb15174c2657f90ded09b6d6af80e41,af16a35a9a72a3ccc850f006c335d5f4b68003b5..fb1d188d440cc2fd3579844045d01a77423a58c7
@@@ -18,7 -18,7 +18,7 @@@ A `gitattributes` file is a simple tex
  
  Each line in `gitattributes` file is of form:
  
 -      pattern attr1 attr2 ...
 +      pattern attr1 attr2 ...
  
  That is, a pattern followed by an attributes list,
  separated by whitespaces. Leading and trailing whitespaces are
@@@ -112,7 -112,8 +112,8 @@@ Checking-out and checking-i
  
  These attributes affect how the contents stored in the
  repository are copied to the working tree files when commands
- such as 'git checkout' and 'git merge' run.  They also affect how
+ such as 'git switch', 'git checkout'  and 'git merge' run.
+ They also affect how
  Git stores the contents you prepare in the working tree in the
  repository upon 'git add' and 'git commit'.
  
@@@ -314,8 -315,8 +315,8 @@@ stored as UTF-8 internally. A client wi
  support will checkout `foo.ps1` as UTF-8 encoded file. This will
  typically cause trouble for the users of this file.
  +
 -If a Git client, that does not support the `working-tree-encoding`
 -attribute, adds a new file `bar.ps1`, then `bar.ps1` will be
 +If a Git client that does not support the `working-tree-encoding`
 +attribute adds a new file `bar.ps1`, then `bar.ps1` will be
  stored "as-is" internally (in this example probably as UTF-16).
  A client with `working-tree-encoding` support will interpret the
  internal contents as UTF-8 and try to convert it to UTF-16 on checkout.
@@@ -819,7 -820,7 +820,7 @@@ patterns are available
  
  - `java` suitable for source code in the Java language.
  
 -- `matlab` suitable for source code in the MATLAB language.
 +- `matlab` suitable for source code in the MATLAB and Octave languages.
  
  - `objc` suitable for source code in the Objective-C language.
  
  
  - `ruby` suitable for source code in the Ruby language.
  
 +- `rust` suitable for source code in the Rust language.
 +
  - `tex` suitable for source code for LaTeX documents.
  
  
index 786e778ab8223a0ee02a44c8c756652fcf147205,8ff72f06132b3388d0e56606ef90a5c46fc18ad7..82cd573776cec696774c467979c6da10f23554c3
@@@ -165,12 -165,13 +165,13 @@@ rebased, and is not set when rebasing t
  post-checkout
  ~~~~~~~~~~~~~
  
- This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
+ This hook is invoked when a linkgit:git-checkout[1] or
+ linkgit:git-switch[1] is run after having updated the
  worktree.  The hook is given three parameters: the ref of the previous HEAD,
  the ref of the new HEAD (which may or may not have changed), and a flag
  indicating whether the checkout was a branch checkout (changing branches,
  flag=1) or a file checkout (retrieving a file from the index, flag=0).
- This hook cannot affect the outcome of `git checkout`.
+ This hook cannot affect the outcome of `git switch` or `git checkout`.
  
  It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
  used. The first parameter given to the hook is the null-ref, the second the
@@@ -406,7 -407,8 +407,8 @@@ exit with a zero status
  For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
  in order to emulate `git fetch` that is run in the reverse direction
  with `git push`, as the two-tree form of `git read-tree -u -m` is
- essentially the same as `git checkout` that switches branches while
+ essentially the same as `git switch` or `git checkout`
+ that switches branches while
  keeping the local changes in the working tree that do not interfere
  with the difference between the branches.
  
@@@ -496,24 -498,6 +498,24 @@@ This hook is invoked by `git-p4 submit`
  from standard input. Exiting with non-zero status from this script prevent
  `git-p4 submit` from launching. Run `git-p4 submit --help` for details.
  
 +post-index-change
 +~~~~~~~~~~~~~~~~~
 +
 +This hook is invoked when the index is written in read-cache.c
 +do_write_locked_index.
 +
 +The first parameter passed to the hook is the indicator for the
 +working directory being updated.  "1" meaning working directory
 +was updated or "0" when the working directory was not updated.
 +
 +The second parameter passed to the hook is the indicator for whether
 +or not the index was updated and the skip-worktree bit could have
 +changed.  "1" meaning skip-worktree bits could have been updated
 +and "0" meaning they were not.
 +
 +Only one parameter should be set to "1" when the hook runs.  The hook
 +running passing "1", "1" should not be possible.
 +
  GIT
  ---
  Part of the linkgit:git[1] suite
index 82c1e5754e775039f4e858fde64619f8a70e7af5,a1c7a65da643fead6608f1d597b3404a30ed18af..97f995e5a9a6012ddd41169f06f7b9ddd3aa1c1a
@@@ -58,14 -58,14 +58,14 @@@ when you run `git merge`
  when you run `git cherry-pick`.
  +
  Note that any of the 'refs/*' cases above may come either from
 -the '$GIT_DIR/refs' directory or from the '$GIT_DIR/packed-refs' file.
 +the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
  While the ref name encoding is unspecified, UTF-8 is preferred as
  some output processing may assume ref names in UTF-8.
  
  '@'::
    '@' alone is a shortcut for `HEAD`.
  
 -'<refname>@{<date>}', e.g. 'master@\{yesterday\}', 'HEAD@{5 minutes ago}'::
 +'[<refname>]@{<date>}', e.g. 'master@\{yesterday\}', 'HEAD@{5 minutes ago}'::
    A ref followed by the suffix '@' with a date specification
    enclosed in a brace
    pair (e.g. '\{yesterday\}', '{1 month 2 weeks 3 days 1 hour 1
@@@ -95,7 -95,7 +95,7 @@@
    The construct '@{-<n>}' means the <n>th branch/commit checked out
    before the current one.
  
 -'<branchname>@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
 +'[<branchname>]@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
    The suffix '@\{upstream\}' to a branchname (short form '<branchname>@\{u\}')
    refers to the branch that the branch specified by branchname is set to build on
    top of (configured with `branch.<name>.remote` and
    current one. These suffixes are also accepted when spelled in uppercase, and
    they mean the same thing no matter the case.
  
 -'<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
 +'[<branchname>]@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
    The suffix '@\{push}' reports the branch "where we would push to" if
    `git push` were run while `branchname` was checked out (or the current
    `HEAD` if no branchname is specified). Since our push destination is
    in a remote repository, of course, we report the local tracking branch
 -  that corresponds to that branch (i.e., something in 'refs/remotes/').
 +  that corresponds to that branch (i.e., something in `refs/remotes/`).
  +
  Here's an example to make it more clear:
  +
  ------------------------------
  $ git config push.default current
  $ git config remote.pushdefault myfork
- $ git checkout -b mybranch origin/master
+ $ git switch -c mybranch origin/master
  
  $ git rev-parse --symbolic-full-name @{upstream}
  refs/remotes/origin/master
@@@ -131,7 -131,7 +131,7 @@@ from one location and push to another. 
  This suffix is also accepted when spelled in uppercase, and means the same
  thing no matter the case.
  
 -'<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
 +'<rev>{caret}[<n>]', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
    A suffix '{caret}' to a revision parameter means the first parent of
    that commit object.  '{caret}<n>' means the <n>th parent (i.e.
    '<rev>{caret}'
    '<rev>{caret}0' means the commit itself and is used when '<rev>' is the
    object name of a tag object that refers to a commit object.
  
 -'<rev>{tilde}<n>', e.g. 'master{tilde}3'::
 +'<rev>{tilde}[<n>]', e.g. 'HEAD{tilde}, master{tilde}3'::
 +  A suffix '{tilde}' to a revision parameter means the first parent of
 +  that commit object.
    A suffix '{tilde}<n>' to a revision parameter means the commit
    object that is the <n>th generation ancestor of the named
    commit object, following only the first parents.  I.e. '<rev>{tilde}3' is
    '<rev>{caret}0'
    is a short-hand for '<rev>{caret}\{commit\}'.
  +
 -'rev{caret}\{object\}' can be used to make sure 'rev' names an
 -object that exists, without requiring 'rev' to be a tag, and
 -without dereferencing 'rev'; because a tag is already an object,
 +'<rev>{caret}\{object\}' can be used to make sure '<rev>' names an
 +object that exists, without requiring '<rev>' to be a tag, and
 +without dereferencing '<rev>'; because a tag is already an object,
  it does not have to be dereferenced even once to get to an object.
  +
 -'rev{caret}\{tag\}' can be used to ensure that 'rev' identifies an
 +'<rev>{caret}\{tag\}' can be used to ensure that '<rev>' identifies an
  existing tag object.
  
  '<rev>{caret}{}', e.g. 'v0.99.8{caret}{}'::
    Depending on the given text, the shell's word splitting rules might
    require additional quoting.
  
 -'<rev>:<path>', e.g. 'HEAD:README', ':README', 'master:./README'::
 +'<rev>:<path>', e.g. 'HEAD:README', 'master:./README'::
    A suffix ':' followed by a path names the blob or tree
    at the given path in the tree-ish object named by the part
    before the colon.
 -  ':path' (with an empty part before the colon)
 -  is a special case of the syntax described next: content
 -  recorded in the index at the given path.
    A path starting with './' or '../' is relative to the current working directory.
    The given path will be converted to be relative to the working tree's root directory.
    This is most useful to address a blob or tree from a commit or tree that has
    the same tree structure as the working tree.
  
 -':<n>:<path>', e.g. ':0:README', ':README'::
 +':[<n>:]<path>', e.g. ':0:README', ':README'::
    A colon, optionally followed by a stage number (0 to 3) and a
    colon, followed by a path, names a blob object in the
    index at the given path. A missing stage number (and the colon
@@@ -301,7 -302,7 +301,7 @@@ The 'r1{caret}@' notation means all par
  The 'r1{caret}!' notation includes commit 'r1' but excludes all of its parents.
  By itself, this notation denotes the single commit 'r1'.
  
 -The '<rev>{caret}-<n>' notation includes '<rev>' but excludes the <n>th
 +The '<rev>{caret}-[<n>]' notation includes '<rev>' but excludes the <n>th
  parent (i.e. a shorthand for '<rev>{caret}<n>..<rev>'), with '<n>' = 1 if
  not given. This is typically useful for merge commits where you
  can just pass '<commit>{caret}-' to get all the commits in the branch
diff --combined Makefile
index f58bf14c7bf3d9b29fcd0a97bc14e253f3eb675d,783b0773a6b27e8aef12f8bf099696415b96cf7d..a6bbe3c407604192260edaa9ef0a0da04175a05a
+++ b/Makefile
@@@ -265,6 -265,10 +265,6 @@@ all:
  #
  # Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
  #
 -# Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
 -# that tells runtime paths to dynamic libraries;
 -# "-Wl,-rpath=/path/lib" is used instead.
 -#
  # Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
  # as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
  #
  #
  # Define FILENO_IS_A_MACRO if fileno() is a macro, not a real function.
  #
 +# Define NEED_ACCESS_ROOT_HANDLER if access() under root may success for X_OK
 +# even if execution permission isn't granted for any user.
 +#
  # Define PAGER_ENV to a SP separated VAR=VAL pairs to define
  # default environment variables to be passed when a pager is spawned, e.g.
  #
  #
  # Define DEVELOPER to enable more compiler warnings. Compiler version
  # and family are auto detected, but could be overridden by defining
 -# COMPILER_FEATURES (see config.mak.dev)
 +# COMPILER_FEATURES (see config.mak.dev). You can still set
 +# CFLAGS="..." in combination with DEVELOPER enables, whether that's
 +# for tweaking something unrelated (e.g. optimization level), or for
 +# selectively overriding something DEVELOPER or one of the DEVOPTS
 +# (see just below) brings in.
  #
  # When DEVELOPER is set, DEVOPTS can be used to control compiler
  # options.  This variable contains keywords separated by
@@@ -509,8 -506,17 +509,8 @@@ GIT-VERSION-FILE: FORC
        @$(SHELL_PATH) ./GIT-VERSION-GEN
  -include GIT-VERSION-FILE
  
 -# CFLAGS and LDFLAGS are for the users to override from the command line.
 -
 -CFLAGS = -g -O2 -Wall
 -LDFLAGS =
 -ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
 -ALL_LDFLAGS = $(LDFLAGS)
 -STRIP ?= strip
 -
 -# Create as necessary, replace existing, make ranlib unneeded.
 -ARFLAGS = rcs
 -
 +# Set our default configuration.
 +#
  # Among the variables below, these:
  #   gitexecdir
  #   template_dir
@@@ -555,7 -561,6 +555,7 @@@ perllibdir_relative = $(patsubst $(pref
  
  export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir
  
 +# Set our default programs
  CC = cc
  AR = ar
  RM = rm -f
@@@ -568,14 -573,29 +568,14 @@@ TCLTK_PATH = wis
  XGETTEXT = xgettext
  MSGFMT = msgfmt
  CURL_CONFIG = curl-config
 -PTHREAD_LIBS = -lpthread
 -PTHREAD_CFLAGS =
  GCOV = gcov
 +STRIP = strip
  SPATCH = spatch
  
  export TCL_PATH TCLTK_PATH
  
 -# user customisation variable for 'sparse' target
 -SPARSE_FLAGS ?=
 -# internal/platform customisation variable for 'sparse'
 -SP_EXTRA_FLAGS =
 -
 -SPATCH_FLAGS = --all-includes --patch .
 -
 -
 -
 -### --- END CONFIGURATION SECTION ---
 -
 -# Those must not be GNU-specific; they are shared with perl/ which may
 -# be built by a different compiler. (Note that this is an artifact now
 -# but it still might be nice to keep that distinction.)
 -BASIC_CFLAGS = -I.
 -BASIC_LDFLAGS =
 +# Set our default LIBS variables
 +PTHREAD_LIBS = -lpthread
  
  # Guard against environment variables
  BUILTIN_OBJS =
@@@ -591,7 -611,6 +591,7 @@@ FUZZ_PROGRAMS 
  LIB_OBJS =
  PROGRAM_OBJS =
  PROGRAMS =
 +EXCLUDED_PROGRAMS =
  SCRIPT_PERL =
  SCRIPT_PYTHON =
  SCRIPT_SH =
@@@ -613,13 -632,17 +613,13 @@@ SCRIPT_SH += git-merge-one-file.s
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-mergetool.sh
  SCRIPT_SH += git-quiltimport.sh
 -SCRIPT_SH += git-legacy-rebase.sh
 -SCRIPT_SH += git-remote-testgit.sh
 +SCRIPT_SH += git-legacy-stash.sh
  SCRIPT_SH += git-request-pull.sh
 -SCRIPT_SH += git-stash.sh
  SCRIPT_SH += git-submodule.sh
  SCRIPT_SH += git-web--browse.sh
  
  SCRIPT_LIB += git-mergetool--lib
  SCRIPT_LIB += git-parse-remote
 -SCRIPT_LIB += git-rebase--am
 -SCRIPT_LIB += git-rebase--common
  SCRIPT_LIB += git-rebase--preserve-merges
  SCRIPT_LIB += git-sh-setup
  SCRIPT_LIB += git-sh-i18n
@@@ -634,11 -657,17 +634,11 @@@ SCRIPT_PERL += git-svn.per
  
  SCRIPT_PYTHON += git-p4.py
  
 -NO_INSTALL += git-remote-testgit
 -
  # Generated files for scripts
  SCRIPT_SH_GEN = $(patsubst %.sh,%,$(SCRIPT_SH))
  SCRIPT_PERL_GEN = $(patsubst %.perl,%,$(SCRIPT_PERL))
  SCRIPT_PYTHON_GEN = $(patsubst %.py,%,$(SCRIPT_PYTHON))
  
 -SCRIPT_SH_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_SH_GEN))
 -SCRIPT_PERL_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PERL_GEN))
 -SCRIPT_PYTHON_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PYTHON_GEN))
 -
  # Individual rules to allow e.g.
  # "make -C ../.. SCRIPT_PERL=contrib/foo/bar.perl build-perl-script"
  # from subdirectories like contrib/*/
@@@ -648,11 -677,11 +648,11 @@@ build-sh-script: $(SCRIPT_SH_GEN
  build-python-script: $(SCRIPT_PYTHON_GEN)
  
  .PHONY: install-perl-script install-sh-script install-python-script
 -install-sh-script: $(SCRIPT_SH_INS)
 +install-sh-script: $(SCRIPT_SH_GEN)
        $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 -install-perl-script: $(SCRIPT_PERL_INS)
 +install-perl-script: $(SCRIPT_PERL_GEN)
        $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 -install-python-script: $(SCRIPT_PYTHON_INS)
 +install-python-script: $(SCRIPT_PYTHON_GEN)
        $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
  
  .PHONY: clean-perl-script clean-sh-script clean-python-script
@@@ -663,9 -692,9 +663,9 @@@ clean-perl-script
  clean-python-script:
        $(RM) $(SCRIPT_PYTHON_GEN)
  
 -SCRIPTS = $(SCRIPT_SH_INS) \
 -        $(SCRIPT_PERL_INS) \
 -        $(SCRIPT_PYTHON_INS) \
 +SCRIPTS = $(SCRIPT_SH_GEN) \
 +        $(SCRIPT_PERL_GEN) \
 +        $(SCRIPT_PYTHON_GEN) \
          git-instaweb
  
  ETAGS_TARGET = TAGS
@@@ -735,7 -764,6 +735,7 @@@ TEST_BUILTINS_OBJS += test-repository.
  TEST_BUILTINS_OBJS += test-revision-walking.o
  TEST_BUILTINS_OBJS += test-run-command.o
  TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 +TEST_BUILTINS_OBJS += test-serve-v2.o
  TEST_BUILTINS_OBJS += test-sha1.o
  TEST_BUILTINS_OBJS += test-sha1-array.o
  TEST_BUILTINS_OBJS += test-sha256.o
@@@ -771,9 -799,11 +771,11 @@@ BUILT_INS += git-format-patch$
  BUILT_INS += git-fsck-objects$X
  BUILT_INS += git-init$X
  BUILT_INS += git-merge-subtree$X
+ BUILT_INS += git-restore$X
  BUILT_INS += git-show$X
  BUILT_INS += git-stage$X
  BUILT_INS += git-status$X
+ BUILT_INS += git-switch$X
  BUILT_INS += git-whatchanged$X
  
  # what 'all' will build and 'install' will install in gitexecdir,
@@@ -814,12 -844,12 +816,12 @@@ VCSSVN_LIB = vcs-svn/lib.
  
  GENERATED_H += command-list.h
  
 -LIB_H := $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
 +LIB_H := $(sort $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
        $(FIND) . \
        -name .git -prune -o \
        -name t -prune -o \
        -name Documentation -prune -o \
 -      -name '*.h' -print)
 +      -name '*.h' -print))
  
  LIB_OBJS += abspath.o
  LIB_OBJS += advice.o
@@@ -996,7 -1026,6 +998,7 @@@ LIB_OBJS += trace2/tr2_cfg.
  LIB_OBJS += trace2/tr2_cmd_name.o
  LIB_OBJS += trace2/tr2_dst.o
  LIB_OBJS += trace2/tr2_sid.o
 +LIB_OBJS += trace2/tr2_sysenv.o
  LIB_OBJS += trace2/tr2_tbuf.o
  LIB_OBJS += trace2/tr2_tgt_event.o
  LIB_OBJS += trace2/tr2_tgt_normal.o
@@@ -1103,6 -1132,7 +1105,6 @@@ BUILTIN_OBJS += builtin/push.
  BUILTIN_OBJS += builtin/range-diff.o
  BUILTIN_OBJS += builtin/read-tree.o
  BUILTIN_OBJS += builtin/rebase.o
 -BUILTIN_OBJS += builtin/rebase--interactive.o
  BUILTIN_OBJS += builtin/receive-pack.o
  BUILTIN_OBJS += builtin/reflog.o
  BUILTIN_OBJS += builtin/remote.o
@@@ -1117,11 -1147,11 +1119,11 @@@ BUILTIN_OBJS += builtin/rev-parse.
  BUILTIN_OBJS += builtin/revert.o
  BUILTIN_OBJS += builtin/rm.o
  BUILTIN_OBJS += builtin/send-pack.o
 -BUILTIN_OBJS += builtin/serve.o
  BUILTIN_OBJS += builtin/shortlog.o
  BUILTIN_OBJS += builtin/show-branch.o
  BUILTIN_OBJS += builtin/show-index.o
  BUILTIN_OBJS += builtin/show-ref.o
 +BUILTIN_OBJS += builtin/stash.o
  BUILTIN_OBJS += builtin/stripspace.o
  BUILTIN_OBJS += builtin/submodule--helper.o
  BUILTIN_OBJS += builtin/symbolic-ref.o
@@@ -1149,29 -1179,6 +1151,29 @@@ ifeq ($(wildcard sha1collisiondetection
  DC_SHA1_SUBMODULE = auto
  endif
  
 +# Set CFLAGS, LDFLAGS and other *FLAGS variables. These might be
 +# tweaked by config.* below as well as the command-line, both of
 +# which'll override these defaults.
 +CFLAGS = -g -O2 -Wall
 +LDFLAGS =
 +CC_LD_DYNPATH = -Wl,-rpath,
 +BASIC_CFLAGS = -I.
 +BASIC_LDFLAGS =
 +
 +# library flags
 +ARFLAGS = rcs
 +PTHREAD_CFLAGS =
 +
 +# For the 'sparse' target
 +SPARSE_FLAGS ?=
 +SP_EXTRA_FLAGS =
 +
 +# For the 'coccicheck' target; setting SPATCH_BATCH_SIZE higher will
 +# usually result in less CPU usage at the cost of higher peak memory.
 +# Setting it to 0 will feed all files in a single spatch invocation.
 +SPATCH_FLAGS = --all-includes --patch .
 +SPATCH_BATCH_SIZE = 1
 +
  include config.mak.uname
  -include config.mak.autogen
  -include config.mak
@@@ -1180,9 -1187,6 +1182,9 @@@ ifdef DEVELOPE
  include config.mak.dev
  endif
  
 +ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
 +ALL_LDFLAGS = $(LDFLAGS)
 +
  comma := ,
  empty :=
  space := $(empty) $(empty)
@@@ -1193,7 -1197,6 +1195,7 @@@ BASIC_CFLAGS += -fsanitize=$(SANITIZE) 
  BASIC_CFLAGS += -fno-omit-frame-pointer
  ifneq ($(filter undefined,$(SANITIZERS)),)
  BASIC_CFLAGS += -DNO_UNALIGNED_LOADS
 +BASIC_CFLAGS += -DSHA1DC_FORCE_ALIGNED_ACCESS
  endif
  ifneq ($(filter leak,$(SANITIZERS)),)
  BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS
@@@ -1285,6 -1288,16 +1287,6 @@@ ifeq ($(uname_S),Darwin
        PTHREAD_LIBS =
  endif
  
 -ifndef CC_LD_DYNPATH
 -      ifdef NO_R_TO_GCC_LINKER
 -              # Some gcc does not accept and pass -R to the linker to specify
 -              # the runtime dynamic library path.
 -              CC_LD_DYNPATH = -Wl,-rpath,
 -      else
 -              CC_LD_DYNPATH = -R
 -      endif
 -endif
 -
  ifdef NO_LIBGEN_H
        COMPAT_CFLAGS += -DNO_LIBGEN_H
        COMPAT_OBJS += compat/basename.o
@@@ -1327,7 -1340,6 +1329,7 @@@ ifdef NO_CUR
        REMOTE_CURL_PRIMARY =
        REMOTE_CURL_ALIASES =
        REMOTE_CURL_NAMES =
 +      EXCLUDED_PROGRAMS += git-http-fetch git-http-push
  else
        ifdef CURLDIR
                # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
@@@ -1352,11 -1364,7 +1354,11 @@@ endi
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
                        PROGRAM_OBJS += http-push.o
 +              else
 +                      EXCLUDED_PROGRAMS += git-http-push
                endif
 +      else
 +              EXCLUDED_PROGRAMS += git-http-push
        endif
        curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "072200"
@@@ -1604,7 -1612,6 +1606,7 @@@ ifdef NO_INET_PTO
  endif
  ifdef NO_UNIX_SOCKETS
        BASIC_CFLAGS += -DNO_UNIX_SOCKETS
 +      EXCLUDED_PROGRAMS += git-credential-cache git-credential-cache--daemon
  else
        LIB_OBJS += unix-socket.o
        PROGRAM_OBJS += credential-cache.o
@@@ -1824,11 -1831,6 +1826,11 @@@ ifdef FILENO_IS_A_MACR
        COMPAT_OBJS += compat/fileno.o
  endif
  
 +ifdef NEED_ACCESS_ROOT_HANDLER
 +      COMPAT_CFLAGS += -DNEED_ACCESS_ROOT_HANDLER
 +      COMPAT_OBJS += compat/access.o
 +endif
 +
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK = NoThanks
  endif
@@@ -2129,9 -2131,7 +2131,9 @@@ $(BUILT_INS): git$
  command-list.h: generate-cmdlist.sh command-list.txt
  
  command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt Documentation/config/*.txt
 -      $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@
 +      $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh \
 +              $(patsubst %,--exclude-program %,$(EXCLUDED_PROGRAMS)) \
 +              command-list.txt >$@+ && mv $@+ $@
  
  SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
@@@ -2464,14 -2464,6 +2466,14 @@@ $(VCSSVN_LIB): $(VCSSVN_OBJS
  
  export DEFAULT_EDITOR DEFAULT_PAGER
  
 +Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
 +      @EXCLUDED='EXCLUDED_PROGRAMS := $(EXCLUDED_PROGRAMS)'; \
 +          if test x"$$EXCLUDED" != \
 +              x"`cat Documentation/GIT-EXCLUDED-PROGRAMS 2>/dev/null`" ; then \
 +              echo >&2 "    * new documentation flags"; \
 +              echo "$$EXCLUDED" >Documentation/GIT-EXCLUDED-PROGRAMS; \
 +            fi
 +
  .PHONY: doc man man-perl html info pdf
  doc: man-perl
        $(MAKE) -C Documentation all
@@@ -2710,6 -2702,7 +2712,6 @@@ endi
  test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
  
  all:: $(TEST_PROGRAMS) $(test_bindir_programs)
 -all:: $(NO_INSTALL)
  
  bin-wrappers/%: wrap-for-bin.sh
        @mkdir -p bin-wrappers
@@@ -2795,14 -2788,12 +2797,14 @@@ endi
  
  %.cocci.patch: %.cocci $(COCCI_SOURCES)
        @echo '    ' SPATCH $<; \
 -      ret=0; \
 -      for f in $(COCCI_SOURCES); do \
 -              $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS) || \
 -                      { ret=$$?; break; }; \
 -      done >$@+ 2>$@.log; \
 -      if test $$ret != 0; \
 +      if test $(SPATCH_BATCH_SIZE) = 0; then \
 +              limit=; \
 +      else \
 +              limit='-n $(SPATCH_BATCH_SIZE)'; \
 +      fi; \
 +      if ! echo $(COCCI_SOURCES) | xargs $$limit \
 +              $(SPATCH) --sp-file $< $(SPATCH_FLAGS) \
 +              >$@+ 2>$@.log; \
        then \
                cat $@.log; \
                exit 1; \
@@@ -2998,7 -2989,7 +3000,7 @@@ rpm:
  
  artifacts-tar:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) \
                GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
 -              $(NO_INSTALL) $(MOFILES)
 +              $(MOFILES)
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
                SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
        test -n "$(ARTIFACTS_DIRECTORY)"
@@@ -3047,7 -3038,7 +3049,7 @@@ clean: profile-clean coverage-clean coc
        $(RM) $(OBJECTS)
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
 -      $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
 +      $(RM) $(TEST_PROGRAMS)
        $(RM) $(FUZZ_PROGRAMS)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
        $(MAKE) -C Documentation/ clean
 +      $(RM) Documentation/GIT-EXCLUDED-PROGRAMS
  ifndef NO_PERL
        $(MAKE) -C gitweb clean
        $(RM) -r perl/build/
@@@ -3086,13 -3076,13 +3088,13 @@@ ALL_COMMANDS += git-gui git-citoo
  .PHONY: check-docs
  check-docs::
        $(MAKE) -C Documentation lint-docs
 -      @(for v in $(ALL_COMMANDS); \
 +      @(for v in $(patsubst %$X,%,$(ALL_COMMANDS)); \
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
                git-merge-resolve | git-merge-subtree | \
                git-fsck-objects | git-init-db | \
 -              git-remote-* | git-stage | \
 +              git-remote-* | git-stage | git-legacy-* | \
                git-?*--?* ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
        ( \
                sed -e '1,/^### command list/d' \
                    -e '/^#/d' \
 +                  -e '/guide$$/d' \
                    -e 's/[     ].*//' \
                    -e 's/^/listed /' command-list.txt; \
                $(MAKE) -C Documentation print-man1 | \
                grep '\.txt$$' | \
 -              sed -e 's|Documentation/|documented |' \
 +              sed -e 's|^|documented |' \
                    -e 's/\.txt//'; \
        ) | while read how cmd; \
        do \
 -              case " $(ALL_COMMANDS) " in \
 +              case " $(patsubst %$X,%,$(ALL_COMMANDS) $(EXCLUDED_PROGRAMS)) " in \
                *" $$cmd "*)    ;; \
                *) echo "removed but $$how: $$cmd" ;; \
                esac; \
diff --combined advice.c
index ce5f374ecd4917346145c8a380ac8c0e45e849a4,24a7741083e14d28557bdcd358af0857be2e96e9..b04709bfec98df6df93d39ed773a95a6dd897c18
+++ b/advice.c
@@@ -26,7 -26,6 +26,7 @@@ int advice_ignored_hook = 1
  int advice_waiting_for_editor = 1;
  int advice_graft_file_deprecated = 1;
  int advice_checkout_ambiguous_remote_branch_name = 1;
 +int advice_nested_tag = 1;
  
  static int advice_use_color = -1;
  static char advice_colors[][COLOR_MAXLEN] = {
@@@ -82,7 -81,6 +82,7 @@@ static struct 
        { "waitingForEditor", &advice_waiting_for_editor },
        { "graftFileDeprecated", &advice_graft_file_deprecated },
        { "checkoutAmbiguousRemoteBranchName", &advice_checkout_ambiguous_remote_branch_name },
 +      { "nestedTag", &advice_nested_tag },
  
        /* make this an alias for backward compatibility */
        { "pushNonFastForward", &advice_push_update_rejected }
@@@ -193,13 -191,22 +193,22 @@@ void NORETURN die_conclude_merge(void
  void detach_advice(const char *new_name)
  {
        const char *fmt =
-       _("Note: checking out '%s'.\n\n"
+       _("Note: switching to '%s'.\n"
+       "\n"
        "You are in 'detached HEAD' state. You can look around, make experimental\n"
        "changes and commit them, and you can discard any commits you make in this\n"
-       "state without impacting any branches by performing another checkout.\n\n"
+       "state without impacting any branches by switching back to a branch.\n"
+       "\n"
        "If you want to create a new branch to retain commits you create, you may\n"
-       "do so (now or later) by using -b with the checkout command again. Example:\n\n"
-       "  git checkout -b <new-branch-name>\n\n");
+       "do so (now or later) by using -c with the switch command. Example:\n"
+       "\n"
+       "  git switch -c <new-branch-name>\n"
+       "\n"
+       "Or undo this operation with:\n"
+       "\n"
+       "  git switch -\n"
+       "\n"
+       "Turn off this advice by setting config variable advice.detachedHead to false\n\n");
  
        fprintf(stderr, fmt, new_name);
  }
diff --combined branch.c
index e70838fb872f98820a641ccaccc06fd4c0c381f7,8dd5bb9f1c38ac5bf94b9cb845b3dba0daceac10..579494738a7f804974d2b396a1c795acd4a44789
+++ b/branch.c
@@@ -5,7 -5,6 +5,7 @@@
  #include "refs.h"
  #include "refspec.h"
  #include "remote.h"
 +#include "sequencer.h"
  #include "commit.h"
  #include "worktree.h"
  
@@@ -269,7 -268,7 +269,7 @@@ void create_branch(struct repository *r
        }
  
        real_ref = NULL;
 -      if (get_oid(start_name, &oid)) {
 +      if (get_oid_mb(start_name, &oid)) {
                if (explicit_tracking) {
                        if (advice_set_upstream_failure) {
                                error(_(upstream_missing), start_name);
        free(real_ref);
  }
  
 -void remove_branch_state(struct repository *r, int verbose)
 +void remove_merge_branch_state(struct repository *r)
  {
 -      if (!unlink(git_path_cherry_pick_head(r)) && verbose)
 -              warning(_("cancelling a cherry picking in progress"));
 -      if (!unlink(git_path_revert_head(r)) && verbose)
 -              warning(_("cancelling a revert in progress"));
 -      if (!unlink(git_path_merge_head(r)) && verbose)
 -              warning(_("cancelling a merge in progress"));
 +      unlink(git_path_merge_head(r));
        unlink(git_path_merge_rr(r));
        unlink(git_path_merge_msg(r));
        unlink(git_path_merge_mode(r));
- void remove_branch_state(struct repository *r)
 +}
 +
-       sequencer_post_commit_cleanup(r);
++void remove_branch_state(struct repository *r, int verbose)
 +{
++      sequencer_post_commit_cleanup(r, verbose);
        unlink(git_path_squash_msg(r));
 +      remove_merge_branch_state(r);
  }
  
  void die_if_checked_out(const char *branch, int ignore_current_worktree)
diff --combined branch.h
index 064ee576f29764637dfdc62141973e935f8fd488,aed045901eb9cfea5ea6348dc8229a2e7af24ff9..df0be61506fd36a0bfb63a911ba532b5db495767
+++ b/branch.h
@@@ -50,7 -50,7 +50,7 @@@ void create_branch(struct repository *r
   * Return 1 if the named branch already exists; return 0 otherwise.
   * Fill ref with the full refname for the branch.
   */
 -extern int validate_branchname(const char *name, struct strbuf *ref);
 +int validate_branchname(const char *name, struct strbuf *ref);
  
  /*
   * Check if a branch 'name' can be created as a new branch; die otherwise.
   * Return 1 if the named branch already exists; return 0 otherwise.
   * Fill ref with the full refname for the branch.
   */
 -extern int validate_new_branchname(const char *name, struct strbuf *ref, int force);
 +int validate_new_branchname(const char *name, struct strbuf *ref, int force);
 +
 +/*
 + * Remove information about the merge state on the current
 + * branch. (E.g., MERGE_HEAD)
 + */
 +void remove_merge_branch_state(struct repository *r);
  
  /*
   * Remove information about the state of working on the current
   * branch. (E.g., MERGE_HEAD)
   */
- void remove_branch_state(struct repository *r);
+ void remove_branch_state(struct repository *r, int verbose);
  
  /*
   * Configure local branch "local" as downstream to branch "remote"
   * Returns 0 on success.
   */
  #define BRANCH_CONFIG_VERBOSE 01
 -extern int install_branch_config(int flag, const char *local, const char *origin, const char *remote);
 +int install_branch_config(int flag, const char *local, const char *origin, const char *remote);
  
  /*
   * Read branch description
   */
 -extern int read_branch_desc(struct strbuf *, const char *branch_name);
 +int read_branch_desc(struct strbuf *, const char *branch_name);
  
  /*
   * Check if a branch is checked out in the main worktree or any linked
   * worktree and die (with a message describing its checkout location) if
   * it is.
   */
 -extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
 +void die_if_checked_out(const char *branch, int ignore_current_worktree);
  
  /*
   * Update all per-worktree HEADs pointing at the old ref to point the new ref.
   * This will be used when renaming a branch. Returns 0 if successful, non-zero
   * otherwise.
   */
 -extern int replace_each_worktree_head_symref(const char *oldref, const char *newref,
 -                                           const char *logmsg);
 +int replace_each_worktree_head_symref(const char *oldref, const char *newref,
 +                                    const char *logmsg);
  
  #endif
diff --combined builtin.h
index ec7e0954c4c8a1da896392bd28158abb74667898,6830000e14396f91a9d4cb8c2ab1b10f551c3dc5..3d449a021002540f1183166c3cfb1dfce765b75b
+++ b/builtin.h
@@@ -102,7 -102,7 +102,7 @@@ extern const char git_more_info_string[
  #define PRUNE_PACKED_DRY_RUN 01
  #define PRUNE_PACKED_VERBOSE 02
  
 -extern void prune_packed_objects(int);
 +void prune_packed_objects(int);
  
  struct fmt_merge_msg_opts {
        unsigned add_title:1,
        int shortlog_len;
  };
  
 -extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 -                       struct fmt_merge_msg_opts *);
 +int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 +                struct fmt_merge_msg_opts *);
  
  /**
   * If a built-in has DELAY_PAGER_CONFIG set, the built-in should call this early
   * You should most likely use a default of 0 or 1. "Punt" (-1) could be useful
   * to be able to fall back to some historical compatibility name.
   */
 -extern void setup_auto_pager(const char *cmd, int def);
 +void setup_auto_pager(const char *cmd, int def);
  
 -extern int is_builtin(const char *s);
 +int is_builtin(const char *s);
  
 -extern int cmd_add(int argc, const char **argv, const char *prefix);
 -extern int cmd_am(int argc, const char **argv, const char *prefix);
 -extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 -extern int cmd_apply(int argc, const char **argv, const char *prefix);
 -extern int cmd_archive(int argc, const char **argv, const char *prefix);
 -extern int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
 -extern int cmd_blame(int argc, const char **argv, const char *prefix);
 -extern int cmd_branch(int argc, const char **argv, const char *prefix);
 -extern int cmd_bundle(int argc, const char **argv, const char *prefix);
 -extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
 -extern int cmd_checkout(int argc, const char **argv, const char *prefix);
 -extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 -extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
 -extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
 -extern int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
 -extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 -extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 -extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
 -extern int cmd_clone(int argc, const char **argv, const char *prefix);
 -extern int cmd_clean(int argc, const char **argv, const char *prefix);
 -extern int cmd_column(int argc, const char **argv, const char *prefix);
 -extern int cmd_commit(int argc, const char **argv, const char *prefix);
 -extern int cmd_commit_graph(int argc, const char **argv, const char *prefix);
 -extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 -extern int cmd_config(int argc, const char **argv, const char *prefix);
 -extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 -extern int cmd_credential(int argc, const char **argv, const char *prefix);
 -extern int cmd_describe(int argc, const char **argv, const char *prefix);
 -extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
 -extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
 -extern int cmd_diff(int argc, const char **argv, const char *prefix);
 -extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
 -extern int cmd_difftool(int argc, const char **argv, const char *prefix);
 -extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
 -extern int cmd_fetch(int argc, const char **argv, const char *prefix);
 -extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
 -extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
 -extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
 -extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
 -extern int cmd_fsck(int argc, const char **argv, const char *prefix);
 -extern int cmd_gc(int argc, const char **argv, const char *prefix);
 -extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
 -extern int cmd_grep(int argc, const char **argv, const char *prefix);
 -extern int cmd_hash_object(int argc, const char **argv, const char *prefix);
 -extern int cmd_help(int argc, const char **argv, const char *prefix);
 -extern int cmd_index_pack(int argc, const char **argv, const char *prefix);
 -extern int cmd_init_db(int argc, const char **argv, const char *prefix);
 -extern int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
 -extern int cmd_log(int argc, const char **argv, const char *prefix);
 -extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
 -extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
 -extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 -extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 -extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 -extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 -extern int cmd_merge(int argc, const char **argv, const char *prefix);
 -extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 -extern int cmd_merge_index(int argc, const char **argv, const char *prefix);
 -extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 -extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
 -extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
 -extern int cmd_merge_tree(int argc, const char **argv, const char *prefix);
 -extern int cmd_mktag(int argc, const char **argv, const char *prefix);
 -extern int cmd_mktree(int argc, const char **argv, const char *prefix);
 -extern int cmd_multi_pack_index(int argc, const char **argv, const char *prefix);
 -extern int cmd_mv(int argc, const char **argv, const char *prefix);
 -extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 -extern int cmd_notes(int argc, const char **argv, const char *prefix);
 -extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
 -extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
 -extern int cmd_patch_id(int argc, const char **argv, const char *prefix);
 -extern int cmd_prune(int argc, const char **argv, const char *prefix);
 -extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 -extern int cmd_pull(int argc, const char **argv, const char *prefix);
 -extern int cmd_push(int argc, const char **argv, const char *prefix);
 -extern int cmd_range_diff(int argc, const char **argv, const char *prefix);
 -extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 -extern int cmd_rebase(int argc, const char **argv, const char *prefix);
 -extern int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
 -extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 -extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 -extern int cmd_remote(int argc, const char **argv, const char *prefix);
 -extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 -extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 -extern int cmd_repack(int argc, const char **argv, const char *prefix);
 -extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 -extern int cmd_reset(int argc, const char **argv, const char *prefix);
 -extern int cmd_restore(int argc, const char **argv, const char *prefix);
 -extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 -extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 -extern int cmd_revert(int argc, const char **argv, const char *prefix);
 -extern int cmd_rm(int argc, const char **argv, const char *prefix);
 -extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
 -extern int cmd_serve(int argc, const char **argv, const char *prefix);
 -extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
 -extern int cmd_show(int argc, const char **argv, const char *prefix);
 -extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
 -extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 -extern int cmd_status(int argc, const char **argv, const char *prefix);
 -extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 -extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
 -extern int cmd_switch(int argc, const char **argv, const char *prefix);
 -extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 -extern int cmd_tag(int argc, const char **argv, const char *prefix);
 -extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
 -extern int cmd_unpack_file(int argc, const char **argv, const char *prefix);
 -extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
 -extern int cmd_update_index(int argc, const char **argv, const char *prefix);
 -extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
 -extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
 -extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
 -extern int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
 -extern int cmd_upload_pack(int argc, const char **argv, const char *prefix);
 -extern int cmd_var(int argc, const char **argv, const char *prefix);
 -extern int cmd_verify_commit(int argc, const char **argv, const char *prefix);
 -extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
 -extern int cmd_version(int argc, const char **argv, const char *prefix);
 -extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
 -extern int cmd_worktree(int argc, const char **argv, const char *prefix);
 -extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
 -extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
 -extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
 -extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
 -extern int cmd_replace(int argc, const char **argv, const char *prefix);
 +int cmd_add(int argc, const char **argv, const char *prefix);
 +int cmd_am(int argc, const char **argv, const char *prefix);
 +int cmd_annotate(int argc, const char **argv, const char *prefix);
 +int cmd_apply(int argc, const char **argv, const char *prefix);
 +int cmd_archive(int argc, const char **argv, const char *prefix);
 +int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
 +int cmd_blame(int argc, const char **argv, const char *prefix);
 +int cmd_branch(int argc, const char **argv, const char *prefix);
 +int cmd_bundle(int argc, const char **argv, const char *prefix);
 +int cmd_cat_file(int argc, const char **argv, const char *prefix);
 +int cmd_checkout(int argc, const char **argv, const char *prefix);
 +int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 +int cmd_check_attr(int argc, const char **argv, const char *prefix);
 +int cmd_check_ignore(int argc, const char **argv, const char *prefix);
 +int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
 +int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 +int cmd_cherry(int argc, const char **argv, const char *prefix);
 +int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
 +int cmd_clone(int argc, const char **argv, const char *prefix);
 +int cmd_clean(int argc, const char **argv, const char *prefix);
 +int cmd_column(int argc, const char **argv, const char *prefix);
 +int cmd_commit(int argc, const char **argv, const char *prefix);
 +int cmd_commit_graph(int argc, const char **argv, const char *prefix);
 +int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 +int cmd_config(int argc, const char **argv, const char *prefix);
 +int cmd_count_objects(int argc, const char **argv, const char *prefix);
 +int cmd_credential(int argc, const char **argv, const char *prefix);
 +int cmd_describe(int argc, const char **argv, const char *prefix);
 +int cmd_diff_files(int argc, const char **argv, const char *prefix);
 +int cmd_diff_index(int argc, const char **argv, const char *prefix);
 +int cmd_diff(int argc, const char **argv, const char *prefix);
 +int cmd_diff_tree(int argc, const char **argv, const char *prefix);
 +int cmd_difftool(int argc, const char **argv, const char *prefix);
 +int cmd_fast_export(int argc, const char **argv, const char *prefix);
 +int cmd_fetch(int argc, const char **argv, const char *prefix);
 +int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
 +int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
 +int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
 +int cmd_format_patch(int argc, const char **argv, const char *prefix);
 +int cmd_fsck(int argc, const char **argv, const char *prefix);
 +int cmd_gc(int argc, const char **argv, const char *prefix);
 +int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
 +int cmd_grep(int argc, const char **argv, const char *prefix);
 +int cmd_hash_object(int argc, const char **argv, const char *prefix);
 +int cmd_help(int argc, const char **argv, const char *prefix);
 +int cmd_index_pack(int argc, const char **argv, const char *prefix);
 +int cmd_init_db(int argc, const char **argv, const char *prefix);
 +int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
 +int cmd_log(int argc, const char **argv, const char *prefix);
 +int cmd_log_reflog(int argc, const char **argv, const char *prefix);
 +int cmd_ls_files(int argc, const char **argv, const char *prefix);
 +int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 +int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 +int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 +int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 +int cmd_merge(int argc, const char **argv, const char *prefix);
 +int cmd_merge_base(int argc, const char **argv, const char *prefix);
 +int cmd_merge_index(int argc, const char **argv, const char *prefix);
 +int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 +int cmd_merge_file(int argc, const char **argv, const char *prefix);
 +int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
 +int cmd_merge_tree(int argc, const char **argv, const char *prefix);
 +int cmd_mktag(int argc, const char **argv, const char *prefix);
 +int cmd_mktree(int argc, const char **argv, const char *prefix);
 +int cmd_multi_pack_index(int argc, const char **argv, const char *prefix);
 +int cmd_mv(int argc, const char **argv, const char *prefix);
 +int cmd_name_rev(int argc, const char **argv, const char *prefix);
 +int cmd_notes(int argc, const char **argv, const char *prefix);
 +int cmd_pack_objects(int argc, const char **argv, const char *prefix);
 +int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
 +int cmd_patch_id(int argc, const char **argv, const char *prefix);
 +int cmd_prune(int argc, const char **argv, const char *prefix);
 +int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 +int cmd_pull(int argc, const char **argv, const char *prefix);
 +int cmd_push(int argc, const char **argv, const char *prefix);
 +int cmd_range_diff(int argc, const char **argv, const char *prefix);
 +int cmd_read_tree(int argc, const char **argv, const char *prefix);
 +int cmd_rebase(int argc, const char **argv, const char *prefix);
 +int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
 +int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 +int cmd_reflog(int argc, const char **argv, const char *prefix);
 +int cmd_remote(int argc, const char **argv, const char *prefix);
 +int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 +int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 +int cmd_repack(int argc, const char **argv, const char *prefix);
 +int cmd_rerere(int argc, const char **argv, const char *prefix);
 +int cmd_reset(int argc, const char **argv, const char *prefix);
++int cmd_restore(int argc, const char **argv, const char *prefix);
 +int cmd_rev_list(int argc, const char **argv, const char *prefix);
 +int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 +int cmd_revert(int argc, const char **argv, const char *prefix);
 +int cmd_rm(int argc, const char **argv, const char *prefix);
 +int cmd_send_pack(int argc, const char **argv, const char *prefix);
 +int cmd_shortlog(int argc, const char **argv, const char *prefix);
 +int cmd_show(int argc, const char **argv, const char *prefix);
 +int cmd_show_branch(int argc, const char **argv, const char *prefix);
 +int cmd_show_index(int argc, const char **argv, const char *prefix);
 +int cmd_status(int argc, const char **argv, const char *prefix);
 +int cmd_stash(int argc, const char **argv, const char *prefix);
 +int cmd_stripspace(int argc, const char **argv, const char *prefix);
 +int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
++int cmd_switch(int argc, const char **argv, const char *prefix);
 +int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 +int cmd_tag(int argc, const char **argv, const char *prefix);
 +int cmd_tar_tree(int argc, const char **argv, const char *prefix);
 +int cmd_unpack_file(int argc, const char **argv, const char *prefix);
 +int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
 +int cmd_update_index(int argc, const char **argv, const char *prefix);
 +int cmd_update_ref(int argc, const char **argv, const char *prefix);
 +int cmd_update_server_info(int argc, const char **argv, const char *prefix);
 +int cmd_upload_archive(int argc, const char **argv, const char *prefix);
 +int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
 +int cmd_upload_pack(int argc, const char **argv, const char *prefix);
 +int cmd_var(int argc, const char **argv, const char *prefix);
 +int cmd_verify_commit(int argc, const char **argv, const char *prefix);
 +int cmd_verify_tag(int argc, const char **argv, const char *prefix);
 +int cmd_version(int argc, const char **argv, const char *prefix);
 +int cmd_whatchanged(int argc, const char **argv, const char *prefix);
 +int cmd_worktree(int argc, const char **argv, const char *prefix);
 +int cmd_write_tree(int argc, const char **argv, const char *prefix);
 +int cmd_verify_pack(int argc, const char **argv, const char *prefix);
 +int cmd_show_ref(int argc, const char **argv, const char *prefix);
 +int cmd_pack_refs(int argc, const char **argv, const char *prefix);
 +int cmd_replace(int argc, const char **argv, const char *prefix);
  
  #endif
diff --combined builtin/am.c
index 252e37ddf08e45eceb98628fe598df3ebcafb1fd,99b66508fdb8f2e7fa5409a7d5f9d9f2901dae5f..1aea657a7f0b56345e1b2d5f8f32df439e278e80
@@@ -486,24 -486,23 +486,24 @@@ static int copy_notes_for_rebase(const 
  
        while (!strbuf_getline_lf(&sb, fp)) {
                struct object_id from_obj, to_obj;
 +              const char *p;
  
 -              if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
 +              if (sb.len != the_hash_algo->hexsz * 2 + 1) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
  
 -              if (get_oid_hex(sb.buf, &from_obj)) {
 +              if (parse_oid_hex(sb.buf, &from_obj, &p)) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
  
 -              if (sb.buf[GIT_SHA1_HEXSZ] != ' ') {
 +              if (*p != ' ') {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
  
 -              if (get_oid_hex(sb.buf + GIT_SHA1_HEXSZ + 1, &to_obj)) {
 +              if (get_oid_hex(p + 1, &to_obj)) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
@@@ -1339,10 -1338,9 +1339,10 @@@ static void write_index_patch(const str
        struct rev_info rev_info;
        FILE *fp;
  
 -      if (!get_oid_tree("HEAD", &head))
 -              tree = lookup_tree(the_repository, &head);
 -      else
 +      if (!get_oid("HEAD", &head)) {
 +              struct commit *commit = lookup_commit_or_die(&head, "HEAD");
 +              tree = get_commit_tree(commit);
 +      } else
                tree = lookup_tree(the_repository,
                                   the_repository->hash_algo->empty_tree);
  
@@@ -1503,11 -1501,11 +1503,11 @@@ static int fall_back_threeway(const str
                 * review them with extra care to spot mismerges.
                 */
                struct rev_info rev_info;
 -              const char *diff_filter_str = "--diff-filter=AM";
  
                repo_init_revisions(the_repository, &rev_info, NULL);
                rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
 -              diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
 +              rev_info.diffopt.filter |= diff_filter_bit('A');
 +              rev_info.diffopt.filter |= diff_filter_bit('M');
                add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
                diff_setup_done(&rev_info.diffopt);
                run_diff_index(&rev_info, 1);
@@@ -1644,8 -1642,11 +1644,8 @@@ static int do_interactive(struct am_sta
  {
        assert(state->msg);
  
 -      if (!isatty(0))
 -              die(_("cannot be interactive without stdin connected to a terminal."));
 -
        for (;;) {
 -              const char *reply;
 +              char reply[64];
  
                puts(_("Commit Body is:"));
                puts("--------------------------");
                 * in your translation. The program will only accept English
                 * input at this point.
                 */
 -              reply = git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);
 +              printf(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "));
 +              if (!fgets(reply, sizeof(reply), stdin))
 +                      die("unable to read from stdin; aborting");
  
 -              if (!reply) {
 -                      continue;
 -              } else if (*reply == 'y' || *reply == 'Y') {
 +              if (*reply == 'y' || *reply == 'Y') {
                        return 0;
                } else if (*reply == 'a' || *reply == 'A') {
                        state->interactive = 0;
@@@ -1801,7 -1802,7 +1801,7 @@@ next
         */
        if (!state->rebasing) {
                am_destroy(state);
 -              close_all_packs(the_repository->objects);
 +              close_object_store(the_repository->objects);
                run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        }
  }
@@@ -1956,7 -1957,7 +1956,7 @@@ static int clean_index(const struct obj
        if (merge_tree(remote_tree))
                return -1;
  
-       remove_branch_state(the_repository);
+       remove_branch_state(the_repository, 0);
  
        return 0;
  }
@@@ -2332,9 -2333,6 +2332,9 @@@ int cmd_am(int argc, const char **argv
                                argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
                }
  
 +              if (state.interactive && !paths.argc)
 +                      die(_("interactive mode requires patches on the command line"));
 +
                am_setup(&state, patch_format, paths.argv, keep_cr);
  
                argv_array_clear(&paths);
diff --combined builtin/checkout.c
index ffa776c6e10c9665489bd4ca6eac1d35efb011c8,f884d27f1f591018937638251278b9f41304793f..91f8509f85396cb957ee87d1b0e8ba1284c8c800
@@@ -1,32 -1,31 +1,31 @@@
  #define USE_THE_INDEX_COMPATIBILITY_MACROS
  #include "builtin.h"
- #include "config.h"
+ #include "advice.h"
+ #include "blob.h"
+ #include "branch.h"
+ #include "cache-tree.h"
  #include "checkout.h"
+ #include "commit.h"
+ #include "config.h"
+ #include "diff.h"
+ #include "dir.h"
+ #include "ll-merge.h"
  #include "lockfile.h"
+ #include "merge-recursive.h"
+ #include "object-store.h"
  #include "parse-options.h"
  #include "refs.h"
- #include "object-store.h"
- #include "commit.h"
+ #include "remote.h"
+ #include "resolve-undo.h"
+ #include "revision.h"
+ #include "run-command.h"
+ #include "submodule.h"
+ #include "submodule-config.h"
  #include "tree.h"
  #include "tree-walk.h"
- #include "cache-tree.h"
  #include "unpack-trees.h"
- #include "dir.h"
- #include "run-command.h"
- #include "merge-recursive.h"
- #include "branch.h"
- #include "diff.h"
- #include "revision.h"
- #include "remote.h"
- #include "blob.h"
+ #include "wt-status.h"
  #include "xdiff-interface.h"
- #include "ll-merge.h"
- #include "resolve-undo.h"
- #include "submodule-config.h"
- #include "submodule.h"
- #include "advice.h"
- static int checkout_optimize_new_branch;
  
  static const char * const checkout_usage[] = {
        N_("git checkout [<options>] <branch>"),
        NULL,
  };
  
+ static const char * const switch_branch_usage[] = {
+       N_("git switch [<options>] [<branch>]"),
+       NULL,
+ };
+ static const char * const restore_usage[] = {
+       N_("git restore [<options>] [--source=<branch>] <file>..."),
+       NULL,
+ };
  struct checkout_opts {
        int patch_mode;
        int quiet;
        int merge;
        int force;
        int force_detach;
+       int implicit_detach;
        int writeout_stage;
        int overwrite_ignore;
        int ignore_skipworktree;
        int show_progress;
        int count_checkout_paths;
        int overlay_mode;
-       /*
-        * If new checkout options are added, skip_merge_working_tree
-        * should be updated accordingly.
-        */
+       int dwim_new_local_branch;
+       int discard_changes;
+       int accept_ref;
+       int accept_pathspec;
+       int switch_branch_doing_nothing_is_ok;
+       int only_merge_on_switching_branches;
+       int can_switch_when_in_progress;
+       int orphan_from_empty_tree;
+       int empty_pathspec_ok;
+       int checkout_index;
+       int checkout_worktree;
+       const char *ignore_unmerged_opt;
+       int ignore_unmerged;
  
        const char *new_branch;
        const char *new_branch_force;
        int new_branch_log;
        enum branch_track track;
        struct diff_options diff_options;
+       char *conflict_style;
  
        int branch_exists;
        const char *prefix;
        struct pathspec pathspec;
+       const char *from_treeish;
        struct tree *source_tree;
  };
  
@@@ -313,17 -334,74 +334,74 @@@ static void mark_ce_for_checkout_no_ove
        }
  }
  
+ static int checkout_worktree(const struct checkout_opts *opts)
+ {
+       struct checkout state = CHECKOUT_INIT;
+       int nr_checkouts = 0, nr_unmerged = 0;
+       int errs = 0;
+       int pos;
+       state.force = 1;
+       state.refresh_cache = 1;
+       state.istate = &the_index;
+       enable_delayed_checkout(&state);
+       for (pos = 0; pos < active_nr; pos++) {
+               struct cache_entry *ce = active_cache[pos];
+               if (ce->ce_flags & CE_MATCHED) {
+                       if (!ce_stage(ce)) {
+                               errs |= checkout_entry(ce, &state,
+                                                      NULL, &nr_checkouts);
+                               continue;
+                       }
+                       if (opts->writeout_stage)
+                               errs |= checkout_stage(opts->writeout_stage,
+                                                      ce, pos,
+                                                      &state,
+                                                      &nr_checkouts, opts->overlay_mode);
+                       else if (opts->merge)
+                               errs |= checkout_merged(pos, &state,
+                                                       &nr_unmerged);
+                       pos = skip_same_name(ce, pos) - 1;
+               }
+       }
+       remove_marked_cache_entries(&the_index, 1);
+       remove_scheduled_dirs();
+       errs |= finish_delayed_checkout(&state, &nr_checkouts);
+       if (opts->count_checkout_paths) {
+               if (nr_unmerged)
+                       fprintf_ln(stderr, Q_("Recreated %d merge conflict",
+                                             "Recreated %d merge conflicts",
+                                             nr_unmerged),
+                                  nr_unmerged);
+               if (opts->source_tree)
+                       fprintf_ln(stderr, Q_("Updated %d path from %s",
+                                             "Updated %d paths from %s",
+                                             nr_checkouts),
+                                  nr_checkouts,
+                                  find_unique_abbrev(&opts->source_tree->object.oid,
+                                                     DEFAULT_ABBREV));
+               else if (!nr_unmerged || nr_checkouts)
+                       fprintf_ln(stderr, Q_("Updated %d path from the index",
+                                             "Updated %d paths from the index",
+                                             nr_checkouts),
+                                  nr_checkouts);
+       }
+       return errs;
+ }
  static int checkout_paths(const struct checkout_opts *opts,
                          const char *revision)
  {
        int pos;
-       struct checkout state = CHECKOUT_INIT;
        static char *ps_matched;
        struct object_id rev;
        struct commit *head;
        int errs = 0;
        struct lock_file lock_file = LOCK_INIT;
-       int nr_checkouts = 0, nr_unmerged = 0;
+       int checkout_index;
  
        trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
  
        if (opts->new_branch_log)
                die(_("'%s' cannot be used with updating paths"), "-l");
  
-       if (opts->force && opts->patch_mode)
-               die(_("'%s' cannot be used with updating paths"), "-f");
+       if (opts->ignore_unmerged && opts->patch_mode)
+               die(_("'%s' cannot be used with updating paths"),
+                   opts->ignore_unmerged_opt);
  
        if (opts->force_detach)
                die(_("'%s' cannot be used with updating paths"), "--detach");
        if (opts->merge && opts->patch_mode)
                die(_("'%s' cannot be used with %s"), "--merge", "--patch");
  
-       if (opts->force && opts->merge)
-               die(_("'%s' cannot be used with %s"), "-f", "-m");
+       if (opts->ignore_unmerged && opts->merge)
+               die(_("'%s' cannot be used with %s"),
+                   opts->ignore_unmerged_opt, "-m");
  
        if (opts->new_branch)
                die(_("Cannot update paths and switch to branch '%s' at the same time."),
                    opts->new_branch);
  
-       if (opts->patch_mode)
-               return run_add_interactive(revision, "--patch=checkout",
-                                          &opts->pathspec);
+       if (!opts->checkout_worktree && !opts->checkout_index)
+               die(_("neither '%s' or '%s' is specified"),
+                   "--staged", "--worktree");
+       if (!opts->checkout_worktree && !opts->from_treeish)
+               die(_("'%s' must be used when '%s' is not specified"),
+                   "--worktree", "--source");
+       if (opts->checkout_index && !opts->checkout_worktree &&
+           opts->writeout_stage)
+               die(_("'%s' or '%s' cannot be used with %s"),
+                   "--ours", "--theirs", "--staged");
+       if (opts->checkout_index && !opts->checkout_worktree &&
+           opts->merge)
+               die(_("'%s' or '%s' cannot be used with %s"),
+                   "--merge", "--conflict", "--staged");
+       if (opts->patch_mode) {
+               const char *patch_mode;
+               if (opts->checkout_index && opts->checkout_worktree)
+                       patch_mode = "--patch=checkout";
+               else if (opts->checkout_index && !opts->checkout_worktree)
+                       patch_mode = "--patch=reset";
+               else if (!opts->checkout_index && opts->checkout_worktree)
+                       patch_mode = "--patch=worktree";
+               else
+                       BUG("either flag must have been set, worktree=%d, index=%d",
+                           opts->checkout_worktree, opts->checkout_index);
+               return run_add_interactive(revision, patch_mode, &opts->pathspec);
+       }
  
        repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(&opts->pathspec) < 0)
                                                        ps_matched,
                                                        opts);
  
 -      if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
 +      if (report_path_error(ps_matched, &opts->pathspec)) {
                free(ps_matched);
                return 1;
        }
                if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce))
                                continue;
-                       if (opts->force) {
-                               warning(_("path '%s' is unmerged"), ce->name);
+                       if (opts->ignore_unmerged) {
+                               if (!opts->quiet)
+                                       warning(_("path '%s' is unmerged"), ce->name);
                        } else if (opts->writeout_stage) {
                                errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
                        } else if (opts->merge) {
                return 1;
  
        /* Now we are committed to check them out */
-       state.force = 1;
-       state.refresh_cache = 1;
-       state.istate = &the_index;
+       if (opts->checkout_worktree)
+               errs |= checkout_worktree(opts);
  
-       enable_delayed_checkout(&state);
-       for (pos = 0; pos < active_nr; pos++) {
-               struct cache_entry *ce = active_cache[pos];
-               if (ce->ce_flags & CE_MATCHED) {
-                       if (!ce_stage(ce)) {
-                               errs |= checkout_entry(ce, &state,
-                                                      NULL, &nr_checkouts);
-                               continue;
-                       }
-                       if (opts->writeout_stage)
-                               errs |= checkout_stage(opts->writeout_stage,
-                                                      ce, pos,
-                                                      &state,
-                                                      &nr_checkouts, opts->overlay_mode);
-                       else if (opts->merge)
-                               errs |= checkout_merged(pos, &state,
-                                                       &nr_unmerged);
-                       pos = skip_same_name(ce, pos) - 1;
-               }
-       }
-       remove_marked_cache_entries(&the_index, 1);
-       remove_scheduled_dirs();
-       errs |= finish_delayed_checkout(&state, &nr_checkouts);
+       /*
+        * Allow updating the index when checking out from the index.
+        * This is to save new stat info.
+        */
+       if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)
+               checkout_index = 1;
+       else
+               checkout_index = opts->checkout_index;
  
-       if (opts->count_checkout_paths) {
-               if (nr_unmerged)
-                       fprintf_ln(stderr, Q_("Recreated %d merge conflict",
-                                             "Recreated %d merge conflicts",
-                                             nr_unmerged),
-                                  nr_unmerged);
-               if (opts->source_tree)
-                       fprintf_ln(stderr, Q_("Updated %d path from %s",
-                                             "Updated %d paths from %s",
-                                             nr_checkouts),
-                                  nr_checkouts,
-                                  find_unique_abbrev(&opts->source_tree->object.oid,
-                                                     DEFAULT_ABBREV));
-               else if (!nr_unmerged || nr_checkouts)
-                       fprintf_ln(stderr, Q_("Updated %d path from the index",
-                                             "Updated %d paths from the index",
-                                             nr_checkouts),
-                                  nr_checkouts);
+       if (checkout_index) {
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+                       die(_("unable to write new index file"));
+       } else {
+               /*
+                * NEEDSWORK: if --worktree is not specified, we
+                * should save stat info of checked out files in the
+                * index to avoid the next (potentially costly)
+                * refresh. But it's a bit tricker to do...
+                */
+               rollback_lock_file(&lock_file);
        }
  
-       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
-               die(_("unable to write new index file"));
        read_ref_full("HEAD", 0, &rev, NULL);
        head = lookup_commit_reference_gently(the_repository, &rev, 1);
  
@@@ -553,112 -637,6 +637,6 @@@ static void setup_branch_path(struct br
        branch->path = strbuf_detach(&buf, NULL);
  }
  
- /*
-  * Skip merging the trees, updating the index and working directory if and
-  * only if we are creating a new branch via "git checkout -b <new_branch>."
-  */
- static int skip_merge_working_tree(const struct checkout_opts *opts,
-       const struct branch_info *old_branch_info,
-       const struct branch_info *new_branch_info)
- {
-       /*
-        * Do the merge if sparse checkout is on and the user has not opted in
-        * to the optimized behavior
-        */
-       if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
-               return 0;
-       /*
-        * We must do the merge if we are actually moving to a new commit.
-        */
-       if (!old_branch_info->commit || !new_branch_info->commit ||
-               !oideq(&old_branch_info->commit->object.oid,
-                      &new_branch_info->commit->object.oid))
-               return 0;
-       /*
-        * opts->patch_mode cannot be used with switching branches so is
-        * not tested here
-        */
-       /*
-        * opts->quiet only impacts output so doesn't require a merge
-        */
-       /*
-        * Honor the explicit request for a three-way merge or to throw away
-        * local changes
-        */
-       if (opts->merge || opts->force)
-               return 0;
-       /*
-        * --detach is documented as "updating the index and the files in the
-        * working tree" but this optimization skips those steps so fall through
-        * to the regular code path.
-        */
-       if (opts->force_detach)
-               return 0;
-       /*
-        * opts->writeout_stage cannot be used with switching branches so is
-        * not tested here
-        */
-       /*
-        * Honor the explicit ignore requests
-        */
-       if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
-               opts->ignore_other_worktrees)
-               return 0;
-       /*
-        * opts->show_progress only impacts output so doesn't require a merge
-        */
-       /*
-        * opts->overlay_mode cannot be used with switching branches so is
-        * not tested here
-        */
-       /*
-        * If we aren't creating a new branch any changes or updates will
-        * happen in the existing branch.  Since that could only be updating
-        * the index and working directory, we don't want to skip those steps
-        * or we've defeated any purpose in running the command.
-        */
-       if (!opts->new_branch)
-               return 0;
-       /*
-        * new_branch_force is defined to "create/reset and checkout a branch"
-        * so needs to go through the merge to do the reset
-        */
-       if (opts->new_branch_force)
-               return 0;
-       /*
-        * A new orphaned branch requrires the index and the working tree to be
-        * adjusted to <start_point>
-        */
-       if (opts->new_orphan_branch)
-               return 0;
-       /*
-        * Remaining variables are not checkout options but used to track state
-        */
-        /*
-         * Do the merge if this is the initial checkout. We cannot use
-         * is_cache_unborn() here because the index hasn't been loaded yet
-         * so cache_nr and timestamp.sec are always zero.
-         */
-       if (!file_exists(get_index_file()))
-               return 0;
-       return 1;
- }
  static int merge_working_tree(const struct checkout_opts *opts,
                              struct branch_info *old_branch_info,
                              struct branch_info *new_branch_info,
  {
        int ret;
        struct lock_file lock_file = LOCK_INIT;
+       struct tree *new_tree;
  
        hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(NULL) < 0)
                return error(_("index file corrupt"));
  
        resolve_undo_clear();
-       if (opts->force) {
-               ret = reset_tree(get_commit_tree(new_branch_info->commit),
-                                opts, 1, writeout_error);
+       if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+               if (new_branch_info->commit)
+                       BUG("'switch --orphan' should never accept a commit as starting point");
+               new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
+       } else
+               new_tree = get_commit_tree(new_branch_info->commit);
+       if (opts->discard_changes) {
+               ret = reset_tree(new_tree, opts, 1, writeout_error);
                if (ret)
                        return ret;
        } else {
                topts.initial_checkout = is_cache_unborn();
                topts.update = 1;
                topts.merge = 1;
 -              topts.gently = opts->merge && old_branch_info->commit;
 +              topts.quiet = opts->merge && old_branch_info->commit;
                topts.verbose_update = opts->show_progress;
                topts.fn = twoway_merge;
                if (opts->overwrite_ignore) {
                                           &old_branch_info->commit->object.oid :
                                           the_hash_algo->empty_tree);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
-               tree = parse_tree_indirect(&new_branch_info->commit->object.oid);
+               parse_tree(new_tree);
+               tree = new_tree;
                init_tree_desc(&trees[1], tree->buffer, tree->size);
  
                ret = unpack_trees(2, trees, &topts);
                         */
                        struct tree *result;
                        struct tree *work;
 +                      struct tree *old_tree;
                        struct merge_options o;
 +                      struct strbuf sb = STRBUF_INIT;
 +
                        if (!opts->merge)
                                return 1;
  
                         */
                        if (!old_branch_info->commit)
                                return 1;
 +                      old_tree = get_commit_tree(old_branch_info->commit);
 +
 +                      if (repo_index_has_changes(the_repository, old_tree, &sb))
 +                              die(_("cannot continue with staged changes in "
 +                                    "the following files:\n%s"), sb.buf);
 +                      strbuf_release(&sb);
 +
 +                      if (repo_index_has_changes(the_repository,
 +                                                 get_commit_tree(old_branch_info->commit),
 +                                                 &sb))
 +                              warning(_("staged changes in the following files may be lost: %s"),
 +                                      sb.buf);
 +                      strbuf_release(&sb);
  
                        /* Do more real merge */
  
                        o.verbosity = 0;
                        work = write_tree_from_memory(&o);
  
-                       ret = reset_tree(get_commit_tree(new_branch_info->commit),
+                       ret = reset_tree(new_tree,
                                         opts, 1,
                                         writeout_error);
                        if (ret)
                        o.branch1 = new_branch_info->name;
                        o.branch2 = "local";
                        ret = merge_trees(&o,
-                                         get_commit_tree(new_branch_info->commit),
+                                         new_tree,
                                          work,
 -                                        get_commit_tree(old_branch_info->commit),
 +                                        old_tree,
                                          &result);
                        if (ret < 0)
                                exit(128);
-                       ret = reset_tree(get_commit_tree(new_branch_info->commit),
+                       ret = reset_tree(new_tree,
                                         opts, 0,
                                         writeout_error);
                        strbuf_release(&o.obuf);
        if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
-       if (!opts->force && !opts->quiet)
+       if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
                show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
  
        return 0;
@@@ -915,7 -884,7 +900,7 @@@ static void update_refs_for_switch(cons
                                delete_reflog(old_branch_info->path);
                }
        }
-       remove_branch_state(the_repository);
+       remove_branch_state(the_repository, !opts->quiet);
        strbuf_release(&msg);
        if (!opts->quiet &&
            (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
@@@ -1011,7 -980,10 +996,10 @@@ static void orphaned_commit_warning(str
        add_pending_object(&revs, object, oid_to_hex(&object->oid));
  
        for_each_ref(add_pending_uninteresting_ref, &revs);
-       add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
+       if (new_commit)
+               add_pending_oid(&revs, "HEAD",
+                               &new_commit->object.oid,
+                               UNINTERESTING);
  
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
@@@ -1032,6 -1004,7 +1020,7 @@@ static int switch_branches(const struc
        void *path_to_free;
        struct object_id rev;
        int flag, writeout_error = 0;
+       int do_merge = 1;
  
        trace2_cmd_mode("branch");
  
        if (old_branch_info.path)
                skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
  
+       if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+               if (new_branch_info->name)
+                       BUG("'switch --orphan' should never accept a commit as starting point");
+               new_branch_info->commit = NULL;
+               new_branch_info->name = "(empty)";
+               do_merge = 1;
+       }
        if (!new_branch_info->name) {
                new_branch_info->name = "HEAD";
                new_branch_info->commit = old_branch_info.commit;
                if (!new_branch_info->commit)
                        die(_("You are on a branch yet to be born"));
                parse_commit_or_die(new_branch_info->commit);
+               if (opts->only_merge_on_switching_branches)
+                       do_merge = 0;
        }
  
-       /* optimize the "checkout -b <new_branch> path */
-       if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
-               if (!checkout_optimize_new_branch && !opts->quiet) {
-                       if (read_cache_preload(NULL) < 0)
-                               return error(_("index file corrupt"));
-                       show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
-               }
-       } else {
+       if (do_merge) {
                ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
                if (ret) {
                        free(path_to_free);
  
  static int git_checkout_config(const char *var, const char *value, void *cb)
  {
-       if (!strcmp(var, "checkout.optimizenewbranch")) {
-               checkout_optimize_new_branch = git_config_bool(var, value);
-               return 0;
-       }
        if (!strcmp(var, "diff.ignoresubmodules")) {
                struct checkout_opts *opts = cb;
                handle_ignore_submodules_arg(&opts->diff_options, value);
        return git_xmerge_config(var, value, NULL);
  }
  
+ static void setup_new_branch_info_and_source_tree(
+       struct branch_info *new_branch_info,
+       struct checkout_opts *opts,
+       struct object_id *rev,
+       const char *arg)
+ {
+       struct tree **source_tree = &opts->source_tree;
+       struct object_id branch_rev;
+       new_branch_info->name = arg;
+       setup_branch_path(new_branch_info);
+       if (!check_refname_format(new_branch_info->path, 0) &&
+           !read_ref(new_branch_info->path, &branch_rev))
+               oidcpy(rev, &branch_rev);
+       else
+               new_branch_info->path = NULL; /* not an existing branch */
+       new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+       if (!new_branch_info->commit) {
+               /* not a commit */
+               *source_tree = parse_tree_indirect(rev);
+       } else {
+               parse_commit_or_die(new_branch_info->commit);
+               *source_tree = get_commit_tree(new_branch_info->commit);
+       }
+ }
  static int parse_branchname_arg(int argc, const char **argv,
                                int dwim_new_local_branch_ok,
                                struct branch_info *new_branch_info,
                                struct object_id *rev,
                                int *dwim_remotes_matched)
  {
-       struct tree **source_tree = &opts->source_tree;
        const char **new_branch = &opts->new_branch;
        int argcount = 0;
-       struct object_id branch_rev;
        const char *arg;
        int dash_dash_pos;
        int has_dash_dash = 0;
        if (!argc)
                return 0;
  
+       if (!opts->accept_pathspec) {
+               if (argc > 1)
+                       die(_("only one reference expected"));
+               has_dash_dash = 1; /* helps disambiguate */
+       }
        arg = argv[0];
        dash_dash_pos = -1;
        for (i = 0; i < argc; i++) {
-               if (!strcmp(argv[i], "--")) {
+               if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
                        dash_dash_pos = i;
                        break;
                }
                        recover_with_dwim = 0;
  
                /*
-                * Accept "git checkout foo" and "git checkout foo --"
-                * as candidates for dwim.
+                * Accept "git checkout foo", "git checkout foo --"
+                * and "git switch foo" as candidates for dwim.
                 */
                if (!(argc == 1 && !has_dash_dash) &&
-                   !(argc == 2 && has_dash_dash))
+                   !(argc == 2 && has_dash_dash) &&
+                   opts->accept_pathspec)
                        recover_with_dwim = 0;
  
                if (recover_with_dwim) {
        argv++;
        argc--;
  
-       new_branch_info->name = arg;
-       setup_branch_path(new_branch_info);
-       if (!check_refname_format(new_branch_info->path, 0) &&
-           !read_ref(new_branch_info->path, &branch_rev))
-               oidcpy(rev, &branch_rev);
-       else
-               new_branch_info->path = NULL; /* not an existing branch */
-       new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
-       if (!new_branch_info->commit) {
-               /* not a commit */
-               *source_tree = parse_tree_indirect(rev);
-       } else {
-               parse_commit_or_die(new_branch_info->commit);
-               *source_tree = get_commit_tree(new_branch_info->commit);
-       }
+       setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
  
-       if (!*source_tree)                   /* case (1): want a tree */
+       if (!opts->source_tree)                   /* case (1): want a tree */
                die(_("reference is not a tree: %s"), arg);
        if (!has_dash_dash) {   /* case (3).(d) -> (1) */
                /*
                 * Do not complain the most common case
                 */
                if (argc)
                        verify_non_filename(opts->prefix, arg);
-       } else {
+       } else if (opts->accept_pathspec) {
                argcount++;
                argv++;
                argc--;
@@@ -1285,6 -1275,60 +1291,60 @@@ static int switch_unborn_to_new_branch(
        return status;
  }
  
+ static void die_expecting_a_branch(const struct branch_info *branch_info)
+ {
+       struct object_id oid;
+       char *to_free;
+       if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
+               const char *ref = to_free;
+               if (skip_prefix(ref, "refs/tags/", &ref))
+                       die(_("a branch is expected, got tag '%s'"), ref);
+               if (skip_prefix(ref, "refs/remotes/", &ref))
+                       die(_("a branch is expected, got remote branch '%s'"), ref);
+               die(_("a branch is expected, got '%s'"), ref);
+       }
+       if (branch_info->commit)
+               die(_("a branch is expected, got commit '%s'"), branch_info->name);
+       /*
+        * This case should never happen because we already die() on
+        * non-commit, but just in case.
+        */
+       die(_("a branch is expected, got '%s'"), branch_info->name);
+ }
+ static void die_if_some_operation_in_progress(void)
+ {
+       struct wt_status_state state;
+       memset(&state, 0, sizeof(state));
+       wt_status_get_state(the_repository, &state, 0);
+       if (state.merge_in_progress)
+               die(_("cannot switch branch while merging\n"
+                     "Consider \"git merge --quit\" "
+                     "or \"git worktree add\"."));
+       if (state.am_in_progress)
+               die(_("cannot switch branch in the middle of an am session\n"
+                     "Consider \"git am --quit\" "
+                     "or \"git worktree add\"."));
+       if (state.rebase_interactive_in_progress || state.rebase_in_progress)
+               die(_("cannot switch branch while rebasing\n"
+                     "Consider \"git rebase --quit\" "
+                     "or \"git worktree add\"."));
+       if (state.cherry_pick_in_progress)
+               die(_("cannot switch branch while cherry-picking\n"
+                     "Consider \"git cherry-pick --quit\" "
+                     "or \"git worktree add\"."));
+       if (state.revert_in_progress)
+               die(_("cannot switch branch while reverting\n"
+                     "Consider \"git revert --quit\" "
+                     "or \"git worktree add\"."));
+       if (state.bisect_in_progress)
+               warning(_("you are switching branch while bisecting"));
+ }
  static int checkout_branch(struct checkout_opts *opts,
                           struct branch_info *new_branch_info)
  {
                die(_("'%s' cannot be used with switching branches"),
                    "--patch");
  
-       if (!opts->overlay_mode)
+       if (opts->overlay_mode != -1)
                die(_("'%s' cannot be used with switching branches"),
-                   "--no-overlay");
+                   "--[no]-overlay");
  
        if (opts->writeout_stage)
                die(_("'%s' cannot be used with switching branches"),
        if (opts->force && opts->merge)
                die(_("'%s' cannot be used with '%s'"), "-f", "-m");
  
+       if (opts->discard_changes && opts->merge)
+               die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge");
        if (opts->force_detach && opts->new_branch)
                die(_("'%s' cannot be used with '%s'"),
                    "--detach", "-b/-B/--orphan");
        if (opts->new_orphan_branch) {
                if (opts->track != BRANCH_TRACK_UNSPECIFIED)
                        die(_("'%s' cannot be used with '%s'"), "--orphan", "-t");
+               if (opts->orphan_from_empty_tree && new_branch_info->name)
+                       die(_("'%s' cannot take <start-point>"), "--orphan");
        } else if (opts->force_detach) {
                if (opts->track != BRANCH_TRACK_UNSPECIFIED)
                        die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
                die(_("Cannot switch branch to a non-commit '%s'"),
                    new_branch_info->name);
  
+       if (!opts->switch_branch_doing_nothing_is_ok &&
+           !new_branch_info->name &&
+           !opts->new_branch &&
+           !opts->force_detach)
+               die(_("missing branch or commit argument"));
+       if (!opts->implicit_detach &&
+           !opts->force_detach &&
+           !opts->new_branch &&
+           !opts->new_branch_force &&
+           new_branch_info->name &&
+           !new_branch_info->path)
+               die_expecting_a_branch(new_branch_info);
+       if (!opts->can_switch_when_in_progress)
+               die_if_some_operation_in_progress();
        if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
            !opts->ignore_other_worktrees) {
                int flag;
        return switch_branches(opts, new_branch_info);
  }
  
- int cmd_checkout(int argc, const char **argv, const char *prefix)
+ static struct option *add_common_options(struct checkout_opts *opts,
+                                        struct option *prevopts)
  {
-       struct checkout_opts opts;
-       struct branch_info new_branch_info;
-       char *conflict_style = NULL;
-       int dwim_new_local_branch, no_dwim_new_local_branch = 0;
-       int dwim_remotes_matched = 0;
        struct option options[] = {
-               OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
-               OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
-                          N_("create and checkout a new branch")),
-               OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
-                          N_("create/reset and checkout a branch")),
-               OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-               OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
-               OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
+               OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
+               { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+                           "checkout", "control recursive updating of submodules",
+                           PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+               OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+               OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+               OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+                          N_("conflict style (merge or diff3)")),
+               OPT_END()
+       };
+       struct option *newopts = parse_options_concat(prevopts, options);
+       free(prevopts);
+       return newopts;
+ }
+ static struct option *add_common_switch_branch_options(
+       struct checkout_opts *opts, struct option *prevopts)
+ {
+       struct option options[] = {
+               OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+               OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
                        BRANCH_TRACK_EXPLICIT),
-               OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-               OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+               OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+                          PARSE_OPT_NOCOMPLETE),
+               OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+               OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+                          N_("update ignored files (default)"),
+                          PARSE_OPT_NOCOMPLETE),
+               OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+                        N_("do not check if another worktree is holding the given ref")),
+               OPT_END()
+       };
+       struct option *newopts = parse_options_concat(prevopts, options);
+       free(prevopts);
+       return newopts;
+ }
+ static struct option *add_checkout_path_options(struct checkout_opts *opts,
+                                               struct option *prevopts)
+ {
+       struct option options[] = {
+               OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
                              N_("checkout our version for unmerged files"),
                              2, PARSE_OPT_NONEG),
-               OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+               OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
                              N_("checkout their version for unmerged files"),
                              3, PARSE_OPT_NONEG),
-               OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
-                          PARSE_OPT_NOCOMPLETE),
-               OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-               OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
-                          N_("update ignored files (default)"),
-                          PARSE_OPT_NOCOMPLETE),
-               OPT_STRING(0, "conflict", &conflict_style, N_("style"),
-                          N_("conflict style (merge or diff3)")),
-               OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
-               OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+               OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+               OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
                         N_("do not limit pathspecs to sparse entries only")),
-               OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
-                        N_("do not second guess 'git checkout <no-such-branch>'")),
-               OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
-                        N_("do not check if another worktree is holding the given ref")),
-               { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
-                           "checkout", "control recursive updating of submodules",
-                           PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
-               OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
-               OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
-               OPT_END(),
+               OPT_END()
        };
+       struct option *newopts = parse_options_concat(prevopts, options);
+       free(prevopts);
+       return newopts;
+ }
+ static int checkout_main(int argc, const char **argv, const char *prefix,
+                        struct checkout_opts *opts, struct option *options,
+                        const char * const usagestr[])
+ {
+       struct branch_info new_branch_info;
+       int dwim_remotes_matched = 0;
+       int parseopt_flags = 0;
  
-       memset(&opts, 0, sizeof(opts));
        memset(&new_branch_info, 0, sizeof(new_branch_info));
-       opts.overwrite_ignore = 1;
-       opts.prefix = prefix;
-       opts.show_progress = -1;
-       opts.overlay_mode = -1;
+       opts->overwrite_ignore = 1;
+       opts->prefix = prefix;
+       opts->show_progress = -1;
+       git_config(git_checkout_config, opts);
  
-       git_config(git_checkout_config, &opts);
+       opts->track = BRANCH_TRACK_UNSPECIFIED;
  
-       opts.track = BRANCH_TRACK_UNSPECIFIED;
+       if (!opts->accept_pathspec && !opts->accept_ref)
+               BUG("make up your mind, you need to take _something_");
+       if (opts->accept_pathspec && opts->accept_ref)
+               parseopt_flags = PARSE_OPT_KEEP_DASHDASH;
  
-       argc = parse_options(argc, argv, prefix, options, checkout_usage,
-                            PARSE_OPT_KEEP_DASHDASH);
+       argc = parse_options(argc, argv, prefix, options,
+                            usagestr, parseopt_flags);
  
-       dwim_new_local_branch = !no_dwim_new_local_branch;
-       if (opts.show_progress < 0) {
-               if (opts.quiet)
-                       opts.show_progress = 0;
+       if (opts->show_progress < 0) {
+               if (opts->quiet)
+                       opts->show_progress = 0;
                else
-                       opts.show_progress = isatty(2);
+                       opts->show_progress = isatty(2);
        }
  
-       if (conflict_style) {
-               opts.merge = 1; /* implied */
-               git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+       if (opts->conflict_style) {
+               opts->merge = 1; /* implied */
+               git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
+       }
+       if (opts->force) {
+               opts->discard_changes = 1;
+               opts->ignore_unmerged_opt = "--force";
+               opts->ignore_unmerged = 1;
        }
  
-       if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+       if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
                die(_("-b, -B and --orphan are mutually exclusive"));
  
-       if (opts.overlay_mode == 1 && opts.patch_mode)
+       if (opts->overlay_mode == 1 && opts->patch_mode)
                die(_("-p and --overlay are mutually exclusive"));
  
+       if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
+               if (opts->checkout_index < 0)
+                       opts->checkout_index = 0;
+               if (opts->checkout_worktree < 0)
+                       opts->checkout_worktree = 0;
+       } else {
+               if (opts->checkout_index < 0)
+                       opts->checkout_index = -opts->checkout_index - 1;
+               if (opts->checkout_worktree < 0)
+                       opts->checkout_worktree = -opts->checkout_worktree - 1;
+       }
+       if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
+               BUG("these flags should be non-negative by now");
+       /*
+        * convenient shortcut: "git restore --staged" equals
+        * "git restore --staged --source HEAD"
+        */
+       if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
+               opts->from_treeish = "HEAD";
        /*
         * From here on, new_branch will contain the branch to be checked out,
         * and new_branch_force and new_orphan_branch will tell us which one of
         * -b/-B/--orphan is being used.
         */
-       if (opts.new_branch_force)
-               opts.new_branch = opts.new_branch_force;
+       if (opts->new_branch_force)
+               opts->new_branch = opts->new_branch_force;
  
-       if (opts.new_orphan_branch)
-               opts.new_branch = opts.new_orphan_branch;
+       if (opts->new_orphan_branch)
+               opts->new_branch = opts->new_orphan_branch;
  
        /* --track without -b/-B/--orphan should DWIM */
-       if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+       if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
                const char *argv0 = argv[0];
                if (!argc || !strcmp(argv0, "--"))
                        die(_("--track needs a branch name"));
                argv0 = strchr(argv0, '/');
                if (!argv0 || !argv0[1])
                        die(_("missing branch name; try -b"));
-               opts.new_branch = argv0 + 1;
+               opts->new_branch = argv0 + 1;
        }
  
        /*
         * including "last branch" syntax and DWIM-ery for names of
         * remote branches, erroring out for invalid or ambiguous cases.
         */
-       if (argc) {
+       if (argc && opts->accept_ref) {
                struct object_id rev;
                int dwim_ok =
-                       !opts.patch_mode &&
-                       dwim_new_local_branch &&
-                       opts.track == BRANCH_TRACK_UNSPECIFIED &&
-                       !opts.new_branch;
+                       !opts->patch_mode &&
+                       opts->dwim_new_local_branch &&
+                       opts->track == BRANCH_TRACK_UNSPECIFIED &&
+                       !opts->new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
-                                            &new_branch_info, &opts, &rev,
+                                            &new_branch_info, opts, &rev,
                                             &dwim_remotes_matched);
                argv += n;
                argc -= n;
+       } else if (!opts->accept_ref && opts->from_treeish) {
+               struct object_id rev;
+               if (get_oid_mb(opts->from_treeish, &rev))
+                       die(_("could not resolve %s"), opts->from_treeish);
+               setup_new_branch_info_and_source_tree(&new_branch_info,
+                                                     opts, &rev,
+                                                     opts->from_treeish);
+               if (!opts->source_tree)
+                       die(_("reference is not a tree: %s"), opts->from_treeish);
        }
  
+       if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
+           !opts->patch_mode)  /* patch mode is special */
+               die(_("you must specify path(s) to restore"));
        if (argc) {
-               parse_pathspec(&opts.pathspec, 0,
-                              opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+               parse_pathspec(&opts->pathspec, 0,
+                              opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
                               prefix, argv);
  
-               if (!opts.pathspec.nr)
+               if (!opts->pathspec.nr)
                        die(_("invalid path specification"));
  
                /*
                 * Try to give more helpful suggestion.
                 * new_branch && argc > 1 will be caught later.
                 */
-               if (opts.new_branch && argc == 1)
+               if (opts->new_branch && argc == 1)
                        die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
-                               argv[0], opts.new_branch);
+                               argv[0], opts->new_branch);
  
-               if (opts.force_detach)
+               if (opts->force_detach)
                        die(_("git checkout: --detach does not take a path argument '%s'"),
                            argv[0]);
  
-               if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+               if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
                        die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
                              "checking out of the index."));
        }
  
-       if (opts.new_branch) {
+       if (opts->new_branch) {
                struct strbuf buf = STRBUF_INIT;
  
-               if (opts.new_branch_force)
-                       opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+               if (opts->new_branch_force)
+                       opts->branch_exists = validate_branchname(opts->new_branch, &buf);
                else
-                       opts.branch_exists =
-                               validate_new_branchname(opts.new_branch, &buf, 0);
+                       opts->branch_exists =
+                               validate_new_branchname(opts->new_branch, &buf, 0);
                strbuf_release(&buf);
        }
  
        UNLEAK(opts);
-       if (opts.patch_mode || opts.pathspec.nr) {
-               int ret = checkout_paths(&opts, new_branch_info.name);
+       if (opts->patch_mode || opts->pathspec.nr) {
+               int ret = checkout_paths(opts, new_branch_info.name);
                if (ret && dwim_remotes_matched > 1 &&
                    advice_checkout_ambiguous_remote_branch_name)
                        advise(_("'%s' matched more than one remote tracking branch.\n"
                               dwim_remotes_matched);
                return ret;
        } else {
-               return checkout_branch(&opts, &new_branch_info);
+               return checkout_branch(opts, &new_branch_info);
        }
  }
+ int cmd_checkout(int argc, const char **argv, const char *prefix)
+ {
+       struct checkout_opts opts;
+       struct option *options;
+       struct option checkout_options[] = {
+               OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+                          N_("create and checkout a new branch")),
+               OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+                          N_("create/reset and checkout a branch")),
+               OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+               OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+                        N_("second guess 'git checkout <no-such-branch>' (default)")),
+               OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
+               OPT_END()
+       };
+       int ret;
+       memset(&opts, 0, sizeof(opts));
+       opts.dwim_new_local_branch = 1;
+       opts.switch_branch_doing_nothing_is_ok = 1;
+       opts.only_merge_on_switching_branches = 0;
+       opts.accept_ref = 1;
+       opts.accept_pathspec = 1;
+       opts.implicit_detach = 1;
+       opts.can_switch_when_in_progress = 1;
+       opts.orphan_from_empty_tree = 0;
+       opts.empty_pathspec_ok = 1;
+       opts.overlay_mode = -1;
+       opts.checkout_index = -2;    /* default on */
+       opts.checkout_worktree = -2; /* default on */
+       options = parse_options_dup(checkout_options);
+       options = add_common_options(&opts, options);
+       options = add_common_switch_branch_options(&opts, options);
+       options = add_checkout_path_options(&opts, options);
+       ret = checkout_main(argc, argv, prefix, &opts,
+                           options, checkout_usage);
+       FREE_AND_NULL(options);
+       return ret;
+ }
+ int cmd_switch(int argc, const char **argv, const char *prefix)
+ {
+       struct checkout_opts opts;
+       struct option *options = NULL;
+       struct option switch_options[] = {
+               OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+                          N_("create and switch to a new branch")),
+               OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+                          N_("create/reset and switch to a branch")),
+               OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+                        N_("second guess 'git switch <no-such-branch>'")),
+               OPT_BOOL(0, "discard-changes", &opts.discard_changes,
+                        N_("throw away local modifications")),
+               OPT_END()
+       };
+       int ret;
+       memset(&opts, 0, sizeof(opts));
+       opts.dwim_new_local_branch = 1;
+       opts.accept_ref = 1;
+       opts.accept_pathspec = 0;
+       opts.switch_branch_doing_nothing_is_ok = 0;
+       opts.only_merge_on_switching_branches = 1;
+       opts.implicit_detach = 0;
+       opts.can_switch_when_in_progress = 0;
+       opts.orphan_from_empty_tree = 1;
+       opts.overlay_mode = -1;
+       options = parse_options_dup(switch_options);
+       options = add_common_options(&opts, options);
+       options = add_common_switch_branch_options(&opts, options);
+       ret = checkout_main(argc, argv, prefix, &opts,
+                           options, switch_branch_usage);
+       FREE_AND_NULL(options);
+       return ret;
+ }
+ int cmd_restore(int argc, const char **argv, const char *prefix)
+ {
+       struct checkout_opts opts;
+       struct option *options;
+       struct option restore_options[] = {
+               OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
+                          N_("where the checkout from")),
+               OPT_BOOL('S', "staged", &opts.checkout_index,
+                          N_("restore the index")),
+               OPT_BOOL('W', "worktree", &opts.checkout_worktree,
+                          N_("restore the working tree (default)")),
+               OPT_BOOL(0, "ignore-unmerged", &opts.ignore_unmerged,
+                        N_("ignore unmerged entries")),
+               OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
+               OPT_END()
+       };
+       int ret;
+       memset(&opts, 0, sizeof(opts));
+       opts.accept_ref = 0;
+       opts.accept_pathspec = 1;
+       opts.empty_pathspec_ok = 0;
+       opts.overlay_mode = 0;
+       opts.checkout_index = -1;    /* default off */
+       opts.checkout_worktree = -2; /* default on */
+       opts.ignore_unmerged_opt = "--ignore-unmerged";
+       options = parse_options_dup(restore_options);
+       options = add_common_options(&opts, options);
+       options = add_checkout_path_options(&opts, options);
+       ret = checkout_main(argc, argv, prefix, &opts,
+                           options, restore_usage);
+       FREE_AND_NULL(options);
+       return ret;
+ }
diff --combined builtin/clone.c
index 189ea1c2a0d2767534cb244c5780ad4a0d74a814,024eb57996402d8111330be3d210f9c544505a60..a4fe72879d43e4e42d6cbb5a4dada2f7a111b872
@@@ -66,8 -66,6 +66,8 @@@ static int option_dissociate
  static int max_jobs = -1;
  static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
  static struct list_objects_filter_options filter_options;
 +static struct string_list server_options = STRING_LIST_INIT_NODUP;
 +static int option_remote_submodules;
  
  static int recurse_submodules_cb(const struct option *opt,
                                 const char *arg, int unset)
@@@ -100,7 -98,10 +100,7 @@@ static struct option builtin_clone_opti
                    N_("don't use local hardlinks, always copy")),
        OPT_BOOL('s', "shared", &option_shared,
                    N_("setup as shared repository")),
 -      { OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules,
 -        N_("pathspec"), N_("initialize submodules in the clone"),
 -        PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb,
 -        (intptr_t)"." },
 +      OPT_ALIAS(0, "recursive", "recurse-submodules"),
        { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
          N_("pathspec"), N_("initialize submodules in the clone"),
          PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
                   N_("separate git dir from working tree")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
                        N_("set config inside the new repository")),
 +      OPT_STRING_LIST(0, "server-option", &server_options,
 +                      N_("server-specific"), N_("option to transmit")),
        OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
                        TRANSPORT_FAMILY_IPV4),
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
                        TRANSPORT_FAMILY_IPV6),
        OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
 +      OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
 +                  N_("any cloned submodules will use their remote-tracking branch")),
        OPT_END()
  };
  
@@@ -357,7 -354,8 +357,7 @@@ static void setup_reference(void
                             add_one_reference, &required);
  }
  
 -static void copy_alternates(struct strbuf *src, struct strbuf *dst,
 -                          const char *src_repo)
 +static void copy_alternates(struct strbuf *src, const char *src_repo)
  {
        /*
         * Read from the source objects/info/alternates file
@@@ -438,7 -436,7 +438,7 @@@ static void copy_or_link_directory(stru
  
                /* Files that cannot be copied bit-for-bit... */
                if (!strcmp(src->buf + src_baselen, "/info/alternates")) {
 -                      copy_alternates(src, dest, src_repo);
 +                      copy_alternates(src, src_repo);
                        continue;
                }
  
@@@ -494,7 -492,7 +494,7 @@@ static enum 
  static const char junk_leave_repo_msg[] =
  N_("Clone succeeded, but checkout failed.\n"
     "You can inspect what was checked out with 'git status'\n"
-    "and retry the checkout with 'git checkout -f HEAD'\n");
+    "and retry with 'git restore --source=HEAD :/'\n");
  
  static void remove_junk(void)
  {
@@@ -659,8 -657,7 +659,8 @@@ static void update_remote_refs(const st
                               const char *branch_top,
                               const char *msg,
                               struct transport *transport,
 -                             int check_connectivity)
 +                             int check_connectivity,
 +                             int check_refs_only)
  {
        const struct ref *rm = mapped_refs;
  
  
                opt.transport = transport;
                opt.progress = transport->progress;
 +              opt.check_refs_only = !!check_refs_only;
  
                if (check_connected(iterate_ref_map, &rm, &opt))
                        die(_("remote did not send all necessary objects"));
@@@ -793,11 -789,6 +793,11 @@@ static int checkout(int submodule_progr
                if (option_verbosity < 0)
                        argv_array_push(&args, "--quiet");
  
 +              if (option_remote_submodules) {
 +                      argv_array_push(&args, "--remote");
 +                      argv_array_push(&args, "--no-fetch");
 +              }
 +
                err = run_command_v_opt(args.argv, RUN_GIT_CMD);
                argv_array_clear(&args);
        }
@@@ -1145,9 -1136,6 +1145,9 @@@ int cmd_clone(int argc, const char **ar
                transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                     option_upload_pack);
  
 +      if (server_options.nr)
 +              transport->server_options = &server_options;
 +
        if (filter_options.choice) {
                struct strbuf expanded_filter_spec = STRBUF_INIT;
                expand_list_objects_filter_spec(&filter_options,
                        remote_head_points_at, &branch_top);
  
        if (filter_options.choice)
 -              partial_clone_register("origin", &filter_options);
 +              partial_clone_register(option_origin, &filter_options);
  
        if (is_local)
                clone_local(path, git_dir);
  
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
                           branch_top.buf, reflog_msg.buf, transport,
 -                         !is_local);
 +                         !is_local, filter_options.choice);
  
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
  
        transport_disconnect(transport);
  
        if (option_dissociate) {
 -              close_all_packs(the_repository->objects);
 +              close_object_store(the_repository->objects);
                dissociate_from_references();
        }
  
diff --combined builtin/commit.c
index 192140111747cc0683c1ae19a785000110808ff3,fa5982cc861ea19ab40622b916a68ec9b3423162..851b257867f5fecd326b5426936252cee1383352
@@@ -235,7 -235,7 +235,7 @@@ static int commit_index_files(void
   * and return the paths that match the given pattern in list.
   */
  static int list_paths(struct string_list *list, const char *with_tree,
 -                    const char *prefix, const struct pathspec *pattern)
 +                    const struct pathspec *pattern)
  {
        int i, ret;
        char *m;
                        item->util = item; /* better a valid pointer than a fake one */
        }
  
 -      ret = report_path_error(m, pattern, prefix);
 +      ret = report_path_error(m, pattern);
        free(m);
        return ret;
  }
@@@ -454,7 -454,7 +454,7 @@@ static const char *prepare_index(int ar
                        die(_("cannot do a partial commit during a cherry-pick."));
        }
  
 -      if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
 +      if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
                exit(1);
  
        discard_cache();
@@@ -668,7 -668,6 +668,7 @@@ static int prepare_to_commit(const cha
        const char *hook_arg2 = NULL;
        int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
        int old_display_comment_prefix;
 +      int merge_contains_scissors = 0;
  
        /* This checks and barfs if author is badly specified */
        determine_author_info(author_ident);
                        strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
        } else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
 +              size_t merge_msg_start;
 +
                /*
                 * prepend SQUASH_MSG here if it exists and a
                 * "merge --squash" was originally performed
                        hook_arg1 = "squash";
                } else
                        hook_arg1 = "merge";
 +
 +              merge_msg_start = sb.len;
                if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)
                        die_errno(_("could not read MERGE_MSG"));
 +
 +              if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
 +                  wt_status_locate_end(sb.buf + merge_msg_start,
 +                                       sb.len - merge_msg_start) <
 +                              sb.len - merge_msg_start)
 +                      merge_contains_scissors = 1;
        } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
                if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
                        die_errno(_("could not read SQUASH_MSG"));
                struct ident_split ci, ai;
  
                if (whence != FROM_COMMIT) {
 -                      if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 +                      if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
 +                              !merge_contains_scissors)
                                wt_status_add_cut_line(s->fp);
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                            whence == FROM_MERGE
                                _("Please enter the commit message for your changes."
                                  " Lines starting\nwith '%c' will be ignored, and an empty"
                                  " message aborts the commit.\n"), comment_line_char);
 -              else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
 -                       whence == FROM_COMMIT)
 -                      wt_status_add_cut_line(s->fp);
 -              else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
 +              else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
 +                      if (whence == FROM_COMMIT && !merge_contains_scissors)
 +                              wt_status_add_cut_line(s->fp);
 +              else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
                        status_printf(s, GIT_COLOR_NORMAL,
                                _("Please enter the commit message for your changes."
                                  " Lines starting\n"
@@@ -1184,13 -1172,29 +1184,13 @@@ static int parse_and_validate_options(i
                die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
        if (argc == 0 && (also || (only && !amend && !allow_empty)))
                die(_("No paths with --include/--only does not make sense."));
 -      if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
 -              cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
 -                                          COMMIT_MSG_CLEANUP_SPACE;
 -      else if (!strcmp(cleanup_arg, "verbatim"))
 -              cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
 -      else if (!strcmp(cleanup_arg, "whitespace"))
 -              cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
 -      else if (!strcmp(cleanup_arg, "strip"))
 -              cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
 -      else if (!strcmp(cleanup_arg, "scissors"))
 -              cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
 -                                          COMMIT_MSG_CLEANUP_SPACE;
 -      /*
 -       * Please update _git_commit() in git-completion.bash when you
 -       * add new options.
 -       */
 -      else
 -              die(_("Invalid cleanup mode %s"), cleanup_arg);
 +      cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
  
        handle_untracked_files_arg(s);
  
        if (all && argc > 0)
 -              die(_("Paths with -a does not make sense."));
 +              die(_("paths '%s ...' with -a does not make sense"),
 +                  argv[0]);
  
        if (status_format != STATUS_FORMAT_NONE)
                dry_run = 1;
@@@ -1486,7 -1490,7 +1486,7 @@@ int cmd_commit(int argc, const char **a
                OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
                OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
 -              OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
 +              OPT_CLEANUP(&cleanup_arg),
                OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
                { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
                  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                die(_("could not read commit message: %s"), strerror(saved_errno));
        }
  
 -      if (verbose || /* Truncate the message just before the diff, if any. */
 -          cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 -              strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
 -      if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
 -              strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 +      cleanup_message(&sb, cleanup_mode, verbose);
  
        if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
                rollback_index_files();
                die("%s", err.buf);
        }
  
-       sequencer_post_commit_cleanup(the_repository);
 -      unlink(git_path_cherry_pick_head(the_repository));
 -      unlink(git_path_revert_head(the_repository));
++      sequencer_post_commit_cleanup(the_repository, 0);
        unlink(git_path_merge_head(the_repository));
        unlink(git_path_merge_msg(the_repository));
        unlink(git_path_merge_mode(the_repository));
        if (commit_index_files())
                die(_("repository has been updated, but unable to write\n"
                      "new_index file. Check that disk is not full and quota is\n"
-                     "not exceeded, and then \"git reset HEAD\" to recover."));
+                     "not exceeded, and then \"git restore --staged :/\" to recover."));
  
 -      if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
 -              write_commit_graph_reachable(get_object_directory(), 0, 0);
 +      if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
 +          write_commit_graph_reachable(get_object_directory(), 0))
 +              return 1;
  
        repo_rerere(the_repository, 0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
diff --combined builtin/rebase.c
index 789bf0e5f92badf1dadd5327990609bebcc4f6e1,646d0f9fb1c2ef9995b77feb0a8013acde73a37e..3c7d8b894a35157a9e1f72f5437608ec2a07c208
@@@ -25,8 -25,6 +25,8 @@@
  #include "commit-reach.h"
  #include "rerere.h"
  #include "branch.h"
 +#include "sequencer.h"
 +#include "rebase-interactive.h"
  
  static char const * const builtin_rebase_usage[] = {
        N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@@ -37,8 -35,6 +37,8 @@@
        NULL
  };
  
 +static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 +static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
  static GIT_PATH_FUNC(apply_dir, "rebase-apply")
  static GIT_PATH_FUNC(merge_dir, "rebase-merge")
  
@@@ -50,6 -46,29 +50,6 @@@ enum rebase_type 
        REBASE_PRESERVE_MERGES
  };
  
 -static int use_builtin_rebase(void)
 -{
 -      struct child_process cp = CHILD_PROCESS_INIT;
 -      struct strbuf out = STRBUF_INIT;
 -      int ret, env = git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1);
 -
 -      if (env != -1)
 -              return env;
 -
 -      argv_array_pushl(&cp.args,
 -                       "config", "--bool", "rebase.usebuiltin", NULL);
 -      cp.git_cmd = 1;
 -      if (capture_command(&cp, &out, 6)) {
 -              strbuf_release(&out);
 -              return 1;
 -      }
 -
 -      strbuf_trim(&out);
 -      ret = !strcmp("true", out.buf);
 -      strbuf_release(&out);
 -      return ret;
 -}
 -
  struct rebase_options {
        enum rebase_type type;
        const char *state_dir;
        char *strategy, *strategy_opts;
        struct strbuf git_format_patch_opt;
        int reschedule_failed_exec;
 +      int use_legacy_rebase;
  };
  
 +#define REBASE_OPTIONS_INIT {                         \
 +              .type = REBASE_UNSPECIFIED,             \
 +              .flags = REBASE_NO_QUIET,               \
 +              .git_am_opts = ARGV_ARRAY_INIT,         \
 +              .git_format_patch_opt = STRBUF_INIT     \
 +      }
 +
 +static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 +{
 +      struct replay_opts replay = REPLAY_OPTS_INIT;
 +
 +      replay.action = REPLAY_INTERACTIVE_REBASE;
 +      sequencer_init_config(&replay);
 +
 +      replay.signoff = opts->signoff;
 +      replay.allow_ff = !(opts->flags & REBASE_FORCE);
 +      if (opts->allow_rerere_autoupdate)
 +              replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
 +      replay.allow_empty = 1;
 +      replay.allow_empty_message = opts->allow_empty_message;
 +      replay.verbose = opts->flags & REBASE_VERBOSE;
 +      replay.reschedule_failed_exec = opts->reschedule_failed_exec;
 +      replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
 +      replay.strategy = opts->strategy;
 +      if (opts->strategy_opts)
 +              parse_strategy_opts(&replay, opts->strategy_opts);
 +
 +      return replay;
 +}
 +
 +enum action {
 +      ACTION_NONE = 0,
 +      ACTION_CONTINUE,
 +      ACTION_SKIP,
 +      ACTION_ABORT,
 +      ACTION_QUIT,
 +      ACTION_EDIT_TODO,
 +      ACTION_SHOW_CURRENT_PATCH,
 +      ACTION_SHORTEN_OIDS,
 +      ACTION_EXPAND_OIDS,
 +      ACTION_CHECK_TODO_LIST,
 +      ACTION_REARRANGE_SQUASH,
 +      ACTION_ADD_EXEC
 +};
 +
 +static const char *action_names[] = { "undefined",
 +                                    "continue",
 +                                    "skip",
 +                                    "abort",
 +                                    "quit",
 +                                    "edit_todo",
 +                                    "show_current_patch" };
 +
 +static int add_exec_commands(struct string_list *commands)
 +{
 +      const char *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      int res;
 +
 +      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +
 +      if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 +                                      &todo_list)) {
 +              todo_list_release(&todo_list);
 +              return error(_("unusable todo list: '%s'"), todo_file);
 +      }
 +
 +      todo_list_add_exec_commands(&todo_list, commands);
 +      res = todo_list_write_to_file(the_repository, &todo_list,
 +                                    todo_file, NULL, NULL, -1, 0);
 +      todo_list_release(&todo_list);
 +
 +      if (res)
 +              return error_errno(_("could not write '%s'."), todo_file);
 +      return 0;
 +}
 +
 +static int rearrange_squash_in_todo_file(void)
 +{
 +      const char *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      int res = 0;
 +
 +      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +      if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 +                                      &todo_list)) {
 +              todo_list_release(&todo_list);
 +              return error(_("unusable todo list: '%s'"), todo_file);
 +      }
 +
 +      res = todo_list_rearrange_squash(&todo_list);
 +      if (!res)
 +              res = todo_list_write_to_file(the_repository, &todo_list,
 +                                            todo_file, NULL, NULL, -1, 0);
 +
 +      todo_list_release(&todo_list);
 +
 +      if (res)
 +              return error_errno(_("could not write '%s'."), todo_file);
 +      return 0;
 +}
 +
 +static int transform_todo_file(unsigned flags)
 +{
 +      const char *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      int res;
 +
 +      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +
 +      if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 +                                      &todo_list)) {
 +              todo_list_release(&todo_list);
 +              return error(_("unusable todo list: '%s'"), todo_file);
 +      }
 +
 +      res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
 +                                    NULL, NULL, -1, flags);
 +      todo_list_release(&todo_list);
 +
 +      if (res)
 +              return error_errno(_("could not write '%s'."), todo_file);
 +      return 0;
 +}
 +
 +static int edit_todo_file(unsigned flags)
 +{
 +      const char *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT,
 +              new_todo = TODO_LIST_INIT;
 +      int res = 0;
 +
 +      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +
 +      strbuf_stripspace(&todo_list.buf, 1);
 +      res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
 +      if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
 +                                          NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
 +              res = error_errno(_("could not write '%s'"), todo_file);
 +
 +      todo_list_release(&todo_list);
 +      todo_list_release(&new_todo);
 +
 +      return res;
 +}
 +
 +static int get_revision_ranges(struct commit *upstream, struct commit *onto,
 +                             const char **head_hash,
 +                             char **revisions, char **shortrevisions)
 +{
 +      struct commit *base_rev = upstream ? upstream : onto;
 +      const char *shorthead;
 +      struct object_id orig_head;
 +
 +      if (get_oid("HEAD", &orig_head))
 +              return error(_("no HEAD?"));
 +
 +      *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
 +      *revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
 +                                                 *head_hash);
 +
 +      shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
 +
 +      if (upstream) {
 +              const char *shortrev;
 +
 +              shortrev = find_unique_abbrev(&base_rev->object.oid,
 +                                            DEFAULT_ABBREV);
 +
 +              *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
 +      } else
 +              *shortrevisions = xstrdup(shorthead);
 +
 +      return 0;
 +}
 +
 +static int init_basic_state(struct replay_opts *opts, const char *head_name,
 +                          struct commit *onto, const char *orig_head)
 +{
 +      FILE *interactive;
 +
 +      if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
 +              return error_errno(_("could not create temporary %s"), merge_dir());
 +
 +      delete_reflog("REBASE_HEAD");
 +
 +      interactive = fopen(path_interactive(), "w");
 +      if (!interactive)
 +              return error_errno(_("could not mark as interactive"));
 +      fclose(interactive);
 +
 +      return write_basic_state(opts, head_name, onto, orig_head);
 +}
 +
 +static void split_exec_commands(const char *cmd, struct string_list *commands)
 +{
 +      if (cmd && *cmd) {
 +              string_list_split(commands, cmd, '\n', -1);
 +
 +              /* rebase.c adds a new line to cmd after every command,
 +               * so here the last command is always empty */
 +              string_list_remove_empty_items(commands, 0);
 +      }
 +}
 +
 +static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
 +{
 +      int ret;
 +      const char *head_hash = NULL;
 +      char *revisions = NULL, *shortrevisions = NULL;
 +      struct argv_array make_script_args = ARGV_ARRAY_INIT;
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      struct replay_opts replay = get_replay_opts(opts);
 +      struct string_list commands = STRING_LIST_INIT_DUP;
 +
 +      if (prepare_branch_to_be_rebased(the_repository, &replay,
 +                                       opts->switch_to))
 +              return -1;
 +
 +      if (get_revision_ranges(opts->upstream, opts->onto, &head_hash,
 +                              &revisions, &shortrevisions))
 +              return -1;
 +
 +      if (init_basic_state(&replay,
 +                           opts->head_name ? opts->head_name : "detached HEAD",
 +                           opts->onto, head_hash)) {
 +              free(revisions);
 +              free(shortrevisions);
 +
 +              return -1;
 +      }
 +
 +      if (!opts->upstream && opts->squash_onto)
 +              write_file(path_squash_onto(), "%s\n",
 +                         oid_to_hex(opts->squash_onto));
 +
 +      argv_array_pushl(&make_script_args, "", revisions, NULL);
 +      if (opts->restrict_revision)
 +              argv_array_push(&make_script_args,
 +                              oid_to_hex(&opts->restrict_revision->object.oid));
 +
 +      ret = sequencer_make_script(the_repository, &todo_list.buf,
 +                                  make_script_args.argc, make_script_args.argv,
 +                                  flags);
 +
 +      if (ret)
 +              error(_("could not generate todo list"));
 +      else {
 +              discard_cache();
 +              if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 +                                              &todo_list))
 +                      BUG("unusable todo list");
 +
 +              split_exec_commands(opts->cmd, &commands);
 +              ret = complete_action(the_repository, &replay, flags,
 +                      shortrevisions, opts->onto_name, opts->onto, head_hash,
 +                      &commands, opts->autosquash, &todo_list);
 +      }
 +
 +      string_list_clear(&commands, 0);
 +      free(revisions);
 +      free(shortrevisions);
 +      todo_list_release(&todo_list);
 +      argv_array_clear(&make_script_args);
 +
 +      return ret;
 +}
 +
 +static int run_rebase_interactive(struct rebase_options *opts,
 +                                enum action command)
 +{
 +      unsigned flags = 0;
 +      int abbreviate_commands = 0, ret = 0;
 +
 +      git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
 +
 +      flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
 +      flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
 +      flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
 +      flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
 +      flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
 +
 +      switch (command) {
 +      case ACTION_NONE: {
 +              if (!opts->onto && !opts->upstream)
 +                      die(_("a base commit must be provided with --upstream or --onto"));
 +
 +              ret = do_interactive_rebase(opts, flags);
 +              break;
 +      }
 +      case ACTION_SKIP: {
 +              struct string_list merge_rr = STRING_LIST_INIT_DUP;
 +
 +              rerere_clear(the_repository, &merge_rr);
 +      }
 +              /* fallthrough */
 +      case ACTION_CONTINUE: {
 +              struct replay_opts replay_opts = get_replay_opts(opts);
 +
 +              ret = sequencer_continue(the_repository, &replay_opts);
 +              break;
 +      }
 +      case ACTION_EDIT_TODO:
 +              ret = edit_todo_file(flags);
 +              break;
 +      case ACTION_SHOW_CURRENT_PATCH: {
 +              struct child_process cmd = CHILD_PROCESS_INIT;
 +
 +              cmd.git_cmd = 1;
 +              argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
 +              ret = run_command(&cmd);
 +
 +              break;
 +      }
 +      case ACTION_SHORTEN_OIDS:
 +      case ACTION_EXPAND_OIDS:
 +              ret = transform_todo_file(flags);
 +              break;
 +      case ACTION_CHECK_TODO_LIST:
 +              ret = check_todo_list_from_file(the_repository);
 +              break;
 +      case ACTION_REARRANGE_SQUASH:
 +              ret = rearrange_squash_in_todo_file();
 +              break;
 +      case ACTION_ADD_EXEC: {
 +              struct string_list commands = STRING_LIST_INIT_DUP;
 +
 +              split_exec_commands(opts->cmd, &commands);
 +              ret = add_exec_commands(&commands);
 +              string_list_clear(&commands, 0);
 +              break;
 +      }
 +      default:
 +              BUG("invalid command '%d'", command);
 +      }
 +
 +      return ret;
 +}
 +
 +static const char * const builtin_rebase_interactive_usage[] = {
 +      N_("git rebase--interactive [<options>]"),
 +      NULL
 +};
 +
 +int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 +{
 +      struct rebase_options opts = REBASE_OPTIONS_INIT;
 +      struct object_id squash_onto = null_oid;
 +      enum action command = ACTION_NONE;
 +      struct option options[] = {
 +              OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
 +                         REBASE_FORCE),
 +              OPT_BOOL(0, "keep-empty", &opts.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, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
 +              OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
 +                       N_("keep original branch points of cousins")),
 +              OPT_BOOL(0, "autosquash", &opts.autosquash,
 +                       N_("move commits that begin with squash!/fixup!")),
 +              OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
 +              OPT_BIT('v', "verbose", &opts.flags,
 +                      N_("display a diffstat of what changed upstream"),
 +                      REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
 +              OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
 +                          ACTION_CONTINUE),
 +              OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP),
 +              OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
 +                          ACTION_EDIT_TODO),
 +              OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
 +                          ACTION_SHOW_CURRENT_PATCH),
 +              OPT_CMDMODE(0, "shorten-ids", &command,
 +                      N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS),
 +              OPT_CMDMODE(0, "expand-ids", &command,
 +                      N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS),
 +              OPT_CMDMODE(0, "check-todo-list", &command,
 +                      N_("check the todo list"), ACTION_CHECK_TODO_LIST),
 +              OPT_CMDMODE(0, "rearrange-squash", &command,
 +                      N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH),
 +              OPT_CMDMODE(0, "add-exec-commands", &command,
 +                      N_("insert exec commands in todo list"), ACTION_ADD_EXEC),
 +              { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"),
 +                PARSE_OPT_NONEG, parse_opt_commit, 0 },
 +              { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision,
 +                N_("restrict-revision"), N_("restrict revision"),
 +                PARSE_OPT_NONEG, parse_opt_commit, 0 },
 +              { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"),
 +                N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 },
 +              { OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"),
 +                N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit,
 +                0 },
 +              OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")),
 +              { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
 +                      N_("GPG-sign commits"),
 +                      PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
 +              OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
 +                         N_("rebase strategy")),
 +              OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
 +                         N_("strategy options")),
 +              OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"),
 +                         N_("the branch or commit to checkout")),
 +              OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")),
 +              OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")),
 +              OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate),
 +              OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
 +                       N_("automatically re-schedule any `exec` that fails")),
 +              OPT_END()
 +      };
 +
 +      opts.rebase_cousins = -1;
 +
 +      if (argc == 1)
 +              usage_with_options(builtin_rebase_interactive_usage, options);
 +
 +      argc = parse_options(argc, argv, prefix, options,
 +                      builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
 +
 +      if (!is_null_oid(&squash_onto))
 +              opts.squash_onto = &squash_onto;
 +
 +      if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
 +              warning(_("--[no-]rebase-cousins has no effect without "
 +                        "--rebase-merges"));
 +
 +      return !!run_rebase_interactive(&opts, command);
 +}
 +
  static int is_interactive(struct rebase_options *opts)
  {
        return opts->type == REBASE_INTERACTIVE ||
@@@ -619,13 -206,14 +619,13 @@@ static int read_basic_state(struct reba
                            &buf))
                        return -1;
                if (!strcmp(buf.buf, "--rerere-autoupdate"))
 -                      opts->allow_rerere_autoupdate = 1;
 +                      opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
                else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
 -                      opts->allow_rerere_autoupdate = 0;
 +                      opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE;
                else
                        warning(_("ignoring invalid allow_rerere_autoupdate: "
                                  "'%s'"), buf.buf);
 -      } else
 -              opts->allow_rerere_autoupdate = -1;
 +      }
  
        if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
                strbuf_reset(&buf);
        return 0;
  }
  
 -static int write_basic_state(struct rebase_options *opts)
 +static int rebase_write_basic_state(struct rebase_options *opts)
  {
        write_file(state_dir_path("head-name", opts), "%s",
                   opts->head_name ? opts->head_name : "detached HEAD");
        if (opts->strategy_opts)
                write_file(state_dir_path("strategy_opts", opts), "%s",
                           opts->strategy_opts);
 -      if (opts->allow_rerere_autoupdate >= 0)
 +      if (opts->allow_rerere_autoupdate > 0)
                write_file(state_dir_path("allow_rerere_autoupdate", opts),
                           "-%s-rerere-autoupdate",
 -                         opts->allow_rerere_autoupdate ? "" : "-no");
 +                         opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
 +                              "" : "-no");
        if (opts->gpg_sign_opt)
                write_file(state_dir_path("gpg_sign_opt", opts), "%s",
                           opts->gpg_sign_opt);
@@@ -738,30 -325,20 +738,30 @@@ static int finish_rebase(struct rebase_
  {
        struct strbuf dir = STRBUF_INIT;
        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
 +      int ret = 0;
  
        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
        apply_autostash(opts);
 -      close_all_packs(the_repository->objects);
 +      close_object_store(the_repository->objects);
        /*
         * We ignore errors in 'gc --auto', since the
         * user should see them.
         */
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
 -      strbuf_addstr(&dir, opts->state_dir);
 -      remove_dir_recursively(&dir, 0);
 -      strbuf_release(&dir);
 +      if (opts->type == REBASE_INTERACTIVE) {
 +              struct replay_opts replay = REPLAY_OPTS_INIT;
  
 -      return 0;
 +              replay.action = REPLAY_INTERACTIVE_REBASE;
 +              ret = sequencer_remove_state(&replay);
 +      } else {
 +              strbuf_addstr(&dir, opts->state_dir);
 +              if (remove_dir_recursively(&dir, 0))
 +                      ret = error(_("could not remove '%s'"),
 +                                  opts->state_dir);
 +              strbuf_release(&dir);
 +      }
 +
 +      return ret;
  }
  
  static struct commit *peel_committish(const char *name)
@@@ -792,7 -369,6 +792,7 @@@ static void add_var(struct strbuf *buf
  #define RESET_HEAD_HARD (1<<1)
  #define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
  #define RESET_HEAD_REFS_ONLY (1<<3)
 +#define RESET_ORIG_HEAD (1<<4)
  
  static int reset_head(struct object_id *oid, const char *action,
                      const char *switch_to_branch, unsigned flags,
        unsigned reset_hard = flags & RESET_HEAD_HARD;
        unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
        unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
 +      unsigned update_orig_head = flags & RESET_ORIG_HEAD;
        struct object_id head_oid;
        struct tree_desc desc[2] = { { NULL }, { NULL } };
        struct lock_file lock = LOCK_INIT;
@@@ -879,21 -454,18 +879,21 @@@ reset_head_refs
        strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
        prefix_len = msg.len;
  
 -      if (!get_oid("ORIG_HEAD", &oid_old_orig))
 -              old_orig = &oid_old_orig;
 -      if (!get_oid("HEAD", &oid_orig)) {
 -              orig = &oid_orig;
 -              if (!reflog_orig_head) {
 -                      strbuf_addstr(&msg, "updating ORIG_HEAD");
 -                      reflog_orig_head = msg.buf;
 -              }
 -              update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0,
 -                         UPDATE_REFS_MSG_ON_ERR);
 -      } else if (old_orig)
 -              delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
 +      if (update_orig_head) {
 +              if (!get_oid("ORIG_HEAD", &oid_old_orig))
 +                      old_orig = &oid_old_orig;
 +              if (!get_oid("HEAD", &oid_orig)) {
 +                      orig = &oid_orig;
 +                      if (!reflog_orig_head) {
 +                              strbuf_addstr(&msg, "updating ORIG_HEAD");
 +                              reflog_orig_head = msg.buf;
 +                      }
 +                      update_ref(reflog_orig_head, "ORIG_HEAD", orig,
 +                                 old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
 +              } else if (old_orig)
 +                      delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
 +      }
 +
        if (!reflog_head) {
                strbuf_setlen(&msg, prefix_len);
                strbuf_addstr(&msg, "updating HEAD");
                                 detach_head ? REF_NO_DEREF : 0,
                                 UPDATE_REFS_MSG_ON_ERR);
        else {
 -              ret = update_ref(reflog_orig_head, switch_to_branch, oid,
 +              ret = update_ref(reflog_head, switch_to_branch, oid,
                                 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
                if (!ret)
                        ret = create_symref("HEAD", switch_to_branch,
@@@ -1053,9 -625,9 +1053,9 @@@ static int run_am(struct rebase_option
        argv_array_push(&am.args, "--rebasing");
        argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
        argv_array_push(&am.args, "--patch-format=mboxrd");
 -      if (opts->allow_rerere_autoupdate > 0)
 +      if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
                argv_array_push(&am.args, "--rerere-autoupdate");
 -      else if (opts->allow_rerere_autoupdate == 0)
 +      else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
                argv_array_push(&am.args, "--no-rerere-autoupdate");
        if (opts->gpg_sign_opt)
                argv_array_push(&am.args, opts->gpg_sign_opt);
        }
  
        if (is_directory(opts->state_dir))
 -              write_basic_state(opts);
 +              rebase_write_basic_state(opts);
  
        return status;
  }
  
 -static int run_specific_rebase(struct rebase_options *opts)
 +static int run_specific_rebase(struct rebase_options *opts, enum action action)
  {
        const char *argv[] = { NULL, NULL };
        struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
  
        if (opts->type == REBASE_INTERACTIVE) {
                /* Run builtin interactive rebase */
 -              struct child_process child = CHILD_PROCESS_INIT;
 -
 -              argv_array_pushf(&child.env_array, "GIT_CHERRY_PICK_HELP=%s",
 -                               resolvemsg);
 +              setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
                if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
 -                      argv_array_push(&child.env_array,
 -                                      "GIT_SEQUENCE_EDITOR=:");
 +                      setenv("GIT_SEQUENCE_EDITOR", ":", 1);
                        opts->autosquash = 0;
                }
 +              if (opts->gpg_sign_opt) {
 +                      /* remove the leading "-S" */
 +                      char *tmp = xstrdup(opts->gpg_sign_opt + 2);
 +                      free(opts->gpg_sign_opt);
 +                      opts->gpg_sign_opt = tmp;
 +              }
  
 -              child.git_cmd = 1;
 -              argv_array_push(&child.args, "rebase--interactive");
 -
 -              if (opts->action)
 -                      argv_array_pushf(&child.args, "--%s", opts->action);
 -              if (opts->keep_empty)
 -                      argv_array_push(&child.args, "--keep-empty");
 -              if (opts->rebase_merges)
 -                      argv_array_push(&child.args, "--rebase-merges");
 -              if (opts->rebase_cousins)
 -                      argv_array_push(&child.args, "--rebase-cousins");
 -              if (opts->autosquash)
 -                      argv_array_push(&child.args, "--autosquash");
 -              if (opts->flags & REBASE_VERBOSE)
 -                      argv_array_push(&child.args, "--verbose");
 -              if (opts->flags & REBASE_FORCE)
 -                      argv_array_push(&child.args, "--no-ff");
 -              if (opts->restrict_revision)
 -                      argv_array_pushf(&child.args,
 -                                       "--restrict-revision=^%s",
 -                                       oid_to_hex(&opts->restrict_revision->object.oid));
 -              if (opts->upstream)
 -                      argv_array_pushf(&child.args, "--upstream=%s",
 -                                       oid_to_hex(&opts->upstream->object.oid));
 -              if (opts->onto)
 -                      argv_array_pushf(&child.args, "--onto=%s",
 -                                       oid_to_hex(&opts->onto->object.oid));
 -              if (opts->squash_onto)
 -                      argv_array_pushf(&child.args, "--squash-onto=%s",
 -                                       oid_to_hex(opts->squash_onto));
 -              if (opts->onto_name)
 -                      argv_array_pushf(&child.args, "--onto-name=%s",
 -                                       opts->onto_name);
 -              argv_array_pushf(&child.args, "--head-name=%s",
 -                               opts->head_name ?
 -                               opts->head_name : "detached HEAD");
 -              if (opts->strategy)
 -                      argv_array_pushf(&child.args, "--strategy=%s",
 -                                       opts->strategy);
 -              if (opts->strategy_opts)
 -                      argv_array_pushf(&child.args, "--strategy-opts=%s",
 -                                       opts->strategy_opts);
 -              if (opts->switch_to)
 -                      argv_array_pushf(&child.args, "--switch-to=%s",
 -                                       opts->switch_to);
 -              if (opts->cmd)
 -                      argv_array_pushf(&child.args, "--cmd=%s", opts->cmd);
 -              if (opts->allow_empty_message)
 -                      argv_array_push(&child.args, "--allow-empty-message");
 -              if (opts->allow_rerere_autoupdate > 0)
 -                      argv_array_push(&child.args, "--rerere-autoupdate");
 -              else if (opts->allow_rerere_autoupdate == 0)
 -                      argv_array_push(&child.args, "--no-rerere-autoupdate");
 -              if (opts->gpg_sign_opt)
 -                      argv_array_push(&child.args, opts->gpg_sign_opt);
 -              if (opts->signoff)
 -                      argv_array_push(&child.args, "--signoff");
 -              if (opts->reschedule_failed_exec)
 -                      argv_array_push(&child.args, "--reschedule-failed-exec");
 -
 -              status = run_command(&child);
 +              status = run_rebase_interactive(opts, action);
                goto finished_rebase;
        }
  
        add_var(&script_snippet, "action", opts->action ? opts->action : "");
        add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : "");
        add_var(&script_snippet, "allow_rerere_autoupdate",
 -              opts->allow_rerere_autoupdate < 0 ? "" :
                opts->allow_rerere_autoupdate ?
 -              "--rerere-autoupdate" : "--no-rerere-autoupdate");
 +                      opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
 +                      "--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
        add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
        add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
        add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
        }
  
        switch (opts->type) {
 -      case REBASE_AM:
 -              backend = "git-rebase--am";
 -              backend_func = "git_rebase__am";
 -              break;
        case REBASE_PRESERVE_MERGES:
                backend = "git-rebase--preserve-merges";
                backend_func = "git_rebase__preserve_merges";
        }
  
        strbuf_addf(&script_snippet,
 -                  ". git-sh-setup && . git-rebase--common &&"
 -                  " . %s && %s", backend, backend_func);
 +                  ". git-sh-setup && . %s && %s", backend, backend_func);
        argv[0] = script_snippet.buf;
  
        status = run_command_v_opt(argv, RUN_USING_SHELL);
@@@ -1208,7 -843,7 +1208,7 @@@ static int rebase_config(const char *va
                if (git_config_bool(var, value))
                        opts->flags |= REBASE_DIFFSTAT;
                else
 -                      opts->flags &= !REBASE_DIFFSTAT;
 +                      opts->flags &= ~REBASE_DIFFSTAT;
                return 0;
        }
  
                return 0;
        }
  
 +      if (!strcmp(var, "rebase.usebuiltin")) {
 +              opts->use_legacy_rebase = !git_config_bool(var, value);
 +              return 0;
 +      }
 +
        return git_default_config(var, value, data);
  }
  
@@@ -1373,7 -1003,14 +1373,7 @@@ static int check_exec_cmd(const char *c
  
  int cmd_rebase(int argc, const char **argv, const char *prefix)
  {
 -      struct rebase_options options = {
 -              .type = REBASE_UNSPECIFIED,
 -              .flags = REBASE_NO_QUIET,
 -              .git_am_opts = ARGV_ARRAY_INIT,
 -              .allow_rerere_autoupdate  = -1,
 -              .allow_empty_message = 1,
 -              .git_format_patch_opt = STRBUF_INIT,
 -      };
 +      struct rebase_options options = REBASE_OPTIONS_INIT;
        const char *branch_name;
        int ret, flags, total_argc, in_progress = 0;
        int ok_to_skip_pre_rebase = 0;
        struct strbuf revisions = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
        struct object_id merge_base;
 -      enum {
 -              NO_ACTION,
 -              ACTION_CONTINUE,
 -              ACTION_SKIP,
 -              ACTION_ABORT,
 -              ACTION_QUIT,
 -              ACTION_EDIT_TODO,
 -              ACTION_SHOW_CURRENT_PATCH,
 -      } action = NO_ACTION;
 -      static const char *action_names[] = { N_("undefined"),
 -                                            N_("continue"),
 -                                            N_("skip"),
 -                                            N_("abort"),
 -                                            N_("quit"),
 -                                            N_("edit_todo"),
 -                                            N_("show_current_patch"),
 -                                            NULL };
 +      enum action action = ACTION_NONE;
        const char *gpg_sign = NULL;
        struct string_list exec = STRING_LIST_INIT_NODUP;
        const char *rebase_merges = NULL;
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        parse_opt_interactive },
                OPT_SET_INT('p', "preserve-merges", &options.type,
 -                          N_("try to recreate merges instead of ignoring "
 -                             "them"), REBASE_PRESERVE_MERGES),
 -              OPT_BOOL(0, "rerere-autoupdate",
 -                       &options.allow_rerere_autoupdate,
 -                       N_("allow rerere to update index with resolved "
 -                          "conflict")),
 +                          N_("(DEPRECATED) try to recreate merges instead of "
 +                             "ignoring them"), REBASE_PRESERVE_MERGES),
 +              OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
                OPT_BOOL('k', "keep-empty", &options.keep_empty,
                         N_("preserve empty commits during rebase")),
                OPT_BOOL(0, "autosquash", &options.autosquash,
        };
        int i;
  
 -      /*
 -       * NEEDSWORK: Once the builtin rebase has been tested enough
 -       * and git-legacy-rebase.sh is retired to contrib/, this preamble
 -       * can be removed.
 -       */
 -
 -      if (!use_builtin_rebase()) {
 -              const char *path = mkpath("%s/git-legacy-rebase",
 -                                        git_exec_path());
 -
 -              if (sane_execvp(path, (char **)argv) < 0)
 -                      die_errno(_("could not exec %s"), path);
 -              else
 -                      BUG("sane_execvp() returned???");
 -      }
 -
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
        trace_repo_setup(prefix);
        setup_work_tree();
  
 +      options.allow_empty_message = 1;
        git_config(rebase_config, &options);
  
 +      if (options.use_legacy_rebase ||
 +          !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1))
 +              warning(_("the rebase.useBuiltin support has been removed!\n"
 +                        "See its entry in 'git help config' for details."));
 +
        strbuf_reset(&buf);
        strbuf_addf(&buf, "%s/applying", apply_dir());
        if(file_exists(buf.buf))
                             builtin_rebase_options,
                             builtin_rebase_usage, 0);
  
 -      if (action != NO_ACTION && total_argc != 2) {
 +      if (action != ACTION_NONE && total_argc != 2) {
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
        }
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
  
 -      if (action != NO_ACTION && !in_progress)
 +      if (options.type == REBASE_PRESERVE_MERGES)
 +              warning(_("git rebase --preserve-merges is deprecated. "
 +                        "Use --rebase-merges instead."));
 +
 +      if (action != ACTION_NONE && !in_progress)
                die(_("No rebase in progress?"));
        setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
  
                if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
                               NULL, NULL) < 0)
                        die(_("could not discard worktree changes"));
-               remove_branch_state(the_repository);
+               remove_branch_state(the_repository, 0);
                if (read_basic_state(&options))
                        exit(1);
                goto run_rebase;
                               NULL, NULL) < 0)
                        die(_("could not move back to %s"),
                            oid_to_hex(&options.orig_head));
-               remove_branch_state(the_repository);
+               remove_branch_state(the_repository, 0);
 -              ret = finish_rebase(&options);
 +              ret = !!finish_rebase(&options);
                goto cleanup;
        }
        case ACTION_QUIT: {
 -              strbuf_reset(&buf);
 -              strbuf_addstr(&buf, options.state_dir);
 -              ret = !!remove_dir_recursively(&buf, 0);
 -              if (ret)
 -                      die(_("could not remove '%s'"), options.state_dir);
 +              if (options.type == REBASE_INTERACTIVE) {
 +                      struct replay_opts replay = REPLAY_OPTS_INIT;
 +
 +                      replay.action = REPLAY_INTERACTIVE_REBASE;
 +                      ret = !!sequencer_remove_state(&replay);
 +              } else {
 +                      strbuf_reset(&buf);
 +                      strbuf_addstr(&buf, options.state_dir);
 +                      ret = !!remove_dir_recursively(&buf, 0);
 +                      if (ret)
 +                              error(_("could not remove '%s'"),
 +                                     options.state_dir);
 +              }
                goto cleanup;
        }
        case ACTION_EDIT_TODO:
                options.action = "show-current-patch";
                options.dont_finish_rebase = 1;
                goto run_rebase;
 -      case NO_ACTION:
 +      case ACTION_NONE:
                break;
        default:
                BUG("action: %d", action);
                                branch_name = options.head_name;
  
                } else {
 -                      free(options.head_name);
 -                      options.head_name = NULL;
 +                      FREE_AND_NULL(options.head_name);
                        branch_name = "HEAD";
                }
                if (get_oid("HEAD", &options.orig_head))
        strbuf_addf(&msg, "%s: checkout %s",
                    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
        if (reset_head(&options.onto->object.oid, "checkout", NULL,
 -                     RESET_HEAD_DETACH | RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
 +                     RESET_HEAD_DETACH | RESET_ORIG_HEAD | 
 +                     RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
                       NULL, msg.buf))
                die(_("Could not detach HEAD"));
        strbuf_release(&msg);
         * we just fast-forwarded.
         */
        strbuf_reset(&msg);
 -      if (!oidcmp(&merge_base, &options.orig_head)) {
 +      if (oideq(&merge_base, &options.orig_head)) {
                printf(_("Fast-forwarded %s to %s.\n"),
                        branch_name, options.onto_name);
                strbuf_addf(&msg, "rebase finished: %s onto %s",
                        options.head_name ? options.head_name : "detached HEAD",
                        oid_to_hex(&options.onto->object.oid));
 -              reset_head(NULL, "Fast-forwarded", options.head_name, 0,
 -                         "HEAD", msg.buf);
 +              reset_head(NULL, "Fast-forwarded", options.head_name,
 +                         RESET_HEAD_REFS_ONLY, "HEAD", msg.buf);
                strbuf_release(&msg);
                ret = !!finish_rebase(&options);
                goto cleanup;
        options.revisions = revisions.buf;
  
  run_rebase:
 -      ret = !!run_specific_rebase(&options);
 +      ret = !!run_specific_rebase(&options, action);
  
  cleanup:
 +      strbuf_release(&buf);
        strbuf_release(&revisions);
        free(options.head_name);
        free(options.gpg_sign_opt);
diff --combined builtin/reset.c
index 26ef9a7bd03ac8925e1cb350c49e5e327999ba52,6d9397c84434c4ba09874a1146095d3d396e045c..c2bb35a4b7048c94f79057ed3db4cbdd30a28504
@@@ -386,7 -386,6 +386,7 @@@ int cmd_reset(int argc, const char **ar
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
                        if (read_from_tree(&pathspec, &oid, intent_to_add))
                                return 1;
 +                      the_index.updated_skipworktree = 1;
                        if (!quiet && get_git_work_tree()) {
                                uint64_t t_begin, t_delta_in_ms;
  
                        print_new_head_line(lookup_commit_reference(the_repository, &oid));
        }
        if (!pathspec.nr)
-               remove_branch_state(the_repository);
+               remove_branch_state(the_repository, 0);
  
        return update_ref_status;
  }
diff --combined builtin/revert.c
index d4dcedbdc683f92d191cc6386f6388b1a918faa8,ebf2789225bf4973d03302dc58ce7fddd76c4eeb..4e71b2f2aa292ffc3187aac61001e7fd529255b3
@@@ -96,13 -96,11 +96,13 @@@ static int run_sequencer(int argc, cons
  {
        const char * const * usage_str = revert_or_cherry_pick_usage(opts);
        const char *me = action_name(opts);
 +      const char *cleanup_arg = NULL;
        int cmd = 0;
        struct option base_options[] = {
                OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
                OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
                OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
 +              OPT_CLEANUP(&cleanup_arg),
                OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
                OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
                OPT_NOOP_NOARG('r', NULL),
        if (opts->keep_redundant_commits)
                opts->allow_empty = 1;
  
 +      if (cleanup_arg) {
 +              opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
 +              opts->explicit_cleanup = 1;
 +      }
 +
        /* Check for incompatible command line arguments */
        if (cmd) {
                char *this_operation;
        if (cmd == 'q') {
                int ret = sequencer_remove_state(opts);
                if (!ret)
-                       remove_branch_state(the_repository);
+                       remove_branch_state(the_repository, 0);
                return ret;
        }
        if (cmd == 'c')
index 8c6b610a2447cd1ec7c2dff0f9dbd09f6a024f57,656e49710e59fea8378c5f93eff2da072231eae6..e087c4bf0085add8e968e128db6b667acbc80320
@@@ -37,7 -37,8 +37,8 @@@
  #   GIT_COMPLETION_CHECKOUT_NO_GUESS
  #
  #     When set to "1", do not include "DWIM" suggestions in git-checkout
- #     completion (e.g., completing "foo" when "origin/foo" exists).
+ #     and git-switch completion (e.g., completing "foo" when "origin/foo"
+ #     exists).
  
  case "$COMP_WORDBREAKS" in
  *:*) : great ;;
@@@ -400,8 -401,7 +401,8 @@@ __gitcomp_builtin (
        if [ -z "$options" ]; then
                # leading and trailing spaces are significant to make
                # option removal work correctly.
 -              options=" $incl $(__git ${cmd/_/ } --git-completion-helper) "
 +              options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " || return
 +
                for i in $excl; do
                        options="${options/ $i / }"
                done
@@@ -1025,7 -1025,7 +1026,7 @@@ __git_all_commands
  __git_compute_all_commands ()
  {
        test -n "$__git_all_commands" ||
 -      __git_all_commands=$(git --list-cmds=main,others,alias,nohelpers)
 +      __git_all_commands=$(__git --list-cmds=main,others,alias,nohelpers)
  }
  
  # Lists all set config variables starting with the given section prefix,
@@@ -1502,8 -1502,7 +1503,8 @@@ _git_diff (
  }
  
  __git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
 -                      tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare
 +                      tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc
 +                      codecompare smerge
  "
  
  _git_difftool ()
@@@ -1537,7 -1536,7 +1538,7 @@@ _git_fetch (
                return
                ;;
        --filter=*)
 -              __gitcomp "blob:none blob:limit= sparse:oid= sparse:path=" "" "${cur##--filter=}"
 +              __gitcomp "blob:none blob:limit= sparse:oid=" "" "${cur##--filter=}"
                return
                ;;
        --*)
@@@ -1654,9 -1653,9 +1655,9 @@@ _git_help (
        esac
        if test -n "$GIT_TESTING_ALL_COMMAND_LIST"
        then
 -              __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk"
 +              __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(__git --list-cmds=alias,list-guide) gitk"
        else
 -              __gitcomp "$(git --list-cmds=main,nohelpers,alias,list-guide) gitk"
 +              __gitcomp "$(__git --list-cmds=main,nohelpers,alias,list-guide) gitk"
        fi
  }
  
@@@ -2160,6 -2159,44 +2161,44 @@@ _git_status (
        __git_complete_index_file "$complete_opt"
  }
  
+ _git_switch ()
+ {
+       case "$cur" in
+       --conflict=*)
+               __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+               ;;
+       --*)
+               __gitcomp_builtin switch
+               ;;
+       *)
+               # check if --track, --no-track, or --no-guess was specified
+               # if so, disable DWIM mode
+               local track_opt="--track" only_local_ref=n
+               if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
+                  [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
+                       track_opt=''
+               fi
+               # explicit --guess enables DWIM mode regardless of
+               # $GIT_COMPLETION_CHECKOUT_NO_GUESS
+               if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
+                       track_opt='--track'
+               fi
+               if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
+                       only_local_ref=y
+               else
+                       # --guess --detach is invalid combination, no
+                       # dwim will be done when --detach is specified
+                       track_opt=
+               fi
+               if [ $only_local_ref = y -a -z "$track_opt" ]; then
+                       __gitcomp_direct "$(__git_heads "" "$cur" " ")"
+               else
+                       __git_complete_refs $track_opt
+               fi
+               ;;
+       esac
+ }
  __git_config_get_set_variables ()
  {
        local prevword word config_file= c=$cword
@@@ -2458,6 -2495,21 +2497,21 @@@ _git_reset (
        __git_complete_refs
  }
  
+ _git_restore ()
+ {
+       case "$cur" in
+       --conflict=*)
+               __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+               ;;
+       --source=*)
+               __git_complete_refs --cur="${cur##--source=}"
+               ;;
+       --*)
+               __gitcomp_builtin restore
+               ;;
+       esac
+ }
  __git_revert_inprogress_options="--continue --quit --abort"
  
  _git_revert ()
@@@ -2612,7 -2664,7 +2666,7 @@@ _git_submodule (
  {
        __git_has_doubledash && return
  
 -      local subcommands="add status init deinit update summary foreach sync absorbgitdirs"
 +      local subcommands="add status init deinit update set-branch summary foreach sync absorbgitdirs"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                case "$cur" in
                        --force --rebase --merge --reference --depth --recursive --jobs
                "
                ;;
 +      set-branch,--*)
 +              __gitcomp "--default --branch"
 +              ;;
        summary,--*)
                __gitcomp "--cached --files --summary-limit"
                ;;
@@@ -2930,7 -2979,7 +2984,7 @@@ __git_main (
                        then
                                __gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
                        else
 -                              __gitcomp "$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)"
 +                              __gitcomp "$(__git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)"
                        fi
                        ;;
                esac
index da5b4ec4bc50e4e3adaba0f25820262b6a7436a0,3dfb3629c99617e83e75c5652fa567ad589f551a..c20ae9e2102bff6bbf7b66135e2174ec68b33e81
@@@ -149,6 -149,20 +149,20 @@@ my %patch_modes = 
                FILTER => undef,
                IS_REVERSE => 0,
        },
+       'worktree_head' => {
+               DIFF => 'diff-index -p',
+               APPLY => sub { apply_patch 'apply -R', @_ },
+               APPLY_CHECK => 'apply -R',
+               FILTER => undef,
+               IS_REVERSE => 1,
+       },
+       'worktree_nothead' => {
+               DIFF => 'diff-index -R -p',
+               APPLY => sub { apply_patch 'apply', @_ },
+               APPLY_CHECK => 'apply',
+               FILTER => undef,
+               IS_REVERSE => 0,
+       },
  );
  
  $patch_mode = 'stage';
@@@ -972,11 -986,7 +986,11 @@@ sub coalesce_overlapping_hunks 
                        next;
                }
                if ($ofs_delta) {
 -                      $n_ofs += $ofs_delta;
 +                      if ($patch_mode_flavour{IS_REVERSE}) {
 +                              $o_ofs -= $ofs_delta;
 +                      } else {
 +                              $n_ofs += $ofs_delta;
 +                      }
                        $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
                                                             $n_ofs, $n_cnt);
                }
@@@ -1053,6 -1063,12 +1067,12 @@@ marked for discarding.")
  marked for discarding."),
        checkout_nothead => N__(
  "If the patch applies cleanly, the edited hunk will immediately be
+ marked for applying."),
+       worktree_head => N__(
+ "If the patch applies cleanly, the edited hunk will immediately be
+ marked for discarding."),
+       worktree_nothead => N__(
+ "If the patch applies cleanly, the edited hunk will immediately be
  marked for applying."),
  );
  
@@@ -1263,6 -1279,18 +1283,18 @@@ d - do not discard this hunk or any of 
  n - do not apply this hunk to index and worktree
  q - quit; do not apply this hunk or any of the remaining ones
  a - apply this hunk and all later hunks in the file
+ d - do not apply this hunk or any of the later hunks in the file"),
+       worktree_head => N__(
+ "y - discard this hunk from worktree
+ n - do not discard this hunk from worktree
+ q - quit; do not discard this hunk or any of the remaining ones
+ a - discard this hunk and all later hunks in the file
+ d - do not discard this hunk or any of the later hunks in the file"),
+       worktree_nothead => N__(
+ "y - apply this hunk to worktree
+ n - do not apply this hunk to worktree
+ q - quit; do not apply this hunk or any of the remaining ones
+ a - apply this hunk and all later hunks in the file
  d - do not apply this hunk or any of the later hunks in the file"),
  );
  
@@@ -1425,6 -1453,16 +1457,16 @@@ my %patch_update_prompt_modes = 
                deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
                hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
        },
+       worktree_head => {
+               mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
+               deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+               hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+       },
+       worktree_nothead => {
+               mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
+               deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
+               hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+       },
  );
  
  sub patch_update_file {
@@@ -1760,6 -1798,16 +1802,16 @@@ sub process_args 
                                                       'checkout_head' : 'checkout_nothead');
                                        $arg = shift @ARGV or die __("missing --");
                                }
+                       } elsif ($1 eq 'worktree') {
+                               $arg = shift @ARGV or die __("missing --");
+                               if ($arg eq '--') {
+                                       $patch_mode = 'checkout_index';
+                               } else {
+                                       $patch_mode_revision = $arg;
+                                       $patch_mode = ($arg eq 'HEAD' ?
+                                                      'worktree_head' : 'worktree_nothead');
+                                       $arg = shift @ARGV or die __("missing --");
+                               }
                        } elsif ($1 eq 'stage' or $1 eq 'stash') {
                                $patch_mode = $1;
                                $arg = shift @ARGV or die __("missing --");
diff --combined git.c
index c2eec470c956c597aef77ff7d3632994986db00f,6d439e723f38ebd8bde2646088e104ce69b84821..f4c0478f320fba5e919238c0af19890cb9185242
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -33,8 -33,7 +33,8 @@@ const char git_usage_string[] 
  const char git_more_info_string[] =
        N_("'git help -a' and 'git help -g' list available subcommands and some\n"
           "concept guides. See 'git help <command>' or 'git help <concept>'\n"
 -         "to read about a specific subcommand or concept.");
 +         "to read about a specific subcommand or concept.\n"
 +         "See 'git help git' for an overview of the system.");
  
  static int use_pager = -1;
  
@@@ -63,13 -62,6 +63,13 @@@ static int list_cmds(const char *spec
  {
        struct string_list list = STRING_LIST_INIT_DUP;
        int i;
 +      int nongit;
 +
 +      /*
 +      * Set up the repository so we can pick up any repo-level config (like
 +      * completion.commands).
 +      */
 +      setup_git_directory_gently(&nongit);
  
        while (*spec) {
                const char *sep = strchrnul(spec, ',');
@@@ -499,7 -491,7 +499,7 @@@ static struct cmd_struct commands[] = 
        { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
        { "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
        { "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
 -      { "difftool", cmd_difftool, RUN_SETUP | NEED_WORK_TREE },
 +      { "difftool", cmd_difftool, RUN_SETUP_GENTLY },
        { "fast-export", cmd_fast_export, RUN_SETUP },
        { "fetch", cmd_fetch, RUN_SETUP },
        { "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
        { "replace", cmd_replace, RUN_SETUP },
        { "rerere", cmd_rerere, RUN_SETUP },
        { "reset", cmd_reset, RUN_SETUP },
+       { "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
        { "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
        { "rev-parse", cmd_rev_parse, NO_PARSEOPT },
        { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
        { "rm", cmd_rm, RUN_SETUP },
        { "send-pack", cmd_send_pack, RUN_SETUP },
 -      { "serve", cmd_serve, RUN_SETUP },
        { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
        { "show", cmd_show, RUN_SETUP },
        { "show-branch", cmd_show_branch, RUN_SETUP },
        { "show-index", cmd_show_index },
        { "show-ref", cmd_show_ref, RUN_SETUP },
        { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
 +      /*
 +       * NEEDSWORK: Until the builtin stash is thoroughly robust and no
 +       * longer needs redirection to the stash shell script this is kept as
 +       * is, then should be changed to RUN_SETUP | NEED_WORK_TREE
 +       */
 +      { "stash", cmd_stash },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
        { "stripspace", cmd_stripspace },
        { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+       { "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
        { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
        { "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
        { "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
diff --combined parse-options-cb.c
index a3de795c581a3aab084efac75ed2d6edc2535a15,caaeed896fbb3d7d05cea0488e96c8b4b1274621..1240a8514e040954cbc7f091e7053b5c76ac6627
@@@ -16,17 -16,14 +16,17 @@@ int parse_opt_abbrev_cb(const struct op
        if (!arg) {
                v = unset ? 0 : DEFAULT_ABBREV;
        } else {
 +              if (!*arg)
 +                      return error(_("option `%s' expects a numerical value"),
 +                                   opt->long_name);
                v = strtol(arg, (char **)&arg, 10);
                if (*arg)
                        return error(_("option `%s' expects a numerical value"),
                                     opt->long_name);
                if (v && v < MINIMUM_ABBREV)
                        v = MINIMUM_ABBREV;
 -              else if (v > 40)
 -                      v = 40;
 +              else if (v > the_hash_algo->hexsz)
 +                      v = the_hash_algo->hexsz;
        }
        *(int *)(opt->value) = v;
        return 0;
@@@ -99,23 -96,6 +99,23 @@@ int parse_opt_commits(const struct opti
        return 0;
  }
  
 +int parse_opt_commit(const struct option *opt, const char *arg, int unset)
 +{
 +      struct object_id oid;
 +      struct commit *commit;
 +      struct commit **target = opt->value;
 +
 +      if (!arg)
 +              return -1;
 +      if (get_oid(arg, &oid))
 +              return error("malformed object name %s", arg);
 +      commit = lookup_commit_reference(the_repository, &oid);
 +      if (!commit)
 +              return error("no such commit %s", arg);
 +      *target = commit;
 +      return 0;
 +}
 +
  int parse_opt_object_name(const struct option *opt, const char *arg, int unset)
  {
        struct object_id oid;
        return 0;
  }
  
 +int parse_opt_object_id(const struct option *opt, const char *arg, int unset)
 +{
 +      struct object_id oid;
 +      struct object_id *target = opt->value;
 +
 +      if (unset) {
 +              *target = null_oid;
 +              return 0;
 +      }
 +      if (!arg)
 +              return -1;
 +      if (get_oid(arg, &oid))
 +              return error(_("malformed object name '%s'"), arg);
 +      *target = oid;
 +      return 0;
 +}
 +
  int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
  {
        int *target = opt->value;
        return 0;
  }
  
+ struct option *parse_options_dup(const struct option *o)
+ {
+       struct option *opts;
+       int nr = 0;
+       while (o && o->type != OPTION_END) {
+               nr++;
+               o++;
+       }
+       ALLOC_ARRAY(opts, nr + 1);
+       memcpy(opts, o - nr, sizeof(*o) * nr);
+       memset(opts + nr, 0, sizeof(*opts));
+       opts[nr].type = OPTION_END;
+       return opts;
+ }
  struct option *parse_options_concat(struct option *a, struct option *b)
  {
        struct option *ret;
diff --combined parse-options.h
index ac6ba8abf9ec7af1712486ecfe3e397359c4a67f,9a90c332a55b840dca6e62b7950c858f4f67c7d5..a4bd40bb6acf90fdafefa0983fb523dca4e8b11c
@@@ -7,7 -7,6 +7,7 @@@ enum parse_opt_type 
        OPTION_ARGUMENT,
        OPTION_GROUP,
        OPTION_NUMBER,
 +      OPTION_ALIAS,
        /* options with no arguments */
        OPTION_BIT,
        OPTION_NEGBIT,
@@@ -137,12 -136,10 +137,12 @@@ struct option 
  #define OPT_BOOL_F(s, l, v, h, f)   OPT_SET_INT_F(s, l, v, h, 1, f)
  #define OPT_CALLBACK_F(s, l, v, a, h, f, cb)                  \
        { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), (cb) }
 +#define OPT_STRING_F(s, l, v, a, h, f)   { OPTION_STRING,  (s), (l), (v), (a), (h), (f) }
 +#define OPT_INTEGER_F(s, l, v, h, f)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
  
  #define OPT_END()                   { OPTION_END }
 -#define OPT_ARGUMENT(l, h)          { OPTION_ARGUMENT, 0, (l), NULL, NULL, \
 -                                    (h), PARSE_OPT_NOARG}
 +#define OPT_ARGUMENT(l, v, h)       { OPTION_ARGUMENT, 0, (l), (v), NULL, \
 +                                    (h), PARSE_OPT_NOARG, NULL, 1 }
  #define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
  #define OPT_BIT(s, l, v, h, b)      OPT_BIT_F(s, l, v, h, b, 0)
  #define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
                                      (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
  #define OPT_CMDMODE(s, l, v, h, i)  { OPTION_CMDMODE, (s), (l), (v), NULL, \
                                      (h), PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) }
 -#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) }
 +#define OPT_INTEGER(s, l, v, h)     OPT_INTEGER_F(s, l, v, h, 0)
  #define OPT_MAGNITUDE(s, l, v, h)   { OPTION_MAGNITUDE, (s), (l), (v), \
                                      N_("n"), (h), PARSE_OPT_NONEG }
 -#define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
 +#define OPT_STRING(s, l, v, a, h)   OPT_STRING_F(s, l, v, a, h, 0)
  #define OPT_STRING_LIST(s, l, v, a, h) \
                                    { OPTION_CALLBACK, (s), (l), (v), (a), \
                                      (h), 0, &parse_opt_string_list }
          N_("no-op (backward compatibility)"),         \
          PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
  
 +#define OPT_ALIAS(s, l, source_long_name) \
 +      { OPTION_ALIAS, (s), (l), (source_long_name) }
 +
  /*
   * parse_options() will filter out the processed options and leave the
   * non-option arguments in argv[]. argv0 is assumed program name and
@@@ -228,17 -222,6 +228,17 @@@ const char *optname(const struct optio
                BUG("option callback does not expect an argument"); \
  } while (0)
  
 +/*
 + * Similar to the assertions above, but checks that "arg" is always non-NULL.
 + * This assertion also implies BUG_ON_OPT_NEG(), letting you declare both
 + * assertions in a single line.
 + */
 +#define BUG_ON_OPT_NEG_NOARG(unset, arg) do { \
 +      BUG_ON_OPT_NEG(unset); \
 +      if(!(arg)) \
 +              BUG("option callback expects an argument"); \
 +} while(0)
 +
  /*----- incremental advanced APIs -----*/
  
  enum parse_opt_result {
@@@ -262,8 -245,6 +262,8 @@@ struct parse_opt_ctx_t 
        const char *opt;
        int flags;
        const char *prefix;
 +      const char **alias_groups; /* must be in groups of 3 elements! */
 +      struct option *updated_options;
  };
  
  void parse_options_start(struct parse_opt_ctx_t *ctx,
@@@ -276,6 -257,7 +276,7 @@@ int parse_options_step(struct parse_opt
  
  int parse_options_end(struct parse_opt_ctx_t *ctx);
  
+ struct option *parse_options_dup(const struct option *a);
  struct option *parse_options_concat(struct option *a, struct option *b);
  
  /*----- some often used options -----*/
@@@ -283,18 -265,12 +284,18 @@@ int parse_opt_abbrev_cb(const struct op
  int parse_opt_expiry_date_cb(const struct option *, const char *, int);
  int parse_opt_color_flag_cb(const struct option *, const char *, int);
  int parse_opt_verbosity_cb(const struct option *, const char *, int);
 +/* value is struct oid_array* */
  int parse_opt_object_name(const struct option *, const char *, int);
 +/* value is struct object_id* */
 +int parse_opt_object_id(const struct option *, const char *, int);
  int parse_opt_commits(const struct option *, const char *, int);
 +int parse_opt_commit(const struct option *, const char *, int);
  int parse_opt_tertiary(const struct option *, const char *, int);
  int parse_opt_string_list(const struct option *, const char *, int);
  int parse_opt_noop_cb(const struct option *, const char *, int);
 -int parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx, const struct option *, const char *, int);
 +enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
 +                                         const struct option *,
 +                                         const char *, int);
  int parse_opt_passthru(const struct option *, const char *, int);
  int parse_opt_passthru_argv(const struct option *, const char *, int);
  
  #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
  #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
  #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 +#define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
  
  #endif
diff --combined sequencer.c
index 4a9b7f1c3fc45dd16e08ef0d4cd6abdfa1e69747,95dda23eee450d1d60a70434d254071c6c05146b..74a7a47d015010a82463691ddc70abce4708687a
@@@ -55,7 -55,8 +55,7 @@@ static GIT_PATH_FUNC(rebase_path, "reba
   * file and written to the tail of 'done'.
   */
  GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
 -static GIT_PATH_FUNC(rebase_path_todo_backup,
 -                   "rebase-merge/git-rebase-todo.backup")
 +GIT_PATH_FUNC(rebase_path_todo_backup, "rebase-merge/git-rebase-todo.backup")
  
  /*
   * The rebase command lines that have already been processed. A line
@@@ -171,22 -172,17 +171,22 @@@ static int git_sequencer_config(const c
                if (status)
                        return status;
  
 -              if (!strcmp(s, "verbatim"))
 +              if (!strcmp(s, "verbatim")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
 -              else if (!strcmp(s, "whitespace"))
 +                      opts->explicit_cleanup = 1;
 +              } else if (!strcmp(s, "whitespace")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
 -              else if (!strcmp(s, "strip"))
 +                      opts->explicit_cleanup = 1;
 +              } else if (!strcmp(s, "strip")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
 -              else if (!strcmp(s, "scissors"))
 -                      opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
 -              else
 +                      opts->explicit_cleanup = 1;
 +              } else if (!strcmp(s, "scissors")) {
 +                      opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
 +                      opts->explicit_cleanup = 1;
 +              } else {
                        warning(_("invalid commit message cleanup mode '%s'"),
                                  s);
 +              }
  
                free((char *)s);
                return status;
@@@ -279,7 -275,7 +279,7 @@@ static const char *gpg_sign_opt_quoted(
  int sequencer_remove_state(struct replay_opts *opts)
  {
        struct strbuf buf = STRBUF_INIT;
 -      int i;
 +      int i, ret = 0;
  
        if (is_rebase_i(opts) &&
            strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
                        char *eol = strchr(p, '\n');
                        if (eol)
                                *eol = '\0';
 -                      if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0)
 +                      if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
                                warning(_("could not delete '%s'"), p);
 +                              ret = -1;
 +                      }
                        if (!eol)
                                break;
                        p = eol + 1;
  
        strbuf_reset(&buf);
        strbuf_addstr(&buf, get_dir(opts));
 -      remove_dir_recursively(&buf, 0);
 +      if (remove_dir_recursively(&buf, 0))
 +              ret = error(_("could not remove '%s'"), buf.buf);
        strbuf_release(&buf);
  
 -      return 0;
 +      return ret;
  }
  
  static const char *action_name(const struct replay_opts *opts)
@@@ -391,8 -384,8 +391,8 @@@ static void print_advice(struct reposit
        }
  }
  
 -int write_message(const void *buf, size_t len, const char *filename,
 -                int append_eol)
 +static int write_message(const void *buf, size_t len, const char *filename,
 +                       int append_eol)
  {
        struct lock_file msg_file = LOCK_INIT;
  
@@@ -518,54 -511,11 +518,54 @@@ static int fast_forward_to(struct repos
        return 0;
  }
  
 +enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
 +      int use_editor)
 +{
 +      if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
 +              return use_editor ? COMMIT_MSG_CLEANUP_ALL :
 +                                  COMMIT_MSG_CLEANUP_SPACE;
 +      else if (!strcmp(cleanup_arg, "verbatim"))
 +              return COMMIT_MSG_CLEANUP_NONE;
 +      else if (!strcmp(cleanup_arg, "whitespace"))
 +              return COMMIT_MSG_CLEANUP_SPACE;
 +      else if (!strcmp(cleanup_arg, "strip"))
 +              return COMMIT_MSG_CLEANUP_ALL;
 +      else if (!strcmp(cleanup_arg, "scissors"))
 +              return use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
 +                                  COMMIT_MSG_CLEANUP_SPACE;
 +      else
 +              die(_("Invalid cleanup mode %s"), cleanup_arg);
 +}
 +
 +/*
 + * NB using int rather than enum cleanup_mode to stop clang's
 + * -Wtautological-constant-out-of-range-compare complaining that the comparison
 + * is always true.
 + */
 +static const char *describe_cleanup_mode(int cleanup_mode)
 +{
 +      static const char *modes[] = { "whitespace",
 +                                     "verbatim",
 +                                     "scissors",
 +                                     "strip" };
 +
 +      if (cleanup_mode < ARRAY_SIZE(modes))
 +              return modes[cleanup_mode];
 +
 +      BUG("invalid cleanup_mode provided (%d)", cleanup_mode);
 +}
 +
  void append_conflicts_hint(struct index_state *istate,
 -                         struct strbuf *msgbuf)
 +      struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)
  {
        int i;
  
 +      if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
 +              strbuf_addch(msgbuf, '\n');
 +              wt_status_append_cut_line(msgbuf);
 +              strbuf_addch(msgbuf, comment_line_char);
 +      }
 +
        strbuf_addch(msgbuf, '\n');
        strbuf_commented_addf(msgbuf, "Conflicts:\n");
        for (i = 0; i < istate->cache_nr;) {
@@@ -633,8 -583,7 +633,8 @@@ static int do_recursive_merge(struct re
                        _(action_name(opts)));
  
        if (!clean)
 -              append_conflicts_hint(r->index, msgbuf);
 +              append_conflicts_hint(r->index, msgbuf,
 +                                    opts->default_msg_cleanup);
  
        return !clean;
  }
@@@ -770,7 -719,7 +770,7 @@@ static int parse_key_value_squoted(cha
   *    GIT_AUTHOR_DATE='$author_date'
   *
   * where $author_name, $author_email and $author_date are quoted. We are strict
 - * with our parsing, as the file was meant to be eval'd in the old
 + * with our parsing, as the file was meant to be eval'd in the now-removed
   * git-am.sh/git-rebase--interactive.sh scripts, and thus if the file differs
   * from what this function expects, it is better to bail out than to do
   * something that the user does not expect.
@@@ -953,6 -902,7 +953,6 @@@ static int run_git_commit(struct reposi
                          unsigned int flags)
  {
        struct child_process cmd = CHILD_PROCESS_INIT;
 -      const char *value;
  
        if ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) {
                struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;
                argv_array_push(&cmd.args, "-e");
        else if (!(flags & CLEANUP_MSG) &&
                 !opts->signoff && !opts->record_origin &&
 -               git_config_get_value("commit.cleanup", &value))
 +               !opts->explicit_cleanup)
                argv_array_push(&cmd.args, "--cleanup=verbatim");
  
        if ((flags & ALLOW_EMPTY))
@@@ -1063,16 -1013,6 +1063,16 @@@ static int rest_is_empty(const struct s
        return 1;
  }
  
 +void cleanup_message(struct strbuf *msgbuf,
 +      enum commit_msg_cleanup_mode cleanup_mode, int verbose)
 +{
 +      if (verbose || /* Truncate the message just before the diff, if any. */
 +          cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 +              strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
 +      if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
 +              strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 +}
 +
  /*
   * Find out if the message in the strbuf contains only whitespace and
   * Signed-off-by lines.
@@@ -1443,13 -1383,8 +1443,13 @@@ static int try_to_commit(struct reposit
                msg = &commit_msg;
        }
  
 -      cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
 -                                        opts->default_msg_cleanup;
 +      if (flags & CLEANUP_MSG)
 +              cleanup = COMMIT_MSG_CLEANUP_ALL;
 +      else if ((opts->signoff || opts->record_origin) &&
 +               !opts->explicit_cleanup)
 +              cleanup = COMMIT_MSG_CLEANUP_SPACE;
 +      else
 +              cleanup = opts->default_msg_cleanup;
  
        if (cleanup != COMMIT_MSG_CLEANUP_NONE)
                strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
@@@ -1578,6 -1513,32 +1578,6 @@@ static int allow_empty(struct repositor
                return 1;
  }
  
 -/*
 - * Note that ordering matters in this enum. Not only must it match the mapping
 - * below, it is also divided into several sections that matter.  When adding
 - * new commands, make sure you add it in the right section.
 - */
 -enum todo_command {
 -      /* commands that handle commits */
 -      TODO_PICK = 0,
 -      TODO_REVERT,
 -      TODO_EDIT,
 -      TODO_REWORD,
 -      TODO_FIXUP,
 -      TODO_SQUASH,
 -      /* commands that do something else than handling a single commit */
 -      TODO_EXEC,
 -      TODO_BREAK,
 -      TODO_LABEL,
 -      TODO_RESET,
 -      TODO_MERGE,
 -      /* commands that do nothing but are counted for reporting progress */
 -      TODO_NOOP,
 -      TODO_DROP,
 -      /* comments (not counted for reporting progress) */
 -      TODO_COMMENT
 -};
 -
  static struct {
        char c;
        const char *str;
@@@ -2060,7 -2021,26 +2060,7 @@@ enum todo_item_flags 
        TODO_EDIT_MERGE_MSG = 1
  };
  
 -struct todo_item {
 -      enum todo_command command;
 -      struct commit *commit;
 -      unsigned int flags;
 -      const char *arg;
 -      int arg_len;
 -      size_t offset_in_buf;
 -};
 -
 -struct todo_list {
 -      struct strbuf buf;
 -      struct todo_item *items;
 -      int nr, alloc, current;
 -      int done_nr, total_nr;
 -      struct stat_data stat;
 -};
 -
 -#define TODO_LIST_INIT { STRBUF_INIT }
 -
 -static void todo_list_release(struct todo_list *todo_list)
 +void todo_list_release(struct todo_list *todo_list)
  {
        strbuf_release(&todo_list->buf);
        FREE_AND_NULL(todo_list->items);
@@@ -2073,14 -2053,8 +2073,14 @@@ static struct todo_item *append_new_tod
        return todo_list->items + todo_list->nr++;
  }
  
 +const char *todo_item_get_arg(struct todo_list *todo_list,
 +                            struct todo_item *item)
 +{
 +      return todo_list->buf.buf + item->arg_offset;
 +}
 +
  static int parse_insn_line(struct repository *r, struct todo_item *item,
 -                         const char *bol, char *eol)
 +                         const char *buf, const char *bol, char *eol)
  {
        struct object_id commit_oid;
        char *end_of_object_name;
        if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
                item->command = TODO_COMMENT;
                item->commit = NULL;
 -              item->arg = bol;
 +              item->arg_offset = bol - buf;
                item->arg_len = eol - bol;
                return 0;
        }
                        return error(_("%s does not accept arguments: '%s'"),
                                     command_to_string(item->command), bol);
                item->commit = NULL;
 -              item->arg = bol;
 +              item->arg_offset = bol - buf;
                item->arg_len = eol - bol;
                return 0;
        }
        if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
            item->command == TODO_RESET) {
                item->commit = NULL;
 -              item->arg = bol;
 +              item->arg_offset = bol - buf;
                item->arg_len = (int)(eol - bol);
                return 0;
        }
                } else {
                        item->flags |= TODO_EDIT_MERGE_MSG;
                        item->commit = NULL;
 -                      item->arg = bol;
 +                      item->arg_offset = bol - buf;
                        item->arg_len = (int)(eol - bol);
                        return 0;
                }
        status = get_oid(bol, &commit_oid);
        *end_of_object_name = saved;
  
 -      item->arg = end_of_object_name + strspn(end_of_object_name, " \t");
 -      item->arg_len = (int)(eol - item->arg);
 +      bol = end_of_object_name + strspn(end_of_object_name, " \t");
 +      item->arg_offset = bol - buf;
 +      item->arg_len = (int)(eol - bol);
  
        if (status < 0)
 -              return -1;
 +              return error(_("could not parse '%.*s'"),
 +                           (int)(end_of_object_name - bol), bol);
  
        item->commit = lookup_commit_reference(r, &commit_oid);
        return !item->commit;
  }
  
 -static int parse_insn_buffer(struct repository *r, char *buf,
 -                           struct todo_list *todo_list)
 +int sequencer_get_last_command(struct repository *r, enum replay_action *action)
 +{
 +      struct todo_item item;
 +      char *eol;
 +      const char *todo_file;
 +      struct strbuf buf = STRBUF_INIT;
 +      int ret = -1;
 +
 +      todo_file = git_path_todo_file();
 +      if (strbuf_read_file(&buf, todo_file, 0) < 0) {
 +              if (errno == ENOENT)
 +                      return -1;
 +              else
 +                      return error_errno("unable to open '%s'", todo_file);
 +      }
 +      eol = strchrnul(buf.buf, '\n');
 +      if (buf.buf != eol && eol[-1] == '\r')
 +              eol--; /* strip Carriage Return */
 +      if (parse_insn_line(r, &item, buf.buf, buf.buf, eol))
 +              goto fail;
 +      if (item.command == TODO_PICK)
 +              *action = REPLAY_PICK;
 +      else if (item.command == TODO_REVERT)
 +              *action = REPLAY_REVERT;
 +      else
 +              goto fail;
 +
 +      ret = 0;
 +
 + fail:
 +      strbuf_release(&buf);
 +
 +      return ret;
 +}
 +
 +int todo_list_parse_insn_buffer(struct repository *r, char *buf,
 +                              struct todo_list *todo_list)
  {
        struct todo_item *item;
        char *p = buf, *next_p;
        int i, res = 0, fixup_okay = file_exists(rebase_path_done());
  
 +      todo_list->current = todo_list->nr = 0;
 +
        for (i = 1; *p; i++, p = next_p) {
                char *eol = strchrnul(p, '\n');
  
  
                item = append_new_todo(todo_list);
                item->offset_in_buf = p - todo_list->buf.buf;
 -              if (parse_insn_line(r, item, p, eol)) {
 +              if (parse_insn_line(r, item, buf, p, eol)) {
                        res = error(_("invalid line %d: %.*s"),
                                i, (int)(eol - p), p);
 -                      item->command = TODO_NOOP;
 +                      item->command = TODO_COMMENT + 1;
 +                      item->arg_offset = p - buf;
 +                      item->arg_len = (int)(eol - p);
 +                      item->commit = NULL;
                }
  
                if (fixup_okay)
@@@ -2289,57 -2221,6 +2289,59 @@@ static ssize_t strbuf_read_file_or_whin
        return len;
  }
  
- void sequencer_post_commit_cleanup(struct repository *r)
 +static int have_finished_the_last_pick(void)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      const char *eol;
 +      const char *todo_path = git_path_todo_file();
 +      int ret = 0;
 +
 +      if (strbuf_read_file(&buf, todo_path, 0) < 0) {
 +              if (errno == ENOENT) {
 +                      return 0;
 +              } else {
 +                      error_errno("unable to open '%s'", todo_path);
 +                      return 0;
 +              }
 +      }
 +      /* If there is only one line then we are done */
 +      eol = strchr(buf.buf, '\n');
 +      if (!eol || !eol[1])
 +              ret = 1;
 +
 +      strbuf_release(&buf);
 +
 +      return ret;
 +}
 +
-               unlink(git_path_cherry_pick_head(r));
++void sequencer_post_commit_cleanup(struct repository *r, int verbose)
 +{
 +      struct replay_opts opts = REPLAY_OPTS_INIT;
 +      int need_cleanup = 0;
 +
 +      if (file_exists(git_path_cherry_pick_head(r))) {
-               unlink(git_path_revert_head(r));
++              if (!unlink(git_path_cherry_pick_head(r)) && verbose)
++                      warning(_("cancelling a cherry picking in progress"));
 +              opts.action = REPLAY_PICK;
 +              need_cleanup = 1;
 +      }
 +
 +      if (file_exists(git_path_revert_head(r))) {
++              if (!unlink(git_path_revert_head(r)) && verbose)
++                      warning(_("cancelling a revert in progress"));
 +              opts.action = REPLAY_REVERT;
 +              need_cleanup = 1;
 +      }
 +
 +      if (!need_cleanup)
 +              return;
 +
 +      if (!have_finished_the_last_pick())
 +              return;
 +
 +      sequencer_remove_state(&opts);
 +}
 +
  static int read_populate_todo(struct repository *r,
                              struct todo_list *todo_list,
                              struct replay_opts *opts)
                return error(_("could not stat '%s'"), todo_file);
        fill_stat_data(&todo_list->stat, &st);
  
 -      res = parse_insn_buffer(r, todo_list->buf.buf, todo_list);
 +      res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
        if (res) {
                if (is_rebase_i(opts))
                        return error(_("please fix this using "
                FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
  
                if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
 -                  !parse_insn_buffer(r, done.buf.buf, &done))
 +                  !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
                        todo_list->done_nr = count_commands(&done);
                else
                        todo_list->done_nr = 0;
@@@ -2427,15 -2308,6 +2429,15 @@@ static int populate_opts_cb(const char 
                opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
        else if (!strcmp(key, "options.edit"))
                opts->edit = git_config_bool_or_int(key, value, &error_flag);
 +      else if (!strcmp(key, "options.allow-empty"))
 +              opts->allow_empty =
 +                      git_config_bool_or_int(key, value, &error_flag);
 +      else if (!strcmp(key, "options.allow-empty-message"))
 +              opts->allow_empty_message =
 +                      git_config_bool_or_int(key, value, &error_flag);
 +      else if (!strcmp(key, "options.keep-redundant-commits"))
 +              opts->keep_redundant_commits =
 +                      git_config_bool_or_int(key, value, &error_flag);
        else if (!strcmp(key, "options.signoff"))
                opts->signoff = git_config_bool_or_int(key, value, &error_flag);
        else if (!strcmp(key, "options.record-origin"))
                opts->allow_rerere_auto =
                        git_config_bool_or_int(key, value, &error_flag) ?
                                RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
 -      else
 +      else if (!strcmp(key, "options.default-msg-cleanup")) {
 +              opts->explicit_cleanup = 1;
 +              opts->default_msg_cleanup = get_cleanup_mode(value, 1);
 +      } else
                return error(_("invalid key: %s"), key);
  
        if (!error_flag)
@@@ -2583,15 -2452,14 +2585,15 @@@ static void write_strategy_opts(struct 
  }
  
  int write_basic_state(struct replay_opts *opts, const char *head_name,
 -                    const char *onto, const char *orig_head)
 +                    struct commit *onto, const char *orig_head)
  {
        const char *quiet = getenv("GIT_QUIET");
  
        if (head_name)
                write_file(rebase_path_head_name(), "%s\n", head_name);
        if (onto)
 -              write_file(rebase_path_onto(), "%s\n", onto);
 +              write_file(rebase_path_onto(), "%s\n",
 +                         oid_to_hex(&onto->object.oid));
        if (orig_head)
                write_file(rebase_path_orig_head(), "%s\n", orig_head);
  
@@@ -2638,7 -2506,7 +2640,7 @@@ static int walk_revs_populate_todo(stru
  
                item->command = command;
                item->commit = commit;
 -              item->arg = NULL;
 +              item->arg_offset = 0;
                item->arg_len = 0;
                item->offset_in_buf = todo_list->buf.len;
                subject_len = find_commit_subject(commit_buffer, &subject);
@@@ -2833,59 -2701,36 +2835,59 @@@ static int save_opts(struct replay_opt
        int res = 0;
  
        if (opts->no_commit)
 -              res |= git_config_set_in_file_gently(opts_file, "options.no-commit", "true");
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.no-commit", "true");
        if (opts->edit)
 -              res |= git_config_set_in_file_gently(opts_file, "options.edit", "true");
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.edit", "true");
 +      if (opts->allow_empty)
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.allow-empty", "true");
 +      if (opts->allow_empty_message)
 +              res |= git_config_set_in_file_gently(opts_file,
 +                              "options.allow-empty-message", "true");
 +      if (opts->keep_redundant_commits)
 +              res |= git_config_set_in_file_gently(opts_file,
 +                              "options.keep-redundant-commits", "true");
        if (opts->signoff)
 -              res |= git_config_set_in_file_gently(opts_file, "options.signoff", "true");
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.signoff", "true");
        if (opts->record_origin)
 -              res |= git_config_set_in_file_gently(opts_file, "options.record-origin", "true");
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.record-origin", "true");
        if (opts->allow_ff)
 -              res |= git_config_set_in_file_gently(opts_file, "options.allow-ff", "true");
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.allow-ff", "true");
        if (opts->mainline) {
                struct strbuf buf = STRBUF_INIT;
                strbuf_addf(&buf, "%d", opts->mainline);
 -              res |= git_config_set_in_file_gently(opts_file, "options.mainline", buf.buf);
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.mainline", buf.buf);
                strbuf_release(&buf);
        }
        if (opts->strategy)
 -              res |= git_config_set_in_file_gently(opts_file, "options.strategy", opts->strategy);
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.strategy", opts->strategy);
        if (opts->gpg_sign)
 -              res |= git_config_set_in_file_gently(opts_file, "options.gpg-sign", opts->gpg_sign);
 +              res |= git_config_set_in_file_gently(opts_file,
 +                                      "options.gpg-sign", opts->gpg_sign);
        if (opts->xopts) {
                int i;
                for (i = 0; i < opts->xopts_nr; i++)
                        res |= git_config_set_multivar_in_file_gently(opts_file,
 -                                                      "options.strategy-option",
 -                                                      opts->xopts[i], "^$", 0);
 +                                      "options.strategy-option",
 +                                      opts->xopts[i], "^$", 0);
        }
        if (opts->allow_rerere_auto)
 -              res |= git_config_set_in_file_gently(opts_file, "options.allow-rerere-auto",
 -                                                   opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
 -                                                   "true" : "false");
 +              res |= git_config_set_in_file_gently(opts_file,
 +                              "options.allow-rerere-auto",
 +                              opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
 +                              "true" : "false");
 +
 +      if (opts->explicit_cleanup)
 +              res |= git_config_set_in_file_gently(opts_file,
 +                              "options.default-msg-cleanup",
 +                              describe_cleanup_mode(opts->default_msg_cleanup));
        return res;
  }
  
@@@ -3404,10 -3249,6 +3406,10 @@@ static int do_merge(struct repository *
                rollback_lock_file(&lock);
                ret = fast_forward_to(r, &commit->object.oid,
                                      &head_commit->object.oid, 0, opts);
 +              if (flags & TODO_EDIT_MERGE_MSG) {
 +                      run_commit_flags |= AMEND_MSG;
 +                      goto fast_forward_edit;
 +              }
                goto leave_merge;
        }
  
                 * value (a negative one would indicate that the `merge`
                 * command needs to be rescheduled).
                 */
 +      fast_forward_edit:
                ret = !!run_git_commit(r, git_path_merge_msg(r), opts,
                                       run_commit_flags);
  
@@@ -3612,11 -3452,10 +3614,11 @@@ static const char *reflog_message(struc
        return buf.buf;
  }
  
 -static int run_git_checkout(struct replay_opts *opts, const char *commit,
 -                          const char *action)
 +static int run_git_checkout(struct repository *r, struct replay_opts *opts,
 +                          const char *commit, const char *action)
  {
        struct child_process cmd = CHILD_PROCESS_INIT;
 +      int ret;
  
        cmd.git_cmd = 1;
  
        argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
  
        if (opts->verbose)
 -              return run_command(&cmd);
 +              ret = run_command(&cmd);
        else
 -              return run_command_silent_on_success(&cmd);
 +              ret = run_command_silent_on_success(&cmd);
 +
 +      if (!ret)
 +              discard_index(r->index);
 +
 +      return ret;
  }
  
 -int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit)
 +int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
 +                               const char *commit)
  {
        const char *action;
  
        if (commit && *commit) {
                action = reflog_message(opts, "start", "checkout %s", commit);
 -              if (run_git_checkout(opts, commit, action))
 +              if (run_git_checkout(r, opts, commit, action))
                        return error(_("could not checkout %s"), commit);
        }
  
        return 0;
  }
  
 -static int checkout_onto(struct replay_opts *opts,
 -                       const char *onto_name, const char *onto,
 +static int checkout_onto(struct repository *r, struct replay_opts *opts,
 +                       const char *onto_name, const struct object_id *onto,
                         const char *orig_head)
  {
        struct object_id oid;
        if (get_oid(orig_head, &oid))
                return error(_("%s: not a valid OID"), orig_head);
  
 -      if (run_git_checkout(opts, onto, action)) {
 +      if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
                apply_autostash(opts);
                sequencer_remove_state(opts);
                return error(_("could not detach HEAD"));
@@@ -3712,8 -3545,6 +3714,8 @@@ static int pick_commits(struct reposito
  
        while (todo_list->current < todo_list->nr) {
                struct todo_item *item = todo_list->items + todo_list->current;
 +              const char *arg = todo_item_get_arg(todo_list, item);
 +
                if (save_todo(todo_list, opts))
                        return -1;
                if (is_rebase_i(opts)) {
                                        fprintf(stderr,
                                                _("Stopped at %s...  %.*s\n"),
                                                short_commit_name(commit),
 -                                              item->arg_len, item->arg);
 +                                              item->arg_len, arg);
                                return error_with_patch(r, commit,
 -                                      item->arg, item->arg_len, opts, res,
 -                                      !res);
 +                                      arg, item->arg_len, opts, res, !res);
                        }
                        if (is_rebase_i(opts) && !res)
                                record_in_rewritten(&item->commit->object.oid,
                                if (res == 1)
                                        intend_to_amend();
                                return error_failed_squash(r, item->commit, opts,
 -                                      item->arg_len, item->arg);
 +                                      item->arg_len, arg);
                        } else if (res && is_rebase_i(opts) && item->commit) {
                                int to_amend = 0;
                                struct object_id oid;
                                        to_amend = 1;
  
                                return res | error_with_patch(r, item->commit,
 -                                              item->arg, item->arg_len, opts,
 +                                              arg, item->arg_len, opts,
                                                res, to_amend);
                        }
                } else if (item->command == TODO_EXEC) {
 -                      char *end_of_arg = (char *)(item->arg + item->arg_len);
 +                      char *end_of_arg = (char *)(arg + item->arg_len);
                        int saved = *end_of_arg;
                        struct stat st;
  
                        *end_of_arg = '\0';
 -                      res = do_exec(r, item->arg);
 +                      res = do_exec(r, arg);
                        *end_of_arg = saved;
  
 -                      /* Reread the todo file if it has changed. */
                        if (res) {
                                if (opts->reschedule_failed_exec)
                                        reschedule = 1;
                                res = error_errno(_("could not stat '%s'"),
                                                  get_todo_path(opts));
                        else if (match_stat_data(&todo_list->stat, &st)) {
 +                              /* Reread the todo file if it has changed. */
                                todo_list_release(todo_list);
                                if (read_populate_todo(r, todo_list, opts))
                                        res = -1; /* message was printed */
                                todo_list->current = -1;
                        }
                } else if (item->command == TODO_LABEL) {
 -                      if ((res = do_label(r, item->arg, item->arg_len)))
 +                      if ((res = do_label(r, arg, item->arg_len)))
                                reschedule = 1;
                } else if (item->command == TODO_RESET) {
 -                      if ((res = do_reset(r, item->arg, item->arg_len, opts)))
 +                      if ((res = do_reset(r, arg, item->arg_len, opts)))
                                reschedule = 1;
                } else if (item->command == TODO_MERGE) {
                        if ((res = do_merge(r, item->commit,
 -                                          item->arg, item->arg_len,
 +                                          arg, item->arg_len,
                                            item->flags, opts)) < 0)
                                reschedule = 1;
                        else if (item->commit)
                        if (res > 0)
                                /* failed with merge conflicts */
                                return error_with_patch(r, item->commit,
 -                                                      item->arg,
 -                                                      item->arg_len, opts,
 -                                                      res, 0);
 +                                                      arg, item->arg_len,
 +                                                      opts, res, 0);
                } else if (!is_noop(item->command))
                        return error(_("unknown command %d"), item->command);
  
                        if (item->commit)
                                return error_with_patch(r,
                                                        item->commit,
 -                                                      item->arg,
 -                                                      item->arg_len, opts,
 -                                                      res, 0);
 +                                                      arg, item->arg_len,
 +                                                      opts, res, 0);
                }
  
                todo_list->current++;
@@@ -4433,7 -4267,7 +4435,7 @@@ static const char *label_oid(struct obj
  }
  
  static int make_script_with_merges(struct pretty_print_context *pp,
 -                                 struct rev_info *revs, FILE *out,
 +                                 struct rev_info *revs, struct strbuf *out,
                                   unsigned flags)
  {
        int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
         * 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);
 +      strbuf_addf(out, "%s onto\n", cmd_label);
        for (iter = tips; iter; iter = iter->next) {
                struct commit_list *list = NULL, *iter2;
  
                entry = oidmap_get(&state.commit2label, &commit->object.oid);
  
                if (entry)
 -                      fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
 +                      strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
                else
 -                      fprintf(out, "\n");
 +                      strbuf_addch(out, '\n');
  
                while (oidset_contains(&interesting, &commit->object.oid) &&
                       !oidset_contains(&shown, &commit->object.oid)) {
                }
  
                if (!commit)
 -                      fprintf(out, "%s %s\n", cmd_reset,
 -                              rebase_cousins ? "onto" : "[new root]");
 +                      strbuf_addf(out, "%s %s\n", cmd_reset,
 +                                  rebase_cousins ? "onto" : "[new root]");
                else {
                        const char *to = NULL;
  
                                               &state);
  
                        if (!to || !strcmp(to, "onto"))
 -                              fprintf(out, "%s onto\n", cmd_reset);
 +                              strbuf_addf(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);
 +                              strbuf_addf(out, "%s %s # %s\n",
 +                                          cmd_reset, to, oneline.buf);
                        }
                }
  
                        entry = oidmap_get(&commit2todo, oid);
                        /* only show if not already upstream */
                        if (entry)
 -                              fprintf(out, "%s\n", entry->string);
 +                              strbuf_addf(out, "%s\n", entry->string);
                        entry = oidmap_get(&state.commit2label, oid);
                        if (entry)
 -                              fprintf(out, "%s %s\n",
 -                                      cmd_label, entry->string);
 +                              strbuf_addf(out, "%s %s\n",
 +                                          cmd_label, entry->string);
                        oidset_insert(&shown, oid);
                }
  
        return 0;
  }
  
 -int sequencer_make_script(struct repository *r, FILE *out,
 -                        int argc, const char **argv,
 -                        unsigned flags)
 +int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 +                        const char **argv, unsigned flags)
  {
        char *format = NULL;
        struct pretty_print_context pp = {0};
 -      struct strbuf buf = STRBUF_INIT;
        struct rev_info revs;
        struct commit *commit;
        int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
  
                if (!is_empty && (commit->object.flags & PATCHSAME))
                        continue;
 -              strbuf_reset(&buf);
                if (!keep_empty && is_empty)
 -                      strbuf_addf(&buf, "%c ", comment_line_char);
 -              strbuf_addf(&buf, "%s %s ", insn,
 +                      strbuf_addf(out, "%c ", comment_line_char);
 +              strbuf_addf(out, "%s %s ", insn,
                            oid_to_hex(&commit->object.oid));
 -              pretty_print_commit(&pp, commit, &buf);
 -              strbuf_addch(&buf, '\n');
 -              fputs(buf.buf, out);
 +              pretty_print_commit(&pp, commit, out);
 +              strbuf_addch(out, '\n');
        }
 -      strbuf_release(&buf);
        return 0;
  }
  
   * Add commands after pick and (series of) squash/fixup commands
   * in the todo list.
   */
 -int sequencer_add_exec_commands(struct repository *r,
 -                              const char *commands)
 +void todo_list_add_exec_commands(struct todo_list *todo_list,
 +                               struct string_list *commands)
  {
 -      const char *todo_file = rebase_path_todo();
 -      struct todo_list todo_list = TODO_LIST_INIT;
 -      struct strbuf *buf = &todo_list.buf;
 -      size_t offset = 0, commands_len = strlen(commands);
 -      int i, insert;
 +      struct strbuf *buf = &todo_list->buf;
 +      size_t base_offset = buf->len;
 +      int i, insert, nr = 0, alloc = 0;
 +      struct todo_item *items = NULL, *base_items = NULL;
  
 -      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 -              return error(_("could not read '%s'."), todo_file);
 +      base_items = xcalloc(commands->nr, sizeof(struct todo_item));
 +      for (i = 0; i < commands->nr; i++) {
 +              size_t command_len = strlen(commands->items[i].string);
  
 -      if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
 -              todo_list_release(&todo_list);
 -              return error(_("unusable todo list: '%s'"), todo_file);
 +              strbuf_addstr(buf, commands->items[i].string);
 +              strbuf_addch(buf, '\n');
 +
 +              base_items[i].command = TODO_EXEC;
 +              base_items[i].offset_in_buf = base_offset;
 +              base_items[i].arg_offset = base_offset + strlen("exec ");
 +              base_items[i].arg_len = command_len - strlen("exec ");
 +
 +              base_offset += command_len + 1;
        }
  
        /*
         * Insert <commands> after every pick. Here, fixup/squash chains
         * are considered part of the pick, so we insert the commands *after*
         * those chains if there are any.
 +       *
 +       * As we insert the exec commands immediatly after rearranging
 +       * any fixups and before the user edits the list, a fixup chain
 +       * can never contain comments (any comments are empty picks that
 +       * have been commented out because the user did not specify
 +       * --keep-empty).  So, it is safe to insert an exec command
 +       * without looking at the command following a comment.
         */
 -      insert = -1;
 -      for (i = 0; i < todo_list.nr; i++) {
 -              enum todo_command command = todo_list.items[i].command;
 -
 -              if (insert >= 0) {
 -                      /* skip fixup/squash chains */
 -                      if (command == TODO_COMMENT)
 -                              continue;
 -                      else if (is_fixup(command)) {
 -                              insert = i + 1;
 -                              continue;
 -                      }
 -                      strbuf_insert(buf,
 -                                    todo_list.items[insert].offset_in_buf +
 -                                    offset, commands, commands_len);
 -                      offset += commands_len;
 -                      insert = -1;
 +      insert = 0;
 +      for (i = 0; i < todo_list->nr; i++) {
 +              enum todo_command command = todo_list->items[i].command;
 +              if (insert && !is_fixup(command)) {
 +                      ALLOC_GROW(items, nr + commands->nr, alloc);
 +                      COPY_ARRAY(items + nr, base_items, commands->nr);
 +                      nr += commands->nr;
 +
 +                      insert = 0;
                }
  
 +              ALLOC_GROW(items, nr + 1, alloc);
 +              items[nr++] = todo_list->items[i];
 +
                if (command == TODO_PICK || command == TODO_MERGE)
 -                      insert = i + 1;
 +                      insert = 1;
        }
  
        /* insert or append final <commands> */
 -      if (insert >= 0 && insert < todo_list.nr)
 -              strbuf_insert(buf, todo_list.items[insert].offset_in_buf +
 -                            offset, commands, commands_len);
 -      else if (insert >= 0 || !offset)
 -              strbuf_add(buf, commands, commands_len);
 +      if (insert || nr == todo_list->nr) {
 +              ALLOC_GROW(items, nr + commands->nr, alloc);
 +              COPY_ARRAY(items + nr, base_items, commands->nr);
 +              nr += commands->nr;
 +      }
  
 -      i = write_message(buf->buf, buf->len, todo_file, 0);
 -      todo_list_release(&todo_list);
 -      return i;
 +      free(base_items);
 +      FREE_AND_NULL(todo_list->items);
 +      todo_list->items = items;
 +      todo_list->nr = nr;
 +      todo_list->alloc = alloc;
  }
  
 -int transform_todos(struct repository *r, unsigned flags)
 +static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
 +                              struct strbuf *buf, int num, unsigned flags)
  {
 -      const char *todo_file = rebase_path_todo();
 -      struct todo_list todo_list = TODO_LIST_INIT;
 -      struct strbuf buf = STRBUF_INIT;
        struct todo_item *item;
 -      int i;
 -
 -      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 -              return error(_("could not read '%s'."), todo_file);
 +      int i, max = todo_list->nr;
  
 -      if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
 -              todo_list_release(&todo_list);
 -              return error(_("unusable todo list: '%s'"), todo_file);
 -      }
 +      if (num > 0 && num < max)
 +              max = num;
  
 -      for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
 +      for (item = todo_list->items, i = 0; i < max; i++, item++) {
                /* if the item is not a command write it and continue */
                if (item->command >= TODO_COMMENT) {
 -                      strbuf_addf(&buf, "%.*s\n", item->arg_len, item->arg);
 +                      strbuf_addf(buf, "%.*s\n", item->arg_len,
 +                                  todo_item_get_arg(todo_list, item));
                        continue;
                }
  
                /* add command to the buffer */
                if (flags & TODO_LIST_ABBREVIATE_CMDS)
 -                      strbuf_addch(&buf, command_to_char(item->command));
 +                      strbuf_addch(buf, command_to_char(item->command));
                else
 -                      strbuf_addstr(&buf, command_to_string(item->command));
 +                      strbuf_addstr(buf, command_to_string(item->command));
  
                /* add commit id */
                if (item->commit) {
  
                        if (item->command == TODO_MERGE) {
                                if (item->flags & TODO_EDIT_MERGE_MSG)
 -                                      strbuf_addstr(&buf, " -c");
 +                                      strbuf_addstr(buf, " -c");
                                else
 -                                      strbuf_addstr(&buf, " -C");
 +                                      strbuf_addstr(buf, " -C");
                        }
  
 -                      strbuf_addf(&buf, " %s", oid);
 +                      strbuf_addf(buf, " %s", oid);
                }
  
                /* add all the rest */
                if (!item->arg_len)
 -                      strbuf_addch(&buf, '\n');
 +                      strbuf_addch(buf, '\n');
                else
 -                      strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
 +                      strbuf_addf(buf, " %.*s\n", item->arg_len,
 +                                  todo_item_get_arg(todo_list, item));
        }
 -
 -      i = write_message(buf.buf, buf.len, todo_file, 0);
 -      todo_list_release(&todo_list);
 -      return i;
  }
  
 -enum missing_commit_check_level get_missing_commit_check_level(void)
 +int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
 +                          const char *file, const char *shortrevisions,
 +                          const char *shortonto, int num, unsigned flags)
  {
 -      const char *value;
 -
 -      if (git_config_get_value("rebase.missingcommitscheck", &value) ||
 -                      !strcasecmp("ignore", value))
 -              return MISSING_COMMIT_CHECK_IGNORE;
 -      if (!strcasecmp("warn", value))
 -              return MISSING_COMMIT_CHECK_WARN;
 -      if (!strcasecmp("error", value))
 -              return MISSING_COMMIT_CHECK_ERROR;
 -      warning(_("unrecognized setting %s for option "
 -                "rebase.missingCommitsCheck. Ignoring."), value);
 -      return MISSING_COMMIT_CHECK_IGNORE;
 -}
 +      int res;
 +      struct strbuf buf = STRBUF_INIT;
  
 -define_commit_slab(commit_seen, unsigned char);
 -/*
 - * Check if the user dropped some commits by mistake
 - * Behaviour determined by rebase.missingCommitsCheck.
 - * Check if there is an unrecognized command or a
 - * bad SHA-1 in a command.
 - */
 -int check_todo_list(struct repository *r)
 -{
 -      enum missing_commit_check_level check_level = get_missing_commit_check_level();
 -      struct strbuf todo_file = STRBUF_INIT;
 -      struct todo_list todo_list = TODO_LIST_INIT;
 -      struct strbuf missing = STRBUF_INIT;
 -      int advise_to_edit_todo = 0, res = 0, i;
 -      struct commit_seen commit_seen;
 +      todo_list_to_strbuf(r, todo_list, &buf, num, flags);
 +      if (flags & TODO_LIST_APPEND_TODO_HELP)
 +              append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
 +                               shortrevisions, shortonto, &buf);
  
 -      init_commit_seen(&commit_seen);
 +      res = write_message(buf.buf, buf.len, file, 0);
 +      strbuf_release(&buf);
  
 -      strbuf_addstr(&todo_file, rebase_path_todo());
 -      if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
 -              res = -1;
 -              goto leave_check;
 -      }
 -      advise_to_edit_todo = res =
 -              parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
 +      return res;
 +}
  
 -      if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
 -              goto leave_check;
 +static const char edit_todo_list_advice[] =
 +N_("You can fix this with 'git rebase --edit-todo' "
 +"and then run 'git rebase --continue'.\n"
 +"Or you can abort the rebase with 'git rebase"
 +" --abort'.\n");
  
 -      /* Mark the commits in git-rebase-todo as seen */
 -      for (i = 0; i < todo_list.nr; i++) {
 -              struct commit *commit = todo_list.items[i].commit;
 -              if (commit)
 -                      *commit_seen_at(&commit_seen, commit) = 1;
 -      }
 +int check_todo_list_from_file(struct repository *r)
 +{
 +      struct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;
 +      int res = 0;
  
 -      todo_list_release(&todo_list);
 -      strbuf_addstr(&todo_file, ".backup");
 -      if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
 +      if (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {
                res = -1;
 -              goto leave_check;
 -      }
 -      strbuf_release(&todo_file);
 -      res = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
 -
 -      /* Find commits in git-rebase-todo.backup yet unseen */
 -      for (i = todo_list.nr - 1; i >= 0; i--) {
 -              struct todo_item *item = todo_list.items + i;
 -              struct commit *commit = item->commit;
 -              if (commit && !*commit_seen_at(&commit_seen, commit)) {
 -                      strbuf_addf(&missing, " - %s %.*s\n",
 -                                  short_commit_name(commit),
 -                                  item->arg_len, item->arg);
 -                      *commit_seen_at(&commit_seen, commit) = 1;
 -              }
 +              goto out;
        }
  
 -      /* Warn about missing commits */
 -      if (!missing.len)
 -              goto leave_check;
 -
 -      if (check_level == MISSING_COMMIT_CHECK_ERROR)
 -              advise_to_edit_todo = res = 1;
 -
 -      fprintf(stderr,
 -              _("Warning: some commits may have been dropped accidentally.\n"
 -              "Dropped commits (newer to older):\n"));
 -
 -      /* Make the list user-friendly and display */
 -      fputs(missing.buf, stderr);
 -      strbuf_release(&missing);
 -
 -      fprintf(stderr, _("To avoid this message, use \"drop\" to "
 -              "explicitly remove a commit.\n\n"
 -              "Use 'git config rebase.missingCommitsCheck' to change "
 -              "the level of warnings.\n"
 -              "The possible behaviours are: ignore, warn, error.\n\n"));
 -
 -leave_check:
 -      clear_commit_seen(&commit_seen);
 -      strbuf_release(&todo_file);
 -      todo_list_release(&todo_list);
 +      if (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {
 +              res = -1;
 +              goto out;
 +      }
  
 -      if (advise_to_edit_todo)
 -              fprintf(stderr,
 -                      _("You can fix this with 'git rebase --edit-todo' "
 -                        "and then run 'git rebase --continue'.\n"
 -                        "Or you can abort the rebase with 'git rebase"
 -                        " --abort'.\n"));
 +      res = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);
 +      if (!res)
 +              res = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);
 +      if (!res)
 +              res = todo_list_check(&old_todo, &new_todo);
 +      if (res)
 +              fprintf(stderr, _(edit_todo_list_advice));
 +out:
 +      todo_list_release(&old_todo);
 +      todo_list_release(&new_todo);
  
        return res;
  }
  
 -static int rewrite_file(const char *path, const char *buf, size_t len)
 -{
 -      int rc = 0;
 -      int fd = open(path, O_WRONLY | O_TRUNC);
 -      if (fd < 0)
 -              return error_errno(_("could not open '%s' for writing"), path);
 -      if (write_in_full(fd, buf, len) < 0)
 -              rc = error_errno(_("could not write to '%s'"), path);
 -      if (close(fd) && !rc)
 -              rc = error_errno(_("could not close '%s'"), path);
 -      return rc;
 -}
 -
  /* skip picking commits whose parents are unchanged */
 -static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)
 +static int skip_unnecessary_picks(struct repository *r,
 +                                struct todo_list *todo_list,
 +                                struct object_id *base_oid)
  {
 -      const char *todo_file = rebase_path_todo();
 -      struct strbuf buf = STRBUF_INIT;
 -      struct todo_list todo_list = TODO_LIST_INIT;
        struct object_id *parent_oid;
 -      int fd, i;
 -
 -      if (!read_oneliner(&buf, rebase_path_onto(), 0))
 -              return error(_("could not read 'onto'"));
 -      if (get_oid(buf.buf, output_oid)) {
 -              strbuf_release(&buf);
 -              return error(_("need a HEAD to fixup"));
 -      }
 -      strbuf_release(&buf);
 -
 -      if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
 -              return -1;
 -      if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
 -              todo_list_release(&todo_list);
 -              return -1;
 -      }
 +      int i;
  
 -      for (i = 0; i < todo_list.nr; i++) {
 -              struct todo_item *item = todo_list.items + i;
 +      for (i = 0; i < todo_list->nr; i++) {
 +              struct todo_item *item = todo_list->items + i;
  
                if (item->command >= TODO_NOOP)
                        continue;
                if (item->command != TODO_PICK)
                        break;
                if (parse_commit(item->commit)) {
 -                      todo_list_release(&todo_list);
                        return error(_("could not parse commit '%s'"),
                                oid_to_hex(&item->commit->object.oid));
                }
                if (item->commit->parents->next)
                        break; /* merge commit */
                parent_oid = &item->commit->parents->item->object.oid;
 -              if (!oideq(parent_oid, output_oid))
 +              if (!oideq(parent_oid, base_oid))
                        break;
 -              oidcpy(output_oid, &item->commit->object.oid);
 +              oidcpy(base_oid, &item->commit->object.oid);
        }
        if (i > 0) {
 -              int offset = get_item_line_offset(&todo_list, i);
                const char *done_path = rebase_path_done();
  
 -              fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
 -              if (fd < 0) {
 -                      error_errno(_("could not open '%s' for writing"),
 -                                  done_path);
 -                      todo_list_release(&todo_list);
 -                      return -1;
 -              }
 -              if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
 +              if (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {
                        error_errno(_("could not write to '%s'"), done_path);
 -                      todo_list_release(&todo_list);
 -                      close(fd);
                        return -1;
                }
 -              close(fd);
  
 -              if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
 -                               todo_list.buf.len - offset) < 0) {
 -                      todo_list_release(&todo_list);
 -                      return -1;
 -              }
 +              MOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);
 +              todo_list->nr -= i;
 +              todo_list->current = 0;
  
 -              todo_list.current = i;
 -              if (is_fixup(peek_command(&todo_list, 0)))
 -                      record_in_rewritten(output_oid, peek_command(&todo_list, 0));
 +              if (is_fixup(peek_command(todo_list, 0)))
 +                      record_in_rewritten(base_oid, peek_command(todo_list, 0));
        }
  
 -      todo_list_release(&todo_list);
 -
        return 0;
  }
  
  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
                    const char *shortrevisions, const char *onto_name,
 -                  const char *onto, const char *orig_head, const char *cmd,
 -                  unsigned autosquash)
 +                  struct commit *onto, const char *orig_head,
 +                  struct string_list *commands, unsigned autosquash,
 +                  struct todo_list *todo_list)
  {
        const char *shortonto, *todo_file = rebase_path_todo();
 -      struct todo_list todo_list = TODO_LIST_INIT;
 -      struct strbuf *buf = &(todo_list.buf);
 -      struct object_id oid;
 -      struct stat st;
 +      struct todo_list new_todo = TODO_LIST_INIT;
 +      struct strbuf *buf = &todo_list->buf;
 +      struct object_id oid = onto->object.oid;
 +      int res;
  
 -      get_oid(onto, &oid);
        shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
  
 -      if (!lstat(todo_file, &st) && st.st_size == 0 &&
 -          write_message("noop\n", 5, todo_file, 0))
 -              return -1;
 +      if (buf->len == 0) {
 +              struct todo_item *item = append_new_todo(todo_list);
 +              item->command = TODO_NOOP;
 +              item->commit = NULL;
 +              item->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;
 +      }
  
 -      if (autosquash && rearrange_squash(r))
 +      if (autosquash && todo_list_rearrange_squash(todo_list))
                return -1;
  
 -      if (cmd && *cmd)
 -              sequencer_add_exec_commands(r, cmd);
 -
 -      if (strbuf_read_file(buf, todo_file, 0) < 0)
 -              return error_errno(_("could not read '%s'."), todo_file);
 +      if (commands->nr)
 +              todo_list_add_exec_commands(todo_list, commands);
  
 -      if (parse_insn_buffer(r, buf->buf, &todo_list)) {
 -              todo_list_release(&todo_list);
 -              return error(_("unusable todo list: '%s'"), todo_file);
 -      }
 -
 -      if (count_commands(&todo_list) == 0) {
 +      if (count_commands(todo_list) == 0) {
                apply_autostash(opts);
                sequencer_remove_state(opts);
 -              todo_list_release(&todo_list);
  
                return error(_("nothing to do"));
        }
  
 -      strbuf_addch(buf, '\n');
 -      strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
 -                                    "Rebase %s onto %s (%d commands)",
 -                                    count_commands(&todo_list)),
 -                            shortrevisions, shortonto, count_commands(&todo_list));
 -      append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
 -
 -      if (write_message(buf->buf, buf->len, todo_file, 0)) {
 -              todo_list_release(&todo_list);
 +      res = edit_todo_list(r, todo_list, &new_todo, shortrevisions,
 +                           shortonto, flags);
 +      if (res == -1)
                return -1;
 -      }
 -
 -      if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
 -              return error(_("could not copy '%s' to '%s'."), todo_file,
 -                           rebase_path_todo_backup());
 -
 -      if (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))
 -              return error(_("could not transform the todo list"));
 -
 -      strbuf_reset(buf);
 -
 -      if (launch_sequence_editor(todo_file, buf, NULL)) {
 +      else if (res == -2) {
                apply_autostash(opts);
                sequencer_remove_state(opts);
 -              todo_list_release(&todo_list);
  
                return -1;
 -      }
 -
 -      strbuf_stripspace(buf, 1);
 -      if (buf->len == 0) {
 +      } else if (res == -3) {
                apply_autostash(opts);
                sequencer_remove_state(opts);
 -              todo_list_release(&todo_list);
 +              todo_list_release(&new_todo);
  
                return error(_("nothing to do"));
        }
  
 -      todo_list_release(&todo_list);
 +      if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||
 +          todo_list_check(todo_list, &new_todo)) {
 +              fprintf(stderr, _(edit_todo_list_advice));
 +              checkout_onto(r, opts, onto_name, &onto->object.oid, orig_head);
 +              todo_list_release(&new_todo);
  
 -      if (check_todo_list(r)) {
 -              checkout_onto(opts, onto_name, onto, orig_head);
                return -1;
        }
  
 -      if (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))
 -              return error(_("could not transform the todo list"));
 -
 -      if (opts->allow_ff && skip_unnecessary_picks(r, &oid))
 +      if (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {
 +              todo_list_release(&new_todo);
                return error(_("could not skip unnecessary pick commands"));
 +      }
 +
 +      if (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,
 +                                  flags & ~(TODO_LIST_SHORTEN_IDS))) {
 +              todo_list_release(&new_todo);
 +              return error_errno(_("could not write '%s'"), todo_file);
 +      }
 +
 +      todo_list_release(&new_todo);
  
 -      if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
 +      if (checkout_onto(r, opts, onto_name, &oid, orig_head))
                return -1;
  
        if (require_clean_work_tree(r, "rebase", "", 1, 1))
@@@ -5039,13 -4997,21 +5041,13 @@@ define_commit_slab(commit_todo_item, st
   * message will have to be retrieved from the commit (as the oneline in the
   * script cannot be trusted) in order to normalize the autosquash arrangement.
   */
 -int rearrange_squash(struct repository *r)
 +int todo_list_rearrange_squash(struct todo_list *todo_list)
  {
 -      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, i;
 +      int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
        char **subjects;
        struct commit_todo_item commit_todo;
 -
 -      if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
 -              return -1;
 -      if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
 -              todo_list_release(&todo_list);
 -              return -1;
 -      }
 +      struct todo_item *items = NULL;
  
        init_commit_todo_item(&commit_todo);
        /*
         * be moved to appear after the i'th.
         */
        hashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,
 -                   NULL, todo_list.nr);
 -      ALLOC_ARRAY(next, todo_list.nr);
 -      ALLOC_ARRAY(tail, todo_list.nr);
 -      ALLOC_ARRAY(subjects, todo_list.nr);
 -      for (i = 0; i < todo_list.nr; i++) {
 +                   NULL, todo_list->nr);
 +      ALLOC_ARRAY(next, todo_list->nr);
 +      ALLOC_ARRAY(tail, todo_list->nr);
 +      ALLOC_ARRAY(subjects, todo_list->nr);
 +      for (i = 0; i < todo_list->nr; i++) {
                struct strbuf buf = STRBUF_INIT;
 -              struct todo_item *item = todo_list.items + i;
 +              struct todo_item *item = todo_list->items + i;
                const char *commit_buffer, *subject, *p;
                size_t subject_len;
                int i2 = -1;
                }
  
                if (is_fixup(item->command)) {
 -                      todo_list_release(&todo_list);
                        clear_commit_todo_item(&commit_todo);
                        return error(_("the script was already rearranged."));
                }
                                 *commit_todo_item_at(&commit_todo, commit2))
                                /* found by commit name */
                                i2 = *commit_todo_item_at(&commit_todo, commit2)
 -                                      - todo_list.items;
 +                                      - todo_list->items;
                        else {
                                /* copy can be a prefix of the commit subject */
                                for (i2 = 0; i2 < i; i2++)
                }
                if (i2 >= 0) {
                        rearranged = 1;
 -                      todo_list.items[i].command =
 +                      todo_list->items[i].command =
                                starts_with(subject, "fixup!") ?
                                TODO_FIXUP : TODO_SQUASH;
                        if (next[i2] < 0)
        }
  
        if (rearranged) {
 -              struct strbuf buf = STRBUF_INIT;
 -
 -              for (i = 0; i < todo_list.nr; i++) {
 -                      enum todo_command command = todo_list.items[i].command;
 +              for (i = 0; i < todo_list->nr; i++) {
 +                      enum todo_command command = todo_list->items[i].command;
                        int cur = i;
  
                        /*
                                continue;
  
                        while (cur >= 0) {
 -                              const char *bol =
 -                                      get_item_line(&todo_list, cur);
 -                              const char *eol =
 -                                      get_item_line(&todo_list, cur + 1);
 -
 -                              /* replace 'pick', by 'fixup' or 'squash' */
 -                              command = todo_list.items[cur].command;
 -                              if (is_fixup(command)) {
 -                                      strbuf_addstr(&buf,
 -                                              todo_command_info[command].str);
 -                                      bol += strcspn(bol, " \t");
 -                              }
 -
 -                              strbuf_add(&buf, bol, eol - bol);
 -
 +                              ALLOC_GROW(items, nr + 1, alloc);
 +                              items[nr++] = todo_list->items[cur];
                                cur = next[cur];
                        }
                }
  
 -              res = rewrite_file(todo_file, buf.buf, buf.len);
 -              strbuf_release(&buf);
 +              FREE_AND_NULL(todo_list->items);
 +              todo_list->items = items;
 +              todo_list->nr = nr;
 +              todo_list->alloc = alloc;
        }
  
        free(next);
        free(tail);
 -      for (i = 0; i < todo_list.nr; i++)
 +      for (i = 0; i < todo_list->nr; i++)
                free(subjects[i]);
        free(subjects);
        hashmap_free(&subject2item, 1);
 -      todo_list_release(&todo_list);
  
        clear_commit_todo_item(&commit_todo);
 -      return res;
 +
 +      return 0;
  }
diff --combined sequencer.h
index 0c494b83d43e2c0d71822a23dd274176fe743490,4d505b3590ed158600844cfe3889112b5da4e511..3d0b68c34ce1fed23899415660127c999fa999c6
@@@ -10,7 -10,6 +10,7 @@@ struct repository
  const char *git_path_commit_editmsg(void);
  const char *git_path_seq_dir(void);
  const char *rebase_path_todo(void);
 +const char *rebase_path_todo_backup(void);
  
  #define APPEND_SIGNOFF_DEDUP (1u << 0)
  
@@@ -48,7 -47,6 +48,7 @@@ struct replay_opts 
  
        char *gpg_sign;
        enum commit_msg_cleanup_mode default_msg_cleanup;
 +      int explicit_cleanup;
  
        /* Merge strategy */
        char *strategy;
  };
  #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
  
 -enum missing_commit_check_level {
 -      MISSING_COMMIT_CHECK_IGNORE = 0,
 -      MISSING_COMMIT_CHECK_WARN,
 -      MISSING_COMMIT_CHECK_ERROR
 +/*
 + * Note that ordering matters in this enum. Not only must it match the mapping
 + * of todo_command_info (in sequencer.c), it is also divided into several
 + * sections that matter.  When adding new commands, make sure you add it in the
 + * right section.
 + */
 +enum todo_command {
 +      /* commands that handle commits */
 +      TODO_PICK = 0,
 +      TODO_REVERT,
 +      TODO_EDIT,
 +      TODO_REWORD,
 +      TODO_FIXUP,
 +      TODO_SQUASH,
 +      /* commands that do something else than handling a single commit */
 +      TODO_EXEC,
 +      TODO_BREAK,
 +      TODO_LABEL,
 +      TODO_RESET,
 +      TODO_MERGE,
 +      /* commands that do nothing but are counted for reporting progress */
 +      TODO_NOOP,
 +      TODO_DROP,
 +      /* comments (not counted for reporting progress) */
 +      TODO_COMMENT
 +};
 +
 +struct todo_item {
 +      enum todo_command command;
 +      struct commit *commit;
 +      unsigned int flags;
 +      int arg_len;
 +      /* The offset of the command and its argument in the strbuf */
 +      size_t offset_in_buf, arg_offset;
 +};
 +
 +struct todo_list {
 +      struct strbuf buf;
 +      struct todo_item *items;
 +      int nr, alloc, current;
 +      int done_nr, total_nr;
 +      struct stat_data stat;
  };
  
 -int write_message(const void *buf, size_t len, const char *filename,
 -                int append_eol);
 +#define TODO_LIST_INIT { STRBUF_INIT }
 +
 +int todo_list_parse_insn_buffer(struct repository *r, char *buf,
 +                              struct todo_list *todo_list);
 +int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
 +                          const char *file, const char *shortrevisions,
 +                          const char *shortonto, int num, unsigned flags);
 +void todo_list_release(struct todo_list *todo_list);
 +const char *todo_item_get_arg(struct todo_list *todo_list,
 +                            struct todo_item *item);
  
  /* Call this to setup defaults before parsing command line options */
  void sequencer_init_config(struct replay_opts *opts);
@@@ -141,19 -93,19 +141,19 @@@ int sequencer_remove_state(struct repla
   * 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(struct repository *repo, FILE *out,
 -                        int argc, const char **argv,
 -                        unsigned flags);
 -
 -int sequencer_add_exec_commands(struct repository *r, const char *command);
 -int transform_todos(struct repository *r, unsigned flags);
 -enum missing_commit_check_level get_missing_commit_check_level(void);
 -int check_todo_list(struct repository *r);
 +#define TODO_LIST_APPEND_TODO_HELP (1U << 5)
 +
 +int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 +                        const char **argv, unsigned flags);
 +
 +void todo_list_add_exec_commands(struct todo_list *todo_list,
 +                               struct string_list *commands);
 +int check_todo_list_from_file(struct repository *r);
  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
                    const char *shortrevisions, const char *onto_name,
 -                  const char *onto, const char *orig_head, const char *cmd,
 -                  unsigned autosquash);
 -int rearrange_squash(struct repository *r);
 +                  struct commit *onto, const char *orig_head, struct string_list *commands,
 +                  unsigned autosquash, struct todo_list *todo_list);
 +int todo_list_rearrange_squash(struct todo_list *todo_list);
  
  /*
   * Append a signoff to the commit message in "msgbuf". The ignore_footer
   */
  void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
  
 -void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);
 +void append_conflicts_hint(struct index_state *istate,
 +              struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);
 +enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
 +      int use_editor);
 +
 +void cleanup_message(struct strbuf *msgbuf,
 +      enum commit_msg_cleanup_mode cleanup_mode, int verbose);
 +
  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,
@@@ -183,8 -128,7 +183,8 @@@ void commit_post_rewrite(struct reposit
                         const struct commit *current_head,
                         const struct object_id *new_head);
  
 -int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit);
 +int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
 +                               const char *commit);
  
  #define SUMMARY_INITIAL_COMMIT   (1 << 0)
  #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
@@@ -199,7 -143,4 +199,7 @@@ int read_author_script(const char *path
  
  void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
  int write_basic_state(struct replay_opts *opts, const char *head_name,
 -                    const char *onto, const char *orig_head);
 +                    struct commit *onto, const char *orig_head);
- void sequencer_post_commit_cleanup(struct repository *r);
++void sequencer_post_commit_cleanup(struct repository *r, int verbose);
 +int sequencer_get_last_command(struct repository* r,
 +                             enum replay_action *action);
diff --combined sha1-name.c
index 728e6f1f61ea4641272e59e6287b6b47bfe0c74e,da0518c8e3c42ee3fe88bf7b5c29fcde4fcd8074..49855ad24f1bc81aa8924fc927e0e32c7a62bf0a
  #include "packfile.h"
  #include "object-store.h"
  #include "repository.h"
 +#include "submodule.h"
  #include "midx.h"
  #include "commit-reach.h"
  
 -static int get_oid_oneline(const char *, struct object_id *, struct commit_list *);
 +static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
  
 -typedef int (*disambiguate_hint_fn)(const struct object_id *, void *);
 +typedef int (*disambiguate_hint_fn)(struct repository *, const struct object_id *, void *);
  
  struct disambiguate_state {
        int len; /* length of prefix in hex chars */
        char hex_pfx[GIT_MAX_HEXSZ + 1];
        struct object_id bin_pfx;
  
 +      struct repository *repo;
        disambiguate_hint_fn fn;
        void *cb_data;
        struct object_id candidate;
@@@ -40,7 -38,7 +40,7 @@@
  static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
  {
        if (ds->always_call_fn) {
 -              ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
 +              ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0;
                return;
        }
        if (!ds->candidate_exists) {
@@@ -60,7 -58,7 +60,7 @@@
        }
  
        if (!ds->candidate_checked) {
 -              ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data);
 +              ds->candidate_ok = ds->fn(ds->repo, &ds->candidate, ds->cb_data);
                ds->disambiguate_fn_used = 1;
                ds->candidate_checked = 1;
        }
@@@ -73,7 -71,7 +73,7 @@@
        }
  
        /* if we reach this point, we know ds->candidate satisfies fn */
 -      if (ds->fn(current, ds->cb_data)) {
 +      if (ds->fn(ds->repo, current, ds->cb_data)) {
                /*
                 * if both current and candidate satisfy fn, we cannot
                 * disambiguate.
@@@ -91,7 -89,9 +91,7 @@@ static void find_short_object_filename(
  {
        struct object_directory *odb;
  
 -      for (odb = the_repository->objects->odb;
 -           odb && !ds->ambiguous;
 -           odb = odb->next) {
 +      for (odb = ds->repo->objects->odb; odb && !ds->ambiguous; odb = odb->next) {
                int pos;
                struct oid_array *loose_objects;
  
@@@ -157,9 -157,6 +157,9 @@@ static void unique_in_pack(struct packe
        uint32_t num, i, first = 0;
        const struct object_id *current = NULL;
  
 +      if (p->multi_pack_index)
 +              return;
 +
        if (open_pack_index(p) || !p->num_objects)
                return;
  
@@@ -185,10 -182,10 +185,10 @@@ static void find_short_packed_object(st
        struct multi_pack_index *m;
        struct packed_git *p;
  
 -      for (m = get_multi_pack_index(the_repository); m && !ds->ambiguous;
 +      for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous;
             m = m->next)
                unique_in_midx(m, ds);
 -      for (p = get_packed_git(the_repository); p && !ds->ambiguous;
 +      for (p = get_packed_git(ds->repo); p && !ds->ambiguous;
             p = p->next)
                unique_in_pack(p, ds);
  }
@@@ -218,7 -215,7 +218,7 @@@ static int finish_object_disambiguation
                 * same repository!
                 */
                ds->candidate_ok = (!ds->disambiguate_fn_used ||
 -                                  ds->fn(&ds->candidate, ds->cb_data));
 +                                  ds->fn(ds->repo, &ds->candidate, ds->cb_data));
  
        if (!ds->candidate_ok)
                return SHORT_NAME_AMBIGUOUS;
        return 0;
  }
  
 -static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused)
 +static int disambiguate_commit_only(struct repository *r,
 +                                  const struct object_id *oid,
 +                                  void *cb_data_unused)
  {
 -      int kind = oid_object_info(the_repository, oid, NULL);
 +      int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_COMMIT;
  }
  
 -static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused)
 +static int disambiguate_committish_only(struct repository *r,
 +                                      const struct object_id *oid,
 +                                      void *cb_data_unused)
  {
        struct object *obj;
        int kind;
  
 -      kind = oid_object_info(the_repository, oid, NULL);
 +      kind = oid_object_info(r, oid, NULL);
        if (kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
  
        /* We need to do this the hard way... */
 -      obj = deref_tag(the_repository, parse_object(the_repository, oid),
 -                      NULL, 0);
 +      obj = deref_tag(r, parse_object(r, oid), NULL, 0);
        if (obj && obj->type == OBJ_COMMIT)
                return 1;
        return 0;
  }
  
 -static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused)
 +static int disambiguate_tree_only(struct repository *r,
 +                                const struct object_id *oid,
 +                                void *cb_data_unused)
  {
 -      int kind = oid_object_info(the_repository, oid, NULL);
 +      int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_TREE;
  }
  
 -static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused)
 +static int disambiguate_treeish_only(struct repository *r,
 +                                   const struct object_id *oid,
 +                                   void *cb_data_unused)
  {
        struct object *obj;
        int kind;
  
 -      kind = oid_object_info(the_repository, oid, NULL);
 +      kind = oid_object_info(r, oid, NULL);
        if (kind == OBJ_TREE || kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
  
        /* We need to do this the hard way... */
 -      obj = deref_tag(the_repository, parse_object(the_repository, oid),
 -                      NULL, 0);
 +      obj = deref_tag(r, parse_object(r, oid), NULL, 0);
        if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
                return 1;
        return 0;
  }
  
 -static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused)
 +static int disambiguate_blob_only(struct repository *r,
 +                                const struct object_id *oid,
 +                                void *cb_data_unused)
  {
 -      int kind = oid_object_info(the_repository, oid, NULL);
 +      int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_BLOB;
  }
  
@@@ -321,8 -310,7 +321,8 @@@ int set_disambiguate_hint_config(const 
        return error("unknown hint type for '%s': %s", var, value);
  }
  
 -static int init_object_disambiguation(const char *name, int len,
 +static int init_object_disambiguation(struct repository *r,
 +                                    const char *name, int len,
                                      struct disambiguate_state *ds)
  {
        int i;
  
        ds->len = len;
        ds->hex_pfx[len] = '\0';
 -      prepare_alt_odb(the_repository);
 +      ds->repo = r;
 +      prepare_alt_odb(r);
        return 0;
  }
  
@@@ -364,25 -351,25 +364,25 @@@ static int show_ambiguous_object(const 
        struct strbuf desc = STRBUF_INIT;
        int type;
  
 -      if (ds->fn && !ds->fn(oid, ds->cb_data))
 +      if (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))
                return 0;
  
 -      type = oid_object_info(the_repository, oid, NULL);
 +      type = oid_object_info(ds->repo, oid, NULL);
        if (type == OBJ_COMMIT) {
 -              struct commit *commit = lookup_commit(the_repository, oid);
 +              struct commit *commit = lookup_commit(ds->repo, oid);
                if (commit) {
                        struct pretty_print_context pp = {0};
                        pp.date_mode.type = DATE_SHORT;
                        format_commit_message(commit, " %ad - %s", &desc, &pp);
                }
        } else if (type == OBJ_TAG) {
 -              struct tag *tag = lookup_tag(the_repository, oid);
 +              struct tag *tag = lookup_tag(ds->repo, oid);
                if (!parse_tag(tag) && tag->tag)
                        strbuf_addf(&desc, " %s", tag->tag);
        }
  
        advise("  %s %s%s",
 -             find_unique_abbrev(oid, DEFAULT_ABBREV),
 +             repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),
               type_name(type) ? type_name(type) : "unknown type",
               desc.buf);
  
@@@ -396,18 -383,10 +396,18 @@@ static int collect_ambiguous(const stru
        return 0;
  }
  
 +static int repo_collect_ambiguous(struct repository *r,
 +                                const struct object_id *oid,
 +                                void *data)
 +{
 +      return collect_ambiguous(oid, data);
 +}
 +
 +static struct repository *sort_ambiguous_repo;
  static int sort_ambiguous(const void *a, const void *b)
  {
 -      int a_type = oid_object_info(the_repository, a, NULL);
 -      int b_type = oid_object_info(the_repository, b, NULL);
 +      int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
 +      int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
        int a_type_sort;
        int b_type_sort;
  
        return a_type_sort > b_type_sort ? 1 : -1;
  }
  
 -static enum get_oid_result get_short_oid(const char *name, int len,
 +static void sort_ambiguous_oid_array(struct repository *r, struct oid_array *a)
 +{
 +      /* mutex will be needed if this code is to be made thread safe */
 +      sort_ambiguous_repo = r;
 +      QSORT(a->oid, a->nr, sort_ambiguous);
 +      sort_ambiguous_repo = NULL;
 +}
 +
 +static enum get_oid_result get_short_oid(struct repository *r,
 +                                       const char *name, int len,
                                         struct object_id *oid,
                                         unsigned flags)
  {
        struct disambiguate_state ds;
        int quietly = !!(flags & GET_OID_QUIETLY);
  
 -      if (init_object_disambiguation(name, len, &ds) < 0)
 +      if (init_object_disambiguation(r, name, len, &ds) < 0)
                return -1;
  
        if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
        find_short_packed_object(&ds);
        status = finish_object_disambiguation(&ds, oid);
  
 +      /*
 +       * If we didn't find it, do the usual reprepare() slow-path,
 +       * since the object may have recently been added to the repository
 +       * or migrated from loose to packed.
 +       */
 +      if (status == MISSING_OBJECT) {
 +              reprepare_packed_git(the_repository);
 +              find_short_object_filename(&ds);
 +              find_short_packed_object(&ds);
 +              status = finish_object_disambiguation(&ds, oid);
 +      }
 +
        if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
                struct oid_array collect = OID_ARRAY_INIT;
  
                        ds.fn = NULL;
  
                advise(_("The candidates are:"));
 -              for_each_abbrev(ds.hex_pfx, collect_ambiguous, &collect);
 -              QSORT(collect.oid, collect.nr, sort_ambiguous);
 +              repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
 +              sort_ambiguous_oid_array(r, &collect);
  
                if (oid_array_for_each(&collect, show_ambiguous_object, &ds))
                        BUG("show_ambiguous_object shouldn't return non-zero");
        return status;
  }
  
 -int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
 +int repo_for_each_abbrev(struct repository *r, const char *prefix,
 +                       each_abbrev_fn fn, void *cb_data)
  {
        struct oid_array collect = OID_ARRAY_INIT;
        struct disambiguate_state ds;
        int ret;
  
 -      if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
 +      if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0)
                return -1;
  
        ds.always_call_fn = 1;
 -      ds.fn = collect_ambiguous;
 +      ds.fn = repo_collect_ambiguous;
        ds.cb_data = &collect;
        find_short_object_filename(&ds);
        find_short_packed_object(&ds);
@@@ -548,7 -505,6 +548,7 @@@ struct min_abbrev_data 
        unsigned int init_len;
        unsigned int cur_len;
        char *hex;
 +      struct repository *repo;
        const struct object_id *oid;
  };
  
@@@ -577,13 -533,6 +577,13 @@@ static int extend_abbrev_len(const stru
        return 0;
  }
  
 +static int repo_extend_abbrev_len(struct repository *r,
 +                                const struct object_id *oid,
 +                                void *cb_data)
 +{
 +      return extend_abbrev_len(oid, cb_data);
 +}
 +
  static void find_abbrev_len_for_midx(struct multi_pack_index *m,
                                     struct min_abbrev_data *mad)
  {
@@@ -628,9 -577,6 +628,9 @@@ static void find_abbrev_len_for_pack(st
        struct object_id oid;
        const struct object_id *mad_oid;
  
 +      if (p->multi_pack_index)
 +              return;
 +
        if (open_pack_index(p) || !p->num_objects)
                return;
  
@@@ -664,22 -610,21 +664,22 @@@ static void find_abbrev_len_packed(stru
        struct multi_pack_index *m;
        struct packed_git *p;
  
 -      for (m = get_multi_pack_index(the_repository); m; m = m->next)
 +      for (m = get_multi_pack_index(mad->repo); m; m = m->next)
                find_abbrev_len_for_midx(m, mad);
 -      for (p = get_packed_git(the_repository); p; p = p->next)
 +      for (p = get_packed_git(mad->repo); p; p = p->next)
                find_abbrev_len_for_pack(p, mad);
  }
  
 -int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
 +int repo_find_unique_abbrev_r(struct repository *r, char *hex,
 +                            const struct object_id *oid, int len)
  {
        struct disambiguate_state ds;
        struct min_abbrev_data mad;
        struct object_id oid_ret;
 -      const unsigned hexsz = the_hash_algo->hexsz;
 +      const unsigned hexsz = r->hash_algo->hexsz;
  
        if (len < 0) {
 -              unsigned long count = approximate_object_count();
 +              unsigned long count = repo_approximate_object_count(r);
                /*
                 * Add one because the MSB only tells us the highest bit set,
                 * not including the value of all the _other_ bits (so "15"
        if (len == hexsz || !len)
                return hexsz;
  
 +      mad.repo = r;
        mad.init_len = len;
        mad.cur_len = len;
        mad.hex = hex;
  
        find_abbrev_len_packed(&mad);
  
 -      if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0)
 +      if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0)
                return -1;
  
 -      ds.fn = extend_abbrev_len;
 +      ds.fn = repo_extend_abbrev_len;
        ds.always_call_fn = 1;
        ds.cb_data = (void *)&mad;
  
        return mad.cur_len;
  }
  
 -const char *find_unique_abbrev(const struct object_id *oid, int len)
 +const char *repo_find_unique_abbrev(struct repository *r,
 +                                  const struct object_id *oid,
 +                                  int len)
  {
        static int bufno;
        static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
        char *hex = hexbuffer[bufno];
        bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
 -      find_unique_abbrev_r(hex, oid, len);
 +      repo_find_unique_abbrev_r(r, hex, oid, len);
        return hex;
  }
  
@@@ -789,11 -731,11 +789,11 @@@ static inline int push_mark(const char 
        return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
  }
  
 -static enum get_oid_result get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
 -static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
 +static enum get_oid_result get_oid_1(struct repository *r, const char *name, int len, struct object_id *oid, unsigned lookup_flags);
 +static int interpret_nth_prior_checkout(struct repository *r, const char *name, int namelen, struct strbuf *buf);
  
 -static int get_oid_basic(const char *str, int len, struct object_id *oid,
 -                        unsigned int flags)
 +static int get_oid_basic(struct repository *r, const char *str, int len,
 +                       struct object_id *oid, unsigned int flags)
  {
        static const char *warn_msg = "refname '%.*s' is ambiguous.";
        static const char *object_name_msg = N_(
        "because it will be ignored when you just specify 40-hex. These refs\n"
        "may be created by mistake. For example,\n"
        "\n"
-       "  git checkout -b $br $(git rev-parse ...)\n"
+       "  git switch -c $br $(git rev-parse ...)\n"
        "\n"
        "where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
        "examine these refs and maybe delete them. Turn this message off by\n"
        int refs_found = 0;
        int at, reflog_len, nth_prior = 0;
  
 -      if (len == the_hash_algo->hexsz && !get_oid_hex(str, oid)) {
 +      if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
                if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
 -                      refs_found = dwim_ref(str, len, &tmp_oid, &real_ref);
 +                      refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref);
                        if (refs_found > 0) {
                                warning(warn_msg, len, str);
                                if (advice_object_name_warning)
                struct strbuf buf = STRBUF_INIT;
                int detached;
  
 -              if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
 -                      detached = (buf.len == the_hash_algo->hexsz && !get_oid_hex(buf.buf, oid));
 +              if (interpret_nth_prior_checkout(r, str, len, &buf) > 0) {
 +                      detached = (buf.len == r->hash_algo->hexsz && !get_oid_hex(buf.buf, oid));
                        strbuf_release(&buf);
                        if (detached)
                                return 0;
  
        if (!len && reflog_len)
                /* allow "@{...}" to mean the current branch reflog */
 -              refs_found = dwim_ref("HEAD", 4, oid, &real_ref);
 +              refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref);
        else if (reflog_len)
 -              refs_found = dwim_log(str, len, oid, &real_ref);
 +              refs_found = repo_dwim_log(r, str, len, oid, &real_ref);
        else
 -              refs_found = dwim_ref(str, len, oid, &real_ref);
 +              refs_found = repo_dwim_ref(r, str, len, oid, &real_ref);
  
        if (!refs_found)
                return -1;
  
        if (warn_ambiguous_refs && !(flags & GET_OID_QUIETLY) &&
            (refs_found > 1 ||
 -           !get_short_oid(str, len, &tmp_oid, GET_OID_QUIETLY)))
 +           !get_short_oid(r, str, len, &tmp_oid, GET_OID_QUIETLY)))
                warning(warn_msg, len, str);
  
        if (reflog_len) {
                                return -1;
                        }
                }
 -              if (read_ref_at(real_ref, flags, at_time, nth, oid, NULL,
 +              if (read_ref_at(get_main_ref_store(r),
 +                              real_ref, flags, at_time, nth, oid, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
                                if (starts_with(real_ref, "refs/heads/")) {
        return 0;
  }
  
 -static enum get_oid_result get_parent(const char *name, int len,
 +static enum get_oid_result get_parent(struct repository *r,
 +                                    const char *name, int len,
                                      struct object_id *result, int idx)
  {
        struct object_id oid;
 -      enum get_oid_result ret = get_oid_1(name, len, &oid,
 +      enum get_oid_result ret = get_oid_1(r, name, len, &oid,
                                            GET_OID_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
  
        if (ret)
                return ret;
 -      commit = lookup_commit_reference(the_repository, &oid);
 +      commit = lookup_commit_reference(r, &oid);
        if (parse_commit(commit))
                return MISSING_OBJECT;
        if (!idx) {
        return MISSING_OBJECT;
  }
  
 -static enum get_oid_result get_nth_ancestor(const char *name, int len,
 +static enum get_oid_result get_nth_ancestor(struct repository *r,
 +                                          const char *name, int len,
                                            struct object_id *result,
                                            int generation)
  {
        struct commit *commit;
        int ret;
  
 -      ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH);
 +      ret = get_oid_1(r, name, len, &oid, GET_OID_COMMITTISH);
        if (ret)
                return ret;
 -      commit = lookup_commit_reference(the_repository, &oid);
 +      commit = lookup_commit_reference(r, &oid);
        if (!commit)
                return MISSING_OBJECT;
  
        return FOUND;
  }
  
 -struct object *peel_to_type(const char *name, int namelen,
 -                          struct object *o, enum object_type expected_type)
 +struct object *repo_peel_to_type(struct repository *r, const char *name, int namelen,
 +                               struct object *o, enum object_type expected_type)
  {
        if (name && !namelen)
                namelen = strlen(name);
        while (1) {
 -              if (!o || (!o->parsed && !parse_object(the_repository, &o->oid)))
 +              if (!o || (!o->parsed && !parse_object(r, &o->oid)))
                        return NULL;
                if (expected_type == OBJ_ANY || o->type == expected_type)
                        return o;
                if (o->type == OBJ_TAG)
                        o = ((struct tag*) o)->tagged;
                else if (o->type == OBJ_COMMIT)
 -                      o = &(get_commit_tree(((struct commit *)o))->object);
 +                      o = &(repo_get_commit_tree(r, ((struct commit *)o))->object);
                else {
                        if (name)
                                error("%.*s: expected %s type, but the object "
        }
  }
  
 -static int peel_onion(const char *name, int len, struct object_id *oid,
 -                    unsigned lookup_flags)
 +static int peel_onion(struct repository *r, const char *name, int len,
 +                    struct object_id *oid, unsigned lookup_flags)
  {
        struct object_id outer;
        const char *sp;
        else if (expected_type == OBJ_TREE)
                lookup_flags |= GET_OID_TREEISH;
  
 -      if (get_oid_1(name, sp - name - 2, &outer, lookup_flags))
 +      if (get_oid_1(r, name, sp - name - 2, &outer, lookup_flags))
                return -1;
  
 -      o = parse_object(the_repository, &outer);
 +      o = parse_object(r, &outer);
        if (!o)
                return -1;
        if (!expected_type) {
 -              o = deref_tag(the_repository, o, name, sp - name - 2);
 -              if (!o || (!o->parsed && !parse_object(the_repository, &o->oid)))
 +              o = deref_tag(r, o, name, sp - name - 2);
 +              if (!o || (!o->parsed && !parse_object(r, &o->oid)))
                        return -1;
                oidcpy(oid, &o->oid);
                return 0;
         * if we do not get the needed object, we should
         * barf.
         */
 -      o = peel_to_type(name, len, o, expected_type);
 +      o = repo_peel_to_type(r, name, len, o, expected_type);
        if (!o)
                return -1;
  
  
                prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
                commit_list_insert((struct commit *)o, &list);
 -              ret = get_oid_oneline(prefix, oid, list);
 +              ret = get_oid_oneline(r, prefix, oid, list);
                free(prefix);
                return ret;
        }
        return 0;
  }
  
 -static int get_describe_name(const char *name, int len, struct object_id *oid)
 +static int get_describe_name(struct repository *r,
 +                           const char *name, int len,
 +                           struct object_id *oid)
  {
        const char *cp;
        unsigned flags = GET_OID_QUIETLY | GET_OID_COMMIT;
                        if (ch == 'g' && cp[-1] == '-') {
                                cp++;
                                len -= cp - name;
 -                              return get_short_oid(cp, len, oid, flags);
 +                              return get_short_oid(r,
 +                                                   cp, len, oid, flags);
                        }
                }
        }
        return -1;
  }
  
 -static enum get_oid_result get_oid_1(const char *name, int len,
 +static enum get_oid_result get_oid_1(struct repository *r,
 +                                   const char *name, int len,
                                     struct object_id *oid,
                                     unsigned lookup_flags)
  {
                if (!num && len1 == len - 1)
                        num = 1;
                if (has_suffix == '^')
 -                      return get_parent(name, len1, oid, num);
 +                      return get_parent(r, name, len1, oid, num);
                /* else if (has_suffix == '~') -- goes without saying */
 -              return get_nth_ancestor(name, len1, oid, num);
 +              return get_nth_ancestor(r, name, len1, oid, num);
        }
  
 -      ret = peel_onion(name, len, oid, lookup_flags);
 +      ret = peel_onion(r, name, len, oid, lookup_flags);
        if (!ret)
                return FOUND;
  
 -      ret = get_oid_basic(name, len, oid, lookup_flags);
 +      ret = get_oid_basic(r, name, len, oid, lookup_flags);
        if (!ret)
                return FOUND;
  
        /* It could be describe output that is "SOMETHING-gXXXX" */
 -      ret = get_describe_name(name, len, oid);
 +      ret = get_describe_name(r, name, len, oid);
        if (!ret)
                return FOUND;
  
 -      return get_short_oid(name, len, oid, lookup_flags);
 +      return get_short_oid(r, name, len, oid, lookup_flags);
  }
  
  /*
  /* Remember to update object flag allocation in object.h */
  #define ONELINE_SEEN (1u<<20)
  
 +struct handle_one_ref_cb {
 +      struct repository *repo;
 +      struct commit_list **list;
 +};
 +
  static int handle_one_ref(const char *path, const struct object_id *oid,
                          int flag, void *cb_data)
  {
 -      struct commit_list **list = cb_data;
 -      struct object *object = parse_object(the_repository, oid);
 +      struct handle_one_ref_cb *cb = cb_data;
 +      struct commit_list **list = cb->list;
 +      struct object *object = parse_object(cb->repo, oid);
        if (!object)
                return 0;
        if (object->type == OBJ_TAG) {
 -              object = deref_tag(the_repository, object, path,
 +              object = deref_tag(cb->repo, object, path,
                                   strlen(path));
                if (!object)
                        return 0;
        return 0;
  }
  
 -static int get_oid_oneline(const char *prefix, struct object_id *oid,
 -                          struct commit_list *list)
 +static int get_oid_oneline(struct repository *r,
 +                         const char *prefix, struct object_id *oid,
 +                         struct commit_list *list)
  {
        struct commit_list *backup = NULL, *l;
        int found = 0;
                int matches;
  
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
 -              if (!parse_object(the_repository, &commit->object.oid))
 +              if (!parse_object(r, &commit->object.oid))
                        continue;
                buf = get_commit_buffer(commit, NULL);
                p = strstr(buf, "\n\n");
@@@ -1318,8 -1246,7 +1318,8 @@@ static int grab_nth_branch_switch(struc
   * Parse @{-N} syntax, return the number of characters parsed
   * if successful; otherwise signal an error with negative value.
   */
 -static int interpret_nth_prior_checkout(const char *name, int namelen,
 +static int interpret_nth_prior_checkout(struct repository *r,
 +                                      const char *name, int namelen,
                                        struct strbuf *buf)
  {
        long nth;
        cb.remaining = nth;
        strbuf_init(&cb.buf, 20);
  
 -      retval = 0;
 -      if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
 +      retval = refs_for_each_reflog_ent_reverse(get_main_ref_store(r),
 +                      "HEAD", grab_nth_branch_switch, &cb);
 +      if (0 < retval) {
                strbuf_reset(buf);
                strbuf_addbuf(buf, &cb.buf);
                retval = brace - name + 1;
 -      }
 +      } else
 +              retval = 0;
  
        strbuf_release(&cb.buf);
        return retval;
  }
  
 -int get_oid_mb(const char *name, struct object_id *oid)
 +int repo_get_oid_mb(struct repository *r,
 +                  const char *name,
 +                  struct object_id *oid)
  {
        struct commit *one, *two;
        struct commit_list *mbs;
  
        dots = strstr(name, "...");
        if (!dots)
 -              return get_oid(name, oid);
 +              return repo_get_oid(r, name, oid);
        if (dots == name)
 -              st = get_oid("HEAD", &oid_tmp);
 +              st = repo_get_oid(r, "HEAD", &oid_tmp);
        else {
                struct strbuf sb;
                strbuf_init(&sb, dots - name);
                strbuf_add(&sb, name, dots - name);
 -              st = get_oid_committish(sb.buf, &oid_tmp);
 +              st = repo_get_oid_committish(r, sb.buf, &oid_tmp);
                strbuf_release(&sb);
        }
        if (st)
                return st;
 -      one = lookup_commit_reference_gently(the_repository, &oid_tmp, 0);
 +      one = lookup_commit_reference_gently(r, &oid_tmp, 0);
        if (!one)
                return -1;
  
 -      if (get_oid_committish(dots[3] ? (dots + 3) : "HEAD", &oid_tmp))
 +      if (repo_get_oid_committish(r, dots[3] ? (dots + 3) : "HEAD", &oid_tmp))
                return -1;
 -      two = lookup_commit_reference_gently(the_repository, &oid_tmp, 0);
 +      two = lookup_commit_reference_gently(r, &oid_tmp, 0);
        if (!two)
                return -1;
 +      if (r != the_repository)
 +              BUG("sorry get_merge_bases() can't take struct repository yet");
        mbs = get_merge_bases(one, two);
        if (!mbs || mbs->next)
                st = -1;
@@@ -1424,8 -1345,7 +1424,8 @@@ static int interpret_empty_at(const cha
        return 1;
  }
  
 -static int reinterpret(const char *name, int namelen, int len,
 +static int reinterpret(struct repository *r,
 +                     const char *name, int namelen, int len,
                       struct strbuf *buf, unsigned allowed)
  {
        /* we have extra data, which might need further processing */
        int ret;
  
        strbuf_add(buf, name + len, namelen - len);
 -      ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed);
 +      ret = repo_interpret_branch_name(r, buf->buf, buf->len, &tmp, allowed);
        /* that data was not interpreted, remove our cruft */
        if (ret < 0) {
                strbuf_setlen(buf, used);
        return ret - used + len;
  }
  
 -static void set_shortened_ref(struct strbuf *buf, const char *ref)
 +static void set_shortened_ref(struct repository *r, struct strbuf *buf, const char *ref)
  {
 -      char *s = shorten_unambiguous_ref(ref, 0);
 +      char *s = refs_shorten_unambiguous_ref(get_main_ref_store(r), ref, 0);
        strbuf_reset(buf);
        strbuf_addstr(buf, s);
        free(s);
@@@ -1470,8 -1390,7 +1470,8 @@@ static int branch_interpret_allowed(con
        return 0;
  }
  
 -static int interpret_branch_mark(const char *name, int namelen,
 +static int interpret_branch_mark(struct repository *r,
 +                               const char *name, int namelen,
                                 int at, struct strbuf *buf,
                                 int (*get_mark)(const char *, int),
                                 const char *(*get_data)(struct branch *,
        if (!branch_interpret_allowed(value, allowed))
                return -1;
  
 -      set_shortened_ref(buf, value);
 +      set_shortened_ref(r, buf, value);
        return len + at;
  }
  
 -int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
 -                        unsigned allowed)
 +int repo_interpret_branch_name(struct repository *r,
 +                             const char *name, int namelen,
 +                             struct strbuf *buf,
 +                             unsigned allowed)
  {
        char *at;
        const char *start;
                namelen = strlen(name);
  
        if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
 -              len = interpret_nth_prior_checkout(name, namelen, buf);
 +              len = interpret_nth_prior_checkout(r, name, namelen, buf);
                if (!len) {
                        return len; /* syntax Ok, not enough switches */
                } else if (len > 0) {
                        if (len == namelen)
                                return len; /* consumed all */
                        else
 -                              return reinterpret(name, namelen, len, buf, allowed);
 +                              return reinterpret(r, name, namelen, len, buf, allowed);
                }
        }
  
                if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
                        len = interpret_empty_at(name, namelen, at - name, buf);
                        if (len > 0)
 -                              return reinterpret(name, namelen, len, buf,
 +                              return reinterpret(r, name, namelen, len, buf,
                                                   allowed);
                }
  
 -              len = interpret_branch_mark(name, namelen, at - name, buf,
 +              len = interpret_branch_mark(r, name, namelen, at - name, buf,
                                            upstream_mark, branch_get_upstream,
                                            allowed);
                if (len > 0)
                        return len;
  
 -              len = interpret_branch_mark(name, namelen, at - name, buf,
 +              len = interpret_branch_mark(r, name, namelen, at - name, buf,
                                            push_mark, branch_get_push,
                                            allowed);
                if (len > 0)
@@@ -1595,31 -1512,12 +1595,31 @@@ int strbuf_check_branch_ref(struct strb
   * This is like "get_oid_basic()", except it allows "object ID expressions",
   * notably "xyz^" for "parent of xyz"
   */
 -int get_oid(const char *name, struct object_id *oid)
 +int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(the_repository, name, 0, oid, &unused);
 +      return get_oid_with_context(r, name, 0, oid, &unused);
  }
  
 +/*
 + * This returns a non-zero value if the string (built using printf
 + * format and the given arguments) is not a valid object.
 + */
 +int get_oidf(struct object_id *oid, const char *fmt, ...)
 +{
 +      va_list ap;
 +      int ret;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      va_start(ap, fmt);
 +      strbuf_vaddf(&sb, fmt, ap);
 +      va_end(ap);
 +
 +      ret = get_oid(sb.buf, oid);
 +      strbuf_release(&sb);
 +
 +      return ret;
 +}
  
  /*
   * Many callers know that the user meant to name a commit-ish by
   * commit-ish. It is merely to give a hint to the disambiguation
   * machinery.
   */
 -int get_oid_committish(const char *name, struct object_id *oid)
 +int repo_get_oid_committish(struct repository *r,
 +                          const char *name,
 +                          struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(the_repository,
 -                                  name, GET_OID_COMMITTISH,
 +      return get_oid_with_context(r, name, GET_OID_COMMITTISH,
                                    oid, &unused);
  }
  
 -int get_oid_treeish(const char *name, struct object_id *oid)
 +int repo_get_oid_treeish(struct repository *r,
 +                       const char *name,
 +                       struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(the_repository,
 -                                  name, GET_OID_TREEISH,
 +      return get_oid_with_context(r, name, GET_OID_TREEISH,
                                    oid, &unused);
  }
  
 -int get_oid_commit(const char *name, struct object_id *oid)
 +int repo_get_oid_commit(struct repository *r,
 +                      const char *name,
 +                      struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(the_repository,
 -                                  name, GET_OID_COMMIT,
 +      return get_oid_with_context(r, name, GET_OID_COMMIT,
                                    oid, &unused);
  }
  
 -int get_oid_tree(const char *name, struct object_id *oid)
 +int repo_get_oid_tree(struct repository *r,
 +                    const char *name,
 +                    struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(the_repository,
 -                                  name, GET_OID_TREE,
 +      return get_oid_with_context(r, name, GET_OID_TREE,
                                    oid, &unused);
  }
  
 -int get_oid_blob(const char *name, struct object_id *oid)
 +int repo_get_oid_blob(struct repository *r,
 +                    const char *name,
 +                    struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(the_repository,
 -                                  name, GET_OID_BLOB,
 +      return get_oid_with_context(r, name, GET_OID_BLOB,
                                    oid, &unused);
  }
  
@@@ -1684,7 -1577,7 +1684,7 @@@ static void diagnose_invalid_oid_path(c
                                      int object_name_len)
  {
        struct object_id oid;
 -      unsigned mode;
 +      unsigned short mode;
  
        if (!prefix)
                prefix = "";
  }
  
  /* Must be called only when :stage:filename doesn't exist. */
 -static void diagnose_invalid_index_path(struct index_state *istate,
 +static void diagnose_invalid_index_path(struct repository *r,
                                        int stage,
                                        const char *prefix,
                                        const char *filename)
  {
 +      struct index_state *istate = r->index;
        const struct cache_entry *ce;
        int pos;
        unsigned namelen = strlen(filename);
                            ce_stage(ce), filename);
        }
  
 -      if (file_exists(filename))
 +      if (repo_file_exists(r, filename))
                die("Path '%s' exists on disk, but not in the index.", filename);
        if (is_missing_file_error(errno))
                die("Path '%s' does not exist (neither on disk nor in the index).",
  }
  
  
 -static char *resolve_relative_path(const char *rel)
 +static char *resolve_relative_path(struct repository *r, const char *rel)
  {
        if (!starts_with(rel, "./") && !starts_with(rel, "../"))
                return NULL;
  
 -      if (!is_inside_work_tree())
 +      if (r != the_repository || !is_inside_work_tree())
                die("relative path syntax can't be used outside working tree.");
  
        /* die() inside prefix_path() if resolved path is outside worktree */
@@@ -1798,7 -1690,7 +1798,7 @@@ static enum get_oid_result get_oid_with
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
        strbuf_init(&oc->symlink_path, 0);
 -      ret = get_oid_1(name, namelen, oid, flags);
 +      ret = get_oid_1(repo, name, namelen, oid, flags);
        if (!ret)
                return ret;
        /*
                char *new_path = NULL;
                int pos;
                if (!only_to_die && namelen > 2 && name[1] == '/') {
 +                      struct handle_one_ref_cb cb;
                        struct commit_list *list = NULL;
  
 -                      for_each_ref(handle_one_ref, &list);
 -                      head_ref(handle_one_ref, &list);
 +                      cb.repo = repo;
 +                      cb.list = &list;
 +                      refs_for_each_ref(repo->refs, handle_one_ref, &cb);
 +                      refs_head_ref(repo->refs, handle_one_ref, &cb);
                        commit_list_sort_by_date(&list);
 -                      return get_oid_oneline(name + 2, oid, list);
 +                      return get_oid_oneline(repo, name + 2, oid, list);
                }
                if (namelen < 3 ||
                    name[2] != ':' ||
                        stage = name[1] - '0';
                        cp = name + 3;
                }
 -              new_path = resolve_relative_path(cp);
 +              new_path = resolve_relative_path(repo, cp);
                if (!new_path) {
                        namelen = namelen - (cp - name);
                } else {
                if (flags & GET_OID_RECORD_PATH)
                        oc->path = xstrdup(cp);
  
 -              if (!repo->index->cache)
 -                      repo_read_index(the_repository);
 +              if (!repo->index || !repo->index->cache)
 +                      repo_read_index(repo);
                pos = index_name_pos(repo->index, cp, namelen);
                if (pos < 0)
                        pos = -pos - 1;
                        pos++;
                }
                if (only_to_die && name[1] && name[1] != '/')
 -                      diagnose_invalid_index_path(repo->index, stage, prefix, cp);
 +                      diagnose_invalid_index_path(repo, stage, prefix, cp);
                free(new_path);
                return -1;
        }
                sub_flags &= ~GET_OID_DISAMBIGUATORS;
                sub_flags |= GET_OID_TREEISH;
  
 -              if (!get_oid_1(name, len, &tree_oid, sub_flags)) {
 +              if (!get_oid_1(repo, name, len, &tree_oid, sub_flags)) {
                        const char *filename = cp+1;
                        char *new_filename = NULL;
  
 -                      new_filename = resolve_relative_path(filename);
 +                      new_filename = resolve_relative_path(repo, filename);
                        if (new_filename)
                                filename = new_filename;
 +                      /*
 +                       * NEEDSWORK: Eventually get_tree_entry*() should
 +                       * learn to take struct repository directly and we
 +                       * would not need to inject submodule odb to the
 +                       * in-core odb.
 +                       */
 +                      if (repo != the_repository)
 +                              add_to_alternates_memory(repo->objects->odb->path);
                        if (flags & GET_OID_FOLLOW_SYMLINKS) {
                                ret = get_tree_entry_follow_symlinks(&tree_oid,
                                        filename, oid, &oc->symlink_path,
   * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case
   * you have a chance to diagnose the error further.
   */
 -void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
 +void maybe_die_on_misspelt_object_name(struct repository *r,
 +                                     const char *name,
 +                                     const char *prefix)
  {
        struct object_context oc;
        struct object_id oid;
 -      get_oid_with_context_1(the_repository, name, GET_OID_ONLY_TO_DIE,
 +      get_oid_with_context_1(r, name, GET_OID_ONLY_TO_DIE,
                               prefix, &oid, &oc);
  }
  
diff --combined t/t7512-status-help.sh
index c1eb72555d0c9878aafe04b8b3f69392a0b32dcd,1b9712c67569a5092fec931c15806935df505a85..b9f5d73423cfec8e61e10b6b42e7f8dc237a37b2
@@@ -85,7 -85,7 +85,7 @@@ You are currently rebasing branch '\''r
    (use "git rebase --abort" to check out the original branch)
  
  Unmerged paths:
-   (use "git reset HEAD <file>..." to unstage)
+   (use "git restore --staged <file>..." to unstage)
    (use "git add <file>..." to mark resolution)
  
        both modified:   main.txt
@@@ -110,7 -110,7 +110,7 @@@ You are currently rebasing branch '\''r
    (all conflicts fixed: run "git rebase --continue")
  
  Changes to be committed:
-   (use "git reset HEAD <file>..." to unstage)
+   (use "git restore --staged <file>..." to unstage)
  
        modified:   main.txt
  
@@@ -148,7 -148,7 +148,7 @@@ You are currently rebasing branch '\''r
    (use "git rebase --abort" to check out the original branch)
  
  Unmerged paths:
-   (use "git reset HEAD <file>..." to unstage)
+   (use "git restore --staged <file>..." to unstage)
    (use "git add <file>..." to mark resolution)
  
        both modified:   main.txt
@@@ -176,7 -176,7 +176,7 @@@ You are currently rebasing branch '\''r
    (all conflicts fixed: run "git rebase --continue")
  
  Changes to be committed:
-   (use "git reset HEAD <file>..." to unstage)
+   (use "git restore --staged <file>..." to unstage)
  
        modified:   main.txt
  
@@@ -246,7 -246,7 +246,7 @@@ You are currently splitting a commit wh
  
  Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
-   (use "git checkout -- <file>..." to discard changes in working directory)
+   (use "git restore <file>..." to discard changes in working directory)
  
        modified:   main.txt
  
@@@ -354,7 -354,7 +354,7 @@@ You are currently splitting a commit wh
  
  Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
-   (use "git checkout -- <file>..." to discard changes in working directory)
+   (use "git restore <file>..." to discard changes in working directory)
  
        modified:   main.txt
  
@@@ -453,7 -453,7 +453,7 @@@ You are currently splitting a commit wh
  
  Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
-   (use "git checkout -- <file>..." to discard changes in working directory)
+   (use "git restore <file>..." to discard changes in working directory)
  
        modified:   main.txt
  
@@@ -557,7 -557,7 +557,7 @@@ You are currently splitting a commit wh
  
  Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
-   (use "git checkout -- <file>..." to discard changes in working directory)
+   (use "git restore <file>..." to discard changes in working directory)
  
        modified:   main.txt
  
@@@ -780,24 -780,6 +780,24 @@@ EO
        test_i18ncmp expected actual
  '
  
 +test_expect_success 'status when cherry-picking after committing conflict resolution' '
 +      git reset --hard cherry_branch &&
 +      test_when_finished "git cherry-pick --abort" &&
 +      test_must_fail git cherry-pick cherry_branch_second one_cherry &&
 +      echo end >main.txt &&
 +      git commit -a &&
 +      cat >expected <<EOF &&
 +On branch cherry_branch
 +Cherry-pick currently in progress.
 +  (run "git cherry-pick --continue" to continue)
 +  (use "git cherry-pick --abort" to cancel the cherry-pick operation)
 +
 +nothing to commit (use -u to show untracked files)
 +EOF
 +      git status --untracked-files=no >actual &&
 +      test_i18ncmp expected actual
 +'
 +
  test_expect_success 'status showing detached at and from a tag' '
        test_commit atag tagging &&
        git checkout atag &&
@@@ -834,7 -816,7 +834,7 @@@ You are currently reverting commit $TO_
    (use "git revert --abort" to cancel the revert operation)
  
  Unmerged paths:
-   (use "git reset HEAD <file>..." to unstage)
+   (use "git restore --staged <file>..." to unstage)
    (use "git add <file>..." to mark resolution)
  
        both modified:   to-revert.txt
@@@ -855,7 -837,7 +855,7 @@@ You are currently reverting commit $TO_
    (use "git revert --abort" to cancel the revert operation)
  
  Changes to be committed:
-   (use "git reset HEAD <file>..." to unstage)
+   (use "git restore --staged <file>..." to unstage)
  
        modified:   to-revert.txt
  
@@@ -870,24 -852,6 +870,24 @@@ test_expect_success 'status after rever
        cat >expected <<\EOF &&
  On branch master
  nothing to commit (use -u to show untracked files)
 +EOF
 +      git status --untracked-files=no >actual &&
 +      test_i18ncmp expected actual
 +'
 +
 +test_expect_success 'status while reverting after committing conflict resolution' '
 +      test_when_finished "git revert --abort" &&
 +      git reset --hard new &&
 +      test_must_fail git revert old new &&
 +      echo reverted >to-revert.txt &&
 +      git commit -a &&
 +      cat >expected <<EOF &&
 +On branch master
 +Revert currently in progress.
 +  (run "git revert --continue" to continue)
 +  (use "git revert --abort" to cancel the revert operation)
 +
 +nothing to commit (use -u to show untracked files)
  EOF
        git status --untracked-files=no >actual &&
        test_i18ncmp expected actual
diff --combined unpack-trees.c
index 50189909b86d6ab48a0aa15893f5b66c2e9ce613,5ff64a983d8e8b09e1d842c5243300a030a929c9..dab713203e15a83e452119d0140bb4c28c8a7bf0
@@@ -219,9 -219,6 +219,9 @@@ static int add_rejected_path(struct unp
                             enum unpack_trees_error_types e,
                             const char *path)
  {
 +      if (o->quiet)
 +              return -1;
 +
        if (!o->show_all_errors)
                return error(ERRORMSG(o, e), super_prefixed(path));
  
@@@ -271,7 -268,8 +271,7 @@@ static int check_submodule_move_head(co
                flags |= SUBMODULE_MOVE_HEAD_FORCE;
  
        if (submodule_move_head(ce->name, old_id, new_id, flags))
 -              return o->gently ? -1 :
 -                                 add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
 +              return add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
        return 0;
  }
  
@@@ -315,7 -313,7 +315,7 @@@ static struct progress *get_progress(st
                        total++;
        }
  
-       return start_delayed_progress(_("Checking out files"), total);
+       return start_delayed_progress(_("Updating files"), total);
  }
  
  static void setup_collided_checkout_detection(struct checkout *state,
@@@ -406,21 -404,20 +406,21 @@@ static int check_updates(struct unpack_
                 * below.
                 */
                struct oid_array to_fetch = OID_ARRAY_INIT;
 -              int fetch_if_missing_store = fetch_if_missing;
 -              fetch_if_missing = 0;
                for (i = 0; i < index->cache_nr; i++) {
                        struct cache_entry *ce = index->cache[i];
 -                      if ((ce->ce_flags & CE_UPDATE) &&
 -                          !S_ISGITLINK(ce->ce_mode)) {
 -                              if (!has_object_file(&ce->oid))
 -                                      oid_array_append(&to_fetch, &ce->oid);
 -                      }
 +
 +                      if (!(ce->ce_flags & CE_UPDATE) ||
 +                          S_ISGITLINK(ce->ce_mode))
 +                              continue;
 +                      if (!oid_object_info_extended(the_repository, &ce->oid,
 +                                                    NULL,
 +                                                    OBJECT_INFO_FOR_PREFETCH))
 +                              continue;
 +                      oid_array_append(&to_fetch, &ce->oid);
                }
                if (to_fetch.nr)
                        fetch_objects(repository_format_partial_clone,
                                      to_fetch.oid, to_fetch.nr);
 -              fetch_if_missing = fetch_if_missing_store;
                oid_array_clear(&to_fetch);
        }
        for (i = 0; i < index->cache_nr; i++) {
@@@ -710,6 -707,7 +710,6 @@@ static int index_pos_by_traverse_info(s
   * instead of ODB since we already know what these trees contain.
   */
  static int traverse_by_cache_tree(int pos, int nr_entries, int nr_names,
 -                                struct name_entry *names,
                                  struct traverse_info *info)
  {
        struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
@@@ -799,7 -797,7 +799,7 @@@ static int traverse_trees_recursive(in
                 * unprocessed entries before 'pos'.
                 */
                bottom = o->cache_bottom;
 -              ret = traverse_by_cache_tree(pos, nr_entries, n, names, info);
 +              ret = traverse_by_cache_tree(pos, nr_entries, n, info);
                o->cache_bottom = bottom;
                return ret;
        }
@@@ -1042,7 -1040,7 +1042,7 @@@ static int unpack_nondirectories(int n
  static int unpack_failed(struct unpack_trees_options *o, const char *message)
  {
        discard_index(&o->result);
 -      if (!o->gently && !o->exiting_early) {
 +      if (!o->quiet && !o->exiting_early) {
                if (message)
                        return error("%s", message);
                return -1;
@@@ -1620,8 -1618,6 +1620,8 @@@ int unpack_trees(unsigned len, struct t
                                                  WRITE_TREE_SILENT |
                                                  WRITE_TREE_REPAIR);
                }
 +
 +              o->result.updated_workdir = 1;
                discard_index(o->dst_index);
                *o->dst_index = o->result;
        } else {
@@@ -1649,7 -1645,8 +1649,7 @@@ return_failed
  static int reject_merge(const struct cache_entry *ce,
                        struct unpack_trees_options *o)
  {
 -      return o->gently ? -1 :
 -              add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 +      return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
  }
  
  static int same(const struct cache_entry *a, const struct cache_entry *b)
@@@ -1696,7 -1693,8 +1696,7 @@@ static int verify_uptodate_1(const stru
                        int r = check_submodule_move_head(ce,
                                "HEAD", oid_to_hex(&ce->oid), o);
                        if (r)
 -                              return o->gently ? -1 :
 -                                      add_rejected_path(o, error_type, ce->name);
 +                              return add_rejected_path(o, error_type, ce->name);
                        return 0;
                }
  
        }
        if (errno == ENOENT)
                return 0;
 -      return o->gently ? -1 :
 -              add_rejected_path(o, error_type, ce->name);
 +      return add_rejected_path(o, error_type, ce->name);
  }
  
  int verify_uptodate(const struct cache_entry *ce,
@@@ -1760,6 -1759,7 +1760,6 @@@ static void invalidate_ce_path(const st
   */
  static int verify_clean_submodule(const char *old_sha1,
                                  const struct cache_entry *ce,
 -                                enum unpack_trees_error_types error_type,
                                  struct unpack_trees_options *o)
  {
        if (!submodule_from_ce(ce))
  }
  
  static int verify_clean_subdirectory(const struct cache_entry *ce,
 -                                   enum unpack_trees_error_types error_type,
                                     struct unpack_trees_options *o)
  {
        /*
                if (!sub_head && oideq(&oid, &ce->oid))
                        return 0;
                return verify_clean_submodule(sub_head ? NULL : oid_to_hex(&oid),
 -                                            ce, error_type, o);
 +                                            ce, o);
        }
  
        /*
                d.exclude_per_dir = o->dir->exclude_per_dir;
        i = read_directory(&d, o->src_index, pathbuf, namelen+1, NULL);
        if (i)
 -              return o->gently ? -1 :
 -                      add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
 +              return add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
        free(pathbuf);
        return cnt;
  }
@@@ -1887,7 -1889,7 +1887,7 @@@ static int check_ok_to_remove(const cha
                 * files that are in "foo/" we would lose
                 * them.
                 */
 -              if (verify_clean_subdirectory(ce, error_type, o) < 0)
 +              if (verify_clean_subdirectory(ce, o) < 0)
                        return -1;
                return 0;
        }
                        return 0;
        }
  
 -      return o->gently ? -1 :
 -              add_rejected_path(o, error_type, name);
 +      return add_rejected_path(o, error_type, name);
  }
  
  /*
@@@ -2343,7 -2346,7 +2343,7 @@@ int bind_merge(const struct cache_entr
                return error("Cannot do a bind merge of %d trees",
                             o->merge_size);
        if (a && old)
 -              return o->gently ? -1 :
 +              return o->quiet ? -1 :
                        error(ERRORMSG(o, ERROR_BIND_OVERLAP),
                              super_prefixed(a->name),
                              super_prefixed(old->name));
@@@ -2383,7 -2386,7 +2383,7 @@@ int oneway_merge(const struct cache_ent
                if (o->update && S_ISGITLINK(old->ce_mode) &&
                    should_update_submodules() && !verify_uptodate(old, o))
                        update |= CE_UPDATE;
 -              add_entry(o, old, update, 0);
 +              add_entry(o, old, update, CE_STAGEMASK);
                return 0;
        }
        return merged_entry(a, old, o);
diff --combined wt-status.c
index c29e4bf091ad7744124f2a47abed058198422fa2,19fd1add75439e2e5351f4c38d46f4cc2faf8b7f..dd5270647ecf277f4a71d0b81dbe5e9f4857b41d
@@@ -17,7 -17,6 +17,7 @@@
  #include "utf8.h"
  #include "worktree.h"
  #include "lockfile.h"
 +#include "sequencer.h"
  
  static const char cut_line[] =
  "------------------------ >8 ------------------------\n";
@@@ -179,9 -178,15 +179,15 @@@ static void wt_longstatus_print_unmerge
                return;
        if (s->whence != FROM_COMMIT)
                ;
-       else if (!s->is_initial)
-               status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-       else
+       else if (!s->is_initial) {
+               if (!strcmp(s->reference, "HEAD"))
+                       status_printf_ln(s, c,
+                                        _("  (use \"git restore --staged <file>...\" to unstage)"));
+               else
+                       status_printf_ln(s, c,
+                                        _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
+                                        s->reference);
+       } else
                status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
  
        if (!both_deleted) {
@@@ -206,9 -211,15 +212,15 @@@ static void wt_longstatus_print_cached_
                return;
        if (s->whence != FROM_COMMIT)
                ; /* NEEDSWORK: use "git reset --unresolve"??? */
-       else if (!s->is_initial)
-               status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-       else
+       else if (!s->is_initial) {
+               if (!strcmp(s->reference, "HEAD"))
+                       status_printf_ln(s, c
+                                        , _("  (use \"git restore --staged <file>...\" to unstage)"));
+               else
+                       status_printf_ln(s, c,
+                                        _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
+                                        s->reference);
+       } else
                status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
        status_printf_ln(s, c, "%s", "");
  }
@@@ -226,7 -237,7 +238,7 @@@ static void wt_longstatus_print_dirty_h
                status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
        else
                status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
-       status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
+       status_printf_ln(s, c, _("  (use \"git restore <file>...\" to discard changes in working directory)"));
        if (has_dirty_submodules)
                status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
        status_printf_ln(s, c, "%s", "");
@@@ -1007,19 -1018,13 +1019,19 @@@ size_t wt_status_locate_end(const char 
        return len;
  }
  
 -void wt_status_add_cut_line(FILE *fp)
 +void wt_status_append_cut_line(struct strbuf *buf)
  {
        const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
 +
 +      strbuf_commented_addf(buf, "%s", cut_line);
 +      strbuf_add_commented_lines(buf, explanation, strlen(explanation));
 +}
 +
 +void wt_status_add_cut_line(FILE *fp)
 +{
        struct strbuf buf = STRBUF_INIT;
  
 -      fprintf(fp, "%c %s", comment_line_char, cut_line);
 -      strbuf_add_commented_lines(&buf, explanation, strlen(explanation));
 +      wt_status_append_cut_line(&buf);
        fputs(buf.buf, fp);
        strbuf_release(&buf);
  }
@@@ -1215,9 -1220,7 +1227,9 @@@ static void abbrev_sha1_in_line(struct 
        int i;
  
        if (starts_with(line->buf, "exec ") ||
 -          starts_with(line->buf, "x "))
 +          starts_with(line->buf, "x ") ||
 +          starts_with(line->buf, "label ") ||
 +          starts_with(line->buf, "l "))
                return;
  
        split = strbuf_split_max(line, ' ', 3);
@@@ -1389,22 -1392,12 +1401,22 @@@ static void show_rebase_in_progress(str
  static void show_cherry_pick_in_progress(struct wt_status *s,
                                         const char *color)
  {
 -      status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
 -                      find_unique_abbrev(&s->state.cherry_pick_head_oid, DEFAULT_ABBREV));
 +      if (is_null_oid(&s->state.cherry_pick_head_oid))
 +              status_printf_ln(s, color,
 +                      _("Cherry-pick currently in progress."));
 +      else
 +              status_printf_ln(s, color,
 +                      _("You are currently cherry-picking commit %s."),
 +                      find_unique_abbrev(&s->state.cherry_pick_head_oid,
 +                                         DEFAULT_ABBREV));
 +
        if (s->hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
                                _("  (fix conflicts and run \"git cherry-pick --continue\")"));
 +              else if (is_null_oid(&s->state.cherry_pick_head_oid))
 +                      status_printf_ln(s, color,
 +                              _("  (run \"git cherry-pick --continue\" to continue)"));
                else
                        status_printf_ln(s, color,
                                _("  (all conflicts fixed: run \"git cherry-pick --continue\")"));
  static void show_revert_in_progress(struct wt_status *s,
                                    const char *color)
  {
 -      status_printf_ln(s, color, _("You are currently reverting commit %s."),
 -                       find_unique_abbrev(&s->state.revert_head_oid, DEFAULT_ABBREV));
 +      if (is_null_oid(&s->state.revert_head_oid))
 +              status_printf_ln(s, color,
 +                      _("Revert currently in progress."));
 +      else
 +              status_printf_ln(s, color,
 +                      _("You are currently reverting commit %s."),
 +                      find_unique_abbrev(&s->state.revert_head_oid,
 +                                         DEFAULT_ABBREV));
        if (s->hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
                                _("  (fix conflicts and run \"git revert --continue\")"));
 +              else if (is_null_oid(&s->state.revert_head_oid))
 +                      status_printf_ln(s, color,
 +                              _("  (run \"git revert --continue\" to continue)"));
                else
                        status_printf_ln(s, color,
                                _("  (all conflicts fixed: run \"git revert --continue\")"));
@@@ -1602,7 -1586,6 +1614,7 @@@ void wt_status_get_state(struct reposit
  {
        struct stat st;
        struct object_id oid;
 +      enum replay_action action;
  
        if (!stat(git_path_merge_head(r), &st)) {
                wt_status_check_rebase(NULL, state);
                state->revert_in_progress = 1;
                oidcpy(&state->revert_head_oid, &oid);
        }
 -
 +      if (!sequencer_get_last_command(r, &action)) {
 +              if (action == REPLAY_PICK) {
 +                      state->cherry_pick_in_progress = 1;
 +                      oidcpy(&state->cherry_pick_head_oid, &null_oid);
 +              } else {
 +                      state->revert_in_progress = 1;
 +                      oidcpy(&state->revert_head_oid, &null_oid);
 +              }
 +      }
        if (get_detached_from)
                wt_status_get_detached_from(r, state);
  }
@@@ -1676,9 -1651,9 +1688,9 @@@ static void wt_longstatus_print(struct 
                        } else if (s->state.detached_from) {
                                branch_name = s->state.detached_from;
                                if (s->state.detached_at)
 -                                      on_what = _("HEAD detached at ");
 +                                      on_what = HEAD_DETACHED_AT;
                                else
 -                                      on_what = _("HEAD detached from ");
 +                                      on_what = HEAD_DETACHED_FROM;
                        } else {
                                branch_name = "";
                                on_what = _("Not currently on any branch.");
@@@ -1888,7 -1863,7 +1900,7 @@@ static void wt_shortstatus_print_tracki
        color_fprintf(s->fp, branch_color_local, "%s", branch_name);
  
        sti = stat_tracking_info(branch, &num_ours, &num_theirs, &base,
 -                               s->ahead_behind_flags);
 +                               0, s->ahead_behind_flags);
        if (sti < 0) {
                if (!base)
                        goto conclude;
@@@ -2027,7 -2002,7 +2039,7 @@@ static void wt_porcelain_v2_print_track
                branch = branch_get(branch_name);
                base = NULL;
                ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
 -                                           &base, s->ahead_behind_flags);
 +                                           &base, 0, s->ahead_behind_flags);
                if (base) {
                        base = shorten_unambiguous_ref(base, 0);
                        fprintf(s->fp, "# branch.upstream %s%c", base, eol);
@@@ -2076,7 -2051,9 +2088,7 @@@ static void wt_porcelain_v2_submodule_s
  /*
   * Fix-up changed entries before we print them.
   */
 -static void wt_porcelain_v2_fix_up_changed(
 -      struct string_list_item *it,
 -      struct wt_status *s)
 +static void wt_porcelain_v2_fix_up_changed(struct string_list_item *it)
  {
        struct wt_status_change_data *d = it->util;
  
@@@ -2136,7 -2113,7 +2148,7 @@@ static void wt_porcelain_v2_print_chang
        char submodule_token[5];
        char sep_char, eol_char;
  
 -      wt_porcelain_v2_fix_up_changed(it, s);
 +      wt_porcelain_v2_fix_up_changed(it);
        wt_porcelain_v2_submodule_state(d, submodule_token);
  
        key[0] = d->index_status ? d->index_status : '.';