The Art of Writing Clean Git Commits: A Practical Guide

Learn how to write clear, meaningful git commits that improve collaboration and make codebase history actually useful.

Stop writing messy commit messages like "fix stuff" or "update code". Here's how to write git commits that your future self (and teammates) will thank you for.


Common Bad Practices

1. Vague Messages

# ❌ BAD: Uninformative commits
git commit -m "fix bug"
git commit -m "update code"
git commit -m "changes"
git commit -m "wip"

2. Mixed Concerns

# ❌ BAD: Multiple unrelated changes
git commit -m "fix login bug and update homepage styling and add new API endpoint"

Better Patterns

1. Structured Messages

# ✅ BETTER: Clear, structured commits
git commit -m "fix(auth): prevent login timeout on slow connections" \
          -m "- Increase token expiry to 24h
              - Add refresh token mechanism
              - Update tests"

2. Conventional Commits

# Type(scope): description

# Examples
feat(user): add password reset functionality
fix(api): handle null response from payment service
docs(readme): update installation instructions
test(auth): add unit tests for login flow
refactor(db): optimize user queries
style(ui): align header elements properly
perf(api): cache frequently accessed data

Practical Examples

1. Bug Fixes

# Before
git commit -m "fix login"

# After
git commit -m "fix(auth): handle expired tokens in refresh flow" \
          -m "When the access token expired, the refresh flow would
              fail silently. This commit:
              
              - Adds proper error handling
              - Implements automatic token refresh
              - Adds logging for debugging
              
              Closes #123"

2. Feature Additions

# Before
git commit -m "add google login"

# After
git commit -m "feat(auth): implement Google OAuth authentication" \
          -m "This adds Google OAuth support with the following:
              
              - Google OAuth2 integration
              - User profile sync
              - Email verification
              - Migration of existing accounts
              
              Breaking Changes:
              - Auth configuration format has changed
              - Requires new ENV variables
              
              Closes #456"

Commit Organization

1. Atomic Commits

# ❌ BAD: Mixed changes
git add .
git commit -m "update user profile"

# ✅ BETTER: Separate logical changes
git add src/auth/*
git commit -m "feat(auth): add email verification"

git add src/styles/*
git commit -m "style(profile): update user avatar layout"

git add src/api/*
git commit -m "refactor(api): optimize profile queries"

2. Interactive Staging

# Stage changes interactively
git add -p

# Select specific files
git add src/components/Auth.tsx
git add src/styles/auth.css

# Review changes before commit
git status
git diff --staged

Commit Templates

1. Local Template

# ~/.gitmessage
feat(scope): concise description

# Why is this change needed?
Why:

# How does it address the issue?
How:

# What side effects does this change have?
Impact:

# Related ticket/issue
Closes #

2. Using Templates

# Configure git to use template
git config --global commit.template ~/.gitmessage

# Use template in commit
git commit

Advanced Techniques

1. Rewriting History

# Fix last commit message
git commit --amend -m "feat(auth): implement password reset"

# Interactive rebase to clean up commits
git rebase -i HEAD~3

# Squash related commits
pick abc123 feat(auth): add password reset
squash def456 fix password reset validation
squash ghi789 update password reset tests

2. Branch Organization

# Feature branch naming
git checkout -b feat/user-authentication
git checkout -b fix/login-timeout
git checkout -b docs/api-documentation

# Temporary branches
git checkout -b temp/experiment-redis
git checkout -b wip/refactor-auth

Best Practices

1. Commit Message Structure

# Format
<type>(<scope>): <description>

[optional body]

[optional footer]

# Example
feat(auth): implement multi-factor authentication

This implements SMS and email-based 2FA with the following:
- SMS verification via Twilio
- Email verification via SendGrid
- Backup codes generation
- Recovery process

Breaking Changes:
- Auth flow now requires 2FA setup
- New environment variables needed

Closes #789

2. Semantic Versioning

# Major changes (breaking)
feat(api)!: change authentication flow

# Minor changes (features)
feat(user): add profile customization

# Patch changes (fixes)
fix(cache): prevent memory leak

Git Hooks

1. Commit Message Validation

// .git/hooks/commit-msg
#!/usr/bin/env node

const message = require('fs')
  .readFileSync(process.argv[2], 'utf8')
  .trim();

const pattern = /^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}/;

if (!pattern.test(message)) {
  console.error('Invalid commit message format.');
  process.exit(1);
}

2. Pre-commit Checks

#!/bin/sh
# .git/hooks/pre-commit

# Run tests
npm test

# Run linter
npm run lint

# Check formatting
npm run format:check

Conclusion

Good commit messages:

  • Are clear and concise
  • Follow a consistent format
  • Explain the why, not just the what
  • Make history tracking valuable
  • Help with debugging
  • Facilitate collaboration

Remember: A commit message is a letter to your future self.