首先,附注:如果您打算在非平凡时期(一次超过两周)使用git worktree add,请确保您的 Git 至少是 2.15 版本。1
出于您的特定目的,我建议不要使用git clone --bare。相反,请使用常规克隆,然后使用您打算执行的 git worktree adds。您在a comment 中注意到:
...您最终不得不创建一个虚拟分支来放置存储库本身,因为不可能同时在同一个分支上同时拥有存储库和工作树。
有几个简单的解决方法:
-
从您要添加的 N 个工作树中选择一个,并将其用作主工作树中的分支:
git checkout -b branch-1 ssh://git@git.example.com/project/repo branch-1
缺点是您现在有一个特殊的、可区分的“主”分支,您不能随时删除它——所有其他分支都依赖于它。
-
或者,在克隆之后,在工作树中使用git checkout --detach,在默认分支上获得一个分离的 HEAD:
git clone ssh://git@git.example.com/project/repo repo.git
cd repo.git
git checkout --detach
-
第二种方法的唯一缺点是工作树充满了文件,可能会浪费空间。还有一个解决方案:使用空树创建一个空白提交,然后检查一下:
git clone ssh://git@git.example.com/project/repo repo.git
cd repo.git
git checkout $(git commit-tree $(git hash-object -t tree /dev/null) < /dev/null)
好的,最后一个并不完全显而易见。不过这真的很简单。 git hash-object -t tree /dev/null 生成每个存储库中已经存在的 empty tree 的哈希 ID。 git commit-tree 做出一个提交来包装那个空树——没有一个,所以我们必须创建一个来检查它——并打印出这个新提交的哈希 ID,git checkout 将其作为一个分离的头。效果是清空我们的索引和工作树,因此存储库工作树中唯一的东西是.git 目录。我们所做的空提交不在分支上,也没有父提交(这是一个单独的根提交),我们永远不会推送到任何地方。
1原因是 Git 2.5,git worktree 首次出现的地方,有一个我认为非常糟糕的错误:git gc 从不扫描 添加的 工作-trees 的HEAD 文件,也不是它们的索引文件。如果添加的工作树总是在某个分支上并且从来没有任何git added 但未提交的工作,这永远不会导致任何问题。如果未提交的工作至少 14 天没有闲置,则默认的修剪保护时间足以防止其被破坏。但是,如果您 git add 一些工作或承诺在一个独立的 HEAD 上,去度假一个月或以其他方式保持这个添加的工作树不受干扰,然后再回到它,和git gc --auto在两周的宽限期结束后运行,您保存的文件已被销毁!
此错误已在 Git 2.15 中修复。
为什么--bare 出错了
问题的根源在于git clone --bare 做了两件事:
- 它创建了一个没有工作树并且没有初始
git checkout 的裸存储库(core.bare 设置为true); 和
- 它将默认的
fetch refspec 从+refs/heads/*:refs/remotes/<em>origin</em>/* 更改为+refs/heads/*:refs/heads/*。
第二项表示没有refs/remotes/origin/ 名称,正如您所发现的。由于refmaps 的(主要是隐藏的/内部的)概念,这不容易修复,git fetch 文档中非常简短地显示了它(参见链接)。
更糟糕的是,这意味着refs/heads/* 将在每个git fetch 上更新。 git worktree add 拒绝创建第二个工作树是有原因的,该工作树引用在任何现有工作树中签出的 same 分支,那就是 Git 从根本上假设没有人会与 HEAD 附加到 this 工作树中的 refs/heads/<em>name</em> 引用混淆。因此,即使您确实解决了 refmap 问题,当您运行 git fetch 并且由于 --prune 和上游删除了相同名称而更新(甚至删除)refs/heads/<em>name</em> 名称时,添加的工作树HEAD 中断,工作树本身就会出现问题。 (见Why does Git allow pushing to a checked-out branch in an added worktree? How shall I recover?)
还有一件事你可以尝试,我根本没有测试过,那就是:像你一样做裸克隆,然后更改 refspec 并重新获取,然后删除所有现有的分支名称,因为它们没有设置上游(或等效地设置它们的上游):
git clone --bare ssh://git@git.example.com/project/repo repo.git
cd repo.git
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
git fetch
git for-each-ref --format='%(refname:short)' refs/heads | xargs git branch -d
(或将xargs 替换为xargs -n1 -I{} git branch --set-upstream-to=origin/{} {})。