Utility for formatting and verifying commit messages.
It's not a linter. At least not a complete replacement for commitlint, because commitfmt can't prevent you from writing a body or force you to write a description in uppercase (I don't know why you might want to do that), but it will help keep git history clean and readable.
By design, commitfmt runs on the prepare-commit-msg
hook and formats the message according to git standards and conventional commits in particular.
commitfmt by default transforms a message like this:
feat ( scope , scope ) : add new feature.
body description
into well-formatted message:
feat(scope, scope): add new feature
body description
commitfmt can check that developers follow the rules set by the project.
For example, check that only allowed types and scopes are used. To do this, add the following to the configuration file:
[lint.header]
# Check allowed commit type
type-enum = ["chore", "ci", "feat", "fix", "refactor", "style", "test"]
# Check allowed commit scopes
scope-enum = ["cc", "config", "git", "linter"]
[lint.footer]
# Check required footers
exists = ["Issue-ID", "Authored-By"]
commitfmt is very fast because its code is written in Rust with memory consumption and performance in mind.
It natively supports following platforms:
OS | Architecture |
---|---|
macOS | x86_64, arm64 |
Windows | x86_64, i686 |
Linux | x86_64, i686, arm64 |
You can use a simple script to install commitfmt. It will download the latest version of the binary and install it to the system.
# Install latest version
curl -sSfL https://raw.githubusercontent.com/mishamyrt/commitfmt/refs/heads/main/scripts/install.sh | bash
pnpm add --save-dev commitfmt
npm install --save-dev commitfmt
yarn add --dev commitfmt
pip install commitfmt
After installing the package, you need to add a hook to the prepare-commit-msg
event. You can use any hook manager.
Important: if you are using a pnpm, yarn or any other package manager, you need to run
pnpm commitfmt
,yarn commitfmt
, etc. instead ofcommitfmt
.
You can use a simple script to add a hook.
echo "#!/bin/sh" > .git/hooks/prepare-commit-msg
echo "commitfmt" >> .git/hooks/prepare-commit-msg
chmod +x .git/hooks/prepare-commit-msg
Add to your lefthook.yml
file:
prepare-commit-msg:
- name: format commit message
run: commitfmt
Add to your .husky
folder prepare-commit-msg
file with the following content:
#!/bin/sh
commitfmt
In commitfmt, you cannot customize basic formatting rules such as extra spaces removal.
It is an opinionated formatter and the author has established best practices that should not harm anyone.
Most of the linting rules are disabled by default. Default config contains 2 rules as they can be safely auto-fixed:
[lint.header]
description-full-stop = true
[lint.footer]
breaking-exclamation = true
To enable more rules, create a commitfmt.toml
or (.commitfmt.toml
) file in the root of your project. Available lint rules can be found in the rules.md file.
If there is a problem with an enabled rule and it cannot be automatically fixed, the commit process will be aborted.
Some rules may be fixed, but in certain contexts this fix may not be what is desired. For example, adding a full stop to the end of body will be useful in most cases, if there is a log at the end of the message, the period may distort it. You can see which rules have unsafe patches in the same rules.md
file mentioned above.
To enable unsafe fixes, add the following to your config file:
[lint]
unsafe-fixes = true
You can extend the configuration of the parent project by adding the extends
key to your config file:
extends = "node_modules/commitfmt-config-standard/commitfmt.toml"
Extension is only possible for the current configuration. If the current configuration extends another configuration, which in turn extends a third configuration, commitfmt will throw an error when trying to load such a configuration.
commitfmt can be configured to use custom footer separators and comment symbols for parsing commit messages.
By default, commitfmt uses git's trailer.separators
configuration to determine which characters separate footer keys from values. You can override this in your config file:
footer-separators = ":#"
This allows footers like Issue-ID: 123
or Issue-ID #123
to be recognized.
By default, commitfmt uses git's core.commentChar
or core.commentString
configuration to identify comment lines in commit messages. You can override this:
comment-symbol = "//"
Lines starting with the comment symbol will be ignored during parsing.
commitfmt can add additional footers to the commit message.
You can add a footer with a static value:
[[additional-footers]]
key = "Authored-By"
value = "John Doe"
You can use shell commands to dynamically generate footer values:
[[additional-footers]]
key = "Authored-By"
value = "{{ echo $USER }}"
Inside the template expression you can use any shell command available in the PATH
.
You can also add the ticket number from the task tracker to the footer if it is in the branch name:
[[additional-footers]]
key = "Ticket-ID"
branch-pattern = "(?:.*)/(?<TICKET_ID>[A-Z0-9-]+)/?(?:.*)"
value = "${{ TICKET_ID }}"
For example, if your branch name is feature/CC-123/add-new-feature
or feature/CC-123
, the Ticket-ID
footer will be added to the commit message with the value CC-123
.
If the ticket number is not found in the branch name, footer will be skipped.
You can use rustexp to test your pattern.
Examples of patterns for branch names in git flow format:
- Jira/YouTrack:
(?:.*)/(?<TICKET_ID>[A-Z0-9-]+)/?(?:.*)
feature/CFMT-123
feature/CFMT-123/add-new-feature
- GitHub:
(?:.*)/(?<ISSUE_ID>[0-9-]+)/?(?:.*)
feature/123
feature/123/add-new-feature
If the footer already exists in the commit message, you can specify what to do with it. By default, the footer will be skipped.
[[additional-footers]]
key = "Ticket-ID"
branch-pattern = "(?:.*)/(?<TICKET_ID>[A-Z0-9-]+)/?(?:.*)"
value = "${{ TICKET_ID }}"
on-conflict = "error" # optional. default: skip. available: skip, append, error
Available options:
-
skip
- skip the footer if it already exists -
append
- append the footer to the end of the footer list -
error
- abort the commit
You can customize how footers are formatted using separator
and alignment
options:
[[additional-footers]]
key = "Ticket-ID"
value = "CFMT-123"
separator = "#"
alignment = "right"
Available alignment options:
-
left
- align separator to the left (default) -
right
- align separator to the right
To enforce conventional commits, you can use the following configuration:
[lint.header]
type-enum = ["chore", "ci", "feat", "fix", "refactor", "style", "test", "docs"]
description-case = "lower-first"
description-max-length = 72
description-full-stop = true
type-required = true
# scope-enum = ["cc", "config", "git", "linter"] # optional
[lint.body]
max-line-length = 72
case = "upper-first"
[lint.footer]
breaking-exclamation = true
To test the configuration and the work of commitfmt, run the following command:
echo "chore ( test ) : test commit" | commitfmt
# or
cat commit_text.txt | commitfmt
To test the history of commits, run the following command:
commitfmt --from HEAD~20
# or
commitfmt --from 1234567890 --to 1234567890