Clone a Git repository with its submodules

Clone nested Git submodules, check out recorded commits, deliberately update dependencies, and configure private-repository access in CI.

Best practices
Illustration of Git history and collaboration

Clone with --recurse-submodules to initialize and check out submodules, including nested ones. If the main repository is already cloned, run git submodule update --init --recursive from that repository.

# A new checkout
git clone --recurse-submodules https://github.com/ORG/REPO.git

# An existing checkout
git submodule update --init --recursive

Replace the URL with your repository. --recursive is also accepted as a clone option. Each submodule needs its own reachable repository and any required credentials. See git-clone.

Understand what the parent repository records

The parent repository stores a submodule commit ID in a special tree entry called a gitlink. The tracked .gitmodules file supplies information such as its path and URL. The commit ID and repository URL are not both stored in the gitlink.

A normal clone does not populate submodule content. --init registers submodule configuration, while the update operation fetches and checks out the recorded commits. --recursive repeats the work for nested submodules. The default checkout procedure typically leaves a submodule in detached HEAD mode.

Keep submodules at the project's recorded versions

For an initialized checkout, one routine is:

git pull --ff-only --recurse-submodules
git submodule update --init --recursive
git submodule status --recursive

The update step also initializes newly introduced submodules. --ff-only makes the pull fail if integrating the parent branch would require a merge; resolve that divergence deliberately. Preserve local edits in both the parent and submodules before changing versions.

If the project changes a submodule URL, synchronize the local configuration before updating:

git submodule sync --recursive
git submodule update --init --recursive

Repository-local git config submodule.recurse true enables recursion for commands that honor that setting. It does not replace --recurse-submodules on the initial clone. See git-config.

Deliberately advance a dependency

This command follows the submodule's configured remote-tracking branch rather than the commit recorded by the parent:

git submodule update --remote -- path/to/submodule
git diff --submodule

Review and test the change, then stage and commit the updated submodule path in the parent repository. --remote is for updating a dependency; it is not the normal command for reproducing an existing parent commit.

Common questions

What does CI need? Enable recursive submodule checkout using the CI platform's documented option. For GitHub's checkout action, that option is submodules: recursive. Confirm credentials can read private submodule repositories, including those outside the parent repository's access scope. See actions/checkout.

Can I commit inside a submodule? Yes; it is a separate repository. Create or select a branch there, commit your changes, and push them to a repository collaborators can read. Then commit the new gitlink in the parent. Publishing the parent reference before the submodule commit is accessible leaves other checkouts unable to fetch it.

How do I remove a submodule from the project? First preserve any work inside it. Use git rm path/to/submodule, review the staged path and .gitmodules change, and commit the removal. git submodule deinit -- path/to/submodule instead unregisters it in your local checkout. Avoid deinit -f unless discarding local modifications is intended. The retained repository under .git/modules can help with older checkouts and does not need to be deleted as part of normal removal.

References

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting