自2015年7月起,Git 2.5提出了一个替代contrib/workdir/git-new-workdir
的方案:git worktree。
参见提交记录68a2e6a,作者是Junio C Hamano(gitster
)。
该发布说明提到:
替代contrib/workdir/git-new-workdir
的方案不再依赖符号链接,使对象和引用的共享更加安全,通过让借用者和被借用者相互感知来实现。
查看 提交 799767cc9(Git 2.5rc2)
这意味着您现在可以 执行 git worktree add <path> [<branch>]
命令
创建一个名为
<path>
的目录,并把工作空间切换到其中。新的工作目录与当前仓库链接,共享除了 HEAD、index 等工作目录特定文件以外的一切内容。
git worktree
部分补充说明:
一个 Git 仓库可以支持多个工作树,允许您同时检出多个分支。通过
git worktree add
命令,可以将新的工作树与该仓库关联。
这个新的工作树称为“链接的工作树”,与由 "
git init
" 或 "
git clone
" 准备的“主工作树”相对应。一个仓库有一个主工作树(如果它不是裸仓库),以及零个或多个链接的工作树。
每个链接的工作树都在存储库的
$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 config
中的
gc.pruneworktreesexpire
),或者您可以在主工作树或任何链接的工作树中运行
git worktree prune
以清除任何过时的管理文件。
警告:仍然需要注意git worktree
"BUGS" 部分。
子模块的支持不完整。
不建议对超级项目进行多次检出。
注意:从 git 2.7rc1(2015 年 11 月)开始,您可以列出您的工作树。
请参见
提交 bb9c03b,
提交 92718b7,
提交 5193490,
提交 1ceb7f9,
提交 1ceb7f9,
提交 5193490,
提交 1ceb7f9,
提交 1ceb7f9 (2015 年 10 月 8 日),
提交 92718b7,
提交 5193490,
提交 1ceb7f9,
提交 1ceb7f9 (2015 年 10 月 8 日),
提交 5193490,
提交 1ceb7f9 (2015 年 10 月 8 日),
提交 1ceb7f9 (2015 年 10 月 8 日) 和
提交 ac6c561(2015 年 10 月 2 日),由
Michael Rappazzo (rappazzo
) 提交。
(由 Junio C Hamano -- gitster
-- 合并于 提交 a46dcfb,2015 年 10 月 26 日)
worktree
: 添加 'list
' 命令
'git worktree list
' 遍历工作树列表,并输出工作树的详细信息,包括工作树路径、当前检出的修订版本和分支,以及工作树是否为裸库。
$ git worktree list
/path/to/bare-source (bare)
/path/to/linked-worktree abcd1234 [master]
/path/to/other-linked-worktree 1234abc (detached HEAD)
此外,还提供了瓷砖格式选项。
瓷砖格式每个属性一行。
- 属性用标签和值分隔的单个空格列出。
- 布尔属性(如“bare”和“detached”)仅列出标签,并且仅在值为true时存在。
- 空行表示工作树的结束
例如:
$ 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
注意:如果您移动了工作树文件夹,则需要手动更新gitdir文件。
请参阅提交 618244e(2016年1月22日)和提交 d4cddd6(2016年1月18日),作者为Nguyễn Thái Ngọc Duy(pclouds
)。
协助者为:Eric Sunshine(sunshineco
)。
(由Junio C Hamano -- gitster
--于提交 d0a1cbc合并,日期为2016年2月10日)
git 2.8(2016年3月)的新文档将包括以下内容:
如果您移动了链接的工作树,您需要更新条目目录中的'gitdir
'文件。
例如,如果链接的工作树被移动到/newpath/test-next
并且它的 .git
文件指向/path/main/.git/worktrees/test-next
,那么请更新
/path/main/.git/worktrees/test-next/gitdir
,将其引用改为/newpath/test-next
。
删除分支时要小心:在git 2.9之前(即2016年6月),您可以删除另一个工作树中正在使用的分支。
当使用“git worktree”功能时,“git branch -d”允许删除在另一个工作树中检出的分支。
请参见提交 f292244 (2016年3月29日) 由Kazuki Yamaguchi (rhenium
)完成。
协助者:Eric Sunshine (sunshineco
)。
(由Junio C Hamano -- gitster
--合并于提交 4fca4e3,2016年4月13日)
branch -d
: 拒绝删除当前正在使用的分支
当一个分支被当前工作树检出时,删除该分支是被禁止的。但是,当该分支仅被其他工作树检出时,错误地删除将会成功。使用
find_shared_symref()
来检查分支是否正在使用,而不仅仅是与当前工作树的HEAD进行比较。
同样地,在git 2.9之前(2016年6月),重命名在另一个工作树中检出的分支没有调整该其他工作树中的符号HEAD。
请参阅提交 18eb3a9(2016年4月8日),提交 70999e9,提交 2233066(2016年3月27日),由Kazuki Yamaguchi (rhenium
)撰写的内容。
(由Junio C Hamano -- gitster
--在提交 741a694中合并,2016年4月18日)
branch -m
: 更新所有工作树的HEAD
When renaming a branch, currently only the HEAD of current working tree
is updated, but it must update HEADs of all working trees which point at
the old branch.
This is the current behavior, /path/to/wt's HEAD is not updated:
% git worktree list
/path/to 2c3c5f2 [master]
/path/to/wt 2c3c5f2 [oldname]
% git branch -m master master2
% git worktree list
/path/to 2c3c5f2 [master2]
/path/to/wt 2c3c5f2 [oldname]
% git branch -m oldname newname
% git worktree list
/path/to 2c3c5f2 [master2]
/path/to/wt 0000000 [oldname]
This patch fixes this issue by updating all relevant worktree HEADs
when renaming a branch.
锁定机制在官方支持的git 2.10版本中(2016年Q3)推出。
请查看 提交 080739b, 提交 6d30862, 提交 58142c0, 提交 346ef53, 提交 346ef53, 提交 58142c0, 提交 346ef53, 提交 346ef53 (2016年6月13日), 和 提交 984ad9e, 提交 6835314 (2016年6月3日) 由 Nguyễn Thái Ngọc Duy (pclouds
) 提出。
建议者为: Eric Sunshine (sunshineco
)。
(由Junio C Hamano -- gitster
--于提交 2c608e0合并, 2016年7月28日)
git worktree lock [
git worktree unlock <worktree>
如果一个链接的工作树存储在便携设备或网络共享上,而且该设备不总是被挂载,您可以通过发出“git worktree lock”命令来防止其管理文件被修剪,并可选择指定“--reason”来解释为什么要锁定工作树。
:如果工作树路径中的最后一部分在工作树之间是唯一的,那么它可以用来标识工作树。例如,如果您只在“/abc/def/ghi”和“/abc/def/ggg”两个地方有两个工作树,则“ghi”或“def/ghi”足以指向前者的工作树。
Git 2.13 (Q2 2017)在
提交507e6e9(2017年4月12日)中添加了一个
lock
选项,作者是
Nguyễn Thái Ngọc Duy(pclouds
)。建议者为
David Taylor(dt
),帮助者为
Jeff King(peff
)。
(由Junio C Hamano -- gitster
--于提交e311597合并,2017年4月26日)
允许在创建工作树后立即锁定它。这有助于防止“git worktree add; git worktree lock”和“git worktree prune”之间的竞争。
git worktree add --lock
相当于在
git worktree add
之后执行
git worktree lock
,但没有竞态条件。
Git 2.17+(2018年第二季度)增加了
git worktree move
/
git worktree remove
功能:
查看此答案。
Git 2.19(2018年第三季度)增加了一个“--quiet”选项,使“git worktree add”更加简洁。
请看提交 371979c (2018年8月15日) ,作者是Elia Pinto (devzero2000
)。
协助者:Martin Ågren martin.agren@gmail.com,Duy Nguyen (pclouds
), 和 Eric Sunshine (sunshineco
)。
(由Junio C Hamano -- gitster
--在提交 a988ce9中合并,2018年8月27日)
worktree
: 添加--quiet
选项
为git worktree
添加'--quiet
'选项,与其他git
命令一样。
只有'add
'命令受其影响,因为除了'list
'之外的所有其他命令当前默认都是静默的。
请注意,"
git worktree add
" 以前是使用 "查找可用名称并使用 stat 和
mkdir
" 的方式,这种方式容易出现竞争问题。
从 Git 2.22(2019 年第二季度)开始,通过使用
mkdir
并在循环中处理
EEXIST
来解决此问题。
请参见
提交 7af01f2(2019 年 2 月 20 日),作者为
Michal Suchanek (hramrach
)。
(由 Junio C Hamano -- gitster
-- 在 提交 20fe798 中合并)
worktree
:修复worktree add
竞争问题
Git运行stat循环来查找可用的工作树名称,然后在找到的名称上执行
mkdir
。
将其转换为
mkdir
循环以避免另一个worktree add调用查找相同的空闲名称并首先创建目录。
Git 2.22(2019年第二季度)修复了逻辑,以判断Git存储库是否具有工作树,并保护"
git branch -D
"以防止错误地删除当前检出的分支。实施此逻辑时,对于具有不寻常名称的存储库存在问题,而这种情况恰恰是子模块的常态。请参见
提交f3534c9(2019年4月19日),作者为
Jonathan Tan (jhowtan
)。
(由Junio C Hamano -- gitster
--于提交ec2642a中合并,2019年5月8日)
worktree
: 更新 is_bare
启发式算法
当运行“
git branch -D <name>
”时,Git通常会首先检查该分支当前是否被检出。
但是,如果该存储库的Git目录不在“
<repo>/.git
”中,则不执行此检查,例如,如果该存储库是作为子模块存在且其Git目录存储为“
super/.git/modules/<repo>
”。
这将导致即使已检出该分支也被删除。
这是因为worktree.c
中的get_main_worktree()
仅使用启发式方法设置工作树上的is_bare
,即如果工作树路径不以“/.git
”结尾,则认为存储库是裸的,否则认为不是裸的。
此is_bare
代码是在92718b7(“worktree
:add details to the worktree struct”,2015-10-08,Git v2.7.0-rc0)中引入的,遵循pre-core.bare
启发式方法。
此补丁执行两个操作:
- 教
get_main_worktree()
使用is_bare_repository()
代替,该函数在7d1864c(“Introduce is_bare_repository() and core.bare configuration variable”,2007-01-07,Git v1.5.0-rc1)中引入并在e90fdc3(“Clean up work-tree handling”,2007-08-01,Git v1.5.3-rc4)中进行了更新。
这解决了上述描述的“git branch -D <name>
”问题。
但是...
- 如果存储库具有
core.bare=1
,但从其中一个辅助工作树运行“git
”命令,则is_bare_repository()
返回false(这是可以的,因为有可用的工作树)。
而且,当将主工作树视为非裸时,会出现问题:例如,无法从由主工作树的HEAD引用的辅助工作树中删除分支,即使该主工作树是裸的。
为了避免这种情况,在设置is_bare
时还要检查core.bare
。
如果core.bare=1
,则信任它;否则,使用is_bare_repository()
。
在 Git 2.29 (2020年第四季度) 中,"worktree
" API 提供了更好的工作树路径确定。
请查看 提交 918d8ff, 提交 1c4854e, 提交 246756f, 提交 62573a5 (2020年7月31日) 由Eric Sunshine (sunshineco
)提交。
(由Junio C Hamano -- gitster
--在提交 197253e中合并,2020年8月10日)
签名作者:Eric Sunshine
The content of .git/worktrees/<id>/gitdir
must be a path of the form "/path/to/worktree/.git
".
Any other content would be indicative of a corrupt "gitdir
" file.
To determine the path of the worktree itself one merely strips the "/.git
" suffix, and this is indeed how the worktree path was determined from inception.
However, 5193490442 ("worktree
: add a function to get worktree details", 2015-10-08, Git v2.7.0-rc0 -- merge listed in batch #7) extended the path manipulation in a mysterious way.
If it is unable to strip "/.git
" from the path, then it instead reports the current working directory as the linked worktree's path:
if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
strbuf_reset(&worktree_path);
strbuf_add_absolute_path(&worktree_path, ".");
strbuf_strip_suffix(&worktree_path, "/.");
}
This logic is clearly bogus; it can never be generally correct behavior. It materialized out of thin air in 5193490442 with neither explanation nor tests to illustrate a case in which it would be desirable.
It's possible that this logic was introduced to somehow deal with a corrupt "gitdir
" file, so that it returns some sort of meaningful value, but returning the current working directory is not helpful. In fact, it is quite misleading (except in the one specific case when the current directory is the worktree whose "gitdir
" entry is corrupt).
Moreover, reporting the corrupt value to the user, rather than fibbing about it and hiding it outright, is more helpful since it may aid in diagnosing the problem.
Therefore, drop this bogus path munging and restore the logic to the original behavior of merely stripping "/.git
".
git-new-workdir
将会被git checkout --to=<path>
替代。请参见下面的答案:https://dev59.com/SW025IYBdhLWcg3wAg_p#30185564 - VonCgit worktree add <路径> [<分支>]
(Git 2.5 rc2)。请参见我下面编辑的答案。 - VonC