a git repo full of git repos

related: git , source control , submodules

With Subversion, you can check out a new svn repo into any directory.  Even if that directory is an svn repo.  It doesn’t really matter,  They can all be embedded in each other, and everything works fine.

Git doesn’t support that, since there is only one .git folder for the whole repo instead of one per directory.  It gets confuse-ed.

They solve that with ‘submodules’, which is an overcomplicated, hard-to-use solution to the problem, just like everything git does.  I love them very much.

I have a common pattern that I like to use at work; something like this:

1
2
3
4
5
6
7
/          <—– 'master’ repository
  /docs
  /releases
  /src        <—– a different repository (maybe)!
  /dependencies
    /toolchain       <—— a different repository!
    /flash_burner  <—— yet another repository!

The /src directory might be a different repository, specifically if it was given to us by a customer with version control history, and we want to retain that.

Each directory under /dependencies is a separate repository, because those are source branches that are required to build/use this project, but are developed independently and possibly shared between multiple projects.

In git, this isn’t all that hard to setup.  You just add each sub-repository as a git submodule:

1
2
3
# git submodule add <git server>:flash_burner ./dependencies/flash_burner
# git submodule init ./dependencies/flash_burner
# git submodule update

Now you have a different git repo checked out into the directory ./dependencies/flash_burner.  Now you should also probably add… something.  ’commit -a’, and push if you have a remote server.  Because now you have a .gitmodules file tracking all of your submodules, and that’s just a regular file in the repo now.

But, alas, checking this whole conglomeration out is kind of a bitch.  It looks something like:

1
2
3
4
5
# git clone <remote repo, the parent of everything>
# git submodule init <path to a submodule>
# git submodule init <path to next submodule…>
…
# git submodule update

And then you still aren’t necessarily done.  A submodule is linked to a single revision, the one that was the head when you created it.  If you want to actually track the head, you need to cd to each submodule and ’git checkout master’.

So, the point of this whole post is that you don’t have to cd to each repo, actually, just run this nonsense:

1
git –git-dir=<path to submodule> –work-tree=<path to submodule> checkout master

And that point of that, dear reader, is that now you make a ‘checkout_submodules.py’ script at the top-level of your master repo, which contains a list of all submodules, inits them all, updates them all, and checks out the head.

I like Mercurial.