Subject: Re: [RFC] worktree: add --recurse-submodules support to git worktree add

From: Jimmy Aguilar Mena <[email-masked]>
Date: Wed, 15 Apr 2026 17:12:48 +0200

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/modules

So 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 [email-masked]