Git Hooks: Automating Git Tasks with Custom Scripts

Git hooks are a powerful feature that allows you to automate tasks during the lifecycle of a Git repository. Hooks are scripts that are triggered by specific Git events, such as making a commit or pushing code. They enable you to enforce coding standards, run tests, and other automated task

Git Hooks: Automating Git Tasks with Custom Scripts

Overview

Git hooks are a powerful feature that allows you to automate tasks during the lifecycle of a Git repository. Hooks are scripts that are triggered by specific Git events, such as making a commit or pushing code. They enable you to enforce coding standards, run tests, or perform any other automated task at various stages of the Git workflow. If you're new to Git hooks, I recommend starting with my previous post "Automating Tasks with Git Hooks: Code Linting and Running Tests" where I explain the basics and demonstrate how to use a pre-commit hook with a practical example. In this post, we'll focus on three commonly used hooks: pre-commit, post-commit, and pre-push.

In this post, you will learn:

  1. What Git hooks are and why they are useful.
  2. How to set up pre-commit, post-commit, and pre-push hooks.
  3. Common use cases for each type of hook.
  4. Best practices for using Git hooks effectively.

1. Introduction to Git Hooks

What Are Git Hooks?

Git hooks are custom scripts that Git executes when certain repository events occur. Each Git repository contains a .git/hooks directory that holds sample hook scripts (disabled by default). You can modify these scripts or create new ones to perform various tasks during Git operations.

Some common types of hooks include:

  • Pre-commit hook: Runs before a commit is made.
  • Post-commit hook: Runs after a commit has been completed.
  • Pre-push hook: Runs before pushing changes to a remote repository.

Why Use Git Hooks?

Git hooks provide a way to enforce rules, improve code quality, and automate tasks within your development workflow. Here are some common use cases:

  • Run linters and formatters before a commit (pre-commit hook).
  • Notify team members via email or Slack after a commit is made (post-commit hook).
  • Run tests and prevent pushing broken code (pre-push hook).

By using Git hooks, you can make your development process more efficient, minimize errors, and maintain consistent standards.

2. Setting Up Git Hooks

How Git Hooks Work

Git hooks are stored in the .git/hooks/ directory of each Git repository. When you initialize a new repository with git init, Git creates this directory and populates it with several example scripts (e.g., pre-commit.sample, post-commit.sample, etc.). These files are not active until you remove the .sample extension and make them executable.

To activate a hook:

  1. Navigate to the .git/hooks/ directory in your repository.
  2. Create a new file (or modify an existing sample script) for the specific hook, such as pre-commit.
  3. Add the necessary script logic.
  4. Ensure the script is executable by running:
chmod +x .git/hooks/pre-commit

Now, let's walk through setting up the pre-commit, post-commit, and pre-push hooks in detail.

3. Pre-Commit Hook: Ensuring Code Quality Before Committing

What Is the Pre-Commit Hook?

The pre-commit hook is triggered right before a commit is made. It’s commonly used to check the code for issues, run linters or formatters, and ensure the code meets certain standards before allowing the commit to proceed.

Use Cases for Pre-Commit Hook

  • Linting: Ensure code follows a specific style or standard.
  • Testing: Run unit tests or static analysis tools before committing.
  • Code Formatting: Automatically format code with tools like Prettier or Black.
  • Preventing Large Files: Ensure that large files or binary files are not accidentally committed.

Setting Up a Pre-Commit Hook

  1. Navigate to the .git/hooks/ directory:
cd .git/hooks
  1. Create a pre-commit script:
touch pre-commit
  1. Make the script executable:
chmod +x pre-commit
  1. Write the hook logic (example: running a linter and preventing large files from being committed):
#!/bin/sh

# Run a linter (for example, ESLint for JavaScript files)
echo "Running linter..."
eslint .

if [ $? -ne 0 ]; then
    echo "Linting failed. Commit aborted."
    exit 1
fi

# Prevent committing files larger than 5MB
max_size=5000000
for file in $(git diff --cached --name-only); do
    if [ $(wc -c <"$file") -gt $max_size ]; then
        echo "Error: $file is larger than 5MB. Commit aborted."
        exit 1
    fi
done

echo "Pre-commit checks passed!"

This script runs ESLint and checks for files larger than 5MB. If either condition fails, the commit is aborted.

Testing the Pre-Commit Hook

Try making a commit with changes. If the linter fails or a file exceeds the size limit, the commit will be rejected, and you'll need to resolve the issues before trying again.

4. Post-Commit Hook: Automating Tasks After Committing

What Is the Post-Commit Hook?

The post-commit hook runs after a commit has been successfully made. Unlike the pre-commit hook, it doesn’t affect the commit process but is ideal for tasks like notifications, logging, or triggering further actions such as continuous integration (CI) pipelines.

Use Cases for Post-Commit Hook

  • Logging: Add a log entry or notify team members that a commit has been made.
  • CI/CD Triggers: Trigger a CI/CD pipeline or deployment process.
  • Commit Notifications: Send a message to Slack, email, or another service.

Setting Up a Post-Commit Hook

  1. Navigate to the .git/hooks/ directory:
cd .git/hooks
  1. Create a post-commit script:
touch post-commit
  1. Make the script executable:
chmod +x post-commit
  1. Write the hook logic (example: logging the commit message):
#!/bin/sh

# Log the commit message to a file
echo "Logging commit..."
echo "Commit made on $(date):" >> commit-log.txt
git log -1 --pretty=%B >> commit-log.txt
echo "------------------------------" >> commit-log.txt

This script logs the commit message to a file named commit-log.txt after every commit.

5. Pre-Push Hook: Ensuring Code Quality Before Pushing

What Is the Pre-Push Hook?

The pre-push hook runs before code is pushed to a remote repository. It’s commonly used to enforce rules or run tests before allowing the push to proceed. For example, you can prevent a push if certain tests fail, ensuring that broken code is not pushed to the main repository.

Use Cases for Pre-Push Hook

  • Running Tests: Ensure all tests pass before pushing.
  • Preventing Pushing Certain Branches: Prevent pushes to specific branches like main or master.
  • Code Coverage: Enforce code coverage thresholds.

Setting Up a Pre-Push Hook

  1. Navigate to the .git/hooks/ directory:
cd .git/hooks
  1. Create a pre-push script:
touch pre-push
  1. Make the script executable:
chmod +x pre-push
  1. Write the hook logic (example: running tests before pushing):
#!/bin/sh

# Run tests
echo "Running tests..."
npm test

if [ $? -ne 0 ]; then
    echo "Tests failed. Push aborted."
    exit 1
fi

echo "Tests passed. Proceeding with push."

This script runs your test suite before allowing the push to continue. If the tests fail, the push will be aborted, ensuring that only working code is pushed to the repository.

6. Best Practices for Using Git Hooks

  1. Keep Hooks Simple: Hooks should run quickly and efficiently. Avoid overly complex scripts that could slow down the commit or push process.
  2. Version Control Your Hooks: While Git hooks are stored in the .git/hooks/ directory, they are not shared across team members by default. To ensure everyone on your team uses the same hooks, you can store them in your repository (e.g., in a hooks/ directory) and set up a script to copy them to the .git/hooks/ directory.
  3. Use Pre-Commit for Code Quality: Pre-commit hooks are especially useful for running linters, formatters, and other tools to maintain code quality before changes are committed.
  4. Automate Notifications with Post-Commit: Post-commit hooks are ideal for sending notifications or updating external systems, such as triggering CI pipelines or posting updates to Slack.
  5. Enforce Testing with Pre-Push: Pre-push hooks are invaluable for ensuring that tests are passing before code is pushed to the remote repository, which helps maintain the stability of shared branches.

Conclusion

Git hooks provide a powerful way to automate tasks and enforce best practices throughout your Git workflow. By using pre-commit, post-commit, and pre-push hooks, you can ensure code quality, streamline your processes, and make your team more efficient.

In this post, we covered:

  1. Setting up pre-commit hooks to run linters and prevent large files.
  2. Creating post-commit hooks to log commit messages or trigger notifications.
  3. Using pre-push hooks to run tests and prevent broken code from being pushed.

By incorporating Git hooks into your workflow, you'll improve code quality, catch errors earlier, and create a more streamlined development process.

Read next

Exploring Commits, Blobs, Trees, and Tags in Git

Git is a powerful version control system built on four core objects: commits, blobs, trees, and tags. These objects are fundamental to Git’s storage model, and understanding them provides valuable insights into how Git operates.

Step-by-Step with Git: Advanced Commit Strategies

Let’s use a practical example to guide us through this process. By the end of this tutorial, you'll understand how to commit changes selectively, review modifications, and manage staged and unstaged changes.

Using Git Stash in Multi-Branch Workflows

You're likely to work with multiple branches for different features, bug fixes, and releases. While managing these branches, you might run into situations where you need to temporarily save your work without committing it, and later apply it on the same or even a different branch.