Engineering

Git tags, releases, and your changelog

5 min read

A git tag, a release, and a changelog entry are three different records of the same event, and conflating them is how a changelog quietly drifts away from what actually shipped. A tag marks a commit. A release packages that tag with artifacts and a description. A changelog entry explains, in terms a reader outside the repository can use, what changed. They usually happen close together in time, which is exactly why it’s easy to treat them as one step instead of three, and why the gap only becomes visible months later when someone asks “what shipped in v2.4” and the honest answer takes real digging to produce.

What’s the actual difference between the three?

RecordLives inWritten for
Git tagThe repository, as a refAnyone checking out that exact commit
ReleaseThe code host (GitHub, GitLab)Anyone downloading a build
Changelog entryThe product’s own changelogAnyone using the product, not just the repo

A tag is the most mechanical of the three: git tag v2.4.0 and it’s done, with no requirement that anything explain what’s in it. A release adds a description and, usually, downloadable artifacts, and its audience is still developers who know what a release page is. A changelog entry is the only one of the three written for a reader who may never open the repository at all, which is why it’s the one that needs the most editorial attention and the one most likely to be skipped under deadline pressure.

Does every git tag need a changelog entry?

No, and treating them as one-to-one is a common mistake. A tag can mark an internal milestone, a release candidate, or a hotfix that never reaches most users; none of those necessarily need a public-facing entry. The test is the same one that decides whether anything belongs in a changelog at all: would a user or caller notice or care about this. Most tags pass that test. Some, like a tag cut purely to trigger a CI pipeline, never should.

Does every changelog entry need its own tag?

Not always, and this is where teams that deploy continuously diverge from teams that ship versioned packages. A SaaS product pushing several deploys a day can group multiple deploys under one dated changelog entry without a 1:1 tag for each; a library published to a package registry usually does need a tag per published version. Go modules and Swift Package Manager resolve versions from the tags themselves; on npm or PyPI the registry holds the published version, and the tag is how anyone maps that version back to its source. Semantic versioning and your changelog covers how the version number itself should map to changelog categories; tags are the mechanism that makes a version number checkable against the actual code. A repository with several independently-versioned packages needs this decided per package, not once for the whole repo; monorepo changelogs covers how tag prefixes and changelog scope should split along package boundaries rather than folder boundaries.

How should a release description relate to the changelog entry?

They can be the same text, but only if the audience for both is actually the same, which is rarer than it looks. A release page on a code host is read almost exclusively by developers; if a product also has non-technical users reading the changelog, duplicating the release description verbatim ships internal terms and code-first phrasing to a reader who needed the plain-language version. The cleanest pattern: write the changelog entry as the primary, reader-facing artifact, and let the release description either link to it or hold a shorter, more technical summary for the audience that’s already comfortable there.

# Release v2.4.0 (GitHub, developer-facing)
Bumps the reports pipeline to the new aggregation engine. See changelog
for the customer-facing summary: https://example.com/changelog#v2.4.0

## 2026-09-07 (Changelog, customer-facing)
### Added
- Reports now load in under a second, even for accounts with more
  than a million rows.

Same release, two documents, each earning its own wording for its own reader.

Where does the changelog entry actually come from?

Two starting points, and most real pipelines are a mix of both. It can be generated from commit messages at tag time, which is fast and never misses a merged pull request; from conventional commits to changelog covers that pipeline in full. Or it can be written by hand, separate from the tag entirely, timed to the moment a feature is considered done rather than the moment code merges. Generated entries are consistent but inherit every vague commit message; hand-written entries are clearer but need someone to actually write them. Most teams that automate still keep a light edit pass on the generated output before it becomes the public entry, which is the same discipline Keep a Changelog, implemented recommends regardless of where the raw text originated.

What breaks when the three get out of sync?

Trust in whichever one a reader checked first. A tag that exists with no matching changelog entry looks, from the changelog reader’s side, like nothing happened that week. A changelog entry with no corresponding tag or release makes it impossible for someone debugging a production issue to check out the exact code that was live when an entry was published. The fix isn’t perfect automation, it’s a single source of truth for the mapping: one place, even if it’s just the release process’s own checklist, that says a shippable change gets all three, in the same commit or pull request that introduces it.

FAQ

Should changelog entries be generated automatically from git tags? They can be a starting point, but a tag alone carries no reader-facing description, only a commit range. Automated generation needs to read the commit messages within that range, not just the tag’s existence, to produce anything a reader could use.

What if we don’t tag every release? Then the changelog entry becomes the primary record, and it should still carry a date and, if the product has one, a version number, so the entry remains something a reader can reference later even without a matching tag.

Should pre-release tags (like v2.4.0-rc.1) get changelog entries? Generally no. A release candidate is for internal or beta testing, and a changelog entry for it trains readers to expect entries for versions that may never ship as described. Reserve entries for tags that reach general availability.

Can a single changelog entry cover multiple git tags? Yes, and it often should for teams that tag frequently. Group related tags under one dated entry describing the net change, rather than publishing a thin entry per tag that fragments one feature across several reads.


The technical claims in this article have not been independently reviewed. If something here is wrong, tell us and we will correct it.

Related on changeloop: Changelog generator, Developer docs

changeloop
The team building a closed-loop changelog. Your users ask, your team ships, the person who asked gets told.