Open-source project
devlooped/GitInfo avatar
devlooped/GitInfo

GitInfo: Git and SemVer data compiled straight into your .NET assembly

Git and SemVer Info from MSBuild, C# and VB

652 stars71 forksPascalMIT

At a glance

What is it?
GitInfo is a NuGet package that turns the current working copy into MSBuild properties and a generated ThisAssembly class, so a C#, F# or VB project gets its version from the repository it was built from. It trades external tagging conventions for a build-time dependency on git itself.
Who is it for?
GitInfo suits teams that want a local build to produce the same version string as CI without adding a tagging service, and that already build on a machine with the repository checked out. It is the wrong choice for a pipeline where the version is decided before checkout, for non .NET projects, or for an organisation that will not accept the Open Source Maintenance Fee for a package it vendors.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Pascal, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The version problem GitInfo removes from every build

Most .NET versioning setups make the build machine's mood decide the version. A CI script writes a version file, a shared build props file parses it, and a developer who runs a local build outside that path gets 1.0.0. GitInfo takes a different route. It reads the repository that is being compiled, works out a SemVer from the tags it finds, and hands the result to MSBuild as ordinary properties plus a generated source file. The project states the goal plainly: because the information is available whenever the project is built, you never depend on CI build scripts that generate versions, and you can compile locally the same version of an assembly that a CI server built.

The audience is specific: teams on C#, F# or VB who want version metadata without compiling a custom MSBuild task and without writing format strings. The package description calls it Git and SemVer info from MSBuild, C# and VB. The homepage points at clarius.org/GitInfo, and the announcement post at cazzulino.com explains the original idea.

Note what is not in scope. GitInfo does not tag your repository for you and it does not publish packages. It reads the working copy that MSBuild is building, so it is a build-time companion to whatever you already do for tagging and packaging.

How the package gets git data into MSBuild without a custom task

The design is described as a fresh and transparent approach to retrieving Git information from MSBuild and code, with no custom tasks or compiled code and tools, and no obscure settings or format strings. The mechanism is plain: the package contributes MSBuild targets and properties that shell out to git during the build, and the values they populate become both MSBuild properties and, for C#, F# and VB projects, constants in a compile-time generated source file.

The generated file exposes a partial ThisAssembly class with a nested Git static class, reachable from anywhere inside the assembly. Alongside it, targets populate properties that any target depending on the GitInfo target can read. The exposed property set starts with $(GitRepositoryUrl), $(GitBranch), $(GitCommit), $(GitCommitDate), $(GitCommits), $(GitTag), $(GitBaseTag) and the SemVer component properties. That list is cut off mid-entry in the version used here, so the exact full set of properties should be read from the repository before relying on a name beyond the ones shown.

That two-surface design is the interesting part. The same values are available as MSBuild strings for targets and packaging, and as typed constants for ordinary code, without a bridge between the two.

Installing the package and reading the first commit constant

Installation is a single NuGet package reference. The README points at the NuGet listing and gives the Package Manager Console form:

pwsh
PM> Install-Package GitInfo

After that, a build of a C#, F# or VB project produces the generated file, and the data is one static property access away:

csharp
Console.WriteLine(ThisAssembly.Git.Commit);

Expect one small wrinkle the README calls out: after installing the package you may need to close and reopen the solution for Visual Studio to refresh intellisense and show the ThisAssembly type the first time. The type exists in the compiled output either way; the refresh is an editor concern, not a build failure.

The default behaviour is also a versioning change, not just an addition. GitInfo sets $(Version) and $(PackageVersion) for the .NET SDK, which then derives AssemblyInfo, FileVersion, InformationalVersion and the packed package version. The default string is assembled from the populated properties as $(GitSemVerMajor).$(GitSemVerMinor).$(GitSemVerPatch)$(GitSemVerDashLabel)+$(GitBranch).$(GitCommit). In practice you get a versioning scheme in place straight after install and build, with no tags of your own and no pipeline changes.

Opting out of the default version and building attributes by hand

If you want the Git data but your own version string, set GitVersion to false in the project file:

xml
<PropertyGroup>
  <GitVersion>false</GitVersion>
</PropertyGroup>

With that off, the constants remain and the versioning is yours to write in plain code. The README shows the same three attributes for C#, F# and VB, combining BaseVersion, SemVer, Commits, Branch and Commit:

csharp
[assembly: AssemblyVersion(ThisAssembly.Git.BaseVersion.Major + "." + ThisAssembly.Git.BaseVersion.Minor + "." + ThisAssembly.Git.BaseVersion.Patch)]

[assembly: AssemblyFileVersion(ThisAssembly.Git.SemVer.Major + "." + ThisAssembly.Git.SemVer.Minor + "." + ThisAssembly.Git.SemVer.Patch)]

[assembly: AssemblyInformationalVersion(
    ThisAssembly.Git.SemVer.Major + "." +
    ThisAssembly.Git.SemVer.Minor + "." +
    ThisAssembly.Git.Commits + "-" +
    ThisAssembly.Git.Branch + "+" +
    ThisAssembly.Git.Commit)]

Two things follow from that snippet. The values are numbers and strings, so you are assembling a version with ordinary expression syntax rather than passing a template string to a tool. And the constants are only the data source; they do not set the attributes for you, which is why the documentation also notes you have to switch off the SDK-generated versions.

The SDK attribute generators must be turned off explicitly

Writing your own assembly version attributes collides with the .NET SDK, which generates its own by default. The README states that when generating your own assembly version attributes you need to turn off the corresponding attribute generation by setting GenerateAssemblyVersionAttribute, GenerateAssemblyFileVersionAttribute and GenerateAssemblyInformationalVersionAttribute to false.

This is the sharpest edge in the whole flow, and it is easy to miss because the failure is not a compile error you would expect. You can have a valid AssemblyVersion attribute in your own code and still see the SDK value in the produced binary, because the SDK emitted its own attribute from $(Version) at the same time. The evidence of a collision is in the output file, not in the build log. Anyone switching from the default GitInfo versioning to hand-written attributes should verify the resulting AssemblyFileVersion and InformationalVersion inside the built assembly rather than assuming the source file won.

A related surprise is documented too: Visual Studio may not show the ThisAssembly type right after installation, so the constant autocomplete can look broken when the build is in fact fine.

Git properties are only readable from a target that depends on GitVersion

There is an ordering constraint that the ordering rule is spelled out. Because the provided properties are populated via targets that need to run before they are available, you cannot use the GitInfo-provided properties in a PropertyGroup at the project level. You can only use them from within a target that in turn depends on the relevant target from GitInfo, typically GitVersion when you consume the SemVer properties.

The full pattern looks like this, with GitVersion disabled and a target named PopulateInfo that depends on GitVersion and runs before GetAssemblyVersion, GenerateNuspec and GetPackageContents:

xml
<PropertyGroup>
  <GitVersion>false</GitVersion> <!-- we'll do our own versioning -->
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="GitInfo" PrivateAssets="all" />
</ItemGroup>

<Target Name="PopulateInfo" DependsOnTargets="GitVersion" BeforeTargets="GetAssemblyVersion;GenerateNuspec;GetPackageContents">
  <PropertyGroup>
    <Version>$(GitSemVerMajor).$(GitSemVerMinor).$(GitSemVerPatch)$(GitSemVerDashLabel)+$(GitBranch).$(GitCommit)</Version>
    <PackageVersion>$(Version)</PackageVersion>
    <RepositoryBranch>$(GitBranch)</RepositoryBranch>
    <RepositoryCommit>$(GitCommit)</RepositoryCommit>
    <SourceRevisionId>$(GitBranch) $(GitCommit)</SourceRevisionId>
  </PropertyGroup>
</Target>

The benefit of routing through a target rather than a project-level PropertyGroup is that RepositoryBranch, RepositoryCommit and SourceRevisionId land in the nuspec and in the assembly metadata as a side effect, so the packaged artifact records where it came from without extra configuration.

Where a CI-driven version pipeline differs from a local git read

The common alternative is a build-server step that computes a version and writes it out, with the build reading that value rather than the repository. The difference in approach is where the version is decided. A CI-driven pipeline can produce a version for a commit that was never checked out, and it can enforce a single authority for version numbers across several repositories. GitInfo decides at build time from the working copy, so the version follows whatever is on disk, including uncommitted local changes and branches that were never tagged.

That difference has two consequences worth weighing. First, local and CI builds agree only when both work from the same commit with the same tag set. Second, a build that runs from an exported source archive or a shallow clone without tags has less information to work with, and the documentation does not say how the SemVer base is selected when the expected tag is missing.

For teams that want a reproducible build server to be the single source of version truth, GitInfo is the wrong shape of tool. For teams that are tired of local builds producing a different assembly version than the pipeline, the package removes the need for a separate version-writing step entirely.

MIT licence, an extra maintenance fee, and the release cadence

GitInfo is published by Devlooped under the MIT licence, with the licence text in license.txt at the root of the repository. The root also carries osmfeula.txt, and the project states that users of this package who generate revenue must pay an Open Source Maintenance Fee, while the source code itself remains freely available under the licence terms. A single fee covers all Devlooped packages, paid by becoming a Sponsor at the matching tier. That is a commercial condition layered on top of an open source licence, so an organisation evaluating the package should read osmfeula.txt and decide for itself whether the fee applies to its use, rather than assuming MIT settles the question. This is a licensing question, not legal advice.

On maintenance, the repository is not archived and the last push was on 2026-10-15, with releases v3.6.1 on 2026-06-24, v3.6.0 on 2025-10-17 and v3.5.0 on 2024-11-02. That is a working project with a maintained NuGet package and a changelog in the root. The upgrade cost is therefore low in effort terms: the package is added as a PackageReference, and the reference to ThisAssembly.Git.Commit in your code does not change across those releases. The risk sits in the version string GitInfo writes into $(Version) by default, which changes the artifact metadata of every build in the repository the moment it is installed.

Editorial conclusion

GitInfo suits teams that want a local build to produce the same version string as CI without adding a tagging service, and that already build on a machine with the repository checked out. It is the wrong choice for a pipeline where the version is decided before checkout, for non .NET projects, or for an organisation that will not accept the Open Source Maintenance Fee for a package it vendors. Before adopting it, check that the git executable is on PATH in every build environment, including local developer machines and container images, and confirm that the SemVer interpretation of your existing tags matches the one GitInfo produces.

Frequently asked questions

What is GitInfo and what does it add to a .NET project?

GitInfo is a NuGet package from Devlooped that retrieves Git and SemVer information from the repository being built and exposes it as MSBuild properties and as constants in a generated ThisAssembly class. By default it also sets $(Version) and $(PackageVersion) so the SDK derives the assembly and package versions.

How do I install GitInfo and read the commit hash in C#?

Install the GitInfo package from NuGet with Install-Package GitInfo, then build the project and read ThisAssembly.Git.Commit. Visual Studio may need the solution closed and reopened to show the ThisAssembly type the first time.

Can I use my own version string with GitInfo?

Yes. Set GitVersion to false in a PropertyGroup, and the constants stay available while the SDK version properties are left alone. If you then write your own assembly version attributes, you also need to set GenerateAssemblyVersionAttribute, GenerateAssemblyFileVersionAttribute and GenerateAssemblyInformationalVersionAttribute to false so the SDK does not emit duplicates.

Why can I not use $(GitCommit) in a project-level PropertyGroup?

The properties are populated by targets that must run first, so they are only available inside a target that depends on the GitInfo target, typically GitVersion. The README documents this ordering constraint and shows a target that sets Version, PackageVersion, RepositoryBranch, RepositoryCommit and SourceRevisionId.

Official sources

  1. devlooped/GitInfo on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/devlooped-gitinfo.svg)](https://hysenlabs.com/projects/devlooped-gitinfo)

Was this useful?