Skip to content

Commit

Permalink
switch from LibGit2Sharp to git
Browse files Browse the repository at this point in the history
  • Loading branch information
adamralph committed Aug 27, 2019
1 parent 8519033 commit 15b5b1c
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 63 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ matrix:
include:
- os: linux
dist: bionic
- os: linux
dist: xenial
- os: osx
13 changes: 13 additions & 0 deletions MinVer.Lib/Commit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace MinVer.Lib
{
using System.Collections.Generic;

internal class Commit
{
public Commit(string sha) => this.Sha = sha;

public string Sha { get; }

public List<Commit> Parents { get; } = new List<Commit>();
}
}
18 changes: 18 additions & 0 deletions MinVer.Lib/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace MinVer.Lib
{
using System;
using System.Collections.Generic;

internal static class DictionaryExtensions
{
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, Func<TValue> valueFactory)
{
if (!dictionary.TryGetValue(key, out var value))
{
dictionary.Add(key, value = valueFactory());
}

return value;
}
}
}
42 changes: 42 additions & 0 deletions MinVer.Lib/Git.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace MinVer.Lib
{
using System;
using System.Collections.Generic;
using System.Linq;

internal static class Git
{
public static bool IsRepository(string directory, ILogger log) => GitCommand.TryRun("status --short", directory, log, out _);

public static Commit GetHeadOrDefault(string directory, ILogger log)
{
if (!GitCommand.TryRun("log --pretty=format:\"%H %P\"", directory, log, out var output))
{
return default;
}

var commits = new Dictionary<string, Commit>();

foreach (var shas in output
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)))
{
commits.GetOrAdd(shas[0], () => new Commit(shas[0]))
.Parents.AddRange(shas.Skip(1).Select(parentSha => commits.GetOrAdd(parentSha, () => new Commit(parentSha))));
}

return commits.Values.FirstOrDefault();
}

public static IEnumerable<Tag> GetTagsOrEmpty(string directory, ILogger log) =>
GitCommand.TryRun("show-ref --tags --dereference", directory, log, out var output)
? output
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Split(new[] { ' ' }, 2))
.Select(tokens => new Tag(tokens[1].Substring(10).RemoveFromEnd("^{}"), tokens[0]))
: Enumerable.Empty<Tag>();

private static string RemoveFromEnd(this string text, string value) =>
text.EndsWith(value) ? text.Substring(0, text.Length - value.Length) : text;
}
}
57 changes: 57 additions & 0 deletions MinVer.Lib/GitCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace MinVer.Lib
{
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;

internal static class GitCommand
{
public static bool TryRun(string args, string workingDirectory, ILogger log, out string output)
{
using (var process = new Process())
{
process.StartInfo = new ProcessStartInfo
{
FileName = "git",
Arguments = args,
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
};

var tcs = new TaskCompletionSource<object>();
process.Exited += (s, e) => tcs.SetResult(null);
process.EnableRaisingEvents = true;

log.Trace($"Running Git: {process.StartInfo.FileName} {process.StartInfo.Arguments}");

try
{
process.Start();
}
catch (Win32Exception ex)
{
throw new Exception("Failed to run Git. Git may not be installed on the system.", ex);
}

var runProcess = tcs.Task;
var readOutput = process.StandardOutput.ReadToEndAsync();
var readError = process.StandardError.ReadToEndAsync();

Task.WaitAll(runProcess, readOutput, readError);

var exitCode = process.ExitCode;
output = readOutput.Result;
var error = readError.Result;

log.Trace($"Git exit code: {exitCode}");
log.Trace($"Git stdout:{Environment.NewLine}{output}");
log.Trace($"Git stderr:{Environment.NewLine}{error}");

return exitCode == 0;
}
}
}
}
1 change: 0 additions & 1 deletion MinVer.Lib/MinVer.Lib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="LibGit2Sharp" Version="0.26.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeQuality.Analyzers" Version="2.9.4" PrivateAssets="All" />
</ItemGroup>
Expand Down
35 changes: 35 additions & 0 deletions MinVer.Lib/Repository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace MinVer.Lib
{
using System.Collections.Generic;
using System.Linq;

internal class Repository
{
private Repository(Commit head, IEnumerable<Tag> tags)
{
this.Head = head;
this.Tags = tags;
}

public Commit Head { get; }

public IEnumerable<Tag> Tags { get; }

public static bool TryCreateRepo(string repoOrWorkDir, out Repository repository, ILogger log)
{
repository = default;

if (!Git.IsRepository(repoOrWorkDir, log))
{
return false;
}

var head = Git.GetHeadOrDefault(repoOrWorkDir, log);
var tags = head != default ? Git.GetTagsOrEmpty(repoOrWorkDir, log) : Enumerable.Empty<Tag>();

repository = new Repository(head, tags);

return true;
}
}
}
28 changes: 0 additions & 28 deletions MinVer.Lib/RepositoryEx.cs

This file was deleted.

13 changes: 6 additions & 7 deletions MinVer.Lib/RepositoryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ namespace MinVer.Lib
{
using System.Collections.Generic;
using System.Linq;
using LibGit2Sharp;

internal static class RepositoryExtensions
{
public static Version GetVersion(this Repository repo, string tagPrefix, VersionPart autoIncrement, string defaultPreReleasePhase, ILogger log)
{
var commit = repo.Commits.FirstOrDefault();
var commit = repo.Head;

if (commit == default)
{
Expand All @@ -20,9 +19,9 @@ public static Version GetVersion(this Repository repo, string tagPrefix, Version
}

var tagsAndVersions = repo.Tags
.Select(tag => (tag, Version.ParseOrDefault(tag.FriendlyName, tagPrefix)))
.Select(tag => (tag, Version.ParseOrDefault(tag.Name, tagPrefix)))
.OrderBy(tagAndVersion => tagAndVersion.Item2)
.ThenBy(tagsAndVersion => tagsAndVersion.tag.FriendlyName)
.ThenBy(tagsAndVersion => tagsAndVersion.tag.Name)
.ToList();

var commitsChecked = new HashSet<string>();
Expand All @@ -45,12 +44,12 @@ public static Version GetVersion(this Repository repo, string tagPrefix, Version
{
++count;

var commitTagsAndVersions = tagsAndVersions.Where(tagAndVersion => tagAndVersion.tag.Target.Sha == commit.Sha).ToList();
var commitTagsAndVersions = tagsAndVersions.Where(tagAndVersion => tagAndVersion.tag.Sha == commit.Sha).ToList();
var foundVersion = false;

foreach (var (tag, commitVersion) in commitTagsAndVersions)
{
var candidate = new Candidate { Commit = commit, Height = height, Tag = tag.FriendlyName, Version = commitVersion, };
var candidate = new Candidate { Commit = commit, Height = height, Tag = tag.Name, Version = commitVersion, };

foundVersion = foundVersion || candidate.Version != default;

Expand Down Expand Up @@ -90,7 +89,7 @@ public static Version GetVersion(this Repository repo, string tagPrefix, Version
}
}

foreach (var parent in commit.Parents.Reverse())
foreach (var parent in ((IEnumerable<Commit>)commit.Parents).Reverse())
{
commitsToCheck.Push((parent, height + 1, commit));
}
Expand Down
15 changes: 15 additions & 0 deletions MinVer.Lib/Tag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace MinVer.Lib
{
internal class Tag
{
public Tag(string name, string sha)
{
this.Name = name;
this.Sha = sha;
}

public string Name { get; }

public string Sha { get; }
}
}
11 changes: 2 additions & 9 deletions MinVer.Lib/Versioner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static Version GetVersion(string repoOrWorkDir, string tagPrefix, MajorMi

private static Version GetVersion(string repoOrWorkDir, string tagPrefix, VersionPart autoIncrement, string defaultPreReleasePhase, ILogger log)
{
if (!RepositoryEx.TryCreateRepo(repoOrWorkDir, out var repo))
if (!Repository.TryCreateRepo(repoOrWorkDir, out var repo, log))
{
var version = new Version(defaultPreReleasePhase);

Expand All @@ -45,14 +45,7 @@ private static Version GetVersion(string repoOrWorkDir, string tagPrefix, Versio
return version;
}

try
{
return repo.GetVersion(tagPrefix, autoIncrement, defaultPreReleasePhase, log);
}
finally
{
repo.Dispose();
}
return repo.GetVersion(tagPrefix, autoIncrement, defaultPreReleasePhase, log);
}
}
}
11 changes: 10 additions & 1 deletion MinVer/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ public static void ErrorInvalidVersionOverride(string versionOverride) =>

private static void Error(int code, string message) => Message($"error MINVER{code:D4} : {message}");

private static void Message(string message) => Console.Error.WriteLine($"MinVer: {message}");
private static void Message(string message)
{
if (message.Contains('\r') || message.Contains('\n'))
{
var lines = message.Replace("\r\n", "\n").Split(new[] { '\r', '\n' });
message = string.Join($"{Environment.NewLine}MinVer: ", lines);
}

Console.Error.WriteLine($"MinVer: {message}");
}
}
}
14 changes: 2 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Also available as a [command line tool](#can-i-use-minver-to-version-software-wh
## Prerequisites

- [.NET Core SDK 2.1.300 or a later 2.x version.](https://www.microsoft.com/net/download)
- [Git](https://git-scm.com/)

## Quick start

Expand Down Expand Up @@ -102,7 +103,6 @@ _(With TL;DR answers inline.)_
- [Can I use MinVer to version software which is not built using a .NET SDK style project?](#can-i-use-minver-to-version-software-which-is-not-built-using-a-net-sdk-style-project) _(yes)_
- [What if the history diverges, and more than one tag is found?](#what-if-the-history-diverges-and-more-than-one-tag-is-found) _(nothing bad)_
- [What if the history diverges, and then converges again, before the latest tag (or root commit) is found?](#what-if-the-history-diverges-and-then-converges-again-before-the-latest-tag-or-root-commit-is-found) _(nothing bad)_
- [Why does MinVer fail with `LibGit2Sharp.NotFoundException`?](#why-does-minver-fail-with-libgit2sharpnotfoundexception) _(easy to fix)_

### Why not use GitVersion, Nerdbank.GitVersioning, or some other tool?

Expand Down Expand Up @@ -256,7 +256,7 @@ Yes! You can do this by using a specific tag prefix for each project. For exampl

Yes! [`MinVerVerbosity`](#options) can be set to `quiet`, `minimal` (default), `normal`, `detailed`, or `diagnostic`. These verbosity levels match those in MSBuild and therefore `dotnet build`, `dotnet pack`, etc. The default is `minimal`, which matches the default in MSBuild. At the `quiet` and `minimal` levels, you will see only warnings and errors. At the `normal` level you will see which commit is being used to calculate the version, and the calculated version. At the `detailed` level you will see how many commits were examined, which version tags were found but ignored, which version was calculated, etc. At the `diagnostic` level you will see how MinVer walks the commit history, in excruciating detail.

In a future version of MinVer, the verbosity level may be inherited from MSBuild, in which case `MinVerVerbosity` will be deprecated. Currently this is not possible due to technical restrictions related to [libgit2](https:/libgit2/libgit2).
In a future version of MinVer, the verbosity level may be inherited from MSBuild, in which case `MinVerVerbosity` will be deprecated.

### Can I use MinVer to version software which is not built using a .NET SDK style project?

Expand All @@ -272,16 +272,6 @@ The tag with the higher version is used.

MinVer will use the height on the first path followed where the history diverges. The paths are followed in the same order that the parents of the commit are stored in git. The first parent is the commit on the branch that was the current branch when the merge was performed. The remaining parents are stored in the order that their branches were specified in the merge command.

### Why does MinVer fail with `LibGit2Sharp.NotFoundException`?

You may see an exception of this form:

> Unhandled Exception: LibGit2Sharp.NotFoundException: object not found - no match for id (...)

This is because you are using a [shallow clone](https://www.git-scm.com/docs/git-clone#git-clone---depthltdepthgt). MinVer uses [libgit2](https:/libgit2/libgit2) to interrogate the repo and [libgit2 does not support shallow clones](https:/libgit2/libgit2/issues/3058). To resolve this problem, use a regular (deep) clone.

**Important:** By default, [Travis CI](https://travis-ci.org/) uses shallow clones with a depth of 50 commits. To build on Travis CI, [remove the `--depth` flag](https://docs.travis-ci.com/user/customizing-the-build#git-clone-depth).

---

<sub>[Tag](https://thenounproject.com/term/tag/938952) by [Ananth](https://thenounproject.com/ananthshas/) from [the Noun Project](https://thenounproject.com/).</sub>
11 changes: 10 additions & 1 deletion minver-cli/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ public static void ErrorInvalidVerbosity(string verbosity) =>

private static void Error(string message) => Message($"error : {message}");

private static void Message(string message) => Console.Error.WriteLine($"MinVer: {message}");
private static void Message(string message)
{
if (message.Contains('\r') || message.Contains('\n'))
{
var lines = message.Replace("\r\n", "\n").Split(new[] { '\r', '\n' });
message = string.Join($"{Environment.NewLine}MinVer: ", lines);
}

Console.Error.WriteLine($"MinVer: {message}");
}
}
}
Loading

0 comments on commit 15b5b1c

Please sign in to comment.