# GitHub releases and versions: tags, SemVer, changelogs

> What a git tag is, how to read a version number like v1.4.1 (SemVer in plain words), how GitHub releases and generated release notes work, how release notes differ from a changelog, how to check whether a fix is in a version, and why merged, released and live for every user can be three different moments.

- Course: [Git and GitHub for Product People](https://productovi.com/courses/git-for-product.md)
- Module: Team workflows
- Lesson 12/15, ~42 min
- Source: https://productovi.com/courses/git-for-product/releases-and-versions

## "Which version is the fix in?"

**Alex Tutor:** Back on CatChow's product team, support has a request about the checkout fix.

**Sofia:** Support wants to tell customers "fixed in version so-and-so", like app update notes. Which version was #134?

**Daniel:** Honest answer: none. Look at the `Tags` page. There are four old tags, `v1.0.0` to `v1.3.0`, which I set by hand for big launches. The last one sits on the cat treats bundle.

**Alex Tutor:** A **tag** is a fixed name for one commit. A branch moves forward with every new commit; a tag stays exactly where it was put. That's why versions are tags.

```text
Commit graph, oldest first: main and its only recent tag
1. commit 8be2f70 on main "Add cat treats bundle", tagged v1.3.0
2. commit 6f2c8a1 on main "Raise free-delivery threshold to 1000 UAH (#133)"
3. commit a7d93e4 on main "Rewrite delivery page copy (#132)"
4. commit d51c7e8 on main "Fix checkout button after cart edits (#134)" (highlighted)
5. commit 3b8e1f6 on main "Add autumn sale banner (#136)"
6. commit 9c4a2d7 on main "Add loyalty points behind a flag (#155)"
```

*v1.3.0 marks 8be2f70. Everything merged after it, including #134, is on the live site but not in any version.*

**Sofia:** So the tag is like a bookmark in the history, and `main` has moved five commits past it.

**Daniel:** Right. And tags can technically be deleted and recreated, but you never move a published one. People and tools rely on `v1.3.0` meaning the same thing forever.

![An orange ribbon bookmark is pinned firmly to one page of a notebook while a pencil keeps adding new pages at the far end.](https://productovi.com/courses/git-for-product/inline/releases-and-versions-illustration-2.webp)

*A tag stays on its commit; never move a published one.*

**Alex Tutor:** So from today the team tags a release every Thursday: the current `main`, which is already live, gets a version number and notes. Urgent fixes get their own version right away.

> **Exercise:** Support asks: "Was the checkout fix, #134, released in v1.3.0?" What is the correct answer on Thursday morning?

**Alex Tutor:** Daniel said you never move a published tag. Let's test that.

> **Exercise:** `v1.3.0` was published months ago on commit `8be2f70`. To make support's life easier, Daniel should move the `v1.3.0` tag to the newest commit of `main`, so that it includes the checkout fix #134.

## Reading a version number

**Sofia:** Why `v1.3.0` and not simply "version 13"?

**Alex Tutor:** It's **semantic versioning**, SemVer: MAJOR.MINOR.PATCH. Each number tells you what kind of change happened. PATCH goes up for fixes, MINOR for new features that break nothing, MAJOR for changes that break something people rely on.

**Alex Tutor:** When a number goes up, the ones to its right reset to zero. After `1.3.0`, a fix gives `1.3.1`, a new feature gives `1.4.0`, a breaking change gives `2.0.0`. More examples in our post on [semantic versioning](/blog/semantic-versioning-explained).

![In MAJOR.MINOR.PATCH, after 1.3.0 a fix gives 1.3.1, a new feature that breaks nothing gives 1.4.0, a breaking change gives 2.0.0.](https://productovi.com/courses/git-for-product/diagrams/m4-l3-semver.png)

**Daniel:** The rules at semver.org are written for software other programs depend on. For a website we decided ourselves: MAJOR if old links, saved carts or partner integrations stop working. We hope never to need it.

**Sofia:** And the `v` in front?

**Daniel:** Just a common habit for tag names. The version itself is `1.3.0`. Sometimes you'll also see `2.0.0-beta.1`: a pre-release, a test version that comes before the real `2.0.0`.

> **Exercise:** CatChow uses semantic versioning: PATCH for fixes, MINOR for new features that break nothing, MAJOR only when old links, saved carts or partner integrations stop working. The last release is `v1.4.0`. Match each next release with the version it should get.

**Sofia:** One trap I can already see: is `1.4.10` older than `1.4.9`? Alphabetically it would be.

**Alex Tutor:** Each part is a whole number, compared left to right. Ten is more than nine, so `1.4.10` is newer. And a pre-release is older than its release: `1.5.0-beta.1` comes before `1.5.0`.

**Daniel:** GitHub doesn't always sort tags that way on the tags page, by the way. Trust the numbers, not the list order.

**Sofia:** Numbers, not alphabet. Got it.

> **Exercise:** Put these versions in order from oldest to newest.

## Publishing a GitHub release: git tag v1.4.0

**Alex Tutor:** A **GitHub release** is a page built on a git tag: the version number, release notes written for people, and files to download. The tag marks the exact commit; the release explains what changed since the previous one. All releases live under `Releases` in the repository.

**Alex Tutor:** Thursday afternoon. Since `v1.3.0` there are two new features, the sale banner and the hidden loyalty groundwork, so it's a MINOR release: `v1.4.0`. Daniel opens `Releases` from the repository page.

**Daniel:** As of 2026: `Draft a new release`, then type `v1.4.0` as a new tag, target `main`. GitHub creates the tag when I publish. Previous tag `v1.3.0`, and then the magic button: `Generate release notes`.

> **What Generate release notes produced**
>
> ```markdown
> ## What's Changed
> * Raise free-delivery threshold to 1000 UAH by @catchow-daniel in https://github.com/catchow/website/pull/133
> * Rewrite delivery page copy by @catchow-emma in https://github.com/catchow/website/pull/132
> * Fix checkout button after cart edits by @catchow-daniel in https://github.com/catchow/website/pull/134
> * Add autumn sale banner by @catchow-emma in https://github.com/catchow/website/pull/136
> * Add loyalty points behind a flag by @catchow-daniel in https://github.com/catchow/website/pull/155
>
> **Full Changelog**: https://github.com/catchow/website/compare/v1.3.0...v1.4.0
> ```

**Sofia:** Every merged pull request since `v1.3.0`, with its title and author. So good titles pay off twice.

**Alex Tutor:** Below the text there's a place to attach files, the **assets**. GitHub always adds the source code as a zip and a tar.gz; app teams attach the built app. We don't need more.

**Daniel:** Then two checkboxes. `Set as a pre-release` for test versions; not today. `Set as the latest release`: yes. `Publish release`, and the repository page shows `v1.4.0` with a green `Latest` badge.

```text
The two newest tags in catchow/website, Thursday evening
- [Tag] v1.4.0: Latest · on 9c4a2d7 · release notes by Daniel and Sofia
- [Tag] v1.3.0: on 8be2f70 · no release page, tag only
```

> **Exercise:** On Thursday Daniel published CatChow's release `v1.4.0`: a new tag on `9c4a2d7`, the newest commit of `main`, with notes generated since the previous tag `v1.3.0` (on `8be2f70`), marked `Latest`. CatChow deploys every merge into `main` within minutes. Which statements are true? Select all that apply.

## Release notes vs changelog: notes for humans

**Sofia:** Support won't understand "Add loyalty points behind a flag". And they must not tell customers about loyalty points yet.

**Alex Tutor:** That's the difference between a list of pull requests and a **changelog**: a record of what changed in each version, written for the people who use it. Good release notes are that record for one version. The generated list is raw material, not the finished text.

![A developer hands a long technical scroll to a colleague, who turns it into a short card with simple pictures for a smiling support agent.](https://productovi.com/courses/git-for-product/inline/releases-and-versions-illustration-3.webp)

**Daniel:** Some teams keep a `CHANGELOG.md` file with sections like Added, Changed, Fixed. We'll use the release pages as our changelog. I'll also add a small config so the generator groups pull requests by label: `bug` under "Fixes".

**Alex Tutor:** So you add a short "For customers and support" part on top, in plain words, and keep the generated list below for engineers.

**Sofia:** What customers notice, what support should say, and nothing about hidden work.

> **Exercise:** GitHub generated this list for `v1.4.0`:
>
> ```markdown
> ## What's Changed
> * Raise free-delivery threshold to 1000 UAH by @catchow-daniel in #133
> * Rewrite delivery page copy by @catchow-emma in #132
> * Fix checkout button after cart edits by @catchow-daniel in #134
> * Add autumn sale banner by @catchow-emma in #136
> * Add loyalty points behind a flag by @catchow-daniel in #155
> ```
>
> The free-delivery threshold used to be 800 UAH. #155 is hidden behind the flag `loyalty-points`, which is off. Write the “For customers and support” part of the notes (3–5 short lines). Loyalty points must not be announced yet.

**Alex Tutor:** One quick check on the tone of those lines.

> **Exercise:** Which line belongs in the “For customers and support” part of the `v1.4.0` notes?

## Merged, released, live for everyone

**Sofia:** Wait. #134 went live last Wednesday, and it was "released" today. So at CatChow the release comes after customers already have the change?

**Alex Tutor:** Yes. For us, merged means live within minutes, and a release is a named snapshot for people to talk about. Three words, but at CatChow two of them usually happen at once.

![A cat-food parcel moves through three doors in a row: a workshop door, a door tied with a ribbon, and an open shop door with customers inside.](https://productovi.com/courses/git-for-product/inline/releases-and-versions-illustration-4.webp)

*Before answering whether it's live, ask: merged, released, or visible?*

**Daniel:** At my old job all three were far apart. Merged into `develop`, released weeks later, then store review, and even after approval the stores can roll a version out gradually to a growing share of users.

**Alex Tutor:** And flags split "released" from "visible" even here. #155 is in `v1.4.0`, and no customer can see it.

**Sofia:** So when someone asks "is it live?", I first ask myself: merged, released, or visible to customers?

> **Exercise:** Match each situation with what customers actually get.

## Friday morning: a hotfix

**Sofia:** Support just forwarded two messages. Customers see a new "Loyalty points" link in their account menu, and it opens an empty page.

**Daniel:** My fault. In #155 the loyalty section checks the flag, the menu link doesn't. It's been visible since Tuesday. The fix is two lines.

**Alex Tutor:** That's a **hotfix**: an urgent fix to what customers already have. In GitHub flow it isn't a special branch type, just a small pull request into `main` that jumps the queue.

**Daniel:** Nothing else has been merged since `v1.4.0`, so after the fix `main` is `v1.4.0` plus one fix. If a feature had slipped in, it would honestly be `v1.5.0`.

**Alex Tutor:** Plan it before Daniel starts: the path to the site, the version, the notes, and what support says to the two customers.

> **Exercise:** Plan the hotfix for the loyalty menu link and write its release notes.

**Daniel:** Done by 11:30. #158 "Hide loyalty menu link when flag is off", Oscar approved, you and Oscar both checked the preview, squash-merged as `e5f81a3`, deployed. `v1.4.1` is published and now carries `Latest`.

## Is #134 in v1.4.1?

**Sofia:** Support's next question, I bet: is the checkout fix in `v1.4.1` too? It isn't in its notes.

```text
Commit graph, oldest first: main with two new tags
1. commit 8be2f70 on main "Add cat treats bundle", tagged v1.3.0
2. commit 6f2c8a1 on main "Raise free-delivery threshold to 1000 UAH (#133)"
3. commit a7d93e4 on main "Rewrite delivery page copy (#132)"
4. commit d51c7e8 on main "Fix checkout button after cart edits (#134)" (highlighted)
5. commit 3b8e1f6 on main "Add autumn sale banner (#136)"
6. commit 9c4a2d7 on main "Add loyalty points behind a flag (#155)", tagged v1.4.0
7. branch fix/loyalty-menu-link starts from commit 9c4a2d7
8. commit 0d7b3c9 on fix/loyalty-menu-link "Check flag before showing menu link"
9. commit e5f81a3: fix/loyalty-menu-link squash-merged into main "Hide loyalty menu link when flag is off (#158)", tagged v1.4.1
```

*Each tag marks one commit. v1.4.1 sits later on the same line, so it contains everything in v1.4.0 plus e5f81a3.*

**Alex Tutor:** Release notes list only what's new since the previous version. But a version contains everything before it on the same line. `v1.4.1` is later than `v1.4.0`, and `v1.4.0` contains `d51c7e8`.

**Daniel:** Two ways to prove it on GitHub. The compare view, `v1.4.0...v1.4.1`, shows exactly what was added between the tags: one commit. And as of 2026 a commit's page lists the tags that contain it.

**Sofia:** So on `d51c7e8` I'd see both `v1.4.0` and `v1.4.1`, but not `v1.3.0`.

**Alex Tutor:** Exactly. That's the answer support can forward: "fixed in 1.4.0, and in every version since".

> **Exercise:** Daniel opens the compare view `v1.4.0...v1.4.1`. Which pull requests does it show?

**Sofia:** And if I only had the release pages?

**Alex Tutor:** Then read them in order. Find the first version whose notes mention the fix, and remember that each later version keeps it, unless a later note says it was reverted.

**Daniel:** That "unless" matters. Remember my March revert: a later version can take something back out.

**Sofia:** So first appearance plus no revert since. Good.

> **Exercise:** Support asks: "Is the checkout fix #134 in `v1.4.1`?" Which ways of checking give a reliable answer? Select all that apply.

**Alex Tutor:** That's module 4: GitHub flow at home, git flow when releases are events, and versions that make "is it live, and in which version?" a question with a one-line answer.

**Sofia:** And now the loyalty points need a home where the whole team can see what's left before the launch.

**Daniel:** Issues, labels, a milestone and a project board. That's next module, and you'll make your first change yourself.

- Previous lesson: [Git flow: develop, release and hotfix branches](https://productovi.com/courses/git-for-product/git-flow.md)
- Next lesson: [GitHub issues: bug reports, templates, labels and milestones](https://productovi.com/courses/git-for-product/issues-labels-milestones.md)

---
Productovi Learning. Free, no sign-up needed to read.
