Create Commit
Turn a set of staged changes into a single, well-formed Conventional Commit. The
message format isn't cosmetic: semantic-release
parses the commit type and BREAKING CHANGE marker to decide the next version
number and to build the changelog. A wrong type means a wrong release, so the type
must honestly describe the change.
Workflow
- Check the branch first. Run
git rev-parse --abbrev-ref HEADand resolve what's protected: the repo's default branch (gh repo view --json defaultBranchRef -q .defaultBranchRef.name), plusmain,master,develop, andrelease/*branches. Committing straight to any of them is off-limits — create or switch to a feature branch first, using the create-branch skill when it's available. - See what's staged. Run
git diff --staged --stat(thengit diff --stagedfor detail). If nothing is staged, stop and ask the user what to stage rather than guessing — nevergit add -Aon their behalf. When staging on the user's behalf, stage explicit paths and watch for unrelated untracked files sitting in the worktree: they belong to other work and must not ride along in this commit. - Pick the type from the diff, not the intent. A change that adds a feature
is
feat; a change that only fixes behavior isfix; a docs-only change isdocs, etc. When a commit genuinely does two things, that's usually a sign it should be two commits — mention that to the user. - Add a scope if it clarifies. Scope is the area of the codebase touched
(
api,auth,deps, a module name). Omit it when it wouldn't add signal. - Extract the ticket ID from the branch. Pull an ID like
PUB-4204out of a branch name such asfeat/PUB-4204-site-creation. If one exists, reference it in the footer (Closes PUB-4204). If the branch has none, skip the footer — don't invent one. - Compose the message (see format below).
- Commit after review. Since the diff has been reviewed in step 2 (and, when
the local-review skill is available, given a proper review pass first),
create the commit with
git commit. Never bypass hooks with--no-verify— a failing hook is feedback, not an obstacle. Report the resulting message back to the user.
After the commit
More work on the same feature loops back here, one reviewed commit at a time. When the branch is complete, the raise-pr skill pushes it and opens the pull request; local-pr-review before raising catches what per-commit review missed.
Message format
<type>(<optional scope>): <subject>
<optional body>
<optional footer>
Build it with one -m per block:
git commit -m "<type>(<scope>): <subject>"
or, when a body and footer are warranted, one -m per block:
git commit -m "<type>(<scope>): <subject>" -m "<body>" -m "<footer>"
Subject line rules and the reasoning behind them:
- Imperative mood ("add", not "added"/"adds") — it reads as "this commit will ", matching how git itself phrases things.
- Lowercase type and subject (proper nouns/acronyms excepted), no trailing period — this is what the Angular preset's parser and changelog generator expect.
- ≤ 72 characters, ideally ~50. Longer detail belongs in the body.
- Put the why and any context in the body; keep the subject to the what.
Types and their release impact
Only feat and fix (and breaking changes) produce a release. The rest still
appear in the changelog but don't bump the version.
| Type | Meaning | Release |
|---|---|---|
feat |
A new feature | minor (0.x.0) |
fix |
A bug fix | patch (0.0.x) |
docs |
Documentation only | none |
style |
Formatting, no logic change | none |
refactor |
Neither fixes a bug nor adds a feature | none |
perf |
Performance improvement | none |
test |
Add or correct tests | none |
build |
Build system or dependencies | none |
ci |
CI config and scripts | none |
chore |
Anything not touching src/test | none |
revert |
Reverts a previous commit | none |
Breaking changes
A breaking change forces a major release. Signal it either way:
- Add
!after the type/scope:feat(api)!: switch response to array shape, or - Add a
BREAKING CHANGE:paragraph in the footer describing the break and the migration.
Prefer the footer when consumers need migration instructions.
Examples
Simple feature:
git commit -m "feat: add user authentication module"
Fix with scope:
git commit -m "fix(api): resolve null pointer in user endpoint"
Body explaining the why, plus a ticket footer:
git commit -m "refactor(fetchers): restructure with shared base class" \
-m "Move report_date handling into GenericFetcher and add GenericReportFetcher as an intermediate abstract class to cut duplication." \
-m "Closes PUB-4204"
Breaking change via footer:
git commit -m "feat(api): change response format to array-based structure" \
-m "BREAKING CHANGE: responses now return an array instead of an object; update all API consumers accordingly."
Revert:
git commit -m "revert: feat(api): change response format" \
-m "This reverts commit abc123def456."