Skip to content
Latchkey

What Is a Git Submodule?

A submodule is a repository nested inside another repository, pinned to a specific commit, so you can include external code while tracking its exact version.

A submodule lets one Git repository contain another as a subdirectory, locked to a particular commit. It is a way to reuse a shared library or component while keeping its history separate and its version explicit. Submodules are powerful but have a reputation for surprising newcomers.

How submodules work

The parent repository stores a pointer to a specific commit of the submodule, not its files. The submodule lives in its own repository with its own history. When you check out the parent at a given commit, the recorded submodule commit tells you exactly which version of the nested code to use.

Adding and updating submodules

You add a submodule, then initialize and update it to pull the pinned commit's contents.

Adding and initializing a submodule
git submodule add https://github.com/owner/lib.git vendor/lib
git submodule update --init --recursive

Why they can be tricky

Submodules add steps to common workflows. A plain clone fetches the parent but leaves submodules empty until you initialize them. Updating the submodule pointer is a separate commit in the parent. Forgetting these steps leads to missing files or stale dependencies, which is a frequent source of confusion.

Submodules in CI/CD

A CI checkout must be told to fetch submodules, or the build will fail with missing code. Most checkout actions expose a submodules option to fetch them, recursively if needed. Because each submodule is its own clone, they add to checkout time and should be fetched only when the build truly depends on them.

Working with submodules

  • Initialize and update submodules after cloning the parent.
  • Commit the parent when you advance a submodule pointer.
  • Enable submodule checkout in CI or the build will miss files.
  • Consider alternatives like package managers for simpler reuse.

Key takeaways

  • A submodule nests one repository inside another, pinned to a commit.
  • A plain clone leaves submodules empty until you initialize them.
  • CI must opt in to fetching submodules or builds miss their code.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →