commit-message
Turn the staged diff into a commit message that actually tells the reader what changed and, when needed, why. Follows the codebase's convention automatically. Refuses to write a message for a diff that shouldn't be one commit.
When to use this
- "Write a commit message"
- "Commit this"
- "What should this commit say"
- User is about to
git commitwith no-mand asks for help
Procedure
1. Read the actual staged changes
git diff --cached --stat
git diff --cached
git status --short
If nothing is staged (git diff --cached is empty), stop and ask the user
what to stage. Do not run git add -A on their behalf — you don't know what
they want in this commit.
If there are unstaged changes alongside staged ones, note that to the user (possible split-commit situation).
2. Detect the codebase's convention
Check git log --pretty=format:"%s" -30 for the pattern in the last ~30 commits:
- Conventional Commits — subjects like
feat:,fix:,docs:,chore:,refactor:, sometimes with a scope (fix(auth): …). Very common in JavaScript/TypeScript ecosystems and any project with a CHANGELOG generator. - Gitmoji / emoji prefix — starts with an emoji, e.g.
🐛 fix crash on load. - Plain imperative — no prefix, just verb-first sentence, e.g.
Fix crash on load when config missing. Common in older projects, Rails, and many system-level codebases. - Trailer conventions — some projects use
Signed-off-by:orRefs: #123trailers; match if present.
Match whichever the history uses. Do not impose Conventional Commits on a repo that has never used them.
If there's a CONTRIBUTING.md, COMMITS.md, or .gitmessage in the repo,
read those first — explicit convention beats detected convention.
3. Write the subject line
Rules that apply to every convention:
- Imperative present tense. "Fix" not "Fixed". "Add" not "Added". "Refactor" not "Refactored". The convention is that the subject completes: "If applied, this commit will _______".
- ≤ 72 characters hard, ≤ 50 preferred. Terminals wrap at 72; GitHub UI truncates around 50.
- No trailing period.
- Capitalize first word after any prefix.
- Specific over generic. "Fix off-by-one in pagination cursor" beats "Fix bug". "Update dependency" is almost never right — say what for and to what version.
If using Conventional Commits, pick the type from the actual change:
feat— user-visible new capabilityfix— user-visible bug fixeddocs— documentation only (READMEs, comments, guides)refactor— internal restructure with no behavior changetest— tests added or fixedperf— performance improvement without behavior changechore— build tooling, deps, configsstyle— formatting only, no logicci— CI pipeline changesrevert— reverts a previous commit
If the diff spans multiple types (feat + fix + refactor together), that's a sign this should be multiple commits. Say so; offer to help split them.
4. Decide whether a body is needed
Write a body when — and only when — the subject alone leaves an obvious "why?" or "why this way?" unanswered. Examples:
- Non-obvious tradeoff was made
- Change is smaller than the discussion around it (fix that took hours to find — one-line diff, worth explaining what was wrong)
- Breaking change (mandatory body noting the break and migration path)
- Change is easy to misread from the diff alone
- Change references an issue or ADR the reader should see
Skip the body when:
- Change is small and self-explanatory
- Change is one of many similar (tenth typo fix, third dep bump)
If you write a body:
- Blank line between subject and body
- Wrap at 72 columns
- Explain why, not what. The diff shows what.
- Reference issues/PRs at the bottom:
Refs: #123orCloses #123
5. Refuse to lie or leak
Before showing the message, verify:
- No fabricated context. If the diff doesn't tell you why the change was made and there are no other clues (issue link in branch name, TODO comments, related recent commits), ask the user for the "why" instead of guessing.
- No secrets in the diff. If the diff appears to contain API keys, tokens,
or
.env-shaped strings, halt. Do not write a message. Tell the user which lines look sensitive. - No
wip,fix stuff,updatesas the whole subject. Even if the user says "just commit it", offer a real message and let them override.
6. Present and offer to commit
Print the full proposed message (subject + body if any). Offer:
- "Commit as-is" — run
git commit -m "<subject>" -m "<body>" - "Edit first" — open in editor via
git commit(no -m) - "Split" — if step 3 flagged multiple types, help split into separate staged sets and commit each
Do not run git commit without explicit approval, and never rewrite history
(--amend) unless the user asks for it.
Anti-patterns
- Do not just paraphrase the file names ("Update foo.js and bar.js").
- Do not use marketing language ("Improve performance dramatically"). Say what by how much: "Cut cold-start time by 40% by caching schema load".
- Do not use
--no-verifyor--no-gpg-signunless the user asks — respect their hook and signing setup. - Do not squash unrelated changes into one commit. If the diff is doing two things, offer to split.