Guided Tutorials
Automate Release Notes from Git History
Generate well-organized release notes from your commit history, grouped by type and linked to PRs. Autohand reads your git log and produces a human-readable changelog in whatever format your team uses.
What you'll learn
- How to generate structured release notes from a git log between two tags
- How to set up conventional commits for better classification
- How to customize the output format for GitHub releases, emails, or JSON
- How to automate release note generation in a CI pipeline
Before you start
- Autohand Code installed. Run
autohand --versionto confirm. See Your First Autohand Session if you need to install it. - Git tags for previous releases. Run
git tag --listto check what tags exist. Adjust the prompt if your project uses a different tagging convention likerelease/2.3.0. - Commit messages with context. Clear messages like
feat: add dark mode toggleproduce much better output thanwipormisc changes. - PR numbers in commits (optional). GitHub and GitLab add PR numbers automatically when you use squash merge or merge commit. Autohand extracts and links them.
Set up conventional commits
Conventional commits are a lightweight standard that makes release notes generation much more reliable. If your project does not use them yet, it takes about five minutes to set up.
The format is simple: type(scope): description. Common types are feat, fix, docs, refactor, test, and chore.
bash
feat(auth): add OAuth2 login with Google
fix(api): correct pagination offset on user list endpoint
docs(readme): update installation instructions for Node.js 20
refactor(db): move connection pooling to shared module
chore(deps): bump typescript from 5.3.0 to 5.4.0Add a commit message linter to enforce the format.
bash
npm install --save-dev @commitlint/cli @commitlint/config-conventional huskyAsk Autohand to generate the configuration files.
bash
Set up commitlint with conventional commits config and add a husky commit-msg hook
that runs commitlint. Create all the config files needed.Run the release notes prompt
Navigate to your repository root and start Autohand. No special flags are needed.
bash
cd path/to/your-repo
autohandRun the prompt with your actual version numbers and tag names.
bash
Generate release notes for version 2.4.0 from the git log since the v2.3.0 tag.
Group changes by type: New Features, Bug Fixes, Performance Improvements, Internal Changes.
Include PR numbers as links to https://github.com/my-org/my-repo/pull/NUMBER.
Include contributor GitHub usernames next to each item.
Format the output as Markdown suitable for a GitHub release.Autohand runs the git log internally, parses the commits, classifies them by type, and writes the Markdown. The whole process takes under a minute for a typical release with 30-80 commits.
Review the output
Autohand produces a Markdown block you can copy directly into your GitHub release or CHANGELOG.md. Here is what a typical output looks like.
markdown
## What's Changed in v2.4.0
### New Features
- Add OAuth2 login with Google (#142) @sarah-dev
- Support dark mode toggle in user preferences (#138) @markj
- Add bulk export for CSV and JSON formats (#145) @sarah-dev
### Bug Fixes
- Fix pagination offset on user list endpoint (#151) @alex-ops
- Resolve login session timeout on mobile Safari (#149) @markj
- Correct email validation for international addresses (#144) @devbot
### Performance Improvements
- Move database connection pooling to shared module (#147) @alex-ops
- Add Redis caching for user profile queries (#139) @sarah-dev
### Internal Changes
- Bump TypeScript from 5.3.0 to 5.4.0 (#153) @devbot
- Update CI to Node.js 20 (#150) @alex-ops
**Full changelog:** https://github.com/my-org/my-repo/compare/v2.3.0...v2.4.0Read through the output and check that nothing important is missing. If a significant change was not captured, follow up in the same session.
bash
The PR #136 added webhook support which is a major feature.
It was committed as "chore: add webhook endpoint" so it was categorized wrong.
Move it to New Features and rewrite the description as "Add webhook support for real-time event delivery (#136) @markj".Customize the format
The default output is GitHub Markdown. You can ask for a different format in the same session.
Plain text for an email announcement.
bash
Reformat the release notes as plain text for an email announcement.
Use a friendly tone, no Markdown syntax, and keep it under 300 words.JSON for a programmatic pipeline.
bash
Output the same data as a JSON array where each item has:
type, description, pr_number, author, and breaking (boolean).Append to an existing CHANGELOG.md file using the Keep a Changelog format.
bash
Format the release notes following the keepachangelog.com format with
Added, Changed, Deprecated, Removed, Fixed, and Security sections.
I will insert it at the top of CHANGELOG.md under the [Unreleased] section.Automate in CI
You can run this as part of your release pipeline using Autohand's headless mode. Add a step to your GitHub Actions workflow that triggers when a version tag is pushed.
yaml
name: Release\n\non:\n push:\n tags:\n - 'v*'\n\njobs:\n release-notes:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0 # full history needed for git log\n\n - name: Get previous tag\n id: prev-tag\n run: |\n PREV=$(git tag --sort=-version:refname | grep -v "^${GITHUB_REF_NAME}$" | head -1)\n echo "tag=$PREV" >> $GITHUB_OUTPUT\n\n - name: Install Autohand\n run: curl -fsSL https://autohand.ai/install.sh | sh\n\n - name: Generate release notes\n env:\n AUTOHAND_PROVIDER: autohandai\n AUTOHAND_API_KEY: ${{ secrets.AUTOHAND_API_KEY }}\n AUTOHAND_AI_API_KEY: ${{ secrets.AUTOHAND_API_KEY }}\n run: |\n autohand --bare --restricted -p "Generate release notes for ${GITHUB_REF_NAME} from the git log since ${{ steps.prev-tag.outputs.tag }}. Format as GitHub release Markdown." > release-notes.md\n\n - name: Create GitHub release\n uses: softprops/action-gh-release@v2\n with:\n body_path: release-notes.mdThe runner has no stored Autohand sign-in, so the step runs Autohand with --bare and sets AUTOHAND_PROVIDER, AUTOHAND_API_KEY, and AUTOHAND_AI_API_KEY. Without --bare, the CLI waits for a browser sign-in and the job hangs. See Authenticate in CI and containers.
Tip: Use fetch-depth: 0 in the checkout step. Without it, GitHub Actions does a shallow clone and the git log since the previous tag will be empty.
Best practices for commit messages
The single highest-leverage thing you can do to improve release notes quality is write better commit messages. Here are the patterns that work best.
- Describe the change from the user's perspective. "Add bulk export" is better than "implement export controller method".
- Put breaking changes in the commit body. Add
BREAKING CHANGE:in the commit body for any change that breaks the public API. Autohand surfaces these prominently in the release notes. - Use imperative mood. "Fix login bug" not "Fixed login bug" or "Fixes login bug". This is the git convention and it reads naturally in a list.
- Reference issues and PRs. Including
(#123)at the end of a commit message gives Autohand and readers a direct link to the discussion context.
bash
# Good commit message\nfeat(api): add rate limiting to public endpoints (#167)\n\nAdds token bucket rate limiting at 100 requests per minute per API key.\nReturns 429 with a Retry-After header when the limit is exceeded.\n\nBREAKING CHANGE: The /v1/search endpoint now requires authentication.\nUnauthenticated requests will receive a 401 instead of being rate-limited.\n\n# Poor commit message\nfix stuff and add some rate limitingWhat you learned
- Generated structured release notes from a git log grouped by change type
- Set up conventional commits with commitlint and husky for consistent messages
- Customized the output format for GitHub releases, plain text emails, and JSON
- Automated release note generation in a GitHub Actions pipeline on tag push