Prevent direct commits to master

I did it again. I pushed my local changes directly into remote master branch, but it was the last time.

Git Hooks

In a nutshell git hooks allow to run custom scripts when some Git actions happen. There are two types of them, client-side which are triggered by local operations like commit or merge, while server-side ones run on network operations like receiving pushed commits. The scripts can be pure shell scripts, but there are no limitations, you can use Ruby or Python as well.

All hooks are stored in hooks subfolder of the Git directory (.git/hook by default) and to be enabled they need to be named correctly (without any extension) and be executable.

A list of all available hooks can be found [here](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks). The most interesting one for my particular problem is pre-push, which runs during git push, after the remote refs have been updated but before any objects have been transferred.

Building the script

A first thing that needs to be done to have the hook running is creating a file named pre-push and making it executable. Inside a directory with Git repository initialized inside it can be done by:

touch .git/hooks/pre-push && chmod +x .git/hooks/pre-push

After the file is created we can start writing the script, to make it easy and understandable it will be a shell script. The most important part is having an access to a name of the current branch. In Git v. 1.8 and later it is as easy as running git symbolic-ref --short HEAD command.

The only thing the script needs to do is comparing the branch’s name with master and prevent executing push command if they are equal.

#!/bin/bash

protected_branch='master'
current_branch=$(git symbolic-ref --short HEAD)

if [ $protected_branch = $current_branch ]
then
  echo 'Pushing directly to master is forbidden'
  exit 1 # prevent push
else
  exit 0 # push will execute
fi

Bypassing the hook

In general pushing directly to remote master branch is considered as a bad practice and usually causes problems. However sometimes you just need to do it. To accomplish it you can bypass the pre-push hook with git push --no-verify.

Extra tip: Git templates to avoid repeated work

Do you want to have pre-push or any other hook defined by default for all future Git local repositories? Where there’s a will, there’s a way.

When you initialize a new repository Git uses a template to create required files and directories. Assuming you have Git installed in /usr/local, the template is placed in /usr/local/share/git-core/templates. You can place the pre-push (without any extension) file in hooks subfolder and it will be automatically copied to all new repos.

If you have updated the template after a repository was initialized, running git init again will pick up the new template.

 

Igor Springer

I build web apps. From time to time I put my thoughts on paper. I hope that some of them will be valuable for you. To teach is to learn twice.