I can see that if I check in from one worktree, the other would end up in a detached HEAD state
Actually, it wouldn't, and that's the problem!
Each work-tree has its own HEAD
, and its own index (aka staging-area or cache). All share the actual underlying repository, and the underlying branch tip files such as .git/refs/heads/mybranch
.
Suppose, then, that two different work-trees (I'll make them both separate from the main repo just so that there's no obvious "preferred" one) both have HEAD
pointing to mybranch
, and you make a commit from one of the two work-trees:
repo$ cd ../worktree1
worktree1$ ... hack away ...
worktree1$ git add bar1 bar2 && git commit -m 'foo some bars'
What happens now is the usual: Git writes the index to one or more trees, writes a new commit using the new tree and whatever commit mybranch
resolves to as its parent commit, and updates mybranch
to point to the new commit. The index for worktree1
now matches the new commit. Now we do this:
worktree1$ cd ../worktree2
worktree2$ ... modify unrelated file, not bar1 or bar2 ...
worktree2$ git add unrelated && git commit -m 'unrelated change'
What happens now is that Git writes the index ... wait, the index? Which index? Well, the index—the index in worktree2
. Which does not have files modified and added from worktree1
. (It does have the two bar
files, unless they're totally new, but it has the old versions.) OK, so Git writes the index into one or more trees, writes a new commit using the new tree and whatever commit mybranch
resolves to as its parent, and updates mybranch
to point to the new commit.
The commit chain now looks like this:
...--o--1--2
where 1
is the commit made in ../worktree1
, and 2
is the commit made in worktree2
. The name mybranch
, in both work-trees, points to commit 2
. The name HEAD
, in both work-trees, contains ref: refs/heads/mybranch
. The index files in the two work trees are different, of course.
The contents for commit 1
are whatever is in the index in worktree1
. It has the changes you made to bar1
and bar2
.
The contents for commit 2
are whatever is in the index in worktree2
. It has the changes you made in unrelated
, but it does not have the changes made in files bar1
and bar2
. In effect, the commit you made in worktree2
reverted the two files!
If you're willing to have one or both work-trees be in "detached HEAD" state, you can check them out that way, with git checkout --detach mybranch
or git checkout refs/heads/mybranch
. Now at least one of them will have HEAD
pointing directly to a commit, rather than to the branch name, and Git should permit the two work-trees to have the same commit checked out.
git switch --ignore-other-worktrees
– Mott