Skip to content

hooks Git Command Guide

The githooks system provides a way to automatically trigger custom scripts at key points in Git operations. Hooks enable automation of workflows, enforcement of policies, and integration with external tools when performing Git operations.

HookDescriptionTrigger Point
pre-commitBefore commit creationgit commit
prepare-commit-msgCommit message templategit commit
commit-msgCommit message validationgit commit
post-commitAfter commit creationgit commit
pre-merge-commitBefore merge commit creationgit merge
post-mergeAfter merge completiongit merge
pre-applypatchBefore applying patchgit am
applypatch-msgPatch message validationgit am
post-applypatchAfter applying patchgit am
pre-rebaseBefore rebase beginsgit rebase
post-rewriteAfter history rewriting commandsgit rebase/cherry-pick
post-checkoutAfter checkout operationsgit checkout
post-mergeAfter successful mergegit pull/merge
HookDescriptionTrigger Point
pre-receiveBefore any ref updategit push
updateBefore each ref updategit push
post-receiveAfter all ref updates completegit push
post-updateAfter repository update completesgit push
push-to-checkoutHandle push-to-deploygit push
HookDescriptionUse Case
pre-auto-gcBefore automatic garbage collectionRepository maintenance
post-rewriteAfter rewriting operationsHistory modification tracking
Terminal window
# Make hook executable
chmod +x .git/hooks/pre-commit
# Create custom hook script
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# Pre-commit hook logic here
echo "Running pre-commit checks..."
EOF
chmod +x .git/hooks/pre-commit
Terminal window
# Copy from template directory
cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
# Or create new hook
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/bash
# Validate commit message format
if ! grep -q "ISSUE-[0-9]\+:" "$1"; then
echo "Commit message must include ISSUE-NNNN: format"
exit 1
fi
EOF
chmod +x .git/hooks/commit-msg
Terminal window
# Configure global hooks path
git config --global core.hooksPath ~/.git-hooks
# Create global hooks directory
mkdir ~/.git-hooks
# Place hooks in ~/.git-hooks/
Terminal window
# Skip pre-commit and commit-msg hooks
git commit --no-verify -m "message"
# Environment variable method
GIT_EDITOR=true git commit --amend
# Skip hooks in scripts
export GIT_COMMITTER_NAME="CI System"
git commit -m "Automated commit"
  • Pre-receive/update/post-receive: Executed in $GIT_DIR (bare repository)
  • All others: Executed in repository root (working directory for non-bare)
Terminal window
# Always available
GIT_DIR # Path to .git directory
GIT_WORK_TREE # Path to working directory (when set)
# For push operations
GIT_PROTOCOL # Protocol used (http, ssh, etc.)
GIT_PACKAGE_URL # Alternate Git server URL
# Custom environment detection
if [ -n "$CI" ] || [ -n "$CONTINUOUS_INTEGRATION" ]; then
echo "Running in CI environment, adjust hook behavior"
fi
Terminal window
# Standard input (for pre-receive/update)
while read old_sha new_sha ref_name; do
echo "Updating $ref_name: $old_sha -> $new_sha"
# Validate changes
done
# Standard output (for messages)
echo "Hook completed successfully"
# Exit codes
exit 0 # Success
exit 1 # Failure (reject operation)
exit 65 # Hook not applicable
.git/hooks/pre-commit
#!/bin/bash
# Run linting
if command -v eslint >/dev/null; then
if ! eslint --ext .js,.jsx,.ts,.tsx .; then
echo "ESLint failed. Fix issues before committing."
exit 1
fi
fi
# Run tests
if [ -f "package.json" ]; then
npm test || exit 1
fi
# Check for debug statements
if grep -r "console\.log\|debugger" --exclude-dir=node_modules .; then
echo "Found debug statements. Remove before committing."
exit 1
fi
.git/hooks/commit-msg
#!/bin/bash
COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat $COMMIT_MSG_FILE)
# Check for minimum length
if [ ${#COMMIT_MSG} -le 10 ]; then
echo "Commit message too short. Minimum 10 characters."
exit 1
fi
# Check for issue number pattern
if ! echo "$COMMIT_MSG" | grep -q "ISSUE-[0-9]\+:"; then
echo "Commit message must contain ISSUE-NNNN: pattern"
exit 1
fi
.git/hooks/pre-receive
#!/bin/bash
while read old_sha new_sha ref_name; do
# Protect master branch
if [ "$ref_name" = "refs/heads/master" ]; then
echo "Direct pushes to master not allowed. Use pull requests."
exit 1
fi
# Check user permissions for branch creation
if [[ "$ref_name" =~ ^refs/heads/feature/ ]] && [[ -z "$old_sha" ]]; then
# New branch creation logic
echo "Creating feature branch: ${ref_name#refs/heads/feature/}"
fi
done
.git/hooks/post-receive
#!/bin/bash
while read old_sha new_sha ref_name; do
if [ "$ref_name" = "refs/heads/master" ]; then
echo "Deploying to production..."
cd /path/to/deploy/dir
# Pull latest changes
git pull origin master
# Restart services
systemctl restart myapp
systemctl reload nginx
echo "Deployment completed successfully"
fi
done
#!/bin/bash
# pre-commit - Run quality checks
STASH_NAME="pre-commit-$(date +%s)"
if git diff --cached --name-only | grep -q .; then
git stash push -q --keep-index -m "$STASH_NAME"
STASHED=1
fi
# Run checks
npm run lint || exit 1
npm test || exit 1
if [ -n "$STASHED" ]; then
git stash pop -q
fi
#!/bin/bash
# pre-receive - Enforce review requirements
while read old_sha new_sha ref_name; do
# Skip checks for maintenance branches
if [[ "$ref_name" =~ ^refs/heads/(main|develop|release/) ]]; then
continue
fi
# Check if commit has been reviewed
# (Integration with external review system)
review_status=$(curl -s "$REVIEW_API/commits/$new_sha")
if [ "$review_status" != "approved" ]; then
echo "Commit requires code review approval"
exit 1
fi
done
#!/bin/bash
# pre-receive - Enforce branch naming
while read old_sha new_sha ref_name; do
if [[ "$ref_name" =~ ^refs/heads/ ]]; then
branch_name=${ref_name#refs/heads/}
# Check naming pattern
if ! [[ "$branch_name" =~ ^(feature|bugfix|hotfix)/.+ ]]; then
echo "Branch name must start with feature/, bugfix/, or hotfix/"
exit 1
fi
fi
done
#!/bin/bash
# post-receive - Send notifications
while read old_sha new_sha ref_name; do
if [ "$ref_name" = "refs/heads/master" ]; then
# Send notification to team
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"Deployed to production: $new_sha\"}" \
"$SLACK_WEBHOOK_URL"
# Update monitoring
curl "$MONITORING_API/deploy/$new_sha"
fi
done

Hooks run automatically at specified points; aliases require manual invocation. Hooks can prevent operations; aliases just provide shortcuts.

What’s the difference between pre-commit and commit-msg?

Section titled “What’s the difference between pre-commit and commit-msg?”

pre-commit runs before commit creation, validates code. commit-msg runs after commit creation, validates the message format.

Yes, but carefully - install in repository’s .git/hooks (get copied on clone) or globally via core.hooksPath configuration.

Why are my hooks not running during rebase?

Section titled “Why are my hooks not running during rebase?”

Rebase uses internal operations that bypass some hooks. Use —no-verify during rebase if hooks cause issues.

What’s the safest way to test hook modifications?

Section titled “What’s the safest way to test hook modifications?”

Test in a separate repository clone. Use git commit —no-verify initially. Make small incremental changes.

Can server hooks work with bare repositories?

Section titled “Can server hooks work with bare repositories?”

Yes, server hooks (pre-receive, update, post-receive) are designed for bare repositories and run in $GIT_DIR.

Add debugging output to hook scripts. Test manually with expected inputs. Use set -x in bash hooks for debugging.

What’s the performance impact of complex hooks?

Section titled “What’s the performance impact of complex hooks?”

Heavy hooks can slow operations. Optimize with caching, move expensive checks to CI/CD, or run asynchronously where possible.

Yes, hooks can run git config commands. Use —global or —local as appropriate for hook context.

How do I handle hooks during repository initialization?

Section titled “How do I handle hooks during repository initialization?”

create .git/hooks directory during repo setup. Consider automated hook installation in project documentation.

What’s the relationship between hooks and CI/CD systems?

Section titled “What’s the relationship between hooks and CI/CD systems?”

Hooks work locally; CI/CD executes on remote systems. Use hooks for pre-flight checks, CI/CD for comprehensive testing.

Can hooks be used for repository analytics?

Section titled “Can hooks be used for repository analytics?”

Yes, post-commit and post-receive hooks can track changes, send metrics, and integrate with analytics systems.

How do I migrate hooks to different scripting languages?

Section titled “How do I migrate hooks to different scripting languages?”

Rewrite in preferred language, keep execution logic same. Update shebang line. Test thoroughly.

What’s the impact of hook failures on team workflow?

Section titled “What’s the impact of hook failures on team workflow?”

Failing hooks stop operations, so they’re great for policy but bad for workflow. Make hooks informative and provide bypass mechanisms.

Can hooks integrate with authentication systems?

Section titled “Can hooks integrate with authentication systems?”

Yes, particularly server hooks. Use LDAP, OAuth, or custom authentication in pre-receive hooks with user context.

  1. Code Quality Enforcement: Automatic linting, testing, and static analysis before commits
  2. Policy Compliance: Enforce commit message standards, branch naming conventions, and file organization
  3. Workflow Automation: Automatic deployment, notification systems, and integration with external tools
  4. Access Control: Server-side permission checks and branch protection in shared repositories
  5. Audit and Monitoring: Tracking repository usage, change metrics, and compliance reporting
  6. Integration Points: Connecting Git operations with project management, CI/CD, and development tools