Doom has adopted conventional commits (with a few minor tweaks). I summarize it here, temporarily, until our new documentation is up.
[SUBJECT]
[BODY]
[FILE-LOG]
[FOOTER]
Where [SUBJECT]
is:
TYPE[!][(SCOPE)]: SUMMARY
As of
074b9eb0,
use doom ci lint-commits HEAD~1
to lint your last commit locally.
TODO
TL;DR examples
Subject
Types
The commit type indicates, at a glance, what kind of change was made. Here are the types our projects recognize:
-
All projects:
-
dev
: for work on project infrastructure: build scripts, CI/CD config files (e.g..github/*
), ignore files, etc. -
docs
: changes to user-facing documentation such as module docs, manuals, function docstrings, and output from CLI commands likedoom help
ordoom doctor
. Also for core file templates, like init.example.el andcore/templates/*
files. -
feat
: code introducing a new feature or extends an existing one. -
fix
: Resolves a bug or misbehavior. Also applicable when fixing stale code that was missed during a recent bump. -
nit
: cosmetic changes to comments, formatting, or spelling/grammar with no effect on any behavior whatsoever. -
perf
: A refactor intent on improving startup/runtime performance. -
refactor
: code changes with little effect on user-facing behavior (unless paired with!
–to indicate a breaking change–in which case it indicates the wholesale removal of code/features). -
revert
: Undoes a previous change. Must be followed by:- Doom modules or categories (reverting bumps)
- Packages (reverting bumps)
- The full subject of the reverted commit (and add
Revert HASH
to FOOTER).
If you are reverting another commit in a same PR, please rebase the earlier commit out instead.
-
test
: Changes to tests (but not testing infrastructure) -
tweak
: Code changes that changes defaults and user-facing behavior, but not drastically. -
Types reserved for contributors:
-
release
:TODO
-
merge
:TODO
-
-
-
doomemacs/doomemacs:
-
bump
: Package updates or code/doc changes intended solely to reflect changes upstream. Userevert:
when downgrading packages. Must include bump commit diffs:bump: [:CATEGORY [MODULE...]|PACKAGE [PACKAGE...]|*] user/repo@a1b2c3da1b2c -> user/repo@z9y8x7wz9y8x user/repo@a1b2c3da1b2c -> user/repo@z9y8x7wz9y8x user/repo@a1b2c3da1b2c -> user/repo@z9y8x7wz9y8x [BODY] [FILE-LOG] [FOOTER]
-
module
: Changes to our module list: adding, removing, moving, or deprecating modules.-
Not for changes /within/ modules.
-
Place scope after colon.
module: add :lang zig
module: move :feature evil to :editor evil
module: remove :ui fill-column With Emacs 26.x support dropped and display-fill-column-indicator-mode introduced in Emacs 27.1, this module is reduced to a single line, and so has become too trivial to warrant its own module.
-
-
-
doomemacs/themes:
-
theme
:TODO
-
-
doomemacs/snippets:
-
snippet
:TODO
-
Scopes
Scopes may be one or more of the following:
-
All projects:
-
ci
– affects continuous integration/delivery infrastucture.
-
-
For doomemacs/doomemacs:
- A module’s name without the category:
nit(python):
,test(doom-dashboard):
,dev(popup):
,docs(everywhere):
- A category without the module, implying it affects all or many modules inside:
fix(:lang):
,feat(:editor):
, … - One of these special scopes:
-
cli
: for anything concerning Doom’s CLI:core-cli.el
,core/cli/**
, orbin/**
. - For
docs
commits:- The basename of the target org file in
docs/*
:docs(migrate):
,docs(contributing):
,docs(index):
(omit me entirely for project README, LICENSE, or major changes across many docs) -
install
for changes to our installation guide(s). This gets its own because it’s expected to change especially often.
- The basename of the target org file in
-
- A module’s name without the category:
-
For doomemacs/themes:
-
base
– affects the base theme (or all themes) - The name of any of its themes, minus the
doom-
prefix and-theme
suffix. E.g.feat(one): ...)
fordoom-one
, orfix(gruvbox-light): ...
for doom-gruvbox-light. - The name of any of any
doom-themes-ext-X.el
extension, minusdoom-themes-
and.el
. E.g.feat(ext-org): ...
-
-
For doomemacs/snippets:
- Any major mode, minus the
-mode
suffix. E.g.fix(org): ...
for a bugfix in an org-mode snippet.
- Any major mode, minus the
The scope may be omitted if:
- The change affects many parts.
- The change affects “core” code.
- Using a scopeless type, like
bump
,revert
,release
, ormerge
(as well astheme
for doomemacs/themes andmodule
for doomemacs/doomemacs). For these, the scope belongs in their SUBJECT.
Does your change touch multiple Doom modules? Only mention the target scope(s). For example, a fix or update for
:tools lsp
that affects LSP implementations in various:lang
modules only needs to specifylsp
as its scope.
No issue/PR references in SUBJECT. See Footer below.
Summary
-
Should be present tense and imperative mood.
-
feat: add X
instead offeat: added Y
-
fix: replace A with B
instead offix: replaced A with B
- When
TYPE
is a verb, it may be used as part of the SUBJECT:-
Bad:
fix: fix TAB breaking when focus is lost
-
Good:
fix: TAB breaking when focus is lost
-
Bad:
-
-
The first word in
SUBJECT
should not be capitalized – unless it’s a code reference, e.g.-
Good:
fix: TAB breaks when focus is lost
-
Good:
fix: remove buggy code
-
Bad:
feat: Add new shenanigans
-
Good:
-
Do not quote symbol references and inline code:
tweak: gc-cons-threshold = (* 128 1024 1024)
-
Only quote key sequences if they are ambiguous in their context:
-
Good:
feat: add C-; keybind
-
Good:
refactor!: remove C-c C-; keybind
-
Bad:
fix: make SPC s m a reality
-
Good:
fix: make 'SPC s m' a reality
-
Bad:
fix: bind 'SPC '' to ivy-resume
-
Good:
fix: bind "SPC '" to ivy-resume
A single quote is preferred. Switch to double quote if
'
is present in sequence, otherwise backquote. If all three characters are present, know that the ancient ones don’t take kindly to your summoning methods -
Good:
Body
BODY is optional and should explain the commit’s changes in greater detail. It has no restrictions on tense, but should follow these conventions:
-
Line width must not exceed 72 characters, except for:
- Machine generated lines, like bump diffs:
user/repo@HASH -> user/repo@HASH
- Long URLs
- Long symbol references (e.g. a long function name)
- Machine generated lines, like bump diffs:
-
Code blocks should be indented by 2 spaces from the previous paragraph, and separated from them with a blank line (before and after).
fix(foo): bar Before: (+ 1 2) After: (+ 2 1) Ref: #1234
-
Inline code should be surrounded by backquotes, ala sub-shell syntax:
fix(foo): bar `foo` calls the juxtapose of `(+ 1 2)`, i.e. `(+ 2 1)`.
File Log
Every commit ought to represent a single, central change, but that change might unavoidably span multiple files and unrelated scopes. Explain that central change in BODY, but use a per-file changelog to explain auxiliary changes, after BODY
. For example:
refactor!: rename foo to bar
BREAKING CHANGE: This changes the semantics of the `foo` function to do
X and Y, instead of Z. Any uses of this function must be updated to
reflect this.
* some/file.el (func-that-used-bar): remove `bar` use as it's no longer
needed.
* another/file.el (bar-hook, other-bar-hook): update call
* foo.el (maybe-calls-bar): update call
* a/big/file.el:
- (maybe-calls-bar): update in case of Z instead of X
- (variablename): change default to accommodate Z instead of Y
Format
The precise format of a file log entry is either:
* FILENAME[ (SUBSCOPE[, SUBSCOPE]...)]: SUMMARY
Or, for multiple changes in one file:
* FILENAME:
- (SUBSCOPE[, SUBSCOPE]): SUMMARY
- (SUBSCOPE[, SUBSCOPE]): SUMMARY
- ...
Strive to document all non-trivial changes this way, so that readers (and parsers) can detect where a symbol was changed.
Notes
- A file log entry isn’t necessary for:
- The core change itself (e.g. where
foo
was changed tobar
). - Changes that are trivial and have no impact on program correctness or behavior (e.g. reformatting, grammar/spelling, etc) unless it’s directly relevant to the core change of the commit.
- The core change itself (e.g. where
-
SUBSCOPE
can be any arbitrary identifier, but 99% of the time should refer to the local symbol(s) affected. - If a change is too broad, the
SUBSCOPE
s may be omitted, but this is discouraged. A long list ofSUBSCOPE
s is preferred over few/no scopes, for posterity. - If a change affects a file’s top-level, and doesn’t affect any particular symbol, then the scope may be omitted.
- Please sort the file list lexicographically (i.e. make it match the order they’re displayed in, in
git status
or the magit diff). - No blank link in between log entries.
Footer
FOOTER
is where commit trailers go. Each line should be comprised of:
- A keyword followed by a colon:
-
Fix: REF|HASH|URL
– This commit resolves a Github issue. UseRef
if it isn’t certain that this commit will fix the issue. -
Ref: REF|HASH|URL
– Consult this resource for more information about this commit. -
Close: REF
– This commit supersedes a Github PR or closes a Github issue (but does not resolve it; e.g. if a commit makes the issue obsolete or indicates a decision not to address the issue). -
Revert: REF|HASH
– For commits that completely revert an earlier commit or un-bumps packages (repins them to earlier commits). For half-reverts useAmend
. -
Amend: REF|HASH
– This commit addresses an issue introduced within the same release (or PR). This merges or hides the commit from the changelog generator (E.g. to avoid announcing a breaking change to behavior that was introduced before it was released). - One of Github’s supported trailers, e.g.
Co-authored-by:
orSigned-off-by:
-
- A single reference:
- An issue or PR
REF
erence:#123
,user/repo#123
,https://git.forge.fake/repo#123
- A 12-character commit
HASH
:3bedae38dd9f
,user/repo@3bedae38dd9f
,https://git.forge.fake/repo@3bedae38dd9f
- An
URL
to an external website: e.g. a mailing list, reddit discussion, etc.
- An issue or PR
Examples:
Amend: #666
Amend: 3bedae38dd9f
Close: #55
Close: user/repo#952
Fix: #1
Fix: #25
Fix: https://git.forge.fake/repo#123
Fix: user/repo#84
Ref: #4
Ref: #5
Ref: 3bedae38dd9f
Ref: https://en.wikipedia.org/wiki/Git
Ref: https://git.forge.fake/repo#123
Ref: user/repo#25
Ref: user/repo@3bedae38dd9f
Revert: 3bedae38dd9f
Co-authored-by: John Doe <john@doe.com>
Signed-off-by: Jane Doe <jane.com>
Order of trailers don’t matter, but please group like-keywords together.
Breaking changes
- Append a
!
to the TYPE to indicate a breaking change. - Prepend
BREAKING CHANGE:
to BODY, followed by an explanation of what is broken (and ideally, how to get around it).
Examples:
-
refactor!: remove X functionality BREAKING CHANGE: Without X, A and B will not work. Enable Y to get similar behavior.
-
fix!(zig): remove lsp support BREAKING CHANGE: removing LSP support reduces how much Microsoft you must ingest to write Zig, but it means no more LSP. Only workaround is to meme on the internet, especially in obscure git convention guides that only a handful of people will ever read.