Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
VGSources
Blog

How to Use Git Hooks to Automate Development and Deployment Tasks

Learn where Git hooks run, how to install and share pre-commit and pre-push checks, enforce server-side policy, and safely trigger deployment from post-receive.
Length10 min Posted Quest giverVGSources Team

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Git hooks are executable programs that Git runs at named points in a local or server-side workflow. Use pre-commit for fast linting and formatting, commit-msg for message rules, pre-push for checks before objects leave a workstation, pre-receive or update to enforce policy centrally, and post-receive to enqueue deployment after a push has been accepted.

The dependable pattern is layered: give developers quick local feedback, reproduce authoritative checks in CI/CD or a receiving server, and trigger deployments only after the server has accepted the intended commit.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What a Git hook is and why the location matters

A Git hook is an executable program that Git invokes at a named event in its execution. In a normal working repository, Git looks in $GIT_DIR/hooks (usually the repository’s .git/hooks directory). A hook file without the executable bit is ignored. The core.hooksPath setting can move the active hooks directory somewhere else, so checking the configured path is essential when a hook appears not to run.

Hooks are automation points, not a complete policy system. A developer can remove or bypass a client-side hook, and a local hook runs in that developer’s shell and toolchain. Rules that must apply to every contributor belong on a receiving server or in a required CI/CD check. Local hooks remain valuable because they find inexpensive errors before a commit or push.

#1 Best Overall

Choose the right hook for each job

Event Where it runs Good uses Can reject the operation? Typical feedback point
pre-commit Developer’s local repository Formatting, linting, fast unit tests Yes; a non-zero exit status stops the commit Before a commit is created
commit-msg Developer’s local repository Issue keys, Conventional Commit syntax, required sign-off text Yes; it can reject or rewrite the proposed message While the commit message is finalized
pre-push Developer’s local repository Checks that are too expensive for every commit, branch-specific tests Yes; it runs before objects are sent Before network transfer
pre-receive Receiving server repository Branch policy, authorization, secret scanning, naming rules Yes; a non-zero status rejects the receive operation Before any proposed refs are updated
update Receiving server repository Per-reference authorization or branch-specific rules Yes; a non-zero status rejects the affected update During ref updates
post-receive Receiving server repository Deployment enqueueing, notifications, ticket updates No; refs have already been updated After a successful receive

Use the earliest event that can give useful feedback without imposing an unreasonable runtime. A formatter belongs in pre-commit; a full integration suite may fit pre-push or CI; a protected-branch rule belongs on the server; deployment should start from an accepted ref update.

Decide where enforcement belongs

Local hooks for speed and ergonomics

Local hooks reduce the cost of mistakes. A developer sees a failed formatter or unit test immediately, without waiting for a remote system. They are also easy to tailor to staged files, local operating-system tools, or an individual branch.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not treat them as the sole authority. Users can skip local hooks, use a different clone, or have an incorrectly installed hook. Keep the same important checks in CI/CD or a server-side hook.

Server hooks and required CI/CD for authority

A receiving repository can reject a push before refs change with pre-receive, or apply rules per reference with update. This is the appropriate boundary for requirements such as protected branches, permitted ref names, authorization, and mandatory secret checks. A required CI/CD status check is another central enforcement point when the hosting service provides it.

Post-acceptance automation for deployment

post-receive runs only after successful ref updates. It receives one line for every successfully updated ref, containing the old object ID, new object ID, and ref name. Because the real receive work is complete, this hook cannot make the already-completed push fail. Use it to enqueue work rather than performing a long build in the receive process.

Install a local pre-commit hook

The following example runs a project formatter or linter and tests before every commit. It assumes the project already defines these commands; replace them with the commands used by your build.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Create .git/hooks/pre-commit:

    #!/usr/bin/env bash
    set -euo pipefail
    npm run lint
    npm test
  2. Make the file executable:

    chmod +x .git/hooks/pre-commit
  3. Stage your changes and run git commit. If either command exits non-zero, the hook exits non-zero and Git does not create the commit.

#!/usr/bin/env bash asks the operating system to find Bash rather than assuming a fixed installation path. If contributors use environments without Bash, choose a shell and commands supported by every required platform, or provide platform-specific wrappers.

Keep pre-commit checks fast

Run deterministic, bounded checks at this point. Formatting staged files, linting changed packages, and a focused unit-test set usually provide better feedback than a multi-minute build. Put slower integration or packaging work in pre-push or CI/CD. Print the command that failed and the corrective action so the developer can fix the commit without guessing.

Share hooks without relying on .git/hooks

The .git directory is repository metadata and is not automatically versioned with project files. A hook placed there will not travel with a clone. Keep shared scripts in a tracked directory such as scripts/hooks/, then configure each clone to use that directory.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Commit the scripts, for example scripts/hooks/pre-commit and scripts/hooks/commit-msg.

  2. Point Git at the tracked directory:

    git config core.hooksPath scripts/hooks
  3. Document this one-time setup in the repository’s contributor guide or bootstrap command.

  4. Pin formatter, test-runner, and runtime versions in the project so two developers do not receive different results from the same hook.

A relative core.hooksPath is interpreted relative to the directory where Git is run. Teams that invoke Git from several locations should verify the behavior in their bootstrap process; an absolute path can be appropriate for machine-wide policy but is less portable for a checked-out project.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the git hook configuration for named commands

Git also supports named, shareable hook configuration. One command can be registered for more than one event:

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 arrangement lets a shared configuration invoke the same linter at both events without duplicating a script. Confirm that the command is available at the configured path for every user and that the command returns a meaningful non-zero status when it finds a violation.

Validate commit messages with commit-msg

Git passes commit-msg the path to the file containing the proposed message. The hook can inspect that file and reject or rewrite it. Typical policies include an issue identifier, a Conventional Commit type, or a required sign-off line.

A useful validator should:

  • Read the file path supplied as its first argument rather than assuming a fixed filename.
  • Print the expected format and a concrete example when it rejects a message.
  • Use an exit status of zero only when the message is acceptable.
  • Handle merge and automated commits deliberately instead of silently applying a rule that was intended only for human-authored changes.

Keep the rule in source control when it is shared, and test it with valid, invalid, merge, and revert messages. The hook is a local convenience unless the same requirement is checked centrally.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run deeper checks with pre-push

pre-push runs before Git sends objects to the remote. It is a useful compromise for checks that are too expensive before every commit but still catch problems before code is shared. Examples include a broader test suite, generated-file verification, or a branch-specific build.

Keep the runtime bounded and explain how to run the failed command directly. If your project permits an override, document the exact procedure and the policy boundary; do not make bypassing an authoritative check the normal workflow. A local pre-push hook should still be duplicated in CI/CD because another clone can omit or skip it.

Reject invalid pushes on the server

Use pre-receive for repository-wide policy

A server-side pre-receive hook runs in the receiving repository before refs are updated. Git supplies proposed ref changes on standard input. A non-zero exit status rejects the receive operation, so this is the right place for rules that must apply to every client.

Common checks include:

  • Allowing updates only to approved branch or tag namespaces.
  • Requiring authorization for protected branches.
  • Rejecting non-fast-forward updates where policy forbids history rewrites.
  • Scanning incoming commits for credentials or prohibited content.
  • Checking commit metadata or signatures when the receiving service supports the required verification.

Read all input before deciding whether to accept the receive, and make rejection messages actionable. Tell the user which ref failed, which rule was violated, and where to find the correction procedure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use update for per-reference decisions

The update hook is suited to rules that differ by branch or tag. For example, it can apply one authorization rule to refs/heads/main and another to a release namespace. Choose pre-receive when one decision should cover the complete batch, and update when each reference needs an independent decision.

Account for the server execution environment

Push hooks run in the receiving repository’s $GIT_DIR, not in the developer’s working directory. Use absolute paths for programs and files, or explicitly set the working directory before invoking a build. Do not assume the interactive shell profile, user-specific PATH, or local configuration exists on the server.

If a hook invokes Git on another repository, clear exported Git environment variables first. Variables inherited from the receive operation can otherwise make the nested command address the wrong repository.

Trigger deployment after a successful push

Use post-receive as an integration boundary after the server has accepted updates. The hook receives lines in the form of an old object ID, a new object ID, and a ref name. A bare-repository example that queues production deployment for the main branch is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#!/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

Pass the immutable new commit ID to the deployment system rather than asking it to deploy whatever happens to be at a moving branch name later. Validate the ref before enqueueing, authenticate the downstream request, and log enough information to trace the request without writing credentials to logs.

Why enqueue instead of building in the hook

A long build or deployment performed inline can keep the push connection open, exhaust server resources, and make transient deployment failures look like Git failures. Enqueue a job or call a CI/CD endpoint that owns retries, isolation, approvals, and status reporting. The receive operation has already succeeded; a failed downstream job should be visible as a deployment failure, not misreported as a rejected push.

Output from post-receive is forwarded to the client, which is useful for a job identifier or a link to status when your deployment system provides one. Do not imply that a successful push means production is healthy; it means only that the refs were updated and the deployment request was accepted.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Compare common designs before implementing one

Design Enforcement Feedback speed Runtime profile Portability
Local pre-commit Advisory; bypassable Immediate, before commit Fast checks and focused tests Depends on each developer’s shell and tools
Local pre-push Advisory; bypassable Before network transfer Moderate or broader test sets Depends on each developer’s environment
Server pre-receive/update Central and authoritative At push time Policy checks must fit receive-time limits Controlled server environment
CI/CD required check Central and auditable After push or pull request creation Full builds, integration tests, scans Controlled workers and pinned tools
post-receive plus deployment queue Runs after acceptance; does not reject the push After refs update Asynchronous build and deployment work Controlled deployment environment

A practical pipeline often combines all four layers: quick formatting in pre-commit, broader checks in pre-push and CI/CD, mandatory policy in pre-receive or update, and deployment enqueueing in post-receive.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshoot a hook that does not run

  1. Check the exact filename. The file must match the event name, such as pre-commit or post-receive; do not leave a sample suffix such as .sample.

  2. Check executable permissions. Run ls -l and confirm the execute bit, then use chmod +x path/to/hook if needed. Git ignores non-executable hook files.

  3. Find the active hooks directory. Run:

    git rev-parse --git-path hooks
    git config --show-origin --get core.hooksPath

    The first command shows the directory Git resolves for hooks; the second reveals an overriding configuration and where it came from.

  4. Verify the interpreter and dependencies. Test the shebang, absolute command paths, runtime versions, and file permissions under the same account that runs Git or the receiving service.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  5. Check the working directory. Server hooks run in $GIT_DIR. Change to the intended project directory before running commands that expect a checkout, or use a bare-repository-compatible command.

  6. Inspect input and arguments. commit-msg receives a message-file path; receive hooks consume ref-update lines on standard input. Log sanitized arguments while debugging.

  7. Look for bypasses. A user can skip local hooks, and a different clone may not have them installed. Reproduce the authoritative check in CI/CD or the server.

Security and reliability practices

  • Keep credentials out of hook files, command lines, and logs. Supply them through the deployment system’s secret store.
  • Validate ref names and commit IDs before passing them to another program; never concatenate untrusted input into a shell command.
  • Use immutable commit IDs for deployment requests and record the ref, actor, enqueue time, and resulting job identifier.
  • Make hooks idempotent where possible. A retry should not create duplicate releases or notifications.
  • Set explicit timeouts for network calls and let a queue or CI/CD system handle retries.
  • Keep error messages useful but avoid exposing secrets, internal tokens, or sensitive repository paths.
  • Version shared scripts and test them on every supported operating system and shell.
  • Monitor hook failures separately from application failures so a broken notification or deployment integration does not silently block development.

A rollout checklist

  1. List each desired action and classify it as local feedback, central policy, or post-acceptance automation.
  2. Choose the narrowest hook that can enforce the requirement without making normal development unnecessarily slow.
  3. Write the hook with a portable shebang, strict error handling, explicit paths, and actionable output.
  4. Make local files executable and verify the resolved core.hooksPath.
  5. Store shared scripts in a tracked directory or register them through named git hook configuration.
  6. Reproduce every authoritative check in CI/CD or a server-side hook.
  7. For deployment, enqueue the exact accepted commit ID and keep long-running work out of the receive process.
  8. Test accepted, rejected, bypassed, retried, multi-ref, and failed-downstream scenarios before enabling enforcement for the whole team.

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.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.