Git Pre-Push Hook: A Practical Guide (with Examples)

Updated: January 27, 2024 By: Guest Contributor Post a comment

Introduction

Version control systems are an integral part of modern software development practices, facilitating collaboration and ensuring code quality among developers. Git pre-push hooks offer a robust mechanism to incorporate automated checks before code is shared with a team. In this guide, we’ll dive deep into the concept of Git pre-push hooks, explore how to set them up, and walk through practical examples to automate your workflows, enhancing productivity and code consistency.

What is a Git Pre-Push Hook?

A Git pre-push hook is a script that runs automatically on your local machine right before your changes are pushed to a remote repository. It’s a client-side hook that can run tests, check code styles, or even abort the push if it falls short of the defined conditions.

Setting Up a Pre-Push Hook

To create a pre-push hook, you’ll start by accessing your project’s .git/hooks directory. Rename the ‘pre-push.sample’ to ‘pre-push’ and ensure it’s executable:

cd /path/to/your/repo/.git/hooks
mv pre-push.sample pre-push
chmod +x pre-push

Now, let’s write a simple script inside the pre-push file that echoes a message.

#!/bin/sh
echo "Running pre-push hook..."
# Add validation scripts here
exit 0

After saving, every time you try to push, this message will appear.

Basic Pre-Push Checks

The most common use for a pre-push hook is to run tests to ensure nothing breaks with the new changes. Let’s look at a basic example for a JavaScript node project:

#!/bin/sh

if ! npm test; then
  echo "Tests failed! Push denied."
  exit 1
fi
exit 0

Attempting to push when tests fail:

$ git push
Running pre-push hook...
Tests failed! Push denied.
error: failed to push some refs to 'repository url'

Advanced Pre-Push Hooks

To improve the usability of our pre-push hook, let’s ensure code quality using linting and also disallow pushing directly to the master branch:

#!/bin/sh

branch_name=$(git symbolic-ref HEAD 2>/dev/null | cut -d"/" -f 3)

if [ "branch" = "master" ]; then
  echo "You can't push directly to master branch."
  exit 1
fi

if ! npm run lint; then
  echo "Linting errors! Push denied."
  exit 1
fi

if ! npm test; then
  echo "Tests failed! Push denied."
  exit 1
fi

exit 0

This script incorporates a check for the branch name and runs a linting script as part of the criteria.

Utilizing Custom Scripts in Hooks

You can also call external scripts within your pre-push hook. For instance, you might have a separate shell or Python script for quality checks or deployment preparation steps:

#!/bin/sh

# Assuming validate.sh is an existing script with proper execution permissions.
if ! .scripts/validate.sh; then
  echo "Validation failed! Push denied."
  exit 1
fi

exit 0

Omitting the validation script’s output:

$ git push
Validation failed! Push denied.
error: failed to push some refs to 'repository url'

Error Handling and Notification

It’s critical to give clear feedback when a hook rejects a push. Expand upon the hook script to include error handling and user-friendly notifications:

#!/bin/sh

function notify {
  # You can use 'osascript' for MacOS, 'notify-send' for Linux, etc.
  # This example uses 'osascript' for MacOS notifications.
  osascript -e 'display notification "'$1'" with title "Git Pre-Push Hook"'
}

if ! npm test; then
  notify "Tests failed! Push denied."
  exit 1
fi

notify "Pre-push checks passed!"
exit 0

Integrating with Continuous Integration Systems

In cases where heavy lifting is offloaded onto a Continuous Integration (CI) system, you can minimalize the pre-push hook to just remind you to run or wait for CI tools:

#!/bin/sh

read -p "Have you remembered to run CI tools? (y/n)" -n 1 -r

if [[ $REPLY =~ ^[Yy]$ ]]; then
  exit 0
else
  echo "
Please run CI tools before pushing!"
  exit 1
fi

This example uses an interactive prompt to remind developers to use CI tools before pushing their code.

Conclusion

Understanding and leveraging Git pre-push hooks can significantly amplify your team’s efficiency and ensure high coding standards. From simple notifications to automated testing, and linting, these hooks act as the first line of defense against potential errors entering the codebase. Consistent application and regular updates to your pre-push hooks can lead to a smoother, more reliable development lifecycle.