LogPulse / guides

How to generate a changelog from git commits

Your git history already knows what changed between two tags. A few `git log` commands turn it into a first draft in seconds, and dedicated tools can do it on every release. The draft still needs a human pass: commits are written for reviewers, a changelog for the people upgrading.

Updated Last checked 5 min readOpen-source libraries and packages

The short answer

  • git describe --tags --abbrev=0 prints the most recent tag reachable from your current commit (--tags includes lightweight tags).
  • git log v1.3.0..HEAD --no-merges --pretty=format:'- %s (%h)' lists every commit since that tag as a Markdown bullet.
  • git shortlog -sn --no-merges v1.3.0..HEAD counts commits per author, for the thank-you line.
  • gh release create v1.4.0 --generate-notes drafts notes from merged pull requests instead of commits.
  • Git has no changelog command of its own. Whatever produces the list, sort it into Added, Changed, Fixed…, drop internal commits and add migration steps.

A first draft in four commands

Run these in a clone with full history and tags (git fetch --tags); CI checkouts are often shallow and miss them.

terminal
# 1. The last release tag reachable from HEAD
PREV=$(git describe --tags --abbrev=0)

# 2. Every non-merge commit since then, as Markdown bullets
git log "$PREV"..HEAD --no-merges --pretty=format:'- %s (%h)'

# 3. Only features and fixes, if you use Conventional Commits
git log "$PREV"..HEAD --no-merges --grep='^feat' --grep='^fix' --pretty=format:'- %s (%h)'

# 4. Who contributed, by number of commits
git shortlog -sn --no-merges "$PREV"..HEAD

With several --grep patterns, git keeps commits matching any of them. Patterns match any line of the message, not only the first.

Free tool · no sign-up

Can a coding agent read your changelog?

Paste your repository, a GitHub release or a CHANGELOG.md link. The free converter shows every version, breaking change, migration step and deprecation it can extract, as JSON plus an llms.txt summary.

No AI and nothing stored. Or paste a CHANGELOG.md instead

01Find the range: from the last tag to now

A changelog entry covers the commits between the previous release and this one. In git, A..B means commits reachable from B but not from A, so v1.3.0..HEAD is everything since v1.3.0, and v1.3.0..v1.4.0 is exactly what went into 1.4.0.

To find the previous tag, git describe --tags --abbrev=0 “finds the most recent tag that is reachable from a commit” (HEAD by default). Without --tags it only considers annotated tags, and --abbrev=0 drops the commit-count suffix so you get just the tag name. It's the nearest tag in your history, not the highest version number, so if you release from more than one branch, pass the tag explicitly.

Tag the release so the next range has a start
git tag -a v1.4.0 -m "v1.4.0"
git push origin v1.4.0

02Format commits as Markdown

--pretty=format: takes placeholders, so you can print each commit as a changelog line. --no-merges skips merge commits; --oneline is a quick shorthand when you just want to look.

PlaceholderPrints
%sThe subject (first line of the message)
%hThe abbreviated commit hash
%anThe author's name
%asThe author date as YYYY-MM-DD
%adThe author date, in the format set by --date=
Illustrative output
- feat(parser): add parseStream() for large inputs (3f2c1ab)
- fix: handle Windows (CRLF) line endings (9d04e7c)
- refactor(parser): split the tokenizer (b81a6f0)
- chore(deps): bump test runner (2c7d9e4)

If you merge pull requests with merge commits, --first-parent follows only the first parent at each merge, so you see one merge commit per pull request on your main branch instead of every commit inside it. If you squash-merge, each pull request is already one commit, and its title is the subject.

03Credit contributors with git shortlog

git shortlog groups commits by author. -s prints only the commit count per person, -n sorts by that count, and --no-merges leaves merges out. Use it for a “Thanks to…” line in the release notes; the commit history already records who did what, so keep the credit brief.

04Let GitHub draft notes from pull requests

Pull request titles are usually better raw material than individual commits. GitHub's generated release notes “include a list of merged pull requests, a list of contributors to the release, and a link to a full changelog”, grouped by the labels you set in .github/release.yml; Keep a Changelog vs GitHub Releases has a ready-made config.

  • gh release create v1.4.0 --generate-notes creates the release with generated notes; --notes-start-tag v1.3.0 sets where they start.
  • The REST endpoint POST /repos/{owner}/{repo}/releases/generate-notes returns the text without creating anything: “The generated release notes are not saved anywhere.” Useful for pasting into CHANGELOG.md.

05Automate it with a changelog generator

If your commits follow Conventional Commits, a generator can sort them for you on every release. git-cliff writes a CHANGELOG.md from your history (git cliff -o CHANGELOG.md, or git cliff --unreleased for what's new since the last tag), and semantic-release, release-please and Changesets go further, choosing the version and creating the release. semantic-release vs release-please vs Changesets compares them. If you only want each GitHub release filled in, the free LogPulse GitHub Action sorts the commits since the last tag on every release, and uses your CHANGELOG.md section instead once you keep one. It's one workflow step, uses: kalmwerk/release-notes-action@v1 (on GitHub).

06Turn the draft into a changelog

Keep a Changelog is blunt about raw logs: “Do not use a list of commits as a changelog. It is full of noise: merge commits, unclear messages, internal changes.” Before you publish the draft:

  1. Sort each line into Added, Changed, Deprecated, Removed, Fixed or Security, under a ## [1.4.0] - YYYY-MM-DD heading. The CHANGELOG.md format guide has the full layout.
  2. Delete internal changes: refactors, test and CI changes, dependency bumps nobody notices.
  3. Merge the five commits that built one feature into one line, and rewrite it around what users see.
  4. Mark breaking changes and add a migration step to each; see how to document breaking changes.
  5. Add the compare link for the new version at the bottom of the file.

Before and after

Too vague

- feat(parser): add parseStream() (3f2c1ab) - refactor(parser): split the tokenizer (b81a6f0) - chore(deps): bump test runner (2c7d9e4)

Specific and actionable

## [1.4.0] - 2026-10-02 · Added: parseStream() parses large files without loading them into memory. Fixed: files with Windows (CRLF) line endings now parse.

Fictional example: use your own verified names, versions and behavior.

LogPulse

Generate release notes with LogPulse

LogPulse is a hosted changelog that people and coding agents can both read. Here's exactly what it can do for the steps above, and what it can't.

  1. Check your changelog, free

    Paste a CHANGELOG.md or a GitHub releases link. A deterministic parser (no AI, nothing stored) shows the versions, breaking changes, migration steps and deprecations an agent can extract.

    Open the converter

  2. Draft notes from GitHub

    Connect a repository: merged pull requests, commits to your default branch and published GitHub releases collect in an inbox. AI drafts a note in the tone you pick, listing breaking changes and deprecations only when the changes clearly show them. Nothing is published until you approve it. 3 AI drafts on Free; unlimited on Open source and Pro. Pro opens soon: .

    Free for open source

  3. Publish once, read everywhere

    Approved notes go to a hosted changelog and an in-app widget, to email subscribers on Open source and Pro (Pro opens soon), and to a JSON feed and MCP server coding agents can query. CI and agents can publish with an API key.

    Read the docs

What it doesn't do: LogPulse doesn't tag releases, bump versions, publish to npm or PyPI, or write CHANGELOG.md back to your repository; keep your release tooling for those. AI drafts can miss context, so review them like a pull request.

Before you publish

  • The range starts at the previous release tag and ends at the new one
  • Merge commits, refactors, test and CI changes are gone
  • Lines are grouped by change type under a versioned, dated heading
  • Related commits are merged into one user-facing line
  • Breaking changes are marked and have migration steps
  • Contributors are credited

Common questions

Can git generate a changelog automatically?

Git has no built-in changelog command. git log with a revision range and --pretty=format: gives you the raw list, and tools such as git-cliff, semantic-release or release-please turn structured commits into a formatted changelog.

How do I list the commits between two tags?

git log v1.3.0..v1.4.0 --no-merges --oneline shows commits reachable from v1.4.0 but not from v1.3.0, which is what went into the 1.4.0 release.

Should merge commits be in the changelog?

No. Use --no-merges to skip them, or --first-parent to see one merge per pull request and read the pull request titles instead. Either way the changelog line should describe the change, not the merge.

What if my commit messages are messy?

Draft from pull request titles instead (GitHub's generated notes, or squash merges that use the title), or ask contributors for a one-line summary in a change file, as Changesets does. Then edit.

Sources

Facts last checked against these sources on .

More guides: open-source libraries and packages

All release notes guides

Tools and resources

LogPulse

Changelogs coding agents can upgrade from.

Publish versions, breaking changes, migration steps and deprecations once. People get a widget and hosted changelog; agents get a JSON feed and MCP server. Free for open source.