1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-04-19 09:13:51 +02:00

Merge branch 'nd/switch-and-restore'

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"
  ...
This commit is contained in:
Junio C Hamano 2019-07-09 15:25:44 -07:00
commit f496b064fc
65 changed files with 1956 additions and 700 deletions

2
.gitignore vendored
View File

@ -139,6 +139,7 @@
/git-request-pull
/git-rerere
/git-reset
/git-restore
/git-rev-list
/git-rev-parse
/git-revert
@ -163,6 +164,7 @@
/git-submodule
/git-submodule--helper
/git-svn
/git-switch
/git-symbolic-ref
/git-tag
/git-unpack-file

View File

@ -42,7 +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
@ -62,12 +63,14 @@ advice.*::
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

View File

@ -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 @@ branch.autoSetupMerge::
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.

View File

@ -1,5 +1,6 @@
checkout.defaultRemote::
When you run 'git checkout <something>' and only have one
When you run 'git checkout <something>'
or 'git switch <something>' and only have one
remote, it may implicitly fall back on checking out and
tracking e.g. 'origin/<something>'. This stops working as soon
as you have more than one remote with a '<something>'
@ -8,16 +9,10 @@ checkout.defaultRemote::
disambiguation. The typical use-case is to set this to
`origin`.
+
Currently this is used by linkgit:git-checkout[1] when 'git checkout
<something>' will checkout the '<something>' branch on another remote,
Currently this is used by linkgit:git-switch[1] and
linkgit:git-checkout[1] when 'git checkout <something>'
or 'git switch <something>'
will checkout the '<something>' branch on another remote,
and by linkgit:git-worktree[1] when 'git worktree add' refers to a
remote branch. This setting might be used for other checkout-like
commands or functionality in the future.
checkout.optimizeNewBranch::
Optimizes the performance of "git checkout -b <new_branch>" when
using sparse-checkout. When set to true, git will not update the
repo based on the current sparse-checkout settings. This means it
will not update the skip-worktree bit in the index nor add/remove
files in the working directory to reflect the current sparse checkout
settings nor will it show the local changes.

View File

@ -78,7 +78,8 @@ diff.external::
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

View File

@ -2,7 +2,8 @@ interactive.singleKey::
In interactive commands, allow the user to provide one-letter
input with a single key (i.e., without hitting enter).
Currently this is used by the `--patch` mode of
linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
linkgit:git-add[1], linkgit:git-checkout[1],
linkgit:git-restore[1], linkgit:git-commit[1],
linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
setting is silently ignored if portable keystroke input
is not available; requires the Perl module Term::ReadKey.

View File

@ -60,7 +60,7 @@ 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
@ -214,7 +214,7 @@ This option is only applicable in non-verbose mode.
+
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 +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
@ -350,9 +350,9 @@ 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:

View File

@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).
When run with `--branch` option in a repository, the input is first
expanded for the ``previous checkout syntax''
`@{-n}`. For example, `@{-1}` is a way to refer the last thing that
was checked out using "git checkout" operation. This option should be
was checked out using "git switch" or "git checkout" operation.
This option should be
used by porcelains to accept this syntax anywhere a branch name is
expected, so they can act as if you typed the branch name. As an
exception note that, the ``previous checkout operation'' might result

View File

@ -23,31 +23,22 @@ or the specified tree. If no paths are given, 'git checkout' will
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 @@ if exists, for the current branch.
`--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 @@ 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 @@ OPTIONS
--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
@ -127,7 +119,7 @@ OPTIONS
-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 @@ on your side branch as `theirs` (i.e. "one contributor's work on top
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 @@ If no `-b` option is given, the name of the new branch will be
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
@ -189,21 +202,21 @@ explicitly give a name with `-b` in such a case.
--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 @@ whose full history contains proprietary or otherwise encumbered bits of
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::
@ -246,25 +259,25 @@ 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
@ -272,38 +285,42 @@ Note that this option uses the no overlay mode by default (see also
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.
Just like linkgit:git-submodule[1], this will detach `HEAD` of the
submodule.
--no-guess::
Do not attempt to create a branch if a remote tracking branch
of the same name exists.
--[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`.
@ -312,7 +329,7 @@ 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
@ -326,9 +343,9 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
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 +358,10 @@ a---b---c branch 'master' (refers to commit '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 +378,7 @@ a---b---c---d branch 'master' (refers to commit 'd')
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 +393,9 @@ a---b---c---d branch 'master' (refers to commit 'd')
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 +412,7 @@ a---b---c---d branch 'master' (refers to commit 'd')
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 +430,7 @@ a---b---c---d branch 'master' (refers to commit 'd')
------------
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 +445,9 @@ a---b---c---d branch 'master' (refers to commit 'd')
------------
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 +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 +479,12 @@ $ git log -g -2 HEAD
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 +492,7 @@ EXAMPLES
--------
. 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 +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 +533,7 @@ $ git checkout -- hello.c
$ 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 +574,11 @@ $ edit frotz
$ git add frotz
------------
SEE ALSO
--------
linkgit:git-switch[1],
linkgit:git-restore[1]
GIT
---
Part of the linkgit:git[1] suite

View File

@ -63,7 +63,7 @@ OPTIONS
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::

View File

@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in
your working tree are temporarily stored to a staging area
called the "index" with 'git add'. A file can be
reverted back, only in the index but not in the working tree,
to that of the last commit with `git reset HEAD -- <file>`,
to that of the last commit with `git restore --staged <file>`,
which effectively reverts 'git add' and prevents the changes to
this file from participating in the next commit. After building
the state to be committed incrementally with these commands,

View File

@ -426,8 +426,8 @@ One way to test if your MUA is set up correctly is:
* 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.

View File

@ -149,7 +149,7 @@ instead.
Discussion on fork-point mode
-----------------------------
After working on the `topic` branch created with `git checkout -b
After working on the `topic` branch created with `git switch -c
topic origin/master`, the history of remote-tracking branch
`origin/master` may have been rewound and rebuilt, leading to a
history of this shape:

View File

@ -87,6 +87,11 @@ will be appended to the specified message.
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.

View File

@ -17,7 +17,7 @@ SYNOPSIS
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

View File

@ -230,7 +230,7 @@ $ git branch -r
staging/master
staging/staging-linus
staging/staging-next
$ git checkout -b staging staging/master
$ git switch -c staging staging/master
...
------------

View File

@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.
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 @@ the upstream might have been advanced since the test merge `+`,
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 @@ merges, you could blow away the test merge, and keep building on
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

View File

@ -25,12 +25,13 @@ The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
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 @@ but carries forward unmerged index entries.
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 @@ See also the `--amend` option to linkgit:git-commit[1].
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 @@ working tree are not in any shape to be committed yet, but you
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 @@ reset it while keeping the changes in your working tree.
+
------------
$ 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`.

View File

@ -0,0 +1,185 @@
git-restore(1)
==============
NAME
----
git-restore - Restore working tree files
SYNOPSIS
--------
[verse]
'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
DESCRIPTION
-----------
Restore specified paths in the working tree with some contents from a
restore source. If a path is tracked but does not exist in the restore
source, it will be removed to match the source.
The command can also be used to restore the content in the index with
`--staged`, or restore both the working tree and the index with
`--staged --worktree`.
By default, the restore sources for working tree and the index are the
index and `HEAD` respectively. `--source` could be used to specify a
commit as the restore source.
See "Reset, restore and revert" in linkgit:git[1] for the differences
between the three commands.
THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
OPTIONS
-------
-s <tree>::
--source=<tree>::
Restore the working tree files with the content from the given
tree. It is common to specify the source tree by naming a
commit, branch or tag associated with it.
+
If not specified, the default restore source for the working tree is
the index, and the default restore source for the index index is
`HEAD`. When both `--staged` and `--worktree` are specified,
`--source` must also be specified.
-p::
--patch::
Interactively select hunks in the difference between the
restore source and the restore location. See the ``Interactive
Mode'' section of linkgit:git-add[1] to learn how to operate
the `--patch` mode.
+
Note that `--patch` can accept no pathspec and will prompt to restore
all modified paths.
-W::
--worktree::
-S::
--staged::
Specify the restore location. If neither option is specified,
by default the working tree is restored. Specifying `--staged`
will only restore the index. Specifying both restores both.
-q::
--quiet::
Quiet, suppress feedback messages. Implies `--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
attached to a terminal, regardless of `--quiet`.
--ours::
--theirs::
When restoring files in the working tree from the index, use
stage #2 ('ours') or #3 ('theirs') for unmerged paths.
+
Note that during `git rebase` and `git pull --rebase`, 'ours' and
'theirs' may appear swapped. See the explanation of the same options
in linkgit:git-checkout[1] for details.
-m::
--merge::
When restoring files on the working tree from the index,
recreate the conflicted merge in the unmerged paths.
--conflict=<style>::
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" (default) and "diff3" (in addition to what is
shown by "merge" style, shows the original contents).
--ignore-unmerged::
When restoring files on the working tree from the index, do
not abort the operation if there are unmerged entries and
neither `--ours`, `--theirs`, `--merge` or `--conflict` is
specified. Unmerged paths on the working tree are left alone.
--ignore-skip-worktree-bits::
In sparse checkout mode, by default is to only update entries
matched by `<pathspec>` and sparse patterns in
$GIT_DIR/info/sparse-checkout. This option ignores the sparse
patterns and unconditionally restores any files in
`<pathspec>`.
--overlay::
--no-overlay::
In overlay mode, the command never removes files when
restoring. In no-overlay mode, tracked files that do not
appear in the `--source` tree are removed, to make them match
`<tree>` exactly. The default is no-overlay mode.
EXAMPLES
--------
The following sequence switches to the `master` branch, reverts the
`Makefile` to two revisions back, deletes hello.c by mistake, and gets
it back from the index.
------------
$ git switch master
$ git restore --source master~2 Makefile <1>
$ rm -f hello.c
$ git restore hello.c <2>
------------
<1> take a file out of another commit
<2> restore hello.c from the index
If you want to restore _all_ C source files to match the version in
the index, you can say
------------
$ git restore '*.c'
------------
Note the quotes around `*.c`. The file `hello.c` will also be
restored, even though it is no longer in the working tree, because the
file globbing is used to match entries in the index (not in the
working tree by the shell).
To restore all files in the current directory
------------
$ git restore .
------------
or to restore all working tree files with 'top' pathspec magic (see
linkgit:gitglossary[7])
------------
$ git restore :/
------------
To restore a file in the index to match the version in `HEAD` (this is
the same as using linkgit:git-reset[1])
------------
$ git restore --staged hello.c
------------
or you can restore both the index and the working tree (this the same
as using linkgit:git-checkout[1])
------------
$ git restore --source=HEAD --staged --worktree hello.c
------------
or the short form which is more practical but less readable:
------------
$ git restore -s@ -SW hello.c
------------
SEE ALSO
--------
linkgit:git-checkout[1],
linkgit:git-reset[1]
GIT
---
Part of the linkgit:git[1] suite

View File

@ -26,10 +26,13 @@ effect of some earlier commits (often only a faulty one). If you want to
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>...::

View File

@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:
+
----------------------------------------------------------------
# ... 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 @@ SEE ALSO
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
---

View File

@ -0,0 +1,273 @@
git-switch(1)
=============
NAME
----
git-switch - Switch branches
SYNOPSIS
--------
[verse]
'git switch' [<options>] [--no-guess] <branch>
'git switch' [<options>] --detach [<start-point>]
'git switch' [<options>] (-c|-C) <new-branch> [<start-point>]
'git switch' [<options>] --orphan <new-branch>
DESCRIPTION
-----------
Switch to a specified branch. The working tree and the index are
updated to match the branch. All new commits will be added to the tip
of this branch.
Optionally a new branch could be created with either `-c`, `-C`,
automatically from a remote branch of same name (see `--guess`), or
detach the working tree from any branch with `--detach`, along with
switching.
Switching branches does not require a clean index and working tree
(i.e. no differences compared to `HEAD`). The operation is aborted
however if the operation leads to loss of local changes, unless told
otherwise with `--discard-changes` or `--merge`.
THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
OPTIONS
-------
<branch>::
Branch to switch to.
<new-branch>::
Name for the new branch.
<start-point>::
The starting point for the new branch. Specifying a
`<start-point>` allows you to create a branch based on some
other point in history than where HEAD currently points. (Or,
in the case of `--detach`, allows you to inspect and detach
from some other point.)
+
You can use the `@{-N}` syntax to refer to the N-th last
branch/commit switched to using "git switch" or "git checkout"
operation. You may also specify `-` which is synonymous to `@{-1}`.
This is often used to switch quickly between two branches, or to undo
a branch switch by mistake.
+
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`.
-c <new-branch>::
--create <new-branch>::
Create a new branch named `<new-branch>` starting at
`<start-point>` before switching to the branch. This is a
convenient shortcut for:
+
------------
$ git branch <new-branch>
$ git switch <new-branch>
------------
-C <new-branch>::
--force-create <new-branch>::
Similar to `--create` except that if `<new-branch>` already
exists, it will be reset to `<start-point>`. This is a
convenient shortcut for:
+
------------
$ git branch -f <new-branch>
$ git switch <new-branch>
------------
-d::
--detach::
Switch to a commit for inspection and discardable
experiments. See the "DETACHED HEAD" section in
linkgit:git-checkout[1] for details.
--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 switch -c <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].
+
`--guess` is the default behavior. Use `--no-guess` to disable it.
-f::
--force::
An alias for `--discard-changes`.
--discard-changes::
Proceed even if the index or the working tree differs from
`HEAD`. Both the index and working tree are restored to match
the switching target. If `--recurse-submodules` is specified,
submodule content is also restored to match the switching
target. This is used to throw away local changes.
-m::
--merge::
If you have local modifications to one or more files that are
different between the current branch and the branch to which
you are switching, the command refuses to switch branches in
order to preserve your modifications in context. However,
with this option, a three-way merge between the current
branch, your working tree contents, and the new branch is
done, and you will be on the new branch.
+
When a merge conflict happens, the index entries for conflicting
paths are left unmerged, and you need to resolve the conflicts
and mark the resolved paths with `git add` (or `git rm` if the merge
should result in deletion of the path).
--conflict=<style>::
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" (default) and "diff3" (in addition to what is shown by
"merge" style, shows the original contents).
-q::
--quiet::
Quiet, suppress feedback messages.
--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
attached to a terminal, regardless of `--quiet`.
-t::
--track::
When creating a new branch, set up "upstream" configuration.
`-c` is implied. See `--track` in linkgit:git-branch[1] for
details.
+
If no `-c` option is given, the name of the new branch will be 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 guessing results in an empty
name, the guessing is aborted. You can explicitly give a name with
`-c` in such a case.
--no-track::
Do not set up "upstream" configuration, even if the
`branch.autoSetupMerge` configuration variable is true.
--orphan <new-branch>::
Create a new 'orphan' branch, named `<new-branch>`. All
tracked files are removed.
--ignore-other-worktrees::
`git switch` refuses when the wanted ref is already
checked out by another worktree. This option makes it check
the ref out anyway. In other words, the ref can be held by
more than one worktree.
--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 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 `HEAD` of the
submodules.
EXAMPLES
--------
The following command switches to the "master" branch:
------------
$ git switch master
------------
After working in the wrong branch, switching to the correct branch
would be done using:
------------
$ git switch mytopic
------------
However, your "wrong" branch and correct "mytopic" branch may differ
in files that you have modified locally, in which case the above
switch would fail like this:
------------
$ git switch mytopic
error: You have local changes to 'frotz'; not switching branches.
------------
You can give the `-m` flag to the command, which would try a three-way
merge:
------------
$ git switch -m mytopic
Auto-merging frotz
------------
After this three-way merge, the local modifications are _not_
registered in your index file, so `git diff` would show you what
changes you made since the tip of the new branch.
To switch back to the previous branch before we switched to mytopic
(i.e. "master" branch):
------------
$ git switch -
------------
You can grow a new branch from any commit. For example, switch to
"HEAD~3" and create branch "fixup":
------------
$ git switch -c fixup HEAD~3
Switched to a new branch 'fixup'
------------
If you want to start a new branch from a remote branch of the same
name:
------------
$ git switch new-topic
Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
Switched to a new branch 'new-topic'
------------
To check out commit `HEAD~3` for temporary inspection or experiment
without creating a new branch:
------------
$ git switch --detach HEAD~3
HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
------------
If it turns out whatever you have done is worth keeping, you can
always create a new name for it (without switching away):
------------
$ git switch -c good-surprises
------------
SEE ALSO
--------
linkgit:git-checkout[1],
linkgit:git-branch[1]
GIT
---
Part of the linkgit:git[1] suite

View File

@ -211,6 +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)
-----------------------------

View File

@ -112,7 +112,8 @@ Checking-out and checking-in
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'.

View File

@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.
things:
+
--------------------------------
$ git checkout -- *.c
$ git checkout -- \*.c
$ git restore *.c
$ git restore \*.c
--------------------------------
+
The former lets your shell expand the fileglob, and you are asking
@ -209,6 +209,18 @@ See also http://marc.info/?l=git&m=116563135620359 and
http://marc.info/?l=git&m=119150393620273 for further
information.
Some other commands that also work on files in the working tree and/or
in the index can take `--staged` and/or `--worktree`.
* `--staged` is exactly like `--cached`, which is used to ask a
command to only work on the index, not the working tree.
* `--worktree` is the opposite, to ask a command to work on the
working tree only, not the index.
* The two options can be specified together to ask a command to work
on both the index and the working tree.
GIT
---
Part of the linkgit:git[1] suite

View File

@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just
saying that you want to check out a new branch:
------------
$ git checkout -b mybranch
$ git switch -c mybranch
------------
will create a new branch based at the current `HEAD` position, and switch
@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.
In other words, if you have an earlier tag or branch, you'd just do
------------
$ git checkout -b mybranch earlier-commit
$ git switch -c mybranch earlier-commit
------------
and it would create the new branch `mybranch` at the earlier commit,
@ -765,7 +765,7 @@ and check out the state at that time.
You can always just jump back to your original `master` branch by doing
------------
$ git checkout master
$ git switch master
------------
(or any other branch-name, for that matter) and if you forget which
@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]
which will simply _create_ the branch, but will not do anything further.
You can then later -- once you decide that you want to actually develop
on that branch -- switch to that branch with a regular 'git checkout'
on that branch -- switch to that branch with a regular 'git switch'
with the branchname as the argument.
@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in
that branch, and do some work there.
------------------------------------------------
$ git checkout mybranch
$ git switch mybranch
$ echo "Work, work, work" >>hello
$ git commit -m "Some work." -i hello
------------------------------------------------
@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back
to the master branch, and editing the same file differently there:
------------
$ git checkout master
$ git switch master
------------
Here, take a moment to look at the contents of `hello`, and notice how they
@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
'git merge' to get the "upstream changes" back to your branch.
------------
$ git checkout mybranch
$ git switch mybranch
$ git merge -m "Merge upstream changes." master
------------
@ -1133,9 +1133,8 @@ Remember, before running 'git merge', our `master` head was at
work." commit.
------------
$ git checkout mybranch
$ git reset --hard master^2
$ git checkout master
$ git switch -C mybranch master^2
$ git switch master
$ git reset --hard master^
------------

View File

@ -41,7 +41,7 @@ following commands.
* linkgit:git-log[1] to see what happened.
* linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
* linkgit:git-switch[1] and linkgit:git-branch[1] to switch
branches.
* linkgit:git-add[1] to manage the index file.
@ -51,8 +51,7 @@ following commands.
* linkgit:git-commit[1] to advance the current branch.
* linkgit:git-reset[1] and linkgit:git-checkout[1] (with
pathname parameters) to undo changes.
* linkgit:git-restore[1] to undo changes.
* linkgit:git-merge[1] to merge between local branches.
@ -80,9 +79,9 @@ $ git tag v2.43 <2>
Create a topic branch and develop.::
+
------------
$ git checkout -b alsa-audio <1>
$ git switch -c alsa-audio <1>
$ edit/compile/test
$ git checkout -- curses/ux_audio_oss.c <2>
$ git restore curses/ux_audio_oss.c <2>
$ git add curses/ux_audio_alsa.c <3>
$ edit/compile/test
$ git diff HEAD <4>
@ -90,7 +89,7 @@ $ git commit -a -s <5>
$ edit/compile/test
$ git diff HEAD^ <6>
$ git commit -a --amend <7>
$ git checkout master <8>
$ git switch master <8>
$ git merge alsa-audio <9>
$ git log --since='3 days ago' <10>
$ git log v2.43.. curses/ <11>
@ -148,11 +147,11 @@ Clone the upstream and work on it. Feed changes to upstream.::
------------
$ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
$ cd my2.6
$ git checkout -b mine master <1>
$ git switch -c mine master <1>
$ edit/compile/test; git commit -a -s <2>
$ git format-patch master <3>
$ git send-email --to="person <email@example.com>" 00*.patch <4>
$ git checkout master <5>
$ git switch master <5>
$ git pull <6>
$ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
$ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
@ -194,7 +193,7 @@ satellite$ edit/compile/test/commit
satellite$ git push origin <4>
mothership$ cd frotz
mothership$ git checkout master
mothership$ git switch master
mothership$ git merge satellite/master <5>
------------
+
@ -216,7 +215,7 @@ machine into the master branch.
Branch off of a specific tag.::
+
------------
$ git checkout -b private2.6.14 v2.6.14 <1>
$ git switch -c private2.6.14 v2.6.14 <1>
$ edit/compile/test; git commit -a
$ git checkout master
$ git cherry-pick v2.6.14..private2.6.14 <2>
@ -274,14 +273,14 @@ $ mailx <3>
& s 2 3 4 5 ./+to-apply
& s 7 8 ./+hold-linus
& q
$ git checkout -b topic/one master
$ git switch -c topic/one master
$ git am -3 -i -s ./+to-apply <4>
$ compile/test
$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
$ git checkout topic/one && git rebase master <6>
$ git checkout pu && git reset --hard next <7>
$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
$ git switch topic/one && git rebase master <6>
$ git switch -C pu next <7>
$ git merge topic/one topic/two && git merge hold/linus <8>
$ git checkout maint
$ git switch maint
$ git cherry-pick master~4 <9>
$ compile/test
$ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>

View File

@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.
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 @@ 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.

View File

@ -370,13 +370,13 @@ situation:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: closing.txt
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: file.txt

View File

@ -110,7 +110,7 @@ $ git status
On branch master
Changes to be committed:
Your branch is up to date with 'origin/master'.
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
modified: file1
modified: file2
@ -207,7 +207,7 @@ automatically. The asterisk marks the branch you are currently on;
type
------------------------------------------------
$ git checkout experimental
$ git switch experimental
------------------------------------------------
to switch to the experimental branch. Now edit a file, commit the
@ -216,7 +216,7 @@ change, and switch back to the master branch:
------------------------------------------------
(edit file)
$ git commit -a
$ git checkout master
$ git switch master
------------------------------------------------
Check that the change you made is no longer visible, since it was

View File

@ -301,8 +301,7 @@ topics on 'next':
.Rewind and rebuild next
[caption="Recipe: "]
=====================================
* `git checkout next`
* `git reset --hard master`
* `git switch -C next master`
* `git merge ai/topic_in_next1`
* `git merge ai/topic_in_next2`
* ...

View File

@ -115,7 +115,7 @@ 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

View File

@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,
while heads are expected to advance as development progresses.
Create a new branch head pointing to one of these versions and check it
out using linkgit:git-checkout[1]:
out using linkgit:git-switch[1]:
------------------------------------------------
$ git checkout -b new v2.6.13
$ git switch -c new v2.6.13
------------------------------------------------
The working directory then reflects the contents that the project had
@ -282,10 +282,10 @@ a summary of the commands:
this command will fail with a warning.
`git branch -D <branch>`::
delete the branch `<branch>` irrespective of its merged status.
`git checkout <branch>`::
`git switch <branch>`::
make the current branch `<branch>`, updating the working
directory to reflect the version referenced by `<branch>`.
`git checkout -b <new> <start-point>`::
`git switch -c <new> <start-point>`::
create a new branch `<new>` referencing `<start-point>`, and
check it out.
@ -302,22 +302,22 @@ ref: refs/heads/master
Examining an old version without creating a new branch
------------------------------------------------------
The `git checkout` command normally expects a branch head, but will also
accept an arbitrary commit; for example, you can check out the commit
referenced by a tag:
The `git switch` command normally expects a branch head, but will also
accept an arbitrary commit when invoked with --detach; for example,
you can check out the commit referenced by a tag:
------------------------------------------------
$ git checkout v2.6.17
$ git switch --detach v2.6.17
Note: checking out 'v2.6.17'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
state without impacting any branches by performing another switch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
do so (now or later) by using -c with the switch command again. Example:
git checkout -b new_branch_name
git switch -c new_branch_name
HEAD is now at 427abfa Linux v2.6.17
------------------------------------------------
@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches
on a branch of your own, just as you would for a tag:
------------------------------------------------
$ git checkout -b my-todo-copy origin/todo
$ git switch -c my-todo-copy origin/todo
------------------------------------------------
You can also check out `origin/todo` directly to examine it or
@ -1408,7 +1408,7 @@ If you get stuck and decide to just give up and throw the whole mess
away, you can always return to the pre-merge state with
-------------------------------------------------
$ git reset --hard HEAD
$ git merge --abort
-------------------------------------------------
Or, if you've already committed the merge that you want to throw away,
@ -1446,7 +1446,7 @@ mistake, you can return the entire working tree to the last committed
state with
-------------------------------------------------
$ git reset --hard HEAD
$ git restore --staged --worktree :/
-------------------------------------------------
If you make a commit that you later wish you hadn't, there are two
@ -1523,12 +1523,10 @@ Checking out an old version of a file
In the process of undoing a previous bad change, you may find it
useful to check out an older version of a particular file using
linkgit:git-checkout[1]. We've used `git checkout` before to switch
branches, but it has quite different behavior if it is given a path
name: the command
linkgit:git-restore[1]. The command
-------------------------------------------------
$ git checkout HEAD^ path/to/file
$ git restore --source=HEAD^ path/to/file
-------------------------------------------------
replaces path/to/file by the contents it had in the commit HEAD^, and
@ -2211,8 +2209,8 @@ $ git branch --track release origin/master
These can be easily kept up to date using linkgit:git-pull[1].
-------------------------------------------------
$ git checkout test && git pull
$ git checkout release && git pull
$ git switch test && git pull
$ git switch release && git pull
-------------------------------------------------
Important note! If you have any local changes in these branches, then
@ -2264,7 +2262,7 @@ tested changes
2) help future bug hunters that use `git bisect` to find problems
-------------------------------------------------
$ git checkout -b speed-up-spinlocks v2.6.35
$ git switch -c speed-up-spinlocks v2.6.35
-------------------------------------------------
Now you apply the patch(es), run some tests, and commit the change(s). If
@ -2279,7 +2277,7 @@ When you are happy with the state of this change, you can merge it into the
"test" branch in preparation to make it public:
-------------------------------------------------
$ git checkout test && git merge speed-up-spinlocks
$ git switch test && git merge speed-up-spinlocks
-------------------------------------------------
It is unlikely that you would have any conflicts here ... but you might if you
@ -2291,7 +2289,7 @@ see the value of keeping each patch (or patch series) in its own branch. It
means that the patches can be moved into the `release` tree in any order.
-------------------------------------------------
$ git checkout release && git merge speed-up-spinlocks
$ git switch release && git merge speed-up-spinlocks
-------------------------------------------------
After a while, you will have a number of branches, and despite the
@ -2512,7 +2510,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch
`origin`, and create some commits on top of it:
-------------------------------------------------
$ git checkout -b mywork origin
$ git switch -c mywork origin
$ vi file.txt
$ git commit
$ vi otherfile.txt
@ -2552,7 +2550,7 @@ commits without any merges, you may instead choose to use
linkgit:git-rebase[1]:
-------------------------------------------------
$ git checkout mywork
$ git switch mywork
$ git rebase origin
-------------------------------------------------
@ -3668,13 +3666,13 @@ change within the submodule, and then update the superproject to reference the
new commit:
-------------------------------------------------
$ git checkout master
$ git switch master
-------------------------------------------------
or
-------------------------------------------------
$ git checkout -b fix-up
$ git switch -c fix-up
-------------------------------------------------
then
@ -3800,8 +3798,8 @@ use linkgit:git-tag[1] for both.
The Workflow
------------
High-level operations such as linkgit:git-commit[1],
linkgit:git-checkout[1] and linkgit:git-reset[1] work by moving data
High-level operations such as linkgit:git-commit[1] and
linkgit:git-restore[1] work by moving data
between the working tree, the index, and the object database. Git
provides low-level operations which perform each of these steps
individually.
@ -4194,7 +4192,7 @@ start.
A good place to start is with the contents of the initial commit, with:
----------------------------------------------------
$ git checkout e83c5163
$ git switch --detach e83c5163
----------------------------------------------------
The initial revision lays the foundation for almost everything Git has
@ -4437,10 +4435,10 @@ Managing branches
-----------------
-----------------------------------------------
$ git branch # list all local branches in this repo
$ git checkout test # switch working directory to branch "test"
$ git branch new # create branch "new" starting at current HEAD
$ git branch -d new # delete branch "new"
$ git branch # list all local branches in this repo
$ git switch test # switch working directory to branch "test"
$ git branch new # create branch "new" starting at current HEAD
$ git branch -d new # delete branch "new"
-----------------------------------------------
Instead of basing a new branch on current HEAD (the default), use:
@ -4456,7 +4454,7 @@ $ git branch new test~10 # ten commits before tip of branch "test"
Create and switch to a new branch at the same time:
-----------------------------------------------
$ git checkout -b new v2.6.15
$ git switch -c new v2.6.15
-----------------------------------------------
Update and examine branches from the repository you cloned from:
@ -4467,7 +4465,7 @@ $ git branch -r # list
origin/master
origin/next
...
$ git checkout -b masterwork origin/master
$ git switch -c masterwork origin/master
-----------------------------------------------
Fetch a branch from a different repository, and give it a new

View File

@ -771,9 +771,11 @@ BUILT_INS += git-format-patch$X
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,

View File

@ -193,13 +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);
}

View File

@ -346,9 +346,9 @@ void remove_merge_branch_state(struct repository *r)
unlink(git_path_merge_mode(r));
}
void remove_branch_state(struct repository *r)
void remove_branch_state(struct repository *r, int verbose)
{
sequencer_post_commit_cleanup(r);
sequencer_post_commit_cleanup(r, verbose);
unlink(git_path_squash_msg(r));
remove_merge_branch_state(r);
}

View File

@ -70,7 +70,7 @@ 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"

View File

@ -214,6 +214,7 @@ 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);
@ -227,6 +228,7 @@ 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);

View File

@ -1956,7 +1956,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
if (merge_tree(remote_tree))
return -1;
remove_branch_state(the_repository);
remove_branch_state(the_repository, 0);
return 0;
}

View File

@ -1,32 +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>"),
@ -34,12 +33,23 @@ static const char * const checkout_usage[] = {
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;
@ -47,10 +57,19 @@ struct checkout_opts {
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;
@ -58,10 +77,12 @@ struct checkout_opts {
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,102 +334,13 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
}
}
static int checkout_paths(const struct checkout_opts *opts,
const char *revision)
static int checkout_worktree(const struct checkout_opts *opts)
{
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 errs = 0;
int pos;
trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
die(_("'%s' cannot be used with updating paths"), "--track");
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->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->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);
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
if (read_cache_preload(&opts->pathspec) < 0)
return error(_("index file corrupt"));
if (opts->source_tree)
read_tree_some(opts->source_tree, &opts->pathspec);
ps_matched = xcalloc(opts->pathspec.nr, 1);
/*
* Make sure all pathspecs participated in locating the paths
* to be checked out.
*/
for (pos = 0; pos < active_nr; pos++)
if (opts->overlay_mode)
mark_ce_for_checkout_overlay(active_cache[pos],
ps_matched,
opts);
else
mark_ce_for_checkout_no_overlay(active_cache[pos],
ps_matched,
opts);
if (report_path_error(ps_matched, &opts->pathspec)) {
free(ps_matched);
return 1;
}
free(ps_matched);
/* "checkout -m path" to recreate conflicted state */
if (opts->merge)
unmerge_marked_index(&the_index);
/* Any unmerged paths? */
for (pos = 0; pos < active_nr; pos++) {
const struct cache_entry *ce = active_cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce))
continue;
if (opts->force) {
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) {
errs |= check_stages((1<<2) | (1<<3), ce, pos);
} else {
errs = 1;
error(_("path '%s' is unmerged"), ce->name);
}
pos = skip_same_name(ce, pos) - 1;
}
}
if (errs)
return 1;
/* Now we are committed to check them out */
state.force = 1;
state.refresh_cache = 1;
state.istate = &the_index;
@ -457,8 +389,160 @@ static int checkout_paths(const struct checkout_opts *opts,
nr_checkouts);
}
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return errs;
}
static int checkout_paths(const struct checkout_opts *opts,
const char *revision)
{
int pos;
static char *ps_matched;
struct object_id rev;
struct commit *head;
int errs = 0;
struct lock_file lock_file = LOCK_INIT;
int checkout_index;
trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
die(_("'%s' cannot be used with updating paths"), "--track");
if (opts->new_branch_log)
die(_("'%s' cannot be used with updating paths"), "-l");
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->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->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)
return error(_("index file corrupt"));
if (opts->source_tree)
read_tree_some(opts->source_tree, &opts->pathspec);
ps_matched = xcalloc(opts->pathspec.nr, 1);
/*
* Make sure all pathspecs participated in locating the paths
* to be checked out.
*/
for (pos = 0; pos < active_nr; pos++)
if (opts->overlay_mode)
mark_ce_for_checkout_overlay(active_cache[pos],
ps_matched,
opts);
else
mark_ce_for_checkout_no_overlay(active_cache[pos],
ps_matched,
opts);
if (report_path_error(ps_matched, &opts->pathspec)) {
free(ps_matched);
return 1;
}
free(ps_matched);
/* "checkout -m path" to recreate conflicted state */
if (opts->merge)
unmerge_marked_index(&the_index);
/* Any unmerged paths? */
for (pos = 0; pos < active_nr; pos++) {
const struct cache_entry *ce = active_cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce))
continue;
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) {
errs |= check_stages((1<<2) | (1<<3), ce, pos);
} else {
errs = 1;
error(_("path '%s' is unmerged"), ce->name);
}
pos = skip_same_name(ce, pos) - 1;
}
}
if (errs)
return 1;
/* Now we are committed to check them out */
if (opts->checkout_worktree)
errs |= checkout_worktree(opts);
/*
* 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 (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);
}
read_ref_full("HEAD", 0, &rev, NULL);
head = lookup_commit_reference_gently(the_repository, &rev, 1);
@ -553,112 +637,6 @@ static void setup_branch_path(struct branch_info *branch)
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,
@ -666,15 +644,21 @@ static int merge_working_tree(const struct checkout_opts *opts,
{
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 {
@ -712,7 +696,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
&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);
@ -777,7 +762,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
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)
@ -786,13 +771,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.branch1 = new_branch_info->name;
o.branch2 = "local";
ret = merge_trees(&o,
get_commit_tree(new_branch_info->commit),
new_tree,
work,
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);
@ -810,7 +795,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
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 +900,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
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 +996,10 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
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 +1020,7 @@ static int switch_branches(const struct checkout_opts *opts,
void *path_to_free;
struct object_id rev;
int flag, writeout_error = 0;
int do_merge = 1;
trace2_cmd_mode("branch");
@ -1045,22 +1034,26 @@ static int switch_branches(const struct checkout_opts *opts,
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);
@ -1080,11 +1073,6 @@ static int switch_branches(const struct checkout_opts *opts,
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);
@ -1097,6 +1085,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
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,
@ -1104,10 +1120,8 @@ static int parse_branchname_arg(int argc, const char **argv,
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;
@ -1157,10 +1171,16 @@ static int parse_branchname_arg(int argc, const char **argv,
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;
}
@ -1194,11 +1214,12 @@ static int parse_branchname_arg(int argc, const char **argv,
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) {
@ -1229,26 +1250,11 @@ static int parse_branchname_arg(int argc, const char **argv,
argv++;
argc--;
new_branch_info->name = arg;
setup_branch_path(new_branch_info);
setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
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);
}
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
@ -1258,7 +1264,7 @@ static int parse_branchname_arg(int argc, const char **argv,
*/
if (argc)
verify_non_filename(opts->prefix, arg);
} else {
} else if (opts->accept_pathspec) {
argcount++;
argv++;
argc--;
@ -1285,6 +1291,60 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
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)
{
@ -1295,9 +1355,9 @@ static int checkout_branch(struct checkout_opts *opts,
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"),
@ -1306,6 +1366,9 @@ static int checkout_branch(struct checkout_opts *opts,
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");
@ -1313,6 +1376,8 @@ static int checkout_branch(struct checkout_opts *opts,
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");
@ -1323,6 +1388,23 @@ static int checkout_branch(struct checkout_opts *opts,
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;
@ -1344,99 +1426,148 @@ static int checkout_branch(struct checkout_opts *opts,
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"),
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,
N_("checkout our version for unmerged files"),
2, PARSE_OPT_NONEG),
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,
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")),
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(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
OPT_END(),
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__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,
N_("checkout their version for unmerged files"),
3, PARSE_OPT_NONEG),
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_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;
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
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;
dwim_new_local_branch = !no_dwim_new_local_branch;
if (opts.show_progress < 0) {
if (opts.quiet)
opts.show_progress = 0;
argc = parse_options(argc, argv, prefix, options,
usagestr, parseopt_flags);
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"));
@ -1445,7 +1576,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argv0 = strchr(argv0, '/');
if (!argv0 || !argv0[1])
die(_("missing branch name; try -b"));
opts.new_branch = argv0 + 1;
opts->new_branch = argv0 + 1;
}
/*
@ -1461,59 +1592,75 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
* 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"
@ -1532,6 +1679,123 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
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;
}

View File

@ -494,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)
{

View File

@ -1658,7 +1658,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die("%s", err.buf);
}
sequencer_post_commit_cleanup(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));
@ -1667,7 +1667,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
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))

View File

@ -1610,7 +1610,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
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;
@ -1630,7 +1630,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
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);
goto cleanup;
}

View File

@ -421,7 +421,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
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;
}

View File

@ -203,7 +203,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
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')

View File

@ -59,7 +59,7 @@ git-cat-file plumbinginterrogators
git-check-attr purehelpers
git-check-ignore purehelpers
git-check-mailmap purehelpers
git-checkout mainporcelain history
git-checkout mainporcelain
git-checkout-index plumbingmanipulators
git-check-ref-format purehelpers
git-cherry plumbinginterrogators complete
@ -81,7 +81,7 @@ git-cvsimport foreignscminterface
git-cvsserver foreignscminterface
git-daemon synchingrepositories
git-describe mainporcelain
git-diff mainporcelain history
git-diff mainporcelain info
git-diff-files plumbinginterrogators
git-diff-index plumbinginterrogators
git-diff-tree plumbinginterrogators
@ -150,7 +150,8 @@ git-repack ancillarymanipulators complete
git-replace ancillarymanipulators complete
git-request-pull foreignscminterface complete
git-rerere ancillaryinterrogators
git-reset mainporcelain worktree
git-reset mainporcelain history
git-restore mainporcelain worktree
git-revert mainporcelain
git-rev-list plumbinginterrogators
git-rev-parse plumbinginterrogators
@ -171,6 +172,7 @@ git-status mainporcelain info
git-stripspace purehelpers
git-submodule mainporcelain
git-svn foreignscminterface
git-switch mainporcelain history
git-symbolic-ref plumbingmanipulators
git-tag mainporcelain history
git-unpack-file plumbinginterrogators

View File

@ -37,7 +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 ;;
@ -2160,6 +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 +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 ()

View File

@ -149,6 +149,20 @@ sub colored {
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';
@ -1053,6 +1067,12 @@ sub color_diff {
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 +1283,18 @@ sub edit_hunk_loop {
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 +1457,16 @@ sub display_hunks {
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 +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 --");

2
git.c
View File

@ -566,6 +566,7 @@ static struct cmd_struct commands[] = {
{ "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 },
@ -586,6 +587,7 @@ static struct cmd_struct commands[] = {
{ "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 },

View File

@ -159,6 +159,23 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
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;

View File

@ -276,6 +276,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
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 -----*/

View File

@ -2314,19 +2314,21 @@ static int have_finished_the_last_pick(void)
return ret;
}
void sequencer_post_commit_cleanup(struct repository *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_cherry_pick_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))) {
unlink(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;
}

View File

@ -200,6 +200,6 @@ int read_author_script(const char *path, char **name, char **email, char **date,
void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
int write_basic_state(struct replay_opts *opts, const char *head_name,
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);

View File

@ -801,7 +801,7 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
"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"

View File

@ -2,28 +2,40 @@
. ./test-lib.sh
# set_state <path> <worktree-content> <index-content>
#
# Prepare the content for path in worktree and the index as specified.
set_state () {
echo "$3" > "$1" &&
git add "$1" &&
echo "$2" > "$1"
}
# save_state <path>
#
# Save index/worktree content of <path> in the files _worktree_<path>
# and _index_<path>
save_state () {
noslash="$(echo "$1" | tr / _)" &&
cat "$1" > _worktree_"$noslash" &&
git show :"$1" > _index_"$noslash"
}
# set_and_save_state <path> <worktree-content> <index-content>
set_and_save_state () {
set_state "$@" &&
save_state "$1"
}
# verify_state <path> <expected-worktree-content> <expected-index-content>
verify_state () {
test "$(cat "$1")" = "$2" &&
test "$(git show :"$1")" = "$3"
}
# verify_saved_state <path>
#
# Call verify_state with expected contents from the last save_state
verify_saved_state () {
noslash="$(echo "$1" | tr / _)" &&
verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"

View File

@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '
test_path_is_file c
'
test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
test_when_finished "
mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
git checkout master
" &&
echo "/b" >>.git/info/sparse-checkout &&
test "$(git ls-files -t b)" = "S b" &&
git -c checkout.optimizeNewBranch=true checkout -b fast &&
test "$(git ls-files -t b)" = "S b" &&
git checkout -b slow &&
test "$(git ls-files -t b)" = "H b"
'
test_expect_success 'merge feature branch into sparse checkout of master' '
git merge feature &&
test_path_is_file a &&

View File

@ -195,16 +195,22 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
# The first detach operation is more chatty than the following ones.
cat >1st_detach <<-EOF &&
Note: checking out 'HEAD^'.
Note: switching to 'HEAD^'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
do so (now or later) by using -c with the switch command. Example:
git checkout -b <new-branch-name>
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at \$commit three
EOF
@ -271,16 +277,22 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
# The first detach operation is more chatty than the following ones.
cat >1st_detach <<-EOF &&
Note: checking out 'HEAD^'.
Note: switching to 'HEAD^'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
do so (now or later) by using -c with the switch command. Example:
git checkout -b <new-branch-name>
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at \$commit... three
EOF

96
t/t2060-switch.sh Executable file
View File

@ -0,0 +1,96 @@
#!/bin/sh
test_description='switch basic functionality'
. ./test-lib.sh
test_expect_success 'setup' '
test_commit first &&
git branch first-branch &&
test_commit second &&
test_commit third &&
git remote add origin nohost:/nopath &&
git update-ref refs/remotes/origin/foo first-branch
'
test_expect_success 'switch branch no arguments' '
test_must_fail git switch
'
test_expect_success 'switch branch' '
git switch first-branch &&
test_path_is_missing second.t
'
test_expect_success 'switch and detach' '
test_when_finished git switch master &&
test_must_fail git switch master^{commit} &&
git switch --detach master^{commit} &&
test_must_fail git symbolic-ref HEAD
'
test_expect_success 'switch and detach current branch' '
test_when_finished git switch master &&
git switch master &&
git switch --detach &&
test_must_fail git symbolic-ref HEAD
'
test_expect_success 'switch and create branch' '
test_when_finished git switch master &&
git switch -c temp master^ &&
test_cmp_rev master^ refs/heads/temp &&
echo refs/heads/temp >expected-branch &&
git symbolic-ref HEAD >actual-branch &&
test_cmp expected-branch actual-branch
'
test_expect_success 'force create branch from HEAD' '
test_when_finished git switch master &&
git switch --detach master &&
test_must_fail git switch -c temp &&
git switch -C temp &&
test_cmp_rev master refs/heads/temp &&
echo refs/heads/temp >expected-branch &&
git symbolic-ref HEAD >actual-branch &&
test_cmp expected-branch actual-branch
'
test_expect_success 'new orphan branch from empty' '
test_when_finished git switch master &&
test_must_fail git switch --orphan new-orphan HEAD &&
git switch --orphan new-orphan &&
test_commit orphan &&
git cat-file commit refs/heads/new-orphan >commit &&
! grep ^parent commit &&
git ls-files >tracked-files &&
echo orphan.t >expected &&
test_cmp expected tracked-files
'
test_expect_success 'switching ignores file of same branch name' '
test_when_finished git switch master &&
: >first-branch &&
git switch first-branch &&
echo refs/heads/first-branch >expected &&
git symbolic-ref HEAD >actual &&
test_cmp expected actual
'
test_expect_success 'guess and create branch ' '
test_when_finished git switch master &&
test_must_fail git switch --no-guess foo &&
git switch foo &&
echo refs/heads/foo >expected &&
git symbolic-ref HEAD >actual &&
test_cmp expected actual
'
test_expect_success 'not switching when something is in progress' '
test_when_finished rm -f .git/MERGE_HEAD &&
# fake a merge-in-progress
cp .git/HEAD .git/MERGE_HEAD &&
test_must_fail git switch -d @^
'
test_done

98
t/t2070-restore.sh Executable file
View File

@ -0,0 +1,98 @@
#!/bin/sh
test_description='restore basic functionality'
. ./test-lib.sh
test_expect_success 'setup' '
test_commit first &&
echo first-and-a-half >>first.t &&
git add first.t &&
test_commit second &&
echo one >one &&
echo two >two &&
echo untracked >untracked &&
echo ignored >ignored &&
echo /ignored >.gitignore &&
git add one two .gitignore &&
git update-ref refs/heads/one master
'
test_expect_success 'restore without pathspec is not ok' '
test_must_fail git restore &&
test_must_fail git restore --source=first
'
test_expect_success 'restore a file, ignoring branch of same name' '
cat one >expected &&
echo dirty >>one &&
git restore one &&
test_cmp expected one
'
test_expect_success 'restore a file on worktree from another ref' '
test_when_finished git reset --hard &&
git cat-file blob first:./first.t >expected &&
git restore --source=first first.t &&
test_cmp expected first.t &&
git cat-file blob HEAD:./first.t >expected &&
git show :first.t >actual &&
test_cmp expected actual
'
test_expect_success 'restore a file in the index from another ref' '
test_when_finished git reset --hard &&
git cat-file blob first:./first.t >expected &&
git restore --source=first --staged first.t &&
git show :first.t >actual &&
test_cmp expected actual &&
git cat-file blob HEAD:./first.t >expected &&
test_cmp expected first.t
'
test_expect_success 'restore a file in both the index and worktree from another ref' '
test_when_finished git reset --hard &&
git cat-file blob first:./first.t >expected &&
git restore --source=first --staged --worktree first.t &&
git show :first.t >actual &&
test_cmp expected actual &&
test_cmp expected first.t
'
test_expect_success 'restore --staged uses HEAD as source' '
test_when_finished git reset --hard &&
git cat-file blob :./first.t >expected &&
echo index-dirty >>first.t &&
git add first.t &&
git restore --staged first.t &&
git cat-file blob :./first.t >actual &&
test_cmp expected actual
'
test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '
git init unmerged &&
(
cd unmerged &&
echo one >unmerged &&
echo one >common &&
git add unmerged common &&
git commit -m common &&
git switch -c first &&
echo first >unmerged &&
git commit -am first &&
git switch -c second master &&
echo second >unmerged &&
git commit -am second &&
test_must_fail git merge first &&
echo dirty >>common &&
test_must_fail git restore . &&
git restore --ignore-unmerged --quiet . >output 2>&1 &&
git diff common >diff-output &&
test_must_be_empty output &&
test_must_be_empty diff-output
)
'
test_done

110
t/t2071-restore-patch.sh Executable file
View File

@ -0,0 +1,110 @@
#!/bin/sh
test_description='git restore --patch'
. ./lib-patch-mode.sh
test_expect_success PERL 'setup' '
mkdir dir &&
echo parent >dir/foo &&
echo dummy >bar &&
git add bar dir/foo &&
git commit -m initial &&
test_tick &&
test_commit second dir/foo head &&
set_and_save_state bar bar_work bar_index &&
save_head
'
test_expect_success PERL 'restore -p without pathspec is fine' '
echo q >cmd &&
git restore -p <cmd
'
# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
test_expect_success PERL 'saying "n" does nothing' '
set_and_save_state dir/foo work head &&
test_write_lines n n | git restore -p &&
verify_saved_state bar &&
verify_saved_state dir/foo
'
test_expect_success PERL 'git restore -p' '
set_and_save_state dir/foo work head &&
test_write_lines n y | git restore -p &&
verify_saved_state bar &&
verify_state dir/foo head head
'
test_expect_success PERL 'git restore -p with staged changes' '
set_state dir/foo work index &&
test_write_lines n y | git restore -p &&
verify_saved_state bar &&
verify_state dir/foo index index
'
test_expect_success PERL 'git restore -p --source=HEAD' '
set_state dir/foo work index &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines n y n | git restore -p --source=HEAD &&
verify_saved_state bar &&
verify_state dir/foo head index
'
test_expect_success PERL 'git restore -p --source=HEAD^' '
set_state dir/foo work index &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines n y n | git restore -p --source=HEAD^ &&
verify_saved_state bar &&
verify_state dir/foo parent index
'
test_expect_success PERL 'git restore -p handles deletion' '
set_state dir/foo work index &&
rm dir/foo &&
test_write_lines n y | git restore -p &&
verify_saved_state bar &&
verify_state dir/foo index index
'
# The idea in the rest is that bar sorts first, so we always say 'y'
# first and if the path limiter fails it'll apply to bar instead of
# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
# the failure case (and thus get out of the loop).
test_expect_success PERL 'path limiting works: dir' '
set_state dir/foo work head &&
test_write_lines y n | git restore -p dir &&
verify_saved_state bar &&
verify_state dir/foo head head
'
test_expect_success PERL 'path limiting works: -- dir' '
set_state dir/foo work head &&
test_write_lines y n | git restore -p -- dir &&
verify_saved_state bar &&
verify_state dir/foo head head
'
test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
set_state dir/foo work head &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
verify_saved_state bar &&
verify_state dir/foo parent head
'
test_expect_success PERL 'path limiting works: foo inside dir' '
set_state dir/foo work head &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines y n n | (cd dir && git restore -p foo) &&
verify_saved_state bar &&
verify_state dir/foo head head
'
test_expect_success PERL 'none of this moved HEAD' '
verify_saved_head
'
test_done

View File

@ -94,13 +94,13 @@ test_expect_success 'status --column' '
# (use "git pull" to merge the remote branch into yours)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# (use "git restore --staged <file>..." to unstage)
#
# new file: dir2/added
#
# 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: dir1/modified
#
@ -128,13 +128,13 @@ cat >expect <<\EOF
# (use "git pull" to merge the remote branch into yours)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# (use "git restore --staged <file>..." to unstage)
#
# new file: dir2/added
#
# 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: dir1/modified
#
@ -278,13 +278,13 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: dir2/added
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: dir1/modified
@ -347,13 +347,13 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: dir2/added
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: dir1/modified
@ -420,13 +420,13 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: dir2/added
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: dir1/modified
@ -484,13 +484,13 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: dir2/added
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: dir1/modified
@ -542,13 +542,13 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: dir2/added
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: dir1/modified
@ -605,13 +605,13 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: ../dir2/added
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: modified
@ -676,13 +676,13 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
<GREEN>new file: dir2/added<RESET>
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)
<RED>modified: dir1/modified<RESET>
@ -802,13 +802,13 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: dir2/added
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: dir1/modified
@ -852,7 +852,7 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
modified: dir1/modified
@ -896,14 +896,14 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: dir2/added
new file: sm
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: dir1/modified
@ -956,14 +956,14 @@ and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
new file: dir2/added
new file: sm
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: dir1/modified
@ -1019,7 +1019,7 @@ and have 2 and 2 different commits each, respectively.
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: dir1/modified
@ -1068,14 +1068,14 @@ and have 2 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD^1 <file>..." to unstage)
(use "git restore --source=HEAD^1 --staged <file>..." to unstage)
new file: dir2/added
new file: sm
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: dir1/modified
@ -1123,13 +1123,13 @@ and have 2 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
modified: sm
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: dir1/modified
@ -1235,13 +1235,13 @@ and have 2 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
modified: sm
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)
(commit or discard the untracked or modified content in submodules)
modified: dir1/modified
@ -1295,13 +1295,13 @@ and have 2 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
modified: sm
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: dir1/modified
modified: sm (new commits)
@ -1379,13 +1379,13 @@ cat > expect << EOF
; (use "git pull" to merge the remote branch into yours)
;
; Changes to be committed:
; (use "git reset HEAD <file>..." to unstage)
; (use "git restore --staged <file>..." to unstage)
;
; modified: sm
;
; 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: dir1/modified
; modified: sm (new commits)
@ -1431,7 +1431,7 @@ and have 2 and 2 different commits each, respectively.
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: dir1/modified
@ -1458,13 +1458,13 @@ and have 2 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
modified: sm
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: dir1/modified
@ -1581,13 +1581,13 @@ and have 2 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
(use "git restore --staged <file>..." to unstage)
modified: sm
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: dir1/modified

View File

@ -85,7 +85,7 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
(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 @@ You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
(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 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
(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 @@ You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO
(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 @@ You are currently splitting a commit while rebasing branch '\''split_commit'\''
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 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
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 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
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 @@ You are currently splitting a commit while rebasing branch '\''several_edits'\''
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
@ -834,7 +834,7 @@ You are currently reverting commit $TO_REVERT.
(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 +855,7 @@ You are currently reverting commit $TO_REVERT.
(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

View File

@ -315,7 +315,7 @@ static struct progress *get_progress(struct unpack_trees_options *o)
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,

View File

@ -179,9 +179,15 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s)
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 +212,15 @@ static void wt_longstatus_print_cached_header(struct wt_status *s)
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 +238,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,
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", "");