# My git changelog listed a feature and its own revert in the same release — commit history isn't release contents

> Source: <https://dev.to/imapphelp/my-git-changelog-listed-a-feature-and-its-own-revert-in-the-same-release-commit-history-isnt-1ea0>
> Published: 2026-10-04 07:34:09+00:00

I built a git changelog generator because I was tired of LLM-written release notes inventing features. Mine is deterministic: it diffs two commits in a repo and emits a structured changelog. No model touches the history.

The first real repo I ran it on humbled me: v1.3 to v1.4, 214 commits. The output read like a phone book. I counted by hand: 96 of the 214 were merge commits — `Merge pull request #…`, `Merge branch 'release' into main`. In a squash-merge repo those carry zero information; the squashed commit already has the PR title and description. `git log --no-merges` went into the pipeline and the list dropped to 118. One caveat: an "evil merge" can introduce changes that exist in no parent, so I only suppress merges whose message matches a recognizable template and keep the oddballs visible.

Then a user did the real damage. Their v2.1 changelog listed "Added CSV export" and, further down, "Removed CSV export". Both commits were real, both in range. One early commit added the feature; a later commit reverted it after reviewers caught a data-loss bug. Net change across the release: nothing. My changelog had faithfully described the journey and completely lied about the destination.

That's when it clicked: a changelog is a statement about the artifact at the two endpoints, not about the road between them. Commit history is a lossy proxy for release contents.

The fix I shipped: after filtering merges, the generator looks for `Revert "<subject>"` commits (plus GitHub-style "This reverts commit " bodies) and pairs them against the original within the same range. Exact pairs get dropped and surface only as a suppressed-changes note. Partial reverts — revert-with-edits, or a feature reverted and later reworked — can't be paired safely, so they stay flagged as unstable during the window instead of getting a confident claim.

Same discipline as my spec-diff tool: diff the states, treat the history as evidence. A witness, not the verdict.

I ended up packaging the whole thing as a small API here: [https://x402.freeq.one/tools/changelog_git.html](https://x402.freeq.one/tools/changelog_git.html)
