What this error means
You ran a Git command on a path nested under a directory that is a submodule. The superproject tracks the submodule as a single gitlink (a commit pointer), not as individual files, so commands like git add, git checkout, git restore, or git rm against files inside that folder fail with:
- error: pathspec 'path/inside/submodule' is in submodule 'path/to/submodule'
Quickstart: choose the right fix
| Intent | Where to run | Command pattern |
|---|---|---|
| Modify files inside the submodule | Inside the submodule repo | cd path/to/submodule; git add/commit; cd ..; git add path/to/submodule; git commit |
| Remove the submodule and track files directly | In superproject | Use deinit + rm, or the keep-files variant shown below |
| Run the same command in many submodules | Superproject | git submodule foreach --recursive '<cmd>' |
| Update/init nested submodules | Superproject | git submodule update --init --recursive |
Minimal working example (reproduce and fix)
# Superproject
mkdir demo && cd demo
git init
printf 'root\n' > main.txt
git add main.txt && git commit -m 'init superproject'
# Create the submodule repo next to it
mkdir ../lib && pushd ../lib >/dev/null
git init
printf 'v1\n' > README.md
git add README.md && git commit -m 'init lib'
popd >/dev/null
# Add submodule
git submodule add ../lib vendor/lib
git commit -m 'add submodule lib'
# Change a file inside the submodule
printf 'change\n' >> vendor/lib/README.md
# This fails in the superproject:
# error: pathspec 'vendor/lib/README.md' is in submodule 'vendor/lib'
if git add vendor/lib/README.md 2>/dev/null; then :; fi
# Correct approach: commit inside the submodule, then update the pointer
git -C vendor/lib add README.md
git -C vendor/lib commit -m 'update lib readme'
git add vendor/lib
git commit -m 'bump lib pointer'
Why it happens (brief)
- In the superproject, a submodule directory is a special entry (mode 160000) pointing to a specific commit in the submodule repository. The superproject does not track the contents of files inside that directory.
- Therefore, pathspecs that refer to nested files are not valid from the superproject. You must operate inside the submodule’s repository.
Step-by-step fixes
1) You intended to edit files inside the submodule
- Enter the submodule repository and make your change.
- Commit in the submodule.
- Stage the submodule path in the superproject (updates the gitlink to the new commit).
- Commit the superproject.
cd path/to/submodule
# make changes here
git add <files>
git commit -m 'your change'
cd -
git add path/to/submodule
git commit -m 'update submodule pointer'
# Push both repos (order matters: push submodule first)
git -C path/to/submodule push
git push
Tips:
- If the submodule has its own remote/branch, ensure you push it; otherwise collaborators will see a pointer to a commit they cannot fetch.
- For automation, use:
git -C path/to/submodule <cmd>instead of cd.
2) You did not want a submodule; convert it to tracked files
Two common approaches.
A) Remove submodule completely (clean slate):
git submodule deinit -f path/to/submodule
rm -rf .git/modules/path/to/submodule
# Removes the gitlink and the working tree
git rm -f path/to/submodule
# Edit .gitmodules if Git didn't do it automatically
# Commit the removal
git commit -m 'remove submodule'
B) Keep the files, stop treating it as a submodule (convert in place):
# Unstage from index but keep working files
git rm --cached -r path/to/submodule
# Remove the submodule's repo metadata so it becomes normal files
rm -rf path/to/submodule/.git
# Clean up Git metadata
git config -f .gitmodules --remove-section submodule.path/to/submodule || true
git config --remove-section submodule.path/to/submodule || true
rm -rf .git/modules/path/to/submodule
git add .gitmodules path/to/submodule
git commit -m 'convert submodule to regular directory'
After conversion, you can git add nested files normally.
3) You need to run a command in many submodules
Use foreach (and recurse if submodules have their own submodules):
git submodule foreach --recursive 'git status -s'
# Example: commit the same file if present in each submodule
# git submodule foreach --recursive 'if [ -f README.md ]; then git add README.md && git commit -m "touch" || true; fi'
4) Reset/checkout/restore files inside a submodule
Run the command inside the submodule:
# Reset a file to HEAD of the submodule
git -C path/to/submodule restore --staged --worktree -- path/inside
# Or checkout a branch inside the submodule
git -C path/to/submodule checkout main
5) Initialize or update nested submodules
If the error appears because the working tree is empty/uninitialized:
git submodule update --init --recursive
Diagnostics
- Is this a submodule? Check the mode 160000 gitlink:
git ls-files -s | grep ' path/to/submodule$'
- List submodules and their commits:
git submodule status --recursive
- See where the submodule stores its Git dir:
cat path/to/submodule/.git # often points into .git/modules/...
Pitfalls
- Forgetting to push the submodule: teammates will see a detached pointer they cannot fetch. Always push the submodule repo first, then the superproject.
- Committing only in the superproject:
git add path/to/submoduleupdates the pointer but does not create commits in the submodule. You must commit inside the submodule for content changes. - Nested submodules: use
--recursivefor init/update and foreach operations. - Converting submodule to files: ensure
path/to/submodule/.gitis removed, or you will have a nested Git repo that Git ignores.
Performance notes
- Cloning large submodules: initialize shallowly when history is not needed.
git submodule update --init --depth 1 --recommend-shallow
- Parallel updates for many submodules:
git submodule update --init --recursive --jobs 8
Avoid unnecessary recursion: only use
--recursivewhen needed to reduce network and disk I/O.Use
git -C pathinstead of repeated cd for scripts; it is faster and simpler.
Tiny FAQ
Q: Why can’t I add a file inside a submodule from the parent? A: The parent tracks a single gitlink entry, not individual files. Operate inside the submodule and then update the pointer in the parent.
Q: How do I make the submodule content part of the main repo? A: Convert it:
git rm --cached -r submodule && rm -rf submodule/.git, clean .gitmodules, then commit and add files normally.Q: How do I reset a file inside a submodule to its last committed state? A: Run
git restore(orcheckout) inside the submodule or withgit -C submodule.Q: Can I avoid submodules altogether? A: Consider
git subtreeto vendor dependencies while keeping them in your history, at the cost of heavier commits in the main repo.