changelog-generator
Changelog Generator
Transform git commits into polished, human-friendly changelogs. Based on Keep a Changelog principles.
Core Principle
Changelogs are for humans, not machines.
Don't dump git logs into changelogs. Commits document the evolution of source code. Changelog entries document noteworthy differences to communicate them clearly to end users.
When to Use
- Preparing release notes for a new version
- Updating the changelog after completing work on a branch
- Creating weekly/monthly product updates
- Documenting changes for customers
Workflow
Step 0: Find the Changelog
Locate the changelog file — the user may just say "update the changelog" without specifying a path:
ls CHANGELOG.md changelog.md docs/CHANGELOG.md 2>/dev/null
If no changelog exists, create CHANGELOG.md at the repo root with the standard header.
Step 1: Determine the Version (Branch-Aware)
CRITICAL: NEVER use [Unreleased]. Every changelog entry MUST have a version number.
CRITICAL: NEVER edit package.json version manually. Only use bun scripts/version.ts patch when a bump is truly needed.
# Current version vs main's version
cat package.json | grep '"version"'
git show origin/main:package.json | grep '"version"'
# Existing changelog entries
grep -E "^## \[" CHANGELOG.md | head -5
# Today's date
date +%Y-%m-%d
Decision flow:
Is package.json version HIGHER than main's version?
YES → This branch already bumped. Use the current version.
Update the existing changelog entry if one exists.
DO NOT bump again — all work on this branch is ONE release.
NO → This is the first changelog on this branch.
Run `bun scripts/version.ts patch` to bump, then use the new version.
The key insight: one MR = one version. If the branch already bumped from 0.2.3 to 0.2.4, every subsequent changelog update on that branch stays at 0.2.4.
Step 2: Get the Right Commits
The commits for this version are everything on this branch that main doesn't have:
# Ensure origin/main is fresh
git fetch origin main --quiet
# Find the merge-base (where this branch diverged from main)
MERGE_BASE=$(git merge-base origin/main HEAD)
# All commits in this branch (excluding merges)
git log --oneline $MERGE_BASE..HEAD --no-merges
If documenting a version already merged to main, use merge boundaries instead:
git log --oneline --merges -5
git log --oneline <older-merge>..<newer-merge> --no-merges
Step 3: Check for Missing Versions
Before adding or updating an entry, verify recent releases are documented:
git log --oneline --merges -7 origin/main
grep -E "^## \[" CHANGELOG.md | head -10
If merges exist without corresponding changelog entries, document those first.
Step 4: Analyze Each Commit
For each commit, read the actual code diff — commit messages lie. Determine what the user will notice, not what the developer changed.
Filter out: chore:, docs: (unless user-facing), test:, refactor: (unless UX change).
Step 5: Categorize
Group findings into: Added (new features), Changed (existing behavior modified), Fixed (bug fixes), Removed (deleted features), Security (vulnerability patches).
Step 6: Write Entries
Format: - **Bold Title**: Description in plain language
# Good — describes what users notice:
- **Timezone Display**: Scheduled posts now show correct local time
- **Bulk Import**: Import up to 10,000 vehicles from CSV in one operation
# Bad — exposes implementation details:
- Fixed timezone handling in TimezoneBridge component
- Added useBulkImport hook with chunked processing
See references/writing-entries.md for more transformation examples and category definitions.
Step 7: Write or Update the Entry
Always update the date to today (YYYY-MM-DD) when modifying a changelog entry.
If the version already has a changelog entry, update it in place — merge new items into existing categories. Don't create a duplicate section.
If it's a new version, insert above the previous version:
## [X.Y.Z] - CodeName (YYYY-MM-DD)
### Added
- **Feature Name**: Brief description of what it does
### Fixed
- **Fix Name**: What was broken, now works
Each version gets a unique Digimon codename — see references/writing-entries.md for the full list.
Checklist
- Changelog file found — located CHANGELOG.md (or created if missing)
- Version is correct — compared package.json against main, not bumped twice on same branch
- Date is today — entry header uses today's date, not a stale date
- No
[Unreleased]— always a real version number - Existing entry updated if version already had one (no duplicate sections)
- Only branch commits included (diff against main, not arbitrary ranges)
- Unique Digimon codename (check existing entries)
- All
feat:andfix:commits included - Each entry has bold title followed by description
- Entries describe user impact, not implementation details
- No code/component/file names in entries
- Grouped by category (Added/Changed/Fixed/etc.)
Anti-Patterns
Don't: Bump the version when the branch already bumped — one MR = one version
Don't: Use [Unreleased]
Don't: Copy commit messages verbatim
Don't: Include implementation details (file names, function names, hooks)
Don't: Write paragraphs — keep entries to one line
Don't: Skip reading the actual code changes
Don't: Create a new version section when one already exists for this version
Do: Compare package.json version against main before deciding to bump
Do: Update the existing entry when adding more changes to the same version
Do: Use format - **Title**: Description
Do: Describe what users will notice
Do: Group related commits into single entries
References
| File | Content |
|---|---|
references/writing-entries.md |
Commit analysis, category definitions, transformation examples, Digimon codenames |