This page looks best with JavaScript enabled

Git Submodules Basics and How to Replace One Submodule With Another

 ·  ☕ 7 min read

Introduction

Git Submodules are the concept related to modularity. One git repository can be added to another as a submodule and maintained separately. Instead of being tightly coupled, it is loosely coupled and is easy to maintain. Suppose you are working with softwareA which depends on libraryA, instead of copy-pasting the libraryA over and over again when a new version of the library is released what we can do is use submodule to make this process DRY and elegant.

1. Setting Up the Repo Structure

For this example I will we demonstrating this concept with my blogging engine Hugo and the submodule which is a theme for Hugo. I keep theme as a separate module because want to reduce overhead when updating to the new theme version, which will be deleting and adding a whole new version of theme repo.

If you are following this tutorial with me, you need to install Hugo on your system. Consult your OS’s package manager or if you using Windows, consult getting started guide at Hugo’s site.

Install Hugo

Easiest method for me to install hugo is to use go get command.

$ go get -u github.com/gohugoio/hugo
go: golang.org/x/net upgrade => v0.0.0-20200904194848-62affa334b73
go: github.com/hashicorp/golang-lru upgrade => v0.5.4
go: github.com/spf13/cobra upgrade => v1.0.0
go: github.com/Azure/azure-pipeline-go upgrade => v0.2.3
go: golang.org/x/sys upgrade => v0.0.0-20200905004654-be1d3432aa8f
go: github.com/google/wire upgrade => v0.4.0
go: github.com/yuin/goldmark upgrade => v1.2.1
go: github.com/pkg/errors upgrade => v0.9.1
go: github.com/niklasfasching/go-org upgrade => v1.3.2
go: github.com/bep/golibsass upgrade => v0.7.0
[...output trimmed...]

As I already have Hugo installed, this command will upgrade it to latest version. Command output will differ if you are installing a fresh Hugo.

Create a Hugo Site

Now I have the latest version of Hugo. Let’s start and create our main/parent git repository for my blog.

$ hugo new site myblog
Congratulations! Your new Hugo site is created in /home/sntshk/repos/myblog.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from https://themes.gohugo.io/ or
      create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
   with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".

Visit https://gohugo.io/ for quickstart guide and full documentation.

$ cd myblog

$ git init
Initialized empty Git repository in /home/sntshk/repos/myblog/.git/

$ git add .

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   archetypes/default.md
        new file:   config.toml


$ git commit -m "Initial commit"
[master (root-commit) 0f1d8e5] Initial commit
 2 files changed, 24 insertions(+)
 create mode 100644 archetypes/default.md
 create mode 100644 config.toml

Above I have created a bare Hugo site, entered and initialized that directory as a git repo and did the initial commit. As you can see below, our site has no theme at this point in time.

$ ls
.git/        resources/   content/  layouts/  themes/
config.toml  archetypes/  data/     static/

$ ls themes

Add a theme

Now instead of picking up a theme and slamming into my themes/ directly (which will obviously work out of the box), I’ll add it as submolule.

The current theme I’m using is called hermit which is available at https://github.com/Track3/hermit. Let’s add this theme.

$ git submodule add https://github.com/Track3/hermit.git themes/hermit
Cloning into '/home/sntshk/repos/myblog/themes/hermit'...
remote: Enumerating objects: 16, done.
remote: Counting objects: 100% (16/16), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 787 (delta 1), reused 9 (delta 1), pack-reused 771
Receiving objects: 100% (787/787), 460.32 KiB | 615.00 KiB/s, done.
Resolving deltas: 100% (344/344), done.

Before we start the local server, we need to configure our config.toml file. For sake of simplicity, we’ll use sample theme hermit already comes with.

$ cp themes/hermit/exampleSite/config.toml .

At present the status of the repo is like so:

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   .gitmodules
        new file:   themes/hermit

The .gitmodule file stores all the git modules added to the current repo. At current, it looks like this:

[submodule "themes/hermit"]
    path = themes/hermit
    url = https://github.com/Track3/hermit.git

It start with a section about the submodule.path is relative to the root, url tracks the URL for future updates. Also you’ll see themes/hermit is now listed as file and not a subdirectory.

It’s time to commit the changes.

$ git commit -m "Add hermit theme"
[master 651c044] Add hermit theme
 2 files changed, 4 insertions(+)
  create mode 100644 .gitmodules
   create mode 160000 themes/hermi

I will start the server and head over to http://127.0.0.1:1313 which is the default address for local prototyping in Hugo.

$ hugo server- D
Start building sites …
                   | EN
-------------------+-----
  Pages            | 10  
  Paginator pages  |  0  
  Non-page files   |  0  
  Static files     | 10  
  Processed images |  0  
  Aliases          |  0  
  Sitemaps         |  1  
  Cleaned          |  0  

Built in 56 ms...
[...output trimmed...]
Hermit Splash Screen

Hermit Splash Screen

2. Working with Submodules

At this stage, you can keep adding new post (or making changes) to the main repository. Let’s make a few post to demonstrate this:

$ hugo new posts/my-first-post.md
/home/sntshk/repos/myblog/content/posts/my-first-post.md created

$ hugo new posts/my-second-post.md
/home/sntshk/repos/myblog/content/posts/my-second-post.md created

$ echo Hello world >> content/posts/my-first-post.md

$ echo Hello Mars >> content/posts/my-first-post.md

My git log tree looks like this:

$ git log
* 61087cf - (HEAD -> master) Add a few posts (63 seconds ago) <Santosh Kumar>
* b5a80a7 - Update config.toml for hermit (5 minutes ago) <Santosh Kumar>
* 651c044 - Add hermit theme (7 minutes ago) <Santosh Kumar>
* 0f1d8e5 - Initial commit (45 minutes ago) <Santosh Kumar>

Submodules are tracked by the exact commit specified in the parent project, not a branch, a ref, or any other symbolic reference.

We have two repository now, the main repository which is maintained by you and hermit theme repository. At this stage the main repository can be pushed to remote, say at github.com/santosh/myblog.

Mostly you’ll be working with the main repo doing commits. Submodule resides in the repo and only get updated when you want to. If you add commits to a submodule, the parent project won’t know. You have to inform it manually.

Removing a Submodule

My website at present is in similar state as shown in the log above. Just that I have many blog post made and each commit represent a post made. Now I want to change my theme to something else. git doesn’t comes with a magic command to replace theme in my situation. We need to first delete the submodule and then add it back. Let’s get started. We’ll divide the process into 4 parts.

  1. Delete relevant files from the .gitmodules file.

At present, my files .gitmodules looks like this:

1
2
3
4
5
6
[submodule "themes/hermit"]
       path = themes/hermit
       url = https://github.com/Track3/hermit.git
[submodule "themes/hugo-shortcodes"]
       path = themes/hugo-shortcodes
       url = https://github.com/santosh/hugo-shortcodes.git

I’ll go ahead and delete the first 3 lines.

  1. Delete the relevant section from .git/config.

While .gitmodules is a file which stores information about modules and should be commited to the repo. There is one other location where this data is stored i.e. .git/config file. This is all your local configuration of your repo.

I removed these lines from the config file.

[submodule "themes/hermit"]
    url = https://github.com/Track3/hermit.git
    active = true
  1. Run git rm --cached themes/hermit

This command will remove the themes/hermit path from the git tracking. Be sure you don’t have a trailing slash in the module path when executing this command.

  1. Delete the now untracked submodule and commit the changes.

This is half the way of my journey to replace my theme. I will delete my themes/hermit directory and commit the changes.

Adding a Submodule

Installation process is same as described early in the tutorial. I’ll add Zzo theme now.

git submodule add https://github.com/zzossig/hugo-theme-zzo.git themes/zzo
git submodule update --remote --merge

The above submodule update command is run for all the submodule in the repo, which is kinda overkill in this situation, but it updates all the existing submodules to their latest.

After some themes specific settings, my site is now ready to charm in the new theme.

Conclusion

Today we’ve got familiar with some git submodule stuff. If you like this post, you may want to subscribe to my newsletter.

Related Readings:

Share on

Santosh Kumar
WRITTEN BY
Santosh Kumar
Fullstack Developer at Method Studios