diff --git a/src/GitVersion.sln.DotSettings b/src/GitVersion.sln.DotSettings index 2f0c9b7de3..4cf1f38322 100644 --- a/src/GitVersion.sln.DotSettings +++ b/src/GitVersion.sln.DotSettings @@ -573,4 +573,5 @@ II.2.12 <HandlesEvent /> <data /> - <data><IncludeFilters /><ExcludeFilters /></data> \ No newline at end of file + <data><IncludeFilters /><ExcludeFilters /></data> + True \ No newline at end of file diff --git a/src/GitVersionCore.Tests/Core/GitVersionExecutorTests.cs b/src/GitVersionCore.Tests/Core/GitVersionExecutorTests.cs index 558c595ed4..cdbea67331 100644 --- a/src/GitVersionCore.Tests/Core/GitVersionExecutorTests.cs +++ b/src/GitVersionCore.Tests/Core/GitVersionExecutorTests.cs @@ -255,7 +255,7 @@ public void CacheFileIsMissing() var gitVersionOptions = new GitVersionOptions { WorkingDirectory = fixture.RepositoryPath }; fixture.Repository.MakeACommit(); - var gitVersionCalculator = GetGitVersionCalculator(gitVersionOptions, log, fixture.Repository); + var gitVersionCalculator = GetGitVersionCalculator(gitVersionOptions, log, new GitRepository(fixture.Repository)); gitVersionCalculator.CalculateVersionVariables(); @@ -299,7 +299,7 @@ public void ConfigChangeInvalidatesCache() CommitsSinceVersionSource: 19 CommitsSinceVersionSourcePadded: 0019 CommitDate: 2015-11-10 - UncommittedChanges: 0 + UncommittedChanges: 0 "; using var fixture = new EmptyRepositoryFixture(); @@ -483,7 +483,7 @@ public void DynamicRepositoriesShouldNotErrorWithFailedToFindGitDirectory() } }; - var gitVersionCalculator = GetGitVersionCalculator(gitVersionOptions, repository: fixture.Repository); + var gitVersionCalculator = GetGitVersionCalculator(gitVersionOptions, repository: new GitRepository(fixture.Repository)); gitPreparer.Prepare(); gitVersionCalculator.CalculateVersionVariables(); } @@ -566,7 +566,7 @@ public void CalculateVersionFromWorktreeHead() version.Sha.ShouldBe(commits.First().Sha); } - private IGitVersionCalculateTool GetGitVersionCalculator(GitVersionOptions gitVersionOptions, ILog logger = null, IRepository repository = null, IFileSystem fs = null) + private IGitVersionCalculateTool GetGitVersionCalculator(GitVersionOptions gitVersionOptions, ILog logger = null, IGitRepository repository = null, IFileSystem fs = null) { sp = GetServiceProvider(gitVersionOptions, logger, repository, fs); @@ -578,7 +578,7 @@ private IGitVersionCalculateTool GetGitVersionCalculator(GitVersionOptions gitVe return sp.GetService(); } - private static IServiceProvider GetServiceProvider(GitVersionOptions gitVersionOptions, ILog log = null, IRepository repository = null, IFileSystem fileSystem = null, IEnvironment environment = null) + private static IServiceProvider GetServiceProvider(GitVersionOptions gitVersionOptions, ILog log = null, IGitRepository repository = null, IFileSystem fileSystem = null, IEnvironment environment = null) { return ConfigureServices(services => { diff --git a/src/GitVersionCore.Tests/Core/RepositoryExtensionsTests.cs b/src/GitVersionCore.Tests/Core/RepositoryExtensionsTests.cs index 54214c03e9..b2af0c4207 100644 --- a/src/GitVersionCore.Tests/Core/RepositoryExtensionsTests.cs +++ b/src/GitVersionCore.Tests/Core/RepositoryExtensionsTests.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using GitVersion; using GitVersion.Extensions; using GitVersion.Logging; using GitVersionCore.Tests.Helpers; -using LibGit2Sharp; using NSubstitute; using NUnit.Framework; @@ -13,6 +13,57 @@ namespace GitVersionCore.Tests [TestFixture] public class RepositoryExtensionsTests : TestBase { + private static void EnsureLocalBranchExistsForCurrentBranch(IGitRepository repo, ILog log, Remote remote, string currentBranch) + { + if (log is null) + { + throw new ArgumentNullException(nameof(log)); + } + + if (remote is null) + { + throw new ArgumentNullException(nameof(remote)); + } + + if (string.IsNullOrEmpty(currentBranch)) return; + + var isRef = currentBranch.Contains("refs"); + var isBranch = currentBranch.Contains("refs/heads"); + var localCanonicalName = !isRef + ? "refs/heads/" + currentBranch + : isBranch + ? currentBranch + : currentBranch.Replace("refs/", "refs/heads/"); + + var repoTip = repo.Head.Tip; + + // We currently have the rep.Head of the *default* branch, now we need to look up the right one + var originCanonicalName = $"{remote.Name}/{currentBranch}"; + var originBranch = repo.Branches[originCanonicalName]; + if (originBranch != null) + { + repoTip = originBranch.Tip; + } + + var repoTipId = repoTip.Id; + + if (repo.Branches.All(b => !b.CanonicalName.IsEquivalentTo(localCanonicalName))) + { + log.Info(isBranch ? $"Creating local branch {localCanonicalName}" + : $"Creating local branch {localCanonicalName} pointing at {repoTipId}"); + repo.Refs.Add(localCanonicalName, repoTipId); + } + else + { + log.Info(isBranch ? $"Updating local branch {localCanonicalName} to point at {repoTip.Sha}" + : $"Updating local branch {localCanonicalName} to match ref {currentBranch}"); + var localRef = repo.Refs[localCanonicalName]; + repo.Refs.UpdateTarget(localRef, repoTipId); + } + + repo.Checkout(localCanonicalName); + } + [Test] public void EnsureLocalBranchExistsForCurrentBranch_CaseInsensitivelyMatchesBranches() { @@ -20,22 +71,20 @@ public void EnsureLocalBranchExistsForCurrentBranch_CaseInsensitivelyMatchesBran var repository = MockRepository(); var remote = MockRemote(repository); - repository.EnsureLocalBranchExistsForCurrentBranch(log, remote, "refs/heads/featurE/feat-test"); + EnsureLocalBranchExistsForCurrentBranch(repository, log, remote, "refs/heads/featurE/feat-test"); } - private IGitRepository MockRepository() + private static IGitRepository MockRepository() { var repository = Substitute.For(); - var commands = Substitute.For(); - repository.Commands.Returns(commands); return repository; } - private Remote MockRemote(IGitRepository repository) + private static Remote MockRemote(IGitRepository repository) { - var branches = new TestableBranchCollection(repository); + var branches = new TestableBranchCollection(); var tipId = new ObjectId("c6d8764d20ff16c0df14c73680e52b255b608926"); - var tip = new TestableCommit(repository, tipId); + var tip = new TestableCommit(tipId); var head = branches.Add("refs/heads/feature/feat-test", tip); var remote = new TesatbleRemote("origin"); var references = new TestableReferenceCollection(); @@ -49,66 +98,23 @@ private Remote MockRemote(IGitRepository repository) private class TestableBranchCollection : BranchCollection { - private readonly IRepository repository; - public TestableBranchCollection(IRepository repository) - { - this.repository = repository; - } - IDictionary branches = new Dictionary(); public override Branch this[string name] => - this.branches.ContainsKey(name) - ? this.branches[name] + branches.ContainsKey(name) + ? branches[name] : null; public override Branch Add(string name, Commit commit) { var branch = new TestableBranch(name, commit); - this.branches.Add(name, branch); + branches.Add(name, branch); return branch; } - public override Branch Add(string name, string committish) - { - var id = new ObjectId(committish); - var commit = new TestableCommit(this.repository, id); - return Add(name, commit); - } - - public override Branch Add(string name, Commit commit, bool allowOverwrite) - { - return Add(name, commit); - } - - public override Branch Add(string name, string committish, bool allowOverwrite) - { - return Add(name, committish); - } - public override IEnumerator GetEnumerator() { - return this.branches.Values.GetEnumerator(); - } - - public override void Remove(string name) - { - this.branches.Remove(name); - } - - public override void Remove(string name, bool isRemote) - { - this.branches.Remove(name); - } - - public override void Remove(Branch branch) - { - this.branches.Remove(branch.CanonicalName); - } - - public override Branch Update(Branch branch, params Action[] actions) - { - return base.Update(branch, actions); + return branches.Values.GetEnumerator(); } } @@ -123,23 +129,20 @@ public TestableBranch(string canonicalName, Commit tip) this.canonicalName = canonicalName; } - public override string CanonicalName => this.canonicalName; - public override Commit Tip => this.tip; + public override string CanonicalName => canonicalName; + public override Commit Tip => tip; } - private class TestableCommit : Commit, IBelongToARepository + private class TestableCommit : Commit { - private IRepository repository; private ObjectId id; - public TestableCommit(IRepository repository, ObjectId id) + public TestableCommit(ObjectId id) { - this.repository = repository; this.id = id; } - public override ObjectId Id => this.id; - public IRepository Repository => this.repository; + public override ObjectId Id => id; } private class TesatbleRemote : Remote @@ -151,46 +154,32 @@ public TesatbleRemote(string name) this.name = name; } - public override string Name => this.name; + public override string Name => name; } private class TestableReferenceCollection : ReferenceCollection { Reference reference; - - public override DirectReference Add(string name, ObjectId targetId) - { - throw new InvalidOperationException("Update should be invoked when case-insensitively comparing branches."); - } - public override Reference Add(string name, string canonicalRefNameOrObjectish) { - return this.reference = new TestableReference(canonicalRefNameOrObjectish); + return reference = new TestableReference(canonicalRefNameOrObjectish); } - public override Reference UpdateTarget(Reference directRef, ObjectId targetId) { - return this.reference; + return reference; } - - public override Reference this[string name] => this.reference; + public override Reference this[string name] => reference; } private class TestableReference : Reference { - private readonly string canonicalName; public TestableReference(string canonicalName) { - this.canonicalName = canonicalName; + this.CanonicalName = canonicalName; } - public override string CanonicalName => this.canonicalName; - - public override DirectReference ResolveToDirectReference() - { - throw new NotImplementedException(); - } + public override string CanonicalName { get; } } } } diff --git a/src/GitVersionCore.Tests/Core/RepositoryMetadataProviderTests.cs b/src/GitVersionCore.Tests/Core/RepositoryMetadataProviderTests.cs index de258ef649..e225cbeb7b 100644 --- a/src/GitVersionCore.Tests/Core/RepositoryMetadataProviderTests.cs +++ b/src/GitVersionCore.Tests/Core/RepositoryMetadataProviderTests.cs @@ -1,7 +1,6 @@ using System; using GitTools.Testing; using GitVersion; -using GitVersion.Extensions; using GitVersion.Logging; using GitVersionCore.Tests.Helpers; using GitVersionCore.Tests.IntegrationTests; @@ -37,7 +36,8 @@ public void FindsCorrectMergeBaseForForwardMerge() using var fixture = new EmptyRepositoryFixture(); fixture.MakeACommit("initial"); fixture.BranchTo("develop"); - var expectedReleaseMergeBase = fixture.Repository.Head.Tip; + var fixtureRepository = new GitRepository(fixture.Repository); + var expectedReleaseMergeBase = fixtureRepository.Head.Tip; // Create release from develop fixture.BranchTo("release-2.0.0"); @@ -45,7 +45,7 @@ public void FindsCorrectMergeBaseForForwardMerge() // Make some commits on release fixture.MakeACommit("release 1"); fixture.MakeACommit("release 2"); - var expectedDevelopMergeBase = fixture.Repository.Head.Tip; + var expectedDevelopMergeBase = fixtureRepository.Head.Tip; // First forward merge release to develop fixture.Checkout("develop"); @@ -61,15 +61,15 @@ public void FindsCorrectMergeBaseForForwardMerge() // Checkout to release (no new commits) fixture.Checkout("release-2.0.0"); - var develop = fixture.Repository.FindBranch("develop"); - var release = fixture.Repository.FindBranch("release-2.0.0"); - var gitRepoMetadataProvider = new RepositoryMetadataProvider(log, fixture.Repository); + var develop = fixtureRepository.FindBranch("develop"); + var release = fixtureRepository.FindBranch("release-2.0.0"); + var gitRepoMetadataProvider = new RepositoryMetadataProvider(log, fixtureRepository); var releaseBranchMergeBase = gitRepoMetadataProvider.FindMergeBase(release, develop); var developMergeBase = gitRepoMetadataProvider.FindMergeBase(develop, release); - fixture.Repository.DumpGraph(Console.WriteLine); + fixtureRepository.DumpGraph(Console.WriteLine); releaseBranchMergeBase.ShouldBe(expectedReleaseMergeBase); developMergeBase.ShouldBe(expectedDevelopMergeBase); @@ -90,7 +90,8 @@ public void FindsCorrectMergeBaseForForwardMergeMovesOn() using var fixture = new EmptyRepositoryFixture(); fixture.MakeACommit("initial"); fixture.BranchTo("develop"); - var expectedReleaseMergeBase = fixture.Repository.Head.Tip; + var fixtureRepository = new GitRepository(fixture.Repository); + var expectedReleaseMergeBase = fixtureRepository.Head.Tip; // Create release from develop fixture.BranchTo("release-2.0.0"); @@ -98,7 +99,7 @@ public void FindsCorrectMergeBaseForForwardMergeMovesOn() // Make some commits on release fixture.MakeACommit("release 1"); fixture.MakeACommit("release 2"); - var expectedDevelopMergeBase = fixture.Repository.Head.Tip; + var expectedDevelopMergeBase = fixtureRepository.Head.Tip; // First forward merge release to develop fixture.Checkout("develop"); @@ -116,15 +117,15 @@ public void FindsCorrectMergeBaseForForwardMergeMovesOn() // Checkout to release (no new commits) fixture.Checkout("release-2.0.0"); - var develop = fixture.Repository.FindBranch("develop"); - var release = fixture.Repository.FindBranch("release-2.0.0"); - var gitRepoMetadataProvider = new RepositoryMetadataProvider(log, fixture.Repository); + var develop = fixtureRepository.FindBranch("develop"); + var release = fixtureRepository.FindBranch("release-2.0.0"); + var gitRepoMetadataProvider = new RepositoryMetadataProvider(log, fixtureRepository); var releaseBranchMergeBase = gitRepoMetadataProvider.FindMergeBase(release, develop); var developMergeBase = gitRepoMetadataProvider.FindMergeBase(develop, release); - fixture.Repository.DumpGraph(Console.WriteLine); + fixtureRepository.DumpGraph(Console.WriteLine); releaseBranchMergeBase.ShouldBe(expectedReleaseMergeBase); developMergeBase.ShouldBe(expectedDevelopMergeBase); @@ -150,7 +151,8 @@ public void FindsCorrectMergeBaseForMultipleForwardMerges() using var fixture = new EmptyRepositoryFixture(); fixture.MakeACommit("initial"); fixture.BranchTo("develop"); - var expectedReleaseMergeBase = fixture.Repository.Head.Tip; + var fixtureRepository = new GitRepository(fixture.Repository); + var expectedReleaseMergeBase = fixtureRepository.Head.Tip; // Create release from develop fixture.BranchTo("release-2.0.0"); @@ -165,22 +167,22 @@ public void FindsCorrectMergeBaseForMultipleForwardMerges() // Make some new commit on release fixture.Checkout("release-2.0.0"); - fixture.Repository.MakeACommit("release 3 - after first merge"); + fixture.MakeACommit("release 3 - after first merge"); // Make new commit on develop fixture.Checkout("develop"); // Checkout to release (no new commits) fixture.Checkout("release-2.0.0"); fixture.Checkout("develop"); - fixture.Repository.MakeACommit("develop after merge"); + fixture.MakeACommit("develop after merge"); // Checkout to release (no new commits) fixture.Checkout("release-2.0.0"); // Make some new commit on release - fixture.Repository.MakeACommit("release 4"); - fixture.Repository.MakeACommit("release 5"); - var expectedDevelopMergeBase = fixture.Repository.Head.Tip; + fixture.MakeACommit("release 4"); + fixture.MakeACommit("release 5"); + var expectedDevelopMergeBase = fixtureRepository.Head.Tip; // Second merge release to develop fixture.Checkout("develop"); @@ -189,16 +191,16 @@ public void FindsCorrectMergeBaseForMultipleForwardMerges() // Checkout to release (no new commits) fixture.Checkout("release-2.0.0"); - var develop = fixture.Repository.FindBranch("develop"); - var release = fixture.Repository.FindBranch("release-2.0.0"); + var develop = fixtureRepository.FindBranch("develop"); + var release = fixtureRepository.FindBranch("release-2.0.0"); - var gitRepoMetadataProvider = new RepositoryMetadataProvider(log, fixture.Repository); + var gitRepoMetadataProvider = new RepositoryMetadataProvider(log, fixtureRepository); var releaseBranchMergeBase = gitRepoMetadataProvider.FindMergeBase(release, develop); var developMergeBase = gitRepoMetadataProvider.FindMergeBase(develop, release); - fixture.Repository.DumpGraph(Console.WriteLine); + fixtureRepository.DumpGraph(Console.WriteLine); releaseBranchMergeBase.ShouldBe(expectedReleaseMergeBase); developMergeBase.ShouldBe(expectedDevelopMergeBase); diff --git a/src/GitVersionCore.Tests/Extensions/GitToolsTestingExtensions.cs b/src/GitVersionCore.Tests/Extensions/GitToolsTestingExtensions.cs index ab1697f3fe..e9df275d33 100644 --- a/src/GitVersionCore.Tests/Extensions/GitToolsTestingExtensions.cs +++ b/src/GitVersionCore.Tests/Extensions/GitToolsTestingExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using GitTools.Testing; using GitVersion; using GitVersion.BuildAgents; @@ -13,17 +14,25 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Shouldly; +using Branch = GitVersion.Branch; namespace GitVersionCore.Tests { public static class GitToolsTestingExtensions { + public static Branch FindBranch(this IGitRepository repository, string branchName) + { + return repository.Branches.FirstOrDefault(x => x.NameWithoutRemote() == branchName); + } + + public static void DumpGraph(this IGitRepository repository, Action writer = null, int? maxCommits = null) + { + GitExtensions.DumpGraph(repository.Path, writer, maxCommits); + } + public static VersionVariables GetVersion(this RepositoryFixtureBase fixture, Config configuration = null, IRepository repository = null, string commitId = null, bool onlyTrackedBranches = true, string branch = null) { - if (configuration == null) - { - configuration = new ConfigurationBuilder().Build(); - } + configuration ??= new ConfigurationBuilder().Build(); repository ??= fixture.Repository; @@ -52,15 +61,15 @@ public static VersionVariables GetVersion(this RepositoryFixtureBase fixture, Co try { - var executeGitVersion = nextVersionCalculator.FindVersion(); - var variables = variableProvider.GetVariablesFor(executeGitVersion, context.Configuration, context.IsCurrentCommitTagged); + var semanticVersion = nextVersionCalculator.FindVersion(); + var variables = variableProvider.GetVariablesFor(semanticVersion, context.Configuration, context.IsCurrentCommitTagged); return variables; } catch (Exception) { Console.WriteLine("Test failing, dumping repository graph"); - repository.DumpGraph(); + new GitRepository(repository).DumpGraph(); throw; } } @@ -87,7 +96,7 @@ public static void AssertFullSemver(this RepositoryFixtureBase fixture, string f } catch (Exception) { - (repository ?? fixture.Repository).DumpGraph(); + new GitRepository(repository ?? fixture.Repository).DumpGraph(); throw; } if (commitId == null) diff --git a/src/GitVersionCore.Tests/Helpers/GitVersionContextBuilder.cs b/src/GitVersionCore.Tests/Helpers/GitVersionContextBuilder.cs index 89ad49c72c..3ae3a52aae 100644 --- a/src/GitVersionCore.Tests/Helpers/GitVersionContextBuilder.cs +++ b/src/GitVersionCore.Tests/Helpers/GitVersionContextBuilder.cs @@ -6,7 +6,6 @@ using GitVersion.Model.Configuration; using GitVersionCore.Tests.Helpers; using GitVersionCore.Tests.Mocks; -using LibGit2Sharp; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -14,12 +13,12 @@ namespace GitVersionCore.Tests { public class GitVersionContextBuilder { - private IRepository repository; + private IGitRepository repository; private Config configuration; public IServiceProvider ServicesProvider; private Action overrideServices; - public GitVersionContextBuilder WithRepository(IRepository gitRepository) + public GitVersionContextBuilder WithRepository(IGitRepository gitRepository) { repository = gitRepository; return this; @@ -95,7 +94,7 @@ public void Build() }); } - private static IRepository CreateRepository() + private static IGitRepository CreateRepository() { var mockBranch = new MockBranch("master") { new MockCommit { CommitterEx = Generate.SignatureNow() } }; var mockRepository = new MockRepository diff --git a/src/GitVersionCore.Tests/Helpers/TestBase.cs b/src/GitVersionCore.Tests/Helpers/TestBase.cs index 57b0fa1e94..46785e5034 100644 --- a/src/GitVersionCore.Tests/Helpers/TestBase.cs +++ b/src/GitVersionCore.Tests/Helpers/TestBase.cs @@ -3,7 +3,6 @@ using GitVersion.Configuration; using GitVersion.Extensions; using GitVersion.Model.Configuration; -using LibGit2Sharp; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -25,7 +24,7 @@ protected static IServiceProvider ConfigureServices(Action o return services.BuildServiceProvider(); } - protected static IServiceProvider BuildServiceProvider(string workingDirectory, IRepository repository, string branch, Config config = null) + protected static IServiceProvider BuildServiceProvider(string workingDirectory, IGitRepository repository, string branch, Config config = null) { config ??= new ConfigurationBuilder().Build(); var options = Options.Create(new GitVersionOptions diff --git a/src/GitVersionCore.Tests/IntegrationTests/DocumentationSamples.cs b/src/GitVersionCore.Tests/IntegrationTests/DocumentationSamples.cs index a63d230d24..442d85e7aa 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/DocumentationSamples.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/DocumentationSamples.cs @@ -1,6 +1,6 @@ using System; using GitTools.Testing; -using GitVersion.Extensions; +using GitVersion; using GitVersionCore.Tests.Helpers; using NUnit.Framework; using Shouldly; @@ -402,7 +402,8 @@ public void GitHubFlowMajorRelease() fixture.ApplyTag("2.0.0"); fixture.AssertFullSemver("2.0.0"); fixture.MakeACommit(); - fixture.Repository.DumpGraph(); + var fixtureRepository = new GitRepository(fixture.Repository); + fixtureRepository.DumpGraph(); fixture.AssertFullSemver("2.0.1+1"); } } diff --git a/src/GitVersionCore.Tests/IntegrationTests/IgnoreBeforeScenarios.cs b/src/GitVersionCore.Tests/IntegrationTests/IgnoreBeforeScenarios.cs index 52c2a6e4b8..ebdb247124 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/IgnoreBeforeScenarios.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/IgnoreBeforeScenarios.cs @@ -1,4 +1,5 @@ using GitTools.Testing; +using GitVersion; using GitVersion.Configuration; using GitVersion.Extensions; using GitVersion.Model.Configuration; @@ -14,7 +15,7 @@ public class IgnoreBeforeScenarios : TestBase public void ShouldFallbackToBaseVersionWhenAllCommitsAreIgnored() { using var fixture = new EmptyRepositoryFixture(); - var commit = fixture.Repository.MakeACommit(); + var commit = (Commit)fixture.Repository.MakeACommit(); var config = new ConfigurationBuilder() .Add(new Config diff --git a/src/GitVersionCore.Tests/IntegrationTests/PullRequestScenarios.cs b/src/GitVersionCore.Tests/IntegrationTests/PullRequestScenarios.cs index 0a48c98ab3..da5d3a0b61 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/PullRequestScenarios.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/PullRequestScenarios.cs @@ -1,5 +1,5 @@ using GitTools.Testing; -using GitVersion.Extensions; +using GitVersion; using GitVersionCore.Tests.Helpers; using LibGit2Sharp; using NUnit.Framework; @@ -19,7 +19,7 @@ public void CanCalculatePullRequestChanges() fixture.Repository.CreatePullRequestRef("feature/Foo", "master", normalise: true); - fixture.Repository.DumpGraph(); + new GitRepository(fixture.Repository).DumpGraph(); fixture.AssertFullSemver("0.1.1-PullRequest0002.2"); } @@ -35,7 +35,7 @@ public void CanCalculatePullRequestChangesInheritingConfig() fixture.Repository.CreatePullRequestRef("feature/Foo", "develop", 44, normalise: true); - fixture.Repository.DumpGraph(); + new GitRepository(fixture.Repository).DumpGraph(); fixture.AssertFullSemver("0.2.0-PullRequest0044.3"); } @@ -50,7 +50,7 @@ public void CanCalculatePullRequestChangesFromRemoteRepo() fixture.Repository.CreatePullRequestRef("feature/Foo", "master", normalise: true); - fixture.Repository.DumpGraph(); + new GitRepository(fixture.Repository).DumpGraph(); fixture.AssertFullSemver("0.1.1-PullRequest0002.2"); } diff --git a/src/GitVersionCore.Tests/IntegrationTests/ReleaseBranchScenarios.cs b/src/GitVersionCore.Tests/IntegrationTests/ReleaseBranchScenarios.cs index c20f8f01b9..5a58ac855f 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/ReleaseBranchScenarios.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/ReleaseBranchScenarios.cs @@ -454,7 +454,7 @@ public void CommitBeetweenMergeReleaseToDevelopShouldNotResetCount() fixture.Repository.MakeACommit(); fixture.AssertFullSemver("2.0.0-beta.2", config); - // Merge release to develop - emulate commit beetween other person release commit push and this commit merge to develop + // Merge release to develop - emulate commit between other person release commit push and this commit merge to develop Commands.Checkout(fixture.Repository, "develop"); fixture.Repository.Merge(commit1, Generate.SignatureNow(), new MergeOptions { FastForwardStrategy = FastForwardStrategy.NoFastForward }); fixture.Repository.MergeNoFF("release-2.0.0", Generate.SignatureNow()); diff --git a/src/GitVersionCore.Tests/Mocks/MockBranch.cs b/src/GitVersionCore.Tests/Mocks/MockBranch.cs index 5e412c3802..cf1465e61e 100644 --- a/src/GitVersionCore.Tests/Mocks/MockBranch.cs +++ b/src/GitVersionCore.Tests/Mocks/MockBranch.cs @@ -1,7 +1,9 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using LibGit2Sharp; +using GitVersion; +using Branch = GitVersion.Branch; +using Commit = GitVersion.Commit; namespace GitVersionCore.Tests.Mocks { @@ -23,12 +25,13 @@ public MockBranch() } - private readonly MockCommitLog commits = new MockCommitLog(); + private readonly MockCommitCollection commits = new MockCommitCollection(); private readonly string friendlyName; public override string FriendlyName => friendlyName; - public override ICommitLog Commits => commits; + public override CommitCollection Commits => commits; public override Commit Tip => commits.First(); public override bool IsTracking => true; + public override bool IsRemote => false; public override string CanonicalName { get; } diff --git a/src/GitVersionCore.Tests/Mocks/MockBranchCollection.cs b/src/GitVersionCore.Tests/Mocks/MockBranchCollection.cs index c4f679068b..60e726c509 100644 --- a/src/GitVersionCore.Tests/Mocks/MockBranchCollection.cs +++ b/src/GitVersionCore.Tests/Mocks/MockBranchCollection.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; using System.Linq; -using LibGit2Sharp; +using Branch = GitVersion.Branch; +using BranchCollection = GitVersion.BranchCollection; namespace GitVersionCore.Tests.Mocks { - public class MockBranchCollection : BranchCollection, ICollection + public class MockBranchCollection : BranchCollection { public List Branches = new List(); @@ -22,32 +23,5 @@ public void Add(Branch item) { Branches.Add(item); } - - public void Clear() - { - Branches.Clear(); - } - - public bool Contains(Branch item) - { - return Branches.Contains(item); - } - - public void CopyTo(Branch[] array, int arrayIndex) - { - Branches.CopyTo(array, arrayIndex); - } - - public override void Remove(Branch item) - { - Branches.Remove(item); - } - bool ICollection.Remove(Branch item) - { - return Branches.Remove(item); - } - - public int Count => Branches.Count; - public bool IsReadOnly => false; } } diff --git a/src/GitVersionCore.Tests/Mocks/MockCommit.cs b/src/GitVersionCore.Tests/Mocks/MockCommit.cs index e6dba7f17c..3574cdc048 100644 --- a/src/GitVersionCore.Tests/Mocks/MockCommit.cs +++ b/src/GitVersionCore.Tests/Mocks/MockCommit.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Diagnostics; using LibGit2Sharp; +using Commit = GitVersion.Commit; +using ObjectId = GitVersion.ObjectId; namespace GitVersionCore.Tests.Mocks { @@ -25,7 +27,7 @@ public MockCommit(ObjectId id = null) public override string Message => MessageEx; public Signature CommitterEx; - public override Signature Committer => CommitterEx; + public override DateTimeOffset? CommitterWhen => CommitterEx.When; private readonly ObjectId idEx; public override ObjectId Id => idEx; diff --git a/src/GitVersionCore.Tests/Mocks/MockCommitLog.cs b/src/GitVersionCore.Tests/Mocks/MockCommitCollection.cs similarity index 76% rename from src/GitVersionCore.Tests/Mocks/MockCommitLog.cs rename to src/GitVersionCore.Tests/Mocks/MockCommitCollection.cs index 9cb5dea91f..e6f8481aee 100644 --- a/src/GitVersionCore.Tests/Mocks/MockCommitLog.cs +++ b/src/GitVersionCore.Tests/Mocks/MockCommitCollection.cs @@ -1,15 +1,14 @@ -using System.Collections; using System.Collections.Generic; using System.Linq; -using LibGit2Sharp; +using GitVersion; namespace GitVersionCore.Tests.Mocks { - public class MockCommitLog : ICommitLog, ICollection + public class MockCommitCollection : CommitCollection { - public List Commits = new List(); + private List Commits = new List(); - public IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { if (SortedBy == CommitSortStrategies.Reverse) return Commits.GetEnumerator(); @@ -17,11 +16,6 @@ public IEnumerator GetEnumerator() return Enumerable.Reverse(Commits).GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - public CommitSortStrategies SortedBy { get; set; } public void Add(Commit item) { @@ -33,7 +27,6 @@ public void Clear() Commits.Clear(); } - public bool Contains(Commit item) { return Commits.Contains(item); @@ -52,5 +45,11 @@ public bool Remove(Commit item) public int Count => Commits.Count; public bool IsReadOnly => false; + + + public override CommitCollection QueryBy(CommitFilter commitFilter) + { + return this; + } } } diff --git a/src/GitVersionCore.Tests/Mocks/MockMergeCommit.cs b/src/GitVersionCore.Tests/Mocks/MockMergeCommit.cs deleted file mode 100644 index 759f4f79c2..0000000000 --- a/src/GitVersionCore.Tests/Mocks/MockMergeCommit.cs +++ /dev/null @@ -1,12 +0,0 @@ -using LibGit2Sharp; - -namespace GitVersionCore.Tests.Mocks -{ - public class MockMergeCommit : MockCommit - { - public MockMergeCommit(ObjectId id = null) : base(id) - { - ParentsEx.Add(null); - } - } -} diff --git a/src/GitVersionCore.Tests/Mocks/MockQueryableCommitLog.cs b/src/GitVersionCore.Tests/Mocks/MockQueryableCommitLog.cs deleted file mode 100644 index 0529d132c7..0000000000 --- a/src/GitVersionCore.Tests/Mocks/MockQueryableCommitLog.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using LibGit2Sharp; - -namespace GitVersionCore.Tests.Mocks -{ - public class MockQueryableCommitLog : IQueryableCommitLog - { - private readonly ICommitLog commits; - - public MockQueryableCommitLog(ICommitLog commits) - { - this.commits = commits; - } - - public IEnumerator GetEnumerator() - { - return commits.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public CommitSortStrategies SortedBy => throw new NotImplementedException(); - - public ICommitLog QueryBy(CommitFilter filter) - { - return this; - } - - public IEnumerable QueryBy(string path) - { - throw new NotImplementedException(); - } - - public Commit FindMergeBase(Commit first, Commit second) - { - return null; - } - - public Commit FindMergeBase(IEnumerable commits, MergeBaseFindingStrategy strategy) - { - throw new NotImplementedException(); - } - - public IEnumerable QueryBy(string path, CommitFilter filter) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/GitVersionCore.Tests/Mocks/MockReferenceCollection.cs b/src/GitVersionCore.Tests/Mocks/MockReferenceCollection.cs index aac90a398e..b2877d6976 100644 --- a/src/GitVersionCore.Tests/Mocks/MockReferenceCollection.cs +++ b/src/GitVersionCore.Tests/Mocks/MockReferenceCollection.cs @@ -1,59 +1,9 @@ -using System.Collections; -using System.Collections.Generic; -using LibGit2Sharp; +using GitVersion; namespace GitVersionCore.Tests.Mocks { - public class MockReferenceCollection : ReferenceCollection, ICollection + public class MockReferenceCollection : ReferenceCollection { - public override ReflogCollection Log(string canonicalName) - { - return new MockReflogCollection - { - Commits = Commits - }; - } - - public List Commits = new List(); - - public new IEnumerator GetEnumerator() - { - return Commits.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public void Add(Commit item) - { - Commits.Add(item); - } - - public void Clear() - { - Commits.Clear(); - } - - public bool Contains(Commit item) - { - return Commits.Contains(item); - } - - public void CopyTo(Commit[] array, int arrayIndex) - { - Commits.CopyTo(array, arrayIndex); - } - - public bool Remove(Commit item) - { - return Commits.Remove(item); - } - - public int Count => Commits.Count; - - public bool IsReadOnly => false; } } diff --git a/src/GitVersionCore.Tests/Mocks/MockReflogCollection.cs b/src/GitVersionCore.Tests/Mocks/MockReflogCollection.cs index 91baf80d68..caf2986355 100644 --- a/src/GitVersionCore.Tests/Mocks/MockReflogCollection.cs +++ b/src/GitVersionCore.Tests/Mocks/MockReflogCollection.cs @@ -1,50 +1,19 @@ -using System.Collections; using System.Collections.Generic; using LibGit2Sharp; namespace GitVersionCore.Tests.Mocks { - public class MockReflogCollection : ReflogCollection, ICollection + public class MockReflogCollection : ReflogCollection { public List Commits = new List(); - public new IEnumerator GetEnumerator() { return Commits.GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - public void Add(Commit item) { Commits.Add(item); } - - public void Clear() - { - Commits.Clear(); - } - - public bool Contains(Commit item) - { - return Commits.Contains(item); - } - - public void CopyTo(Commit[] array, int arrayIndex) - { - Commits.CopyTo(array, arrayIndex); - } - - public bool Remove(Commit item) - { - return Commits.Remove(item); - } - - public int Count => Commits.Count; - - public bool IsReadOnly => false; } } diff --git a/src/GitVersionCore.Tests/Mocks/MockRepository.cs b/src/GitVersionCore.Tests/Mocks/MockRepository.cs index 3327c25e38..1dc356dc71 100644 --- a/src/GitVersionCore.Tests/Mocks/MockRepository.cs +++ b/src/GitVersionCore.Tests/Mocks/MockRepository.cs @@ -1,225 +1,91 @@ using System; using System.Collections.Generic; -using LibGit2Sharp; -using Index = LibGit2Sharp.Index; - +using System.Linq; +using GitVersion; +using GitVersion.Logging; namespace GitVersionCore.Tests.Mocks { - public class MockRepository : IRepository + public class MockRepository : IGitRepository { - private IQueryableCommitLog commits; - + private CommitCollection commits; public MockRepository() { Tags = new MockTagCollection(); Refs = new MockReferenceCollection(); } + public Branch Head { get; set; } + public ReferenceCollection Refs { get; set; } - public void Dispose() - { - throw new NotImplementedException(); - } - - public Branch Checkout(Branch branch, CheckoutOptions options, Signature signature = null) - { - throw new NotImplementedException(); - } - - public Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, Signature signature = null) - { - throw new NotImplementedException(); - } - - public Branch Checkout(Commit commit, CheckoutOptions options, Signature signature = null) - { - throw new NotImplementedException(); - } - - public void CheckoutPaths(string committishOrBranchSpec, IEnumerable paths, CheckoutOptions checkoutOptions = null) - { - throw new NotImplementedException(); - } - - public MergeResult MergeFetchedRefs(Signature merger, MergeOptions options) - { - throw new NotImplementedException(); - } - - public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options = null) - { - throw new NotImplementedException(); - } - - public GitObject Lookup(ObjectId id) - { - throw new NotImplementedException(); - } - - public GitObject Lookup(string objectish) - { - throw new NotImplementedException(); - } - - public GitObject Lookup(ObjectId id, ObjectType type) - { - throw new NotImplementedException(); - } - - public GitObject Lookup(string objectish, ObjectType type) - { - return new MockCommit(); - } - - public Commit Commit(string message, Signature author, Signature committer, CommitOptions options = null) - { - throw new NotImplementedException(); - } - - public void Reset(ResetMode resetMode, Commit commit) - { - throw new NotImplementedException(); - } - - public void Reset(ResetMode resetMode, Commit commit, CheckoutOptions options) - { - throw new NotImplementedException(); - } - - public Commit Commit(string message, Signature author, Signature committer, bool amendPreviousCommit = false) - { - throw new NotImplementedException(); - } - - public void Reset(ResetMode resetMode, Commit commit, Signature signature = null, string logMessage = null) - { - throw new NotImplementedException(); - } - - public void Reset(Commit commit, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null) - { - throw new NotImplementedException(); - } - - public void RemoveUntrackedFiles() - { - throw new NotImplementedException(); - } - - public RevertResult Revert(Commit commit, Signature reverter, RevertOptions options = null) - { - throw new NotImplementedException(); - } - - public MergeResult Merge(Commit commit, Signature merger, MergeOptions options = null) - { - throw new NotImplementedException(); - } - - public MergeResult Merge(Branch branch, Signature merger, MergeOptions options = null) - { - throw new NotImplementedException(); - } - - public MergeResult Merge(string committish, Signature merger, MergeOptions options = null) - { - throw new NotImplementedException(); - } - - public BlameHunkCollection Blame(string path, BlameOptions options = null) + public CommitCollection Commits { - throw new NotImplementedException(); + get => commits ?? Head.Commits; + set => commits = value; } - public void Stage(string path, StageOptions stageOptions) + public BranchCollection Branches { get; set; } + public TagCollection Tags { get; set; } + public string Path { get; } + public string WorkingDirectory { get; } + public bool IsHeadDetached { get; } + public int GetNumberOfUncommittedChanges() => 0; + public Commit FindMergeBase(Commit commit, Commit otherCommit) => throw new NotImplementedException(); + public string ShortenObjectId(Commit commit) => throw new NotImplementedException(); + public void CreateBranchForPullRequestBranch(ILog log, AuthenticationInfo auth) => throw new NotImplementedException(); + public bool GitRepoHasMatchingRemote(string targetUrl) => throw new NotImplementedException(); + public void CleanupDuplicateOrigin(string defaultRemoteName) => throw new NotImplementedException(); + public bool GetMatchingCommitBranch(Commit baseVersionSource, Branch branch, Commit firstMatchingCommit) { throw new NotImplementedException(); } - - public void Stage(IEnumerable paths, StageOptions stageOptions) + public IEnumerable GetCommitsReacheableFrom(Commit commit, Branch branch) { throw new NotImplementedException(); } - - public void Unstage(string path, ExplicitPathsOptions explicitPathsOptions) + public List GetCommitsReacheableFromHead(Commit headCommit) { - throw new NotImplementedException(); - } + var filter = new CommitFilter + { + IncludeReachableFrom = headCommit, + SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Reverse + }; - public void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions) - { - throw new NotImplementedException(); - } + var commitCollection = Commits.QueryBy(filter); - public void Move(string sourcePath, string destinationPath) - { - throw new NotImplementedException(); + return commitCollection.ToList(); } - - public void Move(IEnumerable sourcePaths, IEnumerable destinationPaths) + public Commit GetForwardMerge(Commit commitToFindCommonBase, Commit findMergeBase) { throw new NotImplementedException(); } - - public void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions) + public IEnumerable GetMergeBaseCommits(Commit mergeCommit, Commit mergedHead, Commit findMergeBase) { throw new NotImplementedException(); } - - public void Remove(IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions) + public Commit GetBaseVersionSource(Commit currentBranchTip) { throw new NotImplementedException(); } - - public FileStatus RetrieveStatus(string filePath) + public List GetMainlineCommitLog(Commit baseVersionSource, Commit mainlineTip) { throw new NotImplementedException(); } - - public RepositoryStatus RetrieveStatus(StatusOptions options) + public CommitCollection GetCommitLog(Commit baseVersionSource, Commit currentCommit) { throw new NotImplementedException(); } - - public string Describe(Commit commit, DescribeOptions options) + public void Checkout(string committishOrBranchSpec) { throw new NotImplementedException(); } - - public void Checkout(Tree tree, IEnumerable paths, CheckoutOptions opts) + public void Checkout(Branch branch) { throw new NotImplementedException(); } - - public void RevParse(string revision, out Reference reference, out GitObject obj) + public void Fetch(string remote, IEnumerable refspecs, AuthenticationInfo auth, string logMessage) { throw new NotImplementedException(); } - - public Branch Head { get; set; } - public LibGit2Sharp.Configuration Config { get; set; } - public Index Index { get; set; } - public ReferenceCollection Refs { get; set; } - - public IQueryableCommitLog Commits - { - get => commits ?? new MockQueryableCommitLog(Head.Commits); - set => commits = value; - } - - public BranchCollection Branches { get; set; } - public TagCollection Tags { get; set; } - public RepositoryInformation Info { get; set; } - public Diff Diff { get; set; } - public ObjectDatabase ObjectDatabase { get; set; } - public NoteCollection Notes { get; set; } - public SubmoduleCollection Submodules { get; set; } - public WorktreeCollection Worktrees { get; set; } - public Rebase Rebase { get; private set; } - - public Ignore Ignore => throw new NotImplementedException(); - - public Network Network { get; set; } - - public StashCollection Stashes => throw new NotImplementedException(); + public Remote EnsureOnlyOneRemoteIsDefined(ILog log) => throw new NotImplementedException(); + public void Dispose() => throw new NotImplementedException(); } } diff --git a/src/GitVersionCore.Tests/Mocks/MockTag.cs b/src/GitVersionCore.Tests/Mocks/MockTag.cs index 86e6cadd03..e9874213b4 100644 --- a/src/GitVersionCore.Tests/Mocks/MockTag.cs +++ b/src/GitVersionCore.Tests/Mocks/MockTag.cs @@ -1,15 +1,14 @@ using LibGit2Sharp; +using Tag = GitVersion.Tag; namespace GitVersionCore.Tests.Mocks { public class MockTag : Tag { - public string NameEx; public override string FriendlyName => NameEx; public GitObject TargetEx; - public override GitObject Target => TargetEx; public TagAnnotation AnnotationEx; public MockTag() { } @@ -19,7 +18,5 @@ public MockTag(string name, GitObject target) NameEx = name; TargetEx = target; } - - public override TagAnnotation Annotation => AnnotationEx; } } diff --git a/src/GitVersionCore.Tests/Mocks/MockTagAnnotation.cs b/src/GitVersionCore.Tests/Mocks/MockTagAnnotation.cs deleted file mode 100644 index 3817ca0f62..0000000000 --- a/src/GitVersionCore.Tests/Mocks/MockTagAnnotation.cs +++ /dev/null @@ -1,14 +0,0 @@ -using LibGit2Sharp; - -namespace GitVersionCore.Tests.Mocks -{ - public class MockTagAnnotation : TagAnnotation - { - - public Signature TaggerEx; - public override Signature Tagger => TaggerEx; - - public GitObject TargetEx; - public override GitObject Target => TargetEx; - } -} diff --git a/src/GitVersionCore.Tests/Mocks/MockTagCollection.cs b/src/GitVersionCore.Tests/Mocks/MockTagCollection.cs index 5d5ef0b213..1c187b338c 100644 --- a/src/GitVersionCore.Tests/Mocks/MockTagCollection.cs +++ b/src/GitVersionCore.Tests/Mocks/MockTagCollection.cs @@ -1,59 +1,18 @@ -using System.Collections; using System.Collections.Generic; -using LibGit2Sharp; +using GitVersion; namespace GitVersionCore.Tests.Mocks { - public class MockTagCollection : TagCollection, ICollection + public class MockTagCollection : TagCollection { - - public List Tags = new List(); + private List Tags = new List(); public override IEnumerator GetEnumerator() { return Tags.GetEnumerator(); } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - public void Add(Tag item) { Tags.Add(item); } - - public void Clear() - { - Tags.Clear(); - } - - public bool Contains(Tag item) - { - return Tags.Contains(item); - } - - public void CopyTo(Tag[] array, int arrayIndex) - { - Tags.CopyTo(array, arrayIndex); - } - - public override void Remove(Tag tag) - { - Tags.Remove(tag); - } - - bool ICollection.Remove(Tag item) - { - return Tags.Remove(item); - } - - public int Count => Tags.Count; - public bool IsReadOnly => false; } } diff --git a/src/GitVersionCore.Tests/Model/GitVersionContextTests.cs b/src/GitVersionCore.Tests/Model/GitVersionContextTests.cs index 98a0a24149..35c8b88bd7 100644 --- a/src/GitVersionCore.Tests/Model/GitVersionContextTests.cs +++ b/src/GitVersionCore.Tests/Model/GitVersionContextTests.cs @@ -62,7 +62,7 @@ public void CanInheritIncrement(IncrementStrategy increment, IncrementStrategy? fixture.BranchTo(dummyBranchName); fixture.MakeACommit(); - var context = GetGitVersionContext(fixture.RepositoryPath, fixture.Repository, dummyBranchName, config); + var context = GetGitVersionContext(fixture.RepositoryPath, new GitRepository(fixture.Repository), dummyBranchName, config); context.Configuration.Increment.ShouldBe(alternateExpected ?? increment); } @@ -176,12 +176,12 @@ public void CanFindParentBranchForInheritingIncrementStrategy() Commands.Checkout(fixture.Repository, featureBranch); fixture.Repository.MakeACommit(); - var context = GetGitVersionContext(fixture.RepositoryPath, fixture.Repository, "develop", config); + var context = GetGitVersionContext(fixture.RepositoryPath, new GitRepository(fixture.Repository), "develop", config); context.Configuration.Increment.ShouldBe(IncrementStrategy.Major); } - private static GitVersionContext GetGitVersionContext(string workingDirectory, IRepository repository, string branch, Config config = null) + private static GitVersionContext GetGitVersionContext(string workingDirectory, IGitRepository repository, string branch, Config config = null) { var options = Options.Create(new GitVersionOptions { diff --git a/src/GitVersionCore.Tests/VersionCalculation/BaseVersionCalculatorTests.cs b/src/GitVersionCore.Tests/VersionCalculation/BaseVersionCalculatorTests.cs index e920f4bcf0..51db85d564 100644 --- a/src/GitVersionCore.Tests/VersionCalculation/BaseVersionCalculatorTests.cs +++ b/src/GitVersionCore.Tests/VersionCalculation/BaseVersionCalculatorTests.cs @@ -7,11 +7,11 @@ using GitVersion.VersionCalculation; using GitVersionCore.Tests.Helpers; using GitVersionCore.Tests.Mocks; -using LibGit2Sharp; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using NUnit.Framework; using Shouldly; +using Commit = GitVersion.Commit; namespace GitVersionCore.Tests.VersionCalculation { diff --git a/src/GitVersionCore.Tests/VersionCalculation/NextVersionCalculatorTests.cs b/src/GitVersionCore.Tests/VersionCalculation/NextVersionCalculatorTests.cs index 281cd80360..01126ca84c 100644 --- a/src/GitVersionCore.Tests/VersionCalculation/NextVersionCalculatorTests.cs +++ b/src/GitVersionCore.Tests/VersionCalculation/NextVersionCalculatorTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using GitTools.Testing; using GitVersion; -using GitVersion.Extensions; using GitVersion.Model.Configuration; using GitVersion.VersionCalculation; using GitVersionCore.Tests.Helpers; @@ -302,7 +301,7 @@ public void PreReleaseNumberShouldBeScopeToPreReleaseLabelInContinuousDelivery() fixture.AssertFullSemver("0.1.0-test.2+2", config); Commands.Checkout(fixture.Repository, "master"); - fixture.Repository.Merge(fixture.Repository.FindBranch("feature/test"), Generate.SignatureNow()); + fixture.Repository.Merge(new GitRepository(fixture.Repository).FindBranch("feature/test"), Generate.SignatureNow()); fixture.AssertFullSemver("0.1.0-beta.1+2", config); } diff --git a/src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs b/src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs index e3e4d9c689..bd86c95b34 100644 --- a/src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs +++ b/src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using System.Linq; +using GitVersion; using GitVersion.Extensions; using GitVersion.Model.Configuration; using GitVersion.VersionCalculation; using GitVersionCore.Tests.Helpers; using GitVersionCore.Tests.Mocks; -using LibGit2Sharp; using NUnit.Framework; using Shouldly; diff --git a/src/GitVersionCore.Tests/VersionCalculation/Strategies/VersionInBranchNameBaseVersionStrategyTests.cs b/src/GitVersionCore.Tests/VersionCalculation/Strategies/VersionInBranchNameBaseVersionStrategyTests.cs index bba10b2f95..14226b6373 100644 --- a/src/GitVersionCore.Tests/VersionCalculation/Strategies/VersionInBranchNameBaseVersionStrategyTests.cs +++ b/src/GitVersionCore.Tests/VersionCalculation/Strategies/VersionInBranchNameBaseVersionStrategyTests.cs @@ -1,5 +1,6 @@ using System.Linq; using GitTools.Testing; +using GitVersion; using GitVersion.Configuration; using GitVersion.Extensions; using GitVersion.Model.Configuration; @@ -22,7 +23,7 @@ public void CanTakeVersionFromNameOfReleaseBranch(string branchName, string expe fixture.Repository.MakeACommit(); fixture.Repository.CreateBranch(branchName); - var strategy = GetVersionStrategy(fixture.RepositoryPath, fixture.Repository, branchName); + var strategy = GetVersionStrategy(fixture.RepositoryPath, new GitRepository(fixture.Repository), branchName); var baseVersion = strategy.GetVersions().Single(); baseVersion.SemanticVersion.ToString().ShouldBe(expectedBaseVersion); @@ -38,7 +39,7 @@ public void ShouldNotTakeVersionFromNameOfNonReleaseBranch(string branchName) fixture.Repository.MakeACommit(); fixture.Repository.CreateBranch(branchName); - var strategy = GetVersionStrategy(fixture.RepositoryPath, fixture.Repository, branchName); + var strategy = GetVersionStrategy(fixture.RepositoryPath, new GitRepository(fixture.Repository), branchName); var baseVersions = strategy.GetVersions(); baseVersions.ShouldBeEmpty(); @@ -56,7 +57,7 @@ public void CanTakeVersionFromNameOfConfiguredReleaseBranch(string branchName, s .Add(new Config { Branches = { { "support", new BranchConfig { IsReleaseBranch = true } } } }) .Build(); - var strategy = GetVersionStrategy(fixture.RepositoryPath, fixture.Repository, branchName, config); + var strategy = GetVersionStrategy(fixture.RepositoryPath, new GitRepository(fixture.Repository), branchName, config); var baseVersion = strategy.GetVersions().Single(); @@ -75,13 +76,13 @@ public void CanTakeVersionFromNameOfRemoteReleaseBranch(string branchName, strin Commands.Fetch((Repository)fixture.LocalRepositoryFixture.Repository, fixture.LocalRepositoryFixture.Repository.Network.Remotes.First().Name, new string[0], new FetchOptions(), null); fixture.LocalRepositoryFixture.Checkout($"origin/{branchName}"); - var strategy = GetVersionStrategy(fixture.RepositoryPath, fixture.Repository, branchName); + var strategy = GetVersionStrategy(fixture.RepositoryPath, new GitRepository(fixture.Repository), branchName); var baseVersion = strategy.GetVersions().Single(); baseVersion.SemanticVersion.ToString().ShouldBe(expectedBaseVersion); } - private static IVersionStrategy GetVersionStrategy(string workingDirectory, IRepository repository, string branch, Config config = null) + private static IVersionStrategy GetVersionStrategy(string workingDirectory, IGitRepository repository, string branch, Config config = null) { var sp = BuildServiceProvider(workingDirectory, repository, branch, config); return sp.GetServiceForType(); diff --git a/src/GitVersionCore.Tests/VersionCalculation/TestBaseVersionCalculator.cs b/src/GitVersionCore.Tests/VersionCalculation/TestBaseVersionCalculator.cs index e6bd7e5966..26cdc1a661 100644 --- a/src/GitVersionCore.Tests/VersionCalculation/TestBaseVersionCalculator.cs +++ b/src/GitVersionCore.Tests/VersionCalculation/TestBaseVersionCalculator.cs @@ -1,6 +1,6 @@ using GitVersion; using GitVersion.VersionCalculation; -using LibGit2Sharp; +using Commit = GitVersion.Commit; namespace GitVersionCore.Tests.VersionCalculation { diff --git a/src/GitVersionCore.Tests/VersionCalculation/TestMainlineVersionCalculator.cs b/src/GitVersionCore.Tests/VersionCalculation/TestMainlineVersionCalculator.cs index 473365158d..2e600bbe1e 100644 --- a/src/GitVersionCore.Tests/VersionCalculation/TestMainlineVersionCalculator.cs +++ b/src/GitVersionCore.Tests/VersionCalculation/TestMainlineVersionCalculator.cs @@ -1,6 +1,6 @@ using GitVersion; using GitVersion.VersionCalculation; -using LibGit2Sharp; +using Commit = GitVersion.Commit; namespace GitVersionCore.Tests.VersionCalculation { diff --git a/src/GitVersionCore.Tests/VersionCalculation/VersionSourceTests.cs b/src/GitVersionCore.Tests/VersionCalculation/VersionSourceTests.cs index 433dcdd7f9..96c8df0f38 100644 --- a/src/GitVersionCore.Tests/VersionCalculation/VersionSourceTests.cs +++ b/src/GitVersionCore.Tests/VersionCalculation/VersionSourceTests.cs @@ -1,4 +1,5 @@ using GitTools.Testing; +using GitVersion; using GitVersion.VersionCalculation; using GitVersionCore.Tests.Helpers; using LibGit2Sharp; @@ -66,7 +67,7 @@ public void VersionSourceShaUsingTag() private static INextVersionCalculator GetNextVersionCalculator(RepositoryFixtureBase fixture) { - var sp = BuildServiceProvider(fixture.RepositoryPath, fixture.Repository, fixture.Repository.Head.CanonicalName); + var sp = BuildServiceProvider(fixture.RepositoryPath, new GitRepository(fixture.Repository), fixture.Repository.Head.CanonicalName); return sp.GetService(); } } diff --git a/src/GitVersionCore/Configuration/Abstractions/IBranchConfigurationCalculator.cs b/src/GitVersionCore/Configuration/Abstractions/IBranchConfigurationCalculator.cs index 52b6799a79..9934f2d81b 100644 --- a/src/GitVersionCore/Configuration/Abstractions/IBranchConfigurationCalculator.cs +++ b/src/GitVersionCore/Configuration/Abstractions/IBranchConfigurationCalculator.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using GitVersion.Model.Configuration; -using LibGit2Sharp; namespace GitVersion.Configuration { diff --git a/src/GitVersionCore/Configuration/BranchConfigurationCalculator.cs b/src/GitVersionCore/Configuration/BranchConfigurationCalculator.cs index e3055b8bd3..61615adf9e 100644 --- a/src/GitVersionCore/Configuration/BranchConfigurationCalculator.cs +++ b/src/GitVersionCore/Configuration/BranchConfigurationCalculator.cs @@ -6,7 +6,6 @@ using GitVersion.Extensions; using GitVersion.Logging; using GitVersion.Model.Configuration; -using LibGit2Sharp; namespace GitVersion.Configuration { diff --git a/src/GitVersionCore/Core/Abstractions/IGitRepository.cs b/src/GitVersionCore/Core/Abstractions/IGitRepository.cs index a2d4817de9..3f9bd8b678 100644 --- a/src/GitVersionCore/Core/Abstractions/IGitRepository.cs +++ b/src/GitVersionCore/Core/Abstractions/IGitRepository.cs @@ -1,9 +1,38 @@ -using LibGit2Sharp; +using System; +using System.Collections.Generic; +using GitVersion.Logging; namespace GitVersion { - public interface IGitRepository : IRepository + public interface IGitRepository : IDisposable { - IGitRepositoryCommands Commands { get; } + string Path { get; } + string WorkingDirectory { get; } + bool IsHeadDetached { get; } + Branch Head { get; } + CommitCollection Commits { get; } + BranchCollection Branches { get; } + TagCollection Tags { get; } + ReferenceCollection Refs { get; } + + int GetNumberOfUncommittedChanges(); + Commit FindMergeBase(Commit commit, Commit otherCommit); + string ShortenObjectId(Commit commit); + void CreateBranchForPullRequestBranch(ILog log, AuthenticationInfo auth); + Remote EnsureOnlyOneRemoteIsDefined(ILog log); + bool GitRepoHasMatchingRemote(string targetUrl); + void CleanupDuplicateOrigin(string defaultRemoteName); + bool GetMatchingCommitBranch(Commit baseVersionSource, Branch branch, Commit firstMatchingCommit); + IEnumerable GetCommitsReacheableFrom(Commit commit, Branch branch); + List GetCommitsReacheableFromHead(Commit headCommit); + Commit GetForwardMerge(Commit commitToFindCommonBase, Commit findMergeBase); + IEnumerable GetMergeBaseCommits(Commit mergeCommit, Commit mergedHead, Commit findMergeBase); + Commit GetBaseVersionSource(Commit currentBranchTip); + List GetMainlineCommitLog(Commit baseVersionSource, Commit mainlineTip); + CommitCollection GetCommitLog(Commit baseVersionSource, Commit currentCommit); + + void Checkout(string committishOrBranchSpec); + void Checkout(Branch branch); + void Fetch(string remote, IEnumerable refspecs, AuthenticationInfo auth, string logMessage); } } diff --git a/src/GitVersionCore/Core/Abstractions/IGitRepositoryCommands.cs b/src/GitVersionCore/Core/Abstractions/IGitRepositoryCommands.cs deleted file mode 100644 index da3a12a7a6..0000000000 --- a/src/GitVersionCore/Core/Abstractions/IGitRepositoryCommands.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using LibGit2Sharp; - -namespace GitVersion -{ - /// - /// Mockable and testable interface wrapper for the static - /// class. - /// - public interface IGitRepositoryCommands - { - Branch Checkout(string committishOrBranchSpec); - Branch Checkout(string committishOrBranchSpec, CheckoutOptions options); - Branch Checkout(Branch branch); - Branch Checkout(Branch branch, CheckoutOptions options); - Branch Checkout(Commit commit); - Branch Checkout(Commit commit, CheckoutOptions options); - void Checkout(Tree tree, CheckoutOptions checkoutOptions, string refLogHeadSpec); - void Fetch(string remote, IEnumerable refspecs, FetchOptions options, string logMessage); - void Move(string sourcePath, string destinationPath); - void Move(IEnumerable sourcePaths, IEnumerable destinationPaths); - MergeResult Pull(Signature merger, PullOptions options); - void Remove(string path, bool removeFromWorkingDirectory); - void Remove(IEnumerable paths); - void Remove(IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions); - void Remove(string path); - void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions); - void Stage(string path); - void Stage(string path, StageOptions stageOptions); - void Stage(IEnumerable paths); - void Stage(IEnumerable paths, StageOptions stageOptions); - void Unstage(string path); - void Unstage(string path, ExplicitPathsOptions explicitPathsOptions); - void Unstage(IEnumerable paths); - void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions); - } -} diff --git a/src/GitVersionCore/Core/Abstractions/IRepositoryMetadataProvider.cs b/src/GitVersionCore/Core/Abstractions/IRepositoryMetadataProvider.cs index d467b095cc..6b665275be 100644 --- a/src/GitVersionCore/Core/Abstractions/IRepositoryMetadataProvider.cs +++ b/src/GitVersionCore/Core/Abstractions/IRepositoryMetadataProvider.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using GitVersion.Model.Configuration; using GitVersion.VersionCalculation; -using LibGit2Sharp; namespace GitVersion.Common { @@ -21,7 +20,7 @@ public interface IRepositoryMetadataProvider Branch GetTargetBranch(string targetBranch); Branch FindBranch(string branchName); Branch GetChosenBranch(Config configuration); - List GetBranchesForCommit(GitObject commit); + List GetBranchesForCommit(Commit commit); List GetExcludedInheritBranches(Config configuration); IEnumerable GetReleaseBranches(IEnumerable> releaseBranchConfig); IEnumerable ExcludingBranches(IEnumerable branchesToExclude); @@ -34,14 +33,14 @@ public interface IRepositoryMetadataProvider /// BranchCommit FindCommitBranchWasBranchedFrom(Branch branch, Config configuration, params Branch[] excludedBranches); - SemanticVersion GetCurrentCommitTaggedVersion(GitObject commit, EffectiveConfiguration config); + SemanticVersion GetCurrentCommitTaggedVersion(Commit commit, EffectiveConfiguration config); SemanticVersion MaybeIncrement(BaseVersion baseVersion, GitVersionContext context); IEnumerable GetVersionTagsOnBranch(Branch branch, string tagPrefixRegex); IEnumerable> GetValidVersionTags(string tagPrefixRegex, DateTimeOffset? olderThan = null); - ICommitLog GetCommitLog(Commit baseVersionSource, Commit currentCommit); + CommitCollection GetCommitLog(Commit baseVersionSource, Commit currentCommit); bool GetMatchingCommitBranch(Commit baseVersionSource, Branch branch, Commit firstMatchingCommit); - string ShortenObjectId(GitObject commit); + string ShortenObjectId(Commit commit); VersionField? DetermineIncrementedField(BaseVersion baseVersion, GitVersionContext context); int GetNumberOfUncommittedChanges(); diff --git a/src/GitVersionCore/Core/GitPreparer.cs b/src/GitVersionCore/Core/GitPreparer.cs index fb8d1854f0..10f2b88f81 100644 --- a/src/GitVersionCore/Core/GitPreparer.cs +++ b/src/GitVersionCore/Core/GitPreparer.cs @@ -3,7 +3,6 @@ using System.Linq; using GitVersion.Extensions; using GitVersion.Logging; -using LibGit2Sharp; using Microsoft.Extensions.Options; namespace GitVersion @@ -86,25 +85,8 @@ private string ResolveCurrentBranch() private void CleanupDuplicateOrigin() { - var remoteToKeep = DefaultRemoteName; - using var repo = new Repository(options.Value.GitRootPath); - - // check that we have a remote that matches defaultRemoteName if not take the first remote - if (!repo.Network.Remotes.Any(remote => remote.Name.Equals(DefaultRemoteName, StringComparison.InvariantCultureIgnoreCase))) - { - remoteToKeep = repo.Network.Remotes.First().Name; - } - - var duplicateRepos = repo.Network - .Remotes - .Where(remote => !remote.Name.Equals(remoteToKeep, StringComparison.InvariantCultureIgnoreCase)) - .Select(remote => remote.Name); - - // remove all remotes that are considered duplicates - foreach (var repoName in duplicateRepos) - { - repo.Network.Remotes.Remove(repoName); - } + using IGitRepository repo = new GitRepository(options.Value.GitRootPath); + repo.CleanupDuplicateOrigin(DefaultRemoteName); } private void CreateDynamicRepository(string targetBranch) @@ -144,53 +126,10 @@ private void NormalizeGitDirectory(string targetBranch, string gitDirectory, boo private void CloneRepository(string repositoryUrl, string gitDirectory, AuthenticationInfo auth) { - Credentials credentials = null; - - if (auth != null) + using (log.IndentLog($"Cloning repository from url '{repositoryUrl}'")) { - if (!string.IsNullOrWhiteSpace(auth.Username)) - { - log.Info($"Setting up credentials using name '{auth.Username}'"); - - credentials = new UsernamePasswordCredentials - { - Username = auth.Username, - Password = auth.Password ?? string.Empty - }; - } - } - - try - { - using (log.IndentLog($"Cloning repository from url '{repositoryUrl}'")) - { - var cloneOptions = new CloneOptions - { - Checkout = false, - CredentialsProvider = (url, usernameFromUrl, types) => credentials - }; - - var returnedPath = Repository.Clone(repositoryUrl, gitDirectory, cloneOptions); - log.Info($"Returned path after repository clone: {returnedPath}"); - } - } - catch (LibGit2SharpException ex) - { - var message = ex.Message; - if (message.Contains("401")) - { - throw new Exception("Unauthorized: Incorrect username/password"); - } - if (message.Contains("403")) - { - throw new Exception("Forbidden: Possibly Incorrect username/password"); - } - if (message.Contains("404")) - { - throw new Exception("Not found: The repository was not found"); - } - - throw new Exception("There was an unknown problem with the Git repository you provided", ex); + var returnedPath = GitRepository.Clone(repositoryUrl, gitDirectory, auth); + log.Info($"Returned path after repository clone: {returnedPath}"); } } @@ -201,7 +140,7 @@ private void CloneRepository(string repositoryUrl, string gitDirectory, Authenti private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string currentBranch, bool isDynamicRepository) { var authentication = options.Value.Authentication; - using var repository = new GitRepository(() => gitDirectory); + using IGitRepository repository = new GitRepository(gitDirectory); // Need to ensure the HEAD does not move, this is essentially a BugCheck var expectedSha = repository.Head.Tip.Sha; var expectedBranchName = repository.Head.CanonicalName; @@ -210,8 +149,6 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur { var remote = repository.EnsureOnlyOneRemoteIsDefined(log); - repository.AddMissingRefSpecs(log, remote); - //If noFetch is enabled, then GitVersion will assume that the git repository is normalized before execution, so that fetching from remotes is not required. if (noFetch) { @@ -219,12 +156,12 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur } else { - log.Info($"Fetching from remote '{remote.Name}' using the following refspecs: {string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification))}."); - repository.Commands.Fetch(remote.Name, new string[0], authentication.ToFetchOptions(), null); + log.Info($"Fetching from remote '{remote.Name}' using the following refspecs: {remote.RefSpecs}."); + repository.Fetch(remote.Name, new string[0], authentication, null); } - repository.EnsureLocalBranchExistsForCurrentBranch(log, remote, currentBranch); - repository.CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(log, remote.Name); + EnsureLocalBranchExistsForCurrentBranch(repository, log, remote, currentBranch); + CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(repository, log, remote.Name); // Bug fix for https://github.com/GitTools/GitVersion/issues/1754, head maybe have been changed // if this is a dynamic repository. But only allow this in case the branches are different (branch switch) @@ -241,7 +178,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur var headSha = repository.Refs.Head.TargetIdentifier; - if (!repository.Info.IsHeadDetached) + if (!repository.IsHeadDetached) { log.Info($"HEAD points at branch '{headSha}'."); return; @@ -261,7 +198,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur if (matchingCurrentBranch != null) { log.Info($"Checking out local branch '{currentBranch}'."); - repository.Commands.Checkout(matchingCurrentBranch); + repository.Checkout(matchingCurrentBranch); } else if (localBranchesWhereCommitShaIsHead.Count > 1) { @@ -274,7 +211,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur if (master != null) { log.Warning("Because one of the branches is 'master', will build master." + moveBranchMsg); - repository.Commands.Checkout(master); + repository.Checkout(master); } else { @@ -283,7 +220,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur { var branchWithoutSeparator = branchesWithoutSeparators[0]; log.Warning($"Choosing {branchWithoutSeparator.CanonicalName} as it is the only branch without / or - in it. " + moveBranchMsg); - repository.Commands.Checkout(branchWithoutSeparator); + repository.Checkout(branchWithoutSeparator); } else { @@ -294,12 +231,12 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur else if (localBranchesWhereCommitShaIsHead.Count == 0) { log.Info($"No local branch pointing at the commit '{headSha}'. Fake branch needs to be created."); - repository.CreateFakeBranchPointingAtThePullRequestTip(log, authentication); + repository.CreateBranchForPullRequestBranch(log, authentication); } else { log.Info($"Checking out local branch 'refs/heads/{localBranchesWhereCommitShaIsHead[0].FriendlyName}'."); - repository.Commands.Checkout(repository.Branches[localBranchesWhereCommitShaIsHead[0].FriendlyName]); + repository.Checkout(repository.Branches[localBranchesWhereCommitShaIsHead[0].FriendlyName]); } } finally @@ -313,10 +250,100 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur To disable this error set an environmental variable called IGNORE_NORMALISATION_GIT_HEAD_MOVE to 1 -Please run `git {LibGitExtensions.CreateGitLogArgs(100)}` and submit it along with your build log (with personal info removed) in a new issue at https://github.com/GitTools/GitVersion"); +Please run `git {GitExtensions.CreateGitLogArgs(100)}` and submit it along with your build log (with personal info removed) in a new issue at https://github.com/GitTools/GitVersion"); + } + } + } + } + + private static void CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(IGitRepository repo, ILog log, string remoteName) + { + var prefix = $"refs/remotes/{remoteName}/"; + var remoteHeadCanonicalName = $"{prefix}HEAD"; + var remoteTrackingReferences = repo.Refs + .FromGlob(prefix + "*") + .Where(r => !r.CanonicalName.IsEquivalentTo(remoteHeadCanonicalName)); + + foreach (var remoteTrackingReference in remoteTrackingReferences) + { + var remoteTrackingReferenceName = remoteTrackingReference.CanonicalName; + var branchName = remoteTrackingReferenceName.Substring(prefix.Length); + var localCanonicalName = "refs/heads/" + branchName; + + // We do not want to touch our current branch + if (branchName.IsEquivalentTo(repo.Head.FriendlyName)) continue; + + if (repo.Refs.Any(x => x.CanonicalName.IsEquivalentTo(localCanonicalName))) + { + var localRef = repo.Refs[localCanonicalName]; + if (localRef.DirectReferenceTargetIdentifier == remoteTrackingReference.DirectReferenceTargetIdentifier) + { + log.Info($"Skipping update of '{remoteTrackingReference.CanonicalName}' as it already matches the remote ref."); + continue; } + var remoteRefTipId = remoteTrackingReference.DirectReferenceTargetId; + log.Info($"Updating local ref '{localRef.CanonicalName}' to point at {remoteRefTipId}."); + repo.Refs.UpdateTarget(localRef, remoteRefTipId); + continue; } + + log.Info($"Creating local branch from remote tracking '{remoteTrackingReference.CanonicalName}'."); + repo.Refs.Add(localCanonicalName, new ObjectId(remoteTrackingReference.DirectReferenceTargetIdentifier), true); + + var branch = repo.Branches[branchName]; + repo.Branches.Update(branch, b => b.TrackedBranch = remoteTrackingReferenceName); + } + } + + private static void EnsureLocalBranchExistsForCurrentBranch(IGitRepository repo, ILog log, Remote remote, string currentBranch) + { + if (log is null) + { + throw new ArgumentNullException(nameof(log)); } + + if (remote is null) + { + throw new ArgumentNullException(nameof(remote)); + } + + if (string.IsNullOrEmpty(currentBranch)) return; + + var isRef = currentBranch.Contains("refs"); + var isBranch = currentBranch.Contains("refs/heads"); + var localCanonicalName = !isRef + ? "refs/heads/" + currentBranch + : isBranch + ? currentBranch + : currentBranch.Replace("refs/", "refs/heads/"); + + var repoTip = repo.Head.Tip; + + // We currently have the rep.Head of the *default* branch, now we need to look up the right one + var originCanonicalName = $"{remote.Name}/{currentBranch}"; + var originBranch = repo.Branches[originCanonicalName]; + if (originBranch != null) + { + repoTip = originBranch.Tip; + } + + var repoTipId = repoTip.Id; + + if (repo.Branches.All(b => !b.CanonicalName.IsEquivalentTo(localCanonicalName))) + { + log.Info(isBranch ? $"Creating local branch {localCanonicalName}" + : $"Creating local branch {localCanonicalName} pointing at {repoTipId}"); + repo.Refs.Add(localCanonicalName, repoTipId); + } + else + { + log.Info(isBranch ? $"Updating local branch {localCanonicalName} to point at {repoTip.Sha}" + : $"Updating local branch {localCanonicalName} to match ref {currentBranch}"); + var localRef = repo.Refs[localCanonicalName]; + repo.Refs.UpdateTarget(localRef, repoTipId); + } + + repo.Checkout(localCanonicalName); } } } diff --git a/src/GitVersionCore/Core/GitRepository.cs b/src/GitVersionCore/Core/GitRepository.cs deleted file mode 100644 index 878033780c..0000000000 --- a/src/GitVersionCore/Core/GitRepository.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Collections.Generic; -using LibGit2Sharp; -using Microsoft.Extensions.Options; - -namespace GitVersion -{ - public class GitRepository : IGitRepository - { - private Lazy repositoryLazy; - private IRepository repositoryInstance => repositoryLazy.Value; - - public GitRepository(IOptions options) - : this(() => options.Value.GitRootPath) - { - } - - public GitRepository(Func getGitRootDirectory) - { - repositoryLazy = new Lazy(() => new Repository(getGitRootDirectory())); - Commands = new GitRepositoryCommands(repositoryLazy); - } - - public void Dispose() - { - if (repositoryLazy.IsValueCreated) - { - repositoryInstance.Dispose(); - } - } - - public void Checkout(Tree tree, IEnumerable paths, CheckoutOptions opts) - { - repositoryInstance.Checkout(tree, paths, opts); - } - - public void CheckoutPaths(string committishOrBranchSpec, IEnumerable paths, CheckoutOptions checkoutOptions) - { - repositoryInstance.CheckoutPaths(committishOrBranchSpec, paths, checkoutOptions); - } - - public GitObject Lookup(ObjectId id) - { - return repositoryInstance.Lookup(id); - } - - public GitObject Lookup(string objectish) - { - return repositoryInstance.Lookup(objectish); - } - - public GitObject Lookup(ObjectId id, ObjectType type) - { - return repositoryInstance.Lookup(id, type); - } - - public GitObject Lookup(string objectish, ObjectType type) - { - return repositoryInstance.Lookup(objectish, type); - } - - public Commit Commit(string message, Signature author, Signature committer, CommitOptions options) - { - return repositoryInstance.Commit(message, author, committer, options); - } - - public void Reset(ResetMode resetMode, Commit commit) - { - repositoryInstance.Reset(resetMode, commit); - } - - public void Reset(ResetMode resetMode, Commit commit, CheckoutOptions options) - { - repositoryInstance.Reset(resetMode, commit, options); - } - - public void RemoveUntrackedFiles() - { - repositoryInstance.RemoveUntrackedFiles(); - } - - public RevertResult Revert(Commit commit, Signature reverter, RevertOptions options) - { - return repositoryInstance.Revert(commit, reverter, options); - } - - public MergeResult Merge(Commit commit, Signature merger, MergeOptions options) - { - return repositoryInstance.Merge(commit, merger, options); - } - - public MergeResult Merge(Branch branch, Signature merger, MergeOptions options) - { - return repositoryInstance.Merge(branch, merger, options); - } - - public MergeResult Merge(string committish, Signature merger, MergeOptions options) - { - return repositoryInstance.Merge(committish, merger, options); - } - - public MergeResult MergeFetchedRefs(Signature merger, MergeOptions options) - { - return repositoryInstance.MergeFetchedRefs(merger, options); - } - - public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options) - { - return repositoryInstance.CherryPick(commit, committer, options); - } - - public BlameHunkCollection Blame(string path, BlameOptions options) - { - return repositoryInstance.Blame(path, options); - } - - public FileStatus RetrieveStatus(string filePath) - { - return repositoryInstance.RetrieveStatus(filePath); - } - - public RepositoryStatus RetrieveStatus(StatusOptions options) - { - return repositoryInstance.RetrieveStatus(options); - } - - public string Describe(Commit commit, DescribeOptions options) - { - return repositoryInstance.Describe(commit, options); - } - - public void RevParse(string revision, out Reference reference, out GitObject obj) - { - repositoryInstance.RevParse(revision, out reference, out obj); - } - - public Branch Head => repositoryInstance.Head; - - public LibGit2Sharp.Configuration Config => repositoryInstance.Config; - - public Index Index => repositoryInstance.Index; - - public ReferenceCollection Refs => repositoryInstance.Refs; - - public IQueryableCommitLog Commits => repositoryInstance.Commits; - - public BranchCollection Branches => repositoryInstance.Branches; - - public TagCollection Tags => repositoryInstance.Tags; - - public RepositoryInformation Info => repositoryInstance.Info; - - public Diff Diff => repositoryInstance.Diff; - - public ObjectDatabase ObjectDatabase => repositoryInstance.ObjectDatabase; - - public NoteCollection Notes => repositoryInstance.Notes; - - public SubmoduleCollection Submodules => repositoryInstance.Submodules; - - public WorktreeCollection Worktrees => repositoryInstance.Worktrees; - - public Rebase Rebase => repositoryInstance.Rebase; - - public Ignore Ignore => repositoryInstance.Ignore; - - public Network Network => repositoryInstance.Network; - - public StashCollection Stashes => repositoryInstance.Stashes; - - public IGitRepositoryCommands Commands { get; } - } -} diff --git a/src/GitVersionCore/Core/GitRepositoryCommands.cs b/src/GitVersionCore/Core/GitRepositoryCommands.cs deleted file mode 100644 index 2bd6a5d827..0000000000 --- a/src/GitVersionCore/Core/GitRepositoryCommands.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using System.Collections.Generic; -using LibGit2Sharp; - -namespace GitVersion -{ - /// - /// Default implementation of using - /// the static class. - /// - public class GitRepositoryCommands : IGitRepositoryCommands - { - private readonly Lazy lazyRepository; - private IRepository repository => lazyRepository.Value; - - public GitRepositoryCommands(Lazy lazyRepository) - { - this.lazyRepository = lazyRepository ?? throw new ArgumentNullException(nameof(lazyRepository)); - } - - public Branch Checkout(string committishOrBranchSpec) - { - return Commands.Checkout(this.repository, committishOrBranchSpec); - } - - public Branch Checkout(string committishOrBranchSpec, CheckoutOptions options) - { - return Commands.Checkout(this.repository, committishOrBranchSpec, options); - } - - public Branch Checkout(Branch branch) - { - return Commands.Checkout(this.repository, branch); - } - - public Branch Checkout(Branch branch, CheckoutOptions options) - { - return Commands.Checkout(this.repository, branch, options); - } - - public Branch Checkout(Commit commit) - { - return Commands.Checkout(this.repository, commit); - } - - public Branch Checkout(Commit commit, CheckoutOptions options) - { - return Commands.Checkout(this.repository, commit, options); - } - - public void Checkout(Tree tree, CheckoutOptions checkoutOptions, string refLogHeadSpec) - { - Commands.Checkout(this.repository, tree, checkoutOptions, refLogHeadSpec); - } - - public void Fetch(string remote, IEnumerable refspecs, FetchOptions options, string logMessage) - { - Commands.Fetch((Repository)this.repository, remote, refspecs, options, logMessage); - } - - public void Move(string sourcePath, string destinationPath) - { - Commands.Move(this.repository, sourcePath, destinationPath); - } - - public void Move(IEnumerable sourcePaths, IEnumerable destinationPaths) - { - Commands.Move(this.repository, sourcePaths, destinationPaths); - } - - public MergeResult Pull(Signature merger, PullOptions options) - { - return Commands.Pull((Repository)this.repository, merger, options); - } - - public void Remove(string path, bool removeFromWorkingDirectory) - { - Commands.Remove(this.repository, path, removeFromWorkingDirectory); - } - - public void Remove(IEnumerable paths) - { - Commands.Remove(this.repository, paths); - } - - public void Remove(IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions) - { - Commands.Remove(this.repository, paths, removeFromWorkingDirectory, explicitPathsOptions); - } - - public void Remove(string path) - { - Commands.Remove(this.repository, path); - } - - public void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions) - { - Commands.Remove(this.repository, path, removeFromWorkingDirectory, explicitPathsOptions); - } - - public void Stage(string path) - { - Commands.Stage(this.repository, path); - } - - public void Stage(string path, StageOptions stageOptions) - { - Commands.Stage(this.repository, path, stageOptions); - } - - public void Stage(IEnumerable paths) - { - Commands.Stage(this.repository, paths); - } - - public void Stage(IEnumerable paths, StageOptions stageOptions) - { - Commands.Stage(this.repository, paths, stageOptions); - } - - public void Unstage(string path) - { - Commands.Unstage(this.repository, path); - } - - public void Unstage(string path, ExplicitPathsOptions explicitPathsOptions) - { - Commands.Unstage(this.repository, path, explicitPathsOptions); - } - - public void Unstage(IEnumerable paths) - { - Commands.Unstage(this.repository, paths); - } - - public void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions) - { - Commands.Unstage(this.repository, paths, explicitPathsOptions); - } - } -} diff --git a/src/GitVersionCore/Core/GitVersionContextFactory.cs b/src/GitVersionCore/Core/GitVersionContextFactory.cs index 8a8bd515f1..b9c7b15c54 100644 --- a/src/GitVersionCore/Core/GitVersionContextFactory.cs +++ b/src/GitVersionCore/Core/GitVersionContextFactory.cs @@ -2,7 +2,6 @@ using GitVersion.Common; using GitVersion.Configuration; using GitVersion.Extensions; -using LibGit2Sharp; using Microsoft.Extensions.Options; namespace GitVersion diff --git a/src/GitVersionCore/Core/RepositoryMetadataProvider.cs b/src/GitVersionCore/Core/RepositoryMetadataProvider.cs index 81fde19f3e..c2839fe203 100644 --- a/src/GitVersionCore/Core/RepositoryMetadataProvider.cs +++ b/src/GitVersionCore/Core/RepositoryMetadataProvider.cs @@ -8,7 +8,6 @@ using GitVersion.Logging; using GitVersion.Model.Configuration; using GitVersion.VersionCalculation; -using LibGit2Sharp; namespace GitVersion { @@ -20,9 +19,9 @@ public class RepositoryMetadataProvider : IRepositoryMetadataProvider private const string MissingTipFormat = "{0} has no tip. Please see http://example.com/docs for information on how to fix this."; private readonly ILog log; - private readonly IRepository repository; + private readonly IGitRepository repository; - public RepositoryMetadataProvider(ILog log, IRepository repository) + public RepositoryMetadataProvider(ILog log, IGitRepository repository) { this.log = log ?? throw new ArgumentNullException(nameof(log)); this.repository = repository ?? throw new ArgumentNullException(nameof(log)); @@ -51,7 +50,7 @@ public Commit FindMergeBase(Branch branch, Branch otherBranch) commitToFindCommonBase = otherBranch.Tip.Parents.First(); } - var findMergeBase = repository.ObjectDatabase.FindMergeBase(commit, commitToFindCommonBase); + var findMergeBase = repository.FindMergeBase(commit, commitToFindCommonBase); if (findMergeBase != null) { log.Info($"Found merge base of {findMergeBase.Sha}"); @@ -60,14 +59,14 @@ public Commit FindMergeBase(Branch branch, Branch otherBranch) do { // Now make sure that the merge base is not a forward merge - forwardMerge = GetForwardMerge(commitToFindCommonBase, findMergeBase); + forwardMerge = repository.GetForwardMerge(commitToFindCommonBase, findMergeBase); if (forwardMerge != null) { // TODO Fix the logging up in this section var second = forwardMerge.Parents.First(); log.Debug("Second " + second.Sha); - var mergeBase = repository.ObjectDatabase.FindMergeBase(commit, second); + var mergeBase = repository.FindMergeBase(commit, second); if (mergeBase == null) { log.Warning("Could not find mergbase for " + commit); @@ -98,13 +97,13 @@ public Commit FindMergeBase(Branch branch, Branch otherBranch) public Commit FindMergeBase(Commit commit, Commit mainlineTip) { - return repository.ObjectDatabase.FindMergeBase(commit, mainlineTip); + return repository.FindMergeBase(commit, mainlineTip); } public Commit GetCurrentCommit(Branch currentBranch, string commitId) { Commit currentCommit = null; - if (!String.IsNullOrWhiteSpace(commitId)) + if (!string.IsNullOrWhiteSpace(commitId)) { log.Info($"Searching for specific commit '{commitId}'"); @@ -127,46 +126,19 @@ public Commit GetCurrentCommit(Branch currentBranch, string commitId) return currentCommit; } - public Commit GetBaseVersionSource(Commit currentBranchTip) { - var baseVersionSource = repository.Commits.QueryBy(new CommitFilter - { - IncludeReachableFrom = currentBranchTip - }).First(c => !c.Parents.Any()); - return baseVersionSource; + return repository.GetBaseVersionSource(currentBranchTip); } - public List GetMainlineCommitLog(Commit baseVersionSource, Commit mainlineTip) { - var mainlineCommitLog = repository.Commits - .QueryBy(new CommitFilter - { - IncludeReachableFrom = mainlineTip, - ExcludeReachableFrom = baseVersionSource, - SortBy = CommitSortStrategies.Reverse, - FirstParentOnly = true - }) - .ToList(); - return mainlineCommitLog; + return repository.GetMainlineCommitLog(baseVersionSource, mainlineTip); } - public IEnumerable GetMergeBaseCommits(Commit mergeCommit, Commit mergedHead, Commit findMergeBase) { - var filter = new CommitFilter - { - IncludeReachableFrom = mergedHead, - ExcludeReachableFrom = findMergeBase - }; - var query = repository.Commits.QueryBy(filter); - - var commits = mergeCommit == null ? query.ToList() : new[] { - mergeCommit - }.Union(query).ToList(); - return commits; + return repository.GetMergeBaseCommits(mergeCommit, mergedHead, findMergeBase); } - public Branch GetTargetBranch(string targetBranch) { // By default, we assume HEAD is pointing to the desired branch @@ -200,7 +172,7 @@ public Branch GetTargetBranch(string targetBranch) public Branch FindBranch(string branchName) { - return repository.FindBranch(branchName); + return repository.Branches.FirstOrDefault(x => x.NameWithoutRemote() == branchName); } public Branch GetChosenBranch(Config configuration) @@ -215,7 +187,7 @@ public Branch GetChosenBranch(Config configuration) return chosenBranch; } - public List GetBranchesForCommit(GitObject commit) + public List GetBranchesForCommit(Commit commit) { return repository.Branches.Where(b => !b.IsRemote && b.Tip == commit).ToList(); } @@ -280,7 +252,7 @@ public IEnumerable GetBranchesContainingCommit(Commit commit, IEnumerabl { log.Info($"Searching for commits reachable from '{branch.FriendlyName}'."); - var commits = GetCommitsReacheableFrom(commit, branch); + var commits = repository.GetCommitsReacheableFrom(commit, branch); if (!commits.Any()) { @@ -347,14 +319,15 @@ public BranchCommit FindCommitBranchWasBranchedFrom(Branch branch, Config config } } - - public SemanticVersion GetCurrentCommitTaggedVersion(GitObject commit, EffectiveConfiguration config) + public SemanticVersion GetCurrentCommitTaggedVersion(Commit commit, EffectiveConfiguration config) { return repository.Tags .SelectMany(t => { - if (t.PeeledTarget() == commit && SemanticVersion.TryParse(t.FriendlyName, config.GitTagPrefix, out var version)) - return new[] { + var targetCommit = t.PeeledTargetCommit(); + if (targetCommit != null && targetCommit == commit && SemanticVersion.TryParse(t.FriendlyName, config.GitTagPrefix, out var version)) + return new[] + { version }; return new SemanticVersion[0]; @@ -380,7 +353,7 @@ public IEnumerable GetVersionTagsOnBranch(Branch branch, string { var tags = GetValidVersionTags(tagPrefixRegex); - var versionTags = branch.Commits.SelectMany(c => tags.Where(t => c.Sha == t.Item1.Target.Sha).Select(t => t.Item2)).ToList(); + var versionTags = branch.Commits.SelectMany(c => tags.Where(t => c.Sha == t.Item1.TargetSha).Select(t => t.Item2)).ToList(); semanticVersionTagsOnBranchCache.Add(branch, versionTags); return versionTags; @@ -393,7 +366,12 @@ public IEnumerable> GetValidVersionTags(string tagPr foreach (var tag in repository.Tags) { - if (!(tag.PeeledTarget() is Commit commit) || (olderThan.HasValue && commit.When() > olderThan.Value)) + var commit = tag.PeeledTargetCommit(); + + if (commit == null) + continue; + + if (olderThan.HasValue && commit.When() > olderThan.Value) continue; if (SemanticVersion.TryParse(tag.FriendlyName, tagPrefixRegex, out var semver)) @@ -405,35 +383,14 @@ public IEnumerable> GetValidVersionTags(string tagPr return tags; } - - public ICommitLog GetCommitLog(Commit baseVersionSource, Commit currentCommit) + public CommitCollection GetCommitLog(Commit baseVersionSource, Commit currentCommit) { - var filter = new CommitFilter - { - IncludeReachableFrom = currentCommit, - ExcludeReachableFrom = baseVersionSource, - SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time - }; - - return repository.Commits.QueryBy(filter); + return repository.GetCommitLog(baseVersionSource, currentCommit); } - public bool GetMatchingCommitBranch(Commit baseVersionSource, Branch branch, Commit firstMatchingCommit) + public string ShortenObjectId(Commit commit) { - var filter = new CommitFilter - { - IncludeReachableFrom = branch, - ExcludeReachableFrom = baseVersionSource, - FirstParentOnly = true, - }; - var query = repository.Commits.QueryBy(filter); - - return query.Contains(firstMatchingCommit); - } - - public string ShortenObjectId(GitObject commit) - { - return repository.ObjectDatabase.ShortenObjectId(commit); + return repository.ShortenObjectId(commit); } public VersionField? DetermineIncrementedField(BaseVersion baseVersion, GitVersionContext context) @@ -441,26 +398,11 @@ public string ShortenObjectId(GitObject commit) return IncrementStrategyFinder.DetermineIncrementedField(repository, context, baseVersion); } - private Commit GetForwardMerge(Commit commitToFindCommonBase, Commit findMergeBase) + public bool GetMatchingCommitBranch(Commit baseVersionSource, Branch branch, Commit firstMatchingCommit) { - var forwardMerge = repository.Commits - .QueryBy(new CommitFilter - { - IncludeReachableFrom = commitToFindCommonBase, - ExcludeReachableFrom = findMergeBase - }) - .FirstOrDefault(c => c.Parents.Contains(findMergeBase)); - return forwardMerge; + return repository.GetMatchingCommitBranch(baseVersionSource, branch, firstMatchingCommit); } - private IEnumerable GetCommitsReacheableFrom(Commit commit, Branch branch) - { - var commits = repository.Commits.QueryBy(new CommitFilter - { - IncludeReachableFrom = branch - }).Where(c => c.Sha == commit.Sha); - return commits; - } private IEnumerable GetMergeCommitsForBranch(Branch branch, Config configuration, IEnumerable excludedBranches) { if (mergeBaseCommitsCache.ContainsKey(branch)) @@ -485,7 +427,7 @@ private IEnumerable GetMergeCommitsForBranch(Branch branch, Config { if (otherBranch.Tip == null) { - log.Warning(String.Format(MissingTipFormat, otherBranch.FriendlyName)); + log.Warning(string.Format(MissingTipFormat, otherBranch.FriendlyName)); return BranchCommit.Empty; } @@ -493,43 +435,14 @@ private IEnumerable GetMergeCommitsForBranch(Branch branch, Config return new BranchCommit(findMergeBase, otherBranch); }) .Where(b => b.Commit != null) - .OrderByDescending(b => b.Commit.Committer.When) + .OrderByDescending(b => b.Commit.CommitterWhen) .ToList(); mergeBaseCommitsCache.Add(branch, branchMergeBases); return branchMergeBases; } - public int GetNumberOfUncommittedChanges() - { - // check if we have a branch tip at all to behave properly with empty repos - // => return that we have actually uncomitted changes because we are apparently - // running GitVersion on something which lives inside this brand new repo _/\Ö/\_ - if (repository.Head?.Tip == null || repository.Diff == null) - { - // this is a somewhat cumbersome way of figuring out the number of changes in the repo - // which is more expensive than to use the Diff as it gathers more info, but - // we can't use the other method when we are dealing with a new/empty repo - try - { - var status = repository.RetrieveStatus(); - return status.Untracked.Count() + status.Staged.Count(); - - } - catch (Exception) - { - return Int32.MaxValue; // this should be somewhat puzzling to see, - // so we may have reached our goal to show that - // that repo is really "Dirty"... - } - } - - // gets all changes of the last commit vs Staging area and WT - var changes = repository.Diff.Compare(repository.Head.Tip.Tree, - DiffTargets.Index | DiffTargets.WorkingDirectory); - - return changes.Count; - } + public int GetNumberOfUncommittedChanges() => repository.GetNumberOfUncommittedChanges(); private class MergeBaseData { @@ -541,4 +454,5 @@ public MergeBaseData(Commit mergeBase) } } } + } diff --git a/src/GitVersionCore/Extensions/AssemblyInfo.cs b/src/GitVersionCore/Extensions/AssemblyInfo.cs index 7c1dbae9b9..0e209393e8 100644 --- a/src/GitVersionCore/Extensions/AssemblyInfo.cs +++ b/src/GitVersionCore/Extensions/AssemblyInfo.cs @@ -1,6 +1,5 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("GitVersionTask.Tests")] -[assembly: InternalsVisibleTo("GitVersion")] [assembly: InternalsVisibleTo("GitVersionCore.Tests")] [assembly: InternalsVisibleTo("GitVersionExe.Tests")] +[assembly: InternalsVisibleTo("GitVersion.MsBuild.Tests")] diff --git a/src/GitVersionCore/Extensions/LibGitExtensions.cs b/src/GitVersionCore/Extensions/GitExtensions.cs similarity index 92% rename from src/GitVersionCore/Extensions/LibGitExtensions.cs rename to src/GitVersionCore/Extensions/GitExtensions.cs index 001dbf59a5..1c5e57c3e7 100644 --- a/src/GitVersionCore/Extensions/LibGitExtensions.cs +++ b/src/GitVersionCore/Extensions/GitExtensions.cs @@ -4,15 +4,14 @@ using System.Linq; using System.Text; using GitVersion.Helpers; -using LibGit2Sharp; namespace GitVersion.Extensions { - public static class LibGitExtensions + public static class GitExtensions { public static DateTimeOffset When(this Commit commit) { - return commit.Committer.When; + return commit.CommitterWhen.Value; } public static string NameWithoutRemote(this Branch branch) @@ -56,18 +55,6 @@ public static IEnumerable ExcludingBranches(this IEnumerable bra { return branches.Where(b => branchesToExclude.All(bte => !IsSameBranch(b, bte))); } - - public static GitObject PeeledTarget(this Tag tag) - { - var target = tag.Target; - - while (target is TagAnnotation annotation) - { - target = annotation.Target; - } - return target; - } - public static IEnumerable CommitsPriorToThan(this Branch branch, DateTimeOffset olderThan) { return branch.Commits.SkipWhile(c => c.When() > olderThan); @@ -133,5 +120,6 @@ public static string CreateGitLogArgs(int? maxCommits) { return @"log --graph --format=""%h %cr %d"" --decorate --date=relative --all --remotes=*" + (maxCommits != null ? $" -n {maxCommits}" : null); } + } } diff --git a/src/GitVersionCore/Extensions/GitVersionOptionsExtensions.cs b/src/GitVersionCore/Extensions/GitVersionOptionsExtensions.cs index 25e7c8425a..74ae386ecc 100644 --- a/src/GitVersionCore/Extensions/GitVersionOptionsExtensions.cs +++ b/src/GitVersionCore/Extensions/GitVersionOptionsExtensions.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Linq; -using LibGit2Sharp; namespace GitVersion.Extensions { @@ -11,7 +10,7 @@ public static string GetDotGitDirectory(this GitVersionOptions gitVersionOptions { var dotGitDirectory = !string.IsNullOrWhiteSpace(gitVersionOptions.DynamicGitRepositoryPath) ? gitVersionOptions.DynamicGitRepositoryPath - : Repository.Discover(gitVersionOptions.WorkingDirectory); + : GitRepository.Discover(gitVersionOptions.WorkingDirectory); dotGitDirectory = dotGitDirectory?.TrimEnd('/', '\\'); if (string.IsNullOrEmpty(dotGitDirectory)) @@ -29,13 +28,13 @@ public static string GetProjectRootDirectory(this GitVersionOptions gitVersionOp return gitVersionOptions.WorkingDirectory; } - var dotGitDirectory = Repository.Discover(gitVersionOptions.WorkingDirectory); + var dotGitDirectory = GitRepository.Discover(gitVersionOptions.WorkingDirectory); if (string.IsNullOrEmpty(dotGitDirectory)) throw new DirectoryNotFoundException("Cannot find the .git directory"); - using var repository = new Repository(dotGitDirectory); - return repository.Info.WorkingDirectory; + using var repository = new GitRepository(dotGitDirectory); + return repository.WorkingDirectory; } public static string GetGitRootPath(this GitVersionOptions options) @@ -71,7 +70,6 @@ public static string GetDynamicGitRepositoryPath(this GitVersionOptions gitVersi } var repositoryPath = Path.Combine(possiblePath, ".git"); - return repositoryPath; } @@ -80,8 +78,8 @@ private static bool GitRepoHasMatchingRemote(string possiblePath, string targetU { try { - using var repository = new Repository(possiblePath); - return repository.Network.Remotes.Any(r => r.Url == targetUrl); + using IGitRepository repository = new GitRepository(possiblePath); + return repository.GitRepoHasMatchingRemote(targetUrl); } catch (Exception) { diff --git a/src/GitVersionCore/Extensions/RepositoryExtensions.cs b/src/GitVersionCore/Extensions/RepositoryExtensions.cs deleted file mode 100644 index bd6420b356..0000000000 --- a/src/GitVersionCore/Extensions/RepositoryExtensions.cs +++ /dev/null @@ -1,226 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using GitVersion.Logging; -using LibGit2Sharp; - -namespace GitVersion.Extensions -{ - public static class RepositoryExtensions - { - public static string GetRepositoryDirectory(this IRepository repository, bool omitGitPostFix = true) - { - var gitDirectory = repository.Info.Path; - - gitDirectory = gitDirectory.TrimEnd(Path.DirectorySeparatorChar); - - if (omitGitPostFix && gitDirectory.EndsWith(".git")) - { - gitDirectory = gitDirectory.Substring(0, gitDirectory.Length - ".git".Length); - gitDirectory = gitDirectory.TrimEnd(Path.DirectorySeparatorChar); - } - - return gitDirectory; - } - - public static Branch FindBranch(this IRepository repository, string branchName) - { - return repository.Branches.FirstOrDefault(x => x.NameWithoutRemote() == branchName); - } - - public static void DumpGraph(this IRepository repository, Action writer = null, int? maxCommits = null) - { - LibGitExtensions.DumpGraph(repository.Info.Path, writer, maxCommits); - } - - public static void EnsureLocalBranchExistsForCurrentBranch(this IGitRepository repo, ILog log, Remote remote, string currentBranch) - { - if (log is null) - { - throw new ArgumentNullException(nameof(log)); - } - - if (remote is null) - { - throw new ArgumentNullException(nameof(remote)); - } - - if (string.IsNullOrEmpty(currentBranch)) return; - - var isRef = currentBranch.Contains("refs"); - var isBranch = currentBranch.Contains("refs/heads"); - var localCanonicalName = !isRef - ? "refs/heads/" + currentBranch - : isBranch - ? currentBranch - : currentBranch.Replace("refs/", "refs/heads/"); - - var repoTip = repo.Head.Tip; - - // We currently have the rep.Head of the *default* branch, now we need to look up the right one - var originCanonicalName = $"{remote.Name}/{currentBranch}"; - var originBranch = repo.Branches[originCanonicalName]; - if (originBranch != null) - { - repoTip = originBranch.Tip; - } - - var repoTipId = repoTip.Id; - - if (repo.Branches.All(b => !b.CanonicalName.IsEquivalentTo(localCanonicalName))) - { - log.Info(isBranch ? $"Creating local branch {localCanonicalName}" - : $"Creating local branch {localCanonicalName} pointing at {repoTipId}"); - repo.Refs.Add(localCanonicalName, repoTipId); - } - else - { - log.Info(isBranch ? $"Updating local branch {localCanonicalName} to point at {repoTip.Sha}" - : $"Updating local branch {localCanonicalName} to match ref {currentBranch}"); - var localRef = repo.Refs[localCanonicalName]; - repo.Refs.UpdateTarget(localRef, repoTipId); - } - - repo.Commands.Checkout(localCanonicalName); - } - - public static void AddMissingRefSpecs(this IRepository repo, ILog log, Remote remote) - { - if (remote.FetchRefSpecs.Any(r => r.Source == "refs/heads/*")) - return; - - var allBranchesFetchRefSpec = $"+refs/heads/*:refs/remotes/{remote.Name}/*"; - - log.Info($"Adding refspec: {allBranchesFetchRefSpec}"); - - repo.Network.Remotes.Update(remote.Name, - r => r.FetchRefSpecs.Add(allBranchesFetchRefSpec)); - } - - public static void CreateFakeBranchPointingAtThePullRequestTip(this IGitRepository repo, ILog log, AuthenticationInfo authentication) - { - var remote = repo.Network.Remotes.Single(); - - log.Info("Fetching remote refs to see if there is a pull request ref"); - var remoteTips = (string.IsNullOrEmpty(authentication.Username) ? - repo.GetRemoteTipsForAnonymousUser(remote) : - repo.GetRemoteTipsUsingUsernamePasswordCredentials(remote, authentication.Username, authentication.Password)) - .ToList(); - - log.Info($"Remote Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, remoteTips.Select(r => r.CanonicalName))); - - var headTipSha = repo.Head.Tip.Sha; - - var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); - - if (refs.Count == 0) - { - var message = $"Couldn't find any remote tips from remote '{remote.Url}' pointing at the commit '{headTipSha}'."; - throw new WarningException(message); - } - - if (refs.Count > 1) - { - var names = string.Join(", ", refs.Select(r => r.CanonicalName)); - var message = $"Found more than one remote tip from remote '{remote.Url}' pointing at the commit '{headTipSha}'. Unable to determine which one to use ({names})."; - throw new WarningException(message); - } - - var reference = refs[0]; - var canonicalName = reference.CanonicalName; - log.Info($"Found remote tip '{canonicalName}' pointing at the commit '{headTipSha}'."); - - if (canonicalName.StartsWith("refs/tags")) - { - log.Info($"Checking out tag '{canonicalName}'"); - repo.Commands.Checkout(reference.Target.Sha); - return; - } - - if (!canonicalName.StartsWith("refs/pull/") && !canonicalName.StartsWith("refs/pull-requests/")) - { - var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; - throw new WarningException(message); - } - - var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); - - log.Info($"Creating fake local branch '{fakeBranchName}'."); - repo.Refs.Add(fakeBranchName, new ObjectId(headTipSha)); - - log.Info($"Checking local branch '{fakeBranchName}' out."); - repo.Commands.Checkout(fakeBranchName); - } - - public static void CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(this IRepository repo, ILog log, string remoteName) - { - var prefix = $"refs/remotes/{remoteName}/"; - var remoteHeadCanonicalName = $"{prefix}HEAD"; - var remoteTrackingReferences = repo.Refs - .FromGlob(prefix + "*") - .Where(r => !r.CanonicalName.IsEquivalentTo(remoteHeadCanonicalName)); - - foreach (var remoteTrackingReference in remoteTrackingReferences) - { - var remoteTrackingReferenceName = remoteTrackingReference.CanonicalName; - var branchName = remoteTrackingReferenceName.Substring(prefix.Length); - var localCanonicalName = "refs/heads/" + branchName; - - // We do not want to touch our current branch - if (branchName.IsEquivalentTo(repo.Head.FriendlyName)) continue; - - if (repo.Refs.Any(x => x.CanonicalName.IsEquivalentTo(localCanonicalName))) - { - var localRef = repo.Refs[localCanonicalName]; - var remotedirectReference = remoteTrackingReference.ResolveToDirectReference(); - if (localRef.ResolveToDirectReference().TargetIdentifier == remotedirectReference.TargetIdentifier) - { - log.Info($"Skipping update of '{remoteTrackingReference.CanonicalName}' as it already matches the remote ref."); - continue; - } - var remoteRefTipId = remotedirectReference.Target.Id; - log.Info($"Updating local ref '{localRef.CanonicalName}' to point at {remoteRefTipId}."); - repo.Refs.UpdateTarget(localRef, remoteRefTipId); - continue; - } - - log.Info($"Creating local branch from remote tracking '{remoteTrackingReference.CanonicalName}'."); - repo.Refs.Add(localCanonicalName, new ObjectId(remoteTrackingReference.ResolveToDirectReference().TargetIdentifier), true); - - var branch = repo.Branches[branchName]; - repo.Branches.Update(branch, b => b.TrackedBranch = remoteTrackingReferenceName); - } - } - - public static Remote EnsureOnlyOneRemoteIsDefined(this IRepository repo, ILog log) - { - var remotes = repo.Network.Remotes; - var howMany = remotes.Count(); - - if (howMany == 1) - { - var remote = remotes.Single(); - log.Info($"One remote found ({remote.Name} -> '{remote.Url}')."); - return remote; - } - - var message = $"{howMany} remote(s) have been detected. When being run on a build server, the Git repository is expected to bear one (and no more than one) remote."; - throw new WarningException(message); - } - - private static IEnumerable GetRemoteTipsUsingUsernamePasswordCredentials(this IRepository repository, Remote remote, string username, string password) - { - return repository.Network.ListReferences(remote, (url, fromUrl, types) => new UsernamePasswordCredentials - { - Username = username, - Password = password ?? string.Empty - }).Select(r => r.ResolveToDirectReference()); - } - - private static IEnumerable GetRemoteTipsForAnonymousUser(this IRepository repository, Remote remote) - { - return repository.Network.ListReferences(remote).Select(r => r.ResolveToDirectReference()); - } - } -} diff --git a/src/GitVersionCore/GitVersionCore.csproj b/src/GitVersionCore/GitVersionCore.csproj index 650058671c..079098ea33 100644 --- a/src/GitVersionCore/GitVersionCore.csproj +++ b/src/GitVersionCore/GitVersionCore.csproj @@ -13,25 +13,25 @@ - + - - + + - - - + + + - - + + - - + + diff --git a/src/GitVersionCore/GitVersionCoreModule.cs b/src/GitVersionCore/GitVersionCoreModule.cs index 845f24656e..b7b6ea1daa 100644 --- a/src/GitVersionCore/GitVersionCoreModule.cs +++ b/src/GitVersionCore/GitVersionCoreModule.cs @@ -11,7 +11,6 @@ using GitVersion.VersionConverters.GitVersionInfo; using GitVersion.VersionConverters.OutputGenerator; using GitVersion.VersionConverters.WixUpdater; -using LibGit2Sharp; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -24,7 +23,7 @@ public void RegisterTypes(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/GitVersionCore/LibGit2/GitObjects.cs b/src/GitVersionCore/LibGit2/GitObjects.cs new file mode 100644 index 0000000000..f15533c069 --- /dev/null +++ b/src/GitVersionCore/LibGit2/GitObjects.cs @@ -0,0 +1,364 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using GitVersion.Helpers; +using LibGit2Sharp; + +namespace GitVersion +{ + public class ObjectId + { + private static readonly LambdaEqualityHelper equalityHelper = + new LambdaEqualityHelper(x => x.Sha); + + private readonly LibGit2Sharp.ObjectId innerObjectId; + private ObjectId(LibGit2Sharp.ObjectId objectId) + { + innerObjectId = objectId; + } + + public ObjectId(string sha) + { + innerObjectId = new LibGit2Sharp.ObjectId(sha); + } + + public override bool Equals(object obj) => Equals(obj as ObjectId); + private bool Equals(ObjectId other) => equalityHelper.Equals(this, other); + + public override int GetHashCode() => equalityHelper.GetHashCode(this); + public static bool operator !=(ObjectId left, ObjectId right) => !Equals(left, right); + public static bool operator ==(ObjectId left, ObjectId right) => Equals(left, right); + public static implicit operator LibGit2Sharp.ObjectId(ObjectId d) => d?.innerObjectId; + public static explicit operator ObjectId(LibGit2Sharp.ObjectId b) => b is null ? null : new ObjectId(b); + public string Sha => innerObjectId?.Sha; + + public string ToString(int prefixLength) => innerObjectId.ToString(prefixLength); + } + + public class Tag + { + private readonly LibGit2Sharp.Tag innerTag; + private Tag(LibGit2Sharp.Tag tag) + { + innerTag = tag; + } + + protected Tag() + { + } + public static implicit operator LibGit2Sharp.Tag(Tag d) => d?.innerTag; + public static explicit operator Tag(LibGit2Sharp.Tag b) => b is null ? null : new Tag(b); + + public virtual string TargetSha => innerTag?.Target.Sha; + public virtual string FriendlyName => innerTag?.FriendlyName; + + public Commit PeeledTargetCommit() + { + var target = innerTag.Target; + + while (target is TagAnnotation annotation) + { + target = annotation.Target; + } + + return target is LibGit2Sharp.Commit commit ? (Commit)commit : null; + } + } + + public class Commit + { + private static readonly LambdaEqualityHelper equalityHelper = + new LambdaEqualityHelper(x => x.Id); + + private readonly LibGit2Sharp.Commit innerCommit; + + private Commit(LibGit2Sharp.Commit commit) + { + innerCommit = commit; + } + + protected Commit() + { + } + + public override bool Equals(object obj) => Equals(obj as Commit); + private bool Equals(Commit other) => equalityHelper.Equals(this, other); + + public override int GetHashCode() => equalityHelper.GetHashCode(this); + public static bool operator !=(Commit left, Commit right) => !Equals(left, right); + public static bool operator ==(Commit left, Commit right) => Equals(left, right); + + public static implicit operator LibGit2Sharp.Commit(Commit d) => d?.innerCommit; + public static explicit operator Commit(LibGit2Sharp.Commit b) => b is null ? null : new Commit(b); + + public virtual IEnumerable Parents + { + get + { + if (innerCommit == null) yield return null; + else + foreach (var parent in innerCommit.Parents) + yield return (Commit)parent; + } + } + + public virtual string Sha => innerCommit?.Sha; + public virtual ObjectId Id => (ObjectId)innerCommit?.Id; + public virtual DateTimeOffset? CommitterWhen => innerCommit?.Committer.When; + public virtual string Message => innerCommit?.Message; + } + + public class Branch + { + private readonly LibGit2Sharp.Branch innerBranch; + + private Branch(LibGit2Sharp.Branch branch) + { + innerBranch = branch; + } + + protected Branch() + { + } + public static implicit operator LibGit2Sharp.Branch(Branch d) => d?.innerBranch; + public static explicit operator Branch(LibGit2Sharp.Branch b) => b is null ? null : new Branch(b); + + public virtual string CanonicalName => innerBranch?.CanonicalName; + public virtual string FriendlyName => innerBranch?.FriendlyName; + public virtual Commit Tip => (Commit)innerBranch?.Tip; + public virtual CommitCollection Commits => CommitCollection.FromCommitLog(innerBranch?.Commits); + public virtual bool IsRemote => innerBranch != null && innerBranch.IsRemote; + public virtual bool IsTracking => innerBranch != null && innerBranch.IsTracking; + } + + public class Reference + { + private readonly LibGit2Sharp.Reference innerReference; + private DirectReference directReference => innerReference.ResolveToDirectReference(); + + private Reference(LibGit2Sharp.Reference reference) + { + innerReference = reference; + } + + protected Reference() + { + } + public virtual string CanonicalName => innerReference.CanonicalName; + public virtual string TargetIdentifier => innerReference.TargetIdentifier; + public virtual string DirectReferenceTargetIdentifier => directReference.TargetIdentifier; + public virtual ObjectId DirectReferenceTargetId => (ObjectId)directReference.Target.Id; + + public virtual Reference ResolveToDirectReference() => (Reference)directReference; + public static implicit operator LibGit2Sharp.Reference(Reference d) => d?.innerReference; + public static explicit operator Reference(LibGit2Sharp.Reference b) => b is null ? null : new Reference(b); + } + + public class Remote + { + private readonly LibGit2Sharp.Remote innerRemote; + + private Remote(LibGit2Sharp.Remote remote) + { + innerRemote = remote; + } + + protected Remote() + { + } + public static implicit operator LibGit2Sharp.Remote(Remote d) => d?.innerRemote; + public static explicit operator Remote(LibGit2Sharp.Remote b) => b is null ? null : new Remote(b); + public virtual string Name => innerRemote.Name; + public virtual string RefSpecs => string.Join(", ", innerRemote.FetchRefSpecs.Select(r => r.Specification)); + } + + public class BranchCollection : IEnumerable + { + private readonly LibGit2Sharp.BranchCollection innerCollection; + private BranchCollection(LibGit2Sharp.BranchCollection collection) => innerCollection = collection; + + protected BranchCollection() + { + } + + public static implicit operator LibGit2Sharp.BranchCollection(BranchCollection d) => d.innerCollection; + public static explicit operator BranchCollection(LibGit2Sharp.BranchCollection b) => b is null ? null : new BranchCollection(b); + + public virtual IEnumerator GetEnumerator() + { + foreach (var branch in innerCollection) + yield return (Branch)branch; + } + + public virtual Branch Add(string name, Commit commit) + { + return (Branch)innerCollection.Add(name, commit); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public virtual Branch this[string friendlyName] => (Branch)innerCollection[friendlyName]; + + public void Update(Branch branch, params Action[] actions) + { + innerCollection.Update(branch, actions); + } + } + + public class TagCollection : IEnumerable + { + private readonly LibGit2Sharp.TagCollection innerCollection; + private TagCollection(LibGit2Sharp.TagCollection collection) => innerCollection = collection; + + protected TagCollection() + { + } + + public static implicit operator LibGit2Sharp.TagCollection(TagCollection d) => d.innerCollection; + public static explicit operator TagCollection(LibGit2Sharp.TagCollection b) => b is null ? null : new TagCollection(b); + + public virtual IEnumerator GetEnumerator() + { + foreach (var tag in innerCollection) + yield return (Tag)tag; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public virtual Tag this[string name] => (Tag)innerCollection[name]; + } + + public class ReferenceCollection : IEnumerable + { + private readonly LibGit2Sharp.ReferenceCollection innerCollection; + private ReferenceCollection(LibGit2Sharp.ReferenceCollection collection) => innerCollection = collection; + + protected ReferenceCollection() + { + } + + public static implicit operator LibGit2Sharp.ReferenceCollection(ReferenceCollection d) => d.innerCollection; + public static explicit operator ReferenceCollection(LibGit2Sharp.ReferenceCollection b) => b is null ? null : new ReferenceCollection(b); + + public IEnumerator GetEnumerator() + { + foreach (var reference in innerCollection) + yield return (Reference)reference; + } + + public virtual Reference Add(string name, string canonicalRefNameOrObjectish) + { + return (Reference)innerCollection.Add(name, canonicalRefNameOrObjectish); + } + + public virtual void Add(string name, ObjectId targetId) + { + innerCollection.Add(name, targetId); + } + + public virtual void Add(string name, ObjectId targetId, bool allowOverwrite) + { + innerCollection.Add(name, targetId, allowOverwrite); + } + + public virtual Reference UpdateTarget(Reference directRef, ObjectId targetId) + { + return (Reference)innerCollection.UpdateTarget(directRef, targetId); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public virtual Reference this[string name] => (Reference)innerCollection[name]; + public virtual Reference Head => this["HEAD"]; + + public virtual IEnumerable FromGlob(string pattern) + { + foreach (var reference in innerCollection.FromGlob(pattern)) + yield return (Reference)reference; + } + } + + public class CommitCollection : IEnumerable + { + private readonly ICommitLog innerCollection; + private CommitCollection(ICommitLog collection) => innerCollection = collection; + + protected CommitCollection() + { + } + + internal static CommitCollection FromCommitLog(ICommitLog b) => b is null ? null : new CommitCollection(b); + + public virtual IEnumerator GetEnumerator() + { + foreach (var commit in innerCollection) + yield return (Commit)commit; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public virtual CommitCollection QueryBy(CommitFilter commitFilter) + { + static object GetReacheableFrom(object item) + { + return item switch + { + Commit c => (LibGit2Sharp.Commit)c, + Branch b => (LibGit2Sharp.Branch)b, + _ => null + }; + } + + var includeReachableFrom = GetReacheableFrom(commitFilter.IncludeReachableFrom); + var excludeReachableFrom = GetReacheableFrom(commitFilter.ExcludeReachableFrom); + var filter = new LibGit2Sharp.CommitFilter + { + IncludeReachableFrom = includeReachableFrom, + ExcludeReachableFrom = excludeReachableFrom, + FirstParentOnly = commitFilter.FirstParentOnly, + SortBy = (LibGit2Sharp.CommitSortStrategies)commitFilter.SortBy, + }; + var commitLog = ((IQueryableCommitLog)innerCollection).QueryBy(filter); + return FromCommitLog(commitLog); + } + } + + public class CommitFilter + { + + public bool FirstParentOnly { get; set; } + public object IncludeReachableFrom { get; set; } + public object ExcludeReachableFrom { get; set; } + public CommitSortStrategies SortBy { get; set; } + } + + [Flags] + public enum CommitSortStrategies + { + /// + /// Sort the commits in no particular ordering; + /// this sorting is arbitrary, implementation-specific + /// and subject to change at any time. + /// + None = 0, + + /// + /// Sort the commits in topological order + /// (parents before children); this sorting mode + /// can be combined with time sorting. + /// + Topological = (1 << 0), + + /// + /// Sort the commits by commit time; + /// this sorting mode can be combined with + /// topological sorting. + /// + Time = (1 << 1), + + /// + /// Iterate through the commits in reverse + /// order; this sorting mode can be combined with + /// any of the above. + /// + Reverse = (1 << 2) + } +} diff --git a/src/GitVersionCore/LibGit2/GitRepository.cs b/src/GitVersionCore/LibGit2/GitRepository.cs new file mode 100644 index 0000000000..e78ddb0a2d --- /dev/null +++ b/src/GitVersionCore/LibGit2/GitRepository.cs @@ -0,0 +1,377 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GitVersion.Logging; +using LibGit2Sharp; +using LibGit2Sharp.Handlers; +using Microsoft.Extensions.Options; + +namespace GitVersion +{ + public class GitRepository : IGitRepository + { + private Lazy repositoryLazy; + private IRepository repositoryInstance => repositoryLazy.Value; + + public GitRepository(IOptions options) + : this(() => options.Value.GitRootPath) + { + } + internal GitRepository(string gitRootDirectory) + : this(() => gitRootDirectory) + { + } + + internal GitRepository(IRepository repository) + { + repositoryLazy = new Lazy(() => repository); + } + + private GitRepository(Func getGitRootDirectory) + { + repositoryLazy = new Lazy(() => new Repository(getGitRootDirectory())); + } + + public static string Discover(string path) => Repository.Discover(path); + public static string Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) + { + try + { + return Repository.Clone(sourceUrl, workdirPath, GetCloneOptions(auth)); + } + catch (LibGit2SharpException ex) + { + var message = ex.Message; + if (message.Contains("401")) + { + throw new Exception("Unauthorized: Incorrect username/password"); + } + if (message.Contains("403")) + { + throw new Exception("Forbidden: Possibly Incorrect username/password"); + } + if (message.Contains("404")) + { + throw new Exception("Not found: The repository was not found"); + } + + throw new Exception("There was an unknown problem with the Git repository you provided", ex); + } + } + public void Dispose() + { + if (repositoryLazy.IsValueCreated) repositoryInstance.Dispose(); + } + + public string Path => repositoryInstance.Info.Path; + public string WorkingDirectory => repositoryInstance.Info.WorkingDirectory; + public bool IsHeadDetached => repositoryInstance.Info.IsHeadDetached; + public int GetNumberOfUncommittedChanges() + { + // check if we have a branch tip at all to behave properly with empty repos + // => return that we have actually uncomitted changes because we are apparently + // running GitVersion on something which lives inside this brand new repo _/\Ö/\_ + if (repositoryInstance.Head?.Tip == null || repositoryInstance.Diff == null) + { + // this is a somewhat cumbersome way of figuring out the number of changes in the repo + // which is more expensive than to use the Diff as it gathers more info, but + // we can't use the other method when we are dealing with a new/empty repo + try + { + var status = repositoryInstance.RetrieveStatus(); + return status.Untracked.Count() + status.Staged.Count(); + + } + catch (Exception) + { + return Int32.MaxValue; // this should be somewhat puzzling to see, + // so we may have reached our goal to show that + // that repo is really "Dirty"... + } + } + + // gets all changes of the last commit vs Staging area and WT + var changes = repositoryInstance.Diff.Compare(repositoryInstance.Head.Tip.Tree, + DiffTargets.Index | DiffTargets.WorkingDirectory); + + return changes.Count; + } + public Commit FindMergeBase(Commit commit, Commit otherCommit) + { + return (Commit)repositoryInstance.ObjectDatabase.FindMergeBase(commit, otherCommit); + } + public string ShortenObjectId(Commit commit) + { + return repositoryInstance.ObjectDatabase.ShortenObjectId(commit); + } + + public Branch Head => (Branch)repositoryInstance.Head; + + public ReferenceCollection Refs => (ReferenceCollection)repositoryInstance.Refs; + + public CommitCollection Commits => CommitCollection.FromCommitLog(repositoryInstance.Commits); + + public BranchCollection Branches => (BranchCollection)repositoryInstance.Branches; + + public TagCollection Tags => (TagCollection)repositoryInstance.Tags; + + public void CreateBranchForPullRequestBranch(ILog log, AuthenticationInfo auth) + { + var network = repositoryInstance.Network; + var remote = network.Remotes.Single(); + + log.Info("Fetching remote refs to see if there is a pull request ref"); + var credentialsProvider = GetCredentialsProvider(auth); + var remoteTips = (credentialsProvider != null + ? network.ListReferences(remote, credentialsProvider) + : network.ListReferences(remote)) + .Select(r => r.ResolveToDirectReference()).ToList(); + + log.Info($"Remote Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, remoteTips.Select(r => r.CanonicalName))); + + var headTipSha = Head.Tip.Sha; + + var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); + + if (refs.Count == 0) + { + var message = $"Couldn't find any remote tips from remote '{remote.Url}' pointing at the commit '{headTipSha}'."; + throw new WarningException(message); + } + + if (refs.Count > 1) + { + var names = string.Join(", ", refs.Select(r => r.CanonicalName)); + var message = $"Found more than one remote tip from remote '{remote.Url}' pointing at the commit '{headTipSha}'. Unable to determine which one to use ({names})."; + throw new WarningException(message); + } + + var reference = refs[0]; + var canonicalName = reference.CanonicalName; + log.Info($"Found remote tip '{canonicalName}' pointing at the commit '{headTipSha}'."); + + if (canonicalName.StartsWith("refs/tags")) + { + log.Info($"Checking out tag '{canonicalName}'"); + Checkout(reference.Target.Sha); + return; + } + + if (!canonicalName.StartsWith("refs/pull/") && !canonicalName.StartsWith("refs/pull-requests/")) + { + var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; + throw new WarningException(message); + } + + var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); + + log.Info($"Creating fake local branch '{fakeBranchName}'."); + Refs.Add(fakeBranchName, new ObjectId(headTipSha)); + + log.Info($"Checking local branch '{fakeBranchName}' out."); + Checkout(fakeBranchName); + } + public bool GitRepoHasMatchingRemote(string targetUrl) + { + return repositoryInstance.Network.Remotes.Any(r => r.Url == targetUrl); + } + public void CleanupDuplicateOrigin(string defaultRemoteName) + { + var remoteToKeep = defaultRemoteName; + // check that we have a remote that matches defaultRemoteName if not take the first remote + if (!repositoryInstance.Network.Remotes.Any(remote => remote.Name.Equals(defaultRemoteName, StringComparison.InvariantCultureIgnoreCase))) + { + remoteToKeep = repositoryInstance.Network.Remotes.First().Name; + } + + var duplicateRepos = repositoryInstance.Network + .Remotes + .Where(remote => !remote.Name.Equals(remoteToKeep, StringComparison.InvariantCultureIgnoreCase)) + .Select(remote => remote.Name); + + // remove all remotes that are considered duplicates + foreach (var repoName in duplicateRepos) + { + repositoryInstance.Network.Remotes.Remove(repoName); + } + } + public Remote EnsureOnlyOneRemoteIsDefined(ILog log) + { + var remotes = repositoryInstance.Network.Remotes; + var howMany = remotes.Count(); + + if (howMany == 1) + { + var remote = remotes.Single(); + log.Info($"One remote found ({remote.Name} -> '{remote.Url}')."); + AddMissingRefSpecs(log, remote); + return (Remote)remote; + } + + var message = $"{howMany} remote(s) have been detected. When being run on a build server, the Git repository is expected to bear one (and no more than one) remote."; + throw new WarningException(message); + } + + private void AddMissingRefSpecs(ILog log, LibGit2Sharp.Remote remote) + { + if (remote.FetchRefSpecs.Any(r => r.Source == "refs/heads/*")) + return; + + var allBranchesFetchRefSpec = $"+refs/heads/*:refs/remotes/{remote.Name}/*"; + log.Info($"Adding refspec: {allBranchesFetchRefSpec}"); + repositoryInstance.Network.Remotes.Update(remote.Name, r => r.FetchRefSpecs.Add(allBranchesFetchRefSpec)); + } + + public static FetchOptions GetFetchOptions(AuthenticationInfo auth) + { + return new FetchOptions + { + CredentialsProvider = GetCredentialsProvider(auth) + }; + } + private static CloneOptions GetCloneOptions(AuthenticationInfo auth) + { + return new CloneOptions + { + Checkout = false, + CredentialsProvider = GetCredentialsProvider(auth) + }; + } + private static CredentialsHandler GetCredentialsProvider(AuthenticationInfo auth) + { + if (!string.IsNullOrWhiteSpace(auth.Username)) + { + return (url, user, types) => new UsernamePasswordCredentials + { + Username = auth.Username, + Password = auth.Password ?? string.Empty + }; + } + return null; + } + + public bool GetMatchingCommitBranch(Commit baseVersionSource, Branch branch, Commit firstMatchingCommit) + { + var filter = new CommitFilter + { + IncludeReachableFrom = branch, + ExcludeReachableFrom = baseVersionSource, + FirstParentOnly = true, + }; + var commitCollection = Commits.QueryBy(filter); + + return commitCollection.Contains(firstMatchingCommit); + } + public IEnumerable GetCommitsReacheableFrom(Commit commit, Branch branch) + { + var filter = new CommitFilter + { + IncludeReachableFrom = branch + }; + var commitCollection = Commits.QueryBy(filter); + + return commitCollection.Where(c => c.Sha == commit.Sha); + } + public List GetCommitsReacheableFromHead(Commit headCommit) + { + var filter = new CommitFilter + { + IncludeReachableFrom = headCommit, + SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Reverse + }; + + var commitCollection = Commits.QueryBy(filter); + + return commitCollection.ToList(); + } + public Commit GetForwardMerge(Commit commitToFindCommonBase, Commit findMergeBase) + { + var filter = new CommitFilter + { + IncludeReachableFrom = commitToFindCommonBase, + ExcludeReachableFrom = findMergeBase + }; + var commitCollection = Commits.QueryBy(filter); + + var forwardMerge = commitCollection + .FirstOrDefault(c => c.Parents.Contains(findMergeBase)); + return forwardMerge; + } + public IEnumerable GetMergeBaseCommits(Commit mergeCommit, Commit mergedHead, Commit findMergeBase) + { + var filter = new CommitFilter + { + IncludeReachableFrom = mergedHead, + ExcludeReachableFrom = findMergeBase + }; + var commitCollection = Commits.QueryBy(filter); + + var commits = mergeCommit != null + ? new[] + { + mergeCommit + }.Union(commitCollection) + : commitCollection; + return commits.ToList(); + } + public Commit GetBaseVersionSource(Commit currentBranchTip) + { + try + { + var filter = new CommitFilter + { + IncludeReachableFrom = currentBranchTip + }; + var commitCollection = Commits.QueryBy(filter); + + var baseVersionSource = commitCollection.First(c => !c.Parents.Any()); + return baseVersionSource; + } + catch (NotFoundException exception) + { + throw new GitVersionException($"Cannot find commit {currentBranchTip.Sha}. Please ensure that the repository is an unshallow clone with `git fetch --unshallow`.", exception); + } + } + public List GetMainlineCommitLog(Commit baseVersionSource, Commit mainlineTip) + { + var filter = new CommitFilter + { + IncludeReachableFrom = mainlineTip, + ExcludeReachableFrom = baseVersionSource, + SortBy = CommitSortStrategies.Reverse, + FirstParentOnly = true + }; + var commitCollection = Commits.QueryBy(filter); + + var mainlineCommitLog = commitCollection.ToList(); + return mainlineCommitLog; + } + public CommitCollection GetCommitLog(Commit baseVersionSource, Commit currentCommit) + { + var filter = new CommitFilter + { + IncludeReachableFrom = currentCommit, + ExcludeReachableFrom = baseVersionSource, + SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time + }; + + var commitCollection = Commits.QueryBy(filter); + + return commitCollection; + } + public void Checkout(string committishOrBranchSpec) + { + Commands.Checkout(repositoryInstance, committishOrBranchSpec); + } + + public void Checkout(Branch branch) + { + Commands.Checkout(repositoryInstance, branch); + } + + public void Fetch(string remote, IEnumerable refspecs, AuthenticationInfo auth, string logMessage) + { + Commands.Fetch((Repository)repositoryInstance, remote, refspecs, GitRepository.GetFetchOptions(auth), logMessage); + } + } +} diff --git a/src/GitVersionCore/Model/AuthenticationInfo.cs b/src/GitVersionCore/Model/AuthenticationInfo.cs index d5167d779f..1303cd7556 100644 --- a/src/GitVersionCore/Model/AuthenticationInfo.cs +++ b/src/GitVersionCore/Model/AuthenticationInfo.cs @@ -1,5 +1,3 @@ -using LibGit2Sharp; - namespace GitVersion { public class AuthenticationInfo @@ -7,21 +5,5 @@ public class AuthenticationInfo public string Username { get; set; } public string Password { get; set; } public string Token { get; set; } - - public FetchOptions ToFetchOptions() - { - var fetchOptions = new FetchOptions(); - - if (!string.IsNullOrEmpty(Username)) - { - fetchOptions.CredentialsProvider = (url, user, types) => new UsernamePasswordCredentials - { - Username = Username, - Password = Password ?? string.Empty - }; - } - - return fetchOptions; - } } } diff --git a/src/GitVersionCore/Model/BranchCommit.cs b/src/GitVersionCore/Model/BranchCommit.cs index 57d5bdf8aa..6e34726669 100644 --- a/src/GitVersionCore/Model/BranchCommit.cs +++ b/src/GitVersionCore/Model/BranchCommit.cs @@ -1,5 +1,3 @@ -using LibGit2Sharp; - namespace GitVersion { /// diff --git a/src/GitVersionCore/Model/GitVersionContext.cs b/src/GitVersionCore/Model/GitVersionContext.cs index 19a47dc013..82a20a30e0 100644 --- a/src/GitVersionCore/Model/GitVersionContext.cs +++ b/src/GitVersionCore/Model/GitVersionContext.cs @@ -1,5 +1,4 @@ using GitVersion.Model.Configuration; -using LibGit2Sharp; namespace GitVersion { diff --git a/src/GitVersionCore/VersionCalculation/Abstractions/IMainlineVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/Abstractions/IMainlineVersionCalculator.cs index 624a191d26..2ec73bbbbc 100644 --- a/src/GitVersionCore/VersionCalculation/Abstractions/IMainlineVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/Abstractions/IMainlineVersionCalculator.cs @@ -1,5 +1,3 @@ -using LibGit2Sharp; - namespace GitVersion.VersionCalculation { public interface IMainlineVersionCalculator diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs index 5ef9a4ddb4..d43968995c 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs @@ -75,7 +75,7 @@ public BaseVersion GetBaseVersion() BaseVersion baseVersionWithOldestSource; if (matchingVersionsOnceIncremented.Any()) { - var oldest = matchingVersionsOnceIncremented.Aggregate((v1, v2) => v1.Version.BaseVersionSource.Committer.When < v2.Version.BaseVersionSource.Committer.When ? v1 : v2); + var oldest = matchingVersionsOnceIncremented.Aggregate((v1, v2) => v1.Version.BaseVersionSource.CommitterWhen < v2.Version.BaseVersionSource.CommitterWhen ? v1 : v2); baseVersionWithOldestSource = oldest.Version; maxVersion = oldest; log.Info($"Found multiple base versions which will produce the same SemVer ({maxVersion.IncrementedVersion}), taking oldest source for commit counting ({baseVersionWithOldestSource.Source})"); @@ -85,7 +85,7 @@ public BaseVersion GetBaseVersion() baseVersionWithOldestSource = baseVersions .Where(v => v.Version.BaseVersionSource != null) .OrderByDescending(v => v.IncrementedVersion) - .ThenByDescending(v => v.Version.BaseVersionSource.Committer.When) + .ThenByDescending(v => v.Version.BaseVersionSource.CommitterWhen) .First() .Version; } diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/BaseVersion.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/BaseVersion.cs index a71d68ffe4..e561dee29a 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/BaseVersion.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/BaseVersion.cs @@ -1,5 +1,3 @@ -using LibGit2Sharp; - namespace GitVersion.VersionCalculation { public class BaseVersion diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs index 8f97d37fdb..d8078dcd98 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using GitVersion.Common; -using LibGit2Sharp; namespace GitVersion.VersionCalculation { @@ -20,21 +19,13 @@ public FallbackVersionStrategy(IRepositoryMetadataProvider repositoryMetadataPro } public override IEnumerable GetVersions() { - Commit baseVersionSource; var currentBranchTip = Context.CurrentBranch.Tip; if (currentBranchTip == null) { throw new GitVersionException("No commits found on the current branch."); } - try - { - baseVersionSource = repositoryMetadataProvider.GetBaseVersionSource(currentBranchTip); - } - catch (NotFoundException exception) - { - throw new GitVersionException($"Cannot find commit {currentBranchTip.Sha}. Please ensure that the repository is an unshallow clone with `git fetch --unshallow`.", exception); - } + var baseVersionSource = repositoryMetadataProvider.GetBaseVersionSource(currentBranchTip); yield return new BaseVersion("Fallback base version", false, new SemanticVersion(minor: 1), baseVersionSource, null); } diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs index 6ce2f0c609..3198468d9d 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs @@ -5,7 +5,6 @@ using GitVersion.Configuration; using GitVersion.Extensions; using GitVersion.Logging; -using LibGit2Sharp; namespace GitVersion.VersionCalculation { diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs index 4b87a9418d..e021bf8507 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs @@ -3,7 +3,6 @@ using System.Linq; using GitVersion.Common; using GitVersion.Extensions; -using LibGit2Sharp; namespace GitVersion.VersionCalculation { @@ -35,10 +34,8 @@ internal IEnumerable GetTaggedVersions(Branch currentBranch, DateTi .SelectMany(commit => { return allTags.Where(t => IsValidTag(t.Item1, commit)); }) .Select(t => { - if (t.Item1.PeeledTarget() is Commit) - return new VersionTaggedCommit(t.Item1.PeeledTarget() as Commit, t.Item2, t.Item1.FriendlyName); - - return null; + var commit = t.Item1.PeeledTargetCommit(); + return commit != null ? new VersionTaggedCommit(commit, t.Item2, t.Item1.FriendlyName) : null; }) .Where(versionTaggedCommit => versionTaggedCommit != null) .Select(versionTaggedCommit => CreateBaseVersion(Context, versionTaggedCommit)) @@ -62,7 +59,8 @@ protected virtual string FormatSource(VersionTaggedCommit version) protected virtual bool IsValidTag(Tag tag, Commit commit) { - return tag.PeeledTarget() == commit; + var targetCommit = tag.PeeledTargetCommit(); + return targetCommit != null && targetCommit == commit; } protected class VersionTaggedCommit diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs index c587d3931a..7d855a4c49 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs @@ -3,7 +3,6 @@ using System.Linq; using GitVersion.Common; using GitVersion.Configuration; -using LibGit2Sharp; namespace GitVersion.VersionCalculation { diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs index 494bef8e52..12549e2fb0 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs @@ -3,7 +3,6 @@ using GitVersion.Common; using GitVersion.Configuration; using GitVersion.Extensions; -using LibGit2Sharp; namespace GitVersion.VersionCalculation { diff --git a/src/GitVersionCore/VersionCalculation/Cache/GitVersionCacheKeyFactory.cs b/src/GitVersionCore/VersionCalculation/Cache/GitVersionCacheKeyFactory.cs index 2b05686e2e..71cc621595 100644 --- a/src/GitVersionCore/VersionCalculation/Cache/GitVersionCacheKeyFactory.cs +++ b/src/GitVersionCore/VersionCalculation/Cache/GitVersionCacheKeyFactory.cs @@ -8,7 +8,6 @@ using GitVersion.Configuration; using GitVersion.Logging; using GitVersion.Model.Configuration; -using LibGit2Sharp; using Microsoft.Extensions.Options; namespace GitVersion.VersionCalculation.Cache @@ -19,13 +18,17 @@ public class GitVersionCacheKeyFactory : IGitVersionCacheKeyFactory private readonly ILog log; private readonly IOptions options; private readonly IConfigFileLocator configFileLocator; + private readonly IGitRepository gitRepository; - public GitVersionCacheKeyFactory(IFileSystem fileSystem, ILog log, IOptions options, IConfigFileLocator configFileLocator) + public GitVersionCacheKeyFactory(IFileSystem fileSystem, ILog log, + IOptions options, IConfigFileLocator configFileLocator, + IGitRepository gitRepository) { this.fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); this.log = log ?? throw new ArgumentNullException(nameof(log)); this.options = options ?? throw new ArgumentNullException(nameof(options)); this.configFileLocator = configFileLocator ?? throw new ArgumentNullException(nameof(configFileLocator)); + this.gitRepository = gitRepository ?? throw new ArgumentNullException(nameof(gitRepository)); } public GitVersionCacheKey Create(Config overrideConfig) @@ -141,9 +144,7 @@ private List CalculateDirectoryContents(string root) private string GetRepositorySnapshotHash() { - using var repo = new Repository(options.Value.GitRootPath); - - var head = repo.Head; + var head = gitRepository.Head; if (head.Tip == null) { return head.CanonicalName; diff --git a/src/GitVersionCore/VersionCalculation/IncrementStrategyFinder.cs b/src/GitVersionCore/VersionCalculation/IncrementStrategyFinder.cs index 670f1accac..97d37c8665 100644 --- a/src/GitVersionCore/VersionCalculation/IncrementStrategyFinder.cs +++ b/src/GitVersionCore/VersionCalculation/IncrementStrategyFinder.cs @@ -2,10 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using GitVersion.Configuration; -using GitVersion.Extensions; using GitVersion.VersionCalculation; -using LibGit2Sharp; namespace GitVersion { @@ -31,7 +28,7 @@ public static class IncrementStrategyFinder private static readonly Regex DefaultPatchPatternRegex = new Regex(DefaultPatchPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex DefaultNoBumpPatternRegex = new Regex(DefaultNoBumpPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); - public static VersionField? DetermineIncrementedField(IRepository repository, GitVersionContext context, BaseVersion baseVersion) + public static VersionField? DetermineIncrementedField(IGitRepository repository, GitVersionContext context, BaseVersion baseVersion) { var commitMessageIncrement = FindCommitMessageIncrement(repository, context, baseVersion); var defaultIncrement = context.Configuration.Increment.ToVersionField(); @@ -57,17 +54,7 @@ public static class IncrementStrategyFinder return commitMessageIncrement; } - public static VersionField FindDefaultIncrementForBranch(GitVersionContext context, string branch = null) - { - var config = context.FullConfiguration.GetConfigForBranch(branch ?? context.CurrentBranch.NameWithoutRemote()); - if (config?.Increment != null && config.Increment != IncrementStrategy.Inherit) - { - return config.Increment.Value.ToVersionField(); - } - // Fallback to patch - return VersionField.Patch; - } public static VersionField? GetIncrementForCommits(GitVersionContext context, IEnumerable commits) { var majorRegex = TryGetRegexOrDefault(context.Configuration.MajorVersionBumpMessage, DefaultMajorPatternRegex); @@ -89,7 +76,7 @@ public static VersionField FindDefaultIncrementForBranch(GitVersionContext conte return null; } - private static VersionField? FindCommitMessageIncrement(IRepository repository, GitVersionContext context, BaseVersion baseVersion) + private static VersionField? FindCommitMessageIncrement(IGitRepository repository, GitVersionContext context, BaseVersion baseVersion) { if (context.Configuration.CommitMessageIncrementing == CommitMessageIncrementMode.Disabled) { @@ -114,7 +101,7 @@ private static Regex TryGetRegexOrDefault(string messageRegex, Regex defaultRege return CompiledRegexCache.GetOrAdd(messageRegex, pattern => new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase)); } - private static IEnumerable GetIntermediateCommits(IRepository repo, GitObject baseCommit, Commit headCommit) + private static IEnumerable GetIntermediateCommits(IGitRepository repo, Commit baseCommit, Commit headCommit) { if (baseCommit == null) yield break; @@ -122,7 +109,7 @@ private static IEnumerable GetIntermediateCommits(IRepository repo, GitO if (commitCache == null || commitCache.LastOrDefault() != headCommit) { - commitCache = GetCommitsReacheableFromHead(repo, headCommit); + commitCache = repo.GetCommitsReacheableFromHead(headCommit); intermediateCommitCache = commitCache; } @@ -144,16 +131,5 @@ private static IEnumerable GetIntermediateCommits(IRepository repo, GitO if (none.IsMatch(message)) return VersionField.None; return null; } - - private static List GetCommitsReacheableFromHead(IRepository repository, Commit headCommit) - { - var filter = new CommitFilter - { - IncludeReachableFrom = headCommit, - SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Reverse - }; - - return repository.Commits.QueryBy(filter).ToList(); - } } } diff --git a/src/GitVersionCore/VersionCalculation/MainlineVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/MainlineVersionCalculator.cs index 992ddb2b6f..e40caef2da 100644 --- a/src/GitVersionCore/VersionCalculation/MainlineVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/MainlineVersionCalculator.cs @@ -5,7 +5,6 @@ using GitVersion.Configuration; using GitVersion.Extensions; using GitVersion.Logging; -using LibGit2Sharp; namespace GitVersion.VersionCalculation { @@ -69,6 +68,7 @@ public SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion) // This will increment for any direct commits on mainline mainlineVersion = IncrementForEachCommit(directCommits, mainlineVersion, mainline); } + mainlineVersion.BuildMetaData = CreateVersionBuildMetaData(mergeBase); // branches other than master always get a bump for the act of branching @@ -102,7 +102,6 @@ public SemanticVersionBuildMetaData CreateVersionBuildMetaData(Commit baseVersio } - private SemanticVersion AggregateMergeCommitIncrement(Commit commit, List directCommits, SemanticVersion mainlineVersion, Branch mainline) { // Merge commit, process all merged commits as a batch @@ -230,13 +229,12 @@ private SemanticVersion IncrementForEachCommit(IEnumerable directCommits { foreach (var directCommit in directCommits) { - var directCommitIncrement = IncrementStrategyFinder.GetIncrementForCommits(context, new[] - { - directCommit - }) ?? IncrementStrategyFinder.FindDefaultIncrementForBranch(context, mainline.FriendlyName); + var directCommitIncrement = IncrementStrategyFinder.GetIncrementForCommits(context, new[] { directCommit }) + ?? FindDefaultIncrementForBranch(context, mainline.FriendlyName); mainlineVersion = mainlineVersion.IncrementVersion(directCommitIncrement); log.Info($"Direct commit on master {directCommit.Sha} incremented base versions {directCommitIncrement}, now {mainlineVersion}"); } + return mainlineVersion; } @@ -245,7 +243,7 @@ private VersionField FindMessageIncrement(Commit mergeCommit, Commit mergedHead, var commits = repositoryMetadataProvider.GetMergeBaseCommits(mergeCommit, mergedHead, findMergeBase); commitLog.RemoveAll(c => commits.Any(c1 => c1.Sha == c.Sha)); return IncrementStrategyFinder.GetIncrementForCommits(context, commits) - ?? TryFindIncrementFromMergeMessage(mergeCommit); + ?? TryFindIncrementFromMergeMessage(mergeCommit); } private VersionField TryFindIncrementFromMergeMessage(Commit mergeCommit) @@ -264,7 +262,19 @@ private VersionField TryFindIncrementFromMergeMessage(Commit mergeCommit) } // Fallback to config increment value - return IncrementStrategyFinder.FindDefaultIncrementForBranch(context); + return FindDefaultIncrementForBranch(context); + } + + private static VersionField FindDefaultIncrementForBranch(GitVersionContext context, string branch = null) + { + var config = context.FullConfiguration.GetConfigForBranch(branch ?? context.CurrentBranch.NameWithoutRemote()); + if (config?.Increment != null && config.Increment != IncrementStrategy.Inherit) + { + return config.Increment.Value.ToVersionField(); + } + + // Fallback to patch + return VersionField.Patch; } private static Commit GetMergedHead(Commit mergeCommit) diff --git a/src/GitVersionExe/GitVersionExecutor.cs b/src/GitVersionExe/GitVersionExecutor.cs index 2586d09064..d32d281b49 100644 --- a/src/GitVersionExe/GitVersionExecutor.cs +++ b/src/GitVersionExe/GitVersionExecutor.cs @@ -82,7 +82,7 @@ private int RunGitVersionTool(GitVersionOptions gitVersionOptions) try { - LibGitExtensions.DumpGraph(gitVersionOptions.WorkingDirectory, mess => log.Info(mess), 100); + GitExtensions.DumpGraph(gitVersionOptions.WorkingDirectory, mess => log.Info(mess), 100); } catch (Exception dumpGraphException) { @@ -138,7 +138,7 @@ private bool HandleNonMainCommand(GitVersionOptions gitVersionOptions, out int e if (gitVersionOptions.Diag) { log.Info("Dumping commit graph: "); - LibGitExtensions.DumpGraph(workingDirectory, mess => log.Info(mess), 100); + GitExtensions.DumpGraph(workingDirectory, mess => log.Info(mess), 100); } if (!Directory.Exists(workingDirectory))