Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Git hooks are executable programs that Git runs at named points such as pre-commit, commit-msg, pre-push, pre-receive and post-receive. They can format code, run tests, reject non-compliant pushes and enqueue deployments without a developer remembering a separate command. The reliable design is layered: use local hooks for fast feedback, enforce non-negotiable rules on the server or in required CI/CD, and trigger deployment only after the server has accepted a ref update.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
What a Git hook is and where it runs
A hook is a program placed in a Git hooks directory and associated with an event in Git’s execution flow. Git invokes the program when that event occurs. A hook that exits with status zero allows the operation to continue; a non-zero status stops operations that are still being validated.
| # | Preview | Product | Price | |
|---|---|---|---|---|
| 1 |
|
Pro Git | Buy on Amazon | |
| 2 |
|
Pro Git (Expert's Voice in Software Development) | $37.07 | Buy on Amazon |
| 3 |
|
Professional Git | $24.79 | Buy on Amazon |
| 4 |
|
Pro Git 1st (first) edition Text Only | $83.26 | Buy on Amazon |
| 5 |
|
Git: Github Programming, In 8 Hours, For Beginners, Learn Coding Fast: Git Github Language, Crash... | $2.99 | Buy on Amazon |
As an Amazon Associate I earn from qualifying purchases.
For a normal working repository, the default directory is $GIT_DIR/hooks (usually .git/hooks). A repository can relocate hooks with core.hooksPath. Git ignores a hook file that does not have its executable bit set, and the filename must exactly match the event name, such as pre-commit rather than precommit.
Hooks are not automatically versioned with your source files. That distinction matters: a developer can remove or bypass a local hook, while a server hook runs in the receiving repository and can protect every client. Treat local automation as feedback and central automation as enforcement.
#1 Best Overall
Choose the right enforcement boundary
| Location and event | Best use | Feedback and authority | Main limitation |
|---|---|---|---|
pre-commit (client) |
Formatter, lint, focused unit tests | Immediate, before a commit is created | Can be skipped or removed by the user |
commit-msg (client) |
Issue keys, conventional-commit syntax, sign-off text | Immediate message validation | Not a central policy boundary |
pre-push (client) |
Checks too expensive for every commit, such as an integration test subset | Before objects are transferred | Can be bypassed and adds delay to a push |
pre-receive (server) |
Branch policy, authorization, secret scanning, naming rules | Central and authoritative; can reject the entire receive operation | Requires control of the receiving repository or hosting system |
update (server) |
Per-reference authorization or protection | Central and authoritative for an individual ref | More granular logic is required |
post-receive (server) |
Notifications and deployment work after accepted updates | Runs after refs have changed | Cannot undo or change the completed receive result |
Use the fastest suitable check at each layer. A practical arrangement is formatting in pre-commit, broader tests in pre-push or required CI, policy in pre-receive or update, and deployment from post-receive into a queue or CI/CD pipeline.
Run tests before every commit with pre-commit
1. Create the hook
From the repository root, create .git/hooks/pre-commit with a portable shebang and fail-fast shell settings:
Free tools Windows power users keep installed
One-click scans. No signup required.
#!/usr/bin/env bash
set -euo pipefail
npm run lint
npm test
The commands in this example are project-specific. Replace them with the formatter, linter and test commands documented by your project. Keep the hook deterministic: use the same lockfile and tool versions that CI uses, and avoid commands that modify unrelated files without telling the developer.
2. Make it executable
chmod +x .git/hooks/pre-commit
Now Git runs the script before creating each commit. If either command returns a non-zero status, the commit is stopped and the index remains available for correction. A useful failure message identifies the command that failed and the shortest fix, rather than printing an opaque stack trace.
3. Keep the check usable
Run fast, predictable checks on every commit. Move a long build or a full end-to-end suite to pre-push, CI, or both. If the project permits an emergency bypass, document the exact command and require the bypassed check to run in CI; do not treat a bypass as proof that the code is safe.
Rank #2
Share hooks with a team
The .git/hooks directory is repository metadata, so a hook placed there is not delivered by a normal commit. Store shared scripts in a tracked directory, for example scripts/hooks/, and point each clone at that directory:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
git config core.hooksPath scripts/hooks
The setting is local to each clone unless you configure it through a broader Git configuration mechanism. Include installation instructions in the repository and make the scripts executable in the checkout. Pin runtime and dependency versions in the project so two developers do not receive different answers from the same hook.
A shared hook must not assume the caller’s current directory, shell aliases or globally installed tools. Resolve paths from the repository, check required commands, and print a clear prerequisite error. Keep credentials out of tracked scripts and out of hook output.
Validate commit messages with commit-msg
Git passes commit-msg the path to the proposed commit-message file as its first argument. A hook can inspect that file and reject or rewrite it. This is a good place for a required issue key, a conventional-commit prefix or a sign-off line.
#!/usr/bin/env bash
set -euo pipefail
message_file="$1"
message="$(cat "$message_file")"
if ! printf '%sn' "$message" | grep -Eq '^([a-z]+)(([^)]+))?: .+'; then
printf '%sn' 'Commit message must use type(scope): description format.' >&2
exit 1
fi
Adapt the regular expression to the project’s actual policy. Explain an error in terms of the required correction, and make sure the hook handles empty or multi-line messages safely. A client-side message check improves consistency, but a repository that must guarantee the format should repeat the rule in a server or required CI check.
Rank #3
Run broader checks before a push
pre-push runs before Git sends objects to the remote. Use it for checks that are valuable before sharing code but too expensive for every commit, such as an integration-test subset, generated-file verification or a build that normally completes within a tolerable time.
#!/usr/bin/env bash
set -euo pipefail
./scripts/check-generated-files.sh
npm run test:integration
Git supplies remote and reference information to the hook on standard input. If your script needs to inspect exactly what is being pushed, parse that input rather than assuming the current branch. Keep the work bounded and show progress. A push hook that silently performs a long build encourages developers to disable it.
Do not rely on pre-push for an invariant. A user can invoke a push with hook verification disabled or remove the local file. Required CI and server-side checks must reproduce any rule that protects production or shared branches.
Reject policy violations on a central server
Use pre-receive for receive-wide rules
A server-side pre-receive hook runs in the receiving repository before refs are updated. Git supplies proposed reference changes on standard input, one line containing the old object ID, new object ID and ref name for each update. Returning a non-zero status rejects the receive operation, so none of the proposed refs are updated.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →#!/usr/bin/env bash
set -euo pipefail
while read -r oldrev newrev refname; do
case "$refname" in
refs/heads/main)
if [[ "$newrev" == 0000000000000000000000000000000000000000 ]]; then
printf '%sn' 'Deleting main is not permitted.' &2
exit 1
fi
;;
esac
done
Typical rules include protected-branch behavior, branch naming, authorization, required signatures and secret scanning. Validate every input; do not trust a branch name or object ID supplied by a client. If a rule belongs to one ref rather than the entire receive transaction, an update hook can apply the decision per ref.
Make rejection actionable
Write a concise reason to standard error, identify the ref that failed and state the approved remedy. A server hook should fail closed for malformed input, but it should also log enough context for an administrator to diagnose a legitimate rejection. Coordinate server rules with CI so developers see the same policy before and after pushing.
Trigger deployment after an accepted push
A post-receive hook runs after successful ref updates. It receives the old object ID, new object ID and ref name for each successfully updated ref. Because the real receive work is already complete, its exit status does not alter the receive result. Its output is forwarded to the client, so avoid dumping sensitive data.
#!/usr/bin/env bash
set -euo pipefail
while read -r oldrev newrev refname; do
case "$refname" in
refs/heads/main)
/usr/local/bin/enqueue-deploy production "$newrev"
;;
esac
done
This pattern passes an immutable commit ID to a deployment worker instead of asking the worker to deploy an ambiguous moving branch name. The enqueue operation should authenticate its downstream request, validate the ref, record an audit event and return quickly. Put builds, container publishing and rollout logic in a queue or CI/CD system rather than running a long build inside the receive process; otherwise a successful push can appear to hang while the server performs deployment work.
Handle retries and failures
Design the consumer to be idempotent. A hook may be retried after a process failure, and multiple refs can be updated in one receive operation. Deduplicate by environment and commit ID, record whether a job was accepted, and alert when enqueueing fails. A failed notification should not pretend that the code was rejected: the refs have already been updated, so the recovery action is to retry or investigate the deployment job.
Register shareable commands with git hook
Git’s git hook command supports named hook configuration. A command can be registered once and attached to more than one event:
Best Value
git config set hook.linter.command ~/bin/linter --cpp20
git config set --append hook.linter.event pre-commit
git config set --append hook.linter.event pre-push
This approach is useful when configuration is shared across repositories and the same tool should run at multiple lifecycle points. Decide where that configuration is distributed, document the expected Git version and ensure the command is available on every machine. A named configuration does not remove the need for central enforcement; it is still a client-side convenience unless a server or CI job repeats the check.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Combine hooks and CI into one workflow
- Format and lint locally. Put quick, low-noise checks in
pre-commitso defects are fixed before they enter history. - Validate the message. Use
commit-msgfor naming and traceability rules that developers can correct immediately. - Run a pre-transfer gate. Use
pre-pushfor a bounded integration set or generated-artifact check. - Repeat authoritative checks remotely. Required CI should run in a controlled environment, and server hooks should reject rules that must hold even when a client skips its hooks.
- Accept, then deploy. Use
post-receiveonly to submit an authenticated, immutable job to the deployment system after the update succeeds.
This split balances enforcement, feedback speed, runtime cost and portability. Developers get rapid local answers, while production policy does not depend on a particular shell, operating system or unmodified clone.
Recommended Free Tools
Hook paths, working directories and portability
When a hook behaves unexpectedly, first ask which repository and which hooks directory Git is actually using:
git rev-parse --git-path hooks
git config --show-origin --get core.hooksPath
Push-related hooks execute in the receiving repository’s $GIT_DIR, not necessarily in a checked-out project directory. Use absolute paths for deployment programs, or explicitly change to the intended working directory before invoking project commands. Do not depend on a developer’s home-directory layout on a server.
Git exports repository-related environment variables while running hooks. If a hook invokes Git against a different repository, clear those variables first so the nested command does not accidentally operate on the original repository. A typical shell pattern is:
unset $(git rev-parse --local-env-vars)
Use care with this pattern in scripts: quote paths, handle an empty variable list, and test the nested operation in the same service account and environment used in production.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchQuick Recap
Troubleshoot a hook that does not run
- Wrong filename: Confirm the file is exactly
pre-commit,commit-msg,pre-push,pre-receive,updateorpost-receive, as appropriate. - Missing executable permission: Run
chmod +x path/to/hookand verify the mode in version control for tracked scripts. - Unexpected hook directory: Compare
git rev-parse --git-path hookswith the effectivecore.hooksPath; a global setting can redirect every repository. - Wrong interpreter or line endings: Use a valid shebang such as
#!/usr/bin/env bash, ensure that interpreter exists on the target machine and avoid incompatible line endings. - Relative paths fail: Print the working directory during diagnosis and switch explicitly to the project directory before running project tools.
- Server hook appears to do nothing: Check the receiving repository, service account permissions, executable mode and server logs. A client-side hook in your clone cannot enforce a server policy.
- Deployment starts for the wrong branch: Match the full ref name, such as
refs/heads/main, and pass the new object ID to the worker. - Tests differ from CI: Pin dependencies and runtimes, then move the authoritative check to required CI or a server-controlled environment.
- Need to reproduce a failure: Run the hook directly with representative input in a disposable clone, but do not assume direct invocation reproduces Git's exact environment.
Security and reliability requirements
- Keep tokens, private keys and passwords in a secret manager or deployment system; never commit them to a hook or echo them into logs.
- Use least-privilege service accounts for server hooks and deployment workers. The account should be able to enqueue or deploy only what its role requires.
- Validate ref names, object IDs and user-controlled commit data before constructing commands. Prefer fixed argument lists over shell-evaluated strings.
- Log the repository, ref, commit ID, decision and downstream job identifier, while redacting credentials and sensitive file contents.
- Make deployment consumers idempotent and observable. Record queue acceptance separately from rollout success.
- Keep hooks short and fail with a useful message. Long synchronous work belongs in CI/CD or a worker queue.
- Reproduce local checks remotely. A hook improves developer experience; it is not evidence that every contributor ran the check.
A practical rollout checklist
- Write down which rules are advisory and which must block a receive.
- Choose the event that has the necessary information: commit contents for
pre-commit, the message file forcommit-msg, ref updates forpre-receiveandupdate, or accepted refs forpost-receive. - Implement the smallest fast local hook and test its success and failure paths.
- Move shared scripts into a tracked directory, configure
core.hooksPath, and document installation. - Repeat every production or compliance rule in required CI or a server hook.
- For deployment, enqueue an authenticated job with the exact new commit ID and the full ref name.
- Test rejected pushes, multiple-ref pushes, deleted refs, retries and downstream outages before enabling the workflow on a protected repository.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




