Git Submodules

Image of Author
March 14, 2022 (last updated September 21, 2022)

Git submodules are repositories embedded inside other repositories. According to the docs (git help submodule), the parent repository is referred to as the "superproject" while the children repositories are called "submodules". A core use case for submodules is dependency management.

Add a submodule

Add a submodule by providing the remote git repo url and the path where you want the submodule stored in your own repo.

git submodule add [git-repo-url] [path]
# git submodule add https://github.com/username/repo ./path/to/submodule

This will create a top-level dotfile in the superproject, .gitmodules, that contains a list of all the submodules in your superproject. This file will be reference by git to perform later actions. It will look something like:

[submodule "[path]"]
    path = [path]
    url = [url]
[submodule ...]
...

There will also be an extremely similar changeset in the .git/config folder.

In case you want to move paths later, I recommend using the --name flag to give the submodule a name that is unrelated to the path. For example,

git submodule add --name my-name https://github.com/username/repo path/to/submodule

This would change the .gitmodules,

[submodule "my-name"]
    path = ...
    url = ...

Cloning submodules

When you first clone a superproject you will not clone the submodules. To clone the submodules run

git submodule init
git submodule update

Or combine them with

git submodule update --init

If submodules themselves contain submodules

git submodule update --init --recursive

All variations of the update command are idempotent so you can run them multiple times without problems.

Updating submodules

We've already seen how to do this above. Run one of the git submodule update commands above.

If you want to be more surgical in how you update your dependencies, you can jump into each submodule individually. This will place you in the repo, and you can run commands like git status and git pull like you would a normal repository (because it is just a normal repository).

Moving submodules within a superproject

git mv path/to/submodule new/path/to/submodule

If you did not use the --name flag of the submodule add command, the superproject name of your submodule might still be the outdated path. Check .gitmodules. You can change the submodule's name manually.

How git tracks submodules

Each submodule has a .git file (not a folder) that points to a directory inside the superproject's .git/modules/ folder. The path to the git-tracked folder mimics the path to the submodule within the superproject. For example, a submodule at ./a/b/my-submodule/ in the superproject, would have a git-tracked folder at ./.git/modules/a/b/my-submodule/. That git-internal folder tracks all the regular things git tracks, including the current commit. This is how you can version-lock your dependencies.