Back to Git & Dev Tools
2026-01-039 min read

Git Hooks (Git & Dev Tools)

Learn Git Hooks (Git & Dev Tools) step by step with clear examples and exercises.

Title: Git Hooks - A full guide for Developers (Git & Dev Tools)

Why This Matters

In software development, maintaining a clean and consistent codebase is crucial. Git hooks are an essential tool that helps developers enforce coding standards, automate repetitive tasks, and prevent common mistakes. Whether you're working on a personal project or collaborating with a team, understanding and using Git hooks can significantly improve your productivity and the overall quality of your code.

By implementing custom Git hooks, you can:

  1. Automate tedious tasks like formatting code or running tests before committing changes.
  2. Enforce coding standards across your entire team, ensuring a consistent style and easier collaboration.
  3. Prevent mistakes that might otherwise slip through the cracks, such as forgetting to add or commit files.
  4. Save time by automating repetitive tasks, allowing you to focus on more important aspects of development.
  5. Improve the quality of your codebase by catching and addressing issues early in the development process.

Prerequisites

Before diving into Git hooks, it's important to have a good understanding of:

  1. Git: Familiarity with basic Git commands such as git init, git add, git commit, and git push. If you're new to Git, consider reading our Git Basics tutorial first.
  2. Shell Scripting: Git hooks are written in shell scripts, so some knowledge of shell commands will be helpful. If you're not familiar with shell scripting, check out our Linux Shell Scripting tutorial.
  3. Git Configuration: Understanding how to configure Git settings, such as user name and email, is essential for using Git hooks effectively. You can learn more about this in the Git Basics tutorial.

Core Concept

What are Git Hooks?

Git hooks are scripts that run automatically when certain events occur within a Git repository. These events include:

  1. pre-commit: Runs before committing changes to the repository
  2. prepare-commit-msg: Modifies the commit message before it's saved
  3. commit-msg: Runs after the commit message is saved but before the commit is made
  4. post-commit: Runs immediately after a successful commit
  5. pre-rebase: Runs before starting a rebase
  6. post-checkout, post-merge, and post-rewind: Run after checking out a branch, merging branches, or reverting changes, respectively

These hooks are located in the .git/hooks directory of your Git repository and are written as shell scripts (.sh files). By default, Git provides several example hook scripts in this directory when you initialize a new repository.

Customizing Git Hooks

To customize or create your own Git hooks, follow these steps:

  1. Create a new script file in the .git/hooks directory with the appropriate name (e.g., pre-commit, prepare-commit-msg, etc.).
  2. Make the script executable by running the following command in your terminal while in the repository directory:
chmod +x .git/hooks/[hook_script_name]
  1. Write your custom logic inside the script file, using shell commands to interact with the Git repository as needed.
  2. Test your hook by triggering the appropriate event (e.g., running git commit for a pre-commit hook). If the hook doesn't run or fails, check that it is executable and that the script contains valid shell commands.

Example Git Hook: Enforcing a Coding Standard

Let's create a simple pre-commit hook that checks if the codebase adheres to a specific coding standard (e.g., checking for tabs vs spaces). First, create a new file called pre-commit in the .git/hooks directory:

#!/bin/sh

Check for tabs instead of spaces

if grep -q '\t' ./*; then

echo "Error: Tabs found in the codebase!"

exit 1

fi

Add your custom logic here to enforce other coding standards

exit 0


Make the script executable and test it by committing changes that include tabs:

chmod +x .git/hooks/pre-commit

echo "Tab test" > test.txt # Create a file with tabs

git add test.txt # Add the file to the Git index

git commit -m "Test commit" # Trigger the pre-commit hook


If the hook is working correctly, it will prevent the commit and display an error message:

Error: Tabs found in the codebase!


### Example Git Hook: Automating Code Formatting

In this example, we'll create a `pre-commit` hook that automatically formats Python code using the Black formatter. First, install Black if you haven't already:

pip install black


Next, create a new file called `pre-commit` in the `.git/hooks` directory:

#!/bin/sh

Check for Python files and format them using Black

for file in ./*.py; do

if [ -f "$file" ]; then

black $file --check --diff

if [ $? -ne 0 ]; then

echo "Error: Black formatting failed for $file"

exit 1

fi

fi

done

exit 0


Make the script executable and test it by committing changes with unformatted Python code:

chmod +x .git/hooks/pre-commit

cat > main.py << EOF

def func():

print("Hello, world!")

EOF

git add main.py

git commit -m "Test commit"


If the hook is working correctly, it will format your Python code before committing and display no output:

Black formatting applied automatically


### Customizing Git Hooks for Multiple Repositories

To run Git hooks across multiple repositories, you can create a shell script that loops through each repository and runs the desired hook. You might also consider using tools like `git-hooks` or `pre-commit` to manage your hooks more easily.

Common Mistakes

  1. Forgetting to make the script executable: Remember to run chmod +x .git/hooks/[hook_script_name] after creating a new hook script.
  2. Not testing the hook: Always test your custom Git hooks by triggering the appropriate event and checking that they work as expected.
  3. Ignoring shell syntax errors: Ensure your scripts are written correctly and free of syntax errors, or they will not run as intended.
  4. Not handling edge cases: Consider potential edge cases in your hook logic and adjust your script accordingly to handle them gracefully.
  5. Overcomplicating hooks: Keep your Git hooks simple and focused on a single task. Complex scripts can be harder to maintain and debug.
  6. Not committing hook scripts: Don't forget to add and commit your custom Git hooks to the repository so that they are available for all collaborators.
  7. Ignoring built-in hooks: While you can override built-in Git hooks, it's generally best to leave them in place unless there's a compelling reason to modify their behavior.

Worked Example

Enforcing a Coding Standard with a Custom pre-commit Hook

Let's create a more complex pre-commit hook that checks for multiple coding standards, including checking for the correct number of blank lines between functions and enforcing a maximum line length. First, create a new file called pre-commit in the .git/hooks directory:

#!/bin/sh

Check for tabs instead of spaces

if grep -q '\t' ./*; then

echo "Error: Tabs found in the codebase!"

exit 1

fi

Ensure there are at least two blank lines between functions

for file in ./*.py; do

if [ -f "$file" ]; then

awk '/def\s+/,/^$/{if (++count > 2) print "Error: Function definitions must have at least two blank lines between them"; exit 1}' $file

fi

done

Enforce a maximum line length of 80 characters

for file in ./*.py; do

if [ -f "$file" ]; then

awk 'NF > 80 {print "Error: Line too long (line " NR " in " $file ")"; exit 1}' $file

fi

done

exit 0


Make the script executable and test it by committing changes that violate these coding standards:

chmod +x .git/hooks/pre-commit

cat > main.py << EOF

def func1():

print("Hello, world!")

def func2():

pass

EOF

git add main.py

git commit -m "Test commit"


If the hook is working correctly, it will prevent the commit and display error messages:

Error: Function definitions must have at least two blank lines between them

Error: Line too long (line 3 in main.py)

Common Mistakes

  1. Forgetting to make the script executable: Remember to run chmod +x .git/hooks/[hook_script_name] after creating a new hook script.
  2. Not testing the hook: Always test your custom Git hooks by triggering the appropriate event and checking that they work as expected.
  3. Ignoring shell syntax errors: Ensure your scripts are written correctly and free of syntax errors, or they will not run as intended.
  4. Not handling edge cases: Consider potential edge cases in your hook logic and adjust your script accordingly to handle them gracefully.
  5. Overcomplicating hooks: Keep your Git hooks simple and focused on a single task. Complex scripts can be harder to maintain and debug.
  6. Not committing hook scripts: Don't forget to add and commit your custom Git hooks to the repository so that they are available for all collaborators.
  7. Ignoring built-in hooks: While you can override built-in Git hooks, it's generally best to leave them in place unless there's a compelling reason to modify their behavior.
  8. Not documenting your hooks: Document your custom Git hooks with comments explaining their purpose and behavior. This will help others understand what the hook does and how to use or modify it.
  9. Not using descriptive names for your scripts: Use descriptive names for your hook scripts that clearly indicate their purpose, such as pre-commit-check-coding-standards instead of just pre-commit.
  10. Not updating your hooks when changing coding standards: If you change your coding standards, make sure to update your Git hooks accordingly so that they continue to enforce the new rules.

Practice Questions

  1. Write a pre-commit hook that checks if all JavaScript files in the repository have a license comment at the top.
  2. Create a prepare-commit-msg hook that appends a timestamp to the commit message.
  3. Write a post-checkout hook that automatically installs dependencies for your project after checking out a new branch.
  4. Develop a pre-rebase hook that checks if there are any uncommitted changes before starting a rebase. If there are, it should display an error message and prevent the rebase from proceeding.
  5. Write a post-commit hook that sends a notification to your team when a commit is made.
  6. Create a prepare-commit-msg hook that checks for duplicate commit messages in the repository's history and requires users to enter a unique message if duplicates are found.
  7. Develop a post-merge hook that automatically creates a new branch for each merged branch, preserving the merge history.
  8. Write a custom Git hook that ensures all Python files have tests covering at least 80% of their lines and prevents commits if this condition is not met.
  9. Create a pre-commit hook that checks if there are any unused imports in your Python codebase and displays a warning or prevents the commit based on your team's preferences.
  10. Develop a post-checkout hook that automatically formats your project's files using the appropriate formatter for each language (e.g., Black for Python, Prettier for JavaScript).

FAQ

  1. Can I use other programming languages for Git hooks instead of shell scripts?
  • While shell scripts are the default choice for Git hooks, you can write them in any language that can be executed on the command line. However, Note that that not all platforms support non-shell script Git hooks out of the box.
  1. How do I disable a built-in Git hook?
  • To disable a built-in Git hook, you can rename or remove the file from the .git/hooks directory. Be aware that disabling essential hooks like pre-commit
Git Hooks (Git & Dev Tools) | Git & Dev Tools | XQA Learn