GIT Pre-Commit Hook: How to Commit Yourself to... Committing “Clean” Drupal Code Only

Don't do to others what you don't want others to do unto you! In other words: don't push code that lacks coding standards and is “packed” with syntax errors, if you wouldn't want to work with other developers' “sloppy” code, either. Especially since you can always use Git pre-commit hook to ensure that the code you're about to commit is clean and within standards.

And speeding up & improving your commits are 2 of your “gold objectives” as a Drupal developer, right?

In this case, there's no point debating that Git is THE distributed version control system that developers use for neatly structuring their code.

But there's more! GIT provides several hooks (we'll be focusing mostly on the pre-commit hooks in this post) that you can tap into just to make sure that your code:

… before committing it.

All this in a conveniently streamlined way, that makes:

  1. reviewing code manually
  2. the continuous integration method

… so cumbersome and prone to error.

Now, without any further ado, let's see:

  • What GIT hooks and especially the pre-commit hook(s) are
  • Why and how to enable GIT pre-commit hook
  • How it works precisely
  • How to create Git hooks globally
  • How to adjust your pre-commit hooks to fit your own codebase

     

1. What Are Git Hooks Anyway? And What Is a Git Pre-Commit Hook?

GIT hooks are scripts executed before & after you (or other developers in your team):

  • commit
  • push
  • receive

… code.

Moreover, they're built-in features, so you get them by default, right out-of-the-box (yet disabled) along with any project that you clone on your local instance. 

It's just that probably you've been ignoring them so far, sticking to your own familiar code review methods.

Note: it's in the ‘.git/hooks’ directory that you can scan through a list of supported hooks (and sample code).

And Git hooks come in 3 main “flavors”. Each one “in charge” with one of the 3 above-mentioned actions that you usually perform when handling your code in GIT: 

  1. pre-commit, which practically checks whether the lines of code to be submitted adhere to the Drupal coding standards and are syntax errors-free
  2. pre-receive, checks the line of code that is being pushed to git-repo
  3. post-receive, gets triggered as soon as that file/line of code gets merged to the remote git repo; a practical example would be that where all your teammates get notified through an email including the commit link and the commit message, as well

Now, if I am to give the Git pre-commit hook a “special attention”, I can't skip mentioning that it's a 3-sequence role that it plays:

  1. the first pre-commit: it's PHPLint that it leverages when checking your PHP code, looking for syntax errors and other proves of “code uncleanliness” 
  2. the second pre-commit: will “sniff” your Drupal code, trying to detect any cases where it does not follow the official Drupal coding standards; it's 2 message types that it will “alert” you through: “errors”, respectively “warnings” 
  3. the third pre-commit: it detects any accidentally added debugging code functions and blocks the commit process 

Notes:

  1. using a GIT pre-commit hook, your code gets checked for Drupal coding standards only for those specific lines of code that you have written to be committed
  2. it will check files having certain extensions
  3. but ignore changes made within the docroot folder and also files related somehow to the features (since these are, obviously, features-generated files)

But the major “conclusion” of my “pledge” for using this type of Git hooks is that:

It will prevent any new code from being committed if it does not comply with the Drupal coding standards.

So, once you've passed this “task” over to this specific Git hook, you can instead focus on reviewing your code exclusively for logic and best practices!

You'll find it under .git>hooks!

 

2. Why Should You Install GIT Pre-Commit Hook? How Does It Work?

“Since there already is a dedicated Drupal coder module, plus lots of software packages that I can use for structuring my code so that it should follow the coding standards”.

You must be saying to yourself right now.

Now imagine these 2 common scenarios:

  1. you're working on a large project, one that comes packed with a huge amount of code, previously written by an entire “army” of developers; now taking every single line of code and checking it for errors and for whether it adheres to coding standards or not is pure... madness
  2. you're under a lot of stress, your client keeps urging you to deliver your project when... the commit delivers you an intimidatingly long list of coding standard problems; and where do you add that it's not even you who wrote that code!

One (or rather 2) thing's for sure:

  1. manual code review is out of question when you're dealing with such scenarios
  2. setting up continuous integration, which would ensure automated code checks, might be an option

Now to your:

“But how does it work precisely?”

… question, here's the most comprehensive answer/explanation I can come up with:

Once you perform your GIT commit, the GIT pre-commit hook script gets triggered; and it's then that your code gets checked for syntax errors, accidentally added debugging functions, merge conflict tags, whether it adheres to the framework coding standards etc.

Note: if any issues get detected, you'll need to mark the error with “exit 1", if everything's according to standards, just do “exit 0”.

 

3. How Do You Create Git Hooks? How Do You Enable GIT Pre-Commit Hook?

OK, so we've seen what they are, how they work, why you should use them on your continuous “lookout” for methods to improve your commits, now the legitimate question that arises is:

“How do I enable Git pre-commit hook script?”

Here's an example you could use as “learning material”: manoj-apare/pre-commit.

Just scan through that code and keep in mind that:

  1. it's only staged files (so, no deleted files) that you should be checking for Drupal coding standards
  2. it's this “magic formula” that you need to use: ‘git diff-index --diff-filter=ACMRT --cached --name-only HEAD --’. 

Now getting back to Git hooks:

“How do I create global hooks?”

… you might be wondering.

Needless to add that the very first step to take is to install the following key components:

Next, to update the path and integrate Drupal along with its DrupalPractice Standards.

It's only then that you can set up your git-templates location:

git config --global init.templatedir '~/.git-templates'
mkdir -p ~/.git-templates/hooks

Next, copy your Git hooks into either:

  1. ~/.git-templates/hooks 
  2. or <projectdir>/.git/hooks, if it's only on some of your Drupal projects that you want to use them

Download the pre-commit hooks to ~/.git-templates/hooks and remember to make those files executable:

chmod a+x ~/.git-templates/hooks/*

The next step to take is to add these hooks to your Git repos.

Note: from now on, any repos that you'll init or clone will have these hooks by default!

Now as for the existing repos that you want add your Git hooks to, all you need to do is run “git init” inside the repo directory and they'll get copied there.

 

4. Customizing Your Pre-Commit Hooks: Files to Be Checked/Ignored

Let's see now how you can customize your hooks so that they should:

  1. check specific files only
  2. while intentionally ignoring others

Since all the Git pre-commit hooks share the same structure, getting them “personalized” to your code/file checking needs shouldn't pose any major problems.

Here are the 4 main arrays to use for indicating which files those checks should cover:

  1. the $file_exts array, specifying the file types to be checked; in case you don't want your hooks to check the “.class” files, simply remove that entry from the array
  2. the $ignore_filename_strings array, specifying those strings, under the same filename, that should be skipped; they don't even need to match the entire filename, if they partially match it's enough for these target strings to be ignored 
  3. the $ignore_file_path_strings array, pointing out entire directories (docroot and contrib modules) to be ignored
  4. the $debugging_searches array, an additional one, which indicates specifically what debugging functions your hook should check and eventually prevents the code from being committed if those functions get tracked down

Now here are some practical examples of Git pre-commit hooks set up to check specific files and eventually prevent them from being committed in case any errors or “deviations” from the coding standards get detected:

  • syntax check: you can run a php linter check to identify any compilation errors; filter out exclusively php files ‘git diff-index --diff-filter=ACMRT --cached --name-only HEAD -- | egrep '\.php$|\.inc$|\.module|\.theme$'’. 
  • debugging functions check: it's a grep tool that you can use: ‘git diff --cached $FILE | egrep -x "$pattern" 
  • merge conflict marker check: use egrep patternegrep -in "(<<<<|====|>>>>)+.*(\n)?" $FILE’ for this type of check
  • coding standard check: use PHP codesniffer (phpcs) for this and remember to filter our specific file formats such as fonts, images, install, module, php format files (and the list goes on)

     

5. In Conclusion

Here are some of the top benefits you can “reap” from using Git pre-commit hook for neatly structuring your code:

  1. you make sure that only clean, proper code, following the Drupal coding standards, gets pushed
  2. clean and neatly structured code improves and speeds up the whole project management process
  3. you ensure that only properly formatted commit messages, following the given documentation, get pushed
  4. less failing automated tests in CI
  5. it sets up a proper workflow when deploying something on the repository

How about you? Have you been “playing with” Git hooks whenever you wanted to make sure your commits adhered to Drupal's coding standards and best practices?