This article is based on content originally published by Atlassian. Hook scripts referenced were authored by Atlassian developer advocate Tim Petterson and designed for use with Bamboo, Clover, and Bitbucket.
Understanding Git Hooks
Git hooks are Git’s native mechanism for triggering custom scripts before or after core repository operations such as commit, merge, push, and checkout. Think of them as Git’s plugin system — lightweight, portable, and entirely under your control.
Every Git repository contains a .git/hooks directory populated with example hook scripts. To activate a hook, rename the relevant example file (removing its .sample extension), make it executable, and add your logic. The Git documentation covers installation in detail.

Client-Side vs. Server-Side Hooks
Hooks fall into two broad categories:
- Client-side hooks run on a developer’s local workstation and affect local operations like committing and checking out branches.
- Server-side hooks run on the remote Git host (e.g., Bitbucket Server, GitLab, or GitHub Enterprise) and can gate pushes and merges centrally across the whole team.
Pre-Receive vs. Post-Receive Hooks
Hooks are also classified by when they fire relative to the triggering operation:
- Pre-receive (pre-) hooks run before an operation completes. Crucially, they can exit with a non-zero status to cancel the operation entirely — making them ideal for enforcing quality gates.
- Post-receive (post-) hooks run after an operation has already completed and cannot cancel it. They are well-suited for automation tasks like sending notifications or triggering deployments.
Common tasks automated with Git hooks include:
- Validating that a commit message references the correct issue tracker key (e.g., a Jira ticket)
- Enforcing merge preconditions such as passing tests or minimum code coverage
- Posting build or merge notifications to a team chat channel
- Configuring the workspace automatically when switching branches

Hook 1: Enforce Clean Builds Before Merging to Main
Developers are generally diligent, but it is easy to forget to check the CI status on a branch — especially when multiple contributors are pushing changes concurrently. A server-side pre-receive hook closes this gap by making the check automatic and mandatory.
The hook inspects incoming push operations targeting the main (or master) branch. When it detects a merge, it queries the CI server for the latest build result on the source branch. If any tests are failing, the hook exits with a non-zero code, rejecting the push and printing a message explaining why.
A reference implementation by Atlassian’s Tim Petterson, designed to work with Bamboo CI, is available on Bitbucket. The script is intentionally straightforward to customise for other CI servers such as Jenkins or CircleCI — the key integration point is an API call to retrieve the latest build status for a given branch and revision.
How It Works in Practice
- A developer runs
git push origin feature/my-branchand opens a pull request to merge into main. - On push (or on merge attempt, depending on your server configuration), the pre-receive hook fires.
- The hook script calls the CI server’s API to retrieve the build status for the branch’s HEAD revision.
- If the build is red (failing tests), the push is rejected with a descriptive error message.
- If the build is green, the push proceeds normally.
Hook 2: Protect Hard-Earned Code Coverage
Teams that have invested significant effort in retroactively adding test coverage to a legacy codebase know how painful it is to watch that coverage percentage erode as new features ship without accompanying tests. A pre-receive server-side hook can enforce a coverage floor on the main branch.

This hook also intercepts incoming merges to main. It then performs two lookups against the CI server:
- Current coverage on main — the baseline the incoming branch must meet or exceed.
- Coverage on the feature branch — pulled from the build associated with the commit being merged.
If the branch’s coverage is lower than main’s, the merge is rejected.
Prerequisites and Configuration
Because most CI servers do not expose code coverage metrics through a standard remote API, the script fetches the coverage report directly as a build artifact. This requires that your build pipeline is configured to publish the coverage report as a shared artifact on every build — for both the main branch and feature branches.
The reference implementation targets Bamboo and Clover (Atlassian’s code coverage tool for Java and Groovy). It can be adapted for other tool combinations — the essential requirement is that your CI server makes coverage report artifacts accessible via a predictable URL or API endpoint.
Important: This hook does not generate code coverage. It only reads coverage data that your existing build process already produces. If your builds do not measure coverage today, you will need to add that to your pipeline first.
Hook 3: Surface Branch Build Status at Checkout
Because friends don’t let friends check out broken branches.
This is a client-side post-checkout hook. Unlike the server-side hooks above, it runs on each developer’s local workstation and fires automatically whenever they switch branches with git checkout (or git switch).

What the Hook Does
- After
git checkoutcompletes, the script reads the HEAD revision of the newly checked-out branch from the local repository. - It queries the CI server to determine whether that revision has been built — and if so, whether the build passed or failed.
- The result is printed directly in the terminal alongside the normal checkout output.
Two Practical Scenarios
Creating a new feature branch from main: The hook tells you immediately whether the HEAD of main built successfully, confirming it is a safe and stable starting point for new work.
Detecting a stale local copy: If the hook reports a failed build for a revision but the team’s CI dashboard shows a green build for that branch, your local copy is behind. You can then decide whether to git pull before continuing.
This hook is the lowest-friction option to introduce to a team. It requires no server-side configuration, no repository admin access, and no changes to the shared workflow — each developer installs it independently in their local .git/hooks directory.
Tooling and Portability
The three hooks described above were originally written to target Bamboo (build server), Clover (code coverage), and Bitbucket (Git hosting). However, Git hooks themselves are vendor-neutral — they are simply executable scripts in a well-known directory. The CI-specific logic in any hook script is limited to a handful of HTTP API calls or artifact URL patterns, which can be swapped out for equivalent calls to Jenkins, GitHub Actions, GitLab CI, CircleCI, or any other system that exposes build and coverage status via HTTP.
If you work in a polyglot environment or want to manage hooks consistently across a team, tools such as pre-commit or Husky can help version-control and distribute client-side hooks alongside your project code.
Summary
| Hook | Type | Trigger | What It Enforces |
|---|---|---|---|
| Clean branch builds | Server-side pre-receive | Push / merge to main | All tests must pass on the source branch |
| Code coverage floor | Server-side pre-receive | Push / merge to main | Branch coverage must not be lower than main |
| Branch build status | Client-side post-checkout | git checkout / switch | Displays CI build result for the checked-out revision |
Git hooks are one of the most underutilised features in a CI/CD toolkit. Introducing even one of these hooks can measurably reduce the number of broken builds that reach your main branch — and the post-checkout hook in particular pays dividends immediately with zero team coordination required.