简体中文 ▾ 主题 ▾ 最新版本 ▾ git-worktree 最后更新于 2.52.0

名称

git-worktree - 管理多个工作树

概要

git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]
		 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]
git worktree list [-v | --porcelain [-z]]
git worktree lock [--reason <string>] <worktree>
git worktree move <worktree> <new-path>
git worktree prune [-n] [-v] [--expire <expire>]
git worktree remove [-f] <worktree>
git worktree repair [<path>…​]
git worktree unlock <worktree>

描述

管理附加到同一存储库的多个工作树。

一个 git 存储库可以支持多个工作树,允许您一次检出多个分支。使用 git worktree add 命令,会创建一个新的工作树并将其与存储库关联,同时还会生成额外的元数据来区分该工作树与同一存储库中的其他工作树。工作树及其元数据统称为“worktree”。

这个新的 worktree 被称为“链接工作树”(linked worktree),与 git-init[1]git-clone[1] 准备的“主工作树”(main worktree)相对。一个存储库有一个主工作树(如果它不是裸存储库),以及零个或多个链接工作树。当您完成一个链接工作树后,可以使用 git worktree remove 命令将其删除。

最简单的形式是,git worktree add <path> 会自动创建一个新分支,其名称为 <path> 的最后一个组件,这在您计划处理新主题时很方便。例如,git worktree add ../hotfix 会创建一个名为 hotfix 的新分支,并将其检出到路径 ../hotfix。如果想在新工作树中处理现有分支,请使用 git worktree add <path> <branch>。另一方面,如果您只是想进行一些实验性更改或进行测试而不干扰现有开发,通常会创建一个不与任何分支关联的“临时”工作树。例如,git worktree add -d <path> 会创建一个新的工作树,其 HEAD 指针与当前分支处于同一提交,但处于分离状态。

如果工作树未使用 git worktree remove 命令被删除,那么与之关联的管理文件(位于存储库中,请参阅下面的“详细信息”)最终会被自动删除(请参阅 git-config[1] 中的 gc.worktreePruneExpire),或者您可以在主工作树或任何链接工作树中运行 git worktree prune 来清理任何过时的管理文件。

如果链接工作树的工作树存储在便携式设备或网络共享上,而该设备或共享并非始终挂载,您可以通过发出 git worktree lock 命令来阻止其管理文件被删除,还可以选择指定 --reason 来解释锁定工作树的原因。

命令

add <path> [<commit-ish>]

<path> 创建一个工作树,并将 <commit-ish> 检出到其中。新的工作树链接到当前存储库,共享除每个工作树特有的文件(如 HEADindex 等)之外的所有内容。为了方便起见,<commit-ish> 可以是裸字符串 “-”,它等同于 @{-1}

如果 <commit-ish> 是一个分支名(称之为 <branch>)且未找到,同时未使用 -b-B--detach,但恰好在一个远程存储库(称之为 <remote>)中存在同名的跟踪分支,则将其视为等同于

$ git worktree add --track -b <branch> <path> <remote>/<branch>

如果分支存在于多个远程存储库中,并且其中一个远程存储库由 checkout.defaultRemote 配置变量命名,我们将使用该远程存储库进行歧义消除,即使 <branch> 在所有远程存储库中并不唯一。将其设置为例如 checkout.defaultRemote=origin,以便在 <branch> 存在歧义但存在于 origin 远程存储库时,始终从中检出远程分支。有关更多信息,请参阅 git-config[1] 中的 checkout.defaultRemote

如果省略了 <commit-ish> 并且没有使用 -b-B--detach,那么为了方便起见,新的工作树将关联一个名为 <branch> 的分支(名称源自 <path> 的基本名称)。如果 <branch> 不存在,则会创建一个基于 HEAD 的新分支,就像使用了 -b <branch> 命令一样。如果 <branch> 已经存在,并且没有在其他地方检出,则会在新工作树中检出该分支;否则,命令将拒绝创建工作树(除非使用了 --force)。

如果省略了 <commit-ish>,并且没有使用 --detach--orphan,并且没有有效的本地分支(如果指定了 --guess-remote,则也没有远程分支),那么为了方便起见,新的工作树将关联一个名为 <branch> 的新未出生分支(如果未使用的 -b-B,则名称源自 <path> 的基本名称),就像将 --orphan 选项传递给命令一样。如果存储库有远程并且使用了 --guess-remote,但不存在任何远程或本地分支,则该命令会失败并显示警告,提醒用户先从其远程存储库进行获取(或通过使用 -f/--force 来覆盖)。

list

列出每个工作树的详细信息。主工作树首先列出,然后是每个链接工作树。输出的详细信息包括工作树是否为裸体、当前检出的修订版本、当前检出的分支(如果没有则为“分离的 HEAD”)、如果工作树被锁定则为“locked”、如果工作树可以通过 prune 命令进行修剪则为“prunable”。

lock

如果工作树位于便携式设备或网络共享上,而该设备或共享并非始终挂载,则锁定它以防止其管理文件被自动删除。这还会阻止其被移动或删除。可以选择使用 --reason 指定锁定的原因。

move

将工作树移动到新位置。请注意,主工作树或包含子模块的链接工作树不能使用此命令移动。(然而,git worktree repair 命令可以在手动移动主工作树后重新建立与链接工作树的连接。)

prune

修剪 $GIT_DIR/worktrees 中的工作树信息。

remove

移除一个工作树。只能移除干净的工作树(没有未跟踪的文件,也没有已跟踪文件的修改)。不干净的工作树或包含子模块的工作树可以使用 --force 命令移除。主工作树不能被移除。

repair [<path>...]

如果工作树的管理文件因外部因素而损坏或过时,则在可能的情况下进行修复。

例如,如果主工作树(或裸存储库)被移动,链接工作树将无法找到它。在主工作树中运行 repair 将从链接工作树重新建立与主工作树的连接。

同样,如果链接工作树的工作树在未使用 git worktree move 命令的情况下被移动,主工作树(或裸存储库)将无法找到它。在最近移动的工作树中运行 repair 将重新建立连接。如果多个链接工作树被移动,从任何工作树运行 repair 并将每个工作树的新 <path> 作为参数,将重新建立与所有指定路径的连接。

如果主工作树和链接工作树都被手动移动或复制,则在主工作树中运行 repair 并指定每个链接工作树的新 <path> 将在两个方向上重新建立所有连接。

unlock

解锁工作树,允许其被删除、移动或修剪。

选项

-f
--force

默认情况下,当 <commit-ish> 是一个分支名并且已被另一个工作树检出,或者当 <path> 已分配给某个工作树但已丢失(例如,<path> 被手动删除)时,add 命令会拒绝创建新工作树。此选项会覆盖这些保护措施。要添加一个丢失但被锁定的工作树路径,请指定两次 --force

move 命令默认拒绝移动锁定的工作树,除非指定两次 --force。如果目标已分配给另一个工作树但已丢失(例如,<new-path> 被手动删除),则 --force 允许移动继续;如果目标被锁定,请指定两次 --force

remove 命令默认拒绝移除不干净的工作树,除非使用 --force。要移除锁定的工作树,请指定两次 --force

-b <new-branch>
-B <new-branch>

在使用 add 命令时,创建一个名为 <new-branch> 的新分支,该分支从 <commit-ish> 开始,并将其检出到新工作树中。如果省略了 <commit-ish>,则默认为 HEAD。默认情况下,如果 <new-branch> 已存在,-b 会拒绝创建新分支。-B 会覆盖此保护措施,将 <new-branch> 重置为 <commit-ish>

-d
--detach

在使用 add 命令时,将新工作树中的 HEAD 置于分离状态。请参阅 git-checkout[1] 中的“分离的 HEAD”(DETACHED HEAD)。

--checkout
--no-checkout

默认情况下,add 命令会检出 <commit-ish>。但是,可以使用 --no-checkout 来抑制检出,以便进行自定义,例如配置稀疏检出。请参阅 git-read-tree[1] 中的“稀疏检出”(Sparse checkout)。

--guess-remote
--no-guess-remote

在使用 worktree add <path> 命令并且未指定 <commit-ish> 时,该命令不会从 HEAD 创建新分支,而是会尝试查找一个远程跟踪分支,其名称唯一匹配 <path> 的基本名称。如果找到这样的分支,则会检出该分支并将其设置为新分支的“上游”。

也可以通过使用 worktree.guessRemote 配置选项将其设置为默认行为。

--relative-paths
--no-relative-paths

使用相对路径链接工作树,或使用绝对路径(默认)。此选项会覆盖 worktree.useRelativePaths 配置选项,请参阅 git-config[1]

在使用 repair 命令时,即使链接是正确的,如果存在绝对/相对路径不匹配的情况,也会更新链接文件。

--track
--no-track

创建新分支时,如果 <commit-ish> 是一个分支,则将其标记为新分支的“上游”。如果 <commit-ish> 是一个远程跟踪分支,这会是默认行为。有关详细信息,请参阅 git-branch[1] 中的 --track

--lock

创建工作树后保持其锁定状态。这相当于在 git worktree add 之后运行 git worktree lock,但没有竞态条件。

-n
--dry-run

在使用 prune 命令时,不执行任何删除操作;仅报告将要删除的内容。

--orphan

在使用 add 命令时,使新的工作树和索引为空,并将工作树与一个名为 <new-branch> 的新未出生分支关联起来。

--porcelain

在使用 list 命令时,以易于脚本解析的格式输出。此格式在 Git 版本之间以及忽略用户配置的情况下都将保持稳定。建议与 -z 选项结合使用。详细信息请参阅下文。

-z

当指定 --porcelain 选项与 list 命令一起使用时,使用 NUL 字符而不是换行符来终止每一行。这使得在工作树路径包含换行符时也可以解析输出。

-q
--quiet

在使用 add 命令时,抑制反馈消息。

-v
--verbose

在使用 prune 命令时,报告所有移除操作。

在使用 list 命令时,输出有关工作树的额外信息(见下文)。

--expire <time>

在使用 prune 命令时,仅过期使用时间超过 <time> 的未使用的工作树。

在使用 list 命令时,将旧于 <time> 的丢失工作树标注为可修剪。

--reason <string>

在使用 lock 命令或 add --lock 命令时,提供工作树被锁定的原因说明。

<worktree>

工作树可以通过路径来识别,可以是相对路径或绝对路径。

如果工作树路径的最后一个组件在所有工作树中是唯一的,则可以使用它来标识一个工作树。例如,如果您只有两个工作树,分别位于 /abc/def/ghi/abc/def/ggg,那么 ghidef/ghi 就足以指向第一个工作树。

引用

使用多个工作树时,某些引用(refs)会在所有工作树之间共享,而另一些则特定于单个工作树。一个例子是 HEAD,每个工作树都不同。本节将介绍共享规则以及如何从一个工作树访问另一个工作树的引用。

一般来说,所有伪引用(pseudo refs)都是每个工作树独有的,而所有以 refs/ 开头的引用都是共享的。伪引用是指直接位于 $GIT_DIR 下而不是 $GIT_DIR/refs 中的引用,例如 HEAD。但也有例外:位于 refs/bisectrefs/worktreerefs/rewritten 下的引用不共享。

每个工作树独有的引用仍然可以通过两个特殊路径从另一个工作树访问:main-worktreeworktrees。前者提供对主工作树的每个工作树独有引用的访问,后者提供对所有链接工作树的引用。

例如,main-worktree/HEADmain-worktree/refs/bisect/good 分别解析为主工作树的 HEADrefs/bisect/good。同样,worktrees/foo/HEADworktrees/bar/refs/bisect/bad 分别等同于 $GIT_COMMON_DIR/worktrees/foo/HEAD$GIT_COMMON_DIR/worktrees/bar/refs/bisect/bad

为了访问引用,最好不要直接查看 $GIT_DIR。而是使用 git-rev-parse[1]git-update-ref[1] 等命令,它们会正确处理引用。

配置文件

默认情况下,存储库的 config 文件会在所有工作树之间共享。如果 core.barecore.worktree 配置变量存在于通用配置文件中,并且 extensions.worktreeConfig 已禁用,则它们仅应用于主工作树。

为了实现工作树特定的配置,您可以启用 worktreeConfig 扩展,例如:

$ git config extensions.worktreeConfig true

在此模式下,特定的配置保存在 git rev-parse --git-path config.worktree 指向的路径中。您可以使用 git config --worktree 命令添加或更新此文件中的配置。旧版本的 Git 将拒绝访问启用了此扩展的存储库。

请注意,在此文件中,core.barecore.worktree 的例外情况已不存在。如果它们存在于 $GIT_DIR/config 中,您必须将其移至主工作树的 config.worktree 文件。您也可以借此机会审查并移动其他您不想与所有工作树共享的配置。

  • core.worktree 永远不应被共享。

  • core.bare 如果其值为 core.bare=true,则不应被共享。

  • core.sparseCheckout 不应被共享,除非您确定所有工作树都始终使用稀疏检出。

有关更多详细信息,请参阅 git-config[1]extensions.worktreeConfig 的文档。

详细信息

每个链接工作树在存储库的 $GIT_DIR/worktrees 目录下都有一个私有子目录。私有子目录的名称通常是链接工作树路径的基本名称,可能附加一个数字以使其唯一。例如,当 $GIT_DIR=/path/main/.git 时,命令 git worktree add /path/other/test-next next 会在 /path/other/test-next 创建链接工作树,并创建一个 $GIT_DIR/worktrees/test-next 目录(如果 test-next 已被占用,则为 $GIT_DIR/worktrees/test-next1)。

在链接工作树中,$GIT_DIR 被设置为指向这个私有目录(例如,在上例中为 /path/main/.git/worktrees/test-next),而 $GIT_COMMON_DIR 被设置为指向主工作树的 $GIT_DIR(例如 /path/main/.git)。这些设置在位于链接工作树顶层目录的 .git 文件中完成。

通过 git rev-parse --git-path 进行路径解析,会根据路径使用 $GIT_DIR$GIT_COMMON_DIR。例如,在链接工作树中,git rev-parse --git-path HEAD 返回 /path/main/.git/worktrees/test-next/HEAD(而不是 /path/other/test-next/.git/HEAD/path/main/.git/HEAD),而 git rev-parse --git-path refs/heads/master 使用 $GIT_COMMON_DIR 并返回 /path/main/.git/refs/heads/master,因为引用在所有工作树之间共享,除了 refs/bisectrefs/worktreerefs/rewritten

有关更多信息,请参阅 gitrepository-layout[5]。经验法则是,当您需要直接访问 $GIT_DIR 内的某些内容时,不要假定路径属于 $GIT_DIR$GIT_COMMON_DIR。使用 git rev-parse --git-path 来获取最终路径。

如果您手动移动了链接工作树,则需要更新入口目录中的 gitdir 文件。例如,如果一个链接工作树被移动到 /newpath/test-next,并且其 .git 文件指向 /path/main/.git/worktrees/test-next,则需要将 /path/main/.git/worktrees/test-next/gitdir 更新为引用 /newpath/test-next。更好的做法是运行 git worktree repair 来自动重新建立连接。

为了防止 $GIT_DIR/worktrees 条目被修剪(这在某些情况下可能很有用,例如当该条目的工作树存储在便携式设备上时),请使用 git worktree lock 命令,该命令会在条目目录中添加一个名为 locked 的文件。该文件以纯文本形式包含原因。例如,如果链接工作树的 .git 文件指向 /path/main/.git/worktrees/test-next,那么一个名为 /path/main/.git/worktrees/test-next/locked 的文件将防止 test-next 条目被修剪。有关详细信息,请参阅 gitrepository-layout[5]

extensions.worktreeConfig 启用时,配置文件 .git/worktrees/<id>/config.worktree 会在 .git/config 之后读取。

列表输出格式

worktree list 命令有两种输出格式。默认格式在一行中显示详细信息,包含列。例如:

$ git worktree list
/path/to/bare-source            (bare)
/path/to/linked-worktree        abcd1234 [master]
/path/to/other-linked-worktree  1234abc  (detached HEAD)

该命令还会显示每个工作树的注解,根据其状态。这些注解包括:

  • locked:如果工作树被锁定。

  • prunable:如果工作树可以通过 git worktree prune 命令进行修剪。

$ git worktree list
/path/to/linked-worktree    abcd1234 [master]
/path/to/locked-worktree    acbd5678 (brancha) locked
/path/to/prunable-worktree  5678abc  (detached HEAD) prunable

对于这些注解,还可能提供原因,您可以使用详细模式查看。然后,该注解将移动到下一行,并缩进,后面跟着额外的信息。

$ git worktree list --verbose
/path/to/linked-worktree              abcd1234 [master]
/path/to/locked-worktree-no-reason    abcd5678 (detached HEAD) locked
/path/to/locked-worktree-with-reason  1234abcd (brancha)
	locked: worktree path is mounted on a portable device
/path/to/prunable-worktree            5678abc1 (detached HEAD)
	prunable: gitdir file points to non-existent location

请注意,如果提供了额外信息,注解将移动到下一行;否则,它将保持与工作树本身在同一行。

Porcelain 格式

Porcelain 格式每行显示一个属性。如果指定了 -z,则每行以 NUL 字符而不是换行符终止。属性以标签和值(由单个空格分隔)的形式列出。布尔属性(如 baredetached)仅显示为标签,并且仅在值为 true 时出现。某些属性(如 locked)可以仅显示为标签,或显示为带值的标签,具体取决于是否有原因。工作树的第一个属性始终是 worktree,一个空行表示记录的结束。例如:

$ git worktree list --porcelain
worktree /path/to/bare-source
bare

worktree /path/to/linked-worktree
HEAD abcd1234abcd1234abcd1234abcd1234abcd1234
branch refs/heads/master

worktree /path/to/other-linked-worktree
HEAD 1234abc1234abc1234abc1234abc1234abc1234a
detached

worktree /path/to/linked-worktree-locked-no-reason
HEAD 5678abc5678abc5678abc5678abc5678abc5678c
branch refs/heads/locked-no-reason
locked

worktree /path/to/linked-worktree-locked-with-reason
HEAD 3456def3456def3456def3456def3456def3456b
branch refs/heads/locked-with-reason
locked reason why is locked

worktree /path/to/linked-worktree-prunable
HEAD 1233def1234def1234def1234def1234def1234b
detached
prunable gitdir file points to non-existent location

除非使用了 -z 选项,否则锁定原因中的任何“特殊”字符(如换行符)都会被转义,并且整个原因会像配置变量 core.quotePath(请参阅 git-config[1])中所解释的那样被引用。例如:

$ git worktree list --porcelain
...
locked "reason\nwhy is locked"
...

示例

您正在进行重构,突然老板要求您立即修复一个问题。通常,您会使用 git-stash[1] 来临时存储您的更改,但是,您当前的工作树状态非常混乱(有新的、移动的、删除的文件以及其他杂乱的元素),您不想冒任何风险去干扰它。此时,您可以创建一个临时的链接工作树来修复紧急问题,完成后将其删除,然后继续您之前的重构工作。

$ git worktree add -b emergency-fix ../temp master
$ pushd ../temp
# ... hack hack hack ...
$ git commit -a -m 'emergency fix for boss'
$ popd
$ git worktree remove ../temp

配置

本节中以下所有内容均从 git-config[1] 文档中选择性地包含。内容与彼处相同:

worktree.guessRemote

如果没有指定分支,并且未使用 -b-B--detach 选项,那么 git worktree add 命令默认会从 HEAD 创建一个新分支。如果 worktree.guessRemote 设置为 true,worktree add 会尝试查找一个远程跟踪分支,其名称唯一匹配新分支的名称。如果找到这样的分支,它将被检出并设置为新分支的“上游”。如果没有找到匹配项,它将回退到从当前 HEAD 创建新分支。

worktree.useRelativePaths

使用相对路径(当设置为 "true" 时)或绝对路径(当设置为 "false" 时)来链接工作树。这对于存储库和工作树可能在不同位置或环境中移动的设置特别有用。默认为 "false"。

请注意,将 worktree.useRelativePaths 设置为 "true" 意味着启用了 extensions.relativeWorktrees 配置(请参阅 git-config[1]),因此与旧版本的 Git 不兼容。

BUG

总体而言,多个检出仍然是实验性的,并且对子模块的支持不完整。不建议对超级项目进行多个检出。

GIT

Git[1] 套件的一部分