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

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

Overview

Rebasing is a powerful feature of Git that allows developers to integrate changes from one branch into another, providing a cleaner, linear history. However, it can be dangerous if not managed carefully. This is where the ‘pre-rebase’ hook comes in handy. In this tutorial, we’ll delve into the practicality of using the ‘pre-rebase’ hook in Git with clear and helpful examples.

Understanding Git Hooks

Git hooks are scripts that Git executes before or after events such as commit, push, and rebase. They are used for a variety of reasons, to enforce code standards, run tests, or even to alert you of certain conditions before an operation completes. To find these hooks, you navigate to ‘.git/hooks’ in your Git repository, where you will find a range of sample hooks that Git provides.

What is a ‘pre-rebase’ Hook?

The ‘pre-rebase’ hook is a script that runs automatically right before the ‘git rebase’ operation begins. It’s primary to ensure that the rebase operation is necessary and safe to proceed. If the hook exits with a non-zero status, the rebase will be aborted.

Setting Up a ‘pre-rebase’ Hook

Let’s start by setting up a simple ‘pre-rebase’ hook. We will create a script to check if the branch upon which you’re attempting to rebase is ‘main’ or ‘master’.

#!/bin/sh

BRANCH_NAME=$(git symbolic-ref --short HEAD)

if [ "$BRANCH_NAME" = "master" ] || [ "$BRANCH_NAME" = "main" ]; then
  echo "You are on the main branch, rebasing here is risky and not allowed."
  exit 1
fi

exit 0

To do this, navigate to your ‘.git/hooks’ directory, create a file named ‘pre-rebase’, paste the above code, and make it executable with ‘chmod +x pre-rebase’.

Preventing Rebase on Specific Branches

This example builds on our previous script to prevent rebasing on any branch listed in a text file, say ‘protected-branches.txt’, which could include ‘main’, ‘development’, etc.

#!/bin/sh

PROTECTED_BRANCHES=$(cat /path/to/protected-branches.txt)
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)

for BRANCH in $PROTECTED_BRANCHES; do
  if [ "$CURRENT_BRANCH" = "$BRANCH" ]; then
    echo "Rebasing is not allowed on $CURRENT_BRANCH."
    exit 1
  fi
done

exit 0

This ‘pre-rebase’ hook loops through a list of protected branches, and if the current branch is in that list, it prevents the rebase operation.

Ensuring Code Quality with Linters

The following example demonstrates a pre-rebase hook for running code linters against changes in your feature branch that are about to be rebased onto another branch. This could help ensure code quality and style consistency before integrating changes.

#!/bin/sh

FEATURE_BRANCH=$(git symbolic-ref --short HEAD)
BASE_BRANCH=$1

if [ "$FEATURE_BRANCH" != "main" ] && [ "$FEATURE_BRANCH" != "master" ]; then
  git stash -q --keep-index
  linter_output=$(npm run lint)
  git stash pop -q
  if [ ! -z "$linter_output" ]; then
    echo "Lint errors detected, please fix them before rebasing:"
    echo "$linter_output"
    exit 1
  fi
fi

exit 0

This script temporarily stashes changes, runs a linter on your staged modifications, then unstashes. If lint errors are found, the rebase is aborted, prompting you to fix the issues first.

Enforcing Test Suite Pass Before Rebase

Just like running linters, we can also enforce passing of tests before allowing rebase. Here’s a hook that runs your project’s test suite using ‘npm’ or any other package manager.

#!/bin/sh

test_output=$(npm test)
retval=$?
if [ $retval -ne 0 ]; then
  echo "Unit tests failed, please fix them before rebasing:"
  echo "$test_output"
  exit $retval
fi

exit 0

This script captures both the output and exit status of the ‘npm test’ command, preventing the rebase if tests fail.

Conclusion

With careful implementation, ‘pre-rebase’ hooks can greatly enhance the safety and quality of your codebases during rebases. We’ve looked at basic setups like checking the branch you’re on to more advanced workflows like running linters or tests. By using these examples, developers can build robust workflows that helps maintain a clean, stable, and high-quality codebase.