Git
英语 ▾ 主题 ▾ 最新版本 ▾ git-checkout 最后更新于 2.44.0

名称

git-checkout - 切换分支或恢复工作树文件

概要

git checkout [-q] [-f] [-m] [<branch>]
git checkout [-q] [-f] [-m] --detach [<branch>]
git checkout [-q] [-f] [-m] [--detach] <commit>
git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new-branch>] [<start-point>]
git checkout [-f] <tree-ish> [--] <pathspec>…​
git checkout [-f] <tree-ish> --pathspec-from-file=<file> [--pathspec-file-nul]
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [--] <pathspec>…​
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] --pathspec-from-file=<file> [--pathspec-file-nul]
git checkout (-p|--patch) [<tree-ish>] [--] [<pathspec>…​]

描述

更新工作树中的文件以匹配索引或指定树中的版本。如果没有给出路径规范,git checkout 也会更新 HEAD 以将指定分支设置为当前分支。

git checkout [<branch>]

要准备在 <branch> 上工作,通过更新索引和工作树中的文件,并将 HEAD 指向该分支,切换到该分支。工作树中文件的本地修改将保留,以便可以将它们提交到 <branch>

如果 <branch> 未找到,但只有一个远程分支(称为 <remote>)存在完全匹配的名称并且未指定 --no-guess,则视为等效于

$ git checkout -b <branch> --track <remote>/<branch>

你可以省略 <branch>,在这种情况下,命令退化为“签出当前分支”,这是一个带有一些昂贵副作用的“空操作”,仅显示当前分支的跟踪信息(如果存在)。

git checkout -b|-B <new-branch> [<start-point>]

指定 -b 会导致创建一个新分支,就像调用 git-branch[1] 并将其签出一样。在这种情况下,可以使用 --track--no-track 选项,这些选项将传递给 git branch。为了方便起见,--track 在没有 -b 的情况下表示创建分支;请参阅下面对 --track 的描述。

如果给出 -B,则在 <new-branch> 不存在的情况下创建 <new-branch>;否则,将重置它。这与以下操作等效:

$ git branch -f <branch> [<start-point>]
$ git checkout <branch>

也就是说,除非“git checkout”成功,否则分支不会被重置/创建(例如,当分支在另一个工作树中使用时,不仅当前分支保持不变,而且分支也不会被重置到起点)。

git checkout --detach [<branch>]
git checkout [--detach] <commit>

通过在 <commit> 上分离 HEAD(参见“分离的 HEAD”部分)以及更新索引和工作树中的文件,准备在 <commit> 之上工作。工作树中文件的本地修改将保留,因此生成的工作树将是提交中记录的状态加上本地修改。

<commit> 参数是分支名称时,可以使用 --detach 选项在分支的末端分离 HEADgit checkout <branch> 会签出该分支,但不会分离 HEAD)。

省略 <branch> 会在当前分支的末端分离 HEAD

git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>…​
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]

覆盖与路径规范匹配的文件的内容。当 <tree-ish>(通常是提交)未给出时,用索引中的内容覆盖工作树。当给出 <tree-ish> 时,用 <tree-ish> 的内容覆盖索引和工作树。

索引可能包含由于先前合并失败而导致的未合并条目。默认情况下,如果你尝试从索引中签出这样的条目,签出操作将失败,并且不会签出任何内容。使用 -f 将忽略这些未合并的条目。可以使用 --ours--theirs 从索引中签出合并特定侧面的内容。使用 -m,可以丢弃对工作树文件的更改,以重新创建原始冲突的合并结果。

git checkout (-p|--patch) [<tree-ish>] [--] [<pathspec>…​]

这类似于前面的模式,但允许你使用交互式界面来显示“diff”输出并选择在结果中使用哪些块。请参阅下面的 --patch 选项说明。

选项

-q
--quiet

安静,抑制反馈消息。

--progress
--no-progress

当它附加到终端时,进度状态默认情况下会报告到标准错误流中,除非指定了 --quiet。此标志即使未附加到终端,也会启用进度报告,无论是否指定了 --quiet

-f
--force

在切换分支时,即使索引或工作树与 HEAD 不同,即使有未跟踪的文件存在,也会继续执行。这用于丢弃本地更改以及任何存在的未跟踪文件或目录。

在从索引中签出路径时,不要在未合并的条目上失败;相反,将忽略未合并的条目。

--ours
--theirs

在从索引中签出路径时,签出阶段 #2 (ours) 或 #3 (theirs) 用于未合并的路径。

请注意,在 git rebasegit pull --rebase 期间,ourstheirs 可能会出现颠倒;--ours 提供正在变基到的分支的版本,而 --theirs 提供正在变基的包含你工作的分支的版本。

这是因为 rebase 用于在将远程视为共享规范历史记录的工作流中,并将你在变基的分支上完成的工作视为要集成的第三方工作,并且在变基过程中你暂时扮演规范历史记录维护者的角色。作为规范历史记录的维护者,你需要将远程的历史记录视为 ours(即“我们共享的规范历史记录”),而将你在你的分支上所做的更改视为 theirs(即“一位贡献者在它之上的工作”)。

-b <new-branch>

创建一个名为 <new-branch> 的新分支,从 <start-point> 开始,然后签出生成的分支;有关详细信息,请参见 git-branch[1]

-B <new-branch>

创建分支<new-branch>,从<start-point>开始;如果该分支已存在,则将其重置为<start-point>。然后检出结果分支。这等同于运行带有“-f”的“git branch”命令,然后检出该分支;有关详细信息,请参见git-branch[1]

-t
--track[=(direct|inherit)]

创建新分支时,设置“上游”配置。有关详细信息,请参见git-branch[1] 中的“--track”。

如果没有给出-b选项,则新分支的名称将从远程跟踪分支中推断出来,方法是查看为相应远程配置的 refspec 的本地部分,然后剥离从“*”开始的初始部分。这将告诉我们,当从origin/hack(或remotes/origin/hack,甚至refs/remotes/origin/hack)分支时,使用hack作为本地分支。如果给定的名称没有斜杠,或者上述猜测导致空名称,则猜测将中止。在这种情况下,您可以使用-b显式给出名称。

--no-track

即使branch.autoSetupMerge 配置变量为 true,也不设置“上游”配置。

--guess
--no-guess

如果找不到<branch>,但在一个远程(称为<remote>)中存在一个具有匹配名称的跟踪分支,则视为等同于

$ git checkout -b <branch> --track <remote>/<branch>

如果该分支存在于多个远程中,并且其中一个由checkout.defaultRemote 配置变量命名,即使<branch>在所有远程中不唯一,我们也会为了消除歧义而使用该远程。将其设置为例如checkout.defaultRemote=origin,以始终从那里检出远程分支,如果<branch>是模棱两可的,但在origin远程上存在。另请参见git-config[1] 中的checkout.defaultRemote

--guess 是默认行为。使用--no-guess 禁用它。

默认行为可以通过checkout.guess 配置变量设置。

-l

创建新分支的 reflog;有关详细信息,请参见git-branch[1]

-d
--detach

不要检出分支以进行操作,而是检出提交以进行检查和可丢弃的实验。这是git checkout <commit>的默认行为,其中<commit>不是分支名称。有关详细信息,请参见下面的“DETACHED HEAD”部分。

--orphan <new-branch>

创建一个新的未出生分支,名为<new-branch>,从<start-point>开始,并切换到该分支。在此新分支上进行的第一个提交将没有父级,它将成为与所有其他分支和提交完全断开连接的新历史记录的根。

索引和工作树会像您之前运行git checkout <start-point>一样进行调整。这允许您通过轻松运行git commit -a 来创建根提交,从而启动一个新的历史记录,该历史记录记录了与<start-point>类似的一组路径。

当您想要发布提交的树而不公开其完整历史记录时,这很有用。您可能希望这样做来发布一个项目的开源分支,该分支的当前树是“干净的”,但其完整历史记录包含专有或其他受限制的代码片段。

如果您想要启动一个断开连接的历史记录,该历史记录记录了与<start-point>的路径完全不同的路径集,那么您应该在创建孤儿分支后立即清除索引和工作树,方法是从工作树的顶层运行git rm -rf .。之后,您将准备好准备新的文件,通过从其他地方复制它们、提取 tarball 等来重新填充工作树。

--ignore-skip-worktree-bits

在稀疏检出模式下,git checkout -- <paths> 仅更新与<paths>$GIT_DIR/info/sparse-checkout 中的稀疏模式匹配的条目。此选项忽略稀疏模式,并添加<paths> 中的任何文件。

-m
--merge

切换分支时,如果您对一个或多个文件进行了本地修改,而这些修改在当前分支和要切换到的分支之间存在差异,则该命令会拒绝切换分支以保留修改的上下文。但是,使用此选项,将对当前分支、工作树内容和新分支进行三方合并,您将处于新分支上。

当发生合并冲突时,冲突路径的索引条目将保持未合并状态,您需要解决冲突并使用git add(或git rm,如果合并应该导致路径删除)标记已解决的路径。

当从索引中检出路径时,此选项允许您在指定的路径中重新创建冲突的合并。从树状对象中检出路径时,不能使用此选项。

使用--merge 切换分支时,暂存的更改可能会丢失。

--conflict=<style>

与上面的--merge 选项相同,但更改了冲突块的呈现方式,覆盖了merge.conflictStyle 配置变量。可能的值为“merge”(默认)、“diff3”和“zdiff3”。

-p
--patch

交互式地选择<tree-ish>(或未指定时的索引)和工作树之间的差异中的块。然后,所选块将以相反顺序应用于工作树(如果指定了<tree-ish>,则应用于索引)。

这意味着您可以使用git checkout -p 有选择地丢弃当前工作树中的编辑。有关如何操作--patch 模式,请参见git-add[1] 的“交互模式”部分。

请注意,此选项默认使用无覆盖模式(另请参见--overlay),目前不支持覆盖模式。

--ignore-other-worktrees

当其他工作树已检出所需的 ref 时,git checkout 会拒绝。此选项使其无论如何检出 ref。换句话说,ref 可以被多个工作树持有。

--overwrite-ignore
--no-overwrite-ignore

在切换分支时,静默覆盖被忽略的文件。这是默认行为。使用--no-overwrite-ignore 在新分支包含被忽略的文件时中止操作。

--recurse-submodules
--no-recurse-submodules

使用--recurse-submodules 将根据超级项目中记录的提交更新所有活动子模块的内容。如果子模块中的本地修改将被覆盖,则检出将失败,除非使用-f。如果使用空值(或--no-recurse-submodules),则不会更新子模块的工作树。就像git-submodule[1] 一样,这将分离子模块的HEAD

--overlay
--no-overlay

在默认的覆盖模式下,git checkout 永远不会从索引或工作树中删除文件。当指定--no-overlay 时,将删除出现在索引和工作树中,但在<tree-ish> 中不存在的文件,以使它们与<tree-ish> 完全匹配。

--pathspec-from-file=<file>

路径规格在<file> 中传递,而不是在命令行参数中传递。如果<file> 恰好是-,则使用标准输入。路径规格元素以 LF 或 CR/LF 分隔。路径规格元素可以像为配置变量core.quotePath 解释的那样进行引用(参见git-config[1])。另请参见--pathspec-file-nul 和全局--literal-pathspecs

--pathspec-file-nul

仅对--pathspec-from-file 有意义。路径规格元素以 NUL 字符分隔,所有其他字符都按字面意思解释(包括换行符和引号)。

<branch>

要检出的分支;如果它引用一个分支(即,一个名称,当加上“refs/heads/”后,它是一个有效的 ref),则检出该分支。否则,如果它引用一个有效的提交,则您的HEAD 将变为“分离”状态,并且您不再处于任何分支上(有关详细信息,请参见下文)。

您可以使用@{-N} 语法引用使用“git checkout”操作检出的第 N 个最后分支/提交。您也可以指定-,它与@{-1} 同义。

作为特殊情况,您可以使用A...B 作为AB 的合并基的快捷方式,如果只有一个合并基。您可以最多省略AB 中的一个,在这种情况下,它默认为HEAD

<new-branch>

新分支的名称。

<start-point>

要从其中开始新分支的提交的名称;有关详细信息,请参见git-branch[1]。默认为HEAD

作为特殊情况,您可以使用"A...B" 作为AB 的合并基的快捷方式,如果只有一个合并基。您可以最多省略AB 中的一个,在这种情况下,它默认为HEAD

<tree-ish>

要从中检出的树(当给出路径时)。如果未指定,将使用索引。

作为特殊情况,您可以使用"A...B" 作为AB 的合并基的快捷方式,如果只有一个合并基。您可以最多省略AB 中的一个,在这种情况下,它默认为HEAD

--

不要将任何更多参数解释为选项。

<pathspec>…​

限制操作受影响的路径。

有关更多详细信息,请参见gitglossary[7] 中的pathspec 条目。

DETACHED HEAD

HEAD 通常引用一个命名分支(例如master)。同时,每个分支都引用一个特定的提交。让我们看看一个包含三个提交的仓库,其中一个提交被标记,并且已检出分支master

           HEAD (refers to branch 'master')
            |
            v
a---b---c  branch 'master' (refers to commit 'c')
    ^
    |
  tag 'v2.0' (refers to commit 'b')

当在此状态下创建提交时,分支将更新为引用新的提交。具体来说,git commit 创建一个新的提交d,其父级是提交c,然后更新分支master 以引用新的提交dHEAD 仍然引用分支master,因此现在间接地引用了提交d

$ edit; git add; git commit

               HEAD (refers to branch 'master')
                |
                v
a---b---c---d  branch 'master' (refers to commit 'd')
    ^
    |
  tag 'v2.0' (refers to commit 'b')

有时,能够签出不在任何命名分支顶端的提交,甚至创建不在命名分支引用的新提交,会很有用。让我们看看签出提交 b 时会发生什么(这里展示了两种可能的方法)。

$ git checkout v2.0  # or
$ git checkout master^^

   HEAD (refers to commit 'b')
    |
    v
a---b---c---d  branch 'master' (refers to commit 'd')
    ^
    |
  tag 'v2.0' (refers to commit 'b')

请注意,无论我们使用哪个签出命令,HEAD 现在都直接指向提交 b。这被称为处于分离的 HEAD 状态。它简单地意味着 HEAD 指向一个特定的提交,而不是指向一个命名的分支。让我们看看创建提交时会发生什么。

$ edit; git add; git commit

     HEAD (refers to commit 'e')
      |
      v
      e
     /
a---b---c---d  branch 'master' (refers to commit 'd')
    ^
    |
  tag 'v2.0' (refers to commit 'b')

现在有一个新的提交 e,但它只被 HEAD 引用。当然,我们可以在这种状态下再添加一个提交。

$ edit; git add; git commit

	 HEAD (refers to commit 'f')
	  |
	  v
      e---f
     /
a---b---c---d  branch 'master' (refers to commit 'd')
    ^
    |
  tag 'v2.0' (refers to commit 'b')

事实上,我们可以执行所有正常的 Git 操作。但是,让我们看看当我们签出 master 时会发生什么。

$ git checkout master

               HEAD (refers to branch 'master')
      e---f     |
     /          v
a---b---c---d  branch 'master' (refers to commit 'd')
    ^
    |
  tag 'v2.0' (refers to commit 'b')

重要的是要意识到,在这一点上,没有任何东西引用提交 f。最终,提交 f(以及扩展提交 e)将被例行的 Git 垃圾回收过程删除,除非我们在发生这种情况之前创建一个引用。如果我们还没有从提交 f 离开,任何这些操作都会为它创建一个引用。

$ git checkout -b foo  # or "git switch -c foo"  (1)
$ git branch foo                                 (2)
$ git tag foo                                    (3)
  1. 创建一个新的分支 foo,它引用提交 f,然后更新 HEAD 使它指向分支 foo。换句话说,在此命令之后,我们不再处于分离的 HEAD 状态。

  2. 同样创建一个新的分支 foo,它引用提交 f,但保持 HEAD 分离。

  3. 创建一个新的标签 foo,它引用提交 f,保持 HEAD 分离。

如果我们已经从提交 f 离开,那么我们必须首先恢复它的对象名称(通常通过使用 git reflog),然后我们可以为它创建一个引用。例如,要查看 HEAD 引用的最后两个提交,我们可以使用以下任一命令。

$ git reflog -2 HEAD # or
$ git log -g -2 HEAD

参数歧义

当只给出单个参数且不是 --(例如 git checkout abc),并且当参数同时是有效的 <tree-ish>(例如存在一个名为 abc 的分支)和有效的 <pathspec>(例如存在一个名为 "abc" 的文件或目录)时,Git 通常会要求你进行消除歧义。然而,由于签出分支是一个非常常见的操作,因此 git checkout abc 在这种情况下会将 "abc" 作为 <tree-ish>。如果你想要从索引中签出这些路径,请使用 git checkout -- <pathspec>

示例

1. 路径

以下序列签出 master 分支,将 Makefile 恢复到两个版本之前,错误地删除了 hello.c,然后从索引中将其恢复。

$ git checkout master             (1)
$ git checkout master~2 Makefile  (2)
$ rm -f hello.c
$ git checkout hello.c            (3)
  1. 切换分支

  2. 从另一个提交中取出一个文件

  3. 从索引中恢复 hello.c

如果你想从索引中签出所有 C 源文件,你可以说

$ git checkout -- '*.c'

请注意 *.c 周围的引号。文件 hello.c 也会被签出,即使它不再工作树中,因为文件通配符用于匹配索引中的条目(而不是 Shell 工作树中的条目)。

如果你有一个不幸命名的为 hello.c 的分支,此步骤会误解为切换到该分支的指令。你应该改为编写

$ git checkout -- hello.c

2. 合并

在错误的分支中工作之后,切换到正确的分支将使用以下方法完成

$ git checkout mytopic

但是,你的 "错误" 分支和正确的 mytopic 分支可能在本地修改过的文件中有所不同,在这种情况下,上面的签出会失败,如下所示

$ git checkout mytopic
error: You have local changes to 'frotz'; not switching branches.

你可以给命令提供 -m 标记,它会尝试进行三次合并

$ git checkout -m mytopic
Auto-merging frotz

在这次三次合并之后,本地修改不会在你的索引文件中注册,因此 git diff 会显示你自新分支顶端所做的更改。

3. 合并冲突

当使用 -m 选项切换分支期间发生合并冲突时,你会看到类似以下内容

$ git checkout -m mytopic
Auto-merging frotz
ERROR: Merge conflict in frotz
fatal: merge program failed

此时,git diff 会显示干净合并的更改,就像在前面的示例中一样,以及冲突文件中的更改。像往常一样编辑并解决冲突,并使用 git add 标记为已解决

$ edit frotz
$ git add frotz

配置

本节中此行以下的所有内容都是从 git-config[1] 文档中选择性地包含的。内容与那里找到的内容相同

checkout.defaultRemote

当你运行 git checkout <something>git switch <something> 并且只有一个远程时,它可能会隐式地回退到签出和跟踪例如 origin/<something>。一旦你有多个具有 <something> 引用的远程,这就不再起作用。此设置允许设置首选远程的名称,该远程在消除歧义时应始终获胜。典型的用例是将其设置为 origin

目前,当 git checkout <something>git switch <something> 会签出另一个远程上的 <something> 分支时,git-switch[1]git-checkout[1] 会使用此设置,当 git worktree add 指向远程分支时,git-worktree[1] 会使用此设置。此设置将来可能会用于其他类似签出的命令或功能。

checkout.guess

git checkoutgit switch 中的 --guess--no-guess 选项提供默认值。参见 git-switch[1]git-checkout[1]

checkout.workers

更新工作树时要使用的并行工作程序数量。默认值为一个,即顺序执行。如果设置为小于一个的值,Git 将使用与可用逻辑核心数量相同的工作程序。此设置和 checkout.thresholdForParallelism 会影响执行签出的所有命令。例如,签出、克隆、重置、稀疏签出等。

注意:并行签出通常为位于 SSD 上或通过 NFS 的存储库提供更好的性能。对于位于旋转磁盘上和/或核心数量很少的机器上的存储库,默认的顺序签出通常表现得更好。存储库的大小和压缩级别也可能会影响并行版本执行的效率。

checkout.thresholdForParallelism

当使用少量文件运行并行签出时,子进程生成和进程间通信的成本可能会超过并行化带来的收益。此设置允许你定义尝试并行签出的最小文件数量。默认值为 100。

另请参见

GIT

git[1] 套件的一部分

scroll-to-top