Great work!

XP to next level

BugEater
EN

Sharing Hooks Across a Team (and Why CI Is Still the Real Gate)

Learning Objectives

By the end of this lesson you will be able to:

  • Share hooks with a team using core.hooksPath
  • Describe what a hook manager such as Husky or pre-commit adds
  • Place each check at the right layer: hook, CI, or branch protection
  • Propose a hook to a team in a way that gets adopted

The Problem

You have written excellent hooks. They are in .git/hooks/, which is not part of the repository, so nobody else has them. Cloning the project gives a colleague the sample hooks and nothing more.

There are three ways out, and they differ in how much they ask of the team.

Option 1: core.hooksPath

Git can look for hooks somewhere other than .git/hooks/, and that somewhere can be a committed directory.

mkdir .githooks
git mv .git/hooks/pre-commit .githooks/pre-commit    # or just write them there
chmod +x .githooks/*
git add .githooks
git commit -m "chore: add shared git hooks"

Each person then enables it once:

git config core.hooksPath .githooks

Advantages: no dependencies, works with any language, the hooks are reviewed like any other code.

The catch: that one config command is per-clone and manual. Somebody will forget, and there is no signal when they do. Put it in the README setup steps and in your project's bootstrap script:

{
  "scripts": {
    "prepare": "git config core.hooksPath .githooks"
  }
}

npm runs prepare automatically after npm install, which closes the gap for a JavaScript project.

Option 2: A Hook Manager

For a JavaScript project, Husky:

npm install --save-dev husky
npx husky init

That creates .husky/, commits it, and wires up the prepare script so every npm install installs the hooks. Adding one:

echo "npm test" > .husky/pre-push

For anything else, the pre-commit framework (Python, but language-agnostic):

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: check-merge-conflict
      - id: detect-private-key
      - id: check-added-large-files
pre-commit install

The value here is not the plumbing — it is the catalogue. detect-private-key, check-merge-conflict and check-added-large-files are exactly the checks you would eventually write by hand, already written and maintained by people who have seen every edge case.

The catch: a dependency, a version to keep current, and a config file to understand. Worth it for a team; overkill for a small repository where core.hooksPath will do.

Option 3: Server-Side Enforcement

The only option that cannot be skipped, because it does not run on the developer's machine.

Branch protection rules (GitHub, GitLab, Bitbucket): require a Pull Request, require approvals, require status checks to pass, block force-pushes. Configured once by an admin and applied to everybody.

Server-side hooks (pre-receive) on a self-hosted server: the same idea, arbitrary code.

CI pipelines: run the full suite on a clean machine, and mark the result as a required check.

This is what "the real gate" means. --no-verify reaches nothing here.

Where Each Check Belongs

Layer Speed Skippable Put here
pre-commit Seconds Yes Formatting, lint, secrets, debug leftovers
pre-push Under a minute Yes Fast unit tests, no-push-to-main
CI Minutes No Full suite, integration, e2e, coverage, security scan
Branch protection Instant No Required reviews, required checks, no force-push

The layers are not alternatives. A well-set-up project has all four, arranged so the cheap fast ones catch a problem first and the slow authoritative ones exist to be right.

The design principle: each layer catches what the one above it might have missed, and only the bottom two are guaranteed to run. Design the hooks for speed and convenience; design CI for correctness.

Proposing This to a Team

Three things make the difference between adoption and a rejected pull request.

Start with one hook that has no false positives. A no-push-to-main hook, or a secret scanner. Something nobody can argue with and nothing legitimate trips. Trust is built one hook at a time.

Make it opt-in first. Commit the hooks, document the one-line enable command, let people turn it on. When two people say it saved them, propose making it automatic.

Never make it slow. One colleague waiting sixty seconds to commit will disable it and tell everybody else to do the same. Speed is not a nice-to-have here; it is the adoption strategy.

And frame it honestly: hooks are a convenience that saves everyone time. They are not a control, and presenting them as one is how they get resisted.

Closing the Trail

You started this trail able to branch, merge and resolve a conflict. You can now rewrite history deliberately, move a single fix between branches, undo anything at the right depth, recover work everybody else would consider lost, find the commit that caused a regression in nine tests, and automate the checks that stop the whole cycle starting again.

That is the full set of Git operations a senior QA engineer uses. There is no Trail 4 waiting with the real secrets — from here it is practice, in a scratch repository first and a real one after.

Pro Tip: Pick one thing from this trail and use it this week. git bisect run on a real regression is the one with the largest immediate payoff, and it is the one that will get noticed.

Key Takeaways

  • .git/hooks/ is never committed, so hooks need deliberate sharing
  • core.hooksPath points Git at a committed directory — simple, but enabled per clone
  • Husky (JavaScript) and pre-commit (any language) automate installation and provide a check catalogue
  • Branch protection and CI are the only layers that cannot be skipped
  • Layer the checks: fast and skippable near the developer, slow and authoritative on the server
  • Adoption comes from one uncontroversial hook, opt-in first, and never being slow
  • Hooks are a convenience for the team, not a control over it

Quiz

What does git config core.hooksPath .githooks achieve?

Why is that approach not fully automatic?

Which layer of checks genuinely cannot be skipped by a developer?

What is the most effective way to get a team to adopt hooks?