Git Commit Best Practices
Purpose: This skill teaches AI agents to create high-quality commits with clear messages, proper granularity, and effective use of the staging area.
Core Principles
- •Atomic Commits - One logical change per commit
- •Clear Messages - Follow Conventional Commits format
- •Meaningful History - Each commit tells a story
- •Smart Staging - Stage only what belongs together
Commit Message Format
Conventional Commits Structure
code
<type>(<scope>): <subject> <body> <footer>
Type Categories
bash
# ✓ CORRECT: Use these standard types feat: # New feature for the user fix: # Bug fix docs: # Documentation changes style: # Formatting, missing semicolons, etc. (no code change) refactor: # Code change that neither fixes a bug nor adds a feature perf: # Performance improvement test: # Adding or updating tests chore: # Maintenance tasks, dependencies, config # Examples: feat(auth): add password reset functionality fix(api): handle null response in user endpoint docs(readme): update installation instructions refactor(parser): simplify token extraction logic
Subject Line Guidelines
bash
# ✓ GOOD: Clear, concise, imperative mood feat(auth): add OAuth2 login support fix(cart): prevent duplicate items docs(api): document rate limiting # ✗ BAD: Vague, past tense, too long feat(auth): added some authentication stuff fix(cart): fixed a bug docs(api): updated the documentation to include information about rate limiting
Rules:
- •Use imperative mood ("add" not "added" or "adds")
- •No period at the end
- •Keep under 50 characters
- •Capitalize first letter after type
- •Be specific about what changed
Body Guidelines
bash
# ✓ GOOD: Explains WHY and context feat(cache): add Redis caching layer Improves API response time by 80% for frequently accessed data. Uses Redis with 1-hour TTL for user profile and product catalog endpoints. Falls back to database if Redis is unavailable. Related to performance optimization initiative. # ✗ BAD: Just repeats the subject feat(cache): add Redis caching layer Added Redis caching.
When to include body:
- •Why the change was needed
- •How it solves the problem
- •Any important implementation decisions
- •Side effects or limitations
- •Related issues or context
When to skip body:
- •Self-explanatory changes (e.g., "fix(typo): correct variable name")
Footer Guidelines
bash
# Breaking changes feat(api): change user endpoint response format BREAKING CHANGE: User API now returns camelCase instead of snake_case. Migration guide: https://docs.example.com/migration/v2 # Issue references fix(auth): prevent token expiration race condition Closes #123 Refs #456 # Co-authors feat(search): implement full-text search Co-authored-by: Jane Doe <jane@example.com>
Atomic Commits
What is an Atomic Commit?
Definition: One commit = one logical change
bash
# ✓ GOOD: Atomic commits git commit -m "feat(auth): add login endpoint" git commit -m "feat(auth): add logout endpoint" git commit -m "test(auth): add login tests" # ✗ BAD: Non-atomic commit git commit -m "feat(auth): add login, logout, password reset, and tests"
Benefits
- •Easy to review - Reviewer focuses on one change
- •Easy to revert - Undo specific change without affecting others
- •Easy to cherry-pick - Apply specific change to another branch
- •Clear history - Each commit has clear purpose
How to Create Atomic Commits
bash
# ✓ CORRECT: Stage related changes only # You made changes to auth.js, api.js, and tests.js # Commit 1: Feature implementation git add src/auth.js src/api.js git commit -m "feat(auth): add token refresh mechanism" # Commit 2: Tests git add tests/auth.test.js git commit -m "test(auth): add token refresh tests" # ✗ WRONG: Stage everything together git add . git commit -m "feat(auth): add token refresh and tests"
Decision Tree: Should This Be One Commit?
code
Does this change have a single, clear purpose? ├─ YES → Can it be described in one sentence? │ ├─ YES → One commit ✓ │ └─ NO → Multiple commits │ └─ NO → Multiple commits Examples: ├─ "Add login validation" → One commit ✓ ├─ "Add login and signup" → Two commits └─ "Add login, fix bug, update docs" → Three commits
Staging Strategies
Strategy 1: Partial File Staging
bash
# You changed multiple things in one file # Only stage the lines related to current commit git add -p src/app.js # Interactive prompts: # y - stage this hunk # n - don't stage this hunk # s - split into smaller hunks # e - manually edit the hunk # Then commit only staged changes git commit -m "feat(app): add error handling" # Stage and commit remaining changes separately git add -p src/app.js git commit -m "refactor(app): extract validation logic"
Strategy 2: Multiple Commits from Unstaged Work
bash
# ✓ WORKFLOW: Create multiple atomic commits from current changes # Check what changed git status git diff # Commit 1: Feature A git add src/feature-a.js src/utils.js git commit -m "feat(feature-a): implement feature A" # Commit 2: Feature B git add src/feature-b.js git commit -m "feat(feature-b): implement feature B" # Commit 3: Tests git add tests/ git commit -m "test: add tests for features A and B"
Strategy 3: Amend Last Commit
bash
# Use case: You forgot to include a file in the last commit # ✓ CORRECT: Amend if commit is NOT pushed yet git add forgotten-file.js git commit --amend --no-edit # OR update the commit message too git commit --amend -m "feat(auth): add login endpoint and middleware" # ✗ WRONG: Amend if commit is already pushed # This rewrites history and causes problems for collaborators # If already pushed, create new commit instead: git add forgotten-file.js git commit -m "feat(auth): add missing middleware file"
Common Workflows
Workflow 1: Making a Feature Commit
bash
# 1. Check current state git status git diff # 2. Stage related changes only git add src/feature.js src/api.js # 3. Review what will be committed git diff --staged # 4. Commit with conventional format git commit -m "feat(feature): add user profile customization Allows users to customize avatar, bio, and theme preferences. Settings are persisted to user preferences API endpoint. Closes #234" # 5. Verify commit git log -1 --stat
Workflow 2: Making Multiple Atomic Commits
bash
# You worked on multiple things, now need to commit separately # 1. Check all changes git status # 2. First commit: Core feature git add src/core.js git commit -m "feat(core): add data validation layer" # 3. Second commit: API integration git add src/api.js git commit -m "feat(api): integrate validation with endpoints" # 4. Third commit: Tests git add tests/ git commit -m "test(validation): add comprehensive validation tests" # 5. Fourth commit: Documentation git add docs/ git commit -m "docs(validation): document validation rules"
Workflow 3: Fixing a Bug
bash
# ✓ GOOD: Clear bug fix commit git add src/buggy-file.js git commit -m "fix(cart): prevent duplicate items on double-click Added debounce to 'Add to Cart' button and server-side duplicate check. Prevents race condition when users click rapidly. Fixes #567" # Include: # - What the bug was # - How you fixed it # - Issue reference
Workflow 4: Amending vs New Commit Decision
code
Did I already push this commit?
├─ NO → Safe to amend
│ └─> git commit --amend
│
└─ YES → Create new commit instead
└─> git commit -m "fix: ..."
Is this a fixup for recent commit?
└─> Consider git commit --fixup=<sha>
Then use git rebase -i --autosquash before pushing
Workflow 5: Refactoring Commits
bash
# ✓ GOOD: Separate refactoring from behavior changes
# Commit 1: Pure refactor (no behavior change)
git add src/parser.js
git commit -m "refactor(parser): extract token validation to separate function
No behavior change. Makes code more testable and readable."
# Commit 2: Behavior change
git add src/parser.js
git commit -m "feat(parser): add support for nested tokens
Now supports tokens in format {{parent.child.value}}"
# Why separate?
# - Easy to verify refactor doesn't change behavior
# - Easy to revert feature without losing refactor
# - Clear history
Common Mistakes to Avoid
Mistake 1: Vague Commit Messages
bash
# ✗ BAD: No context git commit -m "update" git commit -m "fix bug" git commit -m "changes" git commit -m "wip" # ✓ GOOD: Clear and specific git commit -m "feat(auth): add JWT token expiration check" git commit -m "fix(api): handle null values in user profile endpoint" git commit -m "refactor(parser): simplify regex patterns" git commit -m "docs(readme): add Docker setup instructions"
Mistake 2: Mixing Unrelated Changes
bash
# ✗ BAD: Multiple unrelated changes in one commit git add . git commit -m "feat: add login, fix cart bug, update docs" # ✓ GOOD: Separate commits git add src/auth.js git commit -m "feat(auth): add OAuth login support" git add src/cart.js git commit -m "fix(cart): prevent negative quantities" git add docs/ git commit -m "docs(api): update authentication endpoints"
Mistake 3: Committing Debug Code
bash
# ✗ BAD: Leaving console.log, debugger statements git add src/app.js # Contains console.log debugging git commit -m "feat(app): add feature" # ✓ GOOD: Review before committing git diff --staged # Check for debug code # Remove debug statements git add src/app.js git commit -m "feat(app): add feature"
Mistake 4: Too Large or Too Small Commits
bash
# ✗ TOO LARGE: Entire feature in one commit git commit -m "feat(auth): complete authentication system" # 50 files changed, 2000+ lines # ✗ TOO SMALL: Meaningless micro-commits git commit -m "add semicolon" git commit -m "fix typo" git commit -m "add newline" # ✓ GOOD: Right-sized atomic commits git commit -m "feat(auth): add login endpoint" git commit -m "feat(auth): add logout endpoint" git commit -m "feat(auth): add token refresh mechanism" git commit -m "test(auth): add authentication tests"
Mistake 5: Amending Pushed Commits
bash
# ✗ DANGEROUS: Amending after push git push origin feature-branch # Oh, forgot a file! git add forgotten.js git commit --amend --no-edit git push --force origin feature-branch # ← Causes problems! # ✓ SAFE: New commit instead git push origin feature-branch # Oh, forgot a file! git add forgotten.js git commit -m "feat(auth): add missing validation helper" git push origin feature-branch
Commit Message Templates
Template for Features
bash
feat(<scope>): <what you added> <why this feature is needed> <how it works (if not obvious)> <any limitations or considerations> Closes #<issue-number>
Template for Bug Fixes
bash
fix(<scope>): <what you fixed> <what was wrong> <how you fixed it> <why this approach> Fixes #<issue-number>
Template for Refactoring
bash
refactor(<scope>): <what you refactored> No behavior change. <why refactor was needed> <what improved (readability, performance, maintainability)>
Integration with AI Code Generation
When AI agents generate code that needs committing:
1. Analyze Changes Before Committing
bash
# ✓ CORRECT: Check what changed git status git diff # Determine: # - Is this one logical change or multiple? # - What type of change? (feat, fix, refactor, etc.) # - What scope? (auth, api, ui, etc.)
2. Create Appropriate Commit Message
bash
# ✓ GOOD: AI-generated message with context git commit -m "feat(api): add rate limiting middleware Implements token bucket algorithm with 100 requests/minute limit. Returns 429 status with Retry-After header when limit exceeded. Addresses security requirement from issue #789" # ✗ BAD: Generic AI message git commit -m "add rate limiting"
3. Split Large Changes
bash
# If AI generated multiple files for different purposes: # ✓ CORRECT: Separate commits git add src/rate-limiter.js git commit -m "feat(api): add rate limiting middleware" git add tests/rate-limiter.test.js git commit -m "test(api): add rate limiter tests" git add docs/api.md git commit -m "docs(api): document rate limiting" # ✗ WRONG: One large commit git add . git commit -m "add rate limiting with tests and docs"
Quick Reference Checklist
Before every commit, ask:
- • Is this one logical change?
- • Did I stage only related files?
- • Is my commit message in Conventional Commits format?
- • Does the subject line clearly describe what changed?
- • Did I explain why (not just what) in the body?
- • Did I remove debug code and comments?
- • Is this commit NOT already pushed? (if planning to amend)
- • Would this be easy to review?
- • Would this be easy to revert if needed?
Advanced: Commit Message Psychology
Good commit messages answer:
- •What changed? (Subject line)
- •Why was it needed? (Body - business reason)
- •How does it work? (Body - technical approach)
- •What are the side effects? (Body - impacts)
bash
# ✓ EXCELLENT: Answers all questions feat(search): implement fuzzy search algorithm Users frequently make typos in search queries, resulting in zero results and poor experience. Implemented Levenshtein distance-based fuzzy matching with tolerance of 2 characters. Performance impact: ~50ms additional latency for searches, acceptable given improved user experience. Results are cached for 5 minutes. Closes #456
Summary
Key Principles:
- •One commit = one logical change (atomic commits)
- •Use Conventional Commits format (type, scope, clear subject)
- •Explain why, not just what (meaningful commit messages)
- •Stage strategically (use
git add -pfor partial staging) - •Never amend pushed commits (creates new commit instead)
Quick Command Reference:
bash
git add <file> # Stage specific file git add -p <file> # Stage parts of file interactively git diff --staged # Review what will be committed git commit -m "type(scope): msg" # Commit with message git commit --amend --no-edit # Amend last commit (if not pushed) git log -1 --stat # Verify last commit
Remember: Commits are documentation of your project's history. Write them for humans who will read them 6 months from now.