How can I extract Git commit hash from VS/msbuild?

2019-09-18 20:16发布

问题:

The scenario is:

Developers are running a PowerShell script from their machines to publish various Nuget packages to OctopusDeploy via MSBuild. We'd like to make the current git commit's hash (the base of the Git repo is the same as the .sln directory) part of the generated version number. To do this we presumably need to extract the current commit's hash and plug it into the version number string we then pass into MSBuild as a parameter.

So the question is, how can we get this from MSBuild or Visual Studio? There's no guarantee that a developer will have Git commandline installed, but they will have at least VS2015 installed and that has Git support built-in so you'd think there would be some way to get the latest Git commit's hash from this? You could see the hash if you went into the Visual Studio GUI and looked in the source control section.

Is there some commandline tool that comes bundled with Visual Studio 2015 or later that will return the current Git commit's hash?

回答1:

Your requirement to not depend on git.exe makes this complicated. However if you want to use the library that Visual Studio uses, LibGit2Sharp and have Visual Studio integration you can do something crazy like this.

Add a new target to your project file. I'm assuming you are using C# so add it to your csproj after

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

  <PropertyGroup>
    <CodeFile>$(MSBuildProjectDirectory)\HelloGit.cs</CodeFile>
    <CodeFileText>$([System.IO.File]::ReadAllText("$(CodeFile)"))</CodeFileText>
    <CodeFileText>$([MSBuild]::Unescape("$(CodeFileText)"))</CodeFileText>
  </PropertyGroup>

  <UsingTask TaskName="HelloGit" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
    <ParameterGroup>
      <Sha1 ParameterType="System.String" Required="False" Output="True" />
      <Repository ParameterType="System.String" Required="True" Output="False" />
    </ParameterGroup>
    <Task>
      <Reference Include="System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      <Reference Include="C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\LibGit2Sharp.dll" />
      <Using Namespace="System" />
      <Using Namespace="System.IO" />
      <Code Type="Class" Language="cs">
        $(CodeFileText)
      </Code>
    </Task>
  </UsingTask>

  <Target Name="RunTask" AfterTargets="BeforeBuild">

    <HelloGit Repository="$(MSBuildProjectDirectory)">
      <Output TaskParameter="Sha1" PropertyName="CommitId" />
    </HelloGit>
    <Warning Text="Your commit is: $(CommitId)" />

  </Target>

What this does is look in the current project directory for "HelloGit.cs". The contents of which look like this...

You could do this with an inline code task (e.g. embedded C# inside XML) but since the task is quite long I wanted to edit it in VS Code as a separate file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using LibGit2Sharp;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;


namespace GitIntrospection {
    public class HelloGit : Task {
        public override bool Execute() {
            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
                if (args.Name.Contains("LibGit2Sharp")) {
                    return Assembly.LoadFrom("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\IDE\\CommonExtensions\\Microsoft\\TeamFoundation\\Team Explorer\\LibGit2Sharp.dll");
                }
                return null;
            };

            GetCommit();

            return !Log.HasLoggedErrors;
        }

        // [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
        private void GetCommit() {
            Repository repository = new Repository(Repository);

            this.Sha1 = repository.Commits.First().Id.Sha;
        }

        [Output]
        public string Sha1 { get; set; }

        [Required]
        public string Repository { get; set; }
    }
}

Now when you build your project the latest commit will be extracted and logged as a warning. This works both inside Visual Studio and inside MS Build.

Works on the command line

Works inside Visual Studio

The results even seem to agree with git.exe... that's handy

Alternative 1 You could just use git.exe and an <Exec> task!

Alternative 2 You could use PowerShell to load the LibGit2Sharp assembly and call the same methods.