DEV Community

Cover image for Enforce Standardized Git Commit Messages with Husky: A Complete Guide
ShaneDushyantha
ShaneDushyantha

Posted on • Edited on

Enforce Standardized Git Commit Messages with Husky: A Complete Guide

How to implement a custom commit message format that improves team collaboration and project tracking


🎯 Why Standardize Commit Messages?

In any development team, consistent commit messages are crucial for:

  • πŸ” Traceability - Link code changes to specific tasks/tickets
  • πŸ“‹ Automation - Generate changelogs and release notes
  • πŸ‘₯ Team Collaboration - Everyone understands what each commit does
  • πŸš€ CI/CD Integration - Automated workflows based on commit patterns
  • πŸ“Š Project Management - Track progress and estimate work

πŸ—οΈ The Problem

Without standardization, you get commit messages like:

git commit -m "fix stuff"
git commit -m "updated things"
git commit -m "WIP"
git commit -m "bug fix"
Enter fullscreen mode Exit fullscreen mode

This makes it impossible to:

  • Track which features are complete
  • Generate meaningful changelogs
  • Link commits to project management tools
  • Understand the scope of changes

πŸ’‘ The Solution: Custom Commit Message Format

We'll implement a format like:

RPP-123 Adding user authentication
RPP-456 Fix login validation bug
RPP-789 Update dashboard layout
Enter fullscreen mode Exit fullscreen mode

Where:

  • RPP- is your project prefix
  • 123 is the task/ticket number
  • Description is optional but recommended

πŸ› οΈ Implementation Guide

Step 1: Install Dependencies

# Install Husky for Git hooks
yarn add -D husky

# Initialize Husky
npx husky init
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the Commit Message Hook

Create .husky/commit-msg:

#!/bin/sh

# Get the commit message from the first argument
commit_msg=$(cat "$1")

# Check if the commit message matches the RPP pattern using secure string operations
# This avoids regex backtracking vulnerabilities
if [ -z "$commit_msg" ]; then
    echo "❌ Commit message cannot be empty!"
    exit 1
fi

# Check if it starts with RPP-
if [ "${commit_msg#RPP-}" = "$commit_msg" ]; then
    echo "❌ Invalid commit message format!"
    echo "βœ… Use format: RPP-[taskNumber] [Description (Optional)]"
    echo "πŸ“ Examples:"
    echo "   RPP-123 Adding Home page navigation"
    echo "   RPP-456 Implement Button component"
    echo "   RPP-789"
    exit 1
fi

# Extract the part after RPP-
task_part="${commit_msg#RPP-}"

# Check if the task part starts with a digit
if [ -z "$task_part" ] || ! echo "$task_part" | grep -q "^[0-9]"; then
    echo "❌ Invalid commit message format!"
    echo "βœ… Use format: RPP-[taskNumber] [Description (Optional)]"
    echo "πŸ“ Examples:"
    echo "   RPP-123 Adding Home page navigation"
    echo "   RPP-456 Implement Button component"
    echo "   RPP-789"
    exit 1
fi

echo "βœ… Commit message format is valid!"
Enter fullscreen mode Exit fullscreen mode

Make it executable:

chmod +x .husky/commit-msg
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a Commit Message Template

Create .gitmessage:

# RPP Commit Message Template
# 
# Format: RPP-[taskNumber] [Description (Optional)]
# 
# Examples:
# RPP-123 Adding Home page navigation
# RPP-456 Implement Button component  
# RPP-789 Fix login validation
# RPP-101
#
# Rules:
# - Must start with RPP- followed by a number
# - Description is optional but recommended
# - Keep it concise and descriptive
# - Use present tense (Add, Fix, Update, etc.)

RPP-
Enter fullscreen mode Exit fullscreen mode

Step 4: Create Setup Scripts

Create scripts/setup-hooks.sh:

#!/bin/bash

echo "πŸ”§ Setting up Git hooks and commit template..."

# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
    echo "⚠️  Not in a git repository."
    echo "   Git hooks will be set up when you run this in a git repo."
    exit 0
fi

# Set up commit template
git config commit.template .gitmessage
echo "βœ… Git commit template configured"

# Force Husky to be properly initialized
echo "πŸ”§ Ensuring Husky is properly set up..."

# Always run husky to ensure proper setup
npx husky

# Fix git hooks path if it's wrong
current_hooks_path=$(git config --get core.hooksPath 2>/dev/null || echo "")
if [ "$current_hooks_path" != ".husky" ]; then
    echo "πŸ”§ Fixing git hooks path..."
    git config core.hooksPath .husky
fi

# Check if .husky/_ directory exists after install
if [ ! -d ".husky/_" ]; then
    echo "❌ .husky/_ directory still not created"
    echo "πŸ”§ Trying alternative approach..."

    # Try to force create the _ directory
    mkdir -p .husky/_

    # Copy husky.sh if it exists in node_modules
    if [ -f "node_modules/husky/lib/sh/husky.sh" ]; then
        cp node_modules/husky/lib/sh/husky.sh .husky/_/
        chmod +x .husky/_/husky.sh
    fi

    # Create basic hook files
    for hook in commit-msg pre-commit; do
        if [ ! -f ".husky/_/$hook" ]; then
            echo "#!/usr/bin/env sh" > ".husky/_/$hook"
            echo ". \"\$(dirname \"\$0\")/husky.sh\"" >> ".husky/_/$hook"
            chmod +x ".husky/_/$hook"
        fi
    done
fi

# Make sure husky hooks are executable
chmod +x .husky/* 2>/dev/null || true
chmod +x .husky/_/* 2>/dev/null || true
echo "βœ… Made Husky hooks executable"

echo "βœ… Git hooks and commit template configured successfully!"
echo "πŸ“ Your commits will now use the RPP-[taskNumber] format."

# Final verification
echo ""
echo "πŸ” Verifying setup..."
if [ -d ".husky/_" ] && [ -f ".husky/commit-msg" ] && [ -x ".husky/commit-msg" ]; then
    echo "βœ… All hooks are properly set up!"
    echo "πŸ§ͺ Test with: git commit -m 'RPP-123 Test commit'"
else
    echo "❌ Setup incomplete. Please run: yarn setup"
fi
Enter fullscreen mode Exit fullscreen mode

Create scripts/debug-husky.sh:

#!/bin/bash

echo "πŸ” Husky Debug Information"
echo "=========================="

echo ""
echo "1. Node.js version:"
node --version

echo ""
echo "2. Yarn version:"
yarn --version

echo ""
echo "3. Husky version:"
npx husky --version 2>/dev/null || echo "Husky not found"

echo ""
echo "4. Git repository status:"
if [ -d ".git" ]; then
    echo "βœ… Git repository found"
    git --version
else
    echo "❌ Not in a git repository"
fi

echo ""
echo "5. .husky directory contents:"
if [ -d ".husky" ]; then
    echo "βœ… .husky directory exists"
    ls -la .husky/

    if [ -d ".husky/_" ]; then
        echo "βœ… .husky/_ directory exists"
        ls -la .husky/_/
    else
        echo "❌ .husky/_ directory missing"
    fi
else
    echo "❌ .husky directory not found"
fi

echo ""
echo "6. Git hooks path:"
git config --get core.hooksPath 2>/dev/null || echo "No hooks path configured"

echo ""
echo "7. Package.json scripts:"
grep -A 5 '"scripts"' package.json

echo ""
echo "8. Testing husky:"
npx husky

echo ""
echo "9. After husky - .husky contents:"
ls -la .husky/ 2>/dev/null || echo "No .husky directory"

echo ""
echo "πŸ”§ Debug complete!"
Enter fullscreen mode Exit fullscreen mode

Step 5: Update package.json

{
  "scripts": {
    "prepare": "husky",
    "postinstall": "bash -c 'if [ -d \".git\" ]; then ./scripts/setup-hooks.sh; else echo \"Not in git repo, run yarn setup when ready\"; fi'",
    "setup": "./scripts/setup-hooks.sh",
    "debug-husky": "./scripts/debug-husky.sh"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Document in README.md

## πŸ“ Git Commit Message Convention

This project follows a standardized commit message format to maintain consistency and improve project tracking.

### Format
Enter fullscreen mode Exit fullscreen mode

RPP-[taskNumber] [Description (Optional)]


### Examples
- `RPP-123 Adding Home page navigation`
- `RPP-456 Implement Button component`
- `RPP-789 Fix login validation`
- `RPP-101` (description is optional)

### Rules
- **Must start with `RPP-`** followed by a task number
- Description is optional but recommended
- Keep it concise and descriptive
- Use present tense (Add, Fix, Update, etc.)

### Setup
The project is configured with:
- **Husky** - Git hooks for automated validation
- **Git template** - Pre-filled commit message template

When you commit, the system will automatically validate your commit message format.
Enter fullscreen mode Exit fullscreen mode

πŸ”’ Security Considerations

Why Avoid Complex Regex?

The original approach used regex patterns that could be vulnerable to ReDoS (Regular Expression Denial of Service) attacks:

# ❌ Vulnerable to backtracking
grep -E "^RPP-[0-9]+( .*)?$"

# βœ… Secure approach using string operations
[ "${commit_msg#RPP-}" = "$commit_msg" ]
Enter fullscreen mode Exit fullscreen mode

Security Benefits of Our Implementation:

  1. No backtracking vulnerabilities - Uses O(1) string operations
  2. Input validation - Checks for empty and malformed input
  3. Defensive programming - Validates each part separately
  4. Clear error messages - Helps developers understand issues

πŸ§ͺ Testing Your Implementation

Valid Commit Messages:

git commit -m "RPP-123 Adding user authentication"
git commit -m "RPP-456 Fix login validation bug"
git commit -m "RPP-789"
git commit -m "RPP-101 Update README"
Enter fullscreen mode Exit fullscreen mode

Invalid Commit Messages (will be rejected):

git commit -m "fix stuff"                    # Missing RPP- prefix
git commit -m "RPP-"                         # Missing task number
git commit -m "RPP-abc"                      # Non-numeric task number
git commit -m ""                             # Empty message
Enter fullscreen mode Exit fullscreen mode

πŸš€ Advanced Customization

Customize the Prefix

To use a different prefix (e.g., TASK-, JIRA-, PROJ-):

# Replace all instances of "RPP-" with your prefix
sed -i 's/RPP-/TASK-/g' .husky/commit-msg
sed -i 's/RPP-/TASK-/g' .gitmessage
Enter fullscreen mode Exit fullscreen mode

Add Pre-commit Hooks

Create .husky/pre-commit for additional checks:

#!/bin/sh

# Run linting before commit
yarn lint

# Run tests before commit
yarn test

# Check for console.log statements
if git diff --cached --name-only | xargs grep -l "console\.log"; then
    echo "❌ console.log statements found in staged files"
    exit 1
fi
Enter fullscreen mode Exit fullscreen mode

Integration with CI/CD

Add to your GitHub Actions workflow:

name: Validate Commit Messages
on: [push, pull_request]

jobs:
  validate-commits:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Validate commit messages
        run: |
          # Check last 10 commits
          for commit in $(git log --oneline -10 | cut -d' ' -f2-); do
            if ! echo "$commit" | grep -q "^RPP-[0-9]"; then
              echo "❌ Invalid commit message: $commit"
              exit 1
            fi
          done
Enter fullscreen mode Exit fullscreen mode

πŸ“Š Benefits and Impact

Immediate Benefits:

  • Consistent History - All commits follow the same format
  • Task Tracking - Easy to see which tasks are complete
  • Automated Validation - No more manual review of commit messages
  • Better Onboarding - New developers know exactly what format to use

Long-term Benefits:

  • Automated Changelogs - Generate release notes automatically
  • Project Analytics - Track velocity and completion rates
  • Integration Ready - Connect with Jira, GitHub Issues, etc.
  • Audit Trail - Complete traceability of changes

Team Productivity:

  • Reduced Review Time - Clear commit messages speed up code reviews
  • Better Communication - Everyone understands what changed and why
  • Faster Debugging - Easy to find which commit introduced a bug
  • Improved Planning - Better estimation based on historical data

πŸ”§ Troubleshooting

Common Issues:

1. Hook not running:

# Check if hooks are executable
ls -la .husky/

# Make executable if needed
chmod +x .husky/commit-msg
Enter fullscreen mode Exit fullscreen mode

2. Template not showing:

# Verify template configuration
git config --get commit.template

# Set template if missing
git config commit.template .gitmessage
Enter fullscreen mode Exit fullscreen mode

3. .husky/_ directory missing:

# Run setup script
yarn setup

# Or debug the issue
yarn debug-husky
Enter fullscreen mode Exit fullscreen mode

4. Bypass validation (emergency only):

git commit -m "RPP-123 Emergency fix" --no-verify
Enter fullscreen mode Exit fullscreen mode

πŸŽ‰ Conclusion

Implementing standardized commit messages with Husky provides:

  • Immediate value through consistent commit history
  • Long-term benefits for automation and analytics
  • Security through proper input validation
  • Scalability as your team and project grow

The investment in setting up this system pays dividends in:

  • Reduced communication overhead
  • Improved project tracking
  • Better automation capabilities
  • Enhanced team collaboration

Start with the basic implementation and gradually add more sophisticated features as your team's needs evolve.


πŸ“š Additional Resources


Happy coding! πŸš€

If you found this helpful, consider sharing it with your team or following me for more development tips.

Top comments (0)