Submodules

Git · Advanced · 12 min

Vendoring shared libraries or firmware SDKs often uses submodules, and they confuse people who don't know the model.

A submodule is a Git repository nested inside another at a fixed path. The parent repository doesn't store the submodule's files; it stores a special entry (a "gitlink", file mode 160000) recording one exact commit of the child, plus the URL in a .gitmodules file.

Adding one:

git submodule add https://github.com/acme/sdk.git vendor/sdk
git commit -m "Add sdk submodule"

Cloning a repository with submodules needs an extra step, or the folders stay empty:

  • git clone --recurse-submodules <url>, or after a plain clone:
  • git submodule update --init --recursive.

The key mental model: the parent pins a commit. Inside vendor/sdk, the submodule is usually in detached HEAD at that pinned commit. To upgrade:

cd vendor/sdk && git fetch && git switch --detach v1.5.0
cd ../.. && git add vendor/sdk && git commit -m "Bump sdk to v1.5.0"

git submodule update --remote does the fetch and checkout for you, following the branch configured in .gitmodules.

When teammates pull a commit that moved the pin, their submodule doesn't follow automatically; they run git submodule update, or set submodule.recurse true so pull, switch and friends do it.

Common traps: committing in the submodule but forgetting to push it (others then can't fetch the pinned commit), and forgetting to commit the new pin in the parent. git status and git diff --submodule show when a pin changed. If you don't need a pinned, separately versioned repository, a package manager dependency is usually simpler.

Example

git clone --recurse-submodules git@github.com:acme/firmware.git
cd firmware
git submodule status
#  4c1d2e3 vendor/sdk (v1.4.0)

git config --global submodule.recurse true   # pull/switch update submodules

git submodule update --remote vendor/sdk     # move to tip of tracked branch
git diff --submodule
# Submodule vendor/sdk 4c1d2e3..9a0b7f1: ...
git add vendor/sdk
git commit -m "Bump sdk"

Remember

  • The parent stores a pinned commit (gitlink) plus the URL in .gitmodules
  • Clone with --recurse-submodules or run submodule update --init --recursive
  • Bumping = change the checkout inside, then commit the new pin in the parent
  • Push the submodule commit before pushing the parent

Watch out for

  • Committing a new pin to a submodule commit that was never pushed — everyone else's update fails.

Check yourself

  1. What does the parent repository record about a submodule?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: git-scm.com/book/en/v2/Git-Tools-Submodules