Git Hooks & Automation
Let Git run your checks for you. By the end of this lesson you'll write a pre-commit hook that blocks bad commits, a commit-msg hook that enforces a message format, and you'll know how Husky shares hooks with your whole team.
Learn Git Hooks & Automation in our free Git course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Git course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
1️⃣ Where Hooks Live
Every repo has a .git/hooks directory full of .sample scripts Git ignores. To activate one, create a file with the real hook name (drop .sample ) and make it executable. The client-side hooks you'll use most are pre-commit , commit-msg , and pre-push .
2️⃣ A pre-commit Hook
The pre-commit hook runs before the commit is recorded — perfect for linting, formatting, or quick checks. The rule is simple: exit non-zero to abort the commit, exit 0 to allow it . Here's one that refuses any commit whose staged changes contain TODO .
3️⃣ A commit-msg Hook
The commit-msg hook receives the path to the message file as its first argument, so it can validate or reject the wording. This one insists every message references a ticket like JIRA-123 .
Your turn. Write a pre-commit hook that blocks the word DEBUG . Fill in the three blanks.
4️⃣ Sharing Hooks with Your Team
Here's the catch: .git/hooks isn't committed, so your hooks don't travel with clones — teammates won't have them. Tools solve this by keeping hooks in the tracked part of the repo. Husky stores them under .husky/ for JavaScript projects; the language-agnostic pre-commit framework uses a committed .pre-commit-config.yaml .
Common Errors (and the fix)
- Hook never runs: it isn't executable. Run chmod +x .git/hooks/pre-commit , and make sure the filename has no .sample extension.
- "cannot execute: required file not found": a missing or wrong shebang. Start the script with #!/bin/sh (or your interpreter).
- Teammates don't have your hooks: expected — .git/hooks isn't cloned. Use Husky or the pre-commit framework to share them.
- A hook blocks a commit you must make: bypass once with git commit --no-verify — but treat that as an escape hatch, not a habit.
- Relying on hooks for security: they're skippable locally. Back them with CI / protected-branch rules for real enforcement.
Pro Tips
- 💡 Keep pre-commit fast: lint and format the staged files only; save the full test suite for pre-push or CI.
- 💡 Point hooks at a tracked folder: git config core.hooksPath .githooks lets you commit hooks directly.
- 💡 Combine with CI: hooks catch issues early; GitHub Actions enforces them where they can't be skipped.
- 💡 Use ready-made hooks: the pre-commit framework has a big library for many languages.
📋 Quick Reference
Hook / Command
Purpose
.git/hooks/pre-commit
Run before a commit (lint/format)
.git/hooks/commit-msg
Validate the commit message ($1)
.git/hooks/pre-push
Run before a push (tests)
chmod +x <hook>
Make a hook runnable
git commit --no-verify
Skip hooks for one commit
npx husky init
Set up shared hooks (JS)
Frequently Asked Questions
Mini-Challenge: Enforce Quality Before Every Commit
No full scripts this time — just the plan. Build hooks that catch bad code and bad messages, then share them.
🎉 Lesson Complete!
- ✅ Hooks are scripts Git runs automatically at workflow events
- ✅ They live in .git/hooks and must be executable
- ✅ pre-commit blocks a commit by exiting non-zero
- ✅ commit-msg validates the message file passed as $1
- ✅ .git/hooks isn't shared — use Husky or the pre-commit framework
- ✅ Hooks are convenience, not security — back them with CI
- 🎓 You've completed the Git course — congratulations!
Practice quiz
What is a Git hook?
- A remote branch
- A merge conflict marker
- A script Git runs automatically at certain points, like before a commit
- A type of tag
Answer: A script Git runs automatically at certain points, like before a commit. Hooks are scripts Git runs on events such as committing or pushing, to automate or enforce steps.
Where do a repository's hooks live by default?
- .git/hooks
- .github/hooks
- .hooks
- the repo root
Answer: .git/hooks. Each repo's hooks live in the .git/hooks directory as executable scripts.
Which hook runs BEFORE a commit is created, often to lint or test?
- post-commit
- pre-push
- commit-msg
- pre-commit
Answer: pre-commit. pre-commit runs before the commit is recorded; a non-zero exit aborts the commit.
How does a pre-commit hook block a commit?
- By printing 'STOP'
- By exiting with a non-zero status code
- By deleting the file
- By returning true
Answer: By exiting with a non-zero status code. If the hook exits non-zero, Git aborts the commit; exit 0 lets it proceed.
Which hook can validate or reformat the commit MESSAGE?
- commit-msg
- pre-commit
- post-checkout
- pre-rebase
Answer: commit-msg. commit-msg receives the path to the message file and can check or reject it.
Why are hooks in .git/hooks NOT shared with teammates by default?
- They are encrypted
- They are too large
- The .git directory isn't committed or cloned
- Git deletes them on push
Answer: The .git directory isn't committed or cloned. The .git directory isn't part of the tracked tree, so its hooks don't travel with clones.
What must a hook script be in order to run?
- Named .hook
- Executable (and have a valid shebang)
- Written in Python
- Committed to main
Answer: Executable (and have a valid shebang). Git only runs hook files that are executable; a shebang like #!/bin/sh tells the OS how to run it.
What problem does a tool like Husky solve?
- It speeds up cloning
- It encrypts commits
- It hosts repos
- It version-controls and installs hooks so the whole team shares them
Answer: It version-controls and installs hooks so the whole team shares them. Husky keeps hooks in the repo and wires them up on install, so everyone gets the same checks.
The 'pre-commit' framework is configured with which file?
- hooks.json
- .pre-commit-config.yaml
- .gitconfig
- Makefile
Answer: .pre-commit-config.yaml. The pre-commit framework reads .pre-commit-config.yaml to manage multi-language hooks.
Which hook runs on the developer's machine just before code is sent to the remote?
- post-receive
- update
- pre-push
- post-merge
Answer: pre-push. pre-push runs locally before a push completes — handy for running the test suite first.
Continue this course
- Previous: Submodules & Monorepos
- Next: Multiple Working Trees with git worktree