为什么两个 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
中的提交,2
是 worktree2
中的提交。在两个工作树中,名称 mybranch
都指向提交 2
。两个工作树中的名称 HEAD
都包含 ref: refs/heads/mybranch
。当然,两个工作树中的索引文件是不同的。
提交 1
的 内容 是 worktree1
中索引中的任何内容。它包含您对 bar1
和 bar2
.
所做的更改
提交 2
的 内容 是 worktree2
中索引中的任何内容。它具有您在 unrelated
中所做的更改,但它 没有 具有在文件 bar1
和 bar2
中所做的更改。实际上,您在 worktree2
中所做的提交还原了 这两个文件!
如果您愿意让一个或两个工作树处于 "detached HEAD" 状态,您可以使用 git checkout --detach mybranch
或 git checkout refs/heads/mybranch
以这种方式检查它们。现在至少其中一个 HEAD
直接指向提交,而不是分支名称,并且 Git 应该允许两个工作树具有相同的 commit签出。
使用单独的 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
中的提交,2
是 worktree2
中的提交。在两个工作树中,名称 mybranch
都指向提交 2
。两个工作树中的名称 HEAD
都包含 ref: refs/heads/mybranch
。当然,两个工作树中的索引文件是不同的。
提交 1
的 内容 是 worktree1
中索引中的任何内容。它包含您对 bar1
和 bar2
.
提交 2
的 内容 是 worktree2
中索引中的任何内容。它具有您在 unrelated
中所做的更改,但它 没有 具有在文件 bar1
和 bar2
中所做的更改。实际上,您在 worktree2
中所做的提交还原了 这两个文件!
如果您愿意让一个或两个工作树处于 "detached HEAD" 状态,您可以使用 git checkout --detach mybranch
或 git checkout refs/heads/mybranch
以这种方式检查它们。现在至少其中一个 HEAD
直接指向提交,而不是分支名称,并且 Git 应该允许两个工作树具有相同的 commit签出。