Git pre-receive hook
Generate bash pre-receive Git server hook with branch/author/size checks.
Git pre-receive hooks: server-side gatekeepers for every push
Git hooks are executable scripts that Git runs automatically at specific events in the lifecycle of a repository. Local hooks live in .git/hooks/; server-side hooks live in the hooks/ directory of a bare repository on the Git server. Any file in those directories whose name matches a known hook event and that is marked executable (chmod +x) becomes the handler for that event. Scripts can be written in any language as long as they start with a proper shebang: #!/bin/bash, #!/usr/bin/env python3, #!/usr/bin/env node โ Git just forks and executes.
The pre-receive hook is the most powerful enforcement point in the entire Git protocol. It runs once per push, on the server, before any ref is updated. It receives one line per ref on stdin, formatted as <old-sha> <new-sha> <ref-name>. If the script exits with a non-zero status, the entire push is rejected โ every branch, every tag, atomically. There is no partial accept.
Skeleton script
#!/bin/bash
# .git/hooks/pre-receive (chmod +x)
while read oldrev newrev refname; do
if [ "$refname" = "refs/heads/main" ]; then
echo "Direct pushes to main are not allowed" >&2
exit 1
fi
# Reject commits whose message does not follow Conventional Commits
for sha in $(git rev-list "$oldrev".."$newrev"); do
msg=$(git log --format=%s -n 1 "$sha")
if ! echo "$msg" | grep -Eq '^(feat|fix|docs|chore|refactor|test)(\(.+\))?: '; then
echo "Commit $sha violates Conventional Commits: $msg" >&2
exit 1
fi
done
done
exit 0
Common policies enforced by pre-receive
- Block direct pushes to
main,master, orrelease/*โ force every change through a pull request. - Validate commit messages against Conventional Commits, Jira ticket IDs, or a custom regex.
- Reject secrets with
gitleaks detect --stagedortrufflehogbefore they leak into history. - Cap commit / push size to avoid 5 GB binary dumps polluting the server.
- Require code review approval via API call back to the platform.
- Enforce signed commits using
git verify-commiton every revision.
The full hook family
pre-commitโ runs locally beforegit commitcreates the commit object; perfect place for linters.commit-msgโ receives the path to the message file; lets you validate or rewrite it.pre-pushโ runs locally before transferring objects to the remote.pre-receiveโ runs on the server before any ref update; rejects the whole push.updateโ runs on the server once per ref; can accept some refs and reject others.post-receiveโ runs on the server after refs are updated; ideal for CI triggers and deploys.post-merge,post-checkout,post-rewriteโ local notification hooks for IDE plugins or dependency installers.
Platform support and alternatives
Server-side hooks depend entirely on the hosting platform. Raw Git (self-hosted bare repo over SSH) gives full freedom. Gitea, Gogs, and Forgejo expose pre-receive through their admin UI. GitHub Enterprise Server supports pre-receive hooks at the instance level, but GitHub.com does not โ you must replicate the same checks via GitHub Actions, branch protection rules, and required status checks. GitLab offers push rules (Premium) and custom server-side hooks; Bitbucket Data Center has its own SDK. Always test the matrix you target before relying on bash for enforcement.
Because local hooks are not versioned with the repository (anything under .git/ is ignored), they cannot be trusted as a security boundary. Husky, lefthook, pre-commit, and simple-git-hooks ship hooks via files committed to the repo and rebound to core.hooksPath, but a malicious contributor can still bypass them with git commit --no-verify. The only reliable enforcement layer is the server side โ which is why pre-receive matters.
FAQ
Does GitHub.com accept custom pre-receive hooks? No. Use Actions with required status checks, plus branch protection rules and CODEOWNERS for review enforcement.
How do I install the hook? Copy the script into the bare repo's hooks/ directory and run chmod +x pre-receive. Git executes it on the next push.
Can I call external programs from the hook? Yes โ bash, python, node, even compiled binaries are fine. Keep startup cheap; the hook runs on every push and slow hooks make developers angry.
Why does my push hang forever? Hook output goes back to the client over the network. If your script writes to /dev/tty or prompts for input, it will block indefinitely โ always read input only from stdin and write user messages to stderr.
How do I bypass the hook for emergencies? Server-side hooks cannot be bypassed by the client. Add a break-glass flag (e.g. a [emergency] trailer the hook recognizes) gated by group membership, or temporarily disable the hook with root access on the server.
Related Tools
Handwriting Generator
Convert typed text into an image with handwriting appearance. Useful for adding a personal touch to digital work.
Resume Generator
Fill a simple printable A4 CV from a form with personal data, education and experience.
Favicon Generator
Generate a favicon from text/emoji in all common sizes (16, 32, 48, 64, 192, 512). PNG download.