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:
- Automate tedious tasks like formatting code or running tests before committing changes.
- Enforce coding standards across your entire team, ensuring a consistent style and easier collaboration.
- Prevent mistakes that might otherwise slip through the cracks, such as forgetting to add or commit files.
- Save time by automating repetitive tasks, allowing you to focus on more important aspects of development.
- 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:
- Git: Familiarity with basic Git commands such as
git init,git add,git commit, andgit push. If you're new to Git, consider reading our Git Basics tutorial first. - 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.
- 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:
pre-commit: Runs before committing changes to the repositoryprepare-commit-msg: Modifies the commit message before it's savedcommit-msg: Runs after the commit message is saved but before the commit is madepost-commit: Runs immediately after a successful commitpre-rebase: Runs before starting a rebasepost-checkout,post-merge, andpost-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:
- Create a new script file in the
.git/hooksdirectory with the appropriate name (e.g.,pre-commit,prepare-commit-msg, etc.). - Make the script executable by running the following command in your terminal while in the repository directory:
chmod +x .git/hooks/[hook_script_name]
- Write your custom logic inside the script file, using shell commands to interact with the Git repository as needed.
- Test your hook by triggering the appropriate event (e.g., running
git commitfor apre-commithook). 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
- Forgetting to make the script executable: Remember to run
chmod +x .git/hooks/[hook_script_name]after creating a new hook script. - Not testing the hook: Always test your custom Git hooks by triggering the appropriate event and checking that they work as expected.
- Ignoring shell syntax errors: Ensure your scripts are written correctly and free of syntax errors, or they will not run as intended.
- Not handling edge cases: Consider potential edge cases in your hook logic and adjust your script accordingly to handle them gracefully.
- Overcomplicating hooks: Keep your Git hooks simple and focused on a single task. Complex scripts can be harder to maintain and debug.
- 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.
- 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
- Forgetting to make the script executable: Remember to run
chmod +x .git/hooks/[hook_script_name]after creating a new hook script. - Not testing the hook: Always test your custom Git hooks by triggering the appropriate event and checking that they work as expected.
- Ignoring shell syntax errors: Ensure your scripts are written correctly and free of syntax errors, or they will not run as intended.
- Not handling edge cases: Consider potential edge cases in your hook logic and adjust your script accordingly to handle them gracefully.
- Overcomplicating hooks: Keep your Git hooks simple and focused on a single task. Complex scripts can be harder to maintain and debug.
- 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.
- 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.
- 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.
- 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-standardsinstead of justpre-commit. - 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
- Write a
pre-commithook that checks if all JavaScript files in the repository have a license comment at the top. - Create a
prepare-commit-msghook that appends a timestamp to the commit message. - Write a
post-checkouthook that automatically installs dependencies for your project after checking out a new branch. - Develop a
pre-rebasehook 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. - Write a
post-commithook that sends a notification to your team when a commit is made. - Create a
prepare-commit-msghook that checks for duplicate commit messages in the repository's history and requires users to enter a unique message if duplicates are found. - Develop a
post-mergehook that automatically creates a new branch for each merged branch, preserving the merge history. - 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.
- Create a
pre-commithook 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. - Develop a
post-checkouthook that automatically formats your project's files using the appropriate formatter for each language (e.g., Black for Python, Prettier for JavaScript).
FAQ
- 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.
- 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/hooksdirectory. Be aware that disabling essential hooks likepre-commit