Commit-msg Hook: Enforcing Message Format

A real commit, genuinely rejected for missing a ticket reference. Then genuinely accepted.

git runs a real script at .git/hooks/commit-msg (if one exists and is executable) every single time a commit is attempted, passing it the path to a real temp file holding the proposed message. If the script exits non-zero, the real commit is genuinely rejected — no exceptions, no way around it short of --no-verify.\n\nThis lab writes a real hook requiring every commit message to start with a ticket reference like RECIPE-123: . A real commit without one gets rejected on the spot; the identical real change, committed with the right prefix, goes through immediately.

A Real, Executable Hook

#!/bin/sh
if ! grep -qE "^[A-Z]+-[0-9]+: " "$1"; then
  echo "ERROR: commit message must start with a ticket reference, e.g. 'RECIPE-123: ...'"
  exit 1
fi

git passes the proposed commit message's real file path as $1. A non-zero exit genuinely blocks the commit.

A Real Rejection

git commit -m "add meal prep containers guide"
# ERROR: commit message must start with a ticket reference...

Rejected immediately — the real commit never happens, the staged changes stay staged.

The Same Real Change, Now Accepted

git commit -m "RECIPE-301: Add meal prep containers guide"

Identical content, real ticket prefix — the hook lets it straight through.

commit-msg hook

A real, local script git runs automatically on every commit attempt, given the proposed message's file path. Confirmed directly in this lab: a non-zero exit genuinely blocked the commit before it was created.

Hooks are local, not shared

A hook committed inside a repository's tracked files is never automatically installed for anyone else — .git/hooks/ itself is never part of a real clone. Real teams distribute hooks separately (a setup script, or a tool like Husky).

🪝 Write the Real Hook

A real, executable script enforcing the ticket-reference format.

cd ~/team-recipes && printf '#!/bin/sh\nif ! grep -qE "^[A-Z]+-[0-9]+: " "$1"; then\n  echo "ERROR: commit message must start with a ticket reference, e.g. '"'"'RECIPE-123: ...'"'"'"\n  exit 1\nfi\n' > .git/hooks/commit-msg
cd ~/team-recipes && chmod +x .git/hooks/commit-msg
cd ~/team-recipes && cat .git/hooks/commit-msg

student@lab:~$ cd ~/team-recipes && printf '#!/bin/sh\nif ! grep -qE "^[A-Z]+-[0-9]+: " "$1"; then\n echo "ERROR: commit message must start with a ticket reference, e.g. '"'"'RECIPE-123: ...'"'"'"\n exit 1\nfi\n' > .git/hooks/commit-msg student@lab:~$ cd ~/team-recipes && chmod +x .git/hooks/commit-msg student@lab:~$ cd ~/team-recipes && cat .git/hooks/commit-msg #!/bin/sh if ! grep -qE "^[A-Z]+-[0-9]+: " "$1"; then echo "ERROR: commit message must start with a ticket reference, e.g. 'RECIPE-123: ...'" exit 1 fi

🚫 A Real Rejection

No ticket prefix — the hook genuinely blocks it.

cd ~/team-recipes && echo 'Meal Prep Containers' > meal-prep-containers.md
cd ~/team-recipes && git add meal-prep-containers.md
cd ~/team-recipes && git commit -m "add meal prep containers guide"

student@lab:~$ cd ~/team-recipes && echo 'Meal Prep Containers' > meal-prep-containers.md student@lab:~$ cd ~/team-recipes && git add meal-prep-containers.md student@lab:~$ cd ~/team-recipes && git commit -m "add meal prep containers guide" ERROR: commit message must start with a ticket reference, e.g. 'RECIPE-123: ...'

✅ The Same Real Change, Accepted

Add the real ticket reference — the hook lets it through.

cd ~/team-recipes && git commit -m "RECIPE-301: Add meal prep containers guide"
cd ~/team-recipes && git log --oneline -1

student@lab:~$ cd ~/team-recipes && git commit -m "RECIPE-301: Add meal prep containers guide" [master 0d592cc] RECIPE-301: Add meal prep containers guide 1 file changed, 1 insertion(+) create mode 100644 meal-prep-containers.md student@lab:~$ cd ~/team-recipes && git log --oneline -1 0d592cc (HEAD -> master) RECIPE-301: Add meal prep containers guide

Lab 3.6.1 complete. A real commit-msg hook, genuinely enforcing the team's format:\n\n\n Hook installed : ✅ real, executable\n No ticket ref : ✅ genuinely rejected\n With ticket ref : ✅ genuinely accepted\n

Enable JavaScript to run the live terminal and track your progress.