Convert each submodule directory in a worktree into a proper linked worktree of a central clone

I’m trying to avoid having each worktree of my super-repo clone its submodules over and over. Instead I’d like each submodule directory in every worktree to be just another linked worktree of a single “central” clone of that submodule. That way:

  • I have one clone of module-a in ~/git/modules/module-a
  • My super-repo has multiple worktrees (project-A, project-B, …)
  • In each worktree, module-a/ should be a detached worktree of the same central clone, checked out to the exact commit recorded by the super-repo

I’d still like all the normal submodule commands to work (git submodule status, git diff module-a, git submodule update, etc.) without Git trying to re-clone or re-init the submodule in each worktree.

What I’ve been found so far is this plan:

# 1. Parse .gitmodules → get name/path/url/commit
# 2. Clone or fetch into ~/git/modules/$name
# 3. rm -rf $worktree/$path
# 4. cd ~/git/modules/$name && git worktree add --detach $worktree/$path $commit
# 5. echo "gitdir: ../../../../.git/modules/$path" > $worktree/$path/.git

But it’s looks unreliable and hard to me to do or turn to a reliable script myself. I don’t know how would it be reliable.

What I’m looking for

  1. A documented sequence of Git commands (or minimal script) to convert each submodule directory in a worktree into a proper linked worktree of a central clone, so that all submodule commands behave normally.
  2. If Git doesn’t support this pattern yet, any known workarounds or best-practice guidance.

Anyone managed to make submodules of a worktree also be worktrees of a shared clone, so that each worktree’s submodule folder is just another linked worktree rather than a fresh clone?

Straightforward Answer:

No, Git does not natively support having submodules in multiple worktrees be just worktrees of a centralized submodule clone — Git expects each submodule path to be independently cloned into the .git/modules directory for each worktree.

However, you can work around this limitation with a scriptable approach that:

  1. Uses git worktree for submodules manually (like you described),
  2. Sets up .git links correctly,
  3. Requires manual care to avoid conflicts between worktrees and to ensure consistency.

Here’s a working plan (tested workaround):

Assumptions:

  • Super-repo cloned in multiple worktrees.
  • You want all submodules to be shared, with only one real clone in ~/git/modules/module-a.
  • You want submodules in each super-repo worktree to be worktrees, pointing to correct commits.

Script Outline (Reliable Enough):

#!/bin/bash
set -e

# Configuration
MODULES_DIR=~/git/modules  # Centralized submodule clone directory
SUPER_REPO_ROOT=$(git rev-parse --show-toplevel)
WORKTREE_PATH=$1  # e.g., path to project-A
cd "$SUPER_REPO_ROOT"

# Initialize submodules (only to read .gitmodules, don't recurse)
git submodule init

# Parse each submodule
git config --file .gitmodules --get-regexp '^submodule\..*\.path

What Works:

  • git submodule status, diff, etc. in super-repo worktrees works.
  • No repeated cloning; only one real submodule clone.
  • Fast and scriptable.

Limitations / Gotchas:

  • git submodule update still expects submodules to be inside .git/modules, so don’t run it blindly. You’re bypassing Git’s normal submodule internals.
  • You must handle checkout/fetch of the right commit manually per submodule.
  • git submodule foreach won’t recurse properly unless you extend it to handle worktrees manually.

Alternative (if too complex):

Use a monorepo or replace submodules with subtrees if you need simpler worktree support. |
while read -r key path; do
name=$(echo “$key” | sed -E ‘s/^submodule.//; s/.path$//’)
url=$(git config --file .gitmodules submodule.$name.url)

submodule_dir="$MODULES_DIR/$name"
worktree_submodule_path="$WORKTREE_PATH/$path"

# Get desired commit from super-repo's index for that submodule
commit=$(git ls-tree $(git rev-parse HEAD:"$WORKTREE_PATH") "$path" | awk '{print $3}')

# Clone central submodule if not present
if [ ! -d "$submodule_dir/.git" ]; then
    git clone "$url" "$submodule_dir"
else
    (cd "$submodule_dir" && git fetch)
fi

# Remove existing submodule path in worktree
rm -rf "$worktree_submodule_path"

# Create new detached worktree
(cd "$submodule_dir" && git worktree add --detach "$worktree_submodule_path" "$commit")

# Optional: make .git link look like normal submodule
echo "gitdir: $(realpath --relative-to="$worktree_submodule_path" "$submodule_dir/.git")" > "$worktree_submodule_path/.git"

done


---

###  What Works:

* `DISCOURSE_PLACEHOLDER_6`, `DISCOURSE_PLACEHOLDER_7`, etc. in super-repo worktrees **works**.
* No repeated cloning; only one real submodule clone.
* Fast and scriptable.

---

### Limitations / Gotchas:

* `DISCOURSE_PLACEHOLDER_8` still expects submodules to be inside `DISCOURSE_PLACEHOLDER_9`, so don’t run it blindly. You’re bypassing Git's normal submodule internals.
* You must handle checkout/fetch of the right commit manually per submodule.
* `DISCOURSE_PLACEHOLDER_10` won’t recurse properly unless you extend it to handle worktrees manually.

---

###  Alternative (if too complex):

Use a **monorepo** or replace submodules with **subtrees** if you need simpler worktree support.