Working with Git Submodules

Submodules are an important part of the MIL repo! These submodules allow our code to function properly! Formally, these submodules are directories inside of our repository somewhere which are actually a Git repository themselves. In essence, these directories (inside of the main MIL directory) are pointing at a specific commit inside of another Git repository.

This pattern can be confusing, so here are some commands to help you out!

Adding a new submodule

To add a new submodule into a repository, you can use git submodule add <url>!

$ git submodule add https://github.com/chaconinc/DbConnector

When you do this, a new directory called DbConnector will appear, and the .gitmodules file (which keeps track of your submodules) will be updated. Now, you’ll need to actually commit it to the repository, like any other folder you add:

$ git commit -am 'Add DbConnector module'
$ git push

Cloning a repository with submodules

When you clone a repository with nested submodules, you must run some commands to get your submodules up and running:

$ git submodule update --init --recursive

In this command, --init initializes your submodule configuration file, while --recursive does this for all nested submodules in the project.

Pulling in upstream changes in submodules

Through the submodule remote

Sometimes, submodules can be updated by another user, and you’ll need to pull these changes into your own copy. If you would like to do this for one submodule, cd into the submodule, and then update the specific submodule yourself:

$ cd DbConnector
$ git fetch
$ git merge origin/master
$ cd ..
$ git commit -am 'Pulled in new submodule code'
$ git push

Because you committed with the new submodule code, whenever others fetch new changes, they will also get the update submodule changes.

While the above is great for small, independent submodule changes where you want more customizability in how the submodule is updated, repeatedly doing multiple commands to update the submodule can get annoying! Thankfully, git helps to fix that with:

$ git submodule update --remote

This command will update all submodules from their remote repositories. If you want to update a submodule from another branch its on, you can change your Git config.

$ git config .gitmodules submodule.DbConnector.branch stable
$ git submodule update --remote DbConnector

Now, when you update the DbConnector repository, it will pull from the stable branch. You can then git commit and git push as normal.

Through the main project remote

Normally, to update the project, you would run git pull. However, when working with submodules, this won’t be enough to create effective changes. This will get the relevant changes that occurred in the submodules, but it won’t actually update the submodules.

Instead, you will also need to run:

$ git submodule update --init --recursive

These two commands can be simplified into one through:

$ git pull --recurse-submodules

Sounds like a lot to type? Let’s make --recurse-submodules default!

$ git config submodule.recurse true

Working on a submodule

Eventually, you may want to make some changes to the submodule to help improve the code!

Before working, let’s merge in any changes from the upstream:

$ git submodule update --remote --merge

Now, we can make some changes:

$ cd DbConnector/
$ vim src/example.cpp
$ git commit -am "Add example test"

You’ve now made a commit to the submodule. If it’s been a while and you want to rebase changes from the upstream:

$ git submodule update --remote --rebase
# OR
$ git submodule update --remote --merge

But, we still haven’t actually pushed the changes!

$ git push --recurse-submodules=on-demand

Note that this will push any commits for every submodule. If you want to push some commits for one submodule, then cd into the submodule directory and use git push.