sajad torkamani

In a nutshell

Git submodules let you add a Git repository as a subdirectory of another Git repository. This lets you treat them as separate Git projects.

An example use case would be if you have a custom WordPress theme that needs to be used on two separate websites. You can create the theme as its own Git repo and then add the theme repo as a submodule to the two website repos.

The workflow can be something like this:

  1. Create Git repo for theme code.
  2. Create separate Git repos for website 1 and website 2.
  3. Add theme repo as a Git submodule to website 1 and website 2.
  4. Assuming you’re working on website 1, make changes to your theme (e.g., some CSS changes). Once happy, push the changes to the theme’s Git remote.
  5. When you’re working on website 2, you can pull the latest changes from theme submodule repo.

Add a submodule

cd into the website project and add the theme repo as a Git submodule. Let’s assume the theme repo is called sajad-wp-theme:

git submodule add https://github.com/sajadtorkamani/sajad-wp-theme wp-content/themes/sajad-wp-theme

This will create a new directory in wp-content/themes/sajad-wp-theme.

It’ll also create .gitmodules file with the following content:

[submodule "wp-content/themes/sajad-wp-theme"]
	path = wp-content/themes/sajad-wp-theme
	url = git@github.com:sajadtorkamani/sajad-wp-theme.git

The .gitmodules file is tracked in version control so that other collaborators can easily clone the sub modules.

If you run git diff --cached wp-content-themes/sajad-wp-theme, you’ll see that Git treats as a submodule and doesn’t track its contents. But it keeps track of the latest commit in the submodule (see last line).

diff --git a/wp-content/themes/sajad-wp-theme b/wp-content/themes/sajad-wp-theme
new file mode 160000
index 0000000..e06215d
--- /dev/null
+++ b/wp-content/themes/sajad-wp-theme
@@ -0,0 +1 @@
+Subproject commit e06215d97e9e470c29413e421b6cfc7865dcd647

Clone a submodule

By default, when you clone a project with submodules, you get the directories containing the submodules (e.g., wp-content/themes/sajad-wp-theme), but those directories will be empty.

To fetch the submodules, first run:

git submodule init

This will initialize your local .git/config file so that it changes from something like:

[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[remote "origin"]
	url = git@github.com:sajadtorkamani/sajadtorkamani.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master

to:

[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[remote "origin"]
	url = git@github.com:sajadtorkamani/sajadtorkamani.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master
[submodule "wp-content/themes/sajad-wp-theme"]
	active = true
	url = git@github.com:sajadtorkamani/sajad-wp-theme.git

Notice the addition of the submodule metadata.

Next, run:

git submodule update

To fetch the fetch the data from the submodules and checkout the commit specified in your superproject.

Clone main project along with submodules

git clone --recurse-submodules https://github.com/<org>/<repo>

View submodules and their latest commits

In a project with submodules, run:

git submodule

Example output:

-e06215d97e9e470c29413e421b6cfc7865dcd647 ../wp-content/themes/sajad-wp-theme

Pull latest upstream changes from the submodule remote

Suppose new changes have been committed to a submodule remo and you want to pull in those changes. You can do this in two ways

1. Pull in changes in two steps

cd into the submodule directory and fetch the latest data from the submodule remote:

git fetch

to fetch the latest data.

Run git status and you should be told that your submodule branch is behind the remote branch:

On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Now, pull in the latest changes:

git pull

Now, cd back to the main project root and run:

git diff

You should see that a submodule’s checked out commit has been updated:

diff --git a/wp-content/themes/sajad-wp-theme b/wp-content/themes/sajad-wp-theme
index e06215d..300fcfb 160000
--- a/wp-content/themes/sajad-wp-theme
+++ b/wp-content/themes/sajad-wp-theme
@@ -1 +1 @@
-Subproject commit e06215d97e9e470c29413e421b6cfc7865dcd647
+Subproject commit 300fcfb57a0b020ce0b1f5c007d382c7609f301a

If you commit these changes, other collaboraters will also be locked into using this submodule version – which is usually what you want.

2. Pull in changes in single step

Pull changes for all submodules:

git submodule update --remote

Pull changes for specific submodule:

 git submodule update --remote <submodule-name>

Sources

Tagged: Git