PHP Storm IDE showing a post-merge git hook

Git (post-merge) Hook for happy Drupal developers

As developers, we like to focus on the complex problems, while getting the mundane stuff quickly out of the way. Git hooks can help us achieve that in an easy, efficient, and reusable manner.

Photo of Salvador Molina Moreno
Fri, 2014-11-28 10:15By salva

Granted, you don't need to be trendiest person in software to know about tools like Grunt, or Gulp (but in case you don't, they're really useful task automation tools to aid with common development tasks). They keep growing at a high pace, and they've plugins for almost anything you can think of. Watching file changes in your SASS Partials folders to compile them into CSS and livereload your browser straight away? Easy. Run unit tests every time you change a test file or a controller? You're sorted, too. 

Now, what if you're doing Drupal development, with no fancy front-end stuff, and the idea of having to use nodejs on your dev environment just to automate tasks is not really that compelling? Well, you still have some other options out there. In this blog post, I'd like to show you a practical use case of git hooks; one that I particularly like, and have been using in recent projects, the post-merge hook. 

If you don't really know what a git hook is, the basic idea is that of a script that can be executed upon certain events happening while working on git repositories. What for? As mentioned, for whatever you want, since you can define what's in those scripts, so you decide what happens when they run. For more information about git hooks, their types, and how to make use of them, refer to the git handbook.

Now, hands on with the post-merge hook. Essentially, as per the documentation, "The post-merge hook runs after a successful merge command". In particular, when looking into git hooks for the tasks I wanted to automate,  I was looking for something to act right after a git pull. There's no such thing as a post-pull hook, but as it happens, git pull is the same as "git fetch <remote>" followed by "git merge origin/<current-branch>".

We usually work with a custom mysite_deploy module that keeps some basic info about the site in code, such as modules / features that should be enabled, and some helper functions to help with deployment or maintenance tasks, particularly during the development phase. Drupal update hooks are in the DNA of all our projects, but having to watch out for certain changes in files to see what updates or deployment changes need to be done in your environment every time you merge a feature branch, or pull the changes a colleage has done, is boring, and tiresome. Also, in early stages of the project, it's a bit of a pain to write an update hook just so that your colleage has the <insert-your-favourite-module-here> module enabled locally to test your feature. 

In short, I wanted a way to make sure everything that has to be enabled, is enabled automatically after pulling, and any database update applied automatically, without having to watch out for it.

With the use case being clear, let's see how my post-merge hook would look like.

changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
 
check_run() {
	echo "$changed_files" | grep --quiet "$1" && eval "$2"
}
 
# If mysite_deploy.module has changed, run "drush mysite-deploy-update"
# If mysite_deploy.install has changed, run "drush updb"
 
# CD into drupal root (otherwise drush call will fail).
# This call *needs* to be here, after the changed_files have been retrieved in
# the command above.
DRUPAL_ROOT=web
cd $DRUPAL_ROOT
 
check_run mysite_deploy.module "drush ldu"
check_run mysite_deploy.install "drush updb -y"

Quick explanation of the script:

changed_files: Holds the diff of the working tree, comparing the contents present before the merge, and the contents after. 

check_run(): This is the function to call as many times as we need (emulating a file watcher). It gets 2 arguments: a file name, and the command to execute if the given file has been changed on the merge.

DRUPAL_ROOT: This variable contains the path to the drupal root folder (from the root of the repository). In my case, it was "web". 

And That's all. In my case, I was watching out for 2 different things. If the .install file of our deploy module changed, I wanted "drush updb" to be called automatically. Also, I wrote a custom drush command to call some functions in case the main .module file of our deploy module changed, in order to enable new modules / features added, flush caches, rebuild permissions, etc. 

As simple as that. Of course, I couldn't be the first one looking for something like this, and it turned out someone else had already gisted pretty much the solution I was looking for, so in this case I didn't really have to do much. 

Of course, this example is a very trivial one, and if you're looking for a file watcher to keep an eye on plenty of things, there are better tools out there. For simple things, though, make sure you're leveraging all the features that git has to offer. They're worth it!