Git submodules in N easy steps

Git has something called submodule support. This allows you to specify one or more other git repositories within another – a bit like svn:externals (except trickier, but more powerful of course :).

The git user manual describes submodules but it took me a while to figure it out, so I’m hoping these examples will help others (and me again when I forget and find my own page when googling about it :)

These examples deal with your_project and the project you’ll be adding as a submodule, other_project

Adding a submodule to your_project

$ git submodule add git@git.example.com:other_project.git other_project Initialized empty Git repository in /home/john/dev/your_project/other_project/.git/ remote: Counting objects: 59, done. remote: Compressing objects: 100% (59/59), done. remote: Total 59 (delta 22), reused 0 (delta 0) Receiving objects: 100% (59/59), 8.33 KiB, done. Resolving deltas: 100% (22/22), done. 

This clones other_project and sets up the .gitmodules config to your_project and adds them both ready to be committed. You’ll notice that the other_project directory is added, not all the files within. Git just records the commit id from the other_project repository and uses that when cloning – a bit like a tag.

$ git status # new file: .gitmodules # new file: other_project 

So now commit those changes:

$ git commit .gitmodules other_project -m "Added other_project submodule" $ git push 

Cloning a tree with submodules

Git doesn’t automatically fetch all your submodules, so you need to do the following after cloning your tree:

$ git submodule init Submodule 'other_project' (git@git.example.com:other_project.git) registered for path 'other_project' $ git submodule update Initialized empty Git repository in /home/john/dev/your_project/other_project/.git/ remote: Counting objects: 59, done. Receiving objects: 100% (59/59), 8.33 KiB, done.bjects: 91% (54/59) Resolving deltas: 100% (22/22), done. remote: Compressing objects: 100% (59/59), done. remote: Total 59 (delta 22), reused 0 (delta 0) Submodule path 'other_project': checked out '6d5ca374208715501832eb33ed6a70022a3bb60c' 

Updating a submodule

So somebody pushed some updates to other_project and you want them in your_project:

$ cd other_project $ git pull origin master Updating 6d5ca37..235996d Fast forward 5 files changed, 100 insertions(+), 10 deletions(-) $ cd .. $ git add other_project $ git commit otherproject -m " Updated other_project submodule to latest HEAD" 

If you want a particular commit rather than the head of master, then just specify that commit id on pull (instead of master). If you want the head of a particular branch then specify that branch name instead of master.

It is important not to have a trailing slash when you add other_project as this will treat it as a normal directory, adding all the files within it to your_project and forgetting about it’s submodule status

Making changes to a submodule within your_project

To make changes to other_project within your_project tree, you need to explicitly checkout a branch first:

$ cd other_project $ git checkout master ...make your changes... $ git commit -a -m "Fixed a bug" $ git push $ cd .. $ git add other_project $ git commit -m "Updated other_project" 

You need to remember to push changes to other_project before you push your_project else others won’t be able to clone your_project properly as it will reference commits to other_project that haven’t been published yet!

Comments

vlkv says:

nice short explanation! Thanks :)

Madhava Jay says:

Hi John, nice post.
The only problem I have with submodules is when someone makes a change to a submodule everyone else with working trees of the same main project dont get those changes on their next pull. Is there a way to enforce or opt in for that.

Additionally, for some reason when people initially clone down the main project their submodule will get init and updated to a specific tag, but that leaves their submodules working tree on (no branch), which means they cant make changes or update unless they switch to master. Its easy to do that but its a pain in the ass.

I would like a simple way to ensure the submodules change to the tag in the commit of their parent repo when you pull down, that way, I can make a change to a submodule and all other working trees get that change as well when they next pull down my main project commit.

Does that make sense, im not sure if its even possible but it would be awesome! :)

Cheers,
Madhava Jay
Kintek.com.au

Anonymous says:

Be careful using tab completion with the module paths when committing. When tabbing, it will append a / (slash) to the end of the directory but this will cause Git to misinterpret the command and you will get an error:

error: pathspec 'lib/other_project/' did not match any file(s) known to git.

So make sure you remove that trailing slash!

James Womack says:

I’ve looked at about 6 submodule descriptions in the last 20 minutes and yours is the one that clicked for me. Thank you.

john skaller says:

Solved my problem in 60 seconds.. thanks!!

[…] my Git submodules post I name the two example projects your_project and other_project and use it consistently throughout. […]

Anonymous says:

Hi
Cloning is not working properly for sub modules.
I have a Main repo and added two repos as sub modules.Inside that sub modules also i have some repos.

for Eg: Repo main ->sub module repo A(A having two repos as sub modules B and C).

While cloning (git clone –recursive git @IP…:Repo main)

I am able to see sub module repo A.but it is not listing the sub module repos inside A(B and C)

Anonymous says:

Anonymous on 10/1/2013 at 07:12 said:

But i am able to clone submodule repo B and C seperately.

Atul kumar says:

awesome, that solves my whole problem

Leave a Reply