Git hooks live in a folder that is not part of the repository, so they are not shared when someone clones it. You can point Git at a tracked folder with the core.hooksPath setting, which lets a team ship the same hooks to everyone, though each developer must still opt in once.
The problem with the default location
By default, the git documentation says, the hooks directory is $GIT_DIR/hooks. That sits inside the hidden Git folder and is never committed. If you write a good hook there, only your machine has it.
Teams often work around this with a README that says "copy these scripts". That works for about a week.
Using a tracked folder
The same page says the location "can be changed via the core.hooksPath configuration variable". So you can keep hooks in the repo and point Git there.
mkdir .githooks
# add your hook files, for example .githooks/pre-commit
chmod +x .githooks/pre-commit
git config core.hooksPath .githooks
Two details from the documentation matter here. Hooks without the executable bit set are ignored, so a hook that silently does nothing is often a missing chmod. And the setting is a normal Git configuration value, so it lives in a developer's local config, not in the repo.
Why the opt in step matters
Because the config is local, cloning the repo does not turn the hooks on. A new teammate has to run the command once. This is deliberate: a repository that could silently change your hooks path on clone would be a way to run code on your machine.
That also means you should read hook scripts in a pull request with the same care as any code that runs on a developer laptop. A hook is a script that executes with the developer's permissions.
Make setup one command
Put the command in a script or Makefile target and mention it in the contributing guide.
./scripts/setup-hooks.sh # runs: git config core.hooksPath .githooks
A check that tells people when hooks are missing helps too. A CI job that runs the same checks catches anyone who skipped setup, which ties back to the point that local hooks need a CI backstop.
Framework or plain scripts
A framework such as pre-commit installs its own hook scripts with pre-commit install and handles versions of hooks for you. Plain scripts in a tracked folder are simpler and have no extra dependency, but you maintain everything yourself.
Pick plain scripts for two or three tiny checks. Pick a framework once you want many hooks across languages.
A worked example
Suppose your team wants one hook that refuses a commit when a file named like an environment file is staged. You write a ten line script in .githooks/pre-commit, mark it executable, and add a line to the setup script. New joiners run the setup once, and the hook is in place for every later commit.
When the rule changes, you edit the script in a pull request. Everyone gets the update on their next pull, with no reinstall. That is the real gain over copying scripts around: hooks become reviewed, versioned code like everything else.
Quick check
Why is my shared hook not running?
Check three things: the file name matches a hook name exactly, the executable bit is set because Git ignores hooks without it, and core.hooksPath points at the folder on that machine.
Can I set core.hooksPath for everyone from the repo?
Not from tracked files. It is a Git configuration value that each developer sets locally, which is why a setup script is the usual answer.
Sources
Last reviewed: 19 October 2026.