wcygan

changelog

Generate a changelog or release notes from git history between two refs (tags, commits, or branches). Use when preparing a release, summarizing changes, or creating release notes. Keywords: changelog, release notes, release, changes, what changed, version, tag, diff, history, summary

wcygan 194 16 Updated 7mo ago
GitHub

Install

npx skillscat add wcygan/dotfiles/changelog

Install via the SkillsCat registry.

About this skill

Generates structured changelogs and release notes from git history between two references such as tags, commits, or branches. It categorizes commits by type and reads diffs to produce a conventional changelog. Use it when preparing a release, summarizing changes since a prior version, or creating a CHANGELOG.md entry.

SKILL.md

Changelog Generator

Generate structured release notes from git history between two refs. Categorizes commits by type, reads diffs for significant changes, and outputs a conventional changelog.

When to Use

  • Preparing a new release and need release notes
  • Summarizing what changed between two tags or commits
  • Creating a CHANGELOG.md entry for a version bump
  • Reviewing what shipped since the last deploy

Workflow

1. Determine the Ref Range

Parse $ARGUMENTS for from-ref and to-ref. Handle these cases:

Two args provided (e.g., /changelog v1.2.0 v1.3.0):

FROM_REF="$1"  # e.g., v1.2.0
TO_REF="$2"    # e.g., v1.3.0

One arg provided (e.g., /changelog v1.2.0):

FROM_REF="$1"  # e.g., v1.2.0
TO_REF="HEAD"

No args provided (e.g., /changelog):

# Auto-detect: latest tag to HEAD
FROM_REF=$(git describe --tags --abbrev=0 2>/dev/null)
TO_REF="HEAD"

# If no tags exist, use first commit
if [ -z "$FROM_REF" ]; then
  FROM_REF=$(git rev-list --max-parents=0 HEAD)
fi

Validate the refs exist:

git rev-parse --verify "$FROM_REF" >/dev/null 2>&1 || echo "ERROR: $FROM_REF is not a valid ref"
git rev-parse --verify "$TO_REF" >/dev/null 2>&1 || echo "ERROR: $TO_REF is not a valid ref"

2. Gather Commit History

# Full commit list with hashes
git log --oneline "$FROM_REF".."$TO_REF"

# With author and date for context
git log --format="%h %s (%an, %ad)" --date=short "$FROM_REF".."$TO_REF"

# Stats summary
git diff --stat "$FROM_REF".."$TO_REF"

# Number of contributors
git log --format="%an" "$FROM_REF".."$TO_REF" | sort -u

3. Categorize Commits

Parse each commit message and categorize using conventional commit prefixes:

Category Prefixes Priority
Breaking Changes BREAKING CHANGE, breaking:, !: 1 (always first)
Features feat:, add:, feature: 2
Bug Fixes fix:, bugfix:, bug: 3
Performance perf:, performance: 4
Documentation docs:, doc: 5
Refactoring refactor:, refact: 6
Tests test:, tests: 7
Build/CI ci:, build:, chore: 8
Other Anything that doesn't match 9

For commits without conventional prefixes, read the commit message body and diff to infer the category:

# Read full commit message and diff for ambiguous commits
git show --stat <hash>
git show <hash> -- '*.rs' '*.ts' '*.py' '*.go'  # filter to source files

4. Enrich Significant Changes

For commits tagged as features or breaking changes, read the actual diff to write a better summary than the commit message alone:

# Read the diff for a significant commit
git show <hash> --stat
git diff <hash>~1..<hash> -- '*.rs' '*.ts' '*.py' '*.go' '*.js' '*.java'

Write a one-line human-readable summary that describes what changed for the user, not implementation details.

Good: "Add bulk export of user data as CSV"
Bad: "Add exportUsers function to UserService"

5. Detect Version and Date

# If TO_REF is a tag, extract version from it
VERSION=$(echo "$TO_REF" | sed 's/^v//')

# If TO_REF is HEAD, mark as unreleased
if [ "$TO_REF" = "HEAD" ]; then
  VERSION="Unreleased"
fi

# Get the date of the TO_REF
DATE=$(git log -1 --format="%ad" --date=short "$TO_REF")

6. Generate the Changelog

Output using the conventional changelog format:

## [VERSION] - YYYY-MM-DD

### Breaking Changes
- Description of breaking change ([`abcdef1`](commit-url))
  - **Migration**: How to update existing code/config

### Features
- Description of feature ([`abcdef2`](commit-url))
- Description of feature ([`abcdef3`](commit-url))

### Bug Fixes
- Description of fix ([`abcdef4`](commit-url))

### Performance
- Description of improvement ([`abcdef5`](commit-url))

### Documentation
- Description of docs change ([`abcdef6`](commit-url))

### Internal
- Refactoring, tests, CI changes ([`abcdef7`](commit-url))

---

**Full diff**: [`FROM_REF...TO_REF`](compare-url)
**Contributors**: @author1, @author2, @author3

Rules for the output:

  • Omit empty categories (don't show "### Performance" if there are none)
  • Breaking changes always come first with migration notes
  • Each entry is one line with the short commit hash linked
  • Group related commits if they're part of the same logical change
  • Include a "Full diff" link at the bottom using GitHub compare URL format
  • List contributors

7. Construct URLs (if GitHub remote exists)

# Detect GitHub remote
REMOTE_URL=$(git remote get-url origin 2>/dev/null | sed 's/\.git$//' | sed 's|git@github.com:|https://github.com/|')

# Commit URL pattern
# ${REMOTE_URL}/commit/${HASH}

# Compare URL
# ${REMOTE_URL}/compare/${FROM_REF}...${TO_REF}

If no GitHub remote, use plain hashes without links.

8. Output

Print the changelog to stdout. The user can then:

  • Copy it into CHANGELOG.md
  • Use it as GitHub release notes
  • Include it in a PR description

Handling Edge Cases

Merge commits: Skip merge commits unless they represent meaningful squash merges:

git log --no-merges --oneline "$FROM_REF".."$TO_REF"

Monorepo / scoped changes: If the user specifies a path, scope the log:

git log --oneline "$FROM_REF".."$TO_REF" -- path/to/package/

No conventional commits: If the repo doesn't use conventional commits, categorize by reading diffs. Group by area of the codebase instead of by type.

Very large ranges (100+ commits): Summarize minor changes (docs, chore, refactor) into a single line each. Focus detail on features and breaking changes.

Example Invocations

Between two tags:

/changelog v2.0.0 v2.1.0

Since last tag to now:

/changelog

Since a specific commit:

/changelog abc1234 HEAD

Scoped to a path (mentioned in arguments):

/changelog v1.0.0 v2.0.0 -- packages/api

Example Output

## [2.1.0] - 2025-03-15

### Features
- Add bulk user export as CSV download ([`a1b2c3d`](https://github.com/org/repo/commit/a1b2c3d))
- Support dark mode in settings panel ([`e4f5g6h`](https://github.com/org/repo/commit/e4f5g6h))

### Bug Fixes
- Fix race condition in webhook delivery causing duplicate events ([`i7j8k9l`](https://github.com/org/repo/commit/i7j8k9l))
- Correct timezone handling for scheduled reports ([`m0n1o2p`](https://github.com/org/repo/commit/m0n1o2p))

### Performance
- Cache user permissions lookup, reducing p99 latency by 40% ([`q3r4s5t`](https://github.com/org/repo/commit/q3r4s5t))

### Internal
- Migrate CI from CircleCI to GitHub Actions ([`u6v7w8x`](https://github.com/org/repo/commit/u6v7w8x))
- Update dependencies ([`y9z0a1b`](https://github.com/org/repo/commit/y9z0a1b))

---

**Full diff**: [`v2.0.0...v2.1.0`](https://github.com/org/repo/compare/v2.0.0...v2.1.0)
**Contributors**: @alice, @bob, @charlie