简体中文 ▾ Topics ▾ Latest version ▾ git-checkout last updated in 2.54.0

名称

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

概述

git checkout' [-q] [-f] [-m] [<分支>]
git checkout' [-q] [-f] [-m] --detach [<分支>]
git checkout' [-q] [-f] [-m] [--detach] <提交>
git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <新分支>] [<起始点>]
git checkout' [-f] <目录树对象> [--] <路径规范>…​
git checkout' [-f] <目录树对象> --pathspec-from-file=<文件> [--pathspec-file-nul]
git checkout' [-f|--ours|--theirs|-m|--conflict=<样式>] [--] <路径规范>…​
git checkout' [-f|--ours|--theirs|-m|--conflict=<样式>] --pathspec-from-file=<文件> [--pathspec-file-nul]
git checkout' (-p|--patch) [<目录树对象>] [--] [<路径规范>…​]

描述

git checkout 有两种主要模式:

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

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

关于 Git 如何决定执行哪一种操作,参见下文 ARGUMENT DISAMBIGUATION。

git checkout [<分支>]

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

如果没有找到该 <分支> ,但正好一个远程(称其为 <远程仓库> )中确实存在一个名称相匹配的已跟踪分支,并且没有指定 --no-guess ,则视为等同于

$ git checkout -b <分支> --track <远程仓库>/<分支>

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

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

创建一个名为"<新分支>"的新分支,从"<起点>"开始,并检出产生的分支;详情见 git-branch[1]

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

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

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

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

git checkout <branch> 相同,但不是让 HEAD 指向该分支,而是让 HEAD 指向提交 ID。详见下文“DETACHED HEAD”。

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

git checkout (-p|--patch) [<树状对象>] [--] [<路径规范>…​]
git checkout [-f|--ours|--theirs|-m|--conflict=<样式>] [<树状对象>] --pathspec-from-file=<文件> [--pathspec-file-nul]

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

例如,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

当从索引中检查出路径时,要检查第二阶段(ours)或第三阶段(theirs)的未合并路径。

注意在 git rebasegit pull --rebase 过程中,ourstheirs 可能会互换;--ours 给出了分支变基后的版本,而 --theirs 则给出你即将变基的分支版本。

这是因为 "rebase"在工作流程中使用,它将远程的历史视为共享的规范历史,并将在你重命名的分支上所做的工作视为需要整合的第三方工作,而你在重命名过程中暂时承担了规范历史守护者的角色。 作为规范历史的守护者,你需要将远程的历史视为 "ours"(即 "我们共享的规范历史"),而将你在侧边分支上的工作视为 "their"(即 "一个贡献者在上面所作的工作")。

-b <新分支>

创建一个名为"<新分支>"的新分支,从"<起点>"开始,并检出产生的分支;详情见 git-branch[1]

-B <新分支>

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

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

创建新分支时,要设置 "上游"配置。详情见 git-branch[1] 中的 --track

如果没有给出 -b 选项,新分支的名称将从远程跟踪分支中推导出来,方法是查看为相应的远程配置引用规范的本地部分,然后剥离初始部分,直至 "*"。由上述可得出,在从 origin/hack (或 remotes/origin/hack ,甚至 refs/remotes/origin/hack )分支时,将使用 hack 作为本地分支。如果给定的名字没有斜线,或者上述推测的结果是一个空名,则会终止推测。在这种情况下,你可以用 -b 明确地给出一个名字。

--no-track

不设置 "上游 "配置,即使`branch.autoSetupMerge`配置变量为true。

--guess
--no-guess

如果没有找到 <分支> ,但确实有一个远程分支(称为 <远程分支> )的跟踪分支存在,且名称匹配,则视为等同于

$ git checkout -b <分支> --track <远程仓库>/<分支>

如果该分支存在于多个远程,并且其中一个是由 checkout.defaultRemote 配置变量命名的,为了消除歧义,我们将使用该变量,即使 <分支> 在所有远程中并不唯一。将其设置为例如 checkout.defaultRemote=origin ,以便在 <分支> 不明确但存在于’origin’远程时,总是从那里检出远程分支。参见 git-config[1] 中的 checkout.defaultRemote

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

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

-l

创建新分支的引用日志;详见 git-branch[1]

-d
--detach

与其检出一个分支来工作,不如检出一个提交来进行检查和可放弃的实验。当 <提交> 不是一个分支名时, 这是 git checkout <提交> 的默认行为,。 详见下面的 "游离状态(DETACHED HEAD)"部分。

--orphan <新分支>

<起点> 开始,创建一个新的 “未出生” 分支,命名为 <新分支> ,并切换到该分支。 在这个新分支上的第一次提交将没有父分支,它将是一个新的历史起点,与所有其他的分支和提交完全断开。

索引和工作区的调整就像你之前运行 git checkout <起点> 一样。 这允许你轻松地通过运行 git commit -a 来启动一个新的历史,记录一组类似于 <新起点> 的路径,进行根提交。

当你想发布一个提交树而不暴露其完整的历史时,这就很有用。你可能想这样做来发布一个项目的开源分支,这个分支的当前树是 "干净的",但其完整的历史包含了专有的或其他一些代码。

如果你想启动一个断开的历史,记录一组完全不同于 <起点> 的路径,那么你应该在创建孤儿分支后立即清除索引和工作区,从工作区的顶层运行 git rm -rf . 。 之后你就可以准备你的新文件了,重新填充工作区,办法是从其他地方复制它们,提取一个包,等等。

--ignore-skip-worktree-bits

在稀疏检出模式下,git checkout -- <路径> 将只更新由 <路径>$GIT_DIR/info/sparse-checkout 中的稀疏模式匹配的条目。这个选项忽略了稀疏模式,并添加回 <路径> 中的任何文件。

-m
--merge

在切换分支时,如果你对一个或多个文件的本地修改在当前分支和你要切换的分支之间是不同的,命令会拒绝切换分支,以保留你的修改内容。 然而,使用这个选项后,就会在当前分支、你的工作区内容和新的分支之间进行三方合并,之后,你会处于新分支。

当合并冲突发生时,冲突路径的索引条目会被留下,你需要解决冲突,并用 git add 标记已解决的路径(如果合并应导致删除路径,则用 git rm )。

从索引中检出路径时,该选项可让你在指定路径中重新创建冲突的合并。 该选项不能用于从目录树对象路径中检出路径。

当用 --merge 切换分支时,可能会丢失阶段性修改。

--conflict=<样式>

与上面的 --merge 选项相同,但改变了冲突块的显示方式,覆盖 merge.conflictStyle 配置变量。可能的值是 merge(默认)和 diff3(除了 merge 样式显示的内容外,还显示原始内容)。

-p
--patch

<树状对象> (或索引,如果没有指定)和工作区之间的差异中,交互式地选择目标。 选择的目标会被反向应用到工作区上(如果指定了 <树状对象> ,则是索引)。

这意味着你可以使用 git checkout -p 来有选择地丢弃当前工作区上的编辑内容。参见 git-add[1] 的 "互动模式''部分,了解 --patch 模式如何操作。

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

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

生成带有 <n> 行上下文的差异。上下文行数默认为 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

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

--pathspec-from-file=<file>

Pathspec 在 <文件> 中传递,而不是在命令行参数中传递。如果 <文件> 正好是 -,则使用标准输入。路径规范元素由 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} 同义。

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

<新分支>

新分支的名称。

<start-point>

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

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

<tree-ish>

检出的树(当路径给定时)。如果没有指定,将使用索引。

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

--

不将之后的参数解释为选项。

<路径规范>...

限制受操作影响的路径。

更多细节请参见 gitglossary[7] 中的 路径规范 条目。

游离状态(DETACHED HEAD)

HEAD`通常指的是一个分支的引用(例如`master)。同时,每个分支指的是一个特定的提交。让我们看看一个有三个提交的仓库,其中一个被标记了,并且分支`master`被检出:

           HEAD('master'分支的引用)
            |
            v
a---b---c  branch 'master'('c' 提交的引用)
    ^
    |
  tag 'v2.0' ('b' 提交的引用)

当在这种状态下创建一个提交时,分支会被更新以引用新的提交。具体来说,git commit`创建了一个新的提交`d,其父级是提交`c`,然后更新分支`master`以引用新的提交`d`。HEAD'仍指向`master',所以现在间接地指向提交`d

$ edit; git add; git commit

               HEAD (指向'master'分支)
                |
                v
a---b---c---d  分支'master' (指向提交'd')
    ^
    |
  tag 'v2.0' (指向提交'b')

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

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

   HEAD (指向提交'b')
    |
    v
a---b---c---d  分支'master'(指向提交'd')
    ^
    |
  tag 'v2.0' (指向提交'b')

请注意,无论我们使用哪条结账命令,HEAD`现在都直接指向提交`b。这就是所谓的分离的`HEAD’状态。 这意味着,`HEAD`指向一个特定的提交,而不是指向一个命名的分支。让我们看看创建提交时发生了什么:

$ edit; git add; git commit

     HEAD (指向提交'e')
      |
      v
      e
     /
a---b---c---d  branch 'master' (指向提交'd')
    ^
    |
  tag 'v2.0' (指向提交'b')

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

$ edit; git add; git commit

	 HEAD(指向提交'f')。
	  |
	  v
      e---f
     /
a---b---c---d 分支"master"(指向提交'd')。
    ^
    |
  tag 'v2.0'(指向提交'b')。

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

$ git checkout master

               HEAD(指向分支 'master')
      e---f     |
     /          v
a---b---c---d  branch 'master' (指向提交 'd')
    ^
    |
  tag 'v2.0' (指向提交 'b')

重要的是要意识到,在这一点上,没有任何引用是指向提交`f`的。最终,提交 f(以及提交 e)会被 Git 的例行垃圾回收程序删除,除非我们在这之前创建一个引用。如果我们还没有离开提交 f,以下任何一个操作都会创建一个对它的引用:

$ git checkout -b 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 # 或
$ git log -g -2 HEAD

参数消歧义

当你运行 git checkout <something> 时,Git 会尝试判断 <something> 代表分支、提交还是一组文件,然后要么切换到该分支或提交,要么恢复指定的文件。

如果存在歧义,Git 会将 <something> 视为分支或提交,但你可以使用双破折号 -- 强制 Git 将该参数视为文件和/或目录列表,例如:

$ git checkout -- hello.c

实例

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

在错误的分支工作后,切换到正确的分支将使用:

$ git checkout mytopic

然而,你的 "错误"分支和正确的`mytopic`分支可能在你本地修改的文件中存在差异,在这种情况下,上述检出会失败,像这样:

$ git checkout mytopic
错误:您对 'footz' 有本地更改;未切换分支。

你可以给命令加上 `-m`标志,这样就可以尝试三方合并了:

$ git checkout -m mytopic
自动合并 frotz 分支

在这个三方合并之后,本地的修改并「没有」登记在你的索引文件中,所以`git diff`会显示你在新分支的提示下做了哪些修改。

3. Merge 冲突

当使用`-m`选项切换分支时发生合并冲突,你会看到类似这样的情况:

$ git checkout -m mytopic
自动合并 frotz 分支
错误:对 frotz 分支的合并冲突
失败:merge 程序终止

在这一点上,`git diff`显示了与前面的例子一样干净地合并的修改,以及冲突文件中的修改。 编辑并解决冲突,让后像往常一样用`git add`标记已解决:

$ 编辑 frotz
$ git add frotz

配置

本节中这一行以下的内容都是从 git-config[1] 文档中摘录的。其内容与那里的内容相同:

checkout.defaultRemote

当你运行 git checkout <something>git switch <something>,且只有一个远程时,它可能会隐式回退为检出并跟踪例如 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

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

Note
并行 checkout 通常能为位于 SSD 或 NFS 上的仓库带来更好的性能。对于位于机械磁盘上的仓库和/或核心数较少的机器,默认的顺序 checkout 往往表现更好。仓库的大小和压缩级别也可能影响并行版本的性能。
checkout.thresholdForParallelism

当对少量文件执行并行 checkout 时,创建子进程和进程间通信的成本可能会超过并行化带来的收益。该设置允许你定义应尝试并行 checkout 的最小文件数。默认值为 100。

GIT

属于 git[1] 文档