Automatic Versioning with Nerdbank.GitVersioning
Manual versioning is error-prone. Someone forgets to bump the version, or two developers bump it to different values in parallel branches. Nerdbank.GitVersioning (NBGV) solves this by deriving version numbers from your Git history — you set the major.minor version, and the patch and build metadata are calculated automatically from the commit graph.
How It Works
NBGV counts the number of commits since you last changed the version to determine the patch number. Given a base version of 1.2, if there have been 47 commits since that version was set, the calculated version is 1.2.47. The version is deterministic — the same commit always produces the same version number, regardless of when or where the build runs.
Installation
Install the nbgv CLI tool and initialise your repository:
dotnet tool install -g nbgv
nbgv install
This creates a version.json file in your repository root:
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.0-beta",
"publicReleaseRefSpec": [
"^refs/heads/main$"
],
"cloudBuild": {
"buildNumber": {
"enabled": true
}
}
}
It also adds the Nerdbank.GitVersioning NuGet package to your project. From this point, all builds automatically get version numbers derived from Git.
Version Configuration
The version.json file controls the base version:
{
"version": "2.0",
"publicReleaseRefSpec": [
"^refs/heads/main$"
]
}
version: The major.minor (and optional pre-release tag) base. NBGV appends the patch number.publicReleaseRefSpec: Branches where "public" release versions are built. On other branches, the version includes the Git height and commit hash for uniqueness.
On main, a build might produce 2.0.15. On a feature branch, it produces 2.0.15-feat-new-feature.42+abc1234.
Pre-Release Versions
For pre-release packages, include a pre-release tag in the version:
{
"version": "3.0-beta"
}
This produces versions like 3.0.12-beta on public branches and 3.0.12-beta.feat-something.5 on feature branches. When you're ready for a stable release, remove the -beta suffix and commit.
Bumping Versions
When you want to increment the major or minor version, use the CLI:
nbgv set-version 2.1
Or simply edit version.json. The patch number resets to the commit height since this change, so it naturally starts low again.
For a major release:
nbgv set-version 3.0
What Gets Versioned
NBGV sets several version properties automatically:
| Property | Example | Used For |
|---|---|---|
AssemblyVersion |
2.0.0.0 |
.NET assembly identity |
FileVersion |
2.0.15.12345 |
Windows file properties |
InformationalVersion |
2.0.15+abc1234 |
Human-readable version |
PackageVersion |
2.0.15 |
NuGet package version |
You don't need to set any of these in your .csproj — NBGV handles all of them.
CI Integration
NBGV works out of the box with most CI systems. The critical requirement is that your CI checks out the full Git history, not a shallow clone:
# GitHub Actions
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history required for version calculation
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Build
run: dotnet build -c Release
- name: Show version
run: nbgv get-version
The fetch-depth: 0 is essential. With a shallow clone, NBGV cannot count commits and will produce incorrect versions.
The cloudBuild section in version.json tells NBGV to set the CI build number to match the calculated version, giving you consistent version numbers across your build system and NuGet packages.
Comparing with GitVersion
GitVersion is the other popular option. The key differences:
- NBGV uses commit height (simple, deterministic). GitVersion uses branch names and merge patterns (more flexible, more complex).
- NBGV requires
version.json. GitVersion can work with tags alone. - NBGV is simpler to reason about. GitVersion offers more control over version calculation strategies (GitFlow, GitHub Flow, etc.).
For most projects, NBGV's simplicity is an advantage. If you follow GitFlow with specific versioning rules per branch type, GitVersion may be a better fit.
Accessing the Version in Code
NBGV generates a ThisAssembly class with version information:
app.MapGet("/version", () => new
{
Version = ThisAssembly.AssemblyInformationalVersion,
Commit = ThisAssembly.GitCommitId
});
This is useful for health endpoints, diagnostics, or logging the running version at startup.
Summary
Nerdbank.GitVersioning removes version management from your workflow. Set the major.minor version in version.json, and every commit gets a unique, deterministic, semver-compliant version derived from your Git history. No tags to manage, no manual bumps, no version conflicts in merge — just predictable versions from your commit graph.