GIT Submodules - Git Series 7
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.
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?
Why does switching branches in the parent repo often leave the submodule “out of date”?
What workflow mistake causes submodule update failures for teammates?
How should teams decide whether something should be a submodule?
What update practice reduces confusion in the parent repo’s history?
Review Questions
- When the parent repo is checked out to an older commit, what command aligns the submodule directory to the parent’s expected pinned commit?
- Why can `git status` show a submodule as modified even when no one edited files inside the submodule directory?
- What specific team mistake leads to submodule update errors during cloning or CI, and how does pushing the submodule fix it?
Key Points
- 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
Branch changes in the parent repo do not automatically update submodule contents, which can make `git status` show the submodule as modified unexpectedly.
- 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
Use submodules only for independent dependencies that can be developed, tested, released, and versioned on their own.
- 5
Document a consistent update workflow, such as updating the submodule in a single parent commit that records the version bump.
- 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
Running tests in CI helps catch submodule pinning and fetch problems early because the pipeline must retrieve the exact pinned submodule commit.