Friday, September 26, 2008

git-submodule > script/plugin install

I'm sure this has been covered elsewhere so I'll keep it short. Git is becoming more and more popular, especially in the Rails community. But one thing that really "grinds my gears" is I still see a lot of plugin pages with the following instructions:
 Use 'script/plugin install git://somesite.com/path' to get this plugin.
C'mon, script/plugin install with a git protocol url? Is that what's good in the streetz these days? Nah, you can do better than that. If you can give a git protocol address to install a plugin, then you already have git installed. Chances are if you've installed git, you're using it for your fancy rails project with all those plugins. So why not use git to manage your plugins as well?

Here's an example of how to install a plugin (say, rspec) as a git submodule:

$ cd /path_to/project
$ git submodule add git://github.com/dchelimsky/rspec.git vendor/plugins/rspec
=>  Initialized empty Git repository in /path_to/project/vendor/plugins/rspec/.git/
    remote: Counting objects: 49365, done.
    remote: Compressing objects: 100% (12304/12304), done.
    remote: Total 49365 (delta 35612), reused 47804 (delta 34389)
    Receiving objects: 100% (49365/49365), 6.31 MiB | 895 KiB/s, done.
    Resolving deltas: 100% (35612/35612), done.
From here, git status shows you have 2 new files in your repository: vendor/plugins/rspec, and .gitmodules. If you take a look at your .gitmodules file you'll see something like the following example:
  [submodule "vendor/plugins/rspec"]
        path = vendor/plugins/rspec
        url = git://github.com/dchelimsky/rspec.git 

You can also use git submodule summary to see the version of any of your submodules:

  $ git submodule summary vendor/plugins/rspec
  =>  593572217fd388081b3e75ab2cefb5f49923167f vendor/plugins/rspec (1.1.8)

In the previous output, you see your submodule's current commit hash, and the tag is in parentheses. If the current commit does not have an associated tag, you would see "undefined" in the parentheses instead of the tag name.

At this point, vendor/plugins/rspec is a regular directory. However, your git repository sees it not as a directory, but as an object with 2 properties: a git repository url, and a commit in that repository. I don't know the git internals well enough to go into further detail, so that's my educated guess based on the contents of the .gitmodules file and the git submodule summary command.

What about cloning a project that already has submodules defined? That's easy too:

  $ git clone git://host/project.git
  $ cd project
  $ git submodule init
  => Submodule 'vendor/plugins/rspec' (git://github.com/dchelimsky/rspec.git) registered for path 'vendor/plugins/rspec'
  $ git submodule update
  => Initialized empty Git repository in /path_to/project/vendor/plugins/rspec/.git/
     remote: Counting objects: 49365, done.
     remote: Compressing objects: 100% (12304/12304), done.
     remote: Total 49365 (delta 35612), reused 47804 (delta 34389)
     Receiving objects: 100% (49365/49365), 6.31 MiB | 299 KiB/s, done.
     Resolving deltas: 100% (35612/35612), done.
     Submodule path 'vendor/plugins/rspec': checked out '593572217fd388081b3e75ab2cefb5f49923167f'
Again there are a lot of other places that already explained this but there are a couple reasons why this is better. It keeps your repository smaller and more organized - your plugins are separate from your project, which makes sense because you generally don't modify your project's plugins. For those who write plugins, it encourages people who are already using git to use git some more. Obviously still leave the old plugin install instructions for people who aren't using git. But I mean, if your plugin is on Github already...

Update: I forgot to mention, you need git 1.5.3 or greater to use submodules.

3 comments:

Phil said...

you should mention you need git version >= 1.5.3 to use submodules.

Harry Seldon said...
This comment has been removed by the author.
Harry Seldon said...

Thanks for this post.

Can you use this configuration to developp both the project and the plugin ?
That is can you easily commit and push your changes to the project repo and the plugin repo ?