Hook execution mechanism
Hook Execution Mechanism
Git hooks are scripts that automatically trigger when specific events occur, used to customize or extend Git workflows. These scripts are located in the .git/hooks
directory and include a series of sample files by default. Modifying or creating these files enables automation. Hooks are divided into client-side hooks and server-side hooks, which operate on local and remote repositories, respectively.
Types of Hooks and Trigger Timing
Git hooks are primarily categorized into two types: client-side hooks and server-side hooks. Client-side hooks are triggered by local operations, such as committing code or merging branches, while server-side hooks execute when a remote repository receives a push.
Client-Side Hooks
- pre-commit: Runs before the commit message is entered, often used for code style checks or running tests.
- prepare-commit-msg: Executes after the default commit message is created but before the editor launches.
- commit-msg: Accepts a file path parameter containing the commit message, used to validate the message format.
- post-commit: Triggers after the entire commit process completes.
#!/bin/sh
# pre-commit example: Check for debug statements
if git diff --cached | grep 'console.log'; then
echo "Debug statements detected. Please remove them before committing."
exit 1
fi
Server-Side Hooks
- pre-receive: Triggers first when handling push operations from clients.
- update: Similar to pre-receive but runs once for each pushed branch.
- post-receive: Executes after the push operation completes, useful for notifying CI systems.
Creating and Configuring Hooks
By default, the .git/hooks
directory in a Git repository contains sample scripts with a .sample
suffix. To enable a hook, remove the .sample
suffix and ensure the script has executable permissions.
# Enable the pre-commit hook
mv .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
For complex hook logic, consider using scripting languages like Node.js:
#!/usr/bin/env node
// commit-msg hook: Validate commit message format
const fs = require('fs');
const msg = fs.readFileSync(process.argv[2], 'utf-8').trim();
const commitRegex = /^(feat|fix|docs|style|refactor|test|chore)\(.*\): .+/;
if (!commitRegex.test(msg)) {
console.error(`Invalid commit message format: "${msg}"`);
console.error('Example: feat(authentication): Add login functionality');
process.exit(1);
}
Hook Execution Environment and Limitations
Git hooks execute in a specific environment, with the following considerations:
- The working directory of a hook script is always the root of the Git repository.
- Standard input may be redirected; interactive input cannot be relied upon.
- The exit status code determines whether the Git operation continues: 0 indicates success, while non-zero aborts the operation.
For shared hooks, consider the following approaches:
- Store hook scripts in the project directory (e.g.,
scripts/hooks
). - Use symbolic links or installation scripts to copy them to
.git/hooks
. - Manage Git hooks using tools like husky.
# Example of creating a symbolic link
ln -s ../../scripts/hooks/pre-commit .git/hooks/pre-commit
Advanced Hook Use Cases
Managing Multiple Hooks
Large projects may require organizing multiple hook scripts, which can be invoked by a main hook:
#!/bin/sh
# Main pre-commit hook
./scripts/hooks/linter.sh && \
./scripts/hooks/tests.sh && \
./scripts/hooks/security-check.sh
Conditional Execution
Sometimes, hooks should execute based on the environment or branch:
#!/usr/bin/env node
// Perform strict checks only on the master branch
const branch = require('child_process').execSync('git symbolic-ref --short HEAD').toString().trim();
if (branch === 'master') {
// Perform strict validation
} else {
process.exit(0);
}
Cross-Platform Compatibility
For Shell scripts, consider cross-platform compatibility by specifying the interpreter at the beginning:
#!/usr/bin/env bash
# Use env to locate bash, improving portability
Debugging and Troubleshooting Hooks
Debugging Git hooks can be challenging because they run during Git command execution. The following methods can help:
- Add debug output to scripts.
- Capture and log standard error.
- Enable Shell script debug mode with
set -x
.
#!/bin/bash
# Enable debug mode
set -x
# Hook logic...
echo "Debug info" >&2
# Disable debug mode
set +x
For complex debugging scenarios, temporarily redirect output to a file:
exec 1> /tmp/git-hook-debug.log 2>&1
echo "Starting hook execution at $(date)"
Performance Considerations for Hooks
Hook execution can increase Git operation latency, especially for time-consuming tasks. Optimization suggestions include:
- Keep frequently triggered hooks (e.g., pre-commit) lightweight.
- Move time-consuming checks to background processes or CI systems.
- Use caching to avoid redundant calculations.
#!/bin/bash
# Optimize ESLint checks with caching
changed_files=$(git diff --cached --name-only --diff-filter=ACM "*.js" | tr '\n' ' ')
if [ -z "$changed_files" ]; then
exit 0
fi
cache_file="/tmp/eslint-cache-$(git rev-parse HEAD)"
if [ -f "$cache_file" ]; then
exit 0
fi
./node_modules/.bin/eslint $changed_files
touch "$cache_file"
Security Considerations
Git hook scripts have the same permissions as the executing user. Special attention is required:
- Do not copy hook scripts from untrusted sources.
- Regularly review hook script content.
- Consider requiring confirmation before sensitive operations.
- Restrict execution permissions for server-side hooks.
#!/bin/bash
# Confirm before dangerous operations
read -p "About to execute database migration. Continue? [y/N] " confirm
if [[ $confirm != [yY] ]]; then
echo "Operation canceled."
exit 1
fi
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn