Even for simple projects, it is good practice to explicitly identify versions in your repository. This document describes a workflow for creating and publishing version tags. We will use the convention called SemVer for the version numbering scheme.
The examples provided use main as the default branch. Replace it with master
or another default branch name when applicable.
If you don't keep a CHANGELOG.md file, it is recommended to double-check your version history.
For listing your tags use the following:
# Fetch tags from remote
git fetch origin --tags
# Show tags with their annotation/message (-n) sorted naturally by version number
git tag -n --sort=-version:refnameUsually you'd tag the current HEAD after committing everything. Verify you are on the proper commit.
# Normally you would want your branch to be up to date with 'origin/main'
git status
# Check your last commit message
git log -1 --oneline
# Update if necessary, refusing to create a merge commit
git pull --ff-only
# Check again your last commit message
git log -1 --onelineTo create a v1.0.0 tag for example:
git tag -a v1.0.0 -m "Version 1.0.0"The option -a creates an annotated tag, which has its own message, author, and date.
Notice that we use a short message for the annotated tag rather than a full description of the version. The detailed history is already available through the commits leading up to this tag.
For projects using GitHub Releases, the user-facing description belongs in the changelog and/or GitHub Release notes.
Inspect your tag with:
git show v1.0.0Note: If during your verification you find you made a mistake, since you haven't pushed the tag, you can delete the local tag by using:
git tag -d v1.0.0Tags are not automatically pushed by a normal git push.
Push this particular tag with:
git push origin v1.0.0This document is intended as an easy guide for version tagging. However you may want to go a step further and use a Release Workflow.
Here you will find the proper procedure for that scheme:
-
Develop normally on
main(or your release branch), committing changes as usual. -
When you're ready to release, decide the next SemVer:
- PATCH -> backward-compatible bug fixes:
1.2.3->1.2.4 - MINOR -> backward-compatible functionality:
1.2.3->1.3.0 - MAJOR -> incompatible/breaking changes:
1.2.3->2.0.0
- PATCH -> backward-compatible bug fixes:
-
Update your changelog.
-
Commit the release metadata.
-
Create an annotated Git tag.
-
Push the commit and tag.
-
Create a GitHub Release from that tag.
Let's imagine we want to make a v1.4.0 release. The procedure would be as follows...
Make sure the tree is clean and you're on the correct commit.
# Make sure the working tree is clean
git status
# Update if necessary, refusing to create a merge commit
git pull --ff-only
# Check again your last commit message
git log -1 --onelineYour CHANGELOG.md will look something like:
## [Unreleased]
### Added
### Changed
### Fixed
## [1.4.0] - 2026-08-26
### Added
- Added JSON output support.
- Added configurable timeout values.
### Changed
- Improved error messages for failed connections.
### Fixed
- Fixed handling of HTTP 403 responses.During normal development, add noteworthy changes to the Unreleased section.
When preparing a release, move those entries into the new version section
and leave Unreleased ready for subsequent development.
Other changelog categories include: Deprecated, Removed, and Security.
Now commit your changelog and tag the commit with the proper version:
# Commit the Changelog
git add CHANGELOG.md
git commit -m "chore: release v1.4.0"
# Tag the commit
git tag -a v1.4.0 -m "Version 1.4.0"
# Push commit and tag
git push origin main
git push origin v1.4.0Your release notes file may look like this:
## What's new
- Added JSON output support.
- Added configurable timeout values.
- Improved error messages for failed connections.
## Fixes
- Fixed handling of HTTP 403 responses.Note: The release-notes.md file will only be used as an input for a later command.
We will proceed to delete it after using it. A good practice is to include release-notes.md
in your .gitignore file. This will avoid accidental commits of this file to your repository.
This step is specific to GitHub, for other platforms please refer to the corresponding official documentation.
Since you created your release-notes.md file manually,
you can create the release using this command:
# Create release
gh release create v1.4.0 \
--verify-tag \
--title "Version 1.4.0" \
--notes-file release-notes.md \
--draft
# Inspect the release
gh release view v1.4.0
# Or inspect it in GitHub
gh release view v1.4.0 --web
# Publish the finalized release
gh release edit v1.4.0 --draft=false
# Inspect finalized release
gh release view v1.4.0
# Delete the temporary release notes
rm release-notes.mdAnother alternative is letting GitHub generate the release notes based on the changes
since the previous release. In this case there is no need to create release-notes.md:
# Generate a draft
gh release create v1.4.0 \
--verify-tag \
--title "Version 1.4.0" \
--generate-notes \
--fail-on-no-commits \
--draft
# Inspect draft in terminal
gh release view v1.4.0
# Or inspect it in GitHub
gh release view v1.4.0 --web
# Publish the finalized release
gh release edit v1.4.0 --draft=false
# Inspect finalized release
gh release view v1.4.0