Git Hooks
Git hooks are scripts that run automatically at specific points in Git's workflow, letting you enforce checks like linting or tests before a commit or push is allowed to proceed.
What Hooks Are and Where They Live
Every Git repository has a .git/hooks directory containing sample scripts for every hook Git supports, each ending in .sample so it's inactive by default. To enable a hook, remove the .sample suffix, make the file executable, and write your logic inside it, typically as a shell script, though any executable — Python, Node, a compiled binary — works.
Hooks fall into two broad categories: client-side hooks, which run on your local machine during actions like committing or pushing, and server-side hooks, which run on the remote server when it receives pushed commits. This lesson focuses on client-side hooks, since those are the ones you set up on your own machine.
The pre-commit Hook
The pre-commit hook runs before Git even opens the commit message editor, making it the ideal place to catch problems early: linting errors, formatting issues, accidentally committed secrets, or failing tests on the changed files. If the script exits with a non-zero status, the commit is aborted and nothing is recorded.
A basic pre-commit hook
$ cat .git/hooks/pre-commit
#!/bin/sh
npx eslint --max-warnings=0 $(git diff --cached --name-only --diff-filter=ACM | grep '\.js$')
$ git commit -m "Add new checkout button"
/src/checkout.js
12:5 error 'total' is never reassigned. Use 'const'
husky - pre-commit hook failed (add --no-verify to bypass)The pre-push Hook
The pre-push hook runs after commits are made but before they leave your machine, right when you run git push. It's a good place for slower checks that would be annoying on every single commit, like running a full test suite, since it only fires once per push rather than once per commit.
A pre-push hook running tests
$ cat .git/hooks/pre-push
#!/bin/sh
npm test
$ git push origin feature-branch
> npm test
Test Suites: 14 passed, 14 total
Tests: 112 passed, 112 total
Enumerating objects: 9, done.
To github.com:org/project.git
a1b2c3d..e4f5g6h feature-branch -> feature-branchMaking Hooks Executable
A hook script that isn't marked executable is silently ignored by Git — no warning, no error, it simply never runs. This is one of the most common reasons a hook "doesn't work" for a new contributor who copied a script but forgot this step.
Enable a hook script
$ chmod +x .git/hooks/pre-commit
$ ls -l .git/hooks/pre-commit
-rwxr-xr-x 1 dev dev 312 Mar 3 10:02 .git/hooks/pre-commitCommon Client-Side Hooks
Sharing Hooks with Your Team
Since .git/hooks lives inside the local .git directory, it is never committed or shared automatically through cloning. Teams that want consistent hooks across every contributor typically use a tool like Husky, which stores hook scripts in a tracked folder (commonly .husky) and installs them into .git/hooks via a setup step, often triggered automatically by npm install.
- Raw hooks live only in .git/hooks and are local to your clone by default
- Tools like Husky or pre-commit (the Python framework) track hook definitions in the repo itself
- A postinstall or setup script wires the tracked definitions into .git/hooks for every contributor
- This ensures a new clone gets the same checks without any manual copying
Exercise: Git Hooks
What is a Git hook?