Git Commit
Write clear, atomic commits. Detect and match project conventions first; use Conventional Commits as fallback. Every commit = one complete, working change.
Feature Branch Workflow
Philosophy: Commit early, commit often.
Feature branch: Create → [Change → Test → COMMIT]* → PR → Merge
Why frequent commits matter:
| Benefit | Description |
| -------------------- | ----------------------------------------------- |
| Easy rollback | Revert to last working state in seconds |
| Safety checkpoints | Never lose more than one small step |
| Better debugging | git bisect works with granular history |
| Collaboration | Others see incremental progress |
| Automation | Enables auto-changelogs and semantic versioning |
Anti-pattern: One giant commit at end of feature (47 files, 2800+ lines). Split into logical increments instead.
Quick Start
Pre-Commit Checklist:
- [ ] Code builds and tests pass
- [ ] ONE logical change (describable in one sentence)
- [ ] Checked project conventions (Step 1)
Workflow
Step 1: Detect Conventions (REQUIRED)
ls .commitlintrc* commitlint.config.* .czrc .cz.json 2>/dev/null
git log --oneline -15
| Finding | Action |
| ---------------------------- | --------------------------------- |
| Config file exists | Follow those rules exactly |
| feat:, fix: pattern | Use Conventional Commits |
| [JIRA-123] pattern | Match ticket prefix format |
| Component: message pattern | Match component prefix format |
| No clear pattern | Use Conventional Commits (Step 2) |
Step 2: Format Message
<type>(<scope>)!: <subject> ← 50 chars ideal, 72 max
← blank line
<body> ← wrap at 72 chars, optional
← blank line
<footer> ← optional (Closes #123, BREAKING CHANGE)
Subject rules: Imperative mood (add not added), lowercase after colon,
no trailing period, no filler words.
Types:
| Type | Purpose | SemVer | Example |
| ---------- | ----------------------- | ------ | ---------------------------- |
| feat | New feature | MINOR | feat(auth): add OAuth |
| fix | Bug fix | PATCH | fix(cart): prevent neg qty |
| docs | Documentation | - | docs(api): add examples |
| style | Formatting | - | style: fix indentation |
| refactor | Code restructure | - | refactor: extract helper |
| perf | Performance | - | perf(db): add index |
| test | Tests | - | test(auth): add edge cases |
| build | Build/deps | - | build: update webpack |
| ci | CI config | - | ci: add deploy workflow |
| chore | Maintenance | - | chore: update gitignore |
| revert | Revert previous commit | - | revert: let us not... |
Breaking changes: Add ! after type/scope, explain in body/footer.
feat(api)!: require API key for all endpoints
BREAKING CHANGE: Anonymous access removed. All requests need X-API-Key header.
Step 3: Body (When Needed)
| Required | Optional | | --------------------------------- | -------------------------------- | | Breaking change (impact/migrate) | Self-explanatory from diff | | Non-obvious fix (root cause) | Simple docs/style/chore | | Complex feature (design decision) | Tests with descriptive names |
Body content: WHY (motivation), WHAT problem, HOW users affected. Not: Which files (diff shows), line-by-line explanation (code comments).
Step 4: Footer
Closes #123 # Issue reference
Fixes #456 # Also closes issue
Refs: #789 # Reference without closing
BREAKING CHANGE: <description> # If not in body
Co-authored-by: Name <email@example.com> # Co-authorship
Reviewed-by: Name # Use hyphens in tokens
Commit Granularity
Atomic Commit = Smallest Complete Change
- ONE purpose - single feature/fix/refactor
- Self-contained - doesn't depend on uncommitted work
- Leaves code working - builds pass, tests pass
- Revertable alone - without breaking other features
What Goes Together vs Separate
| Together (same commit) | Separate (different commits) | | ---------------------------- | ------------------------------ | | Feature + its unit tests | Feature + unrelated formatting | | Bug fix + regression test | Bug fix + dependency update | | API change + docs update | Refactor + new feature | | Refactor + affected tests | Multiple unrelated fixes |
When to Commit (Triggers)
| Trigger | Action | | -------------------------------- | ---------------------------------- | | Function works and tested | COMMIT now | | Test passes (red → green) | COMMIT now | | Bug fixed and verified | COMMIT before next task | | About to refactor | COMMIT working state first | | Starting new sub-task | COMMIT current progress | | End of session / before pull | COMMIT if working |
Key: Before starting new task, commit all current task changes.
When NOT to Commit
| Situation | Why | | -------------------------------- | ---------------------------------- | | Code doesn't build | Breaks bisect, blocks others | | Tests failing* | Not a valid checkpoint | | "I think this works" (untested) | Verify first, commit second | | Mid-debugging / mid-fix | Wait until fix is complete | | Uncertain about approach | Prototype first, commit when solid | | CI still running / red | Wait for green, then commit fix |
*Exception: TDD RED commits (test-only commits that intentionally fail) are valid.
"Commit often" means verified working increments, not untested guesses.
❌ BAD: Commit-and-pray workflow
───────────────────────────────
1. Write fix
2. "Looks right to me" → commit
3. Push → CI fails
4. "Oops, forgot X" → commit again
5. Push → CI fails again
6. Repeat 3 more times...
Result: 5 broken commits in history
✓ GOOD: Verify-then-commit workflow
───────────────────────────────
1. Write fix
2. Run tests locally → fails
3. Fix the issue
4. Run tests locally → passes
5. Commit
6. Push → CI passes
Result: 1 clean commit
Rule: If CI or local tests are red, you're not done. Fix first, verify, then commit.
TDD Pattern
test(auth): add failing test for validation # RED
feat(auth): implement validation # GREEN
refactor(auth): extract to helper # REFACTOR
Examples
See references/examples.md for comprehensive examples. Quick reference:
# Feature with context
feat(auth): add password strength indicator
Users lacked feedback on password quality during signup.
Add real-time strength meter, requirements checklist, submission block.
Closes #234
# Bug fix with root cause
fix(api): handle null response from payment gateway
Gateway returns null on timeout. Add null check with retry logic.
Fixes #567
# Simple changes (no body needed)
docs(readme): add Docker installation steps
test(utils): add edge case tests for date parser
chore(deps): update lodash to 4.17.21
| Bad | Problem | Good |
| ------------- | ---------------- | ------------------------------------ |
| fix bug | No context | fix(cart): prevent duplicate items |
| update code | Meaningless | refactor(api): simplify errors |
| WIP | Incomplete | Finish work, then commit |
| misc fixes | Multiple changes | Split into separate commits |
Integration
| When | Skill | Why |
| ----------------- | -------------------------- | ------------------------- |
| Before committing | code-quality | Ensure checks pass |
| After committing | docs-check | Check if docs need update |
| After PR review | pr-review → git-commit | Resolve then commit |
References
references/examples.md- Extended examples by scenario
Output
Git commits created via standard git commands. No files saved. Commit history visible via git log.