简体中文 ▾ 主题 ▾ 最新版本 ▾ git-checkout 上次更新于 2.52.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 <tree-ish> [--] <pathspec>…​
git checkout <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 有两种主要模式

  1. 切换分支,使用 git checkout <branch>

  2. 恢复文件的不同版本,例如使用 git checkout <commit> <filename>git checkout <filename>

有关 Git 如何决定执行哪种操作,请参阅下文的参数歧义。

git checkout [<branch>]

切换到<branch>。这会将当前分支设置为<branch>,并更新工作目录中的文件。如果<branch>和当前提交的内容不同,并且存在任何未提交的更改,则签出将失败。否则,未提交的更改将被保留。

如果未找到<branch>,但恰好在一个远程(称为<remote>)中存在一个同名的跟踪分支,并且未指定--no-guess,则将其视为等同于

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

运行git checkout而不指定分支不会产生任何效果,除了打印当前分支的跟踪信息。

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

创建一个名为<new-branch>的新分支,从<start-point>(默认为当前提交)开始,并检出新分支。您可以使用--track--no-track选项来设置分支的上游跟踪信息。

如果检出<new-branch>时发生错误,例如检出<start-point>提交会覆盖您的未提交更改,则此操作将失败。

git checkout -B <branch> [<start-point>]

-b相同,但如果分支已存在,则将<branch>重置为起始点,而不是失败。

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

git checkout <branch>相同,但不同的是,它将HEAD指向提交ID,而不是指向分支。更多信息请参阅下文的“分离 HEAD”部分。

省略<branch>会将HEAD分离到当前分支的尖端。

git checkout <tree-ish> [--] <pathspec>...
git checkout <tree-ish> --pathspec-from-file=<file> [--pathspec-file-nul]

将指定的文件和/或目录替换为给定提交或树的版本,并将其添加到索引(也称为“暂存区”)。

例如,git checkout main file.txt会将file.txt替换为来自main的版本。

git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [--] <pathspec>...
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] --pathspec-from-file=<file> [--pathspec-file-nul]

将指定的文件和/或目录替换为索引中的版本。

例如,如果您检出一个提交,编辑了file.txt,然后决定那些更改是错误的,git checkout file.txt将丢弃对file.txt的所有未暂存的更改。

如果文件存在合并冲突,并且您尚未运行git add file.txt(或等效命令)来标记它已解决,则此操作将失败。您可以使用-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>

-b相同,但如果分支已存在,则将<branch>重置为起始点,而不是失败。

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

创建新分支时,设置“上游”配置。有关详细信息,请参阅git-branch[1]中的--track。为了方便起见,不带-b的--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>不是分支名时的默认行为。有关详细信息,请参阅“分离 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 -- <path>... 将仅更新与<paths>$GIT_DIR/info/sparse-checkout中的稀疏模式匹配的条目。此选项将忽略稀疏模式,并重新添加<path>...中的任何文件。

-m
--merge

切换分支时,如果您对当前分支和要切换到的分支之间存在差异的一个或多个文件有本地修改,则命令将拒绝切换分支以在上下文中保留您的修改。但是,使用此选项,将在当前分支、您的工作树内容和新分支之间执行三向合并,然后您将位于新分支上。

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

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

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

--conflict=<style>

与上面的 --merge 选项相同,但会更改冲突块的呈现方式,覆盖 merge.conflictStyle 配置变量。可能的值包括 merge(默认)、diff3zdiff3

-p
--patch

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

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

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

-U<n>
--unified=<n>

生成具有 <n> 行上下文的 diff。如果配置选项未设置,则默认为 diff.context 或 3。

--inter-hunk-context=<n>

在差异块之间显示上下文,最多达指定行数 <number>,从而合并彼此接近的块。默认为 diff.interHunkContext,如果未设置配置选项则为 0。

--ignore-other-worktrees

git checkout在要检出的分支已被检出或被另一个工作树使用时拒绝。此选项使其无论如何都检出该分支。换句话说,一个分支可以被多个工作树使用。

--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>

Pathspec 从 <file> 而不是命令行参数传递。如果 <file>-,则使用标准输入。Pathspec 元素由 LFCR/LF 分隔。Pathspec 元素可以按 core.quotePath 配置变量的解释进行引用(参见 git-config[1])。另请参见 --pathspec-file-nul 和全局 --literal-pathspecs

--pathspec-file-nul

仅在与 --pathspec-from-file 一起使用时有意义。路径规范元素由 NUL 字符分隔,所有其他字符都被视为字面值(包括换行符和引号)。

<branch>

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

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

作为特殊情况,您可以将<rev-a>...<rev-b>用作<rev-a><rev-b>的合并基的快捷方式,前提是只有一个合并基。您可以省略<rev-a><rev-b>中的最多一个,在这种情况下,它默认为HEAD

<new-branch>

新分支的名称。

<start-point>

作为新分支起点的提交的名称;有关详细信息,请参阅git-branch[1]。默认为HEAD

作为特殊情况,您可以将<rev-a>...<rev-b>用作<rev-a><rev-b>的合并基的快捷方式,前提是只有一个合并基。您可以省略<rev-a><rev-b>中的最多一个,在这种情况下,它默认为HEAD

<tree-ish>

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

作为特殊情况,您可以将<rev-a>...<rev-b>用作<rev-a><rev-b>的合并基的快捷方式,前提是只有一个合并基。您可以省略<rev-a><rev-b>中的最多一个,在这种情况下,它默认为HEAD

--

不再将任何后续参数解释为选项。

<pathspec>...

限制受操作影响的路径。

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

分离 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 <something>时,Git会尝试猜测<something>是打算作为分支、提交还是文件集,然后切换到该分支或提交,或恢复指定的文件。

如果存在任何歧义,Git会将<something>视为分支或提交,但您可以使用双破折号--来强制Git将参数视为文件和/或目录列表,如下所示

git checkout -- file.txt

示例

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> 且只有一个远程时,它可能会隐式地回退到签出和跟踪 e.g. origin/<something>。一旦您有多个具有 *<something>* 引用的远程,这种情况就会停止工作。此设置允许设置一个首选远程的名称,该名称在歧义化时应始终优先。典型用例是将其设置为 origin

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

checkout.guess

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

checkout.workers

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

注意
并行 checkout 通常可以提高位于 SSD 或 NFS 上的存储库的性能。对于旋转硬盘和/或核心数较少的机器上的存储库,默认的顺序 checkout 通常性能更好。存储库的大小和压缩级别也可能影响并行版本的性能。
checkout.thresholdForParallelism

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

GIT

Git[1] 套件的一部分