{"slug": "my-git-changelog-listed-a-feature-and-its-own-revert-in-the-same-release-commit", "title": "My git changelog listed a feature and its own revert in the same release — commit history isn't release contents", "summary": "A developer built a deterministic git changelog generator that diffs two commits and emits a structured changelog without any LLM involvement, after finding that LLM-written release notes invented features. Running it on a real repo (v1.3 to v1.4, 214 commits) showed 96 merge commits carried no information, and a later case listed both \"Added CSV export\" and \"Removed CSV export\" in the same release, prompting a fix that pairs and suppresses exact revert commits while flagging partial reverts as unstable.", "body_md": "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.\n\nThe 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.\n\nThen 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.\n\nThat'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.\n\nThe 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.\n\nSame discipline as my spec-diff tool: diff the states, treat the history as evidence. A witness, not the verdict.\n\nI 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)", "url": "https://wpnews.pro/news/my-git-changelog-listed-a-feature-and-its-own-revert-in-the-same-release-commit", "canonical_source": "https://dev.to/imapphelp/my-git-changelog-listed-a-feature-and-its-own-revert-in-the-same-release-commit-history-isnt-1ea0", "published_at": "2026-10-04 07:34:09+00:00", "updated_at": "2026-10-04 07:42:01.021910+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/my-git-changelog-listed-a-feature-and-its-own-revert-in-the-same-release-commit", "markdown": "https://wpnews.pro/news/my-git-changelog-listed-a-feature-and-its-own-revert-in-the-same-release-commit.md", "text": "https://wpnews.pro/news/my-git-changelog-listed-a-feature-and-its-own-revert-in-the-same-release-commit.txt", "jsonld": "https://wpnews.pro/news/my-git-changelog-listed-a-feature-and-its-own-revert-in-the-same-release-commit.jsonld"}}