Conventional Commits & categorisation¶
changelog is opinionated about one thing: commit messages follow the
Conventional Commits specification. That convention
is what lets it turn a flat list of commits into a structured, categorised changelog
without any manual curation. This page explains how the mapping works and why.
From commit type to category¶
Each commit is parsed with the
leodido/go-conventionalcommits parser
(in best-effort mode, so a non-conforming message degrades gracefully instead of failing
the run). The commit type selects a Category:
| Commit type | Category |
|---|---|
feat |
CategoryFeature |
fix |
CategoryFix |
perf |
CategoryPerformance |
anything else (refactor, docs, chore, build, …) |
CategoryOther |
test, ci |
dropped |
test and ci are dropped deliberately: they describe changes to the project's own
scaffolding, not to the thing being released, so they add noise to a user-facing changelog.
The commit scope (feat(cli):) and description are carried onto the Entry as Scope
and Description; the raw subject line is preserved in Raw.
Detecting breaking changes¶
A change is breaking if either signal is present:
- the
!marker on the type —feat!:orfeat(api)!:; or - a
BREAKING CHANGE:footer (its hyphenated synonymBREAKING-CHANGE:is accepted too).
Breaking entries are surfaced through HasBreakingChanges() and BreakingChanges(), and
FormatSummary lists them first under a warning banner — a breaking change is the one thing
a reader must not miss.
Why releases are semver-ordered¶
Tags are validated and ordered with golang.org/x/mod/semver rather than by commit date or
tag-creation order. Semver ordering is the correct model for a changelog: v1.10.0 comes
after v1.9.0 even though "10" sorts before "9" lexically, and re-tagging or importing
history out of order does not scramble the result. A tag that is not valid semver is skipped
for ordering.
Commits newer than the most recent tag form an unreleased section at the top, so a changelog generated mid-cycle reflects work already merged but not yet tagged.
Generation vs parsing use the same model¶
Both directions produce or consume the same Changelog → Release → Entry structure.
Generation classifies commits into it; parsing reconstructs it from existing markdown. That
symmetry is why a changelog this package generated can be parsed back and queried with the
same category helpers — the categories are the stable interface, not the markdown text.