Opinionated conventional commit message linter with imperative mood detection.
Unlike regular expression only tools, commit-guard uses NLP (nltk POS tagging) to verify that commit descriptions start with an imperative verb.
$ commit-guard
✗ subject does not match 'type(scope): description':
Merge pull request #5 from fix/branch
✗ missing 'Signed-off-by' trailer
✗ commit is not signed (GPG/SSH)From PyPI:
uv tool install git-commit-guardor:
pipx install git-commit-guardFrom a local clone:
uv tool install -e .During development:
uv run commit-guard# check HEAD
commit-guard
# check specific commit
commit-guard abc1234
# check commit message file (for git hooks)
commit-guard --message-file .git/COMMIT_EDITMSG
# pipe message via stdin
echo "fix(auth): add token refresh" | commit-guardAll checks run by default. Use --enable or --disable with
comma-separated values:
# only check subject format and imperative mood
commit-guard --enable subject,imperative
# skip body and signature checks
commit-guard --disable body,signed-off,signatureAvailable checks:
subject- Format matchestype(scope): description, valid type, lowercase start, no trailing period, max 72 charsimperative- First word is an imperative verb (for exampleaddnotadded)body- Body is present after a blank linesigned-off-Signed-off-by:trailer existssignature- Verify GPG or SSH signature
# all non-merge commits between tags
git rev-list --no-merges v1.0..v2.0 | while read -r rev; do
commit-guard "$rev" || git log -1 --oneline "$rev"
done
# only subject checks on a PR range
git rev-list --no-merges origin/main..HEAD | while read -r rev; do
commit-guard "$rev" --enable subject,imperative
doneAdd to your .pre-commit-config.yaml:
---
repos:
- repo: https://github.com/benner/commit-guard
rev: v0.1.0
hooks:
- id: commit-guard
- id: commit-guard-signatureInstall the hooks:
pre-commit install --hook-type commit-msg --hook-type post-commitcommit-guard runs at the commit-msg stage and checks message format.
commit-guard-signature runs at the post-commit stage and verifies
the GPG/SSH signature after the commit object is created.
To selectively enable or disable checks, pass args:
- id: commit-guard
args: ["--enable", "subject,imperative"]commit-guard combines two strategies to detect non-imperative descriptions:
- nltk POS tagging — flags words tagged as past tense (
VBD), gerund (VBG), third person (VBZ), etc. - WordNet morphology as a fallback for words the tagger misclassifies.
This catches common mistakes like added logging or fixes bug while
keeping false positives low.
type(scope): description
body
trailers
Supported types: feat, fix, docs, style, refactor, perf, test,
build, ci, chore, revert.
Scope is optional. Mark breaking changes with ! before
the colon.
GPLv2