Hooking Custom Functionality into the Git Pipeline
Git (like many other version control systems) support custom script triggers through hooks. These hooks give you a change to inject functionality at particular points in the standard pipeline:
- Before committing ("pre-commit")
- Before writing a commit message ("prepare-commit-msg")
- After writing a commit message ("commit-msg")
- After committing ("post-commit")
- Before a rebase ("pre-rebase")
- After a checkout ("post-checkout")
- After a merge ("post-merge")
- Before receiving a push ("pre-receive")
- After receiving a push ("post-receive")
- Before receiving a push, run once per branch ("update")
To add a hook, place a file with the hook name in .git/hooks/
.
Every git repository comes with several sample files already there to provide
example usage. Depending on the hook, git may pass in command line arguments
with information, such as a file pointer to the commit message or a list of the
files about to be committed.
Hooks that start with pre- can generally abort whatever operation is in place by returning a non-zero exit code. Why might you do this?
A Simple Hook: Displaying a Banner
Go to one of the repositories you've created from an earlier exercise and create a post-commit hook that displays a congratulatory banner, such as the following:#!/usr/bin/python print "3.. 2.. 1.. LIFTOFF!!"Play around to see that it works.
Preventing a Commit
Now let's try preventing ALL commits. Add a pre-commit hook that simply returns a non-zero exit code:
#!/usr/bin/python import sys print "No soup for you!" # In *nix systems, an "exit code" of 0 means # that execution happened sucessfully, whereas a nonzero # exit code indicates an error. # # In python, you can return an exit code with sys.exit(CODE) sys.exit(-1)
Try changing a file and committing that change to the repository. What happens?
Check the staging area with git status
to verify that the commit
didn't go through.
Let's change that hook to something a little more reasonable: a math puzzle. You're going to have to do a bit of a hack re-routing stdin to accept user input. Here's one math puzzle; try your own.
#!/usr/bin/python import random import sys x = random.randint(1,10) y = random.randint(1,10) # This is required because git hooks are run in non-interactive # mode. You aren't technically supposed to have access to stdin. # This hack works on MaxOS and Linux. Mileage may vary on Windows. sys.stdin = open('/dev/tty') result = input("What is %d * %s:" % (x, y)) if int(result) == x * y: sys.exit(0) else: print "INCORRECT!" sys.exit(-1)
Now try committing again. Verify that the commit is allowed when you get the puzzle right, and rejected when you are wrong.
More Reasonable Interrogation
Talk about some circumstances you can imagine wanting to prevent a commit. Anything here seem familiar to anyone with internships at a big company like Google?
Server-side Hooks
You can add hooks on the server-side, too. Go back to the repository you created on Athena and try adding the following post-update hook:
#!/bin/bash git show --name-status | mail -s "Received Push" eob@csail.mit.edu
See if you can figure out what it is going to do before pushing changes up to your Athena repository to find out. When might you want to be running hooks on a shared server, instead of a developer machine?
Simulating Github Pages
You are only about 10 minutes of work away from creating a simple version of Github Pages. How would you go about this?
We won't do it in class, because it involves writing a script that deletes files, and that is, ahem, dangerous, but this can be a convenient way to let a group of people update a simple website.