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 a gitfile) does the right thing: it changes the relative path inside the gitfile, updates the core.worktree reference in the submodule’s repo inside .git/modules, and updates and stages .gitmodules.

More Examples

cc


Footnotes:

Author: Shi Shougang

Created: 2017-06-05 Mon 23:02

Emacs 24.3.1 (Org mode 8.2.10)

Validate