What's Actually Happening
Git hooks are scripts that run automatically before or after Git events like commit, push, or merge. When hooks don't run, usually the hook file lacks execute permission, is in wrong location, or has script errors.
The Symptom
$ git commit -m "test"
# No hook output, commit happens immediately
# pre-commit hook should have run but didn'tYou expected hook to: - Run tests - Check formatting - Validate commit message - Block bad commits
But nothing happened.
Why This Happens
- 1.Missing execute permission - Hook file not executable (most common)
- 2.Wrong filename - Hook must be named exactly (e.g.,
pre-commit, notpre-commit.sh) - 3.Wrong location - Hooks must be in
.git/hooks/ - 4.Script errors - Hook script has syntax errors or wrong shebang
- 5.Hooks disabled globally - Core.hooksPath configured incorrectly
Step 1: Check Hook Files Exist
ls -la .git/hooks/Shows hook templates and active hooks:
total 16
drwxr-xr-x 2 user user 4096 Apr 3 10:00 .
drwxr-xr-x 8 user user 4096 Apr 3 10:00 ..
-rwxr-xr-x 1 user user 478 Apr 3 10:00 pre-commit
-rwxr-xr-x 1 user user 478 Apr 3 10:00 pre-commit.sampleFiles without .sample extension are active hooks.
Step 2: Check Execute Permission
ls -l .git/hooks/pre-commitMust show -rwxr-xr-x (execute permission for all).
If shows -rw-r--r-- (no execute):
chmod +x .git/hooks/pre-commit
chmod +x .git/hooks/post-commit
chmod +x .git/hooks/pre-pushOr add execute to all hooks:
chmod +x .git/hooks/*Step 3: Verify Hook Filename
Hook names must be exact:
| Hook | Filename (exact) |
|---|---|
| Pre-commit | pre-commit |
| Post-commit | post-commit |
| Pre-push | pre-push |
| Commit-msg | commit-msg |
| Pre-rebase | pre-rebase |
Wrong: pre-commit.sh, pre-commit.bash, pre-commit.py
Correct: pre-commit (no extension)
If named wrong:
mv .git/hooks/pre-commit.sh .git/hooks/pre-commit
chmod +x .git/hooks/pre-commitStep 4: Check Script Content
cat .git/hooks/pre-commitMust start with shebang:
#!/bin/sh
# or
#!/bin/bash
# or
#!/usr/bin/env pythonIf missing shebang, script won't run correctly.
Also check for script errors:
```bash # Test hook manually ./.git/hooks/pre-commit
# Or with bash debug bash -x .git/hooks/pre-commit ```
Step 5: Check Global Hooks Configuration
git config --get core.hooksPathIf this returns a path, Git uses that path instead of .git/hooks/.
To use default hooks location:
git config --unset core.hooksPathOr set custom hooks path:
git config core.hooksPath .githooks
chmod +x .githooks/*Step 6: Test Hook Manually
Run hook directly to debug:
```bash # Test pre-commit hook ./.git/hooks/pre-commit
# Check exit code echo $? # 0 = success, non-0 = failure (blocks commit) ```
If hook runs manually but not during Git operations, check configuration.
Step 7: Use Hook Management Tools
For shared hooks across team, use tools:
Husky (popular for npm projects):
npm install husky --save-dev
npx husky install
npx husky add .husky/pre-commit "npm test"pre-commit (Python tool):
pip install pre-commit
# Create .pre-commit-config.yaml
pre-commit installThese tools handle permissions and setup automatically.
Step 8: Debug Hook Execution
For detailed debugging:
```bash # Run Git command with debug GIT_TRACE=1 git commit -m "test"
# See hook execution git commit -m "test" 2>&1 | grep hook ```
Step 9: Check for Core Hooks Disabled
git config --get core.hooksPath
git config --get init.templateDirIf hooks path is empty string or invalid, hooks won't run.
Verify the Fix
After fixing:
```bash # Test hook runs git commit --allow-empty -m "test"
# Should see hook output (if hook prints anything) # Commit should be blocked if hook exits non-zero
# Check hook execution trace GIT_TRACE=1 git commit -m "test" 2>&1 ```
Prevention Tips
When setting up hooks:
```bash # Always add execute permission chmod +x .git/hooks/pre-commit
# Use proper shebang #!/bin/sh set -e # Exit on error
# Test hook immediately after creating ./.git/hooks/pre-commit
# Use hook managers for team projects npm install husky ```