I’ve spent a little over a month working with Git now. I can honestly say that while there are so many things I like about Git, there are so many things I personally find that are a pain in the butt.
The submodules specifically have managed to be a thorn in my side on many occasions. While the concept of submodules is simple, figuring out how to actually work with them can be an arduous task. I say “find out” because not everything related to working with submodules is well documented. I’ll cover two of the hardest things to solve: removing and updating submodules from your repository.
What are submodules?
The concept of submodules is brilliant. Basically, it allows you to attach an external repository within another repository in a specific path. To illustrate the value of submodules, I’ll probably find it helpful to explain how I’m using them.
My profession is working with WordPress themes. Basically, I develop feature improvements for themes. I develop the code for these improvements in modules that are completely contained in their own folder. This allows code to be easily added to other themes and also simplifies code updates/enhancements as code for specific features is consistent across all themes that use that specific module.
Each topic we produce is maintained in its own Git repository. In addition, I have created a separate repository for each of these feature modules. Instead of putting the feature module code directly into the theme repositories, I simply add the necessary feature module repositories as submodules.
For example, we have a theme called FlexxBold. FlexxBold currently includes a total of seven submodules: billboard, contact-page-plugin, featured-images, feedburner-widget, file-utility, flexx-layout-editor and tutorials. Since I’m using submodules, code can be pulled directly from the relevant submodule repositories instead of requiring me to manually update each individual topic repository.
As I mentioned before, not everything in Git is easy to work with. There are four main functions you’ll need to understand to work with Git submodules. In order, you will need to know how to add, use, delete, and update submodules. I’ll cover each of those uses below.
Adding submodules to a Git repository Fortunately, adding a
submodule to a git repository
is fairly simple. For example, if I am in the working directory of the repository of a new theme called SampleTheme and I need to add the billboard repository to the lib/billboard path, I can do it with the following command
: [user@office SampleTheme]$ git submodule add git@mygithost:billboard lib/billboard Empty Git repository initialized in remote ~/git_dev/SampleTheme/lib/billboard/.git/: Counting objects: 1006, done. Remote: Compress objects: 100% (978/978), done. Remote: Total 1006 (delta 631), reused 0 (delta 0) Object reception: 100% (1006/1006), 408.22 KiB, done. Delta resolution: 100% (631/631), done.
There are three main parts to this command:
- git submodule add – This simply tells Git that we are adding a submodule. This syntax will always be the same.
- git@mygithost:billboard – This is the external repository to be added as a submodule. The exact syntax will vary depending on the configuration of the Git repository you are connecting to. You need to make sure that you have the ability to clone the given repository.
- lib/billboard – This is the path where the submodule repository will be added to the master repository.
Let’s check how the repository is working.
[user@office SampleTheme]$ git status # In branch master # Changes to commit: # (use “git reset HEAD <file>…” to unstage) # # new file: .gitmodules # new file: lib/billboard #
Notice how the supplied path was created and added to the changes to be committed. In addition, a new file named .gitmodules was created. This new file contains the details we provide about the new submodule. Out of curiosity, let’s take a look at the contents of that new
file: [user@office SampleTheme]$ cat .gitmodules [submodule “lib/billboard”] path = lib/billboard url = git@mygithost:billboard
Being able to modify this file later will be useful later
All that’s left to do now is to commit the changes and then send the confirmation to a remote system if necessary.
Having submodules in a repository is great and all, but if I look in my repository, all I have is an empty folder instead of the actual contents of the submodule’s repository. To populate the submodule path with the files from the external repository, you must first initialize the submodules and then update them.
First, we need to initialize the submodule(s). We can do it with the following command
: [user@office SampleTheme]$ git submodule init Submodule ‘lib/billboard’ (git@mygithost:billboard) registered for the path ‘lib/billboard’
Then we need to run the update to deploy the files
. [user@office SampleTheme]$ git submodule update Empty Git repository initialized in remote ~/git_dev/SampleTheme/lib/billboard/.git/ : Counting objects: 26, done. Remote: Compress objects: 100% (22/22), done. Remote: Total 26 (delta 5), reused 0 (delta 0) Object reception: 100% (26/26), 17.37 KiB, done. Delta resolution: 100% (5/5), done. Submodule path ‘lib/billboard’: retired ‘1c407cb2315z0847facb57d79d680f88ca004332’
Looking in the lib/billboard directory now shows a good list of needed files. Deleting
What if we need to delete a submodule? Maybe I made a mistake. It could also be that the design of the project has changed and the submodules need to change with it. No problem, I’ll just run git submodule rm and everything will be great, right?
[user@office SampleTheme]$ git RM submodule lib/billboard error: pathspec ‘rm’ did not match any file known to git. Forgot ‘git add’? b8ff8f68eb56938b9b4bf993619218fa848c5848 lib/billboard (1.2.25)
Unfortunately, this is wrong. Git doesn’t have a built-in way to remove submodules. Hopefully this will be solved in the future, because now we have to do the removal of submodules manually.
Continuing with the example, we will remove the lib/billboard module from SampleTheme. All instructions will be executed from the working directory of the SampleTheme repository. In order, we need to do the following:
- Delete the submodule entry in the .gitmodules file. Since lib/billboard is the only submodule in the SampleTheme repository, we can simply delete the file entirely by running git rm .gitmodules. If lib/billboard is not the only submodule, the .gitmodules file will need to be modified by hand. Open it in vi, or in your favorite text editor, and delete the three lines relevant to the submodule being deleted. In this case, these lines will be deleted: [submodule “lib/billboard”] path = lib/billboard url = git@mygithost:billboard
- Delete the submodule entry in the .git/config. This step is not strictly necessary, but it keeps your configuration file tidy and will help avoid problems in the future. The submodule entry in .git/config will only be present if you have run git submodule init in the repository. If you haven’t, you can skip this step. In this example, the following lines will be deleted: [submodule “billboard”] url = git@mygithost:billboard
- Delete the path created for the submodule. This one is easy. Just run git rm -cached [plugin path]. In this example, I’ll run git rm -cached lib/billboard. As I’ve seen elsewhere, don’t place a trailing forward slash as the command will fail. For example, if I run git rm -cached lib/billboard/, I get an error: fatal: pathspec ‘lib/billboard/’ does not match any files. [user@office SampleTheme]$ git rm -cached lib/billboard rm ‘lib/billboard’
There is an aspect about submodules that some may not realize when they first work with Git submodules. When the submodule is added, the most recent submodule commit is stored in the master repository index. That means that as the code is updated in the submodule repository, the same code will continue to be extracted in the repositories that depend on the submodule.
This makes perfect sense when you consider how your code will have been tested and verified (or at least should be) against a specific version of the submodule code, but the master repository code may not work well with new submodule updates before the changes are tested.
Unfortunately, like deleting submodules, Git doesn’t make it clear how to update a submodule to a later commit. Fortunately, however, it is not that difficult.
- Initialize the repository submodules by running the git init submodule followed by updating the git submodule. [user@office SampleTheme]$ git init submodule ‘lib/billboard’ submodule (git@mygithost:billboard) registered for ‘lib/billboard’ path [user@office SampleTheme]$ git submodule update Empty Git repository initialized in ~/git_dev/SampleTheme/lib/billboard/.git/ remote: Counting objects: 26, done. Remote: Compress objects: 100% (22/22), done. Remote: Total 26 (delta 5), reused 0 (delta 0) Object reception: 100% (26/26), 17.37 KiB, done. Delta resolution: 100% (5/5), done. Submodule path ‘lib/billboard’: retired ‘1c407cb2315z0847facb57d79d680f88ca004332’
- Change to the submodule directory. In this example, cd lib/billboard. [user@office SampleTheme]$ cd lib/billboard [user@office SampleTheme/lib/billboard]$
- repositories added by the git submodule update are “headless”. This means that they are not in a current branch. To fix this, we simply need to switch to a branch. In this example, that would be the master branch. We change with the following command: git checkout master. [user@office SampleTheme/lib/billboard]$ git status # Currently not in any branch. nothing to confirm (clean working directory) [user@office SampleTheme/lib/billboard]$ git checkout master HEAD’s previous position was b8ff8f6… reorder Changed to ‘master’ branch Your branch is behind ‘origin/master’ by 8 confirmations, and you can fast forward. [user@office SampleTheme/lib/billboard]$ git status # On branch master # Your branch is behind ‘origin/master’ by 8 commits, and can be fast-forward. # nothing to confirm (clean working directory) Next
- , we simply need to update the repository to make sure we have the latest updates. We can do that with a pull: git pull. [user@office SampleTheme/lib/billboard]$ git pull remote: Counting objects: 31, done. Remote: Compress objects: 100% (24/24), done. Remote: Total 24 (delta 15), reused 0 (delta 0) Unpacking objects: 100% (24/24), done. From mygithost:billboard b8ff8f6.. 5cab93f master -> origin/master * [new label] 1.2.28 -> 1.2.28 From mygithost:billboard * [new label] 1.2.26 -> 1.2.26 * [new label] 1.2.27 -> 1.2.27 Update c547e0d.. 5cab93f Fast Forward Billboard.php | 109 ++++view_gettingstarted_content.php view_gettingstarted.php+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 38 +++++ css/admin.css | 26 ++++ history.txt | 22 +++- js/admin.js | 17 +++ lib/updater/get.php | 94 ++++++++- lib/updater/history.txt | 9 ++ lib/updater/updater.php | 241 +
- [user@office SampleTheme/lib/billboard]$ cd.. /.. [user@office SampleTheme]$
- Everything is now ready to be confirmed and pushed back (if there is a remote repository to push to that it is). If you run git status, you will notice that the path to the submodule appears as modified. Here’s what you should expect to see. Just add the confirmation path and make a confirmation. When you commit, the index will update the commit string for the submodule. [user@office SampleTheme]$ git status # On branch master # Changed but not updated: # (use “git add…” to update what will be confirmed) # (use “Git Checkout – …” to discard changes to the working directory) # # modified: lib/billboard (new commits) # no changes were added to commit (use “git add” and/or “git commit -a”) [user@office SampleTheme]$ git add lib/billboard [user@office SampleTheme]$ git status # On branch master # Changes to commit: # (use “git reset HEAD …” to unstage) # # modified: lib/billboard #
+++++.css 100644 view_gettingstarted_content.php 100644 view_gettingstarted.php.js.txt++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 100644+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
I’ve learned a lot in my short time with Git. Expect to see more details on how to work with Git in the future. I have a number of traps that I’ve discovered that I should organize together and publish. I’ve also created some really fancy scripts to help me automate numerous things when working with the repositories I’d like to share.
If there is anything specific you would like to know about Git, please add a comment or contact me.
Did I help you?
- Send me some advice through PayPal
- Help with hosting this site with my Linode referral code.