Customizing Git with Ruby Git Hooks

Remember the last time you accidentally checked in a gigantic file you didn’t mean to, and had to dig through the Git history for it?  And everybody had to update their history and that one guy accidentally blew away all his changes?

Ah, Git .  It’s a useful and powerful tool, but it’s also easy to make mistakes that can be a pain to recover from. What if you could avoid some of that pain before you commit your changes?

Over the past year, we have started using Git for some of our projects here at OnLive.  As part of that process, we found the need for tools to help prevent mistakes we didn’t want to accidentally make – like checking in giant files, or forgetting to include a bug ticket number in the commit message.

Git Hooks

Git does provide a mechanism for adding your own customizations, called Git Hooks, but it turns out to be kind of a pain to work with.  Each type of hook interacts with Git in a different way, and most hooks are written in shell scripts.  We wanted to take away the complexity and have a way to write our own checks in plain old Ruby, with the information we need at our fingertips

That turned into Ruby Git Hooks, which became OnLive’s first open-source software project!

How do I use it?

Once you install RubyGitHooks in your development environment, you can use the built-in hooks provided, or write your own.

If you want to catch that giant file before it gets committed to your Git repository, you can use the built-in MaxFileSizeHook. Set it up to run every time you do a git commit by putting the following into the file .git/hooks/pre-commit (that’s where Git looks for a pre-commit hook).

#!/usr/bin/env ruby
# Put this file in .git/hooks/pre-commit 
# and make it executable!
require "ruby_git_hooks/max_file_size"

RubyGitHooks.run MaxFileSizeHook.new

That’s it!  Next time you do a git commit, it will check for extra large files and stop the commit with a warning message if necessary.  I don’t know why you had that gigantic file in the first place, but at least this can prevent it from wreaking havoc.

But wait, what if you want to check more than one attribute?  For example, you also want to be sure you never commit files whose names differ only in case (like Fun.rb and fun.rb).  With RubyGitHooks you can register multiple hooks and run them.

#!/usr/bin/env ruby
# Put this file in .git/hooks/pre-commit and make it executable!
require "ruby_git_hooks/case_clash"
require "ruby_git_hooks/max_file_size"

RubyGitHooks.register CaseClashHook.new
RubyGitHooks.register MaxFileSizeHook.new

RubyGitHooks.run

Can I Make My Own?

What if you also want to do something completely different, like, for example, check that your code passes unit tests before allowing the commit.  There’s no built-in hook for that. But you can add your own with a few lines of ruby.

#!/usr/bin/env ruby
# Put this file in .git/hooks/pre-commit and make it executable!
require "ruby_git_hooks/case_clash"
require "ruby_git_hooks/max_file_size"

class UnitTestHook < RubyGitHooks::Hook
  def check
    # return a boolean value
    system(“rake test”)
  end
end

RubyGitHooks.register CaseClashHook.new
RubyGitHooks.register MaxFileSizeHook.new
RubyGitHooks.register UnitTestHook.new
RubyGitHooks.run

This is a very simple example, but the great thing is that within your hook you have access to information about the commit from git – including which files are in the commit, the diffs, the commit message and even which kind of hook we are being run as. You can look at the code for the built in hooks to see how easy it is to get at this information.

Is that all?

RubyGitHooks also supports other types of hooks besides just pre-commit[1] and has a variety of other built-in hooks. It’s been a valuable tool in our use of Git and we would love to hear how it’s useful (or not) for you too!


Footnotes:

[1]: RubyGitHooks supports local (pre-commit, commit-msg, and post-commit) and server side hooks (pre-receive and post-receive). But the server side hooks can’t be used with repositories hosted on GitHub, because you can’t install arbitrary code on even a GitHub Enterprise server. But that’s another story (stay tuned…)

4 Responses to “Customizing Git with Ruby Git Hooks”

  1. Would love to hear more about git hooks (never used it before)

    • You can Google and get some basic details. Normally Git Hooks are shell scripts, but each hook type tends to take completely different input, which makes them annoying to write. We’ve hand-waved away most of those details while also moving the hook framework to Ruby. We think the result is a lot easier to use (but a bit slower to run!)

  2. […] written before about the obscure and confusing world of implementing Git Hooks and about our RubyGitHooks gem that […]

  3. […] first week here we hosted a Ruby Meetup where Ruth presented our then newest open source project on Ruby Githooks. Which is a huge improvement over the standard bash git hooks. Also, Noah presented the “5 […]

So what do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: