Find the hooks directory
Every Git repository has a `.git/hooks/` folder with sample hooks. Rename a `.sample` file to activate it.
ls .git/hooks/
# applypatch-msg.sample pre-commit.sample
# commit-msg.sample pre-push.sample
# ...Git hooks are scripts that run automatically at specific points in the Git workflow — before or after commits, pushes, and merges. This tutorial shows you how to create and use hooks to enforce code quality, lint code, run tests, and automate repetitive tasks.
Developers on your team keep committing without running the linter, or pushing code that breaks the test suite. You want to enforce quality checks automatically — without relying on everyone to remember.
Git hooks are scripts that run automatically at key points in the Git lifecycle: before a commit, after a push, before a merge, and more. They are the simplest way to enforce coding standards, run tests, and automate repetitive tasks locally — before anything reaches the remote.
Common mistakes developers make with this:
Gitoryx: Gitoryx respects your local Git hooks and shows hook output directly in the commit panel, so you can see linting errors inline without leaving the app.
Git hooks are executable scripts stored in `.git/hooks/` that Git runs automatically at specific events: `pre-commit`, `commit-msg`, `pre-push`, `post-merge`, and more. They can block an action (by exiting non-zero) or simply execute side effects.
Every Git repository has a `.git/hooks/` folder with sample hooks. Rename a `.sample` file to activate it.
ls .git/hooks/
# applypatch-msg.sample pre-commit.sample
# commit-msg.sample pre-push.sample
# ...A `pre-commit` hook runs before Git records the commit. If it exits with a non-zero status, the commit is aborted. This is ideal for running linters.
# .git/hooks/pre-commit
#!/bin/sh
npm run lint
if [ $? -ne 0 ]; then
echo "Linting failed. Commit aborted."
exit 1
fiMake the file executable: `chmod +x .git/hooks/pre-commit`
The `commit-msg` hook receives the path to the file containing the commit message. Use it to enforce a message format.
# .git/hooks/commit-msg
#!/bin/sh
MSG=$(cat "$1")
PATTERN="^(feat|fix|chore|docs|style|refactor|test|ci)(\(.+\))?: .+"
if ! echo "$MSG" | grep -qE "$PATTERN"; then
echo "Commit message must follow Conventional Commits format."
echo "Example: feat(auth): add login validation"
exit 1
fiThe `pre-push` hook runs before `git push` sends commits to the remote. Use it to run the full test suite.
# .git/hooks/pre-push
#!/bin/sh
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Push aborted."
exit 1
fiKeep pre-push hooks fast. A slow test suite in a hook will frustrate developers and encourage them to use `--no-verify`.

See this workflow in Gitoryx. Pre-commit hook errors appear directly in the commit panel
Free downloadBecause `.git/hooks/` is not tracked by Git, hooks must be installed on each machine. Husky is the most popular tool for sharing hooks via `package.json`.
# Install Husky
npm install --save-dev husky
npx husky init
# Add a pre-commit hook
echo "npm run lint" > .husky/pre-commitHusky hooks live in `.husky/` which is committed to the repo — everyone on the team gets the hooks automatically after `npm install`.
You can skip hooks with `--no-verify`. Use this sparingly — for example when committing a WIP in an emergency.
git commit --no-verify -m "wip: emergency fix"
git push --no-verifyBypassing hooks defeats their purpose. Make sure your hooks run fast enough that the team never feels the need to skip them regularly.
Git silently ignores hooks that don't have the execute bit set.
Fix: Run `chmod +x .git/hooks/<hook-name>` after creating the script.
`.git/hooks/` is not committed. Other developers don't get your hooks when they clone the repo.
Fix: Use Husky (or lefthook, lint-staged) to store hooks in the repo and install them via a `prepare` npm script.
Running the full test suite on every commit creates friction and pushes developers to use `--no-verify`.
Fix: Run only fast checks (lint, type-check) in pre-commit. Reserve slower checks (full tests) for pre-push or CI.

No. Files in `.git/hooks/` are not tracked by Git. To share hooks with your team, use a tool like Husky that stores hook scripts in the repo and installs them via `npm install`.
Add the `--no-verify` flag: `git commit --no-verify`. This skips all pre-commit and commit-msg hooks. Use it sparingly.
`pre-commit` runs before Git even reads your commit message — ideal for running linters or tests. `commit-msg` receives the commit message file and runs after you've typed the message — ideal for enforcing message format.
Husky is a JavaScript package that manages Git hooks in a way that can be committed and shared via `package.json`. If you're working in a Node.js/JavaScript project, Husky is the standard way to share hooks across the team.
Everything in this tutorial is faster and clearer with a visual Git client. Gitoryx is free, runs natively on macOS, Windows, and Linux, and built for developers who want to move fast without breaking things.