what a hook actually is

A hook is just an executable script git runs automatically at a specific point in its workflow — no plugin system, no config format, just a file with the right name in the right place. If the script exits non-zero, git stops whatever it was doing (a commit, a push) right there.
ls .git/hooks | https://git-scm.com/docs/githooks | every repo starts with a full set of *.sample files showing what's available |'gh_ls1'
Hooks live inside .git/, which is not tracked by git itself — cloning a repo does not bring its collaborators' hooks along. That single fact is why "sharing hooks across a team" is its own problem, covered further down.

the client-side hooks you'll actually use

HookRunsCommon use
pre-commitbefore a commit message is even openedlint/format the staged diff, block debug prints
commit-msgafter the message is written, before the commit is createdenforce a message format (e.g. conventional commits)
pre-pushbefore anything is sent to a remoterun the test suite, block pushes to protected branches
post-checkoutafter switching branchesreinstall dependencies if the lockfile changed

a real pre-commit hook

Save this as .git/hooks/pre-commit and make it executable. It blocks a commit if any staged Python file still has a debugger breakpoint in it.

#!/bin/sh
if git diff --cached --name-only | grep '\.py$' | xargs grep -l 'breakpoint()' 2>/dev/null; then
  echo "Commit blocked: breakpoint() found in a staged file."
  exit 1
fi
            
chmod +x .git/hooks/pre-commit | | git only runs hook files that are marked executable |'gh_chmod1'

sharing hooks across a team

Since .git/hooks/ isn't tracked, the standard fix is to keep your real hook scripts in a normal, version-controlled folder and point git at it instead.
git config core.hooksPath .githooks | https://git-scm.com/docs/githooks | tells git to run hooks from a tracked folder instead of .git/hooks |'gh_share1'
Everyone who clones the repo and runs that one config command gets the same hooks. In practice, most teams reach for a framework instead of managing this by hand: the pre-commit framework (Python-ecosystem-agnostic, config in .pre-commit-config.yaml) and husky (JS-ecosystem, installs itself via npm) both automate the core.hooksPath step and manage a list of checks for you.

server-side hooks, briefly

pre-receive and post-receive run on whatever machine hosts the remote, not on a contributor's laptop — they can reject a push outright before it's accepted. Self-hosted git servers use these directly; on GitHub, this exact job is done instead by branch protection rules and required status checks (see the GitHub cheat sheet's branch protection section), since you don't have direct access to GitHub's server-side hooks.

related topics

Git Cheat Sheet — the day-to-day commands these hooks intercept.
GitHub Cheat Sheet — branch protection and status checks, GitHub's server-side equivalent.
Debugging — the breakpoints and print statements a pre-commit hook is often built to catch.

reference

git-scm.com — githooks
pre-commit.com
husky