为什么两个git工作树不能检出相同的分支?

16

如果我使用独立的git-worktree,为什么不能检出与主工作副本相同的分支?如果我尝试这样做,就会出现错误:

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

我发现如果我从一个工作树签出,另一个将进入分离 HEAD 状态,但这样做有什么问题吗?为什么我甚至不能签出同一分支?


1
使用 git switch --ignore-other-worktrees - bloody
1个回答

19

我可以看出,如果我从一个工作树中签入,另一个工作树就会进入分离 HEAD 状态。

实际上,不会发生这种情况,而这正是问题所在!

每个工作树都有自己的 HEAD 和索引(也称为暂存区或缓存),它们共享实际的底层存储库和底层分支尖文件,例如 .git/refs/heads/mybranch

那么,假设有两个不同的工作树(我将它们都与主存储库分开,以便没有明显的“首选”工作树)都将 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将索引(Index)写入...等等,什么索引? 哪个索引? 好吧,就是索引——在worktree2中的索引。它没有来自worktree1的修改和添加的文件。(除非它们是全新的,但它有旧版本的两个bar文件。)然后,Git将索引写入一个或多个树中,使用新树和mybranch解析为其父级的任何提交作为新提交,并更新mybranch以指向新提交。

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

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

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

提交1内容worktree1中索引的内容。它包含了你对bar1bar2所做的更改。

提交2内容worktree2中索引的内容。它包含了你在unrelated中所做的更改,但是没有文件bar1bar2的更改。实际上,在worktree2中进行的提交撤销了这两个文件的更改!

如果你愿意让一个或两个工作树处于“分离 HEAD”状态,你可以使用 git checkout --detach mybranchgit checkout refs/heads/mybranch这样的方式进行检出。现在至少有一个工作树将直接指向一个提交,而不是分支名称,Git应该允许两个工作树检出相同的提交


谢谢您的解释。我以为git会注意到checkout不再在当前分支提交上,就像处于分离HEAD状态一样工作。 - Tor Klingberg
不,要获取分离的 HEAD,你必须在文件 .git/HEAD 中有一个原始哈希 ID。这与任何分支名称无关:HEAD 要么是符号性的,因此您位于名称在 .git/HEAD 中的分支上,要么它具有原始哈希 ID,因此您处于分离状态,并且该哈希作为当前提交。 (主要是 git checkout 修改 .git/HEAD,尽管 git symbolic-ref 也可以这样做。) - torek
2
好的,我会尝试使用 git checkout --detach mybranch。有时候我想在一个干净的分支上运行测试,并且不会更改任何内容,所以分离的 HEAD 是可以的。 - Tor Klingberg
1
如果git commit执行与接收推送相同类型的检查,理论上可以允许多个添加的工作树使用相同的分支:但现在你运行git commit并得到一个错误,即拒绝提交。(还有一个推送处理中的错误,不过这个问题最终会被修复。) - torek
@schuelermine:原因与分支名称仅保存一个哈希ID的方式有关,而索引和工作树则假定来自于找到该特定哈希ID的提交所关联的文件。如果相同的分支名称出现在多个工作树中,则在任何一个工作树中进行新提交都会更新分支名称,从而使索引和工作树设置无效。这实际上就是在接收推送时遇到的相同问题,正如我所说的,这就是receive.denyCurrentBranch存在的原因。 - torek
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接