Automating Dev Tasks, Part 1: Git Hooks

Xavier ReedXavier Reed
4 min read

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:

  1. The file has the correct name (like pre-push)

  2. The file has executable permissions (chmod +x)

  3. 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

  1. Create the .githooks directory in your repository root if sharing hooks with all contributors (otherwise, create .git/hooks/ for only you).

  2. 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.

    1. Create .githooks/pre-push file — For git commands that would run before any git push command (with few exceptions, like git push --no-verify. See end of Steps for more). The following example only runs git 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
      
    2. Similarly, create the following git hook files for other Git operations:

      • .githooks/pre-commit - Runs before git commit

      • .githooks/post-commit - Runs after git commit

      • .githooks/pre-rebase - Runs before git rebase

      • And so on...

  3. Make file(s) executable: chmod +x .githooks/pre-push.

    1. 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.
  4. 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.

  5. Commit and push the .githooks directory to your repository

  6. Add instructions to your README telling other contributors to run git config core.hooksPath .githooks after cloning. Done.

    1. Can run git push --no-verify if you want to avoid git hooks from running on push (E.g. if only updating README, etc.).

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 and git 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

0
Subscribe to my newsletter

Read articles from Xavier Reed directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Xavier Reed
Xavier Reed