Cheat sheet + free tool

Conventional commits cheat sheet, on one screen

The format, the types, breaking changes, footers and what commitlint rejects, in tables you can scan while you type. Every commit in the copy section passes commitlint’s default config, because I ran them all through it. The reasoning behind each row lives in the conventional commits guide.

Definition

What this covers

Conventional Commits is a format for the first line of a commit message: a type, an optional scope in parentheses, a colon and a space, then a short description. You use it when you want a tool to pick the next version number and write the changelog from your git history. fix means a patch release, feat means a minor release, and a breaking change means a major one. The other types sort the changelog and carry no version meaning under the spec, which is at version 1.0.0. This sheet is the short form: the shape, the types, both ways to mark a breaking change, footers, what commitlint’s default config rejects, and ten commits you can copy. When a row needs explaining, like why semantic-release ignores feat! out of the box, the guide linked above has it.

Free tool

Check a commit

Paste a message or a few lines of git log --oneline. It runs in your browser against the spec and commitlint’s default rules.

Commit checker

5 commits, 1 with errors. Nothing leaves your browser.

release-please

2.0.0 major

A breaking change.

semantic-release, default config

1.5.0 minor

At least one feat. A "!" commit was skipped.

  1. feat(search): find frogs by location

    featscope: searchspec: minor bumpchangelog: Features

    Valid under the spec and commitlint’s default config.

  2. fix: stop the map jumping on zoom

    fixspec: patch bumpchangelog: Bug Fixes

    Valid under the spec and commitlint’s default config.

  3. feat!: drop the v1 frogs endpoint

    featbreakingspec: major bumpchangelog: BREAKING CHANGES

    Valid under the spec and commitlint’s default config.

  4. chore: bump eslint

    chorespec: no version meaningchangelog: Miscellaneous Chores (hidden)

    Valid under the spec and commitlint’s default config.

  5. docs: Fix typo in README.

    docsspec: no version meaningchangelog: Documentation (hidden)

    • commitlint · subject-case commitlint rejects a description that starts with a capital letter. Lower-case the first word.
    • commitlint · subject-full-stop No full stop at the end of the description.

This tells you what the release tools will do with your commits. Writing the post about the release is a separate job, and that one I automated.

Format

The header

<type>[optional scope][!]: <description>

[optional body]

[optional footer(s)]
  • Colon, then exactly one space. feat:add x fails commitlint.
  • Scope is optional. Empty parentheses pass commitlint, but release-please can’t parse feat(): x, so drop them.
  • No space before the colon. feat : x fails commitlint and release-please.
  • One blank line before the body, one before the footers.
  • The spec says types aren’t case sensitive. commitlint’s default config wants them lower case, so write them lower case.

Reference

Types

Only feat and fix come from the spec. The rest are the Angular list that commitlint enforces by default. The section column is where release-please puts each type; “hidden” means left out of the changelog.

TypeWhenBumpChangelog section
featA new feature someone can useminorFeatures
fixA bug fixpatchBug Fixes
perfFaster, same behaviournone (tools: patch)Performance Improvements
revertUndoes an earlier commitnoneReverts
docsDocumentation onlynonehidden
refactorCode change, no behaviour changenonehidden
testTests onlynonehidden
buildBuild system or dependenciesnonehidden
ciCI configurationnonehidden
styleFormatting, whitespacenonehidden
choreAnything else that ships nothingnonehidden

A release made only of hidden types doesn’t happen. Want a type that isn’t on the list? Add it to your commitlint config or the linter will reject it.

Major releases

Breaking changes

Either a ! before the colon:

feat(api)!: return frogs sorted by name

Or a footer:

feat(api): return frogs sorted by name

BREAKING CHANGE: clients that relied on insertion order must sort.
  • Under the spec, the two mean the same thing, and you can use both at once.
  • The footer must be capitals. BREAKING-CHANGE: with a hyphen also counts under the spec.
  • semantic-release with no config doesn’t understand ! and releases nothing for that commit. The semantic-release guide has the two-line fix.

Last lines

Footers

Each footer is a token, then : or #, then a value. Tokens use a hyphen where a space would go. BREAKING CHANGE is the only token allowed a space.

FooterWhat it does
BREAKING CHANGE: textMajor release, listed under breaking changes
BREAKING-CHANGE: textSame under the spec. semantic-release’s default preset ignores the hyphen form
Refs: #42Free-form reference. No effect on the version
Reviewed-by: SamFree-form. Hyphen instead of the space
Release-As: 2.0.0release-please only. Forces that version

The linter

commitlint default rules

What @commitlint/config-conventional 21.2.3 enforces. Errors fail the commit. Warnings print and let it through.

RuleLevelChecksFails it
type-enumerrorType is one of the 11 in the table abovewip: half done
type-caseerrorType is lower caseFix: login button
type-emptyerrorThere is a typeAdd login
subject-emptyerrorThere is a descriptionfeat:add login
subject-caseerrorDescription does not start with a capitalfeat: Add login
subject-full-stoperrorNo full stop at the endfix: stop the double submit.
header-max-lengtherrorFirst line is 100 characters or fewera 106-character header
header-trimerrorNo whitespace before or after the first line" feat: add login"
body-max-line-lengtherrorBody lines are 100 characters or fewera 120-character body line
footer-max-line-lengtherrorFooter lines are 100 characters or fewera 120-character footer line
body-leading-blankwarningBlank line between header and bodybody on line 2
footer-leading-blankwarningBlank line before the footersRefs: #1 on line 2

subject-case is the one that catches everyone. Any description starting with a capital fails, including OAuth token refresh. Merge commits, Revert "...", fixup! and bare version numbers are skipped by default.

Copy these

Commits to copy

CommitResult
feat(search): find frogs by locationminor, under Features
fix: stop the map jumping on zoompatch, under Bug Fixes
perf(images): serve smaller portraits on mobilepatch in both release tools
docs: explain the release process in CONTRIBUTINGno release, hidden
refactor(auth): move the session check into its own moduleno release, hidden
test: cover the empty search caseno release, hidden
build(deps): bump eslint to 9no release, hidden
ci: run the tests on node 24no release, hidden

A fix with a body and two footers:

fix(auth): stop the double submit

The button fired twice on slow connections.

Refs: #42
Reviewed-by: Sam

A revert. release-please lists it under Reverts and cuts a patch; semantic-release with no config releases nothing:

revert: feat(search): find frogs by location

Refs: a1b2c3d

If you’d rather be asked questions than remember any of this, Commitizen writes the message for you.

Honesty

What this doesn’t fix

Good commits get you the right version and a changelog. They don’t get anyone outside the repo to hear about the release. The changelog waits for people who go looking, and nobody goes looking.

That’s the part I built Merge & Tell for. It reads each merged pull request and drafts a post about it, and I read every one before it goes out. The updates page is it running on itself.

The commit is tidy. Nobody has read it yet.

You were going to merge the PR anyway.

Conventional commits cheat sheet: types, rules, examples