A release page can say “Published today” while its v2.4.0 tag points to a commit tested last Friday; both timestamps can be correct. Treating those dates as contradictory is how teams end up rebuilding the wrong commit, backdating changelogs, or telling customers a fix shipped before it was actually available.
The useful model is simple: a Git tag is a reference to a specific point in repository history, while a GitHub release is a publication record built around a tag. The tag answers, “Which exact source revision is version v2.4.0?” The release answers, “What should a human download, read, and consider available now?”
1. Start by separating the three dates people call “the release date”
Most date confusion comes from using one label for three different events. A commit has its own author and committer timestamps. An annotated tag has a tagger timestamp. A GitHub release has a publication timestamp. Those events may happen minutes apart in a small project or days apart in a team that stages release notes and binary assets.
| Date | What it records | Example meaning | What it should not prove |
|---|---|---|---|
| Commit date | When Git recorded the source change | The v2.4.0 source commit was created on Friday | That a version was released to users on Friday |
| Annotated tag date | When the tag object was created | Release engineering approved and tagged that commit on Monday | That GitHub assets or release notes were public on Monday |
| GitHub release publication date | When the GitHub release was published | Users could see the release page and assets on Tuesday | When the underlying code was written or tested |
GitHub’s own documentation explicitly notes that a tag date can differ from a release date because tags and releases can be created at different times. That is expected behavior, not a GitHub data-quality problem.
For incident reports, compliance records, and customer announcements, use the publication date when you mean availability. For reproducible builds and source audits, use the tag name and resolved commit SHA. Do not use either date as a substitute for the other.
2. A Git tag is a repository reference, not a product announcement
A tag is a named pointer to a Git object, usually a commit. If v2.4.0 resolves to commit abc1234, anyone can check out the same source tree with git checkout v2.4.0 or inspect its target with git rev-list -n 1 v2.4.0.
Tags are valuable because a branch moves while a version identifier should not. The main branch may gain 40 commits after you ship v2.4.0. The tag preserves the exact revision that represents that version.
For software releases, prefer an annotated tag rather than a lightweight tag. A lightweight tag is essentially a name pointing directly at a commit. An annotated tag is a separate Git object that can carry a tagger identity, timestamp, message, and optionally a cryptographic signature.
git tag -a v2.4.0 abc1234 -m "Release v2.4.0"
git push origin v2.4.0
The distinction matters during an audit. With an annotated tag, git show v2.4.0 displays the tag metadata and the commit it targets. With a lightweight tag, there is no tag message or tagger date to inspect. A lightweight tag is fine for a temporary local marker such as before-refactor; it is poor release evidence.
3. A GitHub release is the distribution layer built on top of a tag
GitHub releases are based on Git tags, but they add product-facing material that Git itself does not model: a title, release notes, pre-release status, a latest-release designation, and downloadable assets. GitHub also provides automatically generated source archives for the tagged source.
This is why a repository can have hundreds of tags and few or no releases. The Git project repository is a visible example of a project with tags but no GitHub releases. That does not make its tags invalid; it means the project does not use GitHub Releases as its packaging channel.
Use a tag alone when the audience is primarily developers who clone the repository, build from source, and understand commit identifiers. Publish a GitHub release when users need a stable download page, release notes, or compiled artifacts such as a CLI archive, installer, or checksum file.
| Need | Tag only | GitHub release |
|---|---|---|
| Pin an exact source revision | Yes | Yes, through its tag |
| Record tagger and tag message | Use an annotated tag | Still depends on the underlying tag |
| Publish migration instructions | Not conveniently | Yes, in release notes |
| Offer a binary download | Requires another location | Attach it as a release asset |
| Notify GitHub subscribers about a release | No release publication event | Yes, when published |
4. Decide which timestamp is authoritative before you ship
Teams get into trouble when one person calls the tag date “release date,” finance uses the deployment date, and support links customers to the GitHub publication date. Pick labels that match the question being asked.
- “What source is in v2.4.0?” Use
v2.4.0and its resolved commit SHA. - “When did release engineering approve the version marker?” Use the annotated tag timestamp.
- “When could GitHub users download it?” Use the GitHub release publication date.
- “When reached production?” Record a deployment event separately; neither a tag nor a GitHub release proves deployment.
That last rule is the one teams often skip. A release published on GitHub can exist before a staged rollout, and a production deployment can happen without any GitHub release at all. If your runbook says “release date,” replace it with one of the four meanings above.
A practical convention is to name fields precisely in changelogs and incident tickets: tagged at, published at, and deployed at. The extra words prevent a surprising amount of follow-up work when someone investigates a regression six months later.
5. Build from a verified commit, not from whatever is on main
Consider a CLI project preparing v2.4.0. The release candidate was tested from commit abc1234. Before tagging, the release engineer should verify that exact commit rather than assuming the current main head is unchanged.
git fetch origin --tags
git switch main
git pull --ff-only origin main
git rev-parse HEAD
git log -1 --oneline
If the verified SHA is abc1234 but git rev-parse HEAD returns a different value, stop. Either re-run the required checks on the new commit or explicitly check out the already verified commit. “It was green earlier today” is not enough if three pull requests merged afterward.
git checkout abc1234
git status
git log -1 --format="%H%n%ad%n%s"
At this stage, confirm the version inside the repository matches the intended tag. For example, a Node package might have "version": "2.4.0" in package.json; a Go project may embed its version at build time. The exact file differs, but the decision does not: source version, tag name, build output, and release title should all describe the same version.
The cost of skipping this check is not merely an untidy timeline. You can attach a binary built from one commit to a release tag pointing at another, leaving users unable to reproduce the artifact they downloaded.
6. Create and verify an annotated tag on that exact SHA
Once the commit is verified, create the tag by naming the target SHA explicitly. This avoids accidentally tagging a different checked-out commit after a context switch.
git tag -a v2.4.0 abc1234 -m "Release v2.4.0"
git show v2.4.0
git push origin v2.4.0
The output from git show v2.4.0 should identify an annotated tag and display the intended target commit. Capture the full SHA in the release checklist or build log. A short SHA is readable in conversation, but a full SHA is less ambiguous in automated records.
If your team signs tags, verify the signature before publication with git tag -v v2.4.0. That command verifies a signed annotated tag using keys available to the local Git installation. Signing is useful only when the team also manages verification keys and has a process for responding to a failed verification.
Do not silently retag an already published version to “fix” a mistake. Tags are technically movable unless repository controls prevent changes, but changing what v2.4.0 means breaks reproducibility for anyone who fetched it earlier. If the source is wrong, publish a new version such as v2.4.1 or clearly document the exceptional remediation process.
7. Draft the GitHub release after the tag exists
Now use GitHub’s Releases page: open the repository, select Releases, choose Draft a new release, and select the already pushed v2.4.0 tag. Creating the tag first is deliberate. It makes the source identity reviewable before copywriting, asset uploads, and publication decisions begin.
A draft release is the right place to assemble the public package without sending the publication signal too early. Add a release title such as v2.4.0, then write notes that tell users what changes and what action they need to take.
- State one user-visible headline: “Adds JSON output to the export command.”
- List breaking changes with an upgrade action, not just an internal issue number.
- Link to documentation for configuration or migration details.
- Attach built artifacts and checksums if users download binaries.
- State the target tag and, where useful, the full commit SHA used by the build.
GitHub lets maintainers mark a release as a pre-release when it is not ready for production use. Use that flag for versions such as v2.5.0-rc.1, not as a vague warning on an ordinary stable version. GitHub also supports selecting a release as the latest release; make that choice intentionally when maintaining multiple supported lines.
8. Publish only when the tag, notes, and assets agree
Publication is the moment a GitHub release becomes the public distribution record. That timestamp can be later than the tag timestamp because someone may spend an afternoon generating release notes, waiting for a binary build, or completing a final review.
Before pressing publish, perform a three-way match:
- The release’s selected tag is
v2.4.0. - The tag resolves to the verified commit SHA, such as
abc1234. - Every uploaded artifact was built from that same source revision and is named consistently with v2.4.0.
After publication, test the release as a user would. Download an asset rather than relying on the upload success message. If you publish checksums, verify one downloaded file against the checksum. Then open the release page in a signed-out browser session or ask a teammate without repository write access to confirm that the notes and assets are understandable.
If a release is published Tuesday for a tag created Monday, say exactly that in an announcement when the distinction matters: “v2.4.0 was published on Tuesday; it is built from the v2.4.0 tag created Monday.” This is more credible than editing dates to make the timeline appear tidy.
9. Put this release checklist into use this week
You do not need a release platform migration to make dates reliable. Add a small pull-request template, issue checklist, or RELEASING.md file that makes the handoff between engineering and publishing explicit.
- Choose the next semantic version, for example
v2.4.0. - Record the verified commit SHA after required checks pass.
- Confirm the source version matches the intended version.
- Create and push an annotated tag targeting that SHA.
- Create a GitHub release draft using that existing tag.
- Upload artifacts built from the tagged source and add actionable notes.
- Publish the release, then record its publication time separately from tag and deployment times.
For the next release, look at the tag with git show v2.4.0 and compare it with the GitHub release page. If they describe the same version and commit while showing different dates, your process is working as designed. The tag preserves the source identity; the GitHub release records the moment you made that version available to people.