commitmsgfmt
formats commit messages. It reflows and wraps text, with special
understanding of patterns often seen in commit messages.
For formatting commit messages, commitmsgfmt
's support for non-trivial text
constructs makes it superior to fmt(1)
; its simplicity makes it superior to
Vim's internal formatting function; and its ease of use makes it superior to
par(1)
.
commitmsgfmt
is written specifically for use with Vim's 'formatprg'
option,
which passes text to format on standard input and reads the formatted result on
standard output. If your text editor does not have a similar integration
mechanism commitmsgfmt
can also be used in a Git hook.
In summary, commitmsgfmt
-
ensures a single-line subject of up to 90 characters, separated from the body by a single blank line, by pushing any extra characters into a body paragraph;
-
reflows and wraps all other prose at the specified max width, defaulting to 72 characters based on Tim Pope's recommendation from 2008 [tpope];
-
properly indents continuation lines in numbered and unnumbered lists, recognizing several different list styles;
-
exempts comments; text indented at least 4 spaces or 1 tab; "trailers" (
Signed-off-by:
); and block quotes; -
assumes UTF-8 encoded input but can gracefully degrade to ISO-8859-1 ("latin1") which has been observed in the Linux kernel;
-
counts "characters" as they appear on screen, i.e. graphemes, without mangling multi-byte characters.
For more details on commitmsgfmt
's behaviour, refer to its manual page.
-
commitmsgfmt
is context sensitive but Vim'sgq
command has no context, rendering the general form of it effectively useless. One or more mappings can alleviate this but it has implications for muscle memory. -
commitmsgfmt
was not written to handle certain constructs that appear in merge commit messages in the Git and Linux kernel projects. If you write merge commit messages any differently from non-merge commit messages you may want to disablecommitmsgfmt
when merging, as demonstrated in the installation instructions. -
commitmsgfmt
currently uses a greedy algorithm to wrap text, likefmt
and unlikepar
, because the implementation is easy to write and the result easy to compare against authentic commit messages. Unfortunately this produces flush-left/ragged-right text that tends to look worse than justified text. This behaviour is considered a halfway happy, not a specification, and may or may not change.
commitmsgfmt
provides precompiled binaries for Linux, macOS, and Windows on
a best-effort basis:
https://github.com/commonquail/commitmsgfmt/releases
The Linux binaries are static executables. The Windows binaries are built with Microsoft Visual C++ and require a separate installation of the Microsoft VC++ redistributable [vc-redist].
Alternatively, if you have a working Rust environment you can build the binary
from scratch with cargo build --release
.
Once you have obtained the binary, place it somewhere in your $PATH
.
To integrate with Vim ensure you have filetype plugin on
, then create the
file $HOME/.vim/after/ftplugin/gitcommit.vim
containing, at least,
setlocal formatprg=commitmsgfmt
The above setting is the absolute minimum requirement, and is sufficient, but
can be improved upon in a few ways. See the sample
contrib/vim/after/ftplugin/gitcommit.vim
file for details.
This is basically all text editors I know of, however, I expect Emacs can be made to work like Vim. I would gladly include such a contributed example configuration.
For text editors without the ability to delegate reformatting to a third-party
tool you can leverage the commit-msg
Git hook [hooks] to reformat the
commit message. This works, and is explicitly allowed by the specification, but
you lose the ability to easily undo any undesirable formatting caused by
commitmsgfmt
.
If
commitmsgfmt
makes a mistake you have to correct, rungit commit --amend --no-verify
to reopen the last commit's message for editing and bypass the hook.
contrib/git/hooks/commit-msg
defines a complete commit-msg
hook ready for
use; just copy it to your repository's hook directory:
$ cp contrib/git/hooks/commit-msg ~/foo/bar-repo/.git/hooks/
The provided hook is conservative to make up for the difficulty of correcting
mistakes. If you know you never write merge commit messages any differently
from non-merge commit messages feel free to enable commitmsgfmt
for merge
commits, too.
If you don't want to repeat this setup for every single repository, consider
specifying either the init.templateDir
Git configuration option to always
include this hook for new repositories [templatedir] or the core.hooksPath
option to centralise Git hooks [hookspath].
My reasons for writing commitmsgfmt
are numerous and varied, and include such
things as boredom, a desire to play with Rust, and a capacity for obsessing
over the immaterial. More than anything, though, I had grown weary of tuning
Vim's various formatting related options and never being quite satisfied.
Additionally, the quality of commit integrations in many popular editors and IDEs is simply appalling and that is an obstacle to good version control hygiene. Instead of bemoaning the influx of fashionably inadequate Git integrations I wrote a tool to sidestep one of their limitations. If this sounds judgmental consider this picture of one enormously popular text editor's Git commit integration:
commitmsgfmt
uses Git-like conventions because I use Git but is ultimately
agnostic about version control systems. I welcome contributions on integrating
with other systems.
commitmsgfmt
is a third-party dependency, written in a tool chain less
ubiquitous than C and C++, that solves a trivial, ultimately inconsequential
problem. Other tools more readily available solve a related problem of general
text formatting and their limitations may be a preferable trade-off.
-
fmt(1)
offers limited reflowing and wrapping functionality with some configurability, sufficient for simple prose. It does not handle lists. -
Vim has configurable built-in text formatting and, when configured correctly, tends to do a better job than
fmt
. Its main limitations are configuration complexity and an inability to exempt certain text constructs from formatting rules. -
par(1)
[par] is "vaguely similar tofmt
, but better." It is highly configurable and excellent at justifying text, generally yielding prettier results thanfmt
. It is also very difficult to configure.