Git submodules
Table of Contents
Overview
A submodule allows you to keep another Git repository in a subdirectory of your repository. The other repository has its own history, which does not interfere with the history of the current repository. This can be used to have external dependencies such as third party libraries for example.1
Submodules are composed from a so-called gitlink
tree entry in the
main repository that refers to a particular commit object within the
inner repository that is completely separate. A record in the
.gitmodules
file at the root of the source tree
assigns a logical name to the submodule and describes the default URL
the submodule shall be cloned from.
Submodule fundamentals
Since Git 1.7.8, submodules use a simple .git file
with a single
gitdir:
line mentioning a relative path to the actual repo folder, now
located inside the container’s .git/modules
. This is mostly useful
when the container has branches that don’t have the submodule at all:
this avoid having to scrap the submodule’s repo when switching to
such a container branch.
Be that as it may, the container and the submodule truly act as independent repos: they each have their own history (log), status, diff, etc. Therefore be mindful of your current directory when reading your prompt or typing commands: depending on whether you’re inside the submodule or outside of it, the context and impact of your commands differ drastically!
The submodule commit referenced by the container is stored using its SHA1, not a volatile reference (such as a branch name). Because of this, a submodule does not automatically upgrade.
Traps2
- Every time you add a submodule, change its remote’s URL, or change the referenced commit for it, you demand a manual update by every collaborator.
- Forgetting this explicit update can result in silent regressions of the submodule’s referenced commit.
- Commands such as status and diff display precious little info about submodules by default.
- Because lifecycles are separate, updating a submodule inside its container project requires two commits and two pushes.
- Submodule heads are generally detached, so any local update requires various preparatory actions to avoid creating a lost commit.
- Removing a submodule requires several commands and tweaks, some of which are manual and unassisted.
Configuration settings
git config --global XXX XX
- diff.submodule = log (so you get clearer container diffs when referenced submodule commits changed).
- fetch.recurseSubmodules = on-demand (so you are confident new referenced commits for known submodules get fetched with container updates).
- status.submoduleSummary = true (so git status gets useful again when a referenced submodule commit changed).
Adding or cloning
- Initial add: git submodule add <url> <path>
- Initial container clone: git clone –recursive <url> [<path>]
Grabbing updates inside a submodule
cd path/to/module git fetch git checkout -q <commit-sha1> cd - git commit -am “Updated submodule X to: blah blah”
Grabbing container updates
git pull git submodule sync --recursive git submodule update --init --recursive
Updating a submodule inside container code
git submodule update --remote --rebase -- path/to/module cd path/to/module Local work, testing, eventually staging git commit -am “Update to central submodule: blah blah” git push cd - git commit -am “Updated submodule X to: blah blah”
Permanently removing a submodule (1.7.8+)
git submodule deinit path/to/module git rm path/to/module git commit -am “Removed submodule X”
Git commands
git submodule foreach
lets you run arbitrary commands on all known (initialized) submodules, recursively or not;git submodule status
is a specific status display for submodules, recursive on request.git submodule summary
lists history ranges between the latest referenced commits and the ones currently checked out.git mv
on a 1.7.8+ submodule directory (one with agitfile
) does the right thing: it changes the relative path inside thegitfile
, updates the core.worktree reference in the submodule’s repo inside.git/modules
, and updates and stages.gitmodules
.