What Is a Git Submodule?

What Is a Git Submodule?

A deep, intermediate-level walkthrough of how one repository can deliberately embed another, exact repository inside it, without merging their histories together.

Imagine a furniture maker building a custom desk that needs a specific, precisely engineered drawer slide mechanism — a part made by an entirely different manufacturer, with its own design, its own version numbers, and its own separate improvements happening over time. Rather than copying that mechanism’s blueprint directly into their own desk designs, where it would quickly go out of date, the furniture maker keeps a clear, explicit reference to exactly which version of that mechanism their desk uses, sourced directly from the original manufacturer whenever it’s needed. A Git submodule works exactly this way for software. It lets one repository include another, entirely separate repository inside it, at an exact, specific point in that other project’s history — fully intact, fully independent, and clearly distinguished from the surrounding project rather than blended into it. This post goes deep into what a submodule actually is, how it’s structured internally, and where it genuinely helps versus where it tends to cause pain.

1What Problem Does a Submodule Solve?

To understand submodules, it helps to see the specific situation that makes them necessary in the first place.

The problem: depending on another, separately maintained project

Projects frequently need to use code from another, entirely separate repository — a shared library, a reusable component, or a piece of tooling maintained independently by a different team or a different open-source project. Simply copying that code’s files directly into your own project loses any connection to where it came from, making it hard to track exactly which version you’re using, or to pull in updates when the original project improves.

The solution: an embedded, versioned reference to another repository

A submodule solves this by letting your repository store a precise pointer to another repository, along with the exact commit within that other repository’s history you want to use. Rather than copying the other project’s files in permanently, your project simply remembers “this is where to find it, and this is the exact version to use,” fetching the actual content from the other repository when needed.

Simple Analogy

Think of a research paper that cites a specific edition of another book, rather than reprinting that entire book’s pages directly inside the paper itself. The citation clearly states exactly which book, and which edition, to reference. Anyone reading the paper can go find that exact book separately, and if a newer edition comes out later, the paper’s citation stays precisely accurate to the one it originally referenced, unless the author deliberately updates it. A submodule is that precise citation, applied to code.

Term

Parent Repository

The main repository that includes another repository as a submodule.

Term

Submodule

An entirely separate repository, embedded within a parent repository at a specific, exact commit.

Term

Pinned Commit

The exact, specific commit within the submodule’s own history that the parent repository currently references.

Term

Dependency

Code from another project that your own project relies on to function correctly.

2Why Not Just Copy the Files In Directly?

It’s worth clearly spelling out why simply pasting another project’s code directly into your own repository falls short.

Directly copying files loses all connection to the original project’s own history and identity — from that point forward, your copy is just static files, indistinguishable from code you wrote yourself, with no clear record of where it originated or which specific version it represents. Pulling in future updates from the original project then requires manually finding, copying, and reconciling whatever’s changed, a tedious and error-prone process compared to simply referencing a newer commit directly.

i
Preserving Provenance

A key value of a submodule is preserving exactly where a piece of code came from, and exactly which version of it you’re using — information that’s completely lost the moment files are simply copied and pasted directly into a project.

3What Actually Happens When You Add a Submodule

Understanding the precise mechanics behind adding a submodule clarifies exactly what your parent repository ends up storing.

1

You specify the other repository’s location

You point to exactly where the separate repository lives, so Git knows where to fetch its content from.

2

Git clones that repository into a designated folder

The submodule’s full content is fetched and placed at a specific path within your parent repository’s folder structure.

3

Git records the exact commit currently checked out

Rather than tracking the submodule’s ongoing history, the parent repository simply records the one specific commit it’s currently pointing to.

4

A special configuration file records the submodule’s details

The submodule’s location and its designated path are recorded in a dedicated configuration file, tracked as part of the parent repository itself.

5

The parent repository commits this reference, not the submodule’s actual files

Critically, the parent repository’s own history stores only the pointer to the specific commit — not a full copy of the submodule’s individual files and history.

!
Common Misconception

Beginners sometimes assume a submodule’s files become a normal, tracked part of the parent repository, just like any other folder. In reality, the parent repository only tracks a reference — the submodule’s own history remains entirely separate and independently managed.

4The Architecture of Two Separate Repositories, Linked Together

A closer look at the underlying structure clarifies exactly how the parent and submodule repositories relate to each other.

flowchart TD
    subgraph Parent["Parent Repository"]
    P1["Parent's own history"]
    P2["Pointer: submodule at commit X"]
    end
    subgraph Sub["Submodule Repository"]
    S1["Submodule's own, independent history"]
    S2["Commit X"]
    end
    P2 -- "references" --> S2
        
FIG 1 — The parent repository stores a reference to a specific commit within the submodule’s entirely separate history

Each repository, the parent and the submodule, maintains its own completely independent history, its own commits, and its own branches. They aren’t merged together into one unified timeline — the only connection between them is that single, specific reference the parent repository keeps, pointing at one exact commit within the submodule’s own, separate history.

Component

Parent’s Own History

Commits made directly within the parent repository, entirely separate from the submodule’s own commits.

Component

Submodule’s Own History

The submodule’s complete, independent commit history, maintained entirely within its own separate repository.

Component

Reference Pointer

The single piece of information the parent stores, indicating exactly which submodule commit to use.

5Data Flow and Lifecycle of Working with Submodules

Tracing the everyday workflow around submodules shows exactly how they behave differently from regular, ordinary files.

Cloning a project that contains a submodule

By default, cloning the parent repository alone doesn’t automatically fetch the submodule’s actual content — an extra, deliberate step is typically required to fetch the submodule’s files at the referenced commit, since the two repositories are handled somewhat separately.

Updating the submodule’s referenced commit

When you want your project to use a newer version of the submodule, you explicitly update the submodule to point at a different commit within its own history, and then commit that updated reference within your parent repository — a deliberate, visible change to exactly which version you depend on.

Making changes inside the submodule itself

If you edit files inside the submodule’s folder, those changes belong to the submodule’s own, separate repository, not the parent’s. They need to be committed and, if appropriate, pushed within the submodule’s own repository, entirely separately from any changes to the parent project itself.

sequenceDiagram
    participant You
    participant Parent as Parent Repository
    participant Sub as Submodule Repository
    You->>Parent: Clone parent repository
    Parent-->>You: Submodule folder present, but empty
    You->>Sub: Fetch submodule content at referenced commit
    Sub-->>You: Submodule files now available
    You->>Sub: Make changes inside submodule
    You->>Sub: Commit changes within submodule's own history
    You->>Parent: Update reference to new submodule commit
    You->>Parent: Commit updated reference in parent repository
        
FIG 2 — The separate flow of fetching, editing, and updating a submodule’s reference within the parent repository

6Advantages and Trade-offs of Using Submodules

Submodules offer real, meaningful benefits, alongside genuine complexity that’s worth understanding honestly before adopting them.

Advantages

  • Preserves a clear, exact reference to a specific dependency version
  • Keeps the dependency’s own history and identity fully intact
  • Makes pulling in intentional updates a deliberate, visible action
  • Avoids duplicating a large dependency’s entire history into your own project

Trade-offs

  • Adds meaningful complexity to everyday cloning and updating workflows
  • Easy for teammates to forget the extra steps submodules require
  • Can result in confusing situations if the referenced commit becomes unavailable
  • Requires clear discipline around when and how submodule references are updated
“A submodule doesn’t hide complexity — it makes an existing dependency’s complexity visible and precise, instead of invisible and vague.”

7Security Considerations Around Submodules

Because a submodule fetches content from another, separate location, it introduces its own distinct set of considerations worth understanding.

Since a submodule’s actual content is fetched from wherever its reference points, anyone with the ability to alter your parent repository’s submodule reference could redirect it to a different, potentially malicious repository entirely, or to a different, unexpected commit within the legitimate one. Treating changes to a submodule’s reference with the same scrutiny as any other meaningful code change is an important part of using submodules responsibly.

!
Review Submodule Changes Carefully

A change that simply updates a submodule’s referenced commit can look small and unremarkable in a review, while actually pulling in an entirely different, unreviewed set of changes from the submodule’s own history. Treat submodule reference updates as changes genuinely worth reviewing carefully, not as trivial, automatic bumps.

Pinning to Exact, Trusted Commits

Because a submodule reference points at one exact, specific commit rather than a constantly moving branch, it provides a genuinely stable, auditable foundation — provided that reference is only ever updated deliberately, through reviewed changes.

8Design Patterns: When Submodules Genuinely Help

Certain situations are a genuinely strong fit for the submodule approach, where its trade-offs are clearly worth the benefit.

Sharing a Common Library Across Multiple Projects

A reusable library maintained as its own separate repository, and used by several different, unrelated projects, is a classic, well-suited case for a submodule, since each consuming project can reference the exact version it needs.

Including Third-Party Code at an Exact, Verified Version

When a project needs to rely on external code from a separate, independently maintained repository, submodules provide a precise, auditable way to reference exactly the version that’s been reviewed and trusted.

Splitting a Large Project into Cleanly Separated Components

Large projects sometimes deliberately split distinct components into their own separate repositories, using submodules to compose them together within a broader parent project, while preserving each component’s independent development history.

9Anti-Patterns: Common Submodule Mistakes

A recurring set of mistakes accounts for most of the frustration teams experience when working with submodules.

ANTI-PATTERN-01 Avoid
Problem

Using a submodule for a dependency that changes constantly and needs to always reflect the latest available version, rather than a deliberately pinned one.

Why It’s Harmful

Submodules are specifically designed around a fixed, deliberate reference, making them a poor fit for dependencies meant to always track the newest available changes automatically.

Correct Approach

Reserve submodules for dependencies where pinning to a specific, deliberate version is actually desirable, and consider other dependency management approaches for anything meant to update constantly.

ANTI-PATTERN-02 Avoid
Problem

Teammates forgetting the extra step needed to fetch submodule content after cloning, leading to confusing, seemingly missing files.

Why It’s Harmful

This creates unnecessary confusion and wasted time, especially for anyone newer to the project who isn’t yet familiar with the extra step submodules require.

Correct Approach

Clearly document the extra steps required when working with the project’s submodules, and consider tooling or scripts that handle this automatically wherever possible.

ANTI-PATTERN-03 Avoid
Problem

Treating a submodule reference update as a trivial, automatic change that doesn’t warrant genuine review.

Why It’s Harmful

An update to a submodule reference can silently pull in a large, meaningful set of changes from the submodule’s own history, changes that deserve the same scrutiny as any other meaningful code change.

Correct Approach

Review what’s actually changed within the submodule itself whenever its reference is updated, rather than treating the update as an automatic, unremarkable bump.

10Best Practices and Common Mistakes to Avoid

Beyond the specific anti-patterns already covered, a broader set of habits keeps submodules genuinely manageable rather than a constant source of friction.

1

Document how to work with the project’s submodules clearly

Make the extra steps required for cloning and updating explicit and easy to find, rather than assuming everyone already knows them.

2

Update submodule references deliberately, not casually

Treat each update as a meaningful decision, reviewed with the same care as any other significant dependency change.

3

Keep submodules reserved for genuinely suitable cases

Avoid reaching for a submodule purely out of habit when a simpler dependency management approach might actually fit better.

4

Periodically verify submodule references still resolve correctly

Confirm the referenced repository and commit remain available and accessible, since a broken or removed reference can quietly disrupt the whole project.

11Submodules Compared to Alternative Approaches

Submodules aren’t the only way to include external code, and understanding the alternatives helps clarify when they’re genuinely the right tool.

ApproachWhat It DoesBest Suited For
SubmoduleReferences an exact commit in a separate repositoryPrecise, deliberate pinning to another project’s exact version
Package manager dependencyDownloads a published, versioned packageWidely distributed, formally published libraries
Direct file copyPastes code directly into the projectSmall, rarely changing snippets, with no need to track updates

Each of these approaches solves a similar underlying problem differently, and the right choice depends on how formally the dependency is published, how often it changes, and how precisely you need to track exactly which version you’re using.

12Submodules in the Real World

Submodules show up in a range of genuinely practical, real-world situations across the software industry.

Projects that deliberately compose several independently developed components together — such as a larger system built from multiple, separately maintained modules — frequently rely on submodules to keep each component’s own development history clean and separate, while still combining them into one deployable whole. Organizations managing shared internal tooling or libraries across several distinct products sometimes use submodules to let each product reference an exact, verified version of that shared code, without duplicating it directly into every consuming project.

Precise
Submodules pin to one exact, specific commit, never a moving target
Independent
The submodule’s own history and identity remain fully intact and separate
Deliberate by design
Updating the referenced version is always an explicit, visible action

Composing Multi-Component Systems

Large systems built from several independently developed pieces sometimes use submodules to combine them cleanly, while preserving each component’s own separate history and development process.

Sharing Internal Tooling Across Products

Organizations with shared internal libraries used across multiple products sometimes rely on submodules to let each product reference an exact, deliberately chosen version of that shared code.

13Frequently Asked Questions

Q1Does cloning a repository automatically include its submodules’ actual content?

Not by default. An extra, deliberate step is typically needed to fetch a submodule’s actual files at the referenced commit, since the two repositories are handled somewhat separately by design.

Q2If I edit a file inside a submodule, where does that change get saved?

It belongs entirely to the submodule’s own, separate repository. It needs to be committed there directly, distinct from any changes you might make to the parent repository itself.

Q3Does the parent repository store a full copy of the submodule’s entire history?

No. The parent repository only stores a reference to one exact, specific commit within the submodule’s history, not a full copy of every commit that submodule has ever had.

Q4How do I update a project to use a newer version of a submodule?

You update the submodule to point at a different, newer commit within its own history, and then commit that updated reference within the parent repository, making the version change explicit and deliberate.

Q5Are submodules the only way to manage dependencies in a project?

No. Many projects rely on dedicated package managers for formally published, versioned dependencies, reserving submodules specifically for cases needing a precise, exact reference to another repository’s history.

Q6What happens if the repository a submodule points to becomes unavailable?

Fetching that submodule’s content would fail, since the reference depends on the original repository still being reachable, which is why understanding and monitoring a submodule’s source is an important part of relying on it responsibly.

14Summary and Key Takeaways

A submodule lets one repository deliberately reference an exact, specific commit within an entirely separate repository, preserving that other project’s own history and identity rather than blending it invisibly into your own. This precision comes with genuine trade-offs — extra steps for cloning and updating, and a real need for discipline around when and how references get changed — making submodules a great fit for deliberately pinned, precisely tracked dependencies, and a poor fit for anything meant to update constantly and automatically. Understood clearly, submodules offer a genuinely powerful way to compose independently developed projects together, without sacrificing the clarity of exactly what version of what you’re actually using.

Key Takeaways

  • A submodule references an exact commit in another repository — not a full copy, and not a constantly moving target.
  • The two repositories keep entirely separate histories — linked only by that one specific reference.
  • Cloning requires an extra step — submodule content isn’t automatically fetched by default.
  • Updates are deliberate, not automatic — the parent repository only changes its reference when someone explicitly updates it.
  • Reference updates deserve genuine review — a small-looking change can pull in a large set of underlying changes.
  • Submodules suit precise, pinned dependencies best — they’re a poor fit for anything meant to track the latest version automatically.
  • Alternatives exist for different needs — package managers and direct copies each fit different situations better than a submodule would.