Fix empty submodule folders after cloning
I cloned a repo and the submodule folders are empty — how do I fix it?
Short answer
Cloning records submodules but doesn't populate them. Run git submodule update --init --recursive, or clone with --recurse-submodules next time so it happens automatically.
git submodule update --init --recursive
Fetches and checks out every submodule, including nested ones
Does this match your situation?
- A directory listed in .gitmodules exists but is empty.
- The build fails with missing files from a dependency folder.
- git status reports submodules as 'not initialized'.
- A submodule shows as modified with '(new commits)' you didn't make.
Step-by-step fix
Initialise and fetch every submodule
This registers each submodule from .gitmodules and checks out the exact commit your repository pins. --recursive handles submodules that themselves contain submodules.
step 1git submodule update --init --recursive git submodule statusClone correctly next time
One flag does the whole thing at clone time and avoids the problem entirely.
step 2git clone --recurse-submodules https://github.com/user/repo.git # make it the default forever: git config --global submodule.recurse trueFix a submodule showing unexpected '(new commits)'
This means the submodule is checked out at a different commit than the parent records — usually because you built inside it, or updated it without committing the new pointer. Reset it back to the pinned commit.
step 3git submodule update --init --recursive --force # or intentionally record the new commit: git add path/to/submodule git commit -m "Update submodule to latest"Update a submodule to its latest upstream commit
Submodules are pinned deliberately, so moving one forward is an explicit action that produces a commit in the parent repository.
step 4git submodule update --remote path/to/submodule git add path/to/submodule git commit -m "Bump submodule to latest upstream"Remove a submodule properly
Removal touches four places. Missing any one leaves the repository in a confusing half-state.
step 5git submodule deinit -f path/to/submodule git rm -f path/to/submodule rm -rf .git/modules/path/to/submodule git commit -m "Remove submodule"
Why this works
A submodule isn't a copy of another project — it's a single line in the parent's tree recording a repository URL and one commit hash. Cloning fetches that pointer, but the pointed-to repository is a separate download that Git won't perform unless asked, which is why the folder is created and left empty. `--init` reads .gitmodules into your local config and `update` clones and checks out the pinned commit, which is also why a submodule always lands on a specific commit rather than a branch.
How to stop it happening again
- Set submodule.recurse true globally so clone, pull and checkout handle submodules automatically.
- Document the init command in your README for anyone who hasn't.
- Consider whether a package manager or git subtree would serve you better - submodules add real friction.
Commands used in this guide
git submoduleEmbed another Git repository inside yours at a pinned commit.
git cloneCopy a remote repository, its full history and its branches, onto your machine.
git subtreeVendor another repository into a subdirectory - without submodules' friction.
git configRead and write Git settings for one repo, your user, or the whole machine.
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
Why are my submodule folders empty after cloning?
A clone records the submodule pointer but doesn't download the referenced repository. Run git submodule update --init --recursive to fetch and check out each one at the commit your repository pins.
How do I clone a repository with all its submodules at once?
git clone --recurse-submodules <url>. To make it automatic for every future command, set git config --global submodule.recurse true, which also applies to pull and checkout.
Why does my submodule show as modified when I didn't change it?
The submodule is checked out at a different commit than the parent repository records - often after building inside it or pulling within it. Either reset it with git submodule update --init --force, or commit the new pointer deliberately if the update was intended.
Related rescue guides
Cloning takes forever — how do I clone a big repo faster?
Use --filter=blob:none for a partial clone that fetches file contents lazily, and add sparse-checkout to materialise only the directories you need. --depth 1 is fastest of all but gives you no usable history.
Read the fix →Always fixableHow do I combine two separate repos while keeping both histories?
Add the second repository as a remote, fetch it, and merge with --allow-unrelated-histories into a subdirectory. Both sets of commits end up in one repository with their history intact.
Read the fix →Always fixableHow do I extract a folder from my repo into a separate repo with its history?
git filter-repo --path <dir> --path-rename <dir>/: keeps only that directory and moves it to the root, preserving every commit that touched it. Work on a fresh clone so your original repository is never at risk.
Read the fix →Always fixableWhy is .gitignore not working for files that are already tracked?
.gitignore only applies to untracked files. Once a file has been committed, Git keeps tracking it no matter what the ignore rules say. Remove it from the index with git rm --cached <file> — that stops tracking while leaving the file on your disk.
Read the fix →