简体中文 ▾ 主题 ▾ 最新版本 ▾ git-worktree 上次更新于 2.48.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 命令,可以将新的工作区与仓库关联起来,并附带额外的元数据,以区分同一仓库中的不同工作区。工作区连同其元数据被称为“工作区”。

这个新的工作区被称为“链接工作区”(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>)关联,该分支以 $(basename <path>) 命名。如果 <branch> 不存在,则会自动创建一个基于 HEAD 的新分支,如同给定了 -b <branch>。如果 <branch> 确实存在,并且没有在其他地方被检出,它将在新的工作区中被检出,否则命令将拒绝创建该工作区(除非使用了 --force)。

如果省略 <commit-ish>,且没有使用 --detach--orphan,并且没有有效的本地分支(如果指定了 --guess-remote 则也没有远程分支),那么为方便起见,新的工作区将与一个新的未出生(unborn)分支 <branch> 关联(如果既没有使用 -b 也没有使用 -B,则以 $(basename <path>) 命名),如同向命令传递了 --orphan。如果仓库有远程且使用了 --guess-remote,但不存在任何远程或本地分支,则命令将失败并发出警告,提醒用户首先从远程仓库抓取(fetch)(或通过使用 -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> 开始,并将 <new-branch> 检出到新的工作区中。如果省略 <commit-ish>,则默认为 HEAD。默认情况下,如果新分支已存在,-b 会拒绝创建。-B 会覆盖此安全措施,将 <new-branch> 重置为 <commit-ish>

-d
--detach

使用 add 命令,分离新工作区中的 HEAD。参见 git-checkout[1] 中的“分离的 HEAD”(DETACHED HEAD)。

--[no-]checkout

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

--[no-]guess-remote

使用 worktree add <path>,且不带 <commit-ish> 时,如果恰好有一个远程跟踪分支的名称与 <path> 的基本名称匹配,则新分支将基于该远程跟踪分支创建,并将其标记为新分支的“上游”,而不是从 HEAD 创建新分支。

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

--[no-]relative-paths

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

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

--[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>,则将其标注为可修剪(prunable)。

--reason <string>

使用 lockadd --lock 时,解释工作区被锁定的原因。

<工作区>

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

如果工作区路径中的最后一个路径组件在所有工作区中是唯一的,则可以使用它来识别工作区。例如,如果您只有两个工作区,分别在 /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 不应被共享,除非您确定所有工作区都始终使用稀疏检出(sparse checkout)。

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

  • 如果工作区可以通过 git worktree prune 修剪,则显示 prunable

$ 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

BUG

总的来说,多重检出(multiple checkout)仍处于实验阶段,且对子模块的支持不完整。不建议对超级项目(superproject)进行多重检出。

GIT

Git[1] 套件的一部分

scroll-to-top