Automate Your Workflow: Getting Started with Git Hooks

Git hooks are powerful, customizable scripts that Git executes automatically before or after certain events, such as committing, pushing, or merging. They allow developers to automate tasks, enforce policies, and maintain code quality directly within their version control workflow. Think of them as event listeners for your Git repository.

Where Do Git Hooks Live?

Every Git repository initializes a .git/hooks directory. Inside, you'll find example scripts (e.g., pre-commit.sample, post-merge.sample) that demonstrate how hooks work. To activate a hook, you simply need to remove the .sample extension from the desired script file and make it executable.

For example, to activate the pre-commit hook:
1. Navigate to your repository's .git/hooks directory.
2. Rename pre-commit.sample to pre-commit.
3. Make the script executable: chmod +x .git/hooks/pre-commit

Types of Git Hooks

Git hooks are broadly categorized into two types:

1. Client-Side Hooks: These run on your local machine and affect your local repository operations.
* pre-commit: Runs before a commit is finalized. Ideal for linting, code formatting, or running tests. If this script exits with a non-zero status, the commit is aborted.
* prepare-commit-msg: Runs before the commit message editor is launched. Can be used to inject standard messages or templates.
* commit-msg: Runs after the commit message has been created, but before the commit is finalized. Great for validating commit message formats (e.g., Conventional Commits).
* post-commit: Runs after a commit has been made. Useful for notification, logging, or updating external systems.
* pre-rebase: Runs before a rebase operation. Can prevent rebasing on specific branches.
* post-merge: Runs after a successful git merge command. Useful for installing dependencies (npm install, composer install) or regenerating documentation.

2. Server-Side Hooks: These run on the Git server and affect repository operations like pushes. They are crucial for enforcing project-wide policies.
* pre-receive: Runs when a push is received but before any references are updated. Can reject pushes based on branch names, commit content, or user permissions.
* update: Similar to pre-receive, but runs once for each pushed reference. Allows for granular control over which branches can be updated.
* post-receive: Runs after a push has completed and all references are updated. Commonly used for continuous integration (CI) triggers, sending notifications, or updating project management tools.

Practical Examples

Let's look at some common use cases for client-side hooks.

1. Enforcing Code Style with pre-commit

A pre-commit hook can automatically run a linter or formatter before a commit, ensuring all code adheres to project standards.

Create or edit .git/hooks/pre-commit:

Bash:
#!/bin/sh

# Stash uncommitted changes to avoid linting them
git stash -q --keep-index

# Run your linter/formatter (e.g., ESLint, Prettier, Black, PHP_CodeSniffer)
# Example for JavaScript with ESLint:
echo "Running ESLint..."
if ! npx eslint --cache --fix . ; then
  echo "ESLint found issues. Please fix them before committing."
  git stash pop -q # Restore stashed changes
  exit 1 # Abort the commit
fi

# Example for Python with Black:
# echo "Running Black formatter..."
# if ! black . ; then
#   echo "Black found issues. Please fix them before committing."
#   git stash pop -q
#   exit 1
# fi

# If the linter fixes files, add them to the commit
git add .

# Restore stashed changes
git stash pop -q

exit 0 # Allow the commit to proceed
Make sure npx eslint (or your chosen command) is available in your PATH. If the linter finds errors and exits with a non-zero status, the commit will be blocked, prompting you to fix the issues.

2. Validating Commit Messages with commit-msg

Ensure all commit messages follow a specified format (e.g., Conventional Commits) to maintain a clean and understandable commit history.

Create or edit .git/hooks/commit-msg:

Bash:
#!/bin/sh

# Path to the commit message file
COMMIT_MSG_FILE=$1

# Regular expression for Conventional Commits (e.g., 'type(scope): subject')
# Example: feat(auth): add user login
# Example: fix: correct typo in README
COMMIT_MSG_REGEX="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .{1,50}"

if ! grep -Eq "$COMMIT_MSG_REGEX" "$COMMIT_MSG_FILE"; then
  echo "Error: Invalid commit message format."
  echo "Please follow Conventional Commits specification (e.g., 'type(scope): subject')."
  echo "Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert."
  echo "Example: feat(auth): add user login functionality"
  exit 1
fi

exit 0

3. Automatically Installing Dependencies after post-merge

After merging a branch that might introduce new dependencies, this hook can automatically run your package manager.

Create or edit .git/hooks/post-merge:

Bash:
#!/bin/sh

# Check if package.json has changed (for npm/yarn)
if git diff --name-only HEAD@{1} HEAD | grep -q "package.json"; then
  echo "package.json changed, running npm install..."
  npm install
fi

# Check if composer.json has changed (for PHP Composer)
if git diff --name-only HEAD@{1} HEAD | grep -q "composer.json"; then
  echo "composer.json changed, running composer install..."
  composer install
fi

exit 0

Managing Hooks in a Team

The .git/hooks directory is local to each repository and is *not* version controlled. This means hooks aren't automatically shared when you clone a repo. For team environments, consider these options:

  • core.hooksPath: Git 2.9+ allows you to configure a global hooks directory for all repositories or a project-specific one that *is* version controlled. You could create a githooks directory in your project root, add your scripts there, and then run git config core.hooksPath githooks for each developer.
  • Third-party tools:
* Husky: A popular npm package that makes it easy to set up and manage Git hooks for JavaScript/TypeScript projects. It allows you to define hooks directly in your package.json.
* Pre-commit framework: A language-agnostic framework that manages pre-commit hooks. You define hooks in a .pre-commit-config.yaml file, and pre-commit handles installing and running them.

Bypassing Hooks

Sometimes you need to bypass a hook (e.g., committing a quick fix without waiting for a full lint). You can do this with the --no-verify (or -n) flag:

Bash:
git commit -m "Quick fix" --no-verify
This will skip the pre-commit and commit-msg hooks.

Git hooks are an indispensable tool for maintaining consistency, quality, and automation in any development workflow. By leveraging them effectively, you can significantly streamline your team's development process.
 

Related Threads

Who Read This Thread (Total Members: 1)

Personalization

Style editor

Settings Colours

  • Mobile users cannot use these functions.

    Alternate header

    You can easily choose the alternate header layout for a different look.

    View Mode Selection

    Switch between full screen and narrow screen modes.

    Grid View

    Browse content easily with grid mode and get a tidy look.

    Image Grid Mode

    Display your content in a tidy and visually rich way with background images.

    Close Sidebar

    Create a wider working area by hiding the sidebar.

    Sticky Sidebar

    Keep constant access by pinning the sidebar and manage your content easily.

    Box view

    You can add a box-style frame to the sides of your style or remove the existing frame. Applies to resolutions above 1300px.

    Corner Rounding Control

    Customize the look as you like by turning the corner rounding effect on and off.

  • Choose your colour

    Choose the colour that reflects your style and achieve aesthetic harmony.

Back
QR Code