Version Incrementing

Because GitVersion works with several workflows, the way it does its version incrementing may work perfectly for you, or it may cause you issues. This page is split up into two sections, first is all about understanding the approach GitVersion uses by default, and the second is how you can manually increment the version.

Approach

Semantic Versioning is all about releases, not commits or builds. This means that the version only increases after you release, this directly conflicts with the concept of published CI builds. When you release the next version of your library/app/website/whatever you should only increment major/minor or patch then reset all lower parts to 0, for instance given 1.0.0, the next release should be either 2.0.0, 1.1.0 or 1.0.1.

Because of this, GitVersion works out what the next SemVer of your app is on each commit. When you are ready to release, you simply deploy the latest built version and tag the commit it was created from. This practice is called continuous delivery. GitVersion will increment the metadata for each commit so you can tell them apart. For example 1.0.0+5 followed by 1.0.0+6. It is important to note that build metadata is not part of the semantic version; it is just metadata!.

All this effectively means that GitVersion will produce the same version NuGet package each commit until you tag a release.

This causes problems for people as NuGet and other package managers do not support multiple packages with the same version where only the metadata is different. There are a few ways to handle this problem depending on what your requirements are:

1. GitFlow

If you are using GitFlow then builds off the develop branch will actually increment on every commit. This is known in GitVersion as continuous deployment mode. By default develop builds are tagged with the alpha pre-release tag. This is so they are sorted higher than release branches.

If you need to consume packages built from develop, we recommend publishing these packages to a separate NuGet feed as an alpha channel. That way you can publish beta/release candidate builds and only people who opt into the alpha feed will see the alpha packages.

2. Octopus deploy

See Octopus deploy

Manually incrementing the version

With v3 there are multiple approaches. Read about these below.

Commit messages

Adding +semver: breaking or +semver: major will cause the major version to be increased, +semver: feature or +semver: minor will bump minor and +semver: patch or +semver: fix will bump the patch.

Configuration

The feature is enabled by default but can be disabled via configuration, the regex we use can be changed:

major-version-bump-message: '\+semver:\s?(breaking|major)'
minor-version-bump-message: '\+semver:\s?(feature|minor)'
patch-version-bump-message: '\+semver:\s?(fix|patch)'
commit-message-incrementing: Enabled

The options for commit-message-incrementing are Enabled, MergeMessageOnly and Disabled

If the incrementing mode is set to MergeMessageOnly you can add this information when merging a pull request. This prevents commits within a PR to bump the version number.

Conventional commit messages

If you want to use the Conventional Commits standard, you can leverage this feature as follows:

mode: MainLine # Only add this if you want every version to be created automatically on your main branch.
major-version-bump-message: "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([\\w\\s-,/\\\\]*\\))?(!:|:.*\\n\\n((.+\\n)+\\n)?BREAKING CHANGE:\\s.+)"
minor-version-bump-message: "^(feat)(\\([\\w\\s-,/\\\\]*\\))?:"
patch-version-bump-message: "^(fix|perf)(\\([\\w\\s-,/\\\\]*\\))?:"

This will ensure that your version gets bumped according to the commits you've created.

If your CI/CD workflow uses semantic-release's commit-analyzer, change (fix|perf) to (fix|perf|revert). Why?

Alternatively, you can override this rule in the configuration of @semantic-release/commit-analyzer. If you intend to write rules with patterns, note that instead of using Regular Expression, @semantic-release/commit-analyzer uses micromatch's glob implementation.

GitVersion.yml

The first is by setting the next-version property in the GitVersion.yml file. This property only serves as a base version,

Branch name

If you create a branch with the version number in the branch name, such as release-1.2.0 or hotfix/1.0.1 then GitVersion will take the version number from the branch name as a source. However, GitVersion can't use the branch name as a version source for other branches.

Detached HEAD

If HEAD is in detached state tag will be -no-branch-.

Example: 0.0.1--no-branch-.1+4

Tagging commit

By tagging a commit, GitVersion will use that tag for the version of that commit, then increment the next commit automatically based on the increment rules for that branch (some branches bump patch, some minor).

Be aware that tags are local to a repository and will not be transferred when you perform a default git push. Instead, tags can be pushed separately with their own command. For more information, read the git documentation on tagging.

Incrementing per commit

When using the continuous deployment mode (which will increment the SemVer every commit) all builds must have a pre-release tag, except for builds that are explicitly tagged as stable.

Then the build metadata (which is the commit count) is promoted to the pre-release tag. Applying these rules, the above commit-graph would produce:

e137e9 -> 1.0.0+0
a5f6c5 -> 1.0.1-ci.1
adb29a -> 1.0.1-feature-foo.1 (PR #5 Version: `1.0.1-PullRequest.5+2`)
7c2438 -> 1.0.1-feature-foo.2 (PR #5 Version: `1.0.1-PullRequest.5+3`)
5f413b -> 1.0.1-ci.4
d6155b -> 2.0.0-rc.1+4 (Before and after tag)
d53ab6 -> 2.0.0-rc.2 (If there was another commit on the release branch it would be 2.0.0-rc.3)
b5d142 -> 2.0.0-ci.0 (2.0.0 branch was merged, so main is now at 2.0.0)

As you can see, the versions now no longer conflict. When you want to create a stable 2.0.0 release you simply git tag 2.0.0, then build the tag, and it will produce a stable 2.0.0 package. Be aware that tags are not transferred with git push

For more information/background on why we have come to this conclusion, read Xavier Decoster's blog post on the subject.

GitHub