By Ran Bar-Zik | 9/28/2018 | General |Beginners

Git Hooks

Git Hooks

Git, like SVN and most other version control systems, allows for a very convenient usage of hooks. Hooks are, more or less, various scripts that you can use that will accompany the development process and will carry out certain all kinds of automated processes. In a team I work in, for example, we have Git hooks that carry out static code analysis to the entire system every time a developer runs a commit. If the developer deviates from the set standards, the commit will fail.

 

Each time the developer runs push, all of the unit testing is run. If there is a failure in one of the unit tests, the push doesn’t work. In this, we achieve a few goals. First, the programmer doesn’t need to remember to run a static code analysis or automatic checks or anything else related. Second is preventing unnecessary build failures.

 

There are a few ways to work with Git hooks; let’s look at the simplest way. You can make a random Git project using git init or go into an existing project. If you’re on Windows, make sure you can see hidden files. Go into the main project folder and you’ll see a mysterious folder name .git (notice the dot). This folder is pretty interesting and contains several sub-folders.

git directory

The folder we’re interested in for this article is, no surprise, called hooks and contains all of the hooks. Go into that folder and you’ll see there are lots of files with a .sample ending. Each one is a hook, by which I mean a specific action. For instance, if I see pre-commit.sample, then the name of the hook is pre-commit. You don’t need to be a genius to figure out that we’re talking about an action that occurs before a commit. If I see pre-push.sample, I can assume that we’re talking about a hook called pre-push that runs before a push.

 

What the GIT program does is go into the hooks folder and run each file in order. If there’s a pre-commit file, then everything inside of it will run before any user in the project runs commit.

 

Everything is clearer with an example, no? Let’s make a file called pre-commit (with no ending) or we can remove the .sample from the existing pre-commit file. If we peek inside, we’ll see that there is a script that checks if the user put in a file whose name has a foreign character. Here’s how that script looks:

 

#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".

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

# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --bool hooks.allownonascii)

# Redirect output to stderr.
exec 1>&2

# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
       # Note that the use of brackets around a tr range is ok here, (it's
       # even required, for portability to Solaris 10's /usr/bin/tr), since
       # the square bracket bytes happen to fall in the designated range.
       test $(git diff --cached --name-only --diff-filter=A -z $against |
         LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
       cat < <\EOF
Error: Attempt to add a non-ASCII file name.

This can cause problems if you want to work with people on other platforms.

To be portable it is advisable to rename the file.

If you know what you are doing you can disable this check using:

 git config hooks.allownonascii true
EOF
       exit 1
fi

# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

This script is written in BASH. Don’t be too afraid of this code and don’t worry about analyzing it. There are many BASH scripts that do countless routine actions on the server and this script is no exception. What it does is check to see if there are any non-ASCII characters in the file name. If there are it will print out an error and stop the commit. In other words, it returns an error if a script in the hooks returns an error, and  then the action is not carried out.

 

Once we put this script in the pre-commit file (without the ending!) every commit that’s run in that specific project will be run with the script. Here’s an example in which I added a file with a Hebrew name and tried to run commit:

git hooks

Pretty nice, no? This particular script is found in pre-commit.sample, but there are many other scripts around the net.

 

And what happens if on a specific occasion I want to bypass the hook? No problem—just add the flag --no-verify:

git commit --no-verify

In the team I work on we don’t put our hooks directly in the project file—we use grunt in order to manage our git hooks. But we’ll cover this in a future article.

 

Previous article: Git SSH Keys

Next article: Local Undos in Git

 

About the author: Ran Bar-Zik is an experienced web developer whose personal blog, Internet Israel, features articles and guides on Node.js, MongoDB, Git, SASS, jQuery, HTML 5, MySQL, and more. Translation of the original article by Aaron Raizen.

By Ran Bar-Zik | 9/28/2018 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now