loading...
Cover image for How To Enforce Conventional Commit Messages Using GIT Hooks

How To Enforce Conventional Commit Messages Using GIT Hooks

prahladyeri profile image Prahlad Yeri Updated on ・3 min read

Originally published on prahladyeri.com

Conventional git commit messages are not just nice to have but great to have. In fact, once you get to know them, you'll start feeling that they are essential in any serious programming project. Consider the difference between following two commit messages for instance:

git commit -m "added social login feature for authentication using twitter"
git commit -m "feat(authentication): added social login using twitter"

The second one is not only more readable but it also follows standards which makes it easier to integrate with build tools such as Travis CI and Appveyor. Not just that, you can easily automate CHANGELOG generation when your git log looks like this:

> git log --oneline
61c8ca9 (HEAD -> master) fix: navbar not responsive on mobile
479c48b test: prepared test cases for user authentication
a992020 chore: moved to semantic versioning
b818120 fix: button click even handler firing twice
c6e9a97 fix: login page css
dfdc715 feat(authentication): added social login using twitter

All in all, I like conventional commit messages so much that I don't want to keep it optional but make it the default way of life. How do I do it? The answer is simple: git hooks.

You don't need to be a git ninja or expert to work with hooks. Suffice it to know that inside your special git repository folder (named .git), there are some other special folders:

logs
hooks
objects
refs

The one we are interested in is hooks. To get the hang of it, create a test project on your machine and initialize an empty git repository by running:

git init .

Now visit the .git folder just generated and navigate to the hooks folder. There will be a bunch of scripts with .sample extension (which means they are all disabled). The one we are interested in is commit-msg. This is the hook that fires just before your commit is made and thus allows you to reject the commit by throwing an error if the message isn't in proper format.

Create a new script in this directory named commit-msg and add the below code (you'll need python installed) to it using your favorite editor (mine is notepad++!):

#!/usr/bin/env python
import re, sys, os

def main():
    # example:
    # feat(apikey): added the ability to add api key to configuration
    pattern = r'(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert)(\([\w\-]+\))?:\s.*'
    filename = sys.argv[1]
    ss = open(filename, 'r').read()
    m = re.match(pattern, ss)
    if m == None: raise Exception("conventional commit validation failed")

if __name__ == "__main__":
    main()

Save the script and make it executable (by running chmod u+x commit-msg on linux, not required on windows). Now head back to your source code folder where you initialized the git repo and create a source file just for testing. Then, git add and try to commit just for testing using a non-conventional message. If all goes well, it should fail like this!

> git commit -m "added a new feature for xyz"
Traceback (most recent call last):
  File ".git/hooks/commit-msg", line 22, in <module>
    main()
  File ".git/hooks/commit-msg", line 19, in main
    if m == None: raise Exception("conventional commit validation failed")
Exception: conventional commit validation failed

Now test with a valid commit message and it should work!

Once you practice this a few times and get a hang over it, you'll then want to make this behavior default for whatever projects you start using git init in future, isn't it? Sure, you can do that by creating a global git commit hook template but that will be a post for another day. First things first!

Edit

For those really interested in enforcing and automating this thing, I've just published a python package called enforce-git-message. All you need to do is install that python package and it'll automatically copy the above script to your ~/.git-templates directory and also set the value for git config init.templatedir.

pip install enforce-git-message

References:

Posted on by:

prahladyeri profile

Prahlad Yeri

@prahladyeri

Most programmers like coffee but I'm fond of tea.

Discussion

markdown guide
 

Good post, thank you!

I do have a question. As what you covered here uses a file that is truly at local working folder, how can we ensure that the local commit hook code is not tampered by the developer to permit his commit?

In a CVS, the hook is handled at the server, I guess, so, I believe a CVS fares better in those cases.

 

You can never do such validation on git because git is a decentralized system (unlike CVS). All you can do is a best effort automation and scripting for this (like this package) but ultimately its the user's machine, you can't prevent them from altering their hooks folder.

 

Hey, check out Hooks4Git, a tool I wrote myself to make hooks management easier. I liked your idea for commit message patterns. Perhaps I can add a built-in tool for that. Tks.

 

I prefer to explain the pattern when validation fails.

Good article, thanks for the inspiration.

 

Thanks for the suggestion. I've implemented a more graceful error message in the updated version:

> git commit -m "added a new feature for xyz"

COMMIT FAILED!

Please enter commit message in the conventional format and try to commit again. Examples:

+ 61c8ca9 fix: navbar not responsive on mobile
+ 479c48b test: prepared test cases for user authentication
+ a992020 chore: moved to semantic versioning
+ b818120 fix: button click even handler firing twice
+ c6e9a97 fix: login page css
+ dfdc715 feat(auth): added social login using twitter
 

Nice article! I have a question similar to @arun . Is it possible to enforce a commit message style in open source repository? For all contributors and pull requests? Thanks.

 

You can do that if you have github enterprise, they provide these hooks on the server side through settings. If you run your own git server, you'll have to code an equivalent scripting solution of your own to achieve this.