为什么两个 git 工作树不能签出同一个分支?

Why can two git worktrees not check out the same branch?

使用单独的 git-worktree,为什么我不能签出与主工作副本中相同的分支?如果我尝试,我会收到错误消息:

fatal: 'mybranch' is already checked out at '/path/to/repo'

我可以看到,如果我从一个工作树签入,另一个将以分离的 HEAD 状态结束,但这有那么糟糕吗,为什么我什至不能签出同一个分支?

I can see that if I check in from one worktree, the other would end up in a detached HEAD state

实际上,不会,这就是问题所在!

每个工作树都有自己的 HEAD 和自己的索引(也称为临时区域或缓存)。所有共享实际的底层存储库,以及底层分支提示文件,例如 .git/refs/heads/mybranch.

那么,假设两个不同的工作树(我会让它们都与主仓库分开,这样就没有明显的 "preferred" 一个)都有 HEAD 指向 mybranch,然后您从两个工作树之一进行提交:

repo$ cd ../worktree1
worktree1$ ... hack away ...
worktree1$ git add bar1 bar2 && git commit -m 'foo some bars'

现在发生的是通常情况:Git 将索引写入一棵或多棵树,使用新树写一个新提交,任何提交 mybranch 解析为它的父提交,并且更新 mybranch 以指向新提交。 worktree1 的索引现在与新提交匹配。现在我们这样做:

worktree1$ cd ../worktree2
worktree2$ ... modify unrelated file, not bar1 or bar2 ...
worktree2$ git add unrelated && git commit -m 'unrelated change'

现在发生的是 Git 写入索引...等等, 索引? 哪个索引?嗯, 索引——worktree2 中的索引。其中没有从 worktree1 修改和添加的文件。 (它确实有两个 bar 文件,除非它们是全新的,但它有 旧版本 。)好的,所以 Git 将索引写入一棵或多棵树,使用新树和任何提交 mybranch 解析为其父项的内容写入新提交,并更新 mybranch 以指向新提交。

提交链现在看起来像这样:

...--o--1--2

其中 1../worktree1 中的提交,2worktree2 中的提交。在两个工作树中,名称 mybranch 都指向提交 2。两个工作树中的名称 HEAD 都包含 ref: refs/heads/mybranch。当然,两个工作树中的索引文件是不同的。

提交 1 内容 worktree1 中索引中的任何内容。它包含您对 bar1bar2.

所做的更改

提交 2 内容 worktree2 中索引中的任何内容。它具有您在 unrelated 中所做的更改,但它 没有 具有在文件 bar1bar2 中所做的更改。实际上,您在 worktree2 中所做的提交还原了 这两个文件!

如果您愿意让一个或两个工作树处于 "detached HEAD" 状态,您可以使用 git checkout --detach mybranchgit checkout refs/heads/mybranch 以这种方式检查它们。现在至少其中一个 HEAD 直接指向提交,而不是分支名称,并且 Git 应该允许两个工作树具有相同的 commit签出。