Developer Diaries: adding, using, and removing git submodules
Create an empty git repository:
$ mkdir test_submodule $ cd test_submodule $ git init Initialized empty Git repository in <path>/.git/
Add a submodule
$ git submodule add https://github.com/pivotal/jasmine.git [path] Cloning into 'jasmine'... remote: Reusing existing pack: 9590, done. remote: Total 9590 (delta 0), reused 0 (delta 0) Receiving objects: 100% (9590/9590), 3.58 MiB | 714.00 KiB/s, done. Resolving deltas: 100% (5998/5998), done. Checking connectivity... done
If optional path is not specified, git submodule will use name jasmine for the submodule checkout folder.
Module jasmine is automatically added to the index:
$ git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: .gitmodules # new file: jasmine #
SHA-1 of the checked out commit is registered in the index as well:
$ git submodule 38daa43c7e3f77c7bec85b92c98747c6daa34c80 jasmine (v2.0.0-7-g38daa43)
SHA-1 commit id can prefixed with a '-' if the submodule is not initialised, with a '+' if the currently checked out submodule commit does not match the SHA-1 found in the index of the containing repository, and with 'U' if the submodule has merge conflicts (see the output of git help submodule for more details).
The corresponding entries in the .gitmodules and the .git/config files has already been created:
$ cat .gitmodules [submodule "jasmine"] path = jasmine url = https://github.com/pivotal/jasmine.git $ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = false [submodule "jasmine"] url = https://github.com/pivotal/jasmine.git
Also, the .git/modules directory contains the corresponding submodule directory:
$ ls .git/modules/jasmine/ HEAD config hooks/ info/ objects/ refs/ branches/ description index logs/ packed-refs
Using a specific commit of the submodule
Say I need to use a specific commit id of jasmine:
$ cd jasmine $ git checkout 7bbcf51d458cad3931a7d02bc9c316dd7c396946 Note: checking out '7bbcf51d458cad3931a7d02bc9c316dd7c396946'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b new_branch_name HEAD is now at 7bbcf51... Removed Frank for GHPages generation. Now using Rocco and a Thor task to build it
Notice the '+' sign in front of the commit id in the output of the git submodule command:
# the git submodule must be run from the top-level directory $ cd .. $ git submodule +7bbcf51d458cad3931a7d02bc9c316dd7c396946 jasmine (v1.2.0-1-g7bbcf51)
It means that the currently checked out submodule commit does not match the SHA-1 found in the index of the containing repository. In order to make this change permanent:
$ git add jasmine $ git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: .gitmodules # new file: jasmine # # no '+' anymore in front of the commit id $ git submodule 7bbcf51d458cad3931a7d02bc9c316dd7c396946 jasmine (v1.2.0-1-g7bbcf51) $ git commit -m 'Added submodule Jasmine.' [master (root-commit) 8f2c0e8] Added submodule jasmine. 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 jasmine $ git status # On branch master nothing to commit, working directory clean
Now that we added a submodule, let's see how can we use it.
Checkout the submodule in a fresh clone of the containing repository
$ cd .. $ mkdir test_submodule_checkout $ cd test_submodule_checkout $ git clone ../test_submodule Cloning into 'test_submodule'... done. Checking connectivity... done $ cd test_submodule $ git submodule -7bbcf51d458cad3931a7d02bc9c316dd7c396946 jasmine
Note, the '-' in front of the commit id meaning that the module is not initialised yet. The .gitmodules file already exists and contains an entry corresponding to the jasmine submodule, but the .git/config file does not mention it yet.
$ cat .gitmodules [submodule "jasmine"] path = jasmine url = https://github.com/pivotal/jasmine.git $ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = false [remote "origin"] url = ../test_submodule fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master
Also, the .git/modules/jasmine directory does not exist yet.
We can initialise the jasmine submodule with:
$ git submodule init Submodule 'jasmine' (https://github.com/pivotal/jasmine.git) registered for path 'jasmine'
This will append
[submodule "jasmine"] url = https://github.com/pivotal/jasmine.git
to the .git/config file, but .git/modules/jasmine will not be there until we actually clone and checkout the module. We do this using git submodule update command, which will clone the jasmine repository and check it out at the commit id registered in the containing repository:
$ git submodule update Cloning into 'jasmine'... remote: Reusing existing pack: 9590, done. remote: Total 9590 (delta 0), reused 0 (delta 0) Receiving objects: 100% (9590/9590), 3.58 MiB | 804.00 KiB/s, done. Resolving deltas: 100% (5998/5998), done. Checking connectivity... done Submodule path 'jasmine': checked out '7bbcf51d458cad3931a7d02bc9c316dd7c396946'
Now also the .git/modules/jasmine directory exists.
You can init and update a submodule with one command:
git submodule update --init
Only after the repository is initialised and updated, the git submodule command will report its status as initialised (no '-' sign in front of the checkout id):
$ git submodule 7bbcf51d458cad3931a7d02bc9c316dd7c396946 jasmine (v1.2.0-1-g7bbcf51)
Now that we know how to use a submodule, let's see how to get rid of it.
Remove a submodule
In the previous section we initialised the submodule with the git submodule init command. There is a command that does the opposite: deinitialises the module:
$ git submodule 7bbcf51d458cad3931a7d02bc9c316dd7c396946 jasmine (v1.2.0-1-g7bbcf51) $ git submodule deinit jasmine Cleared directory 'jasmine' Submodule 'jasmine' (https://github.com/pivotal/jasmine.git) unregistered for path 'jasmine' $ git submodule -7bbcf51d458cad3931a7d02bc9c316dd7c396946 jasmine (v1.2.0-1-g7bbcf51)
If you want to deinit all submodules use git submodule deinit .
This will remove the corresponding entry from the .git/config file leaving .gitmodules untouched.
To remove all traces of the submodule, you need to preform the following commands:
$ rm -rf .git/modules/jasmine $ rm .gitmodules # or just the entry corresponding to the submodule if you have more than one submodule $ git rm --cached jasmine rm 'jasmine' $ rm -rf jasmine $ git commit -am 'Removed submodule jasmine.' $ git submodule $
And we're done.
Happy developing!
References:
http://stackoverflow.com/a/16162000/1272679
http://stackoverflow.com/a/1260982/1272679
http://alx.github.io/gitbook/5_submodules.html
















