DEV Community

Cover image for Git Submodules Explained for Beginners
coder7475
coder7475

Posted on

Git Submodules Explained for Beginners

If you've ever wanted to reuse another Git repository inside your project without copying its code, Git Submodules are exactly what you're looking for.

At first, submodules can seem confusing. But once you understand how they work, they're actually straightforward.

In this article, we'll cover:

  • What Git Submodules are
  • Why you should use them
  • How they work
  • Common commands you'll use
  • Common mistakes beginners make
  • How to remove a submodule

What is a Git Submodule?

A Git Submodule lets you include one Git repository inside another Git repository.

Instead of copying files from another project, Git keeps a reference to that repository and remembers which commit should be used.

my-project/
│
├── src/
├── docs/
├── shared-library/   ← Git Submodule
├── package.json
└── .gitmodules
Enter fullscreen mode Exit fullscreen mode

Here:

  • my-project is the main repository (called the superproject).
  • shared-library is another completely separate Git repository.

Each repository keeps its own commits, branches, and history.


Why Use Git Submodules?

Submodules are useful when you want to:

  • Share common libraries between multiple projects
  • Include third-party code without copying it
  • Split a large project into smaller repositories
  • Keep independent version history for reusable components

For example:

Suppose you have three applications that all use the same authentication library.

Instead of copying the authentication code into every project, you can keep it in its own repository and include it as a submodule.

Auth Library Repository
        │
 ┌──────┴─────────┐
 │                │
Project A     Project B
Enter fullscreen mode Exit fullscreen mode

Now every project references the same library.


How Git Stores a Submodule

A common misconception is that Git copies the entire repository into your project.

It doesn't.

Instead, the superproject only stores:

  • the repository URL
  • the folder path
  • the exact commit hash

That means your main repository simply says:

"Use commit abc123 from this repository."

This allows the submodule to evolve independently.


The .gitmodules File

When you add a submodule, Git creates a file called:

.gitmodules
Enter fullscreen mode Exit fullscreen mode

Example:

[submodule "shared-library"]
    path = shared-library
    url = https://github.com/example/shared-library.git
Enter fullscreen mode Exit fullscreen mode

This file tells Git:

  • where the submodule lives
  • where to clone it from

Because .gitmodules is version controlled, everyone on your team gets the same configuration.


Adding a Submodule

Use:

git submodule add <repository-url> <path>
Enter fullscreen mode Exit fullscreen mode

Example:

git submodule add https://github.com/example/shared-library.git shared-library
Enter fullscreen mode Exit fullscreen mode

Git will:

  • clone the repository
  • create .gitmodules
  • stage both the new folder and .gitmodules

Then commit the changes:

git commit -m "Add shared library as submodule"
Enter fullscreen mode Exit fullscreen mode

Cloning a Repository with Submodules

The easiest way is:

git clone --recurse-submodules <repository-url>
Enter fullscreen mode Exit fullscreen mode

This clones:

  • the main repository
  • every submodule
  • nested submodules as well

Forgot --recurse-submodules?

No problem.

Run:

git submodule update --init --recursive
Enter fullscreen mode Exit fullscreen mode

This initializes and downloads all submodules after cloning.


Updating a Submodule

To fetch the latest commits from the remote repository:

git submodule update --remote
Enter fullscreen mode Exit fullscreen mode

Git updates the submodule to the latest commit of its tracked branch.

After that, don't forget to commit the updated reference:

git add shared-library
git commit -m "Update submodule"
Enter fullscreen mode Exit fullscreen mode

Otherwise your teammates won't receive the updated version.


Pushing Changes

If you've made changes inside the submodule, push them first.

A convenient command is:

git push --recurse-submodules=on-demand
Enter fullscreen mode Exit fullscreen mode

This ensures Git pushes:

  1. the submodule commits
  2. then the main repository

This prevents broken references for other developers.


Detached HEAD Explained

One thing that surprises many beginners is seeing this message:

HEAD detached at abc123
Enter fullscreen mode Exit fullscreen mode

This is normal.

The superproject points to a specific commit instead of a branch.

A detached HEAD simply means:

"You're viewing a particular commit."

If you want to make changes inside the submodule, switch to a branch first:

cd shared-library

git checkout main
Enter fullscreen mode Exit fullscreen mode

Then make your changes, commit, push, and finally update the superproject.


Common Beginner Mistakes

1. Forgetting to clone submodules

Incorrect:

git clone <repository>
Enter fullscreen mode Exit fullscreen mode

Correct:

git clone --recurse-submodules <repository>
Enter fullscreen mode Exit fullscreen mode

Or run:

git submodule update --init --recursive
Enter fullscreen mode Exit fullscreen mode

2. Updating the submodule but forgetting to commit

After updating:

git submodule update --remote
Enter fullscreen mode Exit fullscreen mode

You still need:

git add shared-library
git commit -m "Update submodule reference"
Enter fullscreen mode Exit fullscreen mode

3. Editing inside a detached HEAD

Always switch to a branch before making changes:

git checkout main
Enter fullscreen mode Exit fullscreen mode

Removing a Submodule

Removing a submodule involves more than deleting the folder because Git tracks additional metadata.

The process differs slightly depending on your Git version.

A detailed step-by-step guide is available here:

https://stackoverflow.com/questions/1260748/how-do-i-remove-a-submodule


When Should You Use Git Submodules?

Use submodules when:

  • multiple projects share the same codebase
  • you want independent version histories
  • you need to pin an exact version of another repository
  • you want to include third-party repositories without copying code

Avoid submodules if your team is unfamiliar with Git or if your project requires frequent synchronized changes across repositories. In those cases, alternatives like package managers or monorepos may be simpler.


Final Thoughts

Git Submodules are often considered difficult—not because they're complicated, but because many developers don't understand that a submodule is simply another Git repository tracked by a specific commit.

Once you remember these three rules, everything becomes easier:

  • A submodule has its own Git history.
  • The main repository stores only a commit reference.
  • Updating a submodule requires committing the new reference in the superproject.

Master these concepts, and working with shared repositories becomes much cleaner and more maintainable.


Useful Commands Cheat Sheet

# Add a submodule
git submodule add <repo-url> <path>

# Clone with submodules
git clone --recurse-submodules <repo-url>

# Initialize submodules after cloning
git submodule update --init --recursive

# Update submodule to latest remote commit
git submodule update --remote

# Push submodule and superproject together
git push --recurse-submodules=on-demand

# Switch submodule to a branch
cd submodule
git checkout main
Enter fullscreen mode Exit fullscreen mode

Thaks for reading!

Top comments (0)