Git
English ▾ 主题 ▾ 最新版本 ▾ git-worktree 最后更新于 2.43.1

名称

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 将新的工作树与存储库关联,并附带其他元数据以区分同一存储库中的其他工作树。工作树及其元数据称为“工作树”。

这个新的工作树称为“链接的工作树”,以区别于由 git-init[1]git-clone[1] 创建的“主工作树”。存储库有一个主工作树(如果它不是裸存储库)和零个或多个链接的工作树。完成链接的工作树后,使用 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,则为了方便起见,新工作树将与一个名为 $(basename <path>) 的分支(称为 <branch>)关联。如果 <branch> 不存在,则会自动创建一个基于 HEAD 的新分支,就像给出了 -b <branch> 一样。如果 <branch> 存在,它将在新工作树中检出,如果它没有在其他任何地方检出,否则命令将拒绝创建工作树(除非使用 --force)。

如果省略了 <commit-ish>,未使用 --detach--orphan,并且没有有效的本地分支(或如果指定了 --guess-remote 则为远程分支),则为了方便起见,新工作树将与一个新的未创建的分支关联,该分支名为 <branch>(如果未使用 -b-B,则为 $(basename <path>) 之后),就像将 --orphan 传递给命令一样。如果存储库具有远程并且使用 --guess-remote,但不存在远程或本地分支,则命令将失败并显示警告,提醒用户先从远程获取(或通过使用 -f/--force 覆盖)。

list

列出每个工作树的详细信息。首先列出主工作树,然后列出每个链接的工作树。输出详细信息包括工作树是否为裸存储库、当前检出的修订版本、当前检出的分支(如果不存在则为“分离的 HEAD”)、如果工作树被锁定则为“已锁定”、如果工作树可以被 prune 命令修剪则为“可修剪”。

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

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

除非使用 --force,否则 remove 会拒绝删除不干净的工作树。要删除已锁定的工作树,请指定两次 --force

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

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

-d
--detach

使用add命令,在新工作树中分离HEAD。请参阅git-checkout[1]中的“DETACHED HEAD”部分。

--[no-]checkout

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

--[no-]guess-remote

使用worktree add <path>命令,并且没有指定<commit-ish>时,Git不会从HEAD创建新分支。如果存在一个远程分支,其名称与<path>的基本名称完全匹配,则 Git 会将新分支基于此远程跟踪分支创建,并将此远程跟踪分支标记为新分支的“上游”分支。

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

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

当使用list命令并指定--porcelain选项时,以 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足以指向前一个工作树。

引用

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

通常,所有伪引用都是每个工作树的,所有以refs/开头的引用都是共享的。伪引用是指像HEAD这样的引用,它们直接位于$GIT_DIR下,而不是位于$GIT_DIR/refs内部。但是,也有一些例外: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=true,则不应共享core.bare

  • 除非您确定始终对所有工作树使用稀疏检出,否则不应共享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

请注意,如果提供附加信息,则注释将移动到下一行,否则它将保留在与工作树相同的行上。

瓷器格式

瓷器格式每个属性一行。如果给出-z,则行以NUL而不是换行符结尾。属性以标签和值列出,标签和值之间用单个空格分隔。布尔属性(如baredetached)仅列出标签,并且仅在值为真时存在。某些属性(如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

git[1]套件的一部分

scroll-to-top