Get AI summaries of any video or article — Sign up free
GIT Submodules - Git Series 7 thumbnail

GIT Submodules - Git Series 7

Tools on Tech·
5 min read

Based on Tools on Tech's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

A submodule is a parent-repo directory pinned to a specific commit in another repository, so the parent records a reference rather than “live” code.

Briefing

Git submodules are a way to include one Git repository inside another by pinning the submodule directory to a specific commit. That design sounds straightforward—update the submodule by changing which commit the parent repo points to—but it’s also the source of most of the friction people associate with submodules. The core reason they’re hated is that they introduce a second layer of Git state (the parent repo’s branch/commit and the submodule’s pinned commit) that often falls out of sync during normal team workflows.

Submodules get confusing fast because they require different commands and habits than the usual Git flow. Switching branches in the parent repository does not automatically update the submodule contents. This is intentional: the submodule might contain local changes that still need committing, so Git avoids silently overwriting them. In practice, that means developers can pull or switch branches and suddenly see the submodule marked as “modified” even though they didn’t touch it—because the parent repo now expects a different pinned commit inside the submodule directory.

A second major pain point is structural misuse. Teams sometimes use submodules to split what should be one repository into multiple repositories. That turns routine changes into multi-repo coordination: edits must be committed in the submodule, then the parent repo must be updated to point at the new submodule commit, often involving multiple pull requests and careful ordering. The result is more review overhead and more opportunities for mistakes.

The transcript’s practical advice centers on preventing those failure modes. Submodules should link independent repositories—code that can be developed, tested, released, and versioned on its own. If two codebases must constantly move together, the structure likely needs rethinking rather than adding submodules. Good documentation matters too, especially a rule like “update a submodule in a single commit” so the parent repo’s history clearly records “submodule X moved from version 1.5 to 1.6.” Finally, dependency upgrades should be maintained actively because changing the pinned commit can break builds.

The terminal demo reinforces how the workflow behaves. After adding a submodule, the parent repo records only a single commit reference, while the submodule repository maintains its own commit history. Checking out a specific submodule version (e.g., version 1.0) and committing in the parent updates the pinned state. Later, switching the submodule to another version (e.g., version 2) makes the parent repo show the submodule directory as modified; committing in the parent records the new pinned commit.

The demo also shows the classic “modified subproject” confusion when switching parent branches: the submodule stays on the previously pinned version until running `git submodule update`, which checks out the commit the parent expects. Another failure scenario appears when someone makes a change inside the submodule but forgets to push it: teammates can’t fetch the pinned commit, causing submodule update failures. Tests can catch this early because CI will attempt to fetch the parent’s pinned submodule commit; if it’s missing, the pipeline fails and the missing push/version update becomes obvious.

Overall, submodules aren’t inherently broken—they’re just easy to misuse. Used for stable external dependencies with clear versioning and disciplined update practices, they can work cleanly. Used as a substitute for proper repository design or without team-wide workflow alignment, they create confusing diffs, broken builds, and rollback-worthy mistakes.

Cornell Notes

Git submodules let a parent repository include another repository by pinning the submodule directory to a specific commit. That pinning is powerful but creates two layers of state: the parent’s commit/branch and the submodule’s checked-out commit. Branch changes in the parent do not automatically update submodule contents, so developers can see the submodule marked “modified” even without touching it; running `git submodule update` aligns it with the parent’s expected commit. Submodules become especially painful when used to split code that should live together, because changes require committing in the submodule and then updating the parent to point to the new commit. The safest approach is to use submodules only for independent, versioned dependencies and to document a consistent update workflow.

What exactly does a Git submodule record in the parent repository?

A submodule is represented in the parent repo as a directory that points to a specific commit in another repository. In the demo, the parent repo’s history shows a single commit reference for the submodule, while the submodule directory itself contains its own commit history. When the parent updates from one submodule version (e.g., 1.5 to 1.6), the parent commit changes to point at the new submodule commit.

Why does switching branches in the parent repo often leave the submodule “out of date”?

The parent repo’s branch switch changes which submodule commit the parent expects, but submodules don’t auto-update by default. This avoids overwriting potential local changes inside the submodule. The result is that after checking out an earlier parent commit, `git status` can report the submodule as modified because the submodule directory is still on the newer pinned commit (e.g., version 2) while the parent expects an older one (e.g., version 1). Running `git submodule update` checks out the commit the parent expects.

What workflow mistake causes submodule update failures for teammates?

Making a change inside the submodule and committing it locally, then forgetting to push that commit to the remote. In the demo, Bob clones the parent repo and runs submodule initialization/update, but the pinned commit isn’t available in the submodule’s remote history. The update fails with an error indicating the fetched submodule commit isn’t contained. The fix is for the original developer to push the submodule commit/branch so the pinned commit can be fetched.

How should teams decide whether something should be a submodule?

Submodules should link independent repositories—code that can be developed, tested, released, and versioned on its own. If two parts of the system must be synchronized constantly, that’s a sign the structure should be reconsidered rather than relying on submodules. The transcript warns that using submodules to split a single logical codebase creates multi-repo coordination overhead and frequent ordering of commits and pull requests.

What update practice reduces confusion in the parent repo’s history?

Update the submodule in a single parent commit that clearly records the version change. For example: “switch submodule X from version 1.5 to 1.6.” This keeps the parent diff focused and makes it easier to understand what changed when builds break or when teammates need to reproduce the exact dependency state.

Review Questions

  1. When the parent repo is checked out to an older commit, what command aligns the submodule directory to the parent’s expected pinned commit?
  2. Why can `git status` show a submodule as modified even when no one edited files inside the submodule directory?
  3. What specific team mistake leads to submodule update errors during cloning or CI, and how does pushing the submodule fix it?

Key Points

  1. 1

    A submodule is a parent-repo directory pinned to a specific commit in another repository, so the parent records a reference rather than “live” code.

  2. 2

    Branch changes in the parent repo do not automatically update submodule contents, which can make `git status` show the submodule as modified unexpectedly.

  3. 3

    Submodules become especially painful when used to split code that should be one repository, because changes require coordinated commits across repos and careful PR ordering.

  4. 4

    Use submodules only for independent dependencies that can be developed, tested, released, and versioned on their own.

  5. 5

    Document a consistent update workflow, such as updating the submodule in a single parent commit that records the version bump.

  6. 6

    If someone changes the submodule but forgets to push, teammates and CI can’t fetch the pinned commit, causing submodule update failures until the missing commit is pushed.

  7. 7

    Running tests in CI helps catch submodule pinning and fetch problems early because the pipeline must retrieve the exact pinned submodule commit.

Highlights

Submodules don’t auto-update on parent branch switches, so the submodule can appear “modified” even when no one touched it—`git submodule update` is what realigns it.
Forgetting to push a submodule commit breaks others’ ability to fetch the pinned commit, producing submodule update errors until the commit exists on the remote.
The biggest practical risk isn’t submodules themselves—it’s using them to split code that should be one repository, turning simple edits into multi-repo coordination.
Treat submodules like versioned dependencies: pin stable commits, update them in clear single commits, and keep dependency upgrades actively maintained.

Topics

  • Git Submodules
  • Pinned Commits
  • Team Workflows
  • Dependency Versioning
  • CI Failures