Turn the staged changes into one or more atomic Conventional Commits, splitting unrelated concerns and writing messages that explain WHY.
Inspect first
- Run
git diff --cached --statthengit diff --cachedto read every staged hunk. Rungit statusto see staged vs unstaged and to catch an in-progress merge/rebase. - If nothing is staged, stop and report: "Nothing staged. Stage changes with
git addfirst." Do not stage files yourself unless the user explicitly asked. - If
git statusshows a merge or rebase in progress (MERGING/REBASE, or a.git/MERGE_HEAD), stop and report it — do not hand-craft a Conventional message; let the user conclude that operation (usually a plaingit commitwith the prepared message). - Detect conventions before writing: skim
git log --oneline -30for the type/scope vocabulary in use, and honor commit hooks,commitlint, orCONTRIBUTINGrules. Match observed style over the defaults below when they conflict. Ifgit logis empty (first commit), fall back to these defaults.
Group into atomic commits
- One commit = one logical, self-contained change a reviewer could revert in isolation without breaking unrelated features.
- Split these as separate concerns: distinct features, a refactor vs a behavior change, production code vs unrelated formatting/whitespace, dependency bumps, and generated/lockfile churn.
- If the diff is genuinely one concern, make a single commit and skip the re-staging below.
- To split, remember everything is ALREADY staged, so
git add -palone is a no-op and a baregit commitsweeps in every group. First remove the later groups from the index:- File-level:
git restore --staged <paths-for-later-commits>to unstage them,git committhe group left in the index, then re-stage and commit the next. Alternative: keep the index intact and commit one group at a time withgit commit <pathspec> -m ..., which commits only the named paths. - Hunk-level (one file spans commits):
git restore --staged <file>, thengit add -p <file>to re-stage only this commit's hunks, commit, and repeat for the remaining hunks.
- File-level:
Message format (Conventional Commits)
- Subject:
type(scope): summary— imperative mood ("add", not "added"/"adds"), no trailing period, whole line <=72 chars. Start the summary lowercase unless it opens with a proper noun or the repo'sgit logclearly capitalizes subjects (case is a lint choice, not part of the spec — follow the repo). type: feat, fix, refactor, perf, docs, test, build, ci, chore, style, revert.scopeis optional; use the module/package/area touched, matching scopes seen ingit log.- Body (blank line after subject, wrap at 72 cols): explain WHY the change is needed and what it solves — the diff already shows what changed. Note user-visible effects and non-obvious tradeoffs. Omit only for truly trivial changes (typo, version bump).
- Footer:
BREAKING CHANGE: <description>for any incompatible API/behavior change (or append!after type/scope). Reference issues the user mentions (Closes #123); never invent numbers. - Example of a full message:
feat(auth)!: require TOTP on password reset Reset links alone were phishable: a stolen link fully took over an account. Gating reset completion behind an existing TOTP factor closes that path. Users without TOTP fall back to email plus SMS. BREAKING CHANGE: password reset now needs a verified TOTP factor; accounts without one must enroll before they can reset. Closes #482
Rules
- Never commit unless changes are staged; never
git add -A/git add .to sweep in files the user did not stage. - Never amend, force, rebase, or push unless the user explicitly asks.
- Never add "Generated with" trailers, co-author lines, or advertising unless the repo's convention already does.
- Write real messages: no placeholders, no "update files", no restating the filename. If you cannot state why a change exists, ask rather than guess.
- Pass multi-line messages via repeated
-mflags or a heredoc-fed-Ffile; do not embed literal\n.
Output
Before committing, print the plan: for each commit show the subject line, the files/hunks it includes, and a one-line rationale. Run the commits, then show git log --oneline -n <count> for what you created.