How to – Work with Git Hooks?


 

What is Git Hooks?

A git hook is a script that git executes before or after a relevant git event or action is triggered.

Git hooks are scripts that Git executes before or after events such as: commit, push, and receive. Git hooks are a built-in feature - no need to download anything.

Where we can use Git Hook?

  • Commit code only if lint and build
  • Commit message format policy
  • Prevent pushes or merges that don’t conform to certain standards or meet guideline expectations
  • Facilitate continuous deployment
  • Connecting issue tracker with commit policy
  • Custom validations for master branch push
  • And many more….

Types of Git Hooks

  • Local Hooks
    • pre-commit: Runs before finishing commit
    • prepare-commit-msg: Provide a default commit message if one is not given.
    • commit-msg: Commit message validation.
    • post-commit: Runs after a successful commit.
    • post-checkout: Runs after every checkout.
    • pre-rebase: Runs before git rebase.
    • post-merge: Runs after a successful merge.
  • Server side Hooks
    • pre-receive
    • update
    • post-receive

How to use it in Node.js based projects?

Using git hooks with any package.json based project is very simple and everyone in a team doesn’t have to modify local hooks files manually.
Two best ways of using centralized git hooks from npm:

Install: npm install husky --save-dev // Edit package.json { "scripts": { "precommit": "npm test", "prepush": "npm test", "...": "..." } } git commit -m "Keep calm and commit" // Existing hooks aren't replaced and you can use any Git hook. // If you're migrating from ghooks, simply run npm uninstall ghooks --save-dev && npm install husky --save-dev and edit package.json. Husky will automatically migrate ghooks hooks.

Install: npm install --save-dev pre-commit { "scripts": { "test": "echo \"Error: I SHOULD FAIL LOLOLOLOLOL \" && exit 1", "foo": "echo \"fooo\" && exit 0", "bar": "echo \"bar\" && exit 0" }, "pre-commit": [ "foo", "bar", "test" ] }

alt tag

How to use Git Hooks with Gulp?

var gulp = require('gulp'); var guppy = require('git-guppy')(gulp); // Then simply define some gulp tasks in your gulpfile.js // whose names match whichever git-hooks you want to be triggerable by git. gulp.task('pre-commit', function () { // see below }); // less contrived example gulp.task('pre-commit', guppy.src('pre-commit', function (filesBeingCommitted) { return gulp.src(filesBeingCommitted) .pipe(gulpFilter(['*.js'])) .pipe(jshint()) .pipe(jshint.reporter(stylish)) .pipe(jshint.reporter('fail')); })); // another contrived example gulp.task('pre-push', guppy.src('pre-push', function (files, extra, cb) { var branch = execSync('git rev-parse --abbrev-ref HEAD'); if (branch === 'master') { cb('Don\'t push master!') } else { cb(); } }));

How to customize hooks manually?

If you want to write your custom script for any hook, here is how you can do it.
  • Open .git/hooks directory under your project directory [cd ./.git/hooks && ls]
  • you will see following sample hooks files

applypatch-msg.sample pre-applypatch.sample pre-commit.sample prepare-commit-msg.sample commit-msg.sample post-update.sample pre-push.sample pre-rebase.sample update.sample

  • The .sample extension prevents them from being run, so to enable them, remove the .sample extension from the script name.
  • Local hooks order of execution

<pre-commit> | <prepare-commit-msg> | <commit-msg> | <post-commit>

  • Server side hooks order of execution

<pre-receive> | <update> | <post-receive>

commit-msg

#!/bin/sh # # Automatically adds branch name and branch description to every commit message. # NAME=$(git branch | grep '*' | sed 's/* //') DESCRIPTION=$(git config branch."$NAME".description) TEXT=$(cat "$1" | sed '/^#.*/d') if [ -n "$TEXT" ] then echo "$NAME"': '$(cat "$1" | sed '/^#.*/d') > "$1" if [ -n "$DESCRIPTION" ] then echo "" >> "$1" echo $DESCRIPTION >> "$1" fi else echo "Aborting commit due to empty commit message." exit 1 fi

pre-commit

# 1. # This is a pre commit hook to automatically run linters (tslint and build) # before a commit can be made. I created this to force myself to run the linters # before commiting. # #!/bin/sh pass=true RED='\033[1;31m' GREEN='\033[0;32m' NC='\033[0m' echo "Running Linters:" # Run tslint and get the output and return code tslint=$(npm run lint) ret_code=$? # If it didn't pass, announce it failed and print the output if [ $ret_code != 0 ]; then printf "\n${RED}tslint failed:${NC}" echo "$tslint\n" pass=false else printf "${GREEN}tslint passed.${NC}\n" fi echo "-----------------------------------" echo "Running Build:" # Run build and get the output and return code build=$(npm run build) ret_code=$? if [ $ret_code != 0 ]; then printf "${RED}Build failed:${NC}" echo "$Build\n" pass=false else printf "${GREEN}Build passed.${NC}\n" fi # If there were no failures, it is good to commit if $pass; then exit 0 fi exit 1

# 2. # This is a pre commit hook to automatically run linters (tslint and stylelint) # before a commit can be made. I created this to force myself to run the linters # before commiting. # #!/bin/sh pass=true RED='\033[1;31m' GREEN='\033[0;32m' NC='\033[0m' echo "Running Linters:" # Run tslint and get the output and return code tslint=$(npm run tslint) ret_code=$? # If it didn't pass, announce it failed and print the output if [ $ret_code != 0 ]; then printf "\n${RED}tslint failed:${NC}" echo "$tslint\n" pass=false else printf "${GREEN}tslint passed.${NC}\n" fi # Run stylelint and get the output and return code stylelint=$(npm run stylelint) ret_code=$? if [ $ret_code != 0 ]; then printf "${RED}stylelint failed:${NC}" echo "$stylelint\n" pass=false else printf "${GREEN}stylelint passed.${NC}\n" fi # If there were no failures, it is good to commit if $pass; then exit 0 fi exit 1

# 3. #!/bin/bash # # This pre-commit hook checks that you havn't left and DONOTCOMMIT tokens in # your code when you go to commit. # # To use this script copy it to .git/hooks/pre-commit and make it executable. # # This is provided just as an example of how to use a pre-commit hook to # catch nasties in your code. # Work out what to diff against, really HEAD will work for any established repository. if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi diffstr=`git diff --cached $against | grep -e '^\+.*DONOTCOMMIT.*$'` if [[ -n "$diffstr" ]] ; then echo "You have left DONOCOMMIT in your changes, you can't commit until it has been removed." exit 1 fi

Learn more about Git Hooks

Todo

  • More automated scripts to come…
Author
[Bhavin Patel]

Advertisements

Leave a Reply

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