Git Commit
Workflow
IMPORTANT: Follow these steps in order. Do not skip or reorder unless a step explicitly says to.
Check what is staged and what isn't:
git status --shortNothing to commit at all, stop and tell the user. Nothing staged, run
git add -A.Read the staged diff and group it per the Splitting commits rules:
git diff --cachedOne group, proceed. Multiple groups, tell the user how many commits are coming, then stage only the first group:
git reset HEAD git add <file1> <file2>Commit with a message built per the Commit message rules:
git commit -m "<message>"If unstaged changes remain, repeat from step 1.
Commit message
Format is <type>(<scope>): <description>, with ! after the scope for a breaking change.
- Scope is never optional: Always in parentheses, always the area touched.
- Description is present tense imperative, no period, ≤50 chars: "add OAuth2 login flow", not "added OAuth2 login flow."
- No generic messages: Never "update code", "fix bug", or "changes".
- No attribution: Never add
Co-Authored-Bytrailers, footers, or tool signatures.
Types:
feat: A capability the project didn't have before. Renaming, restyling, or reorganizing an existing feature is not feat.fix: Correcting broken or incorrect behavior.docs: Documentation only.refactor: Restructuring without changing behavior, including restyling and layout changes that add nothing new.perf: Performance improvements.chore: Routine upkeep. Versions, lockfiles, renames, linting, formatting.test: Adding or updating tests.ci: CI/CD pipelines and workflow files.revert: Undoing a previous commit.
Examples:
- feat(auth): add OAuth2 login flow
- fix(api): handle null response from payment gateway
- docs(readme): add setup instructions
- refactor(db): extract query builder into separate module
- perf(search): add index on users.email column
- chore(deps): bump express to v5
- chore(lint): apply prettier formatting
- test(auth): add unit tests for token refresh
- ci(deploy): add staging environment workflow
- feat!(api): change response format to JSON:API
Splitting commits
- Atomic commits: A commit contains only related changes that serve a single purpose.
- Split when concerns diverge: Different areas, different types (feat vs fix vs refactor), different categories (source vs docs), or simply large enough that splitting aids review.
Guardrails
- Never bypass hooks: No
--no-verify, no--force. A hook is the project's own check on what enters history, and skipping it commits something the team decided should not be committable. - Never modify git config: It changes the user's behavior in every other repo and outlives this task.
- Never stage secrets:
.env, credentials, private keys. A commit is hard to unpublish and a pushed secret is a leaked secret. - Pre-commit failures stop the run: Report the error, do not fix it or work around it. The hook found a problem in the user's code, and guessing at a fix mid-commit hides it.
- Empty staging stops the run: If nothing is staged after
git add, say so. An empty commit tells the user nothing about why their files were not picked up.