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
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.
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?
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:
Uses git worktree for submodules manually (like you described),
Sets up .git links correctly,
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.