简体中文 ▾ 主题 ▾ 最新版本 ▾ git-checkout 上次更新于 2.50.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。为方便起见,不带 -b--track 意味着创建分支;详见下方 --track 选项的描述。

如果给定 -B,如果 <new-branch> 不存在则创建,否则重置它。这等同于执行以下事务性操作:

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

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

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

通过将 HEAD 分离到 <commit> 上(参见“分离 HEAD”章节),并更新索引和工作区中的文件,准备在 <commit> 之上工作。工作区中文件的本地修改会保留,因此最终的工作区将是提交中记录的状态加上本地修改。

<commit> 参数是分支名称时,可以使用 --detach 选项将 HEAD 分离到该分支的末端(git 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”输出,并选择结果中要使用的代码块(hunks)。有关 --patch 选项的描述,请参见下文。

选项

-q
--quiet

安静模式,抑制反馈消息。

--progress
--no-progress

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

-f
--force

切换分支时,即使索引或工作区与 HEAD 不同,或者存在未跟踪的文件阻碍,也要继续进行。这用于丢弃本地修改以及任何阻碍的未跟踪文件或目录。

从索引中检出路径时,遇到未合并的条目时不失败;相反,未合并的条目将被忽略。

--ours
--theirs

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

请注意,在执行 git rebasegit pull --rebase 时,ourstheirs 可能会互换;--ours 提供更改被 rebase 到其上的分支的版本,而 --theirs 提供包含你正在 rebase 的工作的分支的版本。

这是因为 rebase 在一个工作流中使用,该工作流将远程仓库的历史视为共享的规范历史,并将你在正在 rebase 的分支上所做的工作视为要集成的第三方工作,并且你在 rebase 期间暂时承担着规范历史维护者的角色。作为规范历史的维护者,你需要将来自远程仓库的历史视为 ours(即“我们共享的规范历史”),而将你在旁支上所做的工作视为 theirs(即“一位贡献者在其上所做的工作”)。

-b <new-branch>

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

-B <new-branch>

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

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

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

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

--no-track

不设置“upstream”配置,即使 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

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

-d
--detach

不是检出一个分支来在其上工作,而是检出一个提交用于检查和可丢弃的实验。这是当 <commit> 不是分支名称时,git checkout <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>(如果未指定,则为索引)与工作区之间差异中的代码块(hunks)。然后将选定的代码块反向应用于工作区(如果指定了 <tree-ish>,则也应用于索引)。

这意味着你可以使用 git checkout -p 从当前工作区中选择性地丢弃编辑。有关如何操作 --patch 模式的详细信息,请参见 git-add[1] 的“交互模式”章节。

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

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

路径规范通过 <file> 传递,而不是通过命令行参数。如果 <file> 精确为 -,则使用标准输入。路径规范元素由 LFCR/LF 分隔。路径规范元素可以按照配置变量 core.quotePath 的说明进行引用(参见 git-config[1])。另请参见 --pathspec-file-nul 和全局 --literal-pathspecs

--pathspec-file-nul

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

<分支>

要检出的分支;如果它指向一个分支(即,一个加上“refs/heads/”前缀后是有效引用的名称),那么将检出该分支。否则,如果它指向一个有效的提交,你的 HEAD 将变为“分离”状态,你将不再位于任何分支上(详见下文)。

你可以使用 @{-N} 语法来引用使用“git checkout”操作检出的倒数第 N 个分支/提交。你也可以指定 -,它等同于 @{-1}

作为特例,如果 <rev-a><rev-b> 只有一个合并基准,你可以使用 <rev-a>...<rev-b> 作为它们的合并基准的快捷方式。你可以最多省略 <rev-a><rev-b> 中的一个,在这种情况下它默认为 HEAD

<新分支>

新分支的名称。

<起始点>

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

作为特例,如果 <rev-a><rev-b> 只有一个合并基准,你可以使用 <rev-a>...<rev-b> 作为它们的合并基准的快捷方式。你可以最多省略 <rev-a><rev-b> 中的一个,在这种情况下它默认为 HEAD

<树对象>

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

作为特例,如果 <rev-a><rev-b> 只有一个合并基准,你可以使用 <rev-a>...<rev-b> 作为它们的合并基准的快捷方式。你可以最多省略 <rev-a><rev-b> 中的一个,在这种情况下它默认为 HEAD

--

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

<路径规范>...

限制受操作影响的路径。

有关更多详细信息,请参阅 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. 类似地,创建了一个指向提交 f 的新分支 foo,但保留了分离的 HEAD

  3. 创建了一个指向提交 f 的新标签 foo,保留了分离的 HEAD

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

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

参数消歧

当只给定一个参数且不是 --(例如 git checkout abc),并且该参数既是一个有效的 <树对象>(例如,分支 abc 存在)又是一个有效的 <路径规范>(例如,名为“abc”的文件或目录存在)时,Git 通常会要求你进行消歧。然而,由于检出分支是一个非常常见的操作,在这种情况下,git checkout abc 将“abc”视为一个 <树对象>。如果你想从索引中检出这些路径,请使用 git checkout -- <路径规范>

示例

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 影响所有执行检出操作的命令。例如 checkout、clone、reset、sparse-checkout 等。

注意
并行检出通常对于位于 SSD 或通过 NFS 的仓库提供更好的性能。对于位于旋转磁盘和/或核心数量较少的机器上的仓库,默认的顺序检出通常表现更好。仓库的大小和压缩级别也可能影响并行版本的性能。
checkout.thresholdForParallelism

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

GIT

Git[1] 套件的一部分

scroll-to-top