lint-commit-msg
checks that a Git commit message follows
community standard formatting rules.
- a single, self-contained Bash script: no dependencies, no configuration files
- no need for installation: simply download the script
- reasonable defaults — yet highly customizable
- used normally in Git
commit-msg
hook
$ git commit -m "add first version of REST client and instructions how to use it with HTTPS"
ERROR: commit message not properly formatted
- line 1: subject line too long (74), max length is 60
- line 1: subject not capitalized
add first version of REST client and instructions how to use it with HTTPS
Continue anyway? [yes/no/edit] no
Aborting commit!
Commit message saved in .git/lint-commit-msg.MSG
$
- Download the latest release, make it executable, and put in your
PATH
. - Call it in your commit-msg hook like this
lint-commit-msg "$1" || exit
- Run
lint-commit-msg --help
for usage examples and information about configuration options.
- Prerequisites
- Installation
- Basic usage
- Using in
commit-msg
hook - Advanced usage and configuration
- Examples
- Tips
lint-commit-msg
requires very few things to work (and you probably already have them installed).
- Bash version 3 or later
- minimal set of core utilities like
cp
,cat
, andtr
git
itself
Download the script and make it executable.
curl -L https://github.com/laurivan/lint-commit-msg/releases/latest/download/lint-commit-msg > lint-commit-msg
chmod u+x lint-commit-msg
You can test the script by linting a commit message written in a file. If there are errors in the commit message the script returns a non-zero exit code.
cat > test-message.txt <<EOF
This is the subject line of my commit
And here's the body.
EOF
./lint-commit-msg test-message.txt || echo "Failed"
To lint your commit messages automatically you can call lint-commit-msg
in your
Git repository's commit-msg
hook. For this, there are a couple of approaches.
- Move
lint-commit-msg
somewhere in your PATH.mv lint-commit-msg ~/bin
- Add the line below to your commit-msg hook which is by default
my-repo/.git/hooks/commit-msg
.If the hook doesn't exist you can create it and make it executable.lint-commit-msg "$1" || exit
cat > my-repo/.git/hooks/commit-msg <<'EOF' #!/bin/sh lint-commit-msg "$1" || exit EOF chmod u+x my-repo/.git/hooks/commit-msg
If you want to distribute your hooks to anyone who clones the repository
you can add lint-commit-msg
to your repository and call it using a relative path:
- Add
commit-msg
hook andlint-commit-msg
to your repository.mkdir my-repo/hooks cp lint-commit-msg my-repo/hooks cat > my-repo/hooks/commit-msg <<'EOF' #!/bin/sh hooks/lint-commit-msg "$1" || exit EOF chmod u+x my-repo/hooks/*
- Configure Git to use the
hooks
directory. This step has to be done by anyone who clones the repository.cd my-repo git config core.hooksPath hooks
That's it!
lint-commit-msg
will now check your commit messages
and let you either fix or ignore the potential errors.
$ git commit -m "created README file"
ERROR: commit message not properly formatted
- line 1: subject not capitalized
- line 1: verb 'created' in the subject not in imperative mood,
use for example 'Add' instead of 'Added'
created README file
Continue anyway? [yes/no/edit] no
Aborting commit!
Commit message saved in .git/lint-commit-msg.MSG
$ git commit -m "Create README file"
lint-commit-msg: commit message OK
[main 15d6555] Create README file
1 file changed, 1 insertion(+)
create mode 100644 README.md
First, lint-commit-msg has sensible defaults so one can
simply start using it with minimal or no fine tuning.
That being said, lint-commit-msg
is highly customizable.
One can set environment variables either per invocation
LCM_IGNORE_SUBJECT_NOT_CAPITALIZED=true git commit ...
or more permanently by exporting variables. This can be done for example in the commit-msg hook or in the startup files of your shell.
# e.g. ~/.bashrc
export LCM_INTERACTIVE=never
export LCM_SUBJECT_LINE_MAX_LENGTH=55
There are three categories of configuration: general settings, variables for modifying the linting rules, and variables for ignoring them. The following sections describes each configuration variable. See examples for more guidance on how to use them.
Environment variable | Description | Default value |
---|---|---|
LCM_INTERACTIVE |
Allow the user to choose interactively whether to ignore the reported errors. Options: always , never , auto . Using auto will enable interactive mode if lint-commit-msg is run as part of commit-msg hook AND stdout is terminal. Note that always is hardly ever what you want and doesn't play along well when committing from an IDE. |
auto |
LCM_COLOR |
Whether to use colored output. Options: always , never , auto . With auto colors are used if printing to terminal. |
auto |
The variables above will also accept values true
(alias for always
) and false
(alias for never
).
lint-commit-msg
also inspects environment variables
GIT_EDITOR
, VISUAL
, and EDITOR
(in this order) for a text editor
to launch if the user wishes to edit an invalid commit message interactively.
Environment variable | Description | Default value |
---|---|---|
LCM_SUBJECT_LINE_MAX_LENGTH |
Maximum length of the subject line (the first line). | 60 |
LCM_SUBJECT_LINE_MIN_LENGTH |
Minimum length of the subject line (the first line). | 3 |
LCM_BODY_LINE_MAX_LENGTH |
Maximum length of a line in the message body. | 72 |
LCM_SUBJECT_LINE_PREFIX_REGEX |
A POSIX extended regular expression that the start of the subject line should match. (That is, don't prefix the regex with ^ character.) |
"" |
LCM_SUBJECT_LINE_PREFIX_HELP |
A custom help message to display if the start of the subject line doesn't match LCM_SUBJECT_LINE_PREFIX_REGEX . By default, a somewhat technical, and not that helpful, error message "subject line does not match regex..." is displayed. This variable can be set to provide a more user-friendly message. |
"" |
Note that the part of the subject line that matches LCM_SUBJECT_LINE_PREFIX_REGEX
is not taken into account when checking whether the subject is capitalized and whether (the first word of) the subject is in imperative mood (see LCM_IGNORE_SUBJECT_NOT_CAPITALIZED
and LCM_IGNORE_SUBJECT_MOOD
in the next section).
For example, some software teams have a convention of putting an issue ID in the beginning of the subject line:
DCL-5332 remove redundant SQL tables
Because the first character of the subject line is the capital D
the message would not raise a linting error
even though the actual subject "remove redundant SQL tables" is not capitalized.
The solution is to use
LCM_SUBJECT_LINE_PREFIX_REGEX='DCL-[1-9][0-9]* '
# or more generally
LCM_SUBJECT_LINE_PREFIX_REGEX='[A-Z][A-Z0-9]*-[1-9][0-9]* '
# or if the issue ID is optional
LCM_SUBJECT_LINE_PREFIX_REGEX='([A-Z][A-Z0-9]*-[1-9][0-9]* )?'
Now the subject line prefix is matched against the regular expression and the actual subject is checked to start with an uppercase letter.
Variables in the table below can be set to true
to ignore certain (or all) errors.
Setting them to false
is equivalent to not having them set at all.
Environment variable | Effect (when variable set to "true") |
---|---|
LCM_IGNORE_ALL |
Ignore all linting rules. This effectively skips the linting. |
LCM_IGNORE_SUBJECT_LINE_TOO_SHORT |
Allow too short subject line. This rule is mostly for preventing accidental commits with a message of one or two characters. |
LCM_IGNORE_SUBJECT_LINE_TOO_LONG |
Allow too long subject line. The default maximum length for the subject line is 60 characters. |
LCM_IGNORE_SUBJECT_LINE_ENDS_IN_PERIOD |
Allow subject line to end in a period. |
LCM_IGNORE_SUBJECT_NOT_CAPITALIZED |
Allow subject to start with a lowercase letter. |
LCM_IGNORE_INVALID_SUBJECT_LINE_PREFIX |
Allow subject line not to start with the configured subject line prefix regex. |
LCM_IGNORE_SUBJECT_MOOD |
Don't check that the first word of the subject is (a verb) in imperative mood. Note that this rule uses a simple heuristic of checking that the first word of the subject does not end in s , ed , or ing (for example adds , added , or adding ). The user is adviced to simply ignore the rare false positives. |
LCM_IGNORE_BODY_LINE_TOO_LONG |
Allow too long lines in the body of the commit message. |
LCM_IGNORE_TABS |
Allow the commit message to contain tab characters. |
LCM_IGNORE_TRAILING_WHITESPACE |
Allow the commit message to contain trailing whitespace. |
LCM_IGNORE_MISSING_FINAL_EOL |
Allow the commit message to end with a character other than an EOL. |
LCM_IGNORE_2ND_LINE_NOT_BLANK |
Allow the 2nd line of the commit message contain text. |
LCM_IGNORE_LINE_COUNT_IS_2 |
Allow the commit message to be two lines long. |
# Print usage instructions
lint-commit-message -h
# OR
lint-commit-message --help
# Lint commit message in a file
lint-commit-message msg.txt
# Lint commit message before committing
lint-commit-message msg.txt && git commit --file msg.txt
# The same as above but wrapped in a shell function
lintedcommit() {
local msg_file="$1"; shift
lint-commit-message "${msg_file}" && commit --file "${msg_file}" "$@"
}
lintedcommit msg.txt
# Read from stdin
cat msg.txt | lint-commit-msg
# Lint the message of an existing commit
git log -n1 --format=format:%B HEAD | lint-commit-msg
# Modify behaviour for a single invocation
LCM_IGNORE_SUBJECT_MOOD=true LCM_BODY_LINE_MAX_LENGTH=80 lint-commit-msg msg.txt
The examples below show the contents of commit-msg hook (by default .git/hooks/commit-msg
).
# Use with default configuration
lint-commit-msg "$1" || exit
# Allow longer lines
export LCM_SUBJECT_LINE_MAX_LENGTH=80
export LCM_BODY_LINE_MAX_LENGTH=80
lint-commit-msg "$1" || exit
# Ignore line length restrictions
export LCM_IGNORE_SUBJECT_LINE_TOO_SHORT=true
export LCM_IGNORE_SUBJECT_LINE_TOO_LONG=true
export LCM_IGNORE_BODY_LINE_TOO_LONG=true
lint-commit-msg "$1" || exit
# Require issue ID (e.g. "DCL-1122", "K8SU-13") at the beginning of the subject line.
export LCM_SUBJECT_LINE_PREFIX_REGEX='[A-Z][A-Z0-9]*-[1-9][0-9]* '
export LCM_SUBJECT_LINE_PREFIX_HELP='Subject line should start with the issue ID e.g. "DCL-1122 Fix JWT handling"'
lint-commit-msg "$1" || exit
# Expect an optional issue ID in the subject line. The regex will match any
# message and therefore has no direct effect on the linting. However, without
# it a message like
# DCL-1533 add missing SQL tables
# would be (erroneously) considered valid even though the subject
# "add missing SQL tables" is not capitalized.
export LCM_SUBJECT_LINE_PREFIX_REGEX='([A-Z][A-Z0-9]*-[1-9][0-9]* )?'
lint-commit-msg "$1" || exit
# Require "conventional commit message" style subject lines with two possible
# scopes: "backend" and "frontend". (See https://www.conventionalcommits.org)
# Requires a subject like "fix(backend)!: Rename purchase API input params"
export LCM_SUBJECT_LINE_PREFIX_REGEX='(fix|feat|build|chore|ci|docs|style|refactor|perf|test)(\((backend|frontend)\))?!?: '
lint-commit-msg "$1" || exit
# Specify default values (different from internal defaults of lint-commit-msg)
# in a way that allows the user to override them when invoking 'git commit'
: ${LCM_SUBJECT_LINE_MAX_LENGTH:=50}
: ${LCM_BODY_LINE_MAX_LENGTH:=80}
export LCM_SUBJECT_LINE_MAX_LENGTH LCM_BODY_LINE_MAX_LENGTH
lint-commit-msg "$1" || exit
# This allows the user to run e.g.
# LCM_BODY_LINE_MAX_LENGTH=72 git commit ...
The purpose of lint-commit-msg is to help you make your commit messages more usable; not to earn you a medal for 100% error-free commit messages. If you think a linting error is a false positive simply ignore it. If you think fixing an error reported by lint-commit-msg would not make your message more readable just disregard it.
A commit message is linted only once. It's not like a compilation warning that you see with every build until you fix it.
$ git commit -m "Add unit tests for GET, POST, PUT, etc."
ERROR: commit message not properly formatted
- line 1: subject line ends in a period (.)
Add unit tests for GET, POST, PUT, etc.
Continue anyway? [yes/no/edit] yes