Skip to content

Repository files navigation

PkgGoDev

commitlint

commitlint checks if your commit message meets the conventional commit format

<type>[optional scope]: <description>
[optional body]
[optional footer(s)]

Table of Contents

Installation

Releases

Download binary from releases and add it to your PATH

Using go

go install github.com/conventionalcommit/commitlint@latest

Setup

  • Enable for a single git repository, cd to repository directory
commitlint init
  • Enable globally for all git repositories
commitlint init --global
  • to customize hooks destination pass --hookspath with desired location
commitlint init --hookspath /path/to/hooks
commitlint init --global --hookspath /path/to/hooks

Manual

  • run commitlint hook to create .commitlint/hooks containing git hooks
    • pass --hookspath or -p to customize the hooks output path
  • To enable in single repo
    • run git config core.hooksPath /path/to/.commitlint/hooks
  • To enable globally
    • run git config --global core.hooksPath /path/to/.commitlint/hooks

Remove

  • To remove hooks from a single repository
commitlint remove
  • To remove hooks globally
commitlint remove --global

Both commands ask for confirmation before unsetting core.hooksPath in git config. Hook files are left intact.

Quick Test

  • Valid commit message
echo"feat: good commit message"| commitlint lint
# ✔ commit message
  • Invalid commit message
echo"fear: do not fear for commit message"| commitlint lint
# ❌ type-enum: type 'fear' is not allowed, you can use one of [build chore ci docs feat fix merge perf refactor revert style test]

Commands

config

  • To create a config file, run commitlint config create, this will create .commitlint.yaml with only the enabled rules and their settings (compact format)

  • To create a config file with all rules and settings written out (including disabled ones), run commitlint config create --all

  • To validate a config file, run commitlint config check /path/to/conf.yaml

lint

To lint a message, you can use any one of the following

  • run commitlint lint --message=file
  • run echo "message" | commitlint lint
  • run commitlint lint < file

commitlint lint follows below order for config and message

Config Precedence

  • config file passed to --config command-line argument
  • COMMITLINT_CONFIG env variable
  • config file in current directory or git repo root in the below order
    • .commitlint.yml
    • .commitlint.yaml
    • commitlint.yml
    • commitlint.yaml
  • default config

Message Precedence

  • stdin pipe stream
  • commit message file passed to --message command-line argument
  • .git/COMMIT_EDITMSG in current directory

hook

  • To create hook files, run commitlint hook
    • pass --hookspath or -p to customize the hooks output directory
    • pass --replace or -r to overwrite existing hook files

debug

To prints useful information for debugging commitlint

run commitlint debug

Default Config

min-version: v0.11.0formatter: defaultrules:
- header-min-length
- header-max-length
- body-max-line-length
- footer-max-line-length
- type-enumseverity:
default: errorrules: {}settings:
body-max-line-length:
argument: 100flags: {}footer-max-line-length:
argument: 100flags: {}header-max-length:
argument: 72flags: {}header-min-length:
argument: 10flags: {}type-enum:
argument:
- feat
- fix
- docs
- style
- refactor
- perf
- test
- build
- ci
- chore
- revertflags: {}disable-default-ignores: falseignores: []

Commit Types

Commonly used commit types

TypeDescription
featA new feature
fixA bug fix
docsDocumentation only changes
styleChanges that do not affect the meaning of the code (white-space, formatting etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance
testAdding missing tests or correcting existing tests
buildChanges that affect the build system or external dependencies
ciChanges to our CI configuration files and scripts
choreOther changes that don't modify src or test files
revertReverts a previous commit

Ignore Patterns

commitlint automatically skips linting for commit messages generated by git (merges, reverts, fixups, etc.). If the first line of a commit message matches any ignore pattern, linting is skipped entirely.

Default Ignore Patterns

The following patterns are enabled by default (source: config/default.go):

PatternMatches
^Merge pull request #\d+GitHub pull request merges
^Merge .+ into .+Generic merge (X into Y)
^Merge branch '.+'git merge branch
^Merge tag '.+'git merge tag
^Merge remote-tracking branch '.+'git merge remote-tracking branch
^Merged .+ (in|into) .+Azure DevOps / Bitbucket merged
^Merged PR #?\d+Azure DevOps pull request
^(R|r)evert git revert
^(R|r)eapply git reapply
^(amend|fixup|squash)! git commit --fixup/--squash/--amend
^Automatic mergeAutomatic merges
^Auto-merged .+ into .+Auto-merged branches
^Initial commit$Initial commit (exact match)

Custom Ignore Patterns

Add your own patterns in the config file under ignores:. User-defined patterns are additive, they are checked alongside the built-in defaults.

ignores:
- "^WIP "
- "^TICKET-\\d+"

Disabling Default Ignores

If you want only your custom patterns (no built-in defaults), set disable-default-ignores: true:

disable-default-ignores: trueignores:
- "^WIP "

Available Rules

Rules marked ✅ enabled are active by default. All others can be opted into via the rules: list in your config.

Length rules

nameargumentflagsdescriptiondefault
header-min-lengthintn/amin length of header (first line)✅ enabled (10)
header-max-lengthintn/amax length of header (first line)✅ enabled (72)
body-min-lengthintn/amin length of bodyN/A
body-max-lengthintn/amax length of bodyN/A
body-max-line-lengthintn/amax length of each line in body✅ enabled (100)
footer-min-lengthintn/amin length of footerN/A
footer-max-lengthintn/amax length of footerN/A
footer-max-line-lengthintn/amax length of each line in footer✅ enabled (100)
type-min-lengthintn/amin length of typeN/A
type-max-lengthintn/amax length of typeN/A
scope-min-lengthintn/amin length of scopeN/A
scope-max-lengthintn/amax length of scopeN/A
description-min-lengthintn/amin length of descriptionN/A
description-max-lengthintn/amax length of descriptionN/A

Enum / allow-list rules

nameargumentflagsdescriptiondefault
type-enum[]stringn/arestrict type to given list of strings✅ enabled
scope-enum[]stringallow-empty: boolrestrict scope to given list of stringsN/A
footer-enum[]stringn/arestrict footer token to given listN/A
footer-type-enum[]{token, types, values}n/aenforce footer notes for given typeN/A

Charset rules

nameargumentflagsdescriptiondefault
type-charsetstringn/arestrict type to given charsetN/A
scope-charsetstringn/arestrict scope to given charsetN/A

Case rules

All case rules accept one of: lower-case, upper-case, camel-case, kebab-case, pascal-case, sentence-case, snake-case, start-case.

nameargumentflagsdescriptiondefault
type-casestringn/aenforce case format on typeN/A
scope-casestringn/aenforce case format on scope (skips empty)N/A
description-casestringn/aenforce case format on descriptionN/A
body-casestringn/aenforce case format on entire bodyN/A
header-casestringn/aenforce case format on full headerN/A

Empty / presence rules

These rules enforce that a field is not empty.

nameargumentflagsdescriptiondefault
type-emptyn/an/atype must not be emptyN/A
scope-emptyn/an/ascope must not be emptyN/A
body-emptyn/an/abody must not be emptyN/A
footer-emptyn/an/afooter must not be emptyN/A
description-emptyn/an/adescription must not be emptyN/A

Full-stop rules

Check that a field does not end with a given character (default ".").

nameargumentflagsdescriptiondefault
header-full-stopstringn/aheader must not end with given char (e.g. ".")N/A
body-full-stopstringn/abody must not end with given charN/A
description-full-stopstringn/adescription must not end with given charN/A

Leading-blank rules

Enforce that a blank line separates commit sections (conventional commits spec).

nameargumentflagsdescriptiondefault
body-leading-blankn/an/abody must be preceded by a blank lineN/A
footer-leading-blankn/an/afooter must be preceded by a blank lineN/A

Header formatting rules

nameargumentflagsdescriptiondefault
header-trimn/an/aheader must not have leading or trailing whitespaceN/A

Trailer / sign-off rules

The argument is the trailer token. A trailing : is accepted and stripped automatically, so "Signed-off-by" and "Signed-off-by:" are equivalent.

nameargumentflagsdescriptiondefault
signed-off-bystringn/acommit must have a footer note whose token matches (e.g. "Signed-off-by")N/A
trailer-existsstringn/acommit must have a footer note whose token matches (e.g. "Co-authored-by")N/A

Breaking change rules

nameargumentflagsdescriptiondefault
breaking-change-exclamation-markn/an/aXNOR: either both ! in header and BREAKING CHANGE in footer are present, or neither N/A not just one aloneN/A

Available Formatters

  • default
commitlint
→ input: "fear: do not fear for ..."
Errors:
❌ type-enum: type 'fear' is not allowed, you can use one of [build chore ci docs feat fix perf refactor revert style test]
Total 1 errors, 0 warnings, 0 other severities
  • JSON
{"input":"fear: do not fear for commit message","issues":[{"description":"type 'fear' is not allowed, you can use one of [build chore ci docs feat fix perf refactor revert style test]","name":"type-enum","severity":"error"}]}

Programmatic Usage

All public packages are importable. The module path is github.com/conventionalcommit/commitlint.

go get github.com/conventionalcommit/commitlint@latest

Key packages:

PackagePurpose
configParse config files, build a Linter, access defaults
lintCore types: Linter, Rule, Formatter, Config, Result, Issue
registryRegister and look up custom rules / formatters
ruleBuilt-in rule implementations
formatterBuilt-in formatters (default, json)

One-liner with default config

The simplest entry point — no config file required:

package main
import (
"fmt""github.com/conventionalcommit/commitlint/config"
)
funcmain() {
result, err:=config.LintMessage("feat: add login page")
iferr!=nil {
panic(err)
}
for_, issue:=rangeresult.Issues() {
fmt.Printf("%s: %s: %s\n", issue.Severity(), issue.RuleName(), issue.Description())
}
iflen(result.Issues()) ==0 {
fmt.Println("commit message is valid")
}
}

Full control with default config

Build the linter yourself for more control (e.g. to swap the formatter):

package main
import (
"fmt""github.com/conventionalcommit/commitlint/config""github.com/conventionalcommit/commitlint/formatter"
)
funcmain() {
conf:=config.NewDefault()
// optionally customise conf herelinter, err:=config.NewLinter(conf)
iferr!=nil {
panic(err)
}
result, err:=linter.ParseAndLint("feat: add login page")
iferr!=nil {
panic(err)
}
out, err:= (&formatter.JSONFormatter{}).Format(result)
iferr!=nil {
panic(err)
}
fmt.Println(out)
}

Lint with a config file

Load a .commitlint.yaml and lint against it:

package main
import (
"fmt""github.com/conventionalcommit/commitlint/config"
)
funcmain() {
conf, err:=config.Parse(".commitlint.yaml")
iferr!=nil {
panic(err)
}
linter, err:=config.NewLinter(conf)
iferr!=nil {
panic(err)
}
result, err:=linter.ParseAndLint("feat: add login page")
iferr!=nil {
panic(err)
}
for_, issue:=rangeresult.Issues() {
fmt.Printf("%s: %s\n", issue.RuleName(), issue.Description())
}
}

Custom rules

Implement the lint.Rule interface and register it before building a linter:

package main
import (
"fmt""github.com/conventionalcommit/commitlint/config""github.com/conventionalcommit/commitlint/lint""github.com/conventionalcommit/commitlint/registry"
)
// NoWIPRule rejects commit messages whose description starts with "WIP".typeNoWIPRulestruct{}
func (r*NoWIPRule) Name() string { return"no-wip" }
func (r*NoWIPRule) Apply(setting lint.RuleSetting) error { returnnil }
func (r*NoWIPRule) Validate(commit lint.Commit) (*lint.Issue, error) {
iflen(commit.Description()) >=3&&commit.Description()[:3] =="WIP" {
returnlint.NewIssue("description must not start with WIP"), nil
}
returnnil, nil
}
funcmain() {
iferr:=registry.RegisterRule(&NoWIPRule{}); err!=nil {
panic(err)
}
conf:=config.NewDefault()
conf.Rules=append(conf.Rules, "no-wip")
conf.Settings["no-wip"] = lint.RuleSetting{}
linter, err:=config.NewLinter(conf)
iferr!=nil {
panic(err)
}
result, err:=linter.ParseAndLint("feat: WIP do not merge")
iferr!=nil {
panic(err)
}
for_, issue:=rangeresult.Issues() {
fmt.Printf("%s: %s\n", issue.RuleName(), issue.Description())
}
}

Custom formatters

Implement lint.Formatter and register it:

package main
import (
"fmt""strings""github.com/conventionalcommit/commitlint/config""github.com/conventionalcommit/commitlint/lint""github.com/conventionalcommit/commitlint/registry"
)
typeSimpleFormatterstruct{}
func (f*SimpleFormatter) Name() string { return"simple" }
func (f*SimpleFormatter) Format(result*lint.Result) (string, error) {
iflen(result.Issues()) ==0 {
return"ok", nil
}
varsb strings.Builderfor_, issue:=rangeresult.Issues() {
fmt.Fprintf(&sb, "[%s] %s: %s\n", issue.Severity(), issue.RuleName(), issue.Description())
}
returnsb.String(), nil
}
funcmain() {
iferr:=registry.RegisterFormatter(&SimpleFormatter{}); err!=nil {
panic(err)
}
conf:=config.NewDefault()
conf.Formatter="simple"format, err:=config.GetFormatter(conf)
iferr!=nil {
panic(err)
}
linter, err:=config.NewLinter(conf)
iferr!=nil {
panic(err)
}
result, err:=linter.ParseAndLint("bad message")
iferr!=nil {
panic(err)
}
out, err:=format.Format(result)
iferr!=nil {
panic(err)
}
fmt.Print(out)
}

FAQ

  • How to have custom config for each repository?

    Place .commitlint.yaml file in repo root directory. linter follows config precedence.

    To create a sample config, run commitlint config create (or commitlint config create --all to include all available settings)

  • How can I skip lint check for a commit?

    use --no-verify flag with git commit which skips commit hooks

  • How does commitlint handle merge / revert commits?

    commitlint ships with built-in ignore patterns that automatically skip linting for merge commits, reverts, fixups, squashes, and other git-generated messages. You can add your own patterns with the ignores config key, or disable the defaults with disable-default-ignores: true.

  • Can I use the old version config key?

    Yes. The version key is still accepted for backward compatibility, but new config files should use min-version instead.

License

All packages are licensed under MIT License

About

commitlint checks if your commit messages meets the conventional commit format

Topics

Resources

Stars

87 stars

Watchers

2 watching

Forks

Releases

Used by

Contributors

Languages