I built a Git changelog API that diffs two commits and writes release notes. Worked great until a contributor deleted an entire module between v1.4 and v1.5 and my tool produced a changelog that simply... didn't mention it. No error, no flag, no nothing. Just a cheerful list of what was added and changed.
Here's the gotcha: git diff --name-status reports deletions with a D status, but most changelog generators (mine included, initially) only render A and M. I was filtering to "interesting" changes and silently dropping the single most breaking kind of change there is. A renamed function is a find-and-replace. A deleted endpoint is a 3am page.
The fix was boring and correct: treat D as a first-class citizen, and surface it with louder wording than additions. "Removed POST /v1/legacy-auth" reads very differently from "Added dark mode."
I also learned that commit messages lie. "Refactor auth" was the message on the commit that deleted the module. The diff is the only source of truth; the message is a caption someone wrote in a hurry. My generator now leads with what the diff actually shows and demotes the message to a footnote.
Comparison point: managed changelog tools (release-drafter, semantic-release, changesets) all solve this by keying off commit conventions — BREAKING CHANGE: footers, feat!: prefixes. That works when your team is disciplined. My API is for the undisciplined reality: real repos where people squash-merge, force-push, and write "fix stuff." Conventions are a hope; the diff is a fact.
If you're diffing git history yourself, the lesson is: never trust a changelog that doesn't show deletions. If it can't tell you what disappeared, it's not a changelog — it's a press release.
I ended up packaging it as https://x402.freeq.one/tools/changelog_git.html
Top comments (0)