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:

terminal
dotnet tool install -g nbgv
nbgv install

This creates a version.json file in your repository root:

version.json
{
  "$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.json
{
  "version": "2.0",
  "publicReleaseRefSpec": [
    "^refs/heads/main$"
  ]
}

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:

data.json
{
  "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:

terminal
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:

terminal
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:

config.yaml
# 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:

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:

Example.cs
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.