- CI/CD pipeline: Continuously version your artifacts and uniquely identify your dev-builds.
git-semver
will generate unique SemVer compliant versions for each commit you add to your project. Let's say you were usingAPP_VERSION
in your build-pipeline you could simply replace it with$ APP_VERSION=$(git-semver)
- Tag releases: Automate your workflow for tagging releases of your software. Automatically select
the next patch/minor/major version when creating a new release tag:
or create an alias
$ git tag $(git-semver -target minor)
alias gtg-min="git tag -s -a $(git-semver -target minor)"
Software should be versioned in order to be able to identify a certain feature set or to know when a specific bug has been fixed. It is a good practice to use Semantic Versioning (SemVer) in order to attach a meaning to a version number or the change thereof.
git allows you to conveniently reference a certain state of your code through the usage of tags. Tags can have an arbitrary identifier, so that it seems a natural choice to use them for versioning.
A semantic version consists of three dot-separated parts <major>.<minor>.<patch>
and this should be the name that you give to a tag. Optionally you can prepend
the letter v
if your language specific tooling requires it. It is also possible
to attach a pre-release identifier to a version e.g. for a release candidate. This
identifier is separated with hyphen from the core version component. A valid version
tag would be, e.g. 1.2.3
, v2.3.0
, 1.1.0-rc3
.
$ git tag v2.0.0-rc1
So for a tagged commit we would know which version to assign to our software, but
which version should we use for not tagged commits? We can use git describe
to
get a unique identifier based on the last tagged commit.
$ git describe --tags
3.5.1-22-gbaf822dd5
This is the 22nd commit after the tag 3.5.1
with the abbreviated commit hash gbaf822dd5
.
Sadly this identifier has two drawbacks.
-
It's not compliant to SemVer, because there are multiple hyphens after the core version. See the BNF specification
-
It doesn't allow proper sorting of versions, because the pre-release identifier would make the version smaller than the tagged version, even though it has several commits build on top of that version.
git-semver
collects information about the head commit of a repo similar to how
git describe
would do it and derives a SemVer compliant version from it. E.g.:
git describe |
git-semver |
---|---|
3.5.1-22-gbaf822d |
3.5.2-dev.22+baf822dd |
4.2.0-rc.3-5-gfcf2c8f |
4.2.0-rc.3.dev.5+fcf2c8fd |
1.0.1 |
1.0.1 |
It will attach a pre-release tag of the form dev.N
, where N
is the number of commits
since the last commit, and the commit hash as build-metadata. Additionally the patch level
component will be incremented in case of a pre-release-version. If the last tag itself
contains a pre-release-identifier the dev.N
suffix will be appended but all other parts
will be left untouched. This complies with the precedence rules
defined in the SemVer spec. So that
0.9.9 < 1.0.0-rc.1 < 1.0.0-rc1.dev.3+fcf2c8fd < 1.0.0-rc.2 < 1.0.0
The output of git-semver
can be controlled with the -format
option or one of it shorthand
companions as described here. The format string can include the following
characters
Format char | Description |
---|---|
x |
Major version |
y |
Minor version |
z |
Patch version |
p |
Pre-release version |
m |
Metadata |
The format chars x
, y
and z
are separted with a dot, p
with a hyphen and m
with a
plus character. A valid format string is e.g.: x.y+m
The output and parsing of git-semver
can be controlled with the following options.
Name | Description |
---|---|
-format |
Format string as described here |
-no-minor |
Exclude minor version and all following components |
-no-patch |
Exclude patch version and all following components |
-no-pre |
Exclude pre-release version and all following components |
-no-meta /-no-hash |
Exclude build metadata |
-prefix |
Prefix string for version e.g.: v |
-set-meta |
Set buildmeta to this value |
-guard |
Ignore shorthand formats for pre-release versions |
-target |
Set target release dev (default), patch , minor or major |
$ git-semver
3.5.2-dev.22+8eaec5d3
# Exclude build metadata
$ git-semver -no-meta
3.5.2-dev.22
# Only major and minor version
$ git-semver -no-patch
3.5
$ git-semver -prefix v -no-hash
v3.5.2
$ git-semver -set-meta custom
3.5.2+custom
$ git-semver -target minor
3.6.0
$ git-semver -target major
4.0.0
A common application of git-semver
is to create new
If you use git-semver
to automatically derive versions for your application (e.g. in a CI/CD
environment), and you want to provide convenient shorthand versions (e.g. 1.2
), so that it is
easier to follow non-breaking updates, you might run into the problem that a pre-release version
accidentally overwrites a production version. This is because
# tag of HEAD commit: 1.2.2
$ git-semver -no-patch
1.2
# tag of HEAD commit: 1.2.3-dev.1"
$ git-semver -no-patch
1.2
result in the same shorthand version. To mitigate this problem you can use the -guard
option
that will ignore any output format that doesn't contain the pre-release identifier if the current
version is a pre-release version. E.g.
# tag of HEAD commit: 1.2.3-dev.1"
$ git-semver -guard -no-patch
1.2.3-dev.1+8eaec5d3
If you create multiple annotated tags on the same commit (e.g. you want to promote a release candidate
to be the final release without adding any further commits), git-semver
will pick the tag that was
created last, which is usually what you want. E.g.
$ git tag -a -m "Release candidate" 1.1.0-rc.1
$ git-semver
1.1.0-rc.1
$ git tag -a -m "Final release" 1.1.0
$ git-semver
1.1.0
Currently, git-semver
can be installed with go install
$ go install github.com/mdomke/git-semver/v6@latest
There is also a Homebrew formula that can be installed with
$ brew install mdomke/git-semver/git-semver
You can also use git-semver
as a docker-container. Images are available from DockerHub and
GitHub Container Registry
$ docker run --rm -v `pwd`:/git-semver mdomke/git-semver
or
$ docker run --rm -v `pwd`:/git-semver ghcr.io/mdomke/git-semver