Update all Git submodules to upstream revisions

Distinguish recorded gitlink updates from tracking newer remote commits, and commit nested submodule changes in the correct order.

Best practices
Illustration of Git branches and version history

Use git submodule update --remote --recursive when you deliberately want submodules to follow newer remote branch tips. Use ordinary git submodule update --init --recursive when you want the exact commits recorded by the parent repository. Those are different goals.

Initialize and inspect every checkout

git clone --recurse-submodules REPOSITORY_URL
# Or initialize submodules in an existing clone:
git submodule update --init --recursive

git status --short
git submodule foreach --recursive 'git status --short'

Each submodule has its own index and working tree. Stashing in the outer repository does not automatically save modifications inside submodules. Commit or stash needed work in each affected repository before updating.

Follow newer upstream commits

git submodule update --remote --recursive
git diff --submodule=log
git add -- path/to/submodule
git diff --cached --submodule=log
git commit -m "Update reviewed submodule revisions"

Replace the path with each intended update. --remote fetches the selected remote and uses its tracking branch rather than the gitlink recorded in the parent index. The branch can be set with submodule.NAME.branch, commonly in .gitmodules; without an explicit branch, Git uses the remote's HEAD. Repository-local configuration can override shared settings.

The default checkout update strategy usually leaves a submodule detached at the selected commit. That is normal for pinned dependencies. Create a branch inside the submodule when you intend to develop and commit there. Merge or rebase update strategies have different effects and should be selected deliberately.

Handle nested submodules from the inside out

Updating a nested child does not automatically create a new commit in its direct parent. Review and commit the child's gitlink in that parent submodule, publish the required submodule commits to a reachable remote, then commit the resulting parent gitlink in the next level. Continue outward to the top-level repository.

Committing only the top-level pointer cannot preserve uncommitted nested gitlink changes. Similarly, a parent commit that points to an unpublished submodule commit may work locally but fail for collaborators and CI.

Return to the recorded version

After saving local work, ordinary update restores the commits recorded by the parent index:

git submodule update --init --recursive -- path/to/submodule

If the gitlink has been staged or committed to a newer revision, first restore or create the intended parent gitlink change. Do not assume that command always means “the old HEAD version.” For a published dependency update, a new parent commit restoring the previous gitlink is often clearer than rewriting shared history.

Pull and CI behavior

A plain parent pull does not universally update all populated submodule working trees; recursion options and configuration affect it. A repository-local submodule.recurse setting can request recursive behavior, but following recorded gitlinks is still different from tracking the latest upstream tip.

In CI, initialize recursively and check out recorded commits for reproducibility. Private submodules need appropriate credentials for their own URLs. Inspect git submodule status --recursive when diagnosing mismatches, and avoid running an indiscriminate git pull on a hard-coded branch inside every submodule.

See git-submodule, .gitmodules settings, and recursive pull options.

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