# Commitlint Standards: Enforce Commit Messages

# Commitlint Standards: Enforce Commit Messages with Git Hooks

## Table of Contents
1. [What is Commitlint?](#what-is-commitlint)
2. [Why Enforce Commit Message Standards?](#why-enforce-commit-message-standards)
3. [Setup and Installation](#setup-and-installation)
4. [Configuration Examples](#configuration-examples)
5. [Commit Message Format](#commit-message-format)
6. [Comparison Table](#comparison-table)
7. [Frequently Asked Questions](#frequently-asked-questions)

## What is Commitlint?

Commitlint is a powerful tool that checks if your commit messages meet the conventional commit format. It works seamlessly with Git hooks to enforce commit message standards across your development team, ensuring consistency, readability, and automated changelog generation.

The tool integrates with **Husky** to create Git hooks that validate commit messages before they're accepted into your repository. This prevents poorly formatted commits from entering your codebase and maintains a clean, professional Git history.

### Key Benefits:
- **Automated validation** of commit messages
- **Consistent format** across team members
- **Better changelog generation** from commit history
- **Improved code review** process
- **Semantic versioning** support

## Why Enforce Commit Message Standards?

Enforcing commit message standards isn't just about being pedantic—it provides tangible benefits:

1. **Automated Changelog Generation**: Tools can parse structured commits to generate release notes automatically
2. **Better Navigation**: Quickly filter commits by type (features, fixes, breaking changes)
3. **Clearer History**: Understand what changed and why without reading code
4. **Semantic Versioning**: Automatically determine version bumps based on commit types
5. **Team Collaboration**: Consistent format reduces cognitive load when reviewing history
6. **CI/CD Integration**: Trigger specific workflows based on commit types

## Setup and Installation

### Prerequisites
- Node.js (v14 or higher)
- npm or yarn
- Git repository initialized

### Step-by-Step Installation

**Step 1: Install Dependencies**
```bash
npm install --save-dev @commitlint/{cli,config-conventional} husky
```

**Step 2: Initialize Husky**
```bash
npx husky-init && npm install
```

**Step 3: Create Commitlint Configuration**
```bash
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
```

**Step 4: Add Commit Message Hook**
```bash
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
```

**Step 5: Test Your Setup**
```bash
git commit -m "invalid commit message"  # Should fail
git commit -m "feat: add new feature"   # Should succeed
```

## Configuration Examples

### Example 1: Basic Configuration

```javascript
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',     // New feature
        'fix',      // Bug fix
        'docs',     // Documentation
        'style',    // Formatting
        'refactor', // Code restructuring
        'test',     // Adding tests
        'chore',    // Maintenance
      ],
    ],
    'subject-case': [2, 'never', ['upper-case']],
    'subject-max-length': [2, 'always', 100],
  },
};
```

### Example 2: Custom Rules Configuration

```javascript
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', [
      'feat', 'fix', 'docs', 'style', 'refactor',
      'perf', 'test', 'build', 'ci', 'chore', 'revert'
    ]],
    'scope-enum': [2, 'always', [
      'api', 'ui', 'database', 'auth', 'core'
    ]],
    'scope-empty': [2, 'never'],
    'subject-empty': [2, 'never'],
    'subject-full-stop': [2, 'never', '.'],
    'header-max-length': [2, 'always', 72],
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [2, 'always'],
  },
};
```

### Example 3: Monorepo Configuration

```javascript
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional', '@commitlint/config-lerna-scopes'],
  rules: {
    'scope-enum': async (ctx) => {
      const packages = [
        'frontend',
        'backend',
        'shared',
        'mobile',
        'docs'
      ];
      return [2, 'always', packages];
    },
    'type-enum': [2, 'always', [
      'feat', 'fix', 'docs', 'style', 'refactor',
      'test', 'chore', 'deps', 'release'
    ]],
  },
};
```

### Example 4: Advanced with Custom Plugins

```javascript
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  plugins: ['commitlint-plugin-jira-rules'],
  rules: {
    'jira-task-id-max-length': [2, 'always', 9],
    'jira-task-id-min-length': [2, 'always', 3],
    'jira-task-id-separator': [2, 'always', '-'],
    'type-enum': [2, 'always', [
      'feat', 'fix', 'docs', 'style', 'refactor',
      'perf', 'test', 'build', 'ci', 'chore'
    ]],
    'references-empty': [2, 'never'],
    'header-max-length': [2, 'always', 100],
  },
  parserPreset: {
    parserOpts: {
      issuePrefixes: ['JIRA-', 'PROJ-'],
    },
  },
};
```

### Example 5: Package.json Integration

```json
{
  "name": "my-project",
  "version": "1.0.0",
  "scripts": {
    "commit": "git-cz",
    "commitlint": "commitlint --edit",
    "prepare": "husky install"
  },
  "devDependencies": {
    "@commitlint/cli": "^17.6.0",
    "@commitlint/config-conventional": "^17.6.0",
    "commitizen": "^4.3.0",
    "cz-conventional-changelog": "^3.3.0",
    "husky": "^8.0.3"
  },
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  },
  "commitlint": {
    "extends": ["@commitlint/config-conventional"],
    "rules": {
      "body-max-line-length": [1, "always", 100],
      "footer-max-line-length": [1, "always", 100]
    }
  }
}
```

## Commit Message Format

### Conventional Commit Structure

```
<type>(<scope>): <subject>

<body>

<footer>
```

### Type Values

- **feat**: A new feature for the user
- **fix**: A bug fix for the user
- **docs**: Documentation changes
- **style**: Code style changes (formatting, semicolons, etc.)
- **refactor**: Code changes that neither fix bugs nor add features
- **perf**: Performance improvements
- **test**: Adding or updating tests
- **build**: Changes to build system or dependencies
- **ci**: Changes to CI configuration files
- **chore**: Other changes that don't modify src or test files
- **revert**: Reverts a previous commit

### Examples of Good Commit Messages

```bash
feat(auth): add OAuth2 authentication support

fix(api): resolve null pointer exception in user service

docs(readme): update installation instructions

style(components): format code according to prettier rules

refactor(database): optimize query performance

perf(images): implement lazy loading for gallery

test(auth): add unit tests for login functionality

build(deps): upgrade react to version 18.2.0

ci(github): add automated deployment workflow

chore(release): bump version to 2.0.0
```

### Breaking Changes

```bash
feat(api)!: redesign REST API endpoints

BREAKING CHANGE: API endpoints now use /v2/ prefix
All clients must update their base URL configuration
```

## Comparison Table

| Feature | Commitlint | Git Commit Template | Manual Review | Commitizen |
|---------|-----------|---------------------|---------------|------------|
| **Automated Enforcement** | ✅ Yes | ❌ No | ❌ No | ✅ Yes |
| **Pre-commit Validation** | ✅ Yes | ❌ No | ❌ No | ⚠️ Optional |
| **Custom Rules** | ✅ Extensive | ⚠️ Limited | ✅ Yes | ⚠️ Limited |
| **Team Consistency** | ✅ High | ⚠️ Medium | ❌ Low | ✅ High |
| **Learning Curve** | ⚠️ Medium | ✅ Low | ✅ Low | ⚠️ Medium |
| **CI/CD Integration** | ✅ Easy | ❌ Difficult | ❌ Difficult | ✅ Easy |
| **Changelog Generation** | ✅ Automatic | ❌ Manual | ❌ Manual | ✅ Automatic |
| **Setup Complexity** | ⚠️ Medium | ✅ Simple | ✅ None | ⚠️ Medium |
| **IDE Support** | ✅ Good | ✅ Good | ✅ Native | ✅ Good |
| **Cost** | ✅ Free | ✅ Free | ✅ Free | ✅ Free |
| **Maintenance** | ⚠️ Low | ✅ None | ⚠️ High | ⚠️ Low |
| **Error Messages** | ✅ Detailed | ❌ None | ⚠️ Varies | ✅ Interactive |

## Frequently Asked Questions

### Q1: Can I bypass commitlint for emergency fixes?

**A:** Yes, you can use the `--no-verify` flag to skip Git hooks:
```bash
git commit -m "emergency fix" --no-verify
```
However, this should be used sparingly and only in genuine emergencies. Consider creating a proper commit message afterward with `git commit --amend`.

### Q2: How do I handle commits from external contributors?

**A:** You have several options:
1. **CI/CD Validation**: Add commitlint to your CI pipeline to check all commits
2. **Documentation**: Provide clear contribution guidelines
3. **Squash Merging**: Squash external PRs and write proper commit messages
4. **Bot Integration**: Use GitHub bots to validate PR commit messages

### Q3: What if my team disagrees on commit message format?

**A:** Commitlint is highly configurable. Hold a team meeting to:
- Discuss pain points with current practices
- Review conventional commit benefits
- Customize rules to fit your workflow
- Start with lenient rules and gradually tighten them
- Document decisions in your contributing guide

### Q4: Can commitlint work with existing projects?

**A:** Absolutely! Commitlint only validates new commits. To implement:
1. Install and configure commitlint
2. Announce the change to your team
3. Provide training and documentation
4. Consider a grace period with warnings instead of errors
5. Optionally rewrite history (use with caution)

### Q5: How do I integrate commitlint with CI/CD?

**A:** Add this to your CI configuration:

**GitHub Actions:**
```yaml
- name: Validate commits
  run: npx commitlint --from=HEAD~1 --to=HEAD --verbose
```

**GitLab CI:**
```yaml
commitlint:
  script:
    - npx commitlint --from=$CI_COMMIT_BEFORE_SHA --to=$CI_COMMIT_SHA
```

### Q6: What's the difference between commitlint and commitizen?

**A:** They serve complementary purposes:
- **Commitlint**: Validates commit messages (enforcer)
- **Commitizen**: Helps write commit messages (assistant)

Use both together for the best experience: Commitizen guides users in writing proper commits, while commitlint ensures compliance.

### Q7: Can I use commitlint with other Git hooks?

**A:** Yes! Husky allows multiple hooks. Example `.husky/commit-msg`:
```bash
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no -- commitlint --edit ${1}
# Add other validations here
```

### Q8: How do I handle long commit messages?

**A:** Use the body section for detailed explanations:
```bash
git commit -m "feat(api): add user authentication" -m "
- Implement JWT token generation
- Add password hashing with bcrypt
- Create middleware for route protection
- Add refresh token mechanism
"
```

Or use an editor:
```bash
git commit  # Opens default editor
```

---

## Conclusion

Implementing commitlint with Git hooks transforms your development workflow by enforcing consistent, meaningful commit messages. While there's an initial setup investment, the long-term benefits—automated changelogs, better collaboration, and clearer project history—far outweigh the costs.

Start with the basic configuration, customize rules to fit your team's needs, and gradually adopt best practices. Your future self (and teammates) will thank you for maintaining a clean, professional Git history.

**Next Steps:**
1. Install commitlint in your project today
2. Customize rules for your team's workflow
3. Document your commit message standards
4. Train team members on the new process
5. Monitor and refine your configuration over time

Happy committing! 🚀
