I did it again. I pushed my local changes directly into remote
master branch, but it was the last time.
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
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.