简体中文 ▾ 主题 ▾ 最新版本 ▾ git-checkout 最后更新于 2.55.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 <分支>

  2. 恢复文件的其他版本,例如使用 git checkout <提交> <文件名>git checkout <文件名>

有关 Git 如何决定执行哪种操作,请参阅下面的参数消歧部分。

git checkout [<分支>]

切换到 <分支>。这将把当前分支设置为 <分支> 并更新工作区中的文件。如果 <分支> 与您当前提交的内容有所不同的任何文件存在未提交的更改,检出将失败。否则,未提交的更改将被保留。

如果未找到 <分支>,但在恰好一个远程仓库(称之为 <远程>)中存在一个名称匹配的跟踪分支,且未指定 --no-guess,则等同于

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

运行不指定分支的 git checkout 没有其他效果,只是打印出当前分支的跟踪信息。

git checkout -b <新分支> [<起始点>]

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

如果检出 <新分支> 发生错误,例如检出 <起始点> 提交会覆盖您未提交的更改,则此操作将失败。

git checkout -B <分支> [<起始点>]

-b 相同,不同之处在于如果该分支已存在,它会将 <分支> 重置为起点,而不是报错失败。

git checkout --detach [<分支>]
git checkout [--detach] <提交>

git checkout <分支> 相同,不同之处在于它不会将 HEAD 指向该分支,而是将 HEAD 指向提交 ID。有关更多信息,请参阅下面的“分离 HEAD”部分。

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

git checkout <树对象> [--] <路径规格>...
git checkout <树对象> --pathspec-from-file=<文件> [--pathspec-file-nul]

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

例如,git checkout main file.txt 将用 main 中的版本替换 file.txt

git checkout [-f|--ours|--theirs|-m|--conflict=<样式>] [--] <路径规格>...
git checkout [-f|--ours|--theirs|-m|--conflict=<样式>] --pathspec-from-file=<文件> [--pathspec-file-nul]

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

例如,如果您检出了一个提交,编辑了 file.txt,然后觉得这些修改是个错误,那么 git checkout file.txt 将丢弃对 file.txt 的任何未暂存更改。

如果文件存在合并冲突,且您尚未运行 git add file.txt(或等效命令)将其标记为已解决,则此操作将失败。您可以使用 -f 忽略未合并的文件而不是报错失败,使用 --ours--theirs 将它们替换为合并中特定一方的版本,或者使用 -m 将它们替换为原始的有冲突合并结果。

git checkout (-p|--patch) [<树对象>] [--] [<路径规格>...]

这与前两个模式类似,但允许您使用交互式界面显示 "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 <新分支>

创建一个名为 <新分支> 的新分支,从 <起始点> 开始,并检出该分支;详情请参阅 git-branch[1]

-B <新分支>

-b 相同,不同之处在于如果该分支已存在,它会将 <分支> 重置为起点,而不是报错失败。

-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

如果未找到 <分支>,但在恰好一个远程仓库(称之为 <远程>)中存在一个名称匹配的跟踪分支,则等同于

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

如果分支存在于多个远程仓库中,且其中一个远程仓库由 checkout.defaultRemote 配置变量指定,我们将使用该仓库来消除歧义,即使 <分支> 在所有远程仓库中并不唯一。例如,将其设置为 checkout.defaultRemote=origin,这样在 <分支> 存在歧义但存在于 origin 远程仓库上时,总是从那里检出远程分支。另请参阅 git-config[1] 中的 checkout.defaultRemote

--guess 是默认行为。使用 --no-guess 可将其禁用。

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

-l

创建新分支的引用日志(reflog);详情请参阅 git-branch[1]

-d
--detach

检出提交用于检查和可丢弃的实验,而不是检出分支在上面工作。当 <提交> 不是分支名称时,这是 git checkout <提交> 的默认行为。有关详细信息,请参阅下面的“分离 HEAD”部分。

--orphan <新分支>

创建一个名为 <新分支> 的新未诞生分支,从 <起始点> 开始并切换到该分支。在此新分支上进行的第一次提交将没有父提交,并且它将成为与所有其他分支和提交完全断开的新历史的起点。

索引和工作区会被调整,就像您之前运行过 git checkout <起始点> 一样。这使您可以通过轻松运行 git commit -a 来创建根提交,从而开始一段记录了类似于 <起始点> 路径集合的新历史。

当您想发布某次提交的树对象而不公开其完整历史时,这会很有用。您可能希望这样做来发布项目的开源分支,该项目的当前树是“干净的”,但其完整历史中包含专有或受法律约束的代码。

如果您想开始一段完全不同于 <起始点> 路径集合的独立历史,那么在创建孤儿分支后,您应该立即在工作区的顶层运行 git rm -rf . 来清空索引和工作区。之后,您就可以准备新文件,通过从别处复制文件、解压压缩包等方式重新填充工作区。

--ignore-skip-worktree-bits

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

-m
--merge

切换分支时,如果您本地修改的一个或多个文件在当前分支和要切换到的分支之间存在差异,该命令将拒绝切换分支,以便保留您的修改。使用此选项,冲突的本地更改在切换前会自动存储(stash),并在切换后重新应用。如果本地更改与分支之间的差异不重叠,则切换将在不存储的情况下进行。如果重新应用存储导致冲突,该条目将保存到存储列表中。解决冲突,完成后运行 git stash drop,或者在稍后运行 git stash pop 重新应用更改之前清空工作区(例如使用 git reset --hard)。

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

--conflict=<样式>

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

-p
--patch

交互式地从 <树对象>(如果未指定,则为索引)与工作区的差异中选择改动块。选定的改动块将反向应用到工作区(如果指定了 <树对象>,则应用到索引)。

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

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

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

生成带有 <n> 行上下文的 diff。上下文行数默认为 diff.context,如果未设置该配置变量,则默认为 3。(由于历史原因,不带 <n>-U 被静默接受为 -p 的同义词)。

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

在默认的覆盖(overlay)模式下,git checkout 绝不会从索引或工作区中删除文件。指定 --no-overlay 时,会删除存在于索引和工作区中但不存在于 <树对象> 中的文件,使其与 <树对象> 完全一致。

--pathspec-from-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 字符分隔,所有其他字符都被视为字面值(包括换行符和引号)。

<分支>

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

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

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

<新分支>

新分支的名称。

<起始点>

启动新分支的提交名称;详情请参阅 git-branch[1]。默认为 HEAD

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

<树对象>

从中检出的树对象(如果给出了路径)。如果未指定,将使用索引。

作为一种特殊情况,如果恰好有一个合并基点,您可以使用 <版本A>...<版本B> 作为 <版本A><版本B> 合并基点的快捷方式。您最多可以省略 <版本A><版本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. 同样创建一个新的分支 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
Applied autostash.
Switched to branch 'mytopic'
The following paths have local changes:
M	frotz

切换之后,本地修改会被重新应用,且 不会 注册在您的索引文件中,因此 git diff 将向您显示自新分支末梢以来您所做的更改。

3. 合并冲突

当指定了 --merge (-m) 选项,且本地更改与我们要切换到的分支中的更改发生重叠时,这些更改会在切换前被存储(stash),并在切换后重新应用。如果这个过程导致冲突,存储条目将被保存,并打印出一条信息

$ git checkout -m mytopic
Your local changes are stashed, however applying them
resulted in conflicts.  You can either resolve the conflicts
and then discard the stash with "git stash drop", or, if you
do not want to resolve them now, run "git reset --hard" and
apply the local changes later by running "git stash pop".

配置

本节中以下所有内容均从 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] 套件的一部分