Automating Dev Tasks, Part 1: Git Hooks

Table of contents
Git hooks are any executable script or program (E.g. Shell scripts written in Bash, etc.) you create in your repo to trigger automated tasks during specific Git operations. They run locally on a developer's machine as part of Git's execution flow, typically via the files defined in the .git/hooks
folder (if folder is created).
They can be used as an alternative, or in addition to, things like GitHub Actions — use Git hooks to automate and validate local development workflows (like pre-commit linting or pre-push testing), and use GitHub Actions to automate cloud-based operations on the remote repo (like CI/CD pipelines, deployments, or integrations with services like Docker or CodeQL). See near the end of this article for a detailed breakdown of when to use one over the other.
Git hooks can be written in:
Any shell scripting language (bash, sh, zsh, etc.)
Any scripting language (Python, Ruby, Perl, etc.)
Even compiled executables
The only requirements are that:
The file has the correct name (like
pre-push
)The file has executable permissions (
chmod +x
)The first line uses a proper shebang (like
#!/bin/sh
or#!/usr/bin/python
) if it's a script
The following will provide a general outline of how to create a Git hook, with an example pre-push Git hook in a Next.js project using Bash scripting in MacOS. It is the same general process for other git commands (like git commit
, git merge
, etc), only requiring slight alterations for other languages/frameworks (E.g. may use pytest test_module.py
for a Python project instead of npm test
, etc), and other shell languages.
Steps
Create the
.githooks
directory in your repository root if sharing hooks with all contributors (otherwise, create.git/hooks/
for only you).Create file(s) in your
.githook
folder and name them depending on when that hook script should run. This hook naming convention tells Git when to run the script.Create
.githooks/pre-push
file — For git commands that would run before anygit push
command (with few exceptions, likegit push --no-verify
. See end of Steps for more). The following example only runsgit push
if both the build and tests succeed:#!/bin/bash # Get the current branch name current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\\(.*\\),\\1,') echo "Running build and tests before pushing to $current_branch..." # Run build echo "Running build..." npm run build build_status=$? # If build failed, prevent the push if [ $build_status -ne 0 ]; then echo "Build failed. Push aborted." exit 1 fi # Run tests echo "Running tests..." npm test test_status=$? # If tests failed, prevent the push if [ $test_status -ne 0 ]; then echo "Tests failed. Push aborted." exit 1 fi # If we got here, both build and tests passed echo "Build and tests passed. Continuing with push." exit 0
Similarly, create the following git hook files for other Git operations:
.githooks/pre-commit
- Runs beforegit commit
.githooks/post-commit
- Runs aftergit commit
.githooks/pre-rebase
- Runs beforegit rebase
And so on...
Make file(s) executable:
chmod +x .githooks/pre-push
.- Only you need to run this command. The
chmod +x
permission is preserved in the git repository, so other contributors don't need to run that command themselves.
- Only you need to run this command. The
Run
git config core.hooksPath .githooks
on your machine. This tells Git to look for hooks in the.githooks
directory instead of the default.git/hooks
.Commit and push the
.githooks
directory to your repositoryAdd instructions to your README telling other contributors to run
git config core.hooksPath .githooks
after cloning. Done.- Can run
git push --no-verify
if you want to avoid git hooks from running on push (E.g. if only updatingREADME
, etc.).
- Can run
Hence, from now on, whenever you push directly to the main branch via git push
, the following commands will automatically run beforehand, and crucially, only execute the push if these commands succeed : npm run build
, and npm test
.
Git Hooks vs. GitHub Actions
The following is a general breakdown of the difference between Hooks and Actions so that you can choose the correct approach when automating developer tasks.
Git Hooks:
Where they run: Locally on a developer's machine
When they trigger: During Git operations like commit, push, etc.
Who experiences them: Only the individual developer who has set them up
Setup: Need to be configured on each developer's machine (hence the
chmod
andgit config
commands above)Best for: Local development workflows, preventing bad commits/pushes before they happen
Examples: Pre-commit code linting, running tests before pushing
GitHub Actions
Where they run: On GitHub's servers in the cloud
When they trigger: On repository events like push, pull request, issue creation
Who experiences them: Everyone interacting with the repository
Setup: Configured once in the repository for everyone
Best for: CI/CD, team-wide enforcement, complex workflows
Examples: Automated testing, building and deploying applications, PR checks
When to Use Each
Use Git Hooks when:
You want to enforce standards before code leaves a developer's machine
You want to implement personal productivity workflows
You want to catch issues early in the development process
Use GitHub Actions when:
You want team-wide enforcement of standards
You need integration with GitHub features (PRs, issues, etc.)
You need to run tasks in the cloud (building, deploying)
You want a central, consistent workflow regardless of developer setup
Subscribe to my newsletter
Read articles from Xavier Reed directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
