Phillip,
Good catch — let me clarify what the script actually places on disk.
The hardlink copy does not go into $GIT_COMMON_DIR/modules/ (which both worktrees would then share). It goes into each worktree's own gitdir entry:
cp -al $GIT_COMMON_DIR/modules \
$GIT_COMMON_DIR/worktrees/$id/modulesSo worktree A uses $GIT_COMMON_DIR/modules/sub/ and worktree B uses $GIT_COMMON_DIR/worktrees/B/modules/sub/ — these are physically separate directory trees with separate paths.
Within those trees the files start out as hardlinks (same inode), but that only affects disk space accounting. It does not mean the data is shared. Git writes files atomically by writing to a lock file and then calling rename(2), which gives the path a new inode. So the first time git touches HEAD, index, or any ref in one worktree's submodule gitdir, the rename breaks that hardlink and the file becomes independent. The other worktree's copy is untouched.
The only files that stay hardlinked forever are pack files and loose object files — precisely because git objects are immutable by design. Two pack files with the same content should share the same disk blocks; there is no correctness issue.
The net effect is the same separation git worktree already provides for the outer repo (HEAD and index under $GIT_COMMON_DIR/worktrees/$id/, objects shared under $GIT_COMMON_DIR/objects/), but achieved through the filesystem rather than explicit gitdir configuration. A native --recurse-submodules implementation should do the same thing explicitly — probably by running the equivalent of git -C <sub-gitdir> worktree add for each submodule, which would give each submodule its own proper worktrees/ structure rather than relying on the hardlink + rename behaviour.
Signed-off-by: Jimmy Aguilar Mena kratsbinovish@gmail.com