diff --git a/GitVersionCore.Tests/GitVersionContextBuilder.cs b/GitVersionCore.Tests/GitVersionContextBuilder.cs new file mode 100644 index 0000000000..9894e3fcba --- /dev/null +++ b/GitVersionCore.Tests/GitVersionContextBuilder.cs @@ -0,0 +1,76 @@ +namespace GitVersionCore.Tests +{ + using GitVersion; + using LibGit2Sharp; + + public class GitVersionContextBuilder + { + IRepository repository; + Config config; + + public GitVersionContextBuilder WithRepository(IRepository repository) + { + this.repository = repository; + return this; + } + + public GitVersionContextBuilder WithConfig(Config config) + { + this.config = config; + return this; + } + + public GitVersionContextBuilder WithTaggedMaster() + { + repository = CreateRepository(); + var target = repository.Head.Tip; + ((MockTagCollection)repository.Tags).Add(new MockTag ("1.0.0", target)); + ((MockBranch)repository.Head).Add(new MockCommit { CommitterEx = SignatureBuilder.SignatureNow() }); + return this; + } + + public GitVersionContextBuilder AddCommit() + { + ((MockBranch)repository.Head).Add(new MockCommit()); + return this; + } + + public GitVersionContextBuilder WithDevelopBranch() + { + return WithBranch("develop"); + } + + public GitVersionContextBuilder WithBranch(string branchName) + { + repository = CreateRepository(); + var mockBranch = new MockBranch(branchName) + { + new MockCommit() + }; + ((MockBranchCollection)repository.Branches).Add(mockBranch); + ((MockRepository)repository).Head = mockBranch; + return this; + } + + public GitVersionContext Build() + { + return new GitVersionContext(repository ?? CreateRepository(), config ?? new Config()); + } + + IRepository CreateRepository() + { + var mockBranch = new MockBranch("master") { new MockCommit { CommitterEx = SignatureBuilder.SignatureNow() } }; + var mockRepository = new MockRepository + { + Branches = new MockBranchCollection + { + mockBranch + }, + Tags = new MockTagCollection(), + Head = mockBranch + }; + + return mockRepository; + } + } +} \ No newline at end of file diff --git a/GitVersionCore.Tests/GitVersionCore.Tests.csproj b/GitVersionCore.Tests/GitVersionCore.Tests.csproj index 1ee1fb42dc..1874bf8dfd 100644 --- a/GitVersionCore.Tests/GitVersionCore.Tests.csproj +++ b/GitVersionCore.Tests/GitVersionCore.Tests.csproj @@ -115,6 +115,15 @@ + + + + + + + + + diff --git a/GitVersionCore.Tests/Mocks/MockTag.cs b/GitVersionCore.Tests/Mocks/MockTag.cs index 4ea1364815..5400fb5ee0 100644 --- a/GitVersionCore.Tests/Mocks/MockTag.cs +++ b/GitVersionCore.Tests/Mocks/MockTag.cs @@ -15,6 +15,15 @@ public override GitObject Target get { return TargetEx; } } public TagAnnotation AnnotationEx; + + public MockTag() { } + + public MockTag(string name, Commit target) + { + NameEx = name; + TargetEx = target; + } + public override TagAnnotation Annotation { get { return AnnotationEx; } diff --git a/GitVersionCore.Tests/TestEffectiveConfiguration.cs b/GitVersionCore.Tests/TestEffectiveConfiguration.cs index 055be59556..e6ef0a893e 100644 --- a/GitVersionCore.Tests/TestEffectiveConfiguration.cs +++ b/GitVersionCore.Tests/TestEffectiveConfiguration.cs @@ -10,7 +10,7 @@ public TestEffectiveConfiguration( string gitTagPrefix = "v", string tag = "", string nextVersion = null) : - base(assemblyVersioningScheme, versioningMode, gitTagPrefix, tag, nextVersion) + base(assemblyVersioningScheme, versioningMode, gitTagPrefix, tag, nextVersion, IncrementStrategy.Patch) { } } diff --git a/GitVersionCore.Tests/VersionCalculation/BaseVersionCalculatorTests.cs b/GitVersionCore.Tests/VersionCalculation/BaseVersionCalculatorTests.cs new file mode 100644 index 0000000000..f2fe0e1afe --- /dev/null +++ b/GitVersionCore.Tests/VersionCalculation/BaseVersionCalculatorTests.cs @@ -0,0 +1,86 @@ +namespace GitVersionCore.Tests.VersionCalculation +{ + using System; + using GitVersion; + using GitVersion.VersionCalculation; + using GitVersion.VersionCalculation.BaseVersionCalculators; + using LibGit2Sharp; + using NUnit.Framework; + using Shouldly; + + [TestFixture] + public class BaseVersionCalculatorTests + { + [Test] + public void ChoosesHighestVersionReturnedFromStrategies() + { + var context = new GitVersionContextBuilder().Build(); + var dateTimeOffset = DateTimeOffset.Now; + var sut = new BaseVersionCalculator(new V1Strategy(DateTimeOffset.Now), new V2Strategy(dateTimeOffset)); + + var baseVersion = sut.GetBaseVersion(context); + + baseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); + baseVersion.ShouldIncrement.ShouldBe(true); + baseVersion.BaseVersionSource.When().ShouldBe(dateTimeOffset); + } + + [Test] + public void UsesWhenFromNextBestMatchIfHighestDoesntHaveWhen() + { + var context = new GitVersionContextBuilder().Build(); + var when = DateTimeOffset.Now; + var sut = new BaseVersionCalculator(new V1Strategy(when), new V2Strategy(null)); + + var baseVersion = sut.GetBaseVersion(context); + + baseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); + baseVersion.ShouldIncrement.ShouldBe(true); + baseVersion.BaseVersionSource.When().ShouldBe(when); + } + + [Test] + public void UsesWhenFromNextBestMatchIfHighestDoesntHaveWhenReversedOrder() + { + var context = new GitVersionContextBuilder().Build(); + var when = DateTimeOffset.Now; + var sut = new BaseVersionCalculator(new V1Strategy(null), new V2Strategy(when)); + + var baseVersion = sut.GetBaseVersion(context); + + baseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); + baseVersion.ShouldIncrement.ShouldBe(true); + baseVersion.BaseVersionSource.When().ShouldBe(when); + } + + class V1Strategy : BaseVersionStrategy + { + readonly Commit when; + + public V1Strategy(DateTimeOffset? when) + { + this.when = when == null ? null : new MockCommit { CommitterEx = when.Value.ToSignature() }; + } + + public override BaseVersion GetVersion(GitVersionContext context) + { + return new BaseVersion(false, new SemanticVersion(1), when); + } + } + + class V2Strategy : BaseVersionStrategy + { + Commit when; + + public V2Strategy(DateTimeOffset? when) + { + this.when = when == null ? null : new MockCommit { CommitterEx = when.Value.ToSignature() }; + } + + public override BaseVersion GetVersion(GitVersionContext context) + { + return new BaseVersion(true, new SemanticVersion(2), when); + } + } + } +} \ No newline at end of file diff --git a/GitVersionCore.Tests/VersionCalculation/NewNextVersionCalculatorTests.cs b/GitVersionCore.Tests/VersionCalculation/NewNextVersionCalculatorTests.cs new file mode 100644 index 0000000000..4b934a362c --- /dev/null +++ b/GitVersionCore.Tests/VersionCalculation/NewNextVersionCalculatorTests.cs @@ -0,0 +1,62 @@ +namespace GitVersionCore.Tests.VersionCalculation +{ + using System; + using GitVersion; + using GitVersion.VersionCalculation; + using NUnit.Framework; + using Shouldly; + + public class NewNextVersionCalculatorTests + { + [Test] + public void ShouldIncrementVersionBasedOnConfig() + { + var baseCalculator = new TestBaseVersionCalculator(true, new SemanticVersion(1), new MockCommit()); + var semanticVersionBuildMetaData = new SemanticVersionBuildMetaData(1, "master", "b1a34e", DateTimeOffset.Now); + var sut = new NewNextVersionCalculator(baseCalculator, new TestMetaDataCalculator(semanticVersionBuildMetaData)); + var config = new Config(); + config.Branches.Add("master", new BranchConfig + { + Increment = IncrementStrategy.Major + }); + var context = new GitVersionContextBuilder().WithConfig(config).Build(); + + var version = sut.FindVersion(context); + + version.ToString().ShouldBe("2.0.0"); + } + + [Test] + public void DoesNotIncrementWhenBaseVersionSaysNotTo() + { + var baseCalculator = new TestBaseVersionCalculator(false, new SemanticVersion(1), new MockCommit()); + var semanticVersionBuildMetaData = new SemanticVersionBuildMetaData(1, "master", "b1a34e", DateTimeOffset.Now); + var sut = new NewNextVersionCalculator(baseCalculator, new TestMetaDataCalculator(semanticVersionBuildMetaData)); + var config = new Config(); + config.Branches.Add("master", new BranchConfig + { + Increment = IncrementStrategy.Major + }); + var context = new GitVersionContextBuilder().WithConfig(config).Build(); + + var version = sut.FindVersion(context); + + version.ToString().ShouldBe("1.0.0"); + } + + [Test] + public void AppliesBranchPreReleaseTag() + { + var baseCalculator = new TestBaseVersionCalculator(false, new SemanticVersion(1), new MockCommit()); + var semanticVersionBuildMetaData = new SemanticVersionBuildMetaData(1, "develop", "b1a34e", DateTimeOffset.Now); + var sut = new NewNextVersionCalculator(baseCalculator, new TestMetaDataCalculator(semanticVersionBuildMetaData)); + var context = new GitVersionContextBuilder() + .WithDevelopBranch() + .Build(); + + var version = sut.FindVersion(context); + + version.ToString().ShouldBe("1.0.0-unstable.1"); + } + } +} \ No newline at end of file diff --git a/GitVersionCore.Tests/VersionCalculation/Strategies/ConfigNextVersionBaseVersionStrategyTests.cs b/GitVersionCore.Tests/VersionCalculation/Strategies/ConfigNextVersionBaseVersionStrategyTests.cs new file mode 100644 index 0000000000..d68469ccfd --- /dev/null +++ b/GitVersionCore.Tests/VersionCalculation/Strategies/ConfigNextVersionBaseVersionStrategyTests.cs @@ -0,0 +1,38 @@ +namespace GitVersionCore.Tests.VersionCalculation.Strategies +{ + using GitVersion; + using GitVersion.VersionCalculation.BaseVersionCalculators; + using NUnit.Framework; + using Shouldly; + + [TestFixture] + public class ConfigNextVersionBaseVersionStrategyTests + { + [Test] + public void ShouldNotBeIncremented() + { + var contextBuilder = new GitVersionContextBuilder() + .WithConfig(new Config + { + NextVersion = "1.0.0" + }); + var sut = new ConfigNextVersionBaseVersionStrategy(); + + var baseVersion = sut.GetVersion(contextBuilder.Build()); + + baseVersion.ShouldIncrement.ShouldBe(false); + baseVersion.SemanticVersion.ToString().ShouldBe("1.0.0"); + } + + [Test] + public void ReturnsNullWhenNoNextVersionIsInConfig() + { + var contextBuilder = new GitVersionContextBuilder(); + var sut = new ConfigNextVersionBaseVersionStrategy(); + + var baseVersion = sut.GetVersion(contextBuilder.Build()); + + baseVersion.ShouldBe(null); + } + } +} \ No newline at end of file diff --git a/GitVersionCore.Tests/VersionCalculation/Strategies/LastTagBaseVersionStrategyTests.cs b/GitVersionCore.Tests/VersionCalculation/Strategies/LastTagBaseVersionStrategyTests.cs new file mode 100644 index 0000000000..0f99fcd46f --- /dev/null +++ b/GitVersionCore.Tests/VersionCalculation/Strategies/LastTagBaseVersionStrategyTests.cs @@ -0,0 +1,37 @@ +namespace GitVersionCore.Tests.VersionCalculation.Strategies +{ + using GitVersion.VersionCalculation.BaseVersionCalculators; + using NUnit.Framework; + using Shouldly; + + [TestFixture] + public class LastTagBaseVersionStrategyTests + { + [Test] + public void ShouldAllowVersionIncrement() + { + var context = new GitVersionContextBuilder() + .WithTaggedMaster() + .AddCommit() + .Build(); + var sut = new LastTagBaseVersionStrategy(); + + var baseVersion = sut.GetVersion(context); + + baseVersion.ShouldIncrement.ShouldBe(true); + } + + [Test] + public void ShouldNotAllowVersionIncrementWhenTagComesFromCurrentCommit() + { + var context = new GitVersionContextBuilder() + .WithTaggedMaster() + .Build(); + var sut = new LastTagBaseVersionStrategy(); + + var baseVersion = sut.GetVersion(context); + + baseVersion.ShouldIncrement.ShouldBe(false); + } + } +} \ No newline at end of file diff --git a/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs b/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs new file mode 100644 index 0000000000..ac22497e58 --- /dev/null +++ b/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs @@ -0,0 +1,110 @@ +namespace GitVersionCore.Tests.VersionCalculation.Strategies +{ + using System.Collections.Generic; + using GitVersion.VersionCalculation.BaseVersionCalculators; + using LibGit2Sharp; + using NUnit.Framework; + using Shouldly; + + [TestFixture] + public class MergeMessageBaseVersionStrategyTests + { + [Test] + public void ShouldAllowIncrementOfVersion() + { + var context = new GitVersionContextBuilder().WithRepository(new MockRepository + { + Head = new MockBranch("master") { new MockCommit + { + MessageEx = "Merge branch 'hotfix-0.1.5'", + ParentsEx = GetParents(true) + } } + }).Build(); + var sut = new MergeMessageBaseVersionStrategy(); + + var baseVersion = sut.GetVersion(context); + + baseVersion.ShouldIncrement.ShouldBe(true); + } + + [TestCase("Merge branch 'hotfix-0.1.5'", false, null)] + [TestCase("Merge branch 'develop' of github.com:Particular/NServiceBus into develop", true, null)] + [TestCase("Merge branch '4.0.3'", true, "4.0.3")] //TODO: possible make it a config option to support this + [TestCase("Merge branch 'release-10.10.50'", true, "10.10.50")] + [TestCase("Merge branch 's'", true, null)] // Must start with a number + [TestCase("Merge branch 'release-0.2.0'", true, "0.2.0")] + [TestCase("Merge branch 'hotfix-4.6.6' into support-4.6", true, "4.6.6")] + [TestCase("Merge branch 'hotfix-10.10.50'", true, "10.10.50")] + [TestCase("Merge branch 'hotfix-0.1.5'", true, "0.1.5")] + [TestCase("Merge branch 'hotfix-0.1.5'\n\nRelates to: TicketId", true, "0.1.5")] + [TestCase("Merge branch 'alpha-0.1.5'", true, "0.1.5")] + [TestCase("Merge pull request #165 from Particular/release-1.0.0", true, "1.0.0")] + [TestCase("Merge pull request #95 from Particular/issue-94", false, null)] + [TestCase("Merge pull request #165 in Particular/release-1.0.0", true, "1.0.0")] + [TestCase("Merge pull request #95 in Particular/issue-94", true, null)] + [TestCase("Merge pull request #95 in Particular/issue-94", false, null)] + [TestCase("Merge pull request #64 from arledesma/feature-VS2013_3rd_party_test_framework_support", true, null)] + [TestCase("Finish Release-0.12.0", true, "0.12.0")] //Support Syntevo SmartGit/Hg's Gitflow merge commit messages for finishing a 'Release' branch + [TestCase("Finish 0.14.1", true, "0.14.1")] //Support Syntevo SmartGit/Hg's Gitflow merge commit messages for finishing a 'Hotfix' branch + public void AssertMergeMessage(string message, bool isMergeCommit, string expectedVersion) + { + var parents = GetParents(isMergeCommit); + AssertMergeMessage(message, expectedVersion, parents); + AssertMergeMessage(message + " ", expectedVersion, parents); + AssertMergeMessage(message + "\r ", expectedVersion, parents); + AssertMergeMessage(message + "\r", expectedVersion, parents); + AssertMergeMessage(message + "\r\n", expectedVersion, parents); + AssertMergeMessage(message + "\r\n ", expectedVersion, parents); + AssertMergeMessage(message + "\n", expectedVersion, parents); + AssertMergeMessage(message + "\n ", expectedVersion, parents); + } + + static void AssertMergeMessage(string message, string expectedVersion, List parents) + { + var commit = new MockCommit + { + MessageEx = message, + ParentsEx = parents + }; + + var context = new GitVersionContextBuilder() + .WithRepository(new MockRepository + { + Head = new MockBranch("master") + { + commit, + new MockCommit() + } + }) + .Build(); + var sut = new MergeMessageBaseVersionStrategy(); + + var baseVersion = sut.GetVersion(context); + + if (expectedVersion == null) + { + baseVersion.ShouldBe(null); + } + else + { + baseVersion.SemanticVersion.ToString().ShouldBe(expectedVersion); + } + } + + static List GetParents(bool isMergeCommit) + { + if (isMergeCommit) + { + return new List + { + null, + null + }; + } + return new List + { + null + }; + } + } +} \ No newline at end of file diff --git a/GitVersionCore.Tests/VersionCalculation/Strategies/VersionInBranchBaseVersionStrategyTests.cs b/GitVersionCore.Tests/VersionCalculation/Strategies/VersionInBranchBaseVersionStrategyTests.cs new file mode 100644 index 0000000000..bffb3f0a0e --- /dev/null +++ b/GitVersionCore.Tests/VersionCalculation/Strategies/VersionInBranchBaseVersionStrategyTests.cs @@ -0,0 +1,34 @@ +namespace GitVersionCore.Tests.VersionCalculation.Strategies +{ + using GitVersion.VersionCalculation.BaseVersionCalculators; + using NUnit.Framework; + using Shouldly; + + [TestFixture] + public class VersionInBranchBaseVersionStrategyTests + { + [Test] + [TestCase("release-2.0.0", "2.0.0")] + [TestCase("release/2.0.0", "2.0.0")] + [TestCase("hotfix-2.0.0", "2.0.0")] + [TestCase("hotfix/2.0.0", "2.0.0")] + [TestCase("hotfix/2.0.0", "2.0.0")] + [TestCase("feature/JIRA-123", null)] + public void CanTakeVersionFromBranchName(string branchName, string expectedBaseVersion) + { + var context = new GitVersionContextBuilder() + .WithBranch(branchName) + .AddCommit() + .Build(); + + var sut = new VersionInBranchBaseVersionStrategy(); + + var baseVersion = sut.GetVersion(context); + + if (expectedBaseVersion == null) + baseVersion.ShouldBe(null); + else + baseVersion.SemanticVersion.ToString().ShouldBe(expectedBaseVersion); + } + } +} diff --git a/GitVersionCore.Tests/VersionCalculation/TestBaseVersionCalculator.cs b/GitVersionCore.Tests/VersionCalculation/TestBaseVersionCalculator.cs new file mode 100644 index 0000000000..c094ad625e --- /dev/null +++ b/GitVersionCore.Tests/VersionCalculation/TestBaseVersionCalculator.cs @@ -0,0 +1,26 @@ +namespace GitVersionCore.Tests.VersionCalculation +{ + using GitVersion; + using GitVersion.VersionCalculation; + using GitVersion.VersionCalculation.BaseVersionCalculators; + using LibGit2Sharp; + + public class TestBaseVersionCalculator : IBaseVersionCalculator + { + readonly SemanticVersion semanticVersion; + bool shouldIncrement; + Commit source; + + public TestBaseVersionCalculator(bool shouldIncrement, SemanticVersion semanticVersion, Commit source) + { + this.semanticVersion = semanticVersion; + this.source = source; + this.shouldIncrement = shouldIncrement; + } + + public BaseVersion GetBaseVersion(GitVersionContext context) + { + return new BaseVersion(shouldIncrement, semanticVersion, source); + } + } +} \ No newline at end of file diff --git a/GitVersionCore.Tests/VersionCalculation/TestMetaDataCalculator.cs b/GitVersionCore.Tests/VersionCalculation/TestMetaDataCalculator.cs new file mode 100644 index 0000000000..51f7a95b22 --- /dev/null +++ b/GitVersionCore.Tests/VersionCalculation/TestMetaDataCalculator.cs @@ -0,0 +1,21 @@ +namespace GitVersionCore.Tests.VersionCalculation +{ + using GitVersion; + using GitVersion.VersionCalculation; + using LibGit2Sharp; + + public class TestMetaDataCalculator : IMetaDataCalculator + { + SemanticVersionBuildMetaData metaData; + + public TestMetaDataCalculator(SemanticVersionBuildMetaData metaData) + { + this.metaData = metaData; + } + + public SemanticVersionBuildMetaData Create(Commit baseVersionSource, GitVersionContext context) + { + return metaData; + } + } +} \ No newline at end of file diff --git a/GitVersionCore/Configuration/BranchConfig.cs b/GitVersionCore/Configuration/BranchConfig.cs index 1b7494e0f0..e0f10d561e 100644 --- a/GitVersionCore/Configuration/BranchConfig.cs +++ b/GitVersionCore/Configuration/BranchConfig.cs @@ -9,5 +9,8 @@ public class BranchConfig [YamlAlias("tag")] public string Tag { get; set; } + + [YamlAlias("increment")] + public IncrementStrategy? Increment { get; set; } } } diff --git a/GitVersionCore/Configuration/Config.cs b/GitVersionCore/Configuration/Config.cs index ed487c57db..9157e84293 100644 --- a/GitVersionCore/Configuration/Config.cs +++ b/GitVersionCore/Configuration/Config.cs @@ -18,6 +18,7 @@ public Config() Branches["develop"] = new BranchConfig { Tag = "unstable", + Increment = IncrementStrategy.Minor, VersioningMode = GitVersion.VersioningMode.ContinuousDeployment }; } diff --git a/GitVersionCore/Configuration/IncrementStrategy.cs b/GitVersionCore/Configuration/IncrementStrategy.cs new file mode 100644 index 0000000000..49c251a9b2 --- /dev/null +++ b/GitVersionCore/Configuration/IncrementStrategy.cs @@ -0,0 +1,10 @@ +namespace GitVersion +{ + public enum IncrementStrategy + { + None, + Major, + Minor, + Patch + } +} \ No newline at end of file diff --git a/GitVersionCore/EffectiveConfiguration.cs b/GitVersionCore/EffectiveConfiguration.cs index 9daed63748..5a8211349f 100644 --- a/GitVersionCore/EffectiveConfiguration.cs +++ b/GitVersionCore/EffectiveConfiguration.cs @@ -5,13 +5,14 @@ /// public class EffectiveConfiguration { - public EffectiveConfiguration(AssemblyVersioningScheme assemblyVersioningScheme, VersioningMode versioningMode, string gitTagPrefix, string tag, string nextVersion) + public EffectiveConfiguration(AssemblyVersioningScheme assemblyVersioningScheme, VersioningMode versioningMode, string gitTagPrefix, string tag, string nextVersion, IncrementStrategy increment) { AssemblyVersioningScheme = assemblyVersioningScheme; VersioningMode = versioningMode; GitTagPrefix = gitTagPrefix; Tag = tag; NextVersion = nextVersion; + Increment = increment; } public VersioningMode VersioningMode { get; private set; } @@ -29,5 +30,6 @@ public EffectiveConfiguration(AssemblyVersioningScheme assemblyVersioningScheme, public string Tag { get; private set; } public string NextVersion { get; private set; } + public IncrementStrategy Increment { get; private set; } } } \ No newline at end of file diff --git a/GitVersionCore/GitVersionContext.cs b/GitVersionCore/GitVersionContext.cs index 3bd8a0ef7e..c858437386 100644 --- a/GitVersionCore/GitVersionContext.cs +++ b/GitVersionCore/GitVersionContext.cs @@ -88,8 +88,9 @@ void CalculateEffectiveConfiguration() var versioningMode = currentBranchConfig.VersioningMode ?? configuration.VersioningMode ?? VersioningMode.ContinuousDelivery; var tag = currentBranchConfig.Tag; var nextVersion = configuration.NextVersion; + var incrementStrategy = currentBranchConfig.Increment ?? IncrementStrategy.Patch; - Configuration = new EffectiveConfiguration(configuration.AssemblyVersioningScheme, versioningMode, configuration.TagPrefix, tag, nextVersion); + Configuration = new EffectiveConfiguration(configuration.AssemblyVersioningScheme, versioningMode, configuration.TagPrefix, tag, nextVersion, incrementStrategy); } BranchConfig GetBranchConfiguration(KeyValuePair[] matchingBranches) diff --git a/GitVersionCore/GitVersionCore.csproj b/GitVersionCore/GitVersionCore.csproj index a906b5bb25..cd29124ef3 100644 --- a/GitVersionCore/GitVersionCore.csproj +++ b/GitVersionCore/GitVersionCore.csproj @@ -74,6 +74,7 @@ + @@ -87,6 +88,17 @@ + + + + + + + + + + + @@ -144,6 +156,7 @@ + diff --git a/GitVersionCore/GitVersionFinder.cs b/GitVersionCore/GitVersionFinder.cs index 418c934ec7..41a71066f9 100644 --- a/GitVersionCore/GitVersionFinder.cs +++ b/GitVersionCore/GitVersionFinder.cs @@ -10,6 +10,8 @@ public SemanticVersion FindVersion(GitVersionContext context) Logger.WriteInfo("Running against branch: " + context.CurrentBranch.Name); EnsureMainTopologyConstraints(context); + //return new NewNextVersionCalculator().FindVersion(context); + if (ShouldGitHubFlowVersioningSchemeApply(context.Repository)) { Logger.WriteInfo("GitHubFlow version strategy will be used"); diff --git a/GitVersionCore/MergeMessageParser.cs b/GitVersionCore/MergeMessageParser.cs index e97b741335..a477a4da27 100644 --- a/GitVersionCore/MergeMessageParser.cs +++ b/GitVersionCore/MergeMessageParser.cs @@ -6,14 +6,14 @@ namespace GitVersion static class MergeMessageParser { - public static bool TryParse(Commit mergeCommit, EffectiveConfiguration configuration, out SemanticVersion shortVersion) + public static bool TryParse(Commit mergeCommit, EffectiveConfiguration configuration, out SemanticVersion semanticVersion) { string versionPart; if (Inner(mergeCommit, out versionPart)) { - return SemanticVersion.TryParse(versionPart, configuration.GitTagPrefix, out shortVersion); + return SemanticVersion.TryParse(versionPart, configuration.GitTagPrefix, out semanticVersion); } - shortVersion = null; + semanticVersion = null; return false; } diff --git a/GitVersionCore/SemanticVersion.cs b/GitVersionCore/SemanticVersion.cs index 77e628ce14..e5a4778854 100644 --- a/GitVersionCore/SemanticVersion.cs +++ b/GitVersionCore/SemanticVersion.cs @@ -15,8 +15,11 @@ public class SemanticVersion : IFormattable, IComparable public SemanticVersionPreReleaseTag PreReleaseTag; public SemanticVersionBuildMetaData BuildMetaData; - public SemanticVersion() + public SemanticVersion(int major = 0, int minor = 0, int patch = 0) { + Major = major; + Minor = minor; + Patch = patch; PreReleaseTag = new SemanticVersionPreReleaseTag(); BuildMetaData = new SemanticVersionBuildMetaData(); } diff --git a/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs b/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs new file mode 100644 index 0000000000..ebe6c56153 --- /dev/null +++ b/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs @@ -0,0 +1,31 @@ +namespace GitVersion.VersionCalculation +{ + using System.Linq; + using BaseVersionCalculators; + + public class BaseVersionCalculator : IBaseVersionCalculator + { + readonly BaseVersionStrategy[] strategies; + + public BaseVersionCalculator(params BaseVersionStrategy[] strategies) + { + this.strategies = strategies; + } + + public BaseVersion GetBaseVersion(GitVersionContext context) + { + return strategies + .Select(s => s.GetVersion(context)) + .Where(v => v != null) + .Aggregate((v1, v2) => + { + if (v1.SemanticVersion > v2.SemanticVersion) + { + return new BaseVersion(v1.ShouldIncrement, v1.SemanticVersion, v1.BaseVersionSource ?? v2.BaseVersionSource); + } + + return new BaseVersion(v2.ShouldIncrement, v2.SemanticVersion, v2.BaseVersionSource ?? v1.BaseVersionSource); + }); + } + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/BaseVersionCalculators/BaseVersion.cs b/GitVersionCore/VersionCalculation/BaseVersionCalculators/BaseVersion.cs new file mode 100644 index 0000000000..9f4b417db9 --- /dev/null +++ b/GitVersionCore/VersionCalculation/BaseVersionCalculators/BaseVersion.cs @@ -0,0 +1,20 @@ +namespace GitVersion.VersionCalculation.BaseVersionCalculators +{ + using LibGit2Sharp; + + public class BaseVersion + { + public BaseVersion(bool shouldIncrement, SemanticVersion semanticVersion, Commit baseVersionSource) + { + ShouldIncrement = shouldIncrement; + SemanticVersion = semanticVersion; + BaseVersionSource = baseVersionSource; + } + + public bool ShouldIncrement { get; private set; } + + public SemanticVersion SemanticVersion { get; private set; } + + public Commit BaseVersionSource { get; private set; } + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/BaseVersionCalculators/ConfigNextVersionBaseVersionStrategy.cs b/GitVersionCore/VersionCalculation/BaseVersionCalculators/ConfigNextVersionBaseVersionStrategy.cs new file mode 100644 index 0000000000..cd1f907ec1 --- /dev/null +++ b/GitVersionCore/VersionCalculation/BaseVersionCalculators/ConfigNextVersionBaseVersionStrategy.cs @@ -0,0 +1,12 @@ +namespace GitVersion.VersionCalculation.BaseVersionCalculators +{ + public class ConfigNextVersionBaseVersionStrategy : BaseVersionStrategy + { + public override BaseVersion GetVersion(GitVersionContext context) + { + if (string.IsNullOrEmpty(context.Configuration.NextVersion)) + return null; + return new BaseVersion(false, SemanticVersion.Parse(context.Configuration.NextVersion, context.Configuration.GitTagPrefix), null); + } + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/BaseVersionCalculators/LastTagBaseVersionStrategy.cs b/GitVersionCore/VersionCalculation/BaseVersionCalculators/LastTagBaseVersionStrategy.cs new file mode 100644 index 0000000000..a3a6749196 --- /dev/null +++ b/GitVersionCore/VersionCalculation/BaseVersionCalculators/LastTagBaseVersionStrategy.cs @@ -0,0 +1,17 @@ +namespace GitVersion.VersionCalculation.BaseVersionCalculators +{ + public class LastTagBaseVersionStrategy : BaseVersionStrategy + { + public override BaseVersion GetVersion(GitVersionContext context) + { + VersionTaggedCommit version; + if (new LastTaggedReleaseFinder(context).GetVersion(out version)) + { + var shouldIncrement = version.Commit != context.CurrentCommit; + return new BaseVersion(shouldIncrement, version.SemVer, version.Commit); + } + + return null; + } + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageBaseVersionStrategy.cs b/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageBaseVersionStrategy.cs new file mode 100644 index 0000000000..44af2a1865 --- /dev/null +++ b/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageBaseVersionStrategy.cs @@ -0,0 +1,17 @@ +namespace GitVersion.VersionCalculation.BaseVersionCalculators +{ + public class MergeMessageBaseVersionStrategy : BaseVersionStrategy + { + public override BaseVersion GetVersion(GitVersionContext context) + { + foreach (var commit in context.CurrentBranch.CommitsPriorToThan(context.CurrentCommit.When())) + { + SemanticVersion semanticVersion; + // TODO when this approach works, inline the other class into here + if (MergeMessageParser.TryParse(context.CurrentCommit, context.Configuration, out semanticVersion)) + return new BaseVersion(true, semanticVersion, commit); + } + return null; + } + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchBaseVersionStrategy.cs b/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchBaseVersionStrategy.cs new file mode 100644 index 0000000000..1f384f7131 --- /dev/null +++ b/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchBaseVersionStrategy.cs @@ -0,0 +1,35 @@ +namespace GitVersion.VersionCalculation.BaseVersionCalculators +{ + using System; + using System.Linq; + + public class VersionInBranchBaseVersionStrategy : BaseVersionStrategy + { + public override BaseVersion GetVersion(GitVersionContext context) + { + var versionInBranch = GetVersionInBranch(context); + if (versionInBranch != null) + { + var firstCommitOfBranch = context.CurrentBranch.Commits.Last(); + return new BaseVersion(false, versionInBranch.Item2, firstCommitOfBranch); + } + + return null; + } + + Tuple GetVersionInBranch(GitVersionContext context) + { + var branchParts = context.CurrentBranch.Name.Split('/', '-'); + foreach (var part in branchParts) + { + SemanticVersion semanticVersion; + if (SemanticVersion.TryParse(part, context.Configuration.GitTagPrefix, out semanticVersion)) + { + return Tuple.Create(part, semanticVersion); + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/BaseVersionStrategy.cs b/GitVersionCore/VersionCalculation/BaseVersionStrategy.cs new file mode 100644 index 0000000000..855d4cbc3a --- /dev/null +++ b/GitVersionCore/VersionCalculation/BaseVersionStrategy.cs @@ -0,0 +1,9 @@ +namespace GitVersion.VersionCalculation +{ + using GitVersion.VersionCalculation.BaseVersionCalculators; + + public abstract class BaseVersionStrategy + { + public abstract BaseVersion GetVersion(GitVersionContext context); + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/IBaseVersionCalculator.cs b/GitVersionCore/VersionCalculation/IBaseVersionCalculator.cs new file mode 100644 index 0000000000..b6e8f3d67a --- /dev/null +++ b/GitVersionCore/VersionCalculation/IBaseVersionCalculator.cs @@ -0,0 +1,9 @@ +namespace GitVersion.VersionCalculation +{ + using GitVersion.VersionCalculation.BaseVersionCalculators; + + public interface IBaseVersionCalculator + { + BaseVersion GetBaseVersion(GitVersionContext context); + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/IMetaDataCalculator.cs b/GitVersionCore/VersionCalculation/IMetaDataCalculator.cs new file mode 100644 index 0000000000..967baf8528 --- /dev/null +++ b/GitVersionCore/VersionCalculation/IMetaDataCalculator.cs @@ -0,0 +1,9 @@ +namespace GitVersion.VersionCalculation +{ + using LibGit2Sharp; + + public interface IMetaDataCalculator + { + SemanticVersionBuildMetaData Create(Commit baseVersionSource, GitVersionContext context); + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/MetaDataCalculator.cs b/GitVersionCore/VersionCalculation/MetaDataCalculator.cs new file mode 100644 index 0000000000..ac0f76bb32 --- /dev/null +++ b/GitVersionCore/VersionCalculation/MetaDataCalculator.cs @@ -0,0 +1,26 @@ +namespace GitVersion.VersionCalculation +{ + using System.Linq; + using LibGit2Sharp; + + public class MetaDataCalculator : IMetaDataCalculator + { + public SemanticVersionBuildMetaData Create(Commit baseVersionSource, GitVersionContext context) + { + var qf = new CommitFilter + { + Since = baseVersionSource, + Until = context.CurrentCommit, + SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time + }; + + var commitsSinceTag = context.Repository.Commits.QueryBy(qf).Count(); + + return new SemanticVersionBuildMetaData( + commitsSinceTag, + context.CurrentBranch.Name, + context.CurrentCommit.Sha, + context.CurrentCommit.When()); + } + } +} \ No newline at end of file diff --git a/GitVersionCore/VersionCalculation/NewNextVersionCalculator.cs b/GitVersionCore/VersionCalculation/NewNextVersionCalculator.cs new file mode 100644 index 0000000000..d5f2ec7c37 --- /dev/null +++ b/GitVersionCore/VersionCalculation/NewNextVersionCalculator.cs @@ -0,0 +1,69 @@ +namespace GitVersion.VersionCalculation +{ + using System; + using BaseVersionCalculators; + + public class NewNextVersionCalculator + { + IBaseVersionCalculator baseVersionFinder; + IMetaDataCalculator metaDataCalculator; + + public NewNextVersionCalculator(IBaseVersionCalculator baseVersionCalculator = null, IMetaDataCalculator metaDataCalculator = null) + { + this.metaDataCalculator = metaDataCalculator ?? new MetaDataCalculator(); + baseVersionFinder = baseVersionCalculator ?? + new BaseVersionCalculator( + new ConfigNextVersionBaseVersionStrategy(), + new LastTagBaseVersionStrategy(), + new MergeMessageBaseVersionStrategy(), + new VersionInBranchBaseVersionStrategy()); + } + + public SemanticVersion FindVersion(GitVersionContext context) + { + var baseVersion = baseVersionFinder.GetBaseVersion(context); + + if (baseVersion.ShouldIncrement) IncrementVersion(context, baseVersion); + + if (!baseVersion.SemanticVersion.PreReleaseTag.HasTag() && !string.IsNullOrEmpty(context.Configuration.Tag)) + { + baseVersion.SemanticVersion.PreReleaseTag = new SemanticVersionPreReleaseTag(context.Configuration.Tag, 1); + } + + baseVersion.SemanticVersion.BuildMetaData = metaDataCalculator.Create(baseVersion.BaseVersionSource, context); + + return baseVersion.SemanticVersion; + } + + static void IncrementVersion(GitVersionContext context, BaseVersion baseVersion) + { + if (!baseVersion.SemanticVersion.PreReleaseTag.HasTag()) + { + switch (context.Configuration.Increment) + { + case IncrementStrategy.None: + break; + case IncrementStrategy.Major: + baseVersion.SemanticVersion.Major++; + break; + case IncrementStrategy.Minor: + baseVersion.SemanticVersion.Minor++; + break; + case IncrementStrategy.Patch: + baseVersion.SemanticVersion.Patch++; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + else + { + if (baseVersion.SemanticVersion.PreReleaseTag.Number != null) + { + baseVersion.SemanticVersion.PreReleaseTag.Number = baseVersion.SemanticVersion.PreReleaseTag.Number; + baseVersion.SemanticVersion.PreReleaseTag.Number++; + } + } + } + } +} \ No newline at end of file