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)