Use Git hooks to translate this sentence into English.
Basic Concepts of Git Hooks
Git hooks are scripts that run automatically when specific Git events occur. They are stored in the .git/hooks
directory. When certain Git operations are performed, such as committing code or pushing changes, Git checks if corresponding hook scripts exist in this directory and executes them if found. Hooks are divided into two categories: client-side hooks and server-side hooks. Client-side hooks are triggered by local operations, while server-side hooks run on the server in response to network operations.
Each hook script must be an executable file. Git identifies them by their filenames. By default, the .git/hooks
directory contains some sample scripts with a .sample
extension. To enable them, remove the .sample
suffix.
Client-Side Hooks
Client-side hooks affect the local workflow of developers and can be categorized into commit workflow hooks, email workflow hooks, and other client-side hooks.
Pre-Commit Hook
The pre-commit
hook runs before the commit message is entered and is used to inspect the upcoming snapshot. For example, it can be used to run code style checks:
#!/bin/sh
# Run ESLint check
npm run lint
# If lint check fails, abort the commit
if [ $? -ne 0 ]; then
echo "Lint check failed. Please fix errors before committing."
exit 1
fi
Prepare-Commit-Msg Hook
This hook runs before the commit message editor is launched, allowing us to automatically generate commit message templates or modify the default message. For example:
#!/bin/sh
# Get the current branch name
BRANCH_NAME=$(git symbolic-ref --short HEAD)
# If the branch name contains a JIRA ID, add it to the commit message
if [[ $BRANCH_NAME =~ (PROJ-[0-9]+) ]]; then
echo "[${BASH_REMATCH[1]}] $(cat $1)" > $1
fi
Commit-Msg Hook
The commit-msg
hook takes one parameter, which is the path to a temporary file containing the commit message. It can be used to validate the commit message format:
#!/usr/bin/env node
const fs = require('fs');
const msg = fs.readFileSync(process.argv[2], 'utf8').trim();
const commitRegex = /^(revert: )?(feat|fix|docs|style|refactor|perf|test|chore)\(.+\): .{1,50}/;
if (!commitRegex.test(msg)) {
console.error(`Invalid commit message format: "${msg}"`);
console.error('Commit messages should follow: type(scope): description');
console.error('Example: feat(login): add login validation');
process.exit(1);
}
Server-Side Hooks
Server-side hooks execute when changes are pushed to the server and can be used to enforce project policies. These hooks run on the server, not on the developer's machine.
Pre-Receive Hook
The pre-receive
hook runs first when processing a push from a client and can be used to reject pushes that violate certain policies. For example, checking if the committer's email is a company email:
#!/bin/sh
while read oldrev newrev refname; do
# Get all commits in the push
commits=$(git rev-list $oldrev..$newrev)
for commit in $commits; do
email=$(git show -s --format='%ae' $commit)
if [[ ! "$email" =~ @company\.com$ ]]; then
echo "Error: Commit ${commit} uses non-company email ${email}"
exit 1
fi
done
done
Update Hook
The update
hook is similar to pre-receive
but runs once for each branch being updated. It can be used to ensure the main branch can only be updated via merge requests:
#!/bin/sh
refname="$1"
oldrev="$2"
newrev="$3"
# If it's the main branch and not a fast-forward update
if [ "$refname" = "refs/heads/main" ] && [ "$(git merge-base $oldrev $newrev)" != "$oldrev" ]; then
echo "Error: The main branch can only be updated via merge requests"
exit 1
fi
Practical Hook Examples
Automatically Run Tests
Run tests automatically before each commit to ensure no failing code is committed:
#!/bin/sh
# Run tests
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Please fix them before committing."
exit 1
fi
Prevent Committing Large Files
Prevent accidentally committing large files to the repository:
#!/usr/bin/env node
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const execSync = require('child_process').execSync;
const files = execSync('git diff --cached --name-only --diff-filter=ACM')
.toString()
.split('\n')
.filter(Boolean);
for (const file of files) {
const size = parseInt(execSync(`git cat-file -s ":${file}"`).toString(), 10);
if (size > MAX_FILE_SIZE) {
console.error(`Error: File ${file} is too large (${size} bytes)`);
console.error('Please use Git LFS or remove the file from the commit');
process.exit(1);
}
}
Automatically Increment Version Number
Automatically increment the version number in package.json
after each commit to the main branch:
#!/bin/sh
branch=$(git symbolic-ref --short HEAD)
if [ "$branch" = "main" ]; then
# Increment the patch version
npm version patch --no-git-tag-version
# Add the version change to the current commit
git add package.json
git commit --amend --no-edit
fi
Sharing and Managing Hooks
By default, hooks are stored in the .git/hooks
directory, which is not tracked by Git. To share hooks within a team, consider the following methods:
-
Store hooks in the project: Create a
hooks
directory in the project and document how to manually copy them to.git/hooks
in the README. -
Use tools like husky: Husky allows defining Git hooks in
package.json
:
{
"husky": {
"hooks": {
"pre-commit": "npm run lint",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
- Use the pre-commit framework: Create a
.pre-commit-config.yaml
file to define hooks:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
Debugging Hooks
Debugging Git hooks can be challenging because they run during Git command execution. Here are some debugging tips:
- Log output: Add logging to hook scripts:
#!/bin/sh
echo "pre-commit hook started" >> /tmp/git-hooks.log
date >> /tmp/git-hooks.log
- Run hooks manually: Test hooks by executing them directly:
./.git/hooks/pre-commit
- Temporarily disable hooks: Rename hook files while debugging:
mv .git/hooks/pre-commit .git/hooks/pre-commit.disabled
- Use
set -x
: Addset -x
to bash scripts to show executed commands:
#!/bin/sh
set -x # Start debug output
npm run lint
set +x # End debug output
Performance Considerations
Hook scripts can increase the time taken for Git operations, especially if they run complex checks or tests. To minimize impact on the development workflow:
-
Only check staged files: Use
git diff --cached
to get staged files instead of the entire project. -
Run tasks in parallel: Use tools to run checks in parallel if possible.
-
Cache results: Consider caching results for time-consuming operations.
-
Provide skip options: Allow skipping certain checks in emergencies:
#!/bin/sh
if [ -n "$SKIP_HOOKS" ]; then
echo "Skipping hook checks"
exit 0
fi
Usage example:
SKIP_HOOKS=1 git commit -m "Emergency fix"
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:重写提交历史