简体中文 ▾ 主题 ▾ 最新版本 ▾ git-worktree 上次更新于 2.53.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)”。

这个新的工作树被称为“链接工作树”,以区别于由 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

列出每个工作树的详细信息。首先列出主工作树,然后列出每个链接工作树。输出详情包括工作树是否为裸仓库、当前检出的修订版本、当前检出的分支(如果没有则显示 "detached 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 使用,从 <commit-ish> 开始创建一个名为 <new-branch> 的新分支,并在新工作树中检出 <new-branch>。如果省略 <commit-ish>,则默认为 HEAD。默认情况下,如果新分支已存在,-b 会拒绝创建。使用 -B 则会覆盖此保护,将 <new-branch> 重置为 <commit-ish>

-d
--detach

配合 add 使用,在新工作树中分离 HEAD。请参阅 git-checkout[1] 中的“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> 时,如果恰好在一个远程仓库中存在一个与 <path> 的基名匹配的跟踪分支,则不是从 HEAD 创建新分支,而是基于该远程跟踪分支创建新分支,并将其标记为新分支的“上游”。

也可以通过使用 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

list 指定了 --porcelain 时,以 NUL 而非换行符结束每一行。这使得在工作树路径包含换行符时解析输出成为可能。

-q
--quiet

配合 add 使用时,抑制反馈消息。

-v
--verbose

配合 prune 使用时,报告所有移除操作。

配合 list 使用时,输出关于工作树的附加信息(见下文)。

--expire <time>

配合 prune 使用时,仅使早于 <time> 的未使用工作树过期。

配合 list 使用时,将早于 <time> 的缺失工作树标注为可修剪。

--reason <string>

配合 lockadd --lock 使用,说明工作树锁定的原因。

<worktree>

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

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

引用 (REFS)

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

通常,所有伪引用(pseudo refs)都是特定于工作树的,而所有以 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/config 之后读取配置文件 .git/worktrees/<id>/config.worktree

列表输出格式

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) 格式

机器解析格式每个属性占用一行。如果给出了 -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-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] 套件的一部分