Submodules

Git submodules provide a means of referencing external dependencies without having to import their history into our repository. Locally, submodules are checked out into the working directory. Submodules track commits directly by their references: they do not track branches. This allows us control over the exact revision we're using.

It's possible to nest submodules, but it's not possible for us to track changes to submodules in the parent repository: we can only point at references which exist upstream. If tracking changes is required, we can copy the history into our repository using worktrees instead.

Configuration

  • status.submoduleSummary determines whether git status should include the up to date status of submodules or not.
  • push.recurseSubmodules causes git push to push new objects from submodules to their remotes.

Storage

  • .gitmodules contains named submodules, with their in-tree paths and remote URLs. It's checked in and versioned alongside our code.
  • Submodule content is stored in .git/modules.
  • Submodules are linked from their configured path to their checkout in .git/modules.

Fetching

We can have git recurse into submodules and initialise and update them for us on clone:

git clone --recursive

In an existing repository we can initialise them, then update them:

git submodule init
git submodule update

Or combine both operations in a single command:

git submodule update --init

Add

We can add a module as follows:

git submodule add https://github.com/some-vendor/some-lib.git vendor/some-lib

We must then stage and commit changes to the .gitmodules file.

Sync

If a submodule's remote URL changes in .gitmodules, e.g. because the repository is moved or forked, local checkouts need to be synchronised with the new remote address with git submodule sync.

Update

git submodule update does the equivalent of a git pull in the submodule, only without using a branch.

Remove

We can remove the local checkout of a submodule like so:

git submodule deinit vendor/some-lib

Permanent deletion requires deletion of the link, which must be staged and commited:

git rm vendor/some-lib

For each

We can execute a command for each submodule:

git submodule foreach 'cat .gitmodules'