Git Submodules

Submodules let one Git repository embed another repository at a fixed commit, useful for pulling in a shared library while keeping its history separate from your own project's.

What a Submodule Is

A submodule is a reference inside your repository that points to a specific commit in another repository. Instead of copying that other project's files into yours, Git stores a pointer: the submodule's URL and the exact commit hash it should be checked out at. This means your main repository's history doesn't grow with every change in the dependency, and the dependency keeps its own independent commit history.

Submodules are commonly used for shared component libraries, vendored dependencies without a package manager, or documentation repositories that need to travel alongside several projects.

Adding a Submodule

Use git submodule add with the URL of the repository you want to embed and, optionally, the local path where it should live. Git creates a .gitmodules file at the repository root recording the mapping between path and URL.

Add a submodule

$ git submodule add git@github.com:org/shared-ui.git libs/shared-ui
Cloning into 'libs/shared-ui'...
remote: Enumerating objects: 340, done.
Resolving deltas: 100% (120/120), done.
$ git status
new file:   .gitmodules
new file:   libs/shared-ui
Note: The submodule shows up as a single special entry (a "gitlink") in git status, not as a folder full of individual files — that's expected.

Cloning a Repository That Has Submodules

A plain git clone does not automatically download submodule contents; it only creates empty directories where they belong. You need an extra flag or command to actually populate them.

Clone with submodules included

$ git clone --recurse-submodules git@github.com:org/main-app.git
Cloning into 'main-app'...
Submodule 'libs/shared-ui' (git@github.com:org/shared-ui.git) registered
Submodule path 'libs/shared-ui': checked out '9f2a1c4'

If you already cloned without that flag, run the initialize-and-update pair instead of re-cloning from scratch.

Populate submodules after a plain clone

$ git submodule init
$ git submodule update
Submodule path 'libs/shared-ui': checked out '9f2a1c4'

Updating a Submodule to a Newer Commit

Moving a submodule forward is a two-step change: first update the submodule's own checkout, then commit that new pointer in the parent repository so everyone else picks it up on their next pull.

  • cd into the submodule directory
  • git pull origin main (or checkout a specific tag/commit)
  • cd back to the parent repository root
  • git add path/to/submodule to stage the new pointer
  • git commit -m "Update shared-ui submodule to latest"

Pulling Updates Across a Whole Project

When teammates update the submodule pointer, a normal git pull on the parent repository only updates the pointer reference, not the submodule's actual checked-out files. Run the update command with --recursive to sync everything in one pass.

Sync all submodules after pulling

$ git pull
$ git submodule update --init --recursive
Submodule path 'libs/shared-ui': checked out 'a83f9d1'

Submodule Commands Summary

CommandEffect
git submodule add <url> <path>Register and clone a new submodule
git submodule initPrepare local config for existing submodule entries
git submodule updateCheck out the commit each submodule pointer references
git submodule update --remoteMove submodule to the latest commit on its tracked branch
git submodule statusShow current commit of each submodule and whether it's changed
Note: Submodules are notorious for confusing new contributors because forgetting --recurse-submodules or the init/update pair silently leaves empty folders with no error. Document the requirement clearly in your project's README.

Exercise: Git Submodules

What is a Git submodule?