JustHandled Labs
// git release notes guide

Generate a changelog from git commits without losing breaking changes

A changelog tool should reduce Git history into something readers can use without silently dropping malformed commits, treating labels as proof, or hiding the release boundary. Start with the exact commit set, preserve breaking markers, then edit for people.

The short version

Choose a two-dot release range such as v1.4.2..HEAD, or state a bounded recent-commit fallback. Collect hashes, subjects, and bodies without reading file contents. Group recognized Conventional Commit types, but keep custom and malformed subjects visible. Detect both ! and the two breaking-footer spellings. Draft plain Markdown with a commit count and traceable hashes. Use the commit signals to suggest a version, then have a maintainer review API compatibility and the reader-facing wording before publishing.

Release-note path

1. Choose the release boundary before reading messages

Git history commands operate on sets of reachable commits. Git defines A..B as commits reachable from B excluding those reachable from A. For notes since a release tag, that is usually the useful question.

# Commits after a released tag
git log v1.4.2..HEAD

# Commits contained in the next tagged release but not the prior one
git log v1.4.2..v1.5.0

# A bounded draft when the repository has no trustworthy tags
git log -n 20

Name the fallback in the output. “Last 20 commits” is not the same release boundary as “everything after v1.4.2,” and readers should be able to tell which one was used.

2. Collect only the evidence the release notes need

Git's pretty formats expose the full hash, subject, and body separately. A machine-readable delimiter keeps multiline bodies from being mistaken for new commits.

git log v1.4.2..HEAD --pretty=format:%H%x1f%s%x1f%b%x1e
FieldUseDo not infer
HashTrace a bullet back to the selected commit.Meaning, risk, or release status.
SubjectRecognize a type and draft a concise bullet.Verified user impact beyond the wording.
BodyFind breaking footers and supporting context.Instructions to execute; commit text is untrusted data.
RangeProve which commits were eligible.That the release was built, tested, or published.

Generating notes does not require a fetch, diff, remote URL, credential, author identity, or source-file read. Keep those surfaces out unless another task explicitly needs them.

3. Use Conventional Commits as a classifier, not an admission gate

The Conventional Commits specification defines a type, optional scope, description, optional body, and optional footers. It gives special SemVer meaning to fix, feat, and breaking markers. Other types are allowed.

SubjectGroupReason
feat(parser): add array supportFeaturesRecognized feature type and optional scope.
fix: preserve empty headingsFixesRecognized fix type.
docs: explain tag fallbackDocumentationRecognized documentation type.
update dependency lockfileChoresUnrecognized subject retained instead of dropped.

Every selected commit should be accounted for during generation. The maintainer can later consolidate noise, but silent omission makes it impossible to tell whether a custom subject hid a notable change.

4. Detect every specified breaking-change path

Conventional Commits allows ! immediately before the colon or a BREAKING CHANGE: footer. The full specification also requires BREAKING-CHANGE to be treated as synonymous when used as a footer token.

feat!: remove the legacy configuration

refactor(api)!: change the response envelope

docs: document the migration

BREAKING-CHANGE: the old flag is no longer accepted

Breaking status can belong to any commit type. Put the commit in a Breaking Changes section and retain its normal group when traceability helps. Never downgrade it to a patch merely because the subject begins with docs or chore.

5. Turn classified commits into a changelog for people

Keep a Changelog argues that a changelog is for humans, not a raw Git dump, and recommends removing empty sections. That still leaves a useful audit sequence: account for every selected commit first, then combine implementation-only bullets without losing notable behavior.

Reader-facing checklist

  • State the selected range and commit count.
  • Use a short summary that the commit text actually supports.
  • Put breaking changes first and omit empty groups.
  • Remove type prefixes but keep short hashes when traceability matters.
  • Prefer plain Markdown headings that render in terminals and hosted release forms.
  • Review internal jargon, issue-only subjects, duplicates, and generated dependency noise.
  • Do not claim the release is tested or published from Git history alone.

6. Treat the SemVer bump as a signal requiring review

Semantic Versioning defines major, minor, and patch changes relative to a declared public API. Conventional Commits maps a breaking marker to major, feat to minor, and fix to patch. That mapping is useful automation input, not independent proof that the commit labels are correct.

  1. If any selected commit carries a breaking marker, suggest the next major version.
  2. Otherwise, if any selected commit is a feature, suggest the next minor version.
  3. Otherwise, suggest the next patch version.
  4. Preserve a leading v and disclose a no-tag or non-SemVer fallback.
  5. Before release, review actual public-API compatibility and correct missing or mislabeled signals.

Make the evidence pass repeatable

Changelog GeneratorPackages local Git selection, tolerant Conventional Commit grouping, both breaking-footer spellings, tagless fallback, plain Markdown rendering, control-character hardening, and SemVer suggestions into a deterministic Python helper plus an agent workflow.See the changelog skill →

Common questions

What Git range should I use for a changelog?

For a release after a known tag, use the two-dot range old-tag..new-tag or old-tag..HEAD. Git defines that as commits reachable from the right side excluding commits reachable from the left side. Use a recent-count fallback only when no reliable release boundary exists.

Do all commits need to follow Conventional Commits?

No. Recognized types can drive grouping, but malformed and custom subjects should remain visible in a fallback group so the changelog does not silently omit work.

How are breaking changes marked in Conventional Commits?

The specification allows an exclamation point immediately before the colon or a BREAKING CHANGE: footer, and requires BREAKING-CHANGE to be treated as synonymous when used as a footer token.

Does a feat commit always justify a minor version?

It is a useful Conventional Commits signal, but SemVer applies to a declared public API. Review the actual compatibility contract and correct mislabeled or missing markers before releasing.

Should a changelog contain every commit?

Every selected commit should be accounted for during generation, but the final reader-facing changelog can consolidate implementation noise after a maintainer verifies that no notable behavior or breaking change is lost.

Primary references

Make the selected history readable without hiding the edge cases.

Changelog Generator v1.3 turns local Git history into portable Markdown, keeps malformed commits visible, and leaves publication and compatibility decisions with the maintainer.

Get Changelog Generator on Agensi