feat: standardize commit message format - #234
Open
gregggreg wants to merge 6 commits into
Open
Conversation
Dependency-free bash hook enforcing <type>[(<scope>)][!]: <description>, derived from existing history rather than invented: 132 of the 174 commits on main already used a Conventional Commits prefix. Ships with a 35-case fixture harness, a .gitmessage commit template, and an extended `make install-hooks` that wires both hooks and the template. Nightshift-Task: commit-normalize Nightshift-Ref: https://github.com/marcus/nightshift
Adds CONTRIBUTING.md with the format spec, type and scope tables, breaking change rules, agent trailers, and the grandfathering decision. Cross-links from README.md and AGENTS.md. Nightshift-Task: commit-normalize Nightshift-Ref: https://github.com/marcus/nightshift
Validates only origin/<base>..HEAD so grandfathered history never fails CI, and runs the validator's own fixture suite as a regression check. Nightshift-Task: commit-normalize Nightshift-Ref: https://github.com/marcus/nightshift
Three defects found in review of the initial validator: Trailing whitespace defeated the no-trailing-period and length rules. "feat: add thing. " was accepted, then git's own cleanup — which runs after the hook — stripped the space and landed "feat: add thing." on the branch, exactly the form the rule forbids. Each line is now right-trimmed as it is read, mirroring git's cleanup. The lowercase rule was implemented as ^[a-z], which rejected "2x faster lookups", "HTTP retry support" and "OAuth token refresh". The intent was "not sentence-cased", so that is now what is checked: a capital is only an error when a lowercase letter follows it immediately. Docs, template and the reject message were reworded to match. Also honor core.commentChar / core.commentString instead of hardcoding "#", and resolve the hooks directory via git rev-parse in install-hooks so it works inside linked worktrees, where .git is a file. Verified: 43/43 fixture tests pass under bash 3.2 and bash 5. Nightshift-Task: commit-normalize Nightshift-Ref: https://github.com/marcus/nightshift
The figures were measured on the feature branch rather than on main, so they counted this PR's own commits: 174/132/113 instead of 171/129/110. The 61-rejection figure was correct either way. These numbers are the stated evidence for deriving the convention from existing history rather than imposing one, so they need to reproduce. Verified against `git rev-list --count main` and by replaying every subject on main through scripts/commit-msg.sh. Nightshift-Task: commit-normalize Nightshift-Ref: https://github.com/marcus/nightshift
install-hooks resolved link targets with `git rev-parse --show-toplevel`,
which is the *current* worktree. The hooks directory is shared across all
worktrees, so running `make install-hooks` from a linked worktree pointed
.git/hooks/{pre-commit,commit-msg} into that worktree. Removing the
worktree left both symlinks dangling, and git skips broken hooks silently
-- disabling commit-msg validation and the pre-existing gofmt/vet/build
pre-commit checks with no error.
Resolve the main worktree via --git-common-dir/.. instead, which stays
valid after any linked worktree is removed.
Also document how to reproduce the history statistics, and note that the
129 figure counts any parenthesised scope (a stricter count is 128; the
sole difference is `fix(marcus#19)`, whose scope the validator rejects).
Nightshift-Task: commit-normalize
Nightshift-Ref: https://github.com/marcus/nightshift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Standardizes the commit message format going forward, without rewriting history.
The convention is derived, not invented. Of the 171 commits on
main, 129 (75%)already use a Conventional Commits prefix. This PR writes that de facto convention
down and enforces it on new commits.
What lands
scripts/commit-msg.shscripts/commit-msg-test.sh.gitmessagemake install-hooksCONTRIBUTING.md.github/workflows/ci.ymlFormat:
<type>[(<scope>)][!]: <description>, subject ≤ 72 chars, blank line beforeany body. Types:
feat fix docs chore test refactor perf build ci style revert.Design decisions
History is grandfathered. 61 subjects on
maindo not pass — merge commits,Bump version to ..., and otherwise-valid subjects pushed past 72 chars by anappended
(#42)ref. Rewriting them is destructive and out of proportion, so CIvalidates only
origin/<base>..HEAD. A non-conformingmaincan never fail CI.Local hooks are opt-in. Nothing installs automatically;
--no-verifybypasses.CI is the actual gate.
Git-generated messages are exempt — merge, revert,
fixup!/squash!/amend!.Review fixes (iteration 2)
feat: add thing.was accepted, thengit's cleanup — which runs after the hook — stripped the space and landed
feat: add thing.on the branch, exactly the forbidden form. Lines are nowright-trimmed as they are read, mirroring git's cleanup.
^[a-z]over-rejected. It refusedfix: 2x faster lookups,feat: HTTP retry supportandfeat: OAuth token refresh. The intent was"not sentence-cased", so that is now what is checked: a capital is only wrong when
a lowercase letter immediately follows.
main, so they counted this PR'sown commits (174/132/113). Corrected to 171/129/110, which reproduce.
core.commentChar/core.commentStringrather than hardcoding#, andresolve the hooks dir via
git rev-parsesomake install-hooksworks insidelinked worktrees, where
.gitis a file.Verification
make build,go vet ./...,go test ./...pass;gofmt -l .empty (no Go changes)git commitin a scratch repo: sentence-case and trailing-period-past-spacerejected with clear diagnostics; acronym and leading-digit subjects accepted;
commit -vdiff correctly stripped at the scissors markermainsubjects yields exactly the 61 documented grandfatheredrejections — no surprises
Draft #186 (
chore: normalize commit messages) covers overlapping scope with adifferent design — a
.githooks/directory plus a normalizer that rewrites subjectsin place, rather than a validator that rejects them. It also targets
docs-backfillrather than
mainand has not been updated since 2026-07-15.The two should not both land. Key difference worth a maintainer decision: #186
rewrites your message silently; this PR rejects it and tells you why. I favor
rejection — silent rewriting of a commit subject is surprising — but that is a call
for the maintainer, not this PR.
🤖 Generated with Claude Code