diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 638f40e2f6..cb277452fd 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -1,6 +1,17 @@ ## Unreleased * When using a commit message that matches **both** `*-version-bump-message` and `no-bump-message`, there is no increment for that commit. In other words, `no-bump-message` now takes precedence over `*-version-bump-message`. +* The fallback version strategy now returns `0.0.0` and is flagged with `ShouldIncrement` equal to `true`. This yields the version `0.1.0` on the `develop` branch (`IncrementStrategy.Minor` by default) and `0.0.1` on the `main` branch (`IncremetnStrategy.Patch` by default). +* The current branch (child) inherits its configuration from the source (parent) branch if the `increment` strategy is set to `Inherit`. This makes branch configuration recursive, simpler, more intuitive, more flexible, and more robust. +* Instead of having a single effective configuration, we now have one effective configuration per branch where the increment strategy is not set to `inherit`. +* The new implementation of the branch configuration inheritance affects per default only the pull-requests, hotfix and feature branches. In this case the next version will be generated like the child branch is not existing and the commits have been made on the source branch. + * The following example illustrates this behavior. On the feature branch the semantic version `1.1.0-just-a-test.1+2` will now be generated instead of version `1.0.0-just-a-test.1+3` previously: + ``` + * 1f1cfb4 52 minutes ago (HEAD -> feature/just-a-test) + * 1f9654d 54 minutes ago (release/1.1.0) + * be72411 56 minutes ago (develop) + * 14800ff 58 minutes ago (tag: 1.0.0, main) + ``` ## v5.0.0 diff --git a/docs/input/docs/learn/how-it-works.md b/docs/input/docs/learn/how-it-works.md index 08224952d0..99cf2b75ed 100644 --- a/docs/input/docs/learn/how-it-works.md +++ b/docs/input/docs/learn/how-it-works.md @@ -47,7 +47,9 @@ Currently we have the following strategies: GitVersion.yaml file * `MergeMessageBaseVersionStrategy` - Finds version numbers from merge messages (e.g., `Merge 'release/3.0.0' into 'main'` will return `3.0.0`) -* `FallbackBaseVersionStrategy` - Always returns 0.1.0 for new repositories +* `FallbackBaseVersionStrategy` - Always returns 0.0.0 and will be used for + calculating the next version which is dependent on the increment strategy of + the effected branch (e.g. on main the next version is 0.0.1 or on develop it is 0.1.0) Each strategy needs to return an instance of `BaseVersion` which has the following properties: diff --git a/docs/input/docs/reference/configuration.md b/docs/input/docs/reference/configuration.md index 3de05cdbd2..f586f821a1 100644 --- a/docs/input/docs/reference/configuration.md +++ b/docs/input/docs/reference/configuration.md @@ -329,39 +329,24 @@ branches: feature: regex: ^features?[/-] mode: ContinuousDelivery - tag: useBranchName + tag: '{BranchName}' increment: Inherit - prevent-increment-of-merged-branch-version: false - track-merge-target: false source-branches: [ 'develop', 'main', 'release', 'feature', 'support', 'hotfix' ] - tracks-release-branches: false - is-release-branch: false - is-mainline: false - pre-release-weight: 30000 + pre-release-weight: 30000 pull-request: regex: ^(pull|pull\-requests|pr)[/-] mode: ContinuousDelivery tag: PullRequest increment: Inherit - prevent-increment-of-merged-branch-version: false tag-number-pattern: '[/-](?\d+)[-/]' - track-merge-target: false source-branches: [ 'develop', 'main', 'release', 'feature', 'support', 'hotfix' ] - tracks-release-branches: false - is-release-branch: false - is-mainline: false pre-release-weight: 30000 hotfix: regex: ^hotfix(es)?[/-] mode: ContinuousDelivery tag: beta - increment: Patch - prevent-increment-of-merged-branch-version: false - track-merge-target: false - source-branches: [ 'develop', 'main', 'support' ] - tracks-release-branches: false - is-release-branch: false - is-mainline: false + increment: Inherit + source-branches: [ 'release', 'main', 'support', 'hotfix' ] pre-release-weight: 30000 support: regex: ^support[/-] diff --git a/src/GitVersion.App.Tests/JsonOutputOnBuildServerTest.cs b/src/GitVersion.App.Tests/JsonOutputOnBuildServerTest.cs index 6a41333cd7..475878ad1f 100644 --- a/src/GitVersion.App.Tests/JsonOutputOnBuildServerTest.cs +++ b/src/GitVersion.App.Tests/JsonOutputOnBuildServerTest.cs @@ -37,10 +37,10 @@ public void BeingOnBuildServerWithOutputJsonDoesNotFail() var result = GitVersionHelper.ExecuteIn(fixture.LocalRepositoryFixture.RepositoryPath, arguments: " /output json /output buildserver", environments: env); result.ExitCode.ShouldBe(0); - const string version = "0.1.0+4"; - result.Output.ShouldContain($"##teamcity[buildNumber '{version}']"); + const string expectedVersion = "0.0.1+5"; + result.Output.ShouldContain($"##teamcity[buildNumber '{expectedVersion}']"); result.OutputVariables.ShouldNotBeNull(); - result.OutputVariables.FullSemVer.ShouldBeEquivalentTo(version); + result.OutputVariables.FullSemVer.ShouldBeEquivalentTo(expectedVersion); } [TestCase("", "GitVersion.json")] @@ -56,16 +56,16 @@ public void BeingOnBuildServerWithOutputJsonAndOutputFileDoesNotFail(string outp var result = GitVersionHelper.ExecuteIn(fixture.LocalRepositoryFixture.RepositoryPath, arguments: $" /output json /output buildserver /output file /outputfile {outputFile}", environments: env); result.ExitCode.ShouldBe(0); - const string version = "0.1.0+4"; - result.Output.ShouldContain($"##teamcity[buildNumber '{version}']"); + const string expectedVersion = "0.0.1+5"; + result.Output.ShouldContain($"##teamcity[buildNumber '{expectedVersion}']"); result.OutputVariables.ShouldNotBeNull(); - result.OutputVariables.FullSemVer.ShouldBeEquivalentTo(version); + result.OutputVariables.FullSemVer.ShouldBeEquivalentTo(expectedVersion); var filePath = PathHelper.Combine(fixture.LocalRepositoryFixture.RepositoryPath, fileName); var json = File.ReadAllText(filePath); var outputVariables = VersionVariables.FromJson(json); outputVariables.ShouldNotBeNull(); - outputVariables.FullSemVer.ShouldBeEquivalentTo(version); + outputVariables.FullSemVer.ShouldBeEquivalentTo(expectedVersion); } } diff --git a/src/GitVersion.Core.Tests/Configuration/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt b/src/GitVersion.Core.Tests/Configuration/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt index 173aca96de..f141ccc585 100644 --- a/src/GitVersion.Core.Tests/Configuration/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt +++ b/src/GitVersion.Core.Tests/Configuration/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt @@ -82,18 +82,13 @@ branches: hotfix: mode: ContinuousDelivery tag: beta - increment: Patch - prevent-increment-of-merged-branch-version: false - track-merge-target: false + increment: Inherit regex: ^hotfix(es)?[/-] source-branches: - release - main - support - hotfix - tracks-release-branches: false - is-release-branch: false - is-mainline: false pre-release-weight: 30000 support: mode: ContinuousDelivery diff --git a/src/GitVersion.Core.Tests/Core/GitVersionExecutorTests.cs b/src/GitVersion.Core.Tests/Core/GitVersionExecutorTests.cs index ca669db1d2..7498274ec8 100644 --- a/src/GitVersion.Core.Tests/Core/GitVersionExecutorTests.cs +++ b/src/GitVersion.Core.Tests/Core/GitVersionExecutorTests.cs @@ -150,7 +150,7 @@ public void CacheFileExistsOnDisk() var gitVersionCalculator = GetGitVersionCalculator(gitVersionOptions, this.log); var versionVariables = gitVersionCalculator.CalculateVersionVariables(); - versionVariables.AssemblySemVer.ShouldBe("0.1.0.0"); + versionVariables.AssemblySemVer.ShouldBe("0.0.1.0"); this.fileSystem.WriteAllText(versionVariables.FileName, versionCacheFileContent); versionVariables = gitVersionCalculator.CalculateVersionVariables(); @@ -197,7 +197,7 @@ public void CacheFileExistsOnDiskWhenOverrideConfigIsSpecifiedVersionShouldBeDyn var gitVersionCalculator = GetGitVersionCalculator(gitVersionOptions, this.log); var versionVariables = gitVersionCalculator.CalculateVersionVariables(); - versionVariables.AssemblySemVer.ShouldBe("0.1.0.0"); + versionVariables.AssemblySemVer.ShouldBe("0.0.1.0"); this.fileSystem.WriteAllText(versionVariables.FileName, versionCacheFileContent); @@ -211,7 +211,7 @@ public void CacheFileExistsOnDiskWhenOverrideConfigIsSpecifiedVersionShouldBeDyn gitVersionCalculator = GetGitVersionCalculator(gitVersionOptions); versionVariables = gitVersionCalculator.CalculateVersionVariables(); - versionVariables.AssemblySemVer.ShouldBe("0.1.0.0"); + versionVariables.AssemblySemVer.ShouldBe("0.0.1.0"); var cachedDirectoryTimestampAfter = this.fileSystem.GetLastDirectoryWrite(cacheDirectory); cachedDirectoryTimestampAfter.ShouldBe(cacheDirectoryTimestamp, "Cache was updated when override config was set"); @@ -279,7 +279,7 @@ public void ConfigChangeInvalidatesCache() var gitVersionCalculator = GetGitVersionCalculator(gitVersionOptions); var versionVariables = gitVersionCalculator.CalculateVersionVariables(); - versionVariables.AssemblySemVer.ShouldBe("0.1.0.0"); + versionVariables.AssemblySemVer.ShouldBe("0.0.1.0"); versionVariables.FileName.ShouldNotBeNullOrEmpty(); this.fileSystem.WriteAllText(versionVariables.FileName, versionCacheFileContent); @@ -336,7 +336,7 @@ public void NoCacheBypassesCache() var versionVariables = gitVersionCalculator.CalculateVersionVariables(); - versionVariables.AssemblySemVer.ShouldBe("0.1.0.0"); + versionVariables.AssemblySemVer.ShouldBe("0.0.1.0"); versionVariables.FileName.ShouldNotBeNullOrEmpty(); this.fileSystem.WriteAllText(versionVariables.FileName, versionCacheFileContent); @@ -345,7 +345,7 @@ public void NoCacheBypassesCache() gitVersionOptions.Settings.NoCache = true; versionVariables = gitVersionCalculator.CalculateVersionVariables(); - versionVariables.AssemblySemVer.ShouldBe("0.1.0.0"); + versionVariables.AssemblySemVer.ShouldBe("0.0.1.0"); } [Test] diff --git a/src/GitVersion.Core.Tests/Core/RepositoryStoreTests.cs b/src/GitVersion.Core.Tests/Core/RepositoryStoreTests.cs index bd60860d71..8aca3eeeb1 100644 --- a/src/GitVersion.Core.Tests/Core/RepositoryStoreTests.cs +++ b/src/GitVersion.Core.Tests/Core/RepositoryStoreTests.cs @@ -68,7 +68,7 @@ public void FindsCorrectMergeBaseForForwardMerge() var developMergeBase = gitRepoMetadataProvider.FindMergeBase(develop, release); - fixtureRepository.DumpGraph(Console.WriteLine); + fixtureRepository.DumpGraph(); releaseBranchMergeBase.ShouldBe(expectedReleaseMergeBase); developMergeBase.ShouldBe(expectedDevelopMergeBase); @@ -124,7 +124,7 @@ public void FindsCorrectMergeBaseForForwardMergeMovesOn() var developMergeBase = gitRepoMetadataProvider.FindMergeBase(develop, release); - fixtureRepository.DumpGraph(Console.WriteLine); + fixtureRepository.DumpGraph(); releaseBranchMergeBase.ShouldBe(expectedReleaseMergeBase); developMergeBase.ShouldBe(expectedDevelopMergeBase); @@ -199,7 +199,7 @@ public void FindsCorrectMergeBaseForMultipleForwardMerges() var developMergeBase = gitRepoMetadataProvider.FindMergeBase(develop, release); - fixtureRepository.DumpGraph(Console.WriteLine); + fixtureRepository.DumpGraph(); releaseBranchMergeBase.ShouldBe(expectedReleaseMergeBase); developMergeBase.ShouldBe(expectedDevelopMergeBase); diff --git a/src/GitVersion.Core.Tests/Extensions/GitToolsTestingExtensions.cs b/src/GitVersion.Core.Tests/Extensions/GitToolsTestingExtensions.cs index cca12fe9fd..f50d11f9f3 100644 --- a/src/GitVersion.Core.Tests/Extensions/GitToolsTestingExtensions.cs +++ b/src/GitVersion.Core.Tests/Extensions/GitToolsTestingExtensions.cs @@ -106,8 +106,6 @@ public static void WriteVersionVariables(this RepositoryFixtureBase fixture, str public static void AssertFullSemver(this RepositoryFixtureBase fixture, string fullSemver, Config? configuration = null, IRepository? repository = null, string? commitId = null, bool onlyTrackedBranches = true, string? targetBranch = null) { - configuration ??= new Config(); - configuration = new ConfigurationBuilder().Add(configuration).Build(); Console.WriteLine("---------"); try diff --git a/src/GitVersion.Core.Tests/Helpers/TestConfigurationBuilder.cs b/src/GitVersion.Core.Tests/Helpers/TestConfigurationBuilder.cs index 150d96a939..2dcbf077c8 100644 --- a/src/GitVersion.Core.Tests/Helpers/TestConfigurationBuilder.cs +++ b/src/GitVersion.Core.Tests/Helpers/TestConfigurationBuilder.cs @@ -8,7 +8,7 @@ public sealed class TestConfigurationBuilder { public static TestConfigurationBuilder New => new(); - private string? nextVerson; + private string? nextVersion; private VersioningMode? versioningMode; private readonly Dictionary versioningModeDictionary = new(); private bool withoutAnyTrackMergeTargets; @@ -28,7 +28,7 @@ private TestConfigurationBuilder() public TestConfigurationBuilder WithNextVersion(string? value) { - nextVerson = value; + nextVersion = value; return this; } @@ -109,7 +109,7 @@ public Config Build() { Config configuration = new() { - NextVersion = nextVerson, + NextVersion = nextVersion, VersioningMode = versioningMode }; diff --git a/src/GitVersion.Core.Tests/IntegrationTests/ContinuousDeploymentTestScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/ContinuousDeploymentTestScenarios.cs new file mode 100644 index 0000000000..85a7d72e15 --- /dev/null +++ b/src/GitVersion.Core.Tests/IntegrationTests/ContinuousDeploymentTestScenarios.cs @@ -0,0 +1,405 @@ +using GitTools.Testing; +using GitVersion.Core.Tests.Helpers; +using GitVersion.VersionCalculation; +using NUnit.Framework; + +namespace GitVersion.Core.Tests.IntegrationTests; + +[TestFixture] +public class ContinuousDeploymentTestScenarios +{ + [Test] + public void ShouldUseTheFallbackVersionOnMainWhenNoVersionsAreAvailable() + { + // * 2373a87 58 minutes ago (HEAD -> main) + + var configuration = TestConfigurationBuilder.New.WithVersioningMode(VersioningMode.ContinuousDeployment).Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-ci.1", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldUseTheFallbackVersionOnDevelopWhenNoVersionsAreAvailable() + { + // * a831d61 58 minutes ago (HEAD -> develop) + + var configuration = TestConfigurationBuilder.New.WithVersioningMode(VersioningMode.ContinuousDeployment).Build(); + + using var fixture = new EmptyRepositoryFixture("develop"); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldUseConfiguredNextVersionOnMainWhenNoHigherVersionsAvailable() + { + // * 8c64db3 58 minutes ago (HEAD -> main) + + var configuration = TestConfigurationBuilder.New + .WithVersioningMode(VersioningMode.ContinuousDeployment) + .WithNextVersion("1.0.0").Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-ci.1", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldNotMatterWhenConfiguredNextVersionIsEqualsToTheTaggeVersion() + { + // * 858f71b 58 minutes ago (HEAD -> main, tag: 1.0.0) + + var configuration = TestConfigurationBuilder.New + .WithVersioningMode(VersioningMode.ContinuousDeployment) + .WithNextVersion("1.0.0").Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeATaggedCommit("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldUseTaggedVersionWhenGreaterThanConfiguredNextVersion() + { + // * ba74727 58 minutes ago (HEAD -> main, tag: 1.1.0) + + var configuration = TestConfigurationBuilder.New + .WithVersioningMode(VersioningMode.ContinuousDeployment) + .WithNextVersion("1.0.0").Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeATaggedCommit("1.1.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldCalculateTheCorrectVersionWhenMergingFromMainToFeatureBranch() + { + // *94f03f8 55 minutes ago(HEAD -> main) + // |\ + // | *b1f41a4 56 minutes ago + // |/ + // *ec77f9c 58 minutes ago + + var configuration = TestConfigurationBuilder.New.WithVersioningMode(VersioningMode.ContinuousDeployment).Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-ci.1", configuration); + + fixture.BranchTo("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-just-a-test.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-just-a-test.2", configuration); + + fixture.Checkout("main"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-ci.1", configuration); + + fixture.MergeNoFF("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-ci.3", configuration); + + fixture.Repository.Branches.Remove("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-ci.3", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldCalculateTheCorrectVersionWhenMergingFromDevelopToFeatureBranch() + { + // *2c475bf 55 minutes ago(HEAD -> develop) + // |\ + // | *e05365d 56 minutes ago + // |/ + // *67acc03 58 minutes ago(main) + + var configuration = TestConfigurationBuilder.New.WithVersioningMode(VersioningMode.ContinuousDeployment).Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + fixture.BranchTo("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.BranchTo("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-just-a-test.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-just-a-test.2", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.MergeNoFF("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.3", configuration); + + fixture.Repository.Branches.Remove("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.3", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldCalculateTheCorrectVersionWhenMergingFromReleaseToFeatureBranch() + { + // *b1e5593 53 minutes ago(HEAD -> release/ 1.0.0) + // *8752695 55 minutes ago + // |\ + // | *0965b88 56 minutes ago + // |/ + // *f63a536 58 minutes ago(main) + + var configuration = TestConfigurationBuilder.New.WithVersioningMode(VersioningMode.ContinuousDeployment).Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.0", configuration); + + fixture.BranchTo("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1", configuration); + + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.0", configuration); + + fixture.MergeNoFF("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.2", configuration); + + fixture.Repository.Branches.Remove("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.2", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldFallbackToTheVersionOnDevelopLikeTheReleaseWasNeverCreatedWhenReleaseHasBeenCanceled() + { + // *8f062c7 49 minutes ago(HEAD -> develop) + // |\ + // | *bda6ba8 52 minutes ago + // | *6f5cf19 54 minutes ago + // * | 3b20f15 50 minutes ago + // |/ + // *f5640b3 56 minutes ago + // *2099a07 58 minutes ago(main) + + var configuration = TestConfigurationBuilder.New + .WithVersioningMode(VersioningMode.ContinuousDeployment) + .WithoutAnyTrackMergeTargets().Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-ci.1", configuration); + + fixture.BranchTo("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.2", configuration); + + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.2", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.MergeNoFF("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.4", configuration); + + // cancel the release 1.0.0 + fixture.Repository.Branches.Remove("release/1.0.0"); + + // ❔ expected: "0.1.0-alpha.6" + fixture.AssertFullSemver("1.1.0-alpha.4", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldConsiderTheMergeCommitFromMainToDevelopWhenReleaseHasBeenMergedAndTaggedOnMain() + { + // * 5d13120 48 minutes ago (HEAD -> develop) + // |\ + // | * 8ddd9b0 49 minutes ago (tag: 1.0.0, main) + // | |\ + // | | * 4b826b8 52 minutes ago + // | | * d4b0047 54 minutes ago + // * | | 0457671 50 minutes ago + // | |/ + // |/| + // * | 5f31f30 56 minutes ago + // |/ + // * 252971e 58 minutes ago + + var configuration = TestConfigurationBuilder.New + .WithVersioningMode(VersioningMode.ContinuousDeployment) + .WithoutAnyTrackMergeTargets().Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1-ci.1", configuration); + + fixture.BranchTo("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.2", configuration); + + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.2", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.Checkout("main"); + fixture.MergeNoFF("release/1.0.0"); + + // ❔ expected: "0.0.1-ci.5" + fixture.AssertFullSemver("1.0.0-ci.0", configuration); + + fixture.ApplyTag("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.MergeNoFF("main"); + + // ❔ expected: "1.1.0-alpha.2" + fixture.AssertFullSemver("1.1.0-alpha.6", configuration); + + fixture.Repository.Branches.Remove("release/1.0.0"); + + // ❔ expected: "1.1.0-alpha.2 + fixture.AssertFullSemver("1.1.0-alpha.6", configuration); + + fixture.Repository.DumpGraph(); + } +} diff --git a/src/GitVersion.Core.Tests/IntegrationTests/CreatingAFeatureBranchFromAReleaseBranchScenario.cs b/src/GitVersion.Core.Tests/IntegrationTests/CreatingAFeatureBranchFromAReleaseBranchScenario.cs new file mode 100644 index 0000000000..bdc6e9d8d6 --- /dev/null +++ b/src/GitVersion.Core.Tests/IntegrationTests/CreatingAFeatureBranchFromAReleaseBranchScenario.cs @@ -0,0 +1,701 @@ +using GitTools.Testing; +using GitVersion.Core.Tests.Helpers; +using NUnit.Framework; + +namespace GitVersion.Core.Tests.IntegrationTests; + +/// +/// Version not generated correct when creating a feature branch from a release branch #3101 +/// +[TestFixture] +public class CreatingAFeatureBranchFromAReleaseBranchScenario +{ + [Test] + public void ShouldTreatTheFeatureBranchLikeTheFirstReleaseBranchWhenItHasBeenBranchedFromMainAndFirstReleaseBranchButNotFromTheSecondReleaseBranch() + { + // *f59b84f in the future(HEAD -> release/ 1.0.0) + // *d0f4669 in the future + // |\ + // | *471acec in the future + // |/ + // | *266fa68 in the future(release/ 1.1.0, main) + // |/ + // *e0b5034 6 seconds ago + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture("main"); + + fixture.Repository.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1+1", configuration); + + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.BranchTo("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+0", configuration); + + fixture.Checkout("main"); + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1+2", configuration); + + fixture.BranchTo("release/1.1.0"); + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.Checkout("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+1", configuration); + + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.MergeNoFF("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.Repository.Branches.Remove("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldTreatTheFeatureBranchNotLikeTheReleaseBranchWhenItHasBeenBranchedFromDevelopAndFirstReleaseBranchButNotFromTheSecondReleaseBranch() + { + // *19ed1e8 in the future(HEAD -> release/ 1.0.0) + // *1684169 in the future + // |\ + // | *07bd75c in the future + // |/ + // | *ff34213 in the future(release/ 1.1.0, develop) + // |/ + // *d5ac9aa in the future + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture("develop"); + + fixture.Repository.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + // 1.1.0 is correct because the base branch points to develop and release + // maybe we can fix it somehow using the configuration with PreReleaseWeight? + fixture.BranchTo("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-just-a-test.1+0", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.BranchTo("release/1.1.0"); + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.Checkout("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+1", configuration); + + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.MergeNoFF("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.Repository.Branches.Remove("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.Repository.DumpGraph(); + } + + [TestCase("main")] + [TestCase("develop")] + public void ShouldTreatTheHotfixBranchLikeTheFirstReleaseBranchWhenItHasBeenBranchedFromMainOrDevelopAndFirstReleaseBranchButNotFromTheSecondReleaseBranch( + string branchName) + { + // *2b9c8bf 42 minutes ago(HEAD -> release/ 1.0.0) + // *66cfc66 44 minutes ago + // |\ + // | *e9978b9 45 minutes ago + // |/ + // | *c2b96e5 47 minutes ago(release/ 1.1.0, main|develop) + // |/ + // *e00f53d 49 minutes ago + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture(branchName); + + fixture.Repository.MakeACommit(); + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.BranchTo("hotfix/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.Checkout(branchName); + fixture.MakeACommit(); + fixture.BranchTo("release/1.1.0"); + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.Checkout("hotfix/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.MergeNoFF("hotfix/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.Repository.Branches.Remove("hotfix/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.Repository.DumpGraph(); + } + + [TestCase("main")] + [TestCase("develop")] + public void ShouldTreatTheFeatureBranchLikeTheFirstReleaseBranchWhenItHasBeenBranchedFromFirstButNotFromTheSecondReleaseBranch( + string branchName) + { + // *1525ad0 38 minutes ago(HEAD -> release/ 1.0.0) + // *476fc51 40 minutes ago + // |\ + // | *c8c5030 41 minutes ago + // |/ + // *d91061d 45 minutes ago + // | *1ac98f5 43 minutes ago(release/ 1.1.0, develop) + // |/ + // *22596b8 47 minutes ago + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture(branchName); + fixture.Repository.MakeACommit(); + + fixture.BranchTo("release/1.0.0"); + fixture.Repository.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.BranchTo("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+1", configuration); + + fixture.Checkout(branchName); + fixture.MakeACommit(); + fixture.BranchTo("release/1.1.0"); + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.Checkout("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+2", configuration); + + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.MergeNoFF("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.Repository.Branches.Remove("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+4", configuration); + + fixture.Repository.DumpGraph(); + } + + [TestCase("main")] + [TestCase("develop")] + public void ShouldTreatTheHotfixBranchLikeTheFirstReleaseBranchWhenItHasBeenBranchedFromFirstButNotFromTheSecondReleaseBranch( + string branchName) + { + // *1525ad0 38 minutes ago(HEAD -> release/ 1.0.0) + // *476fc51 40 minutes ago + // |\ + // | *c8c5030 41 minutes ago + // |/ + // *d91061d 45 minutes ago + // | *1ac98f5 43 minutes ago(release/ 1.1.0, develop) + // |/ + // *22596b8 47 minutes ago + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture(branchName); + fixture.Repository.MakeACommit(); + + fixture.BranchTo("release/1.0.0"); + fixture.Repository.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.BranchTo("hotfix/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.Checkout(branchName); + fixture.MakeACommit(); + fixture.BranchTo("release/1.1.0"); + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.Checkout("hotfix/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.MergeNoFF("hotfix/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.Repository.Branches.Remove("hotfix/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+4", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldTreatTheFeatureBranchLikeTheReleaseBranchWhenItHasBeenBranchedFromRelease() + { + // *588f0de in the future(HEAD -> release/ 1.0.0) + // *56f660c in the future + // |\ + // | *9450fb0 in the future + // |/ + // *9e557cd in the future + // *2e022d7 in the future(main) + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.Repository.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1+1", configuration); + + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.BranchTo("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-just-a-test.1+2", configuration); + + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.MergeNoFF("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.Repository.Branches.Remove("feature/just-a-test"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+3", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+4", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldTreatTheMergeFromReleaseToDevelopLikeTheReleaseBranchHasNeverBeenExistingWhenReleaseHasBeenCanceled() + { + // *809eaa7 in the future(HEAD -> develop) + // *46e2cb8 in the future + // |\ + // | *08bd8ff in the future + // | *9b741de in the future + // * | 13206fd in the future + // |/ + // *9dc9b22 in the future + // *f708abd in the future(main) + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1+1", configuration); + + fixture.BranchTo("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.2", configuration); + + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.MergeNoFF("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.4", configuration); + + fixture.Repository.Branches.Remove("release/1.0.0"); + + // ❔ expected: "0.1.0+6" because the release has been canceled and should be treated like it was never existing + fixture.AssertFullSemver("1.1.0-alpha.4", configuration); + + fixture.MakeACommit(); + + // ❔ expected: "0.1.0+7" because the release has been canceled and should be treated like it was never existing + fixture.AssertFullSemver("1.1.0-alpha.5", configuration); + + fixture.Repository.DumpGraph(); + } + + + [Test] + public void ShouldOnlyTrackTheCommitsOnDevelopBranchForNextReleaseWhenReleaseHasBeenShippedToProduction() + { + // *9afb0ca in the future(HEAD -> develop) + // |\ + // | *90c96f2 in the future(tag: 1.0.0, main) + // | |\ + // | | *7de3d63 in the future + // | | *2ccf33b in the future + // * | | e050757 in the future + // | |/ + // |/| + // * | cf1ff87 in the future + // |/ + // *838a95b in the future + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1+1", configuration); + + fixture.BranchTo("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.2", configuration); + + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.Checkout("main"); + fixture.MergeNoFF("release/1.0.0"); + + // ❔ expected: "0.0.1+4" because until the commit is not tagged it's a hotfix + fixture.AssertFullSemver("1.0.0+0", configuration); + + fixture.ApplyTag("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.MergeNoFF("main"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.6", configuration); + + fixture.Repository.Branches.Remove("release/1.0.0"); + + // ❔ expected: "1.1.0-alpha.2" because only one commit and one merge has been pushed + fixture.AssertFullSemver("1.1.0-alpha.6", configuration); + + fixture.Repository.DumpGraph(); + } + + [Test] + public void ShouldNotConsiderTheMergeCommitFromReleaseToMainWhenCommitHasNotBeenTagged() + { + // *457e0cd in the future(HEAD -> develop) + // |\ + // | *d9da657 in the future(tag: 1.0.0, main) + // | |\ + // | | *026a6cd in the future + // | | *7f5de6e in the future + // * | | 3db6e6f in the future + // | |/ + // |/| + // * | 845926e in the future + // |/ + // *42db9ba in the future + + var configuration = TestConfigurationBuilder.New.Build(); + + using var fixture = new EmptyRepositoryFixture(); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1+1", configuration); + + fixture.BranchTo("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.2", configuration); + + fixture.BranchTo("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+2", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configuration); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.Checkout("main"); + fixture.MergeNoFF("release/1.0.0"); + + // ❔ expected: "0.0.1+4" because until the commit is not tagged it's a hotfix + fixture.AssertFullSemver("1.0.0+0", configuration); + + fixture.Repository.Branches.Remove("release/1.0.0"); + + // ❔ expected: "0.0.1+4" because until the commit is not tagged it's a hotfix + fixture.AssertFullSemver("1.0.0+0", configuration); + + fixture.ApplyTag("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0", configuration); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + + fixture.MergeNoFF("main"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.6", configuration); + + fixture.Repository.DumpGraph(); + } +} diff --git a/src/GitVersion.Core.Tests/IntegrationTests/DevelopScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/DevelopScenarios.cs index bd9f36b2a4..666393118d 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/DevelopScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/DevelopScenarios.cs @@ -140,7 +140,7 @@ public void WhenDevelopBranchedFromMainDetachedHeadMinorIsIncreased() } [Test] - public void InheritVersionFromReleaseBranch() + public void InheritVersionFromParentReleaseBranch() { using var fixture = new EmptyRepositoryFixture(); fixture.MakeATaggedCommit("1.0.0"); @@ -150,7 +150,7 @@ public void InheritVersionFromReleaseBranch() fixture.MakeACommit(); fixture.MakeACommit(); fixture.Checkout("develop"); - fixture.AssertFullSemver("1.1.0-alpha.1"); + fixture.AssertFullSemver("2.1.0-alpha.0"); fixture.MakeACommit(); fixture.AssertFullSemver("2.1.0-alpha.1"); fixture.MergeNoFF("release/2.0.0"); @@ -160,6 +160,30 @@ public void InheritVersionFromReleaseBranch() fixture.AssertFullSemver("2.1.0-MyFeature.1+5"); } + [Test] + public void InheritVersionFromParentReleaseBranchWithVersion2InsteadOfVersion3() + { + using var fixture = new EmptyRepositoryFixture(); + fixture.MakeATaggedCommit("1.0.0"); + fixture.BranchTo("develop"); + fixture.Repository.CreateBranch("release/3.0.0"); + fixture.MakeACommit(); + fixture.BranchTo("release/2.0.0"); + fixture.MakeACommit(); + fixture.MakeACommit(); + fixture.AssertFullSemver("2.0.0-beta.1+2"); + fixture.Checkout("develop"); + fixture.AssertFullSemver("3.1.0-alpha.1"); + fixture.MakeACommit(); + fixture.AssertFullSemver("3.1.0-alpha.2"); + fixture.MergeNoFF("release/2.0.0"); + fixture.AssertFullSemver("3.1.0-alpha.5"); + fixture.Checkout("release/2.0.0"); + fixture.BranchTo("feature/MyFeature"); + fixture.MakeACommit(); + fixture.AssertFullSemver("2.0.0-MyFeature.1+3"); + } + [Test] public void WhenMultipleDevelopBranchesExistAndCurrentBranchHasIncrementInheritPolicyAndCurrentCommitIsAMerge() { @@ -327,12 +351,47 @@ public void WhenPreventIncrementOfMergedBranchVersionIsSetToFalseForDevelopCommi Assert.AreEqual(versionSourceBeforeReleaseBranchIsRemoved, versionSourceAfterReleaseBranchIsRemoved); fixture.AssertFullSemver("1.2.0-alpha.6"); fixture.AssertFullSemver("1.2.0-alpha.6", config); + } - config.Branches = new Dictionary - { - { "develop", new BranchConfig { PreventIncrementOfMergedBranchVersion = true } } - }; - fixture.AssertFullSemver("1.2.0-alpha.3", config); + [Test] + public void WhenPreventIncrementOfMergedBranchVersionIsSetToTrueForDevelopCommitsSinceVersionSourceShouldNotGoDownWhenMergingReleaseToDevelop() + { + var configuration = TestConfigurationBuilder.New + .WithVersioningMode(VersioningMode.ContinuousDeployment) + .WithPreventIncrementOfMergedBranchVersion("develop", true) + .Build(); + + using var fixture = new EmptyRepositoryFixture(); + const string ReleaseBranch = "release/1.1.0"; + fixture.MakeACommit(); + fixture.BranchTo("develop"); + fixture.MakeATaggedCommit("1.0.0"); + fixture.Repository.MakeCommits(1); + + // Create a release branch and make some commits + fixture.BranchTo(ReleaseBranch); + fixture.Repository.MakeCommits(3); + + // Simulate a GitFlow release finish. + fixture.Checkout(MainBranch); + fixture.MergeNoFF(ReleaseBranch); + fixture.ApplyTag("v1.1.0"); + fixture.Checkout("develop"); + // Simulate some work done on develop while the release branch was open. + fixture.Repository.MakeCommits(2); + fixture.MergeNoFF(ReleaseBranch); + + // Version numbers will still be correct when the release branch is around. + fixture.AssertFullSemver("1.2.0-alpha.6"); + fixture.AssertFullSemver("1.2.0-alpha.6", configuration); + + var versionSourceBeforeReleaseBranchIsRemoved = fixture.GetVersion(configuration).Sha; + + fixture.Repository.Branches.Remove(ReleaseBranch); + var versionSourceAfterReleaseBranchIsRemoved = fixture.GetVersion(configuration).Sha; + Assert.AreEqual(versionSourceBeforeReleaseBranchIsRemoved, versionSourceAfterReleaseBranchIsRemoved); + fixture.AssertFullSemver("1.2.0-alpha.6"); + fixture.AssertFullSemver("1.2.0-alpha.3", configuration); } [Test] @@ -398,4 +457,111 @@ public void WhenPreventIncrementOfMergedBranchVersionIsSetToFalseForDevelopCommi fixture.Repository.Branches.Remove(HotfixBranch); fixture.AssertFullSemver("1.2.0-alpha.19", config); } + + [Test] + public void NextVersionShouldBeConsideredOnTheMainBranch() + { + using EmptyRepositoryFixture fixture = new("main"); + + var configurationBuilder = TestConfigurationBuilder.New; + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1+1", configurationBuilder.Build()); + + configurationBuilder.WithNextVersion("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0+1", configurationBuilder.Build()); + + fixture.MakeACommit(); + configurationBuilder.WithNextVersion(null); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.0.1+2", configurationBuilder.Build()); + + configurationBuilder.WithNextVersion("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0+2", configurationBuilder.Build()); + } + + /// + /// Prevent decrementation of versions on the develop branch #3177 + /// (see https://github.com/GitTools/GitVersion/discussions/3177) + /// + [Test] + public void PreventDecrementationOfVersionsOnTheMainBranch() + { + using EmptyRepositoryFixture fixture = new("develop"); + + var configurationBuilder = TestConfigurationBuilder.New; + + fixture.MakeACommit(); + configurationBuilder.WithNextVersion("1.0.0"); + + // now we are ready to start with the preparation of the 1.0.0 release + fixture.BranchTo("release/1.0.0"); + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configurationBuilder.Build()); + + // now we makes changes on develop that may or may not end up in the 1.0.0 release + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configurationBuilder.Build()); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configurationBuilder.Build()); + + // now we do the actual release of beta 1 + fixture.Checkout("release/1.0.0"); + fixture.ApplyTag("1.0.0-beta.1"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1", configurationBuilder.Build()); + + // continue with more work on develop that may or may not end up in the 1.0.0 release + fixture.Checkout("develop"); + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.2", configurationBuilder.Build()); + + fixture.MergeNoFF("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.4", configurationBuilder.Build()); + + fixture.Repository.Branches.Remove("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.4", configurationBuilder.Build()); + + fixture.Repository.Tags.Remove("1.0.0-beta.1"); + + // Merge from develop to main + fixture.BranchTo("main"); + + // ❔ expected: "0.0.1+4" + // This behavior needs to be changed for the git flow workflow using the track-merge-message or track-merge-target options. + // [Bug] track-merge-changes produces unexpected result when combining hotfix and support branches #3052 + fixture.AssertFullSemver("1.0.0+0", configurationBuilder.Build()); + + configurationBuilder.WithNextVersion("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0+0", configurationBuilder.Build()); + + // Mark this version as RTM + fixture.ApplyTag("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0", configurationBuilder.Build()); + } } diff --git a/src/GitVersion.Core.Tests/IntegrationTests/FeatureBranchScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/FeatureBranchScenarios.cs index 12f97532c7..2dfff4b0ed 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/FeatureBranchScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/FeatureBranchScenarios.cs @@ -141,7 +141,7 @@ public void WhenTwoFeatureBranchPointToTheSameCommit() fixture.Repository.CreateBranch("feature/feature2"); Commands.Checkout(fixture.Repository, "feature/feature2"); - fixture.AssertFullSemver("0.1.0-feature2.1+1"); + fixture.AssertFullSemver("0.1.0-feature2.1+2"); } [Test] @@ -192,7 +192,7 @@ public void CanUseBranchNameOffAReleaseBranch() fixture.BranchTo("feature/PROJ-1"); fixture.MakeACommit(); - fixture.AssertFullSemver("0.3.0-PROJ-1.1+2", config); + fixture.AssertFullSemver("0.3.0-PROJ-1.1+3", config); } [TestCase("alpha", "JIRA-123", "alpha")] diff --git a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs index 630400c766..54845f7a8a 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs @@ -70,7 +70,7 @@ public void GitflowComplexExample() fixture.Checkout(MainBranch); fixture.BranchTo(hotfixBranch); fixture.MakeACommit("added hotfix"); - fixture.AssertFullSemver("1.2.1-beta.1+7"); + fixture.AssertFullSemver("1.2.1-beta.1+1"); fixture.Checkout(MainBranch); fixture.MergeNoFF(hotfixBranch); fixture.AssertFullSemver("1.2.1+2"); diff --git a/src/GitVersion.Core.Tests/IntegrationTests/IgnoreBeforeScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/IgnoreBeforeScenarios.cs index 78ba4c9c25..4116e01855 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/IgnoreBeforeScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/IgnoreBeforeScenarios.cs @@ -1,8 +1,5 @@ using GitTools.Testing; -using GitVersion.Configuration; using GitVersion.Core.Tests.Helpers; -using GitVersion.Model.Configuration; -using NSubstitute; using NUnit.Framework; namespace GitVersion.Core.Tests.IntegrationTests; @@ -10,24 +7,35 @@ namespace GitVersion.Core.Tests.IntegrationTests; [TestFixture] public class IgnoreBeforeScenarios : TestBase { - [Test] - public void ShouldFallbackToBaseVersionWhenAllCommitsAreIgnored() + [TestCase(null, "0.0.1+0")] + [TestCase("0.0.1", "0.0.1+0")] + [TestCase("0.1.0", "0.1.0+0")] + [TestCase("1.0.0", "1.0.0+0")] + public void ShouldFallbackToBaseVersionWhenAllCommitsAreIgnored(string? nextVersion, string expectedFullSemVer) { using var fixture = new EmptyRepositoryFixture(); - var objectId = fixture.Repository.MakeACommit(); - var commit = Substitute.For(); - commit.Sha.Returns(objectId.Sha); - commit.When.Returns(DateTimeOffset.Now); + var dateTimeNow = DateTimeOffset.Now; + fixture.MakeACommit(); - var config = new ConfigurationBuilder() - .Add(new Config - { - Ignore = new IgnoreConfig - { - Before = commit.When.AddMinutes(1) - } - }).Build(); + var configuration = TestConfigurationBuilder.New.WithNextVersion(nextVersion) + .WithIgnoreConfig(new() { Before = dateTimeNow.AddDays(1) }).Build(); - fixture.AssertFullSemver("0.1.0+0", config); + fixture.AssertFullSemver(expectedFullSemVer, configuration); + } + + [TestCase(null, "0.0.1+1")] + [TestCase("0.0.1", "0.0.1+1")] + [TestCase("0.1.0", "0.1.0+1")] + [TestCase("1.0.0", "1.0.0+1")] + public void ShouldNotFallbackToBaseVersionWhenAllCommitsAreNotIgnored(string? nextVersion, string expectedFullSemVer) + { + using var fixture = new EmptyRepositoryFixture(); + var dateTimeNow = DateTimeOffset.Now; + fixture.MakeACommit(); + + var configuration = TestConfigurationBuilder.New.WithNextVersion(nextVersion) + .WithIgnoreConfig(new() { Before = dateTimeNow.AddDays(-1) }).Build(); + + fixture.AssertFullSemver(expectedFullSemVer, configuration); } } diff --git a/src/GitVersion.Core.Tests/IntegrationTests/MainScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/MainScenarios.cs index 4b53420583..f2bd6b697a 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/MainScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/MainScenarios.cs @@ -62,7 +62,7 @@ public void GivenARepositoryWithCommitsButNoTagsVersionShouldBe01() fixture.Repository.MakeACommit(); // When - fixture.AssertFullSemver("0.1.0+2"); + fixture.AssertFullSemver("0.0.1+3"); } [Test] @@ -76,7 +76,7 @@ public void GivenARepositoryWithCommitsButBadTagsVersionShouldBe01() fixture.Repository.MakeACommit(); // When - fixture.AssertFullSemver("0.1.0+2"); + fixture.AssertFullSemver("0.0.1+3"); } [Test] @@ -84,16 +84,16 @@ public void GivenARepositoryWithCommitsButNoTagsWithDetachedHeadVersionShouldBe0 { using var fixture = new EmptyRepositoryFixture(); // Given - fixture.Repository.MakeACommit(); - fixture.Repository.MakeACommit(); - fixture.Repository.MakeACommit(); + fixture.Repository.MakeACommit("one"); + fixture.Repository.MakeACommit("two"); + fixture.Repository.MakeACommit("three"); var commit = fixture.Repository.Head.Tip; fixture.Repository.MakeACommit(); Commands.Checkout(fixture.Repository, commit); // When - fixture.AssertFullSemver("0.1.0+2", onlyTrackedBranches: false); + fixture.AssertFullSemver("0.0.1+3", onlyTrackedBranches: false); } [Test] @@ -115,10 +115,45 @@ public void GivenARepositoryWithTagAndANextVersionTxtFileAndNoCommitsVersionShou using var fixture = new EmptyRepositoryFixture(); const string taggedVersion = "1.0.3"; fixture.Repository.MakeATaggedCommit(taggedVersion); - + fixture.AssertFullSemver("1.0.3"); fixture.AssertFullSemver("1.0.3", new Config { NextVersion = "1.1.0" }); } + [Test] + public void GivenARepositoryWithTagAndANextVersionTxtFileAndNoCommitsVersionShouldBeTag2() + { + using var fixture = new EmptyRepositoryFixture(); + const string taggedVersion = "1.0.3"; + fixture.Repository.MakeATaggedCommit(taggedVersion); + fixture.Repository.MakeACommit(); + fixture.AssertFullSemver("1.0.4+1"); + + // I'm not sure if the postfix +1 is correct here... + // but the next version configuration property is something for the user to manipulate the resulting version. + fixture.AssertFullSemver("1.1.0+1", new Config { NextVersion = "1.1.0" }); + } + + [Test] + public void GivenARepositoryWithTagAndANextVersionTxtFileAndNoCommitsVersionShouldBeTag3() + { + using var fixture = new EmptyRepositoryFixture(); + const string taggedVersion = "1.0.3"; + fixture.Repository.MakeATaggedCommit(taggedVersion); + fixture.AssertFullSemver("1.0.3"); + fixture.AssertFullSemver("1.0.3", new Config { NextVersion = "1.0.2" }); + } + + [Test] + public void GivenARepositoryWithTagAndANextVersionTxtFileAndNoCommitsVersionShouldBeTag4() + { + using var fixture = new EmptyRepositoryFixture(); + const string taggedVersion = "1.0.3"; + fixture.Repository.MakeATaggedCommit(taggedVersion); + fixture.Repository.MakeACommit(); + fixture.AssertFullSemver("1.0.4+1"); + fixture.AssertFullSemver("1.0.4+1", new Config { NextVersion = "1.0.4" }); + } + [Test] public void GivenARepositoryWithTagAndNoNextVersionTxtFileVersionShouldBeTagWithBumpedPatch() { @@ -199,11 +234,150 @@ public void AreTagsNotAdheringToTagPrefixIgnored() fixture.Repository.MakeATaggedCommit(taggedVersion); fixture.Repository.MakeCommits(5); - fixture.AssertFullSemver("0.1.0+5", config); //Fallback version + 5 commits since tag + fixture.AssertFullSemver("0.0.1+6", config); taggedVersion = "bad/1.0.3"; fixture.Repository.MakeATaggedCommit(taggedVersion); - fixture.AssertFullSemver("0.1.0+6", config); //Fallback version + 6 commits since tag + fixture.AssertFullSemver("0.0.1+7", config); + } + + [Test] + public void NextVersionShouldBeConsideredOnTheDevelopmentBranch() + { + using EmptyRepositoryFixture fixture = new("develop"); + + var configurationBuilder = TestConfigurationBuilder.New; + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.1", configurationBuilder.Build()); + + configurationBuilder.WithNextVersion("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-alpha.1", configurationBuilder.Build()); + + fixture.MakeACommit(); + configurationBuilder.WithNextVersion(null); + + // ✅ succeeds as expected + fixture.AssertFullSemver("0.1.0-alpha.2", configurationBuilder.Build()); + + configurationBuilder.WithNextVersion("1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-alpha.2", configurationBuilder.Build()); + } + + /// + /// Prevent decrementation of versions on the develop branch #3177 + /// (see https://github.com/GitTools/GitVersion/discussions/3177) + /// + [Test] + public void PreventDecrementationOfVersionsOnTheDevelopmentBranch() + { + using EmptyRepositoryFixture fixture = new("develop"); + + var configurationBuilder = TestConfigurationBuilder.New; + + configurationBuilder.WithNextVersion("1.0.0"); + fixture.MakeACommit(); + + // now we are ready to start with the preparation of the 1.0.0 release + fixture.BranchTo("release/1.0.0"); + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configurationBuilder.Build()); + + fixture.Checkout("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+0", configurationBuilder.Build()); + + // make another commit on release/1.0.0 to prepare the actual beta1 release + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1+1", configurationBuilder.Build()); + + // now we makes changes on develop that may or may not end up in the 1.0.0 release + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configurationBuilder.Build()); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configurationBuilder.Build()); + + // now we do the actual release of beta 1 + fixture.Checkout("release/1.0.0"); + fixture.ApplyTag("1.0.0-beta.1"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.1", configurationBuilder.Build()); + + // continue with more work on develop that may or may not end up in the 1.0.0 release + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.1", configurationBuilder.Build()); + + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.2", configurationBuilder.Build()); + + // now we decide that the new on develop should be part of the beta 2 release + // so we merge it into release/1.0.0 with --no-ff because it is a protected branch + // but we don't do the release of beta 2 just yet + fixture.Checkout("release/1.0.0"); + fixture.MergeNoFF("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.2+2", configurationBuilder.Build()); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configurationBuilder.Build()); + + fixture.Checkout("release/1.0.0"); + fixture.ApplyTag("1.0.0-beta.2"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.0.0-beta.2", configurationBuilder.Build()); + + fixture.Checkout("develop"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.0", configurationBuilder.Build()); + + fixture.MergeNoFF("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.3", configurationBuilder.Build()); + + fixture.Repository.Branches.Remove("release/1.0.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.3", configurationBuilder.Build()); + + fixture.Repository.Tags.Remove("1.0.0-beta.1"); + fixture.Repository.Tags.Remove("1.0.0-beta.2"); + + // ❔ expected: "1.0.0-alpha.3" + // This behavior needs to be changed for the git flow workflow using the track-merge-message or track-merge-target options. + // [Bug] track-merge-changes produces unexpected result when combining hotfix and support branches #3052 + fixture.AssertFullSemver("1.1.0-alpha.3", configurationBuilder.Build()); + + configurationBuilder.WithNextVersion("1.1.0"); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-alpha.3", configurationBuilder.Build()); } } diff --git a/src/GitVersion.Core.Tests/IntegrationTests/MainlineDevelopmentMode.cs b/src/GitVersion.Core.Tests/IntegrationTests/MainlineDevelopmentMode.cs index 404bf16b0b..3ba84e8e74 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/MainlineDevelopmentMode.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/MainlineDevelopmentMode.cs @@ -335,6 +335,7 @@ public void VerifyIssue1154CanForwardMergeMainToFeatureBranch() { using var fixture = new EmptyRepositoryFixture(); fixture.MakeACommit(); + fixture.AssertFullSemver("0.0.1", this.config); fixture.BranchTo("feature/branch2"); fixture.BranchTo("feature/branch1"); fixture.MakeACommit(); @@ -342,7 +343,7 @@ public void VerifyIssue1154CanForwardMergeMainToFeatureBranch() fixture.Checkout(MainBranch); fixture.MergeNoFF("feature/branch1"); - fixture.AssertFullSemver("0.1.1", this.config); + fixture.AssertFullSemver("0.0.2", this.config); fixture.Checkout("feature/branch2"); fixture.MakeACommit(); @@ -350,7 +351,7 @@ public void VerifyIssue1154CanForwardMergeMainToFeatureBranch() fixture.MakeACommit(); fixture.MergeNoFF(MainBranch); - fixture.AssertFullSemver("0.1.2-branch2.4", this.config); + fixture.AssertFullSemver("0.0.3-branch2.4", this.config); } [Test] @@ -488,24 +489,27 @@ public void BranchWithoutMergeBaseMainlineBranchIsFound() using var fixture = new EmptyRepositoryFixture(); fixture.Repository.MakeACommit(); + fixture.AssertFullSemver("0.0.1", currentConfig); Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("master")); fixture.Repository.Branches.Remove(fixture.Repository.Branches["main"]); + fixture.AssertFullSemver("0.0.1", currentConfig); fixture.Repository.MakeCommits(2); + fixture.AssertFullSemver("0.0.3", currentConfig); Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("issue-branch")); fixture.Repository.MakeACommit(); - fixture.AssertFullSemver("0.1.3-issue-branch.1", currentConfig); + fixture.AssertFullSemver("0.0.4-issue-branch.1", currentConfig); } [Test] public void GivenARemoteGitRepositoryWithCommitsThenClonedLocalDevelopShouldMatchRemoteVersion() { using var fixture = new RemoteRepositoryFixture(); - fixture.AssertFullSemver("0.1.4", config); + fixture.AssertFullSemver("0.0.5", config); // RemoteRepositoryFixture creates 5 commits. fixture.BranchTo("develop"); - fixture.AssertFullSemver("0.2.0-alpha.0", config); + fixture.AssertFullSemver("0.1.0-alpha.0", config); Console.WriteLine(fixture.SequenceDiagram.GetDiagram()); var local = fixture.CloneRepository(); - fixture.AssertFullSemver("0.2.0-alpha.0", config, repository: local.Repository); + fixture.AssertFullSemver("0.1.0-alpha.0", config, repository: local.Repository); local.Repository.DumpGraph(); } diff --git a/src/GitVersion.Core.Tests/IntegrationTests/OtherScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/OtherScenarios.cs index eb6c77a110..712f1bc985 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/OtherScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/OtherScenarios.cs @@ -18,9 +18,9 @@ public class OtherScenarios : TestBase public void DoNotBlowUpWhenMainAndDevelopPointAtSameCommit() { using var fixture = new RemoteRepositoryFixture(); - fixture.Repository.MakeACommit(); - fixture.Repository.MakeATaggedCommit("1.0.0"); - fixture.Repository.MakeACommit(); + fixture.MakeACommit(); + fixture.MakeATaggedCommit("1.0.0"); + fixture.MakeACommit(); fixture.Repository.CreateBranch("develop"); Commands.Fetch((Repository)fixture.LocalRepositoryFixture.Repository, fixture.LocalRepositoryFixture.Repository.Network.Remotes.First().Name, Array.Empty(), new FetchOptions(), null); @@ -34,10 +34,10 @@ public void DoNotBlowUpWhenMainAndDevelopPointAtSameCommit() public void AllowNotHavingMain() { using var fixture = new EmptyRepositoryFixture(); - fixture.Repository.MakeACommit(); - fixture.Repository.MakeATaggedCommit("1.0.0"); - fixture.Repository.MakeACommit(); - Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("develop")); + fixture.MakeACommit(); + fixture.MakeATaggedCommit("1.0.0"); + fixture.MakeACommit(); + fixture.BranchTo("develop"); fixture.Repository.Branches.Remove(fixture.Repository.Branches[MainBranch]); fixture.AssertFullSemver("1.1.0-alpha.1"); @@ -47,10 +47,10 @@ public void AllowNotHavingMain() public void AllowHavingVariantsStartingWithMaster() { using var fixture = new EmptyRepositoryFixture(); - fixture.Repository.MakeACommit(); - fixture.Repository.MakeATaggedCommit("1.0.0"); - fixture.Repository.MakeACommit(); - Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("masterfix")); + fixture.MakeACommit(); + fixture.MakeATaggedCommit("1.0.0"); + fixture.MakeACommit(); + fixture.BranchTo("masterfix"); fixture.AssertFullSemver("1.0.1-masterfix.1+1"); } @@ -59,23 +59,22 @@ public void AllowHavingVariantsStartingWithMaster() public void AllowHavingMasterInsteadOfMain() { using var fixture = new EmptyRepositoryFixture(); - fixture.Repository.MakeACommit(); - Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("develop")); - Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("master")); - fixture.Repository.Branches.Remove(fixture.Repository.Branches["main"]); + fixture.MakeACommit("one"); + fixture.BranchTo("develop"); + fixture.BranchTo("master"); + fixture.Repository.Branches.Remove("main"); - fixture.AssertFullSemver("0.1.0+0"); + fixture.AssertFullSemver("0.0.1+1"); } [Test] public void AllowHavingVariantsStartingWithMain() { using var fixture = new EmptyRepositoryFixture(); - fixture.Repository.MakeACommit(); - fixture.Repository.MakeATaggedCommit("1.0.0"); - fixture.Repository.MakeACommit(); - Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("mainfix")); - + fixture.MakeACommit(); + fixture.MakeATaggedCommit("1.0.0"); + fixture.MakeACommit(); + fixture.BranchTo("mainfix"); fixture.AssertFullSemver("1.0.1-mainfix.1+1"); } @@ -83,11 +82,11 @@ public void AllowHavingVariantsStartingWithMain() public void DoNotBlowUpWhenDevelopAndFeatureBranchPointAtSameCommit() { using var fixture = new RemoteRepositoryFixture(); - fixture.Repository.MakeACommit(); - Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("develop")); - fixture.Repository.MakeACommit(); - fixture.Repository.MakeATaggedCommit("1.0.0"); - fixture.Repository.MakeACommit(); + fixture.MakeACommit(); + fixture.BranchTo("develop"); + fixture.MakeACommit(); + fixture.MakeATaggedCommit("1.0.0"); + fixture.MakeACommit(); fixture.Repository.CreateBranch("feature/someFeature"); Commands.Fetch((Repository)fixture.LocalRepositoryFixture.Repository, fixture.LocalRepositoryFixture.Repository.Network.Remotes.First().Name, Array.Empty(), new FetchOptions(), null); @@ -104,7 +103,7 @@ public void DoNotBlowUpWhenDevelopAndFeatureBranchPointAtSameCommit() public void HasDirtyFlagWhenUncommittedChangesAreInRepo(bool stageFile, int numberOfFiles) { using var fixture = new EmptyRepositoryFixture(); - fixture.Repository.MakeACommit(); + fixture.MakeACommit(); for (int i = 0; i < numberOfFiles; i++) { @@ -125,7 +124,7 @@ public void HasDirtyFlagWhenUncommittedChangesAreInRepo(bool stageFile, int numb public void NoDirtyFlagInCleanRepository() { using var fixture = new EmptyRepositoryFixture(); - fixture.Repository.MakeACommit(); + fixture.MakeACommit(); var version = fixture.GetVersion(); const int zero = 0; diff --git a/src/GitVersion.Core.Tests/IntegrationTests/PullRequestScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/PullRequestScenarios.cs index 13cd261f34..c7dad8c1f4 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/PullRequestScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/PullRequestScenarios.cs @@ -89,13 +89,13 @@ public void CalculatesCorrectVersionAfterReleaseBranchMergedToMain() { using var fixture = new EmptyRepositoryFixture(); fixture.Repository.MakeATaggedCommit("1.0.0"); - fixture.Repository.MakeACommit(); + fixture.Repository.MakeACommit("one"); Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("release/2.0.0")); - fixture.Repository.MakeACommit(); - fixture.Repository.MakeACommit(); + fixture.Repository.MakeACommit("two"); + fixture.Repository.MakeACommit("three"); fixture.Repository.CreatePullRequestRef("release/2.0.0", MainBranch, normalise: true); - fixture.AssertFullSemver("2.0.0-PullRequest2.0"); + fixture.AssertFullSemver("2.0.0-PullRequest2.3"); } } diff --git a/src/GitVersion.Core.Tests/IntegrationTests/ReleaseBranchScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/ReleaseBranchScenarios.cs index 30ef7f92a6..42ef678c35 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/ReleaseBranchScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/ReleaseBranchScenarios.cs @@ -47,12 +47,12 @@ public void NoMergeBacksToDevelopInCaseThereAreChangesInReleaseBranch() // Merge to develop fixture.Checkout("develop"); + fixture.AssertFullSemver("1.1.0-alpha.0"); fixture.Repository.MergeNoFF("release/1.0.0"); fixture.AssertFullSemver("1.1.0-alpha.2"); - fixture.Repository.MakeACommit(); + fixture.AssertFullSemver("1.1.0-alpha.3"); fixture.Repository.Branches.Remove("release/1.0.0"); - fixture.AssertFullSemver("1.1.0-alpha.3"); } @@ -195,6 +195,8 @@ public void MainVersioningContinuousCorrectlyAfterMergingReleaseBranch() fixture.Checkout(MainBranch); fixture.Repository.MergeNoFF("release-2.0.0", Generate.SignatureNow()); + fixture.AssertFullSemver("2.0.0+0"); + fixture.Repository.Branches.Remove("release-2.0.0"); fixture.AssertFullSemver("2.0.0+0"); fixture.Repository.ApplyTag("2.0.0"); fixture.Repository.MakeCommits(1); diff --git a/src/GitVersion.Core.Tests/IntegrationTests/RemoteRepositoryScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/RemoteRepositoryScenarios.cs index 7bc38a0226..43e4a6ab77 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/RemoteRepositoryScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/RemoteRepositoryScenarios.cs @@ -16,8 +16,8 @@ public class RemoteRepositoryScenarios : TestBase public void GivenARemoteGitRepositoryWithCommitsThenClonedLocalShouldMatchRemoteVersion() { using var fixture = new RemoteRepositoryFixture(); - fixture.AssertFullSemver("0.1.0+4"); - fixture.AssertFullSemver("0.1.0+4", repository: fixture.LocalRepositoryFixture.Repository); + fixture.AssertFullSemver("0.0.1+5"); + fixture.AssertFullSemver("0.0.1+5", repository: fixture.LocalRepositoryFixture.Repository); } [Test] @@ -79,11 +79,11 @@ public void GivenARemoteGitRepositoryAheadOfLocalRepositoryThenChangesShouldPull { using var fixture = new RemoteRepositoryFixture(); fixture.Repository.MakeACommit(); - fixture.AssertFullSemver("0.1.0+5"); - fixture.AssertFullSemver("0.1.0+4", repository: fixture.LocalRepositoryFixture.Repository); + fixture.AssertFullSemver("0.0.1+6"); + fixture.AssertFullSemver("0.0.1+5", repository: fixture.LocalRepositoryFixture.Repository); var buildSignature = fixture.LocalRepositoryFixture.Repository.Config.BuildSignature(new DateTimeOffset(DateTime.Now)); Commands.Pull((Repository)fixture.LocalRepositoryFixture.Repository, buildSignature, new PullOptions()); - fixture.AssertFullSemver("0.1.0+5", repository: fixture.LocalRepositoryFixture.Repository); + fixture.AssertFullSemver("0.0.1+6", repository: fixture.LocalRepositoryFixture.Repository); } [Test] diff --git a/src/GitVersion.Core.Tests/IntegrationTests/SemVerOfAFeatureBranchStartedFromAReleaseBranchGetsDecrementedScenario.cs b/src/GitVersion.Core.Tests/IntegrationTests/SemVerOfAFeatureBranchStartedFromAReleaseBranchGetsDecrementedScenario.cs new file mode 100644 index 0000000000..71b3082861 --- /dev/null +++ b/src/GitVersion.Core.Tests/IntegrationTests/SemVerOfAFeatureBranchStartedFromAReleaseBranchGetsDecrementedScenario.cs @@ -0,0 +1,34 @@ +using GitTools.Testing; +using NUnit.Framework; + +namespace GitVersion.Core.Tests.IntegrationTests; + +/// +/// [Bug] SemVer of a feature branch started from a release branch gets decremented #3151 +/// +[TestFixture] +public class SemVerOfAFeatureBranchStartedFromAReleaseBranchGetsDecrementedScenario +{ + [Test] + public void ShouldPickUpReleaseVersionAfterCreatedFromRelease() + { + using var fixture = new EmptyRepositoryFixture(); + + // Create develop and a release branch + fixture.MakeATaggedCommit("1.0.0"); + fixture.MakeACommit(); + fixture.BranchTo("develop"); + fixture.BranchTo("release/1.1.0"); + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-beta.1+1"); + + // Create a feature branch from the release/1.1.0 branch + fixture.BranchTo("feature/test"); + fixture.MakeACommit(); + + // ✅ succeeds as expected + fixture.AssertFullSemver("1.1.0-test.1+2"); + } +} diff --git a/src/GitVersion.Core.Tests/IntegrationTests/VersionInCurrentBranchNameScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/VersionInCurrentBranchNameScenarios.cs index 33ac445def..a87299d274 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/VersionInCurrentBranchNameScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/VersionInCurrentBranchNameScenarios.cs @@ -65,6 +65,6 @@ public void DoesNotTakeVersionFromNameOfRemoteReleaseBranchInCustomRemote() fixture.LocalRepositoryFixture.Checkout("upstream/release/2.0.0"); - fixture.LocalRepositoryFixture.AssertFullSemver("0.1.0-beta.1+5"); + fixture.LocalRepositoryFixture.AssertFullSemver("0.0.0-beta.1+6"); } } diff --git a/src/GitVersion.Core.Tests/IntegrationTests/VersionInMergedBranchNameScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/VersionInMergedBranchNameScenarios.cs index fe026c9d13..bb58125509 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/VersionInMergedBranchNameScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/VersionInMergedBranchNameScenarios.cs @@ -66,7 +66,7 @@ public void DoesNotTakeVersionFromNameOfRemoteReleaseBranchInCustomRemote() fixture.LocalRepositoryFixture.MergeNoFF("upstream/release/2.0.0"); - fixture.LocalRepositoryFixture.AssertFullSemver("0.1.0+6"); + fixture.LocalRepositoryFixture.AssertFullSemver("0.0.1+7"); } } diff --git a/src/GitVersion.Core.Tests/Model/GitVersionContextTests.cs b/src/GitVersion.Core.Tests/Model/GitVersionContextTests.cs deleted file mode 100644 index e16a66d8ec..0000000000 --- a/src/GitVersion.Core.Tests/Model/GitVersionContextTests.cs +++ /dev/null @@ -1,204 +0,0 @@ -using GitTools.Testing; -using GitVersion.Configuration; -using GitVersion.Core.Tests.Helpers; -using GitVersion.Model.Configuration; -using GitVersion.VersionCalculation; -using LibGit2Sharp; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using NSubstitute; -using NUnit.Framework; -using Shouldly; - -namespace GitVersion.Core.Tests; - -public class GitVersionContextTests : TestBase -{ - [Test] - [Theory] - public void CanInheritVersioningMode(VersioningMode mode) - { - using var fixture = new EmptyRepositoryFixture(); - - var config = new ConfigurationBuilder() - .Add(new Config { VersioningMode = mode }) - .Build(); - - const string branchName = MainBranch; - - var mockCommit = GitToolsTestingExtensions.CreateMockCommit(); - var mockBranch = GitToolsTestingExtensions.CreateMockBranch(branchName, mockCommit); - - var branches = Substitute.For(); - branches.GetEnumerator().Returns(_ => ((IEnumerable)new[] { mockBranch }).GetEnumerator()); - - var mockRepository = Substitute.For(); - mockRepository.Head.Returns(mockBranch); - mockRepository.Branches.Returns(branches); - mockRepository.Commits.Returns(mockBranch.Commits); - - var effectiveConfiguration = GetEffectiveConfiguration(fixture.RepositoryPath, mockRepository, branchName, config); - - effectiveConfiguration.VersioningMode.ShouldBe(mode); - } - - [TestCase(IncrementStrategy.Inherit, IncrementStrategy.Patch)] // Since it inherits, the increment strategy of main is used => Patch - [TestCase(IncrementStrategy.Patch, null)] - [TestCase(IncrementStrategy.Major, null)] - [TestCase(IncrementStrategy.Minor, null)] - [TestCase(IncrementStrategy.None, null)] - public void CanInheritIncrement(IncrementStrategy increment, IncrementStrategy? alternateExpected) - { - // Dummy branch name to make sure that no default config exists. - const string dummyBranchName = "dummy"; - - var config = new ConfigurationBuilder() - .Add(new Config { Increment = increment }) - .Build(); - - using var fixture = new EmptyRepositoryFixture(); - fixture.MakeACommit(); - fixture.BranchTo(dummyBranchName); - fixture.MakeACommit(); - - var effectiveConfiguration = GetEffectiveConfiguration(fixture.RepositoryPath, fixture.Repository.ToGitRepository(), dummyBranchName, config); - - effectiveConfiguration.Increment.ShouldBe(alternateExpected ?? increment); - } - - [Test] - public void UsesBranchSpecificConfigOverTopLevelDefaults() - { - using var fixture = new EmptyRepositoryFixture(); - - const string branchName = "develop"; - var config = new ConfigurationBuilder() - .Add(new Config - { - VersioningMode = VersioningMode.ContinuousDelivery, - Branches = - { - { - branchName, new BranchConfig - { - VersioningMode = VersioningMode.ContinuousDeployment, - Tag = "alpha" - } - } - } - }) - .Build(); - - var main = GitToolsTestingExtensions.CreateMockBranch(MainBranch, GitToolsTestingExtensions.CreateMockCommit()); - var develop = GitToolsTestingExtensions.CreateMockBranch(branchName, GitToolsTestingExtensions.CreateMockCommit()); - - var branches = Substitute.For(); - branches.GetEnumerator().Returns(_ => ((IEnumerable)new[] { main, develop }).GetEnumerator()); - - var mockRepository = Substitute.For(); - mockRepository.Head.Returns(develop); - mockRepository.Branches.Returns(branches); - mockRepository.Commits.Returns(develop.Commits); - - var effectiveConfiguration = GetEffectiveConfiguration(fixture.RepositoryPath, mockRepository, branchName, config); - - effectiveConfiguration.Tag.ShouldBe("alpha"); - } - - [Test] - public void UsesFirstBranchConfigWhenMultipleMatch() - { - using var fixture = new EmptyRepositoryFixture(); - - var branchConfig = new BranchConfig - { - VersioningMode = VersioningMode.Mainline, - Increment = IncrementStrategy.None, - PreventIncrementOfMergedBranchVersion = false, - TrackMergeTarget = false, - TracksReleaseBranches = false, - IsReleaseBranch = false, - SourceBranches = new HashSet() - }; - var config = new ConfigurationBuilder() - .Add(new Config - { - VersioningMode = VersioningMode.ContinuousDelivery, - Branches = - { - { "release/latest", new BranchConfig(branchConfig) { Increment = IncrementStrategy.None, Regex = "release/latest" } }, - { "release", new BranchConfig(branchConfig) { Increment = IncrementStrategy.Patch, Regex = "releases?[/-]" } } - } - }) - .Build(); - - var releaseLatestBranch = GitToolsTestingExtensions.CreateMockBranch("release/latest", GitToolsTestingExtensions.CreateMockCommit()); - var releaseVersionBranch = GitToolsTestingExtensions.CreateMockBranch("release/1.0.0", GitToolsTestingExtensions.CreateMockCommit()); - - var branches = Substitute.For(); - branches.GetEnumerator().Returns(_ => ((IEnumerable)new[] { releaseLatestBranch, releaseVersionBranch }).GetEnumerator()); - - var mockRepository = Substitute.For(); - mockRepository.Branches.Returns(branches); - mockRepository.Head.Returns(releaseLatestBranch); - mockRepository.Commits.Returns(releaseLatestBranch.Commits); - - var latestEffectiveConfiguration = GetEffectiveConfiguration(fixture.RepositoryPath, mockRepository, releaseLatestBranch.Name.Canonical, config); - latestEffectiveConfiguration.Increment.ShouldBe(IncrementStrategy.None); - - mockRepository.Head.Returns(releaseVersionBranch); - var effectiveConfiguration = GetEffectiveConfiguration(fixture.RepositoryPath, mockRepository, releaseVersionBranch.Name.Canonical, config); - effectiveConfiguration.Increment.ShouldBe(IncrementStrategy.Patch); - } - - [Test] - public void CanFindParentBranchForInheritingIncrementStrategy() - { - var config = new ConfigurationBuilder() - .Add(new Config - { - Branches = - { - { "develop", new BranchConfig { Increment = IncrementStrategy.Major } }, - { "feature", new BranchConfig { Increment = IncrementStrategy.Inherit } } - } - }) - .Build(); - - using var fixture = new EmptyRepositoryFixture(); - fixture.Repository.MakeACommit(); - Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("develop")); - fixture.Repository.MakeACommit(); - var featureBranch = fixture.Repository.CreateBranch("feature/foo"); - Commands.Checkout(fixture.Repository, featureBranch); - fixture.Repository.MakeACommit(); - - var effectiveConfiguration = GetEffectiveConfiguration(fixture.RepositoryPath, fixture.Repository.ToGitRepository(), "develop", config); - - effectiveConfiguration.Increment.ShouldBe(IncrementStrategy.Major); - } - - private static EffectiveConfiguration GetEffectiveConfiguration(string workingDirectory, IGitRepository repository, string branch, Config? config = null) - { - var options = Options.Create(new GitVersionOptions - { - WorkingDirectory = workingDirectory, - RepositoryInfo = { TargetBranch = branch }, - ConfigInfo = { OverrideConfig = config } - }); - - var sp = ConfigureServices(services => - { - services.AddSingleton(options); - services.AddSingleton(repository); - }); - - var context = sp.GetRequiredService>().Value; - var branchConfigurationCalculator = sp.GetRequiredService(); - var configuration = context.FullConfiguration; - var currentBranchConfig = branchConfigurationCalculator.GetBranchConfiguration( - context.CurrentBranch, context.CurrentCommit, configuration - ); - return new(configuration, currentBranchConfig); - } -} diff --git a/src/GitVersion.Core.Tests/VersionCalculation/BaseVersionCalculatorTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/BaseVersionCalculatorTests.cs deleted file mode 100644 index ec2229e460..0000000000 --- a/src/GitVersion.Core.Tests/VersionCalculation/BaseVersionCalculatorTests.cs +++ /dev/null @@ -1,249 +0,0 @@ -using GitVersion.Core.Tests.Helpers; -using GitVersion.Model.Configuration; -using GitVersion.VersionCalculation; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using NSubstitute; -using NUnit.Framework; -using Shouldly; - -namespace GitVersion.Core.Tests.VersionCalculation; - -[TestFixture] -public class BaseVersionCalculatorTests : TestBase -{ - [Test] - public void ChoosesHighestVersionReturnedFromStrategies() - { - var dateTimeOffset = DateTimeOffset.Now; - var versionCalculator = GetBaseVersionCalculator(contextBuilder => - contextBuilder.OverrideServices(services => - { - services.RemoveAll(); - services.AddSingleton(new V1Strategy(DateTimeOffset.Now)); - services.AddSingleton(new V2Strategy(dateTimeOffset)); - })); - - var (baseVersion, _) = versionCalculator.GetBaseVersion(); - - baseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); - baseVersion.ShouldIncrement.ShouldBe(true); - baseVersion.BaseVersionSource.ShouldNotBeNull(); - baseVersion.BaseVersionSource.When.ShouldBe(dateTimeOffset); - } - - [Test] - public void UsesWhenFromNextBestMatchIfHighestDoesntHaveWhen() - { - var when = DateTimeOffset.Now; - - var versionCalculator = GetBaseVersionCalculator(contextBuilder => - contextBuilder.OverrideServices(services => - { - services.RemoveAll(); - services.AddSingleton(new V1Strategy(when)); - services.AddSingleton(new V2Strategy(null)); - })); - - var (baseVersion, _) = versionCalculator.GetBaseVersion(); - - baseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); - baseVersion.ShouldIncrement.ShouldBe(true); - baseVersion.BaseVersionSource.ShouldNotBeNull(); - baseVersion.BaseVersionSource.When.ShouldBe(when); - } - - [Test] - public void UsesWhenFromNextBestMatchIfHighestDoesntHaveWhenReversedOrder() - { - var when = DateTimeOffset.Now; - - var versionCalculator = GetBaseVersionCalculator(contextBuilder => - contextBuilder.OverrideServices(services => - { - services.RemoveAll(); - services.AddSingleton(new V1Strategy(null)); - services.AddSingleton(new V2Strategy(when)); - })); - - var (baseVersion, _) = versionCalculator.GetBaseVersion(); - - baseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); - baseVersion.ShouldIncrement.ShouldBe(true); - baseVersion.BaseVersionSource.ShouldNotBeNull(); - baseVersion.BaseVersionSource.When.ShouldBe(when); - } - - [Test] - public void ShouldNotFilterVersion() - { - var fakeIgnoreConfig = new TestIgnoreConfig(new ExcludeSourcesContainingExclude()); - var version = new BaseVersion("dummy", false, new SemanticVersion(2), GitToolsTestingExtensions.CreateMockCommit(), null); - - var versionCalculator = GetBaseVersionCalculator(contextBuilder => contextBuilder - .WithConfig(new Config { Ignore = fakeIgnoreConfig }) - .OverrideServices(services => - { - services.RemoveAll(); - services.AddSingleton(new TestVersionStrategy(version)); - })); - - var (baseVersion, _) = versionCalculator.GetBaseVersion(); - - baseVersion.Source.ShouldBe(version.Source); - baseVersion.ShouldIncrement.ShouldBe(version.ShouldIncrement); - baseVersion.SemanticVersion.ShouldBe(version.SemanticVersion); - } - - [Test] - public void ShouldFilterVersion() - { - var fakeIgnoreConfig = new TestIgnoreConfig(new ExcludeSourcesContainingExclude()); - - var higherVersion = new BaseVersion("exclude", false, new SemanticVersion(2), GitToolsTestingExtensions.CreateMockCommit(), null); - var lowerVersion = new BaseVersion("dummy", false, new SemanticVersion(1), GitToolsTestingExtensions.CreateMockCommit(), null); - - var versionCalculator = GetBaseVersionCalculator(contextBuilder => contextBuilder - .WithConfig(new Config { Ignore = fakeIgnoreConfig }) - .OverrideServices(services => - { - services.RemoveAll(); - services.AddSingleton(new TestVersionStrategy(higherVersion, lowerVersion)); - })); - var (baseVersion, _) = versionCalculator.GetBaseVersion(); - - baseVersion.Source.ShouldNotBe(higherVersion.Source); - baseVersion.SemanticVersion.ShouldNotBe(higherVersion.SemanticVersion); - baseVersion.Source.ShouldBe(lowerVersion.Source); - baseVersion.SemanticVersion.ShouldBe(lowerVersion.SemanticVersion); - } - - [Test] - public void ShouldIgnorePreReleaseVersionInMainlineMode() - { - var fakeIgnoreConfig = new TestIgnoreConfig(new ExcludeSourcesContainingExclude()); - - var lowerVersion = new BaseVersion("dummy", false, new SemanticVersion(1), GitToolsTestingExtensions.CreateMockCommit(), null); - var preReleaseVersion = new BaseVersion( - "prerelease", - false, - new SemanticVersion(1, 0, 1) - { - PreReleaseTag = new SemanticVersionPreReleaseTag - { - Name = "alpha", - Number = 1 - } - }, - GitToolsTestingExtensions.CreateMockCommit(), - null - ); - - var versionCalculator = GetBaseVersionCalculator(contextBuilder => contextBuilder - .WithConfig(new Config { VersioningMode = VersioningMode.Mainline, Ignore = fakeIgnoreConfig }) - .OverrideServices(services => - { - services.RemoveAll(); - services.AddSingleton(new TestVersionStrategy(preReleaseVersion, lowerVersion)); - })); - var (baseVersion, _) = versionCalculator.GetBaseVersion(); - - baseVersion.Source.ShouldNotBe(preReleaseVersion.Source); - baseVersion.SemanticVersion.ShouldNotBe(preReleaseVersion.SemanticVersion); - baseVersion.Source.ShouldBe(lowerVersion.Source); - baseVersion.SemanticVersion.ShouldBe(lowerVersion.SemanticVersion); - } - - private static IBaseVersionCalculator GetBaseVersionCalculator(Action contextBuilderAction) - { - var contextBuilder = new GitVersionContextBuilder(); - contextBuilderAction.Invoke(contextBuilder); - - contextBuilder.Build(); - contextBuilder.ServicesProvider.ShouldNotBeNull(); - return contextBuilder.ServicesProvider.GetRequiredService(); - } - - private class TestIgnoreConfig : IgnoreConfig - { - private readonly IVersionFilter filter; - - public override bool IsEmpty => false; - - public TestIgnoreConfig(IVersionFilter filter) => this.filter = filter; - - public override IEnumerable ToFilters() - { - yield return this.filter; - } - } - - private class ExcludeSourcesContainingExclude : IVersionFilter - { - public bool Exclude(BaseVersion version, out string? reason) - { - reason = null; - - if (!version.Source.Contains("exclude")) - return false; - - reason = "was excluded"; - return true; - } - } - - private sealed class V1Strategy : IVersionStrategy - { - private readonly ICommit? when; - - public V1Strategy(DateTimeOffset? when) - { - if (when != null) - { - this.when = GitToolsTestingExtensions.CreateMockCommit(); - this.when.When.Returns(when.Value); - } - else - { - this.when = null; - } - } - - public IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) - { - yield return new BaseVersion("Source 1", false, new SemanticVersion(1), this.when, null); - } - } - - private sealed class V2Strategy : IVersionStrategy - { - private readonly ICommit? when; - - public V2Strategy(DateTimeOffset? when) - { - if (when != null) - { - this.when = GitToolsTestingExtensions.CreateMockCommit(); - this.when.When.Returns(when.Value); - } - else - { - this.when = null; - } - } - - public IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) - { - yield return new BaseVersion("Source 2", true, new SemanticVersion(2), this.when, null); - } - } - - private sealed class TestVersionStrategy : IVersionStrategy - { - private readonly IEnumerable baseVersions; - - public TestVersionStrategy(params BaseVersion[] baseVersions) => this.baseVersions = baseVersions; - - public IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) => this.baseVersions; - } -} diff --git a/src/GitVersion.Core.Tests/VersionCalculation/EffectiveBranchConfigurationFinderTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/EffectiveBranchConfigurationFinderTests.cs new file mode 100644 index 0000000000..43c9a5409f --- /dev/null +++ b/src/GitVersion.Core.Tests/VersionCalculation/EffectiveBranchConfigurationFinderTests.cs @@ -0,0 +1,368 @@ +using GitVersion.Common; +using GitVersion.Configuration; +using GitVersion.Core.Tests.Helpers; +using GitVersion.Logging; +using GitVersion.Model.Configuration; +using GitVersion.VersionCalculation; +using NSubstitute; +using NUnit.Framework; +using Shouldly; + +namespace GitVersion.Core.Tests.VersionCalculation; + +[TestFixture] +public class EffectiveBranchConfigurationFinderTests +{ + [Theory] + public void When_getting_configurations_of_a_branch_without_versioning_mode_Given_fallback_configuaration_with_versioning_mode_Then_result_should_have_versioning_mode( + VersioningMode versioningMode) + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithVersioningMode(versioningMode).WithoutVersioningMode("main").Build(); + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(branchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(branchMock); + actual[0].Value.VersioningMode.ShouldBe(versioningMode); + } + + [Theory] + public void When_getting_configurations_of_a_branch_with_versioning_mode_Given_fallback_configuaration_without_versioning_mode_Then_result_should_have_versioning_mode( + VersioningMode versioningMode) + { + // Arrange + var mainBranchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var developBranchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithoutVersioningMode().WithVersioningMode("main", versioningMode) + .WithoutVersioningMode("develop").WithIncrement("develop", IncrementStrategy.Inherit).Build(); + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(developBranchMock, configuration, Arg.Any>()).Returns(new[] { mainBranchMock }); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(developBranchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(mainBranchMock); + actual[0].Value.VersioningMode.ShouldBe(versioningMode); + } + + [Theory] + public void When_getting_configurations_of_a_branch_with_versioning_mode_Given_parent_configuaration_with_versioning_mode_Then_result_should_not_have_versioning_mode_of_parent( + VersioningMode versioningMode) + { + // Arrange + var mainBranchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var developBranchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithoutVersioningMode().WithVersioningMode("main", versioningMode) + .WithVersioningMode("develop", VersioningMode.ContinuousDelivery).WithIncrement("develop", IncrementStrategy.Inherit).Build(); + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(developBranchMock, configuration, Arg.Any>()).Returns(new[] { mainBranchMock }); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(developBranchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(mainBranchMock); + if (versioningMode == VersioningMode.ContinuousDelivery) + { + actual[0].Value.VersioningMode.ShouldBe(versioningMode); + } + else + { + actual[0].Value.VersioningMode.ShouldNotBe(versioningMode); + } + } + + [Test] + public void When_getting_configurations_of_a_branch_with_tag_alpha_Given_branch_which_inherits_from_parent_branch_Then_result_should_have_tag_alpha() + { + // Arrange + var mainBranchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var developBranchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement("develop", IncrementStrategy.Inherit) + .WithTag("main", string.Empty).WithTag("develop", "alpha").Build(); + + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(developBranchMock, configuration, Arg.Any>()).Returns(new[] { mainBranchMock }); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(developBranchMock, configuration).ToArray(); + + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(mainBranchMock); + actual[0].Value.Tag.ShouldBe("alpha"); + } + + [Test] + public void When_getting_configurations_of_a_branch_without_tag_Given_branch_which_inherits_from_parent_branch_Then_result_should_have_tag_from_parent() + { + // Arrange + var mainBranchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var developBranchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement("develop", IncrementStrategy.Inherit) + .WithTag("main", string.Empty).WithoutTag("develop").Build(); + + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(developBranchMock, configuration, Arg.Any>()).Returns(new[] { mainBranchMock }); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(developBranchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(mainBranchMock); + actual[0].Value.Tag.ShouldBe(string.Empty); + } + + [TestCase("release/latest", IncrementStrategy.None, "latest")] + [TestCase("release/1.0.0", IncrementStrategy.Patch, "not-latest")] + public void UsesFirstBranchConfigWhenMultipleMatch(string branchName, IncrementStrategy incrementStrategy, string tag) + { + // Arrange + var releaseBranchMock = GitToolsTestingExtensions.CreateMockBranch(branchName, GitToolsTestingExtensions.CreateMockCommit()); + var branchConfig = new BranchConfig + { + VersioningMode = VersioningMode.Mainline, + Increment = IncrementStrategy.None, + PreventIncrementOfMergedBranchVersion = false, + TrackMergeTarget = false, + TracksReleaseBranches = false, + IsReleaseBranch = false, + SourceBranches = new HashSet() + }; + var configuration = new ConfigurationBuilder().Add(new Config + { + VersioningMode = VersioningMode.ContinuousDelivery, + Branches = + { + { "release/latest", new BranchConfig(branchConfig) { Increment = IncrementStrategy.None, Tag = "latest", Regex = "release/latest" } }, + { "release", new BranchConfig(branchConfig) { Increment = IncrementStrategy.Patch, Tag = "not-latest", Regex = "releases?[/-]" } } + } + }).Build(); + + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(releaseBranchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(releaseBranchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(releaseBranchMock); + actual[0].Value.Increment.ShouldBe(incrementStrategy); + actual[0].Value.Tag.ShouldBe(tag); + } + + [Test] + public void When_getting_configurations_of_an_orphaned_branch_Given_fallback_configuaration_without_increment_Then_result_should_be_empty() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement(null).WithIncrement("develop", IncrementStrategy.Inherit).Build(); + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(branchMock, configuration).ToArray(); + + // Assert + actual.ShouldBeEmpty(); + } + + [Test] + public void When_getting_configurations_of_an_orphaned_branch_Given_fallback_configuration_with_increment_inherit_Then_result_should_have_increment_none() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement(IncrementStrategy.Inherit).WithIncrement("develop", IncrementStrategy.Inherit).Build(); + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(branchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(branchMock); + actual[0].Value.Increment.ShouldBe(IncrementStrategy.None); + } + + [TestCase(IncrementStrategy.None)] + [TestCase(IncrementStrategy.Patch)] + [TestCase(IncrementStrategy.Minor)] + [TestCase(IncrementStrategy.Major)] + public void When_getting_configurations_of_an_orphaned_branch_Given_fallback_configuration_with_increment_Then_result_should_have_fallback_increment( + IncrementStrategy fallbackIncrement) + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement(fallbackIncrement).WithIncrement("develop", IncrementStrategy.Inherit).Build(); + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(branchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(branchMock); + actual[0].Value.Increment.ShouldBe(fallbackIncrement); + } + + [Test] + public void When_getting_configurations_of_an_unknown_branch_Given_fallback_configuaration_without_increment_and_unknown_configuration_with_increment_inherit_Then_result_should_be_empty() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("unknown", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement(null).Build(); + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(branchMock, configuration).ToArray(); + + // Assert + actual.ShouldBeEmpty(); + } + + [TestCase(IncrementStrategy.None)] + [TestCase(IncrementStrategy.Patch)] + [TestCase(IncrementStrategy.Minor)] + [TestCase(IncrementStrategy.Major)] + public void When_getting_configurations_of_an_unknown_branch_Given_fallback_configuaration_with_increment_and_unknown_configuration_with_increment_inherit_Then_result_should_have_fallback_increment( + IncrementStrategy fallbackIncrement) + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("unknown", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement(fallbackIncrement).Build(); + var repositoryStoreMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(branchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(branchMock); + actual[0].Value.Increment.ShouldBe(fallbackIncrement); + } + + [Theory] + public void When_getting_configurations_of_an_unknown_branch_Given_fallback_configuaration_with_increment_and_develop_branch_with_increment_Then_result_should_have_develop_increment( + IncrementStrategy fallbackIncrement, IncrementStrategy developBranchIncrement) + { + // Arrange + var unknownBranchMock = GitToolsTestingExtensions.CreateMockBranch("unknown", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement(fallbackIncrement).WithIncrement("develop", developBranchIncrement).Build(); + var repositoryStoreMock = Substitute.For(); + var developBranchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + repositoryStoreMock.GetSourceBranches(unknownBranchMock, configuration, Arg.Any>()).Returns(new[] { developBranchMock }); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(unknownBranchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(developBranchMock); + + if (developBranchIncrement == IncrementStrategy.Inherit) + { + if (fallbackIncrement == IncrementStrategy.Inherit) + { + fallbackIncrement = IncrementStrategy.None; + } + actual[0].Value.Increment.ShouldBe(fallbackIncrement); + } + else + { + actual[0].Value.Increment.ShouldBe(developBranchIncrement); + } + + } + + [Theory] + public void When_getting_configurations_of_an_unknown_branch_Given_fallback_configuaration_with_increment_and_develop_branch_with_increment_inherit_Then_result_should_have_fallback_increment( + IncrementStrategy fallbackIncrement) + { + // Arrange + var unknownBranchMock = GitToolsTestingExtensions.CreateMockBranch("unknown", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement(fallbackIncrement).WithIncrement("develop", IncrementStrategy.Inherit).Build(); + var repositoryStoreMock = Substitute.For(); + var developBranchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + repositoryStoreMock.GetSourceBranches(unknownBranchMock, configuration, Arg.Any>()).Returns(new[] { developBranchMock }); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(unknownBranchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(developBranchMock); + + if (fallbackIncrement == IncrementStrategy.Inherit) + { + fallbackIncrement = IncrementStrategy.None; + } + actual[0].Value.Increment.ShouldBe(fallbackIncrement); + } + + [TestCase(IncrementStrategy.None)] + [TestCase(IncrementStrategy.Patch)] + [TestCase(IncrementStrategy.Minor)] + [TestCase(IncrementStrategy.Major)] + public void When_getting_configurations_of_an_unknown_branch_Given_fallback_and_unknown_configuaration_with_increment_inherit_and_develop_branch_with_increment_Then_result_should_have_develop_branch_increment( + IncrementStrategy developBranchIncrement) + { + // Arrange + var unknownBranchMock = GitToolsTestingExtensions.CreateMockBranch("unknown", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.WithIncrement(IncrementStrategy.Inherit).WithIncrement("develop", developBranchIncrement).Build(); + var repositoryStoreMock = Substitute.For(); + var developBranchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); + repositoryStoreMock.GetSourceBranches(Arg.Any(), Arg.Any(), Arg.Any>()).Returns(new[] { developBranchMock }); + + var unitUnderTest = new EffectiveBranchConfigurationFinder(Substitute.For(), repositoryStoreMock); + + // Act + var actual = unitUnderTest.GetConfigurations(unknownBranchMock, configuration).ToArray(); + + // Assert + actual.ShouldHaveSingleItem(); + actual[0].Branch.ShouldBe(developBranchMock); + actual[0].Value.Increment.ShouldBe(developBranchIncrement); + } +} diff --git a/src/GitVersion.Core.Tests/VersionCalculation/NextVersionCalculatorTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/NextVersionCalculatorTests.cs index 0f6117dfbb..19208599d7 100644 --- a/src/GitVersion.Core.Tests/VersionCalculation/NextVersionCalculatorTests.cs +++ b/src/GitVersion.Core.Tests/VersionCalculation/NextVersionCalculatorTests.cs @@ -1,11 +1,13 @@ using GitTools.Testing; -using GitVersion.Configuration; +using GitVersion.Common; using GitVersion.Core.Tests.Helpers; using GitVersion.Core.Tests.IntegrationTests; +using GitVersion.Logging; using GitVersion.Model.Configuration; using GitVersion.VersionCalculation; using LibGit2Sharp; using Microsoft.Extensions.DependencyInjection; +using NSubstitute; using NUnit.Framework; using Shouldly; @@ -16,24 +18,9 @@ public class NextVersionCalculatorTests : TestBase [Test] public void ShouldIncrementVersionBasedOnConfig() { - var semanticVersionBuildMetaData = new SemanticVersionBuildMetaData("ef7d0d7e1e700f1c7c9fa01ea6791bb778a5c37c", 1, MainBranch, "b1a34edbd80e141f7cc046c074f109be7d022074", "b1a34e", DateTimeOffset.Now, 0); - var contextBuilder = new GitVersionContextBuilder(); - contextBuilder - .OverrideServices(services => - { - var configuration = TestConfigurationBuilder.New.Build(); - var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); - var effectiveConfiguration = new EffectiveConfiguration(configuration, configuration.GetBranchConfiguration("main")); - var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); - var testBaseVersionCalculator = new TestBaseVersionCalculator( - true, new SemanticVersion(1), GitToolsTestingExtensions.CreateMockCommit(), effectiveBranchConfiguration - ); - services.AddSingleton(testBaseVersionCalculator); - services.AddSingleton(new TestMainlineVersionCalculator(semanticVersionBuildMetaData)); - }) - .WithConfig(new Config()) - .Build(); + + contextBuilder.Build(); contextBuilder.ServicesProvider.ShouldNotBeNull(); var nextVersionCalculator = contextBuilder.ServicesProvider.GetRequiredService(); @@ -41,31 +28,15 @@ public void ShouldIncrementVersionBasedOnConfig() var nextVersion = nextVersionCalculator.FindVersion(); - nextVersion.IncrementedVersion.ToString().ShouldBe("1.0.1"); + nextVersion.IncrementedVersion.ToString().ShouldBe("0.0.1"); } [Test] public void DoesNotIncrementWhenBaseVersionSaysNotTo() { - var semanticVersionBuildMetaData = new SemanticVersionBuildMetaData("ef7d0d7e1e700f1c7c9fa01ea6791bb778a5c37c", 1, MainBranch, "b1a34edbd80e141f7cc046c074f109be7d022074", "b1a34e", DateTimeOffset.Now, 0); - var contextBuilder = new GitVersionContextBuilder(); - contextBuilder - .OverrideServices(services => - { - var configuration = TestConfigurationBuilder.New.Build(); - var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); - var effectiveConfiguration = new EffectiveConfiguration(configuration, configuration.GetBranchConfiguration("main")); - var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); - var testBaseVersionCalculator = new TestBaseVersionCalculator( - false, new SemanticVersion(1), GitToolsTestingExtensions.CreateMockCommit(), effectiveBranchConfiguration - ); - services.AddSingleton(testBaseVersionCalculator); - services.AddSingleton(new TestMainlineVersionCalculator(semanticVersionBuildMetaData)); - }) - .WithConfig(new Config()) - .Build(); + contextBuilder.WithConfig(new Config() { NextVersion = "1.0.0" }).Build(); contextBuilder.ServicesProvider.ShouldNotBeNull(); var nextVersionCalculator = contextBuilder.ServicesProvider.GetRequiredService(); @@ -80,24 +51,9 @@ public void DoesNotIncrementWhenBaseVersionSaysNotTo() [Test] public void AppliesBranchPreReleaseTag() { - var semanticVersionBuildMetaData = new SemanticVersionBuildMetaData("ef7d0d7e1e700f1c7c9fa01ea6791bb778a5c37c", 2, "develop", "b1a34edbd80e141f7cc046c074f109be7d022074", "b1a34e", DateTimeOffset.Now, 0); var contextBuilder = new GitVersionContextBuilder(); - contextBuilder - .OverrideServices(services => - { - var configuration = TestConfigurationBuilder.New.Build(); - var branchMock = GitToolsTestingExtensions.CreateMockBranch("develop", GitToolsTestingExtensions.CreateMockCommit()); - var effectiveConfiguration = new EffectiveConfiguration(configuration, configuration.GetBranchConfiguration("develop")); - var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); - var testBaseVersionCalculator = new TestBaseVersionCalculator( - false, new SemanticVersion(1), GitToolsTestingExtensions.CreateMockCommit(), effectiveBranchConfiguration - ); - services.AddSingleton(testBaseVersionCalculator); - services.AddSingleton(new TestMainlineVersionCalculator(semanticVersionBuildMetaData)); - }) - .WithDevelopBranch() - .Build(); + contextBuilder.WithDevelopBranch().Build(); contextBuilder.ServicesProvider.ShouldNotBeNull(); var nextVersionCalculator = contextBuilder.ServicesProvider.GetRequiredService(); @@ -105,7 +61,7 @@ public void AppliesBranchPreReleaseTag() var nextVersion = nextVersionCalculator.FindVersion(); - nextVersion.IncrementedVersion.ToString("f").ShouldBe("1.0.0-alpha.1+2"); + nextVersion.IncrementedVersion.ToString("f").ShouldBe("0.1.0-alpha.1+0"); } [Test] @@ -134,7 +90,7 @@ public void PreReleaseTagCanUseBranchName() fixture.BranchTo("custom/foo"); fixture.MakeACommit(); - fixture.AssertFullSemver("1.0.0-foo.1+2", config); + fixture.AssertFullSemver("1.0.0-foo.1+3", config); } [Test] @@ -290,7 +246,7 @@ public void PreReleaseTagCanUseBranchNameVariable() fixture.BranchTo("custom/foo"); fixture.MakeACommit(); - fixture.AssertFullSemver("1.0.0-alpha.foo.1+2", config); + fixture.AssertFullSemver("1.0.0-alpha.foo.1+3", config); } [Test] @@ -318,12 +274,12 @@ public void PreReleaseNumberShouldBeScopeToPreReleaseLabelInContinuousDelivery() fixture.Repository.MakeATaggedCommit("0.1.0-test.1"); fixture.Repository.MakeACommit(); - fixture.AssertFullSemver("0.1.0-test.2+2", config); + fixture.AssertFullSemver("0.1.0-test.2+1", config); Commands.Checkout(fixture.Repository, MainBranch); fixture.Repository.Merge("feature/test", Generate.SignatureNow()); - fixture.AssertFullSemver("0.1.0-beta.1+2", config); + fixture.AssertFullSemver("0.1.0-beta.1+1", config); // just one commit no fast forward merge here. } [Test] @@ -340,4 +296,280 @@ public void GetNextVersionOnNonMainlineBranchWithoutCommitsShouldWorkNormally() fixture.BranchTo("feature/f1"); fixture.AssertFullSemver("1.0.0-f1.0", config); } + + [Test] + public void ChoosesHighestVersionReturnedFromStrategies() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.Build(); + var context = new GitVersionContext(branchMock, null, configuration, null, 0); + var repositoryStoreMock = Substitute.For(); + var effectiveConfiguration = context.GetEffectiveConfiguration(branchMock); + var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); + var effectiveBranchConfigurationFinderMock = Substitute.For(); + effectiveBranchConfigurationFinderMock.GetConfigurations(branchMock, configuration).Returns(new[] { effectiveBranchConfiguration }); + var incrementStrategyFinderMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + var dateTimeOffset = DateTimeOffset.Now; + var versionStrategies = new IVersionStrategy[] { new V1Strategy(DateTimeOffset.Now), new V2Strategy(dateTimeOffset) }; + var unitUnderTest = new NextVersionCalculator(Substitute.For(), Substitute.For(), + repositoryStoreMock, new(context), versionStrategies, effectiveBranchConfigurationFinderMock, incrementStrategyFinderMock); + + // Act + var nextVersion = unitUnderTest.FindVersion(); + + // Assert + nextVersion.BaseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); + nextVersion.BaseVersion.ShouldIncrement.ShouldBe(true); + nextVersion.BaseVersion.BaseVersionSource.ShouldNotBeNull(); + nextVersion.BaseVersion.BaseVersionSource.When.ShouldBe(dateTimeOffset); + } + + [Test] + public void UsesWhenFromNextBestMatchIfHighestDoesntHaveWhen() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.Build(); + var context = new GitVersionContext(branchMock, null, configuration, null, 0); + var repositoryStoreMock = Substitute.For(); + var effectiveConfiguration = context.GetEffectiveConfiguration(branchMock); + var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); + var effectiveBranchConfigurationFinderMock = Substitute.For(); + effectiveBranchConfigurationFinderMock.GetConfigurations(branchMock, configuration).Returns(new[] { effectiveBranchConfiguration }); + var incrementStrategyFinderMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + var when = DateTimeOffset.Now; + var versionStrategies = new IVersionStrategy[] { new V1Strategy(when), new V2Strategy(null) }; + var unitUnderTest = new NextVersionCalculator(Substitute.For(), Substitute.For(), + repositoryStoreMock, new(context), versionStrategies, effectiveBranchConfigurationFinderMock, incrementStrategyFinderMock); + + // Act + var nextVersion = unitUnderTest.FindVersion(); + + // Assert + nextVersion.BaseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); + nextVersion.BaseVersion.ShouldIncrement.ShouldBe(true); + nextVersion.BaseVersion.BaseVersionSource.ShouldNotBeNull(); + nextVersion.BaseVersion.BaseVersionSource.When.ShouldBe(when); + } + + [Test] + public void UsesWhenFromNextBestMatchIfHighestDoesntHaveWhenReversedOrder() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var configuration = TestConfigurationBuilder.New.Build(); + var context = new GitVersionContext(branchMock, null, configuration, null, 0); + var repositoryStoreMock = Substitute.For(); + var effectiveConfiguration = context.GetEffectiveConfiguration(branchMock); + var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); + var effectiveBranchConfigurationFinderMock = Substitute.For(); + effectiveBranchConfigurationFinderMock.GetConfigurations(branchMock, configuration).Returns(new[] { effectiveBranchConfiguration }); + var incrementStrategyFinderMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + var when = DateTimeOffset.Now; + var versionStrategies = new IVersionStrategy[] { new V2Strategy(null), new V1Strategy(when) }; + var unitUnderTest = new NextVersionCalculator(Substitute.For(), Substitute.For(), + repositoryStoreMock, new(context), versionStrategies, effectiveBranchConfigurationFinderMock, incrementStrategyFinderMock); + + // Act + var nextVersion = unitUnderTest.FindVersion(); + + // Assert + nextVersion.BaseVersion.SemanticVersion.ToString().ShouldBe("2.0.0"); + nextVersion.BaseVersion.ShouldIncrement.ShouldBe(true); + nextVersion.BaseVersion.BaseVersionSource.ShouldNotBeNull(); + nextVersion.BaseVersion.BaseVersionSource.When.ShouldBe(when); + } + + [Test] + public void ShouldNotFilterVersion() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var fakeIgnoreConfig = new TestIgnoreConfig(new ExcludeSourcesContainingExclude()); + var configuration = TestConfigurationBuilder.New.WithIgnoreConfig(fakeIgnoreConfig).Build(); + var context = new GitVersionContext(branchMock, null, configuration, null, 0); + var repositoryStoreMock = Substitute.For(); + var effectiveConfiguration = context.GetEffectiveConfiguration(branchMock); + var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); + var effectiveBranchConfigurationFinderMock = Substitute.For(); + effectiveBranchConfigurationFinderMock.GetConfigurations(branchMock, configuration).Returns(new[] { effectiveBranchConfiguration }); + var incrementStrategyFinderMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + var version = new BaseVersion("dummy", false, new SemanticVersion(2), GitToolsTestingExtensions.CreateMockCommit(), null); + var versionStrategies = new IVersionStrategy[] { new TestVersionStrategy(version) }; + var unitUnderTest = new NextVersionCalculator(Substitute.For(), Substitute.For(), + repositoryStoreMock, new(context), versionStrategies, effectiveBranchConfigurationFinderMock, incrementStrategyFinderMock); + + // Act + var nextVersion = unitUnderTest.FindVersion(); + + // Assert + nextVersion.BaseVersion.Source.ShouldBe(version.Source); + nextVersion.BaseVersion.ShouldIncrement.ShouldBe(version.ShouldIncrement); + nextVersion.BaseVersion.SemanticVersion.ShouldBe(version.SemanticVersion); + } + + [Test] + public void ShouldFilterVersion() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var fakeIgnoreConfig = new TestIgnoreConfig(new ExcludeSourcesContainingExclude()); + var configuration = TestConfigurationBuilder.New.WithIgnoreConfig(fakeIgnoreConfig).Build(); + var context = new GitVersionContext(branchMock, null, configuration, null, 0); + var repositoryStoreMock = Substitute.For(); + var effectiveConfiguration = context.GetEffectiveConfiguration(branchMock); + var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); + var effectiveBranchConfigurationFinderMock = Substitute.For(); + effectiveBranchConfigurationFinderMock.GetConfigurations(branchMock, configuration).Returns(new[] { effectiveBranchConfiguration }); + var incrementStrategyFinderMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + var higherVersion = new BaseVersion("exclude", false, new SemanticVersion(2), GitToolsTestingExtensions.CreateMockCommit(), null); + var lowerVersion = new BaseVersion("dummy", false, new SemanticVersion(1), GitToolsTestingExtensions.CreateMockCommit(), null); + var versionStrategies = new IVersionStrategy[] { new TestVersionStrategy(higherVersion, lowerVersion) }; + var unitUnderTest = new NextVersionCalculator(Substitute.For(), Substitute.For(), + repositoryStoreMock, new(context), versionStrategies, effectiveBranchConfigurationFinderMock, incrementStrategyFinderMock); + + // Act + var nextVersion = unitUnderTest.FindVersion(); + + // Assert + nextVersion.BaseVersion.Source.ShouldNotBe(higherVersion.Source); + nextVersion.BaseVersion.SemanticVersion.ShouldNotBe(higherVersion.SemanticVersion); + nextVersion.BaseVersion.Source.ShouldBe(lowerVersion.Source); + nextVersion.BaseVersion.SemanticVersion.ShouldBe(lowerVersion.SemanticVersion); + } + + [Test] + public void ShouldIgnorePreReleaseVersionInMainlineMode() + { + // Arrange + var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); + var fakeIgnoreConfig = new TestIgnoreConfig(new ExcludeSourcesContainingExclude()); + var configuration = TestConfigurationBuilder.New.WithIgnoreConfig(fakeIgnoreConfig).WithVersioningMode(VersioningMode.Mainline).Build(); + var context = new GitVersionContext(branchMock, null, configuration, null, 0); + var repositoryStoreMock = Substitute.For(); + var effectiveConfiguration = context.GetEffectiveConfiguration(branchMock); + var effectiveBranchConfiguration = new EffectiveBranchConfiguration(branchMock, effectiveConfiguration); + var effectiveBranchConfigurationFinderMock = Substitute.For(); + effectiveBranchConfigurationFinderMock.GetConfigurations(branchMock, configuration).Returns(new[] { effectiveBranchConfiguration }); + var incrementStrategyFinderMock = Substitute.For(); + repositoryStoreMock.GetSourceBranches(branchMock, configuration, Arg.Any>()).Returns(Enumerable.Empty()); + var lowerVersion = new BaseVersion("dummy", false, new SemanticVersion(1), GitToolsTestingExtensions.CreateMockCommit(), null); + var preReleaseVersion = new BaseVersion( + "prerelease", + false, + new SemanticVersion(1, 0, 1) + { + PreReleaseTag = new SemanticVersionPreReleaseTag + { + Name = "alpha", + Number = 1 + } + }, + GitToolsTestingExtensions.CreateMockCommit(), + null + ); + var mainlineVersionCalculatorMock = Substitute.For(); + mainlineVersionCalculatorMock.FindMainlineModeVersion(Arg.Any()).Returns(lowerVersion.SemanticVersion); + var versionStrategies = new IVersionStrategy[] { new TestVersionStrategy(preReleaseVersion, lowerVersion) }; + var unitUnderTest = new NextVersionCalculator(Substitute.For(), mainlineVersionCalculatorMock, + repositoryStoreMock, new(context), versionStrategies, effectiveBranchConfigurationFinderMock, incrementStrategyFinderMock); + + // Act + var nextVersion = unitUnderTest.FindVersion(); + + // Assert + nextVersion.BaseVersion.Source.ShouldNotBe(preReleaseVersion.Source); + nextVersion.BaseVersion.SemanticVersion.ShouldNotBe(preReleaseVersion.SemanticVersion); + nextVersion.BaseVersion.Source.ShouldBe(lowerVersion.Source); + nextVersion.BaseVersion.SemanticVersion.ShouldBe(lowerVersion.SemanticVersion); + } + + private class TestIgnoreConfig : IgnoreConfig + { + private readonly IVersionFilter filter; + + public override bool IsEmpty => false; + + public TestIgnoreConfig(IVersionFilter filter) => this.filter = filter; + + public override IEnumerable ToFilters() + { + yield return this.filter; + } + } + + private class ExcludeSourcesContainingExclude : IVersionFilter + { + public bool Exclude(BaseVersion version, out string? reason) + { + reason = null; + + if (!version.Source.Contains("exclude")) + return false; + + reason = "was excluded"; + return true; + } + } + + private sealed class V1Strategy : IVersionStrategy + { + private readonly ICommit? when; + + public V1Strategy(DateTimeOffset? when) + { + if (when != null) + { + this.when = GitToolsTestingExtensions.CreateMockCommit(); + this.when.When.Returns(when.Value); + } + else + { + this.when = null; + } + } + + public IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) + { + yield return new BaseVersion("Source 1", false, new SemanticVersion(1), this.when, null); + } + } + + private sealed class V2Strategy : IVersionStrategy + { + private readonly ICommit? when; + + public V2Strategy(DateTimeOffset? when) + { + if (when != null) + { + this.when = GitToolsTestingExtensions.CreateMockCommit(); + this.when.When.Returns(when.Value); + } + else + { + this.when = null; + } + } + + public IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) + { + yield return new BaseVersion("Source 2", true, new SemanticVersion(2), this.when, null); + } + } + + private sealed class TestVersionStrategy : IVersionStrategy + { + private readonly IEnumerable baseVersions; + + public TestVersionStrategy(params BaseVersion[] baseVersions) => this.baseVersions = baseVersions; + + public IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) => this.baseVersions; + } } diff --git a/src/GitVersion.Core.Tests/VersionCalculation/Strategies/ConfigNextVersionBaseVersionStrategyTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/Strategies/ConfigNextVersionBaseVersionStrategyTests.cs index 1c3979751f..4ad762837b 100644 --- a/src/GitVersion.Core.Tests/VersionCalculation/Strategies/ConfigNextVersionBaseVersionStrategyTests.cs +++ b/src/GitVersion.Core.Tests/VersionCalculation/Strategies/ConfigNextVersionBaseVersionStrategyTests.cs @@ -48,8 +48,8 @@ public void ConfigNextVersionTest(string nextVersion, string expectedVersion) var strategy = contextBuilder.ServicesProvider.GetServiceForType(); var context = contextBuilder.ServicesProvider.GetRequiredService>().Value; var branchMock = GitToolsTestingExtensions.CreateMockBranch("main", GitToolsTestingExtensions.CreateMockCommit()); - var branchConfiguration = context.FullConfiguration.GetBranchConfiguration(branchMock); - var effectiveConfiguration = new EffectiveConfiguration(context.FullConfiguration, branchConfiguration); + var branchConfiguration = context.Configuration.GetBranchConfiguration(branchMock); + var effectiveConfiguration = new EffectiveConfiguration(context.Configuration, branchConfiguration); return strategy.GetBaseVersions(new(branchMock, effectiveConfiguration)).SingleOrDefault(); } } diff --git a/src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs index 3ecb7680df..95c545cf78 100644 --- a/src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs +++ b/src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs @@ -39,8 +39,8 @@ public void ShouldNotAllowIncrementOfVersion() contextBuilder.ServicesProvider.ShouldNotBeNull(); var strategy = contextBuilder.ServicesProvider.GetServiceForType(); var context = contextBuilder.ServicesProvider.GetRequiredService>().Value; - var branchConfiguration = context.FullConfiguration.GetBranchConfiguration(mockBranch); - var effectiveConfiguration = new EffectiveConfiguration(context.FullConfiguration, branchConfiguration); + var branchConfiguration = context.Configuration.GetBranchConfiguration(mockBranch); + var effectiveConfiguration = new EffectiveConfiguration(context.Configuration, branchConfiguration); var baseVersion = strategy.GetBaseVersions(new(mockBranch, effectiveConfiguration)).Single(); baseVersion.ShouldIncrement.ShouldBe(false); @@ -123,11 +123,9 @@ public void ShouldNotTakeVersionFromMergeOfReleaseBranchWithRemoteOtherThanOrigi } [TestCase(@"Merge pull request #1 in FOO/bar from feature/ISSUE-1 to develop - * commit '38560a7eed06e8d3f3f1aaf091befcdf8bf50fea': Updated jQuery to v2.1.3")] [TestCase(@"Merge pull request #45 in BRIKKS/brikks from feature/NOX-68 to develop - * commit '38560a7eed06e8d3f3f1aaf091befcdf8bf50fea': Another commit message Commit message including a IP-number https://10.50.1.1 @@ -169,14 +167,13 @@ private static void AssertMergeMessage(string message, string? expectedVersion, mockRepository.Commits.Returns(mockBranch.Commits); var contextBuilder = new GitVersionContextBuilder() - .WithConfig(config ?? new Config()) - .WithRepository(mockRepository); + .WithConfig(config ?? new Config()).WithRepository(mockRepository); contextBuilder.Build(); contextBuilder.ServicesProvider.ShouldNotBeNull(); var strategy = contextBuilder.ServicesProvider.GetServiceForType(); var context = contextBuilder.ServicesProvider.GetRequiredService>().Value; - var branchConfiguration = context.FullConfiguration.GetBranchConfiguration(mockBranch); - var effectiveConfiguration = new EffectiveConfiguration(context.FullConfiguration, branchConfiguration); + var branchConfiguration = context.Configuration.GetBranchConfiguration(mockBranch); + var effectiveConfiguration = new EffectiveConfiguration(context.Configuration, branchConfiguration); var baseVersion = strategy.GetBaseVersions(new(mockBranch, effectiveConfiguration)).SingleOrDefault(); if (expectedVersion == null) @@ -192,7 +189,7 @@ private static void AssertMergeMessage(string message, string? expectedVersion, private static List GetParents(bool isMergeCommit) => isMergeCommit - ? new List { new MockCommit(), new MockCommit(), } + ? new List { new MockCommit(), new MockCommit() } : new List { new MockCommit(), }; private class MockCommit : ICommit diff --git a/src/GitVersion.Core.Tests/VersionCalculation/TestBaseVersionCalculator.cs b/src/GitVersion.Core.Tests/VersionCalculation/TestBaseVersionCalculator.cs deleted file mode 100644 index 467f899b8d..0000000000 --- a/src/GitVersion.Core.Tests/VersionCalculation/TestBaseVersionCalculator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using GitVersion.Model.Configuration; -using GitVersion.VersionCalculation; - -namespace GitVersion.Core.Tests.VersionCalculation; - -public class TestBaseVersionCalculator : IBaseVersionCalculator -{ - private readonly SemanticVersion semanticVersion; - private readonly bool shouldIncrement; - private readonly ICommit source; - private readonly EffectiveBranchConfiguration configuration; - - public TestBaseVersionCalculator(bool shouldIncrement, SemanticVersion semanticVersion, ICommit source, EffectiveBranchConfiguration configuration) - { - this.semanticVersion = semanticVersion; - this.source = source; - this.shouldIncrement = shouldIncrement; - this.configuration = configuration; - } - - public (BaseVersion, EffectiveBranchConfiguration) GetBaseVersion() => new(new("Test source", this.shouldIncrement, this.semanticVersion, this.source, null), this.configuration); -} diff --git a/src/GitVersion.Core.Tests/VersionCalculation/VersionSourceTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/VersionSourceTests.cs index b48ad5f5c5..0df146f6d5 100644 --- a/src/GitVersion.Core.Tests/VersionCalculation/VersionSourceTests.cs +++ b/src/GitVersion.Core.Tests/VersionCalculation/VersionSourceTests.cs @@ -1,5 +1,6 @@ using GitTools.Testing; using GitVersion.Core.Tests.Helpers; +using GitVersion.Core.Tests.IntegrationTests; using GitVersion.VersionCalculation; using LibGit2Sharp; using Microsoft.Extensions.DependencyInjection; @@ -27,8 +28,8 @@ public void VersionSourceSha() var nextVersion = nextVersionCalculator.FindVersion(); nextVersion.IncrementedVersion.BuildMetaData.ShouldNotBeNull(); - nextVersion.IncrementedVersion.BuildMetaData.VersionSourceSha.ShouldBe(initialCommit.Sha); - nextVersion.IncrementedVersion.BuildMetaData.CommitsSinceVersionSource.ShouldBe(2); + nextVersion.IncrementedVersion.BuildMetaData.VersionSourceSha.ShouldBeNull(); + nextVersion.IncrementedVersion.BuildMetaData.CommitsSinceVersionSource.ShouldBe(3); } [Test] @@ -42,8 +43,8 @@ public void VersionSourceShaOneCommit() var nextVersion = nextVersionCalculator.FindVersion(); nextVersion.IncrementedVersion.BuildMetaData.ShouldNotBeNull(); - nextVersion.IncrementedVersion.BuildMetaData.VersionSourceSha.ShouldBe(initialCommit.Sha); - nextVersion.IncrementedVersion.BuildMetaData.CommitsSinceVersionSource.ShouldBe(0); + nextVersion.IncrementedVersion.BuildMetaData.VersionSourceSha.ShouldBeNull(); + nextVersion.IncrementedVersion.BuildMetaData.CommitsSinceVersionSource.ShouldBe(1); } [Test] diff --git a/src/GitVersion.Core/Configuration/Abstractions/IBranchConfigurationCalculator.cs b/src/GitVersion.Core/Configuration/Abstractions/IBranchConfigurationCalculator.cs deleted file mode 100644 index ed49a4e085..0000000000 --- a/src/GitVersion.Core/Configuration/Abstractions/IBranchConfigurationCalculator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using GitVersion.Model.Configuration; - -namespace GitVersion.Configuration; - -public interface IBranchConfigurationCalculator -{ - /// - /// Gets the for the current commit. - /// - BranchConfig GetBranchConfiguration(IBranch targetBranch, ICommit? currentCommit, Config configuration, IList? excludedInheritBranches = null); -} diff --git a/src/GitVersion.Core/Configuration/BranchConfigurationCalculator.cs b/src/GitVersion.Core/Configuration/BranchConfigurationCalculator.cs deleted file mode 100644 index dc33672dd5..0000000000 --- a/src/GitVersion.Core/Configuration/BranchConfigurationCalculator.cs +++ /dev/null @@ -1,275 +0,0 @@ -using System.Text.RegularExpressions; -using GitVersion.Common; -using GitVersion.Extensions; -using GitVersion.Logging; -using GitVersion.Model.Configuration; -using GitVersion.Model.Exceptions; - -namespace GitVersion.Configuration; - -public class BranchConfigurationCalculator : IBranchConfigurationCalculator -{ - private const string FallbackConfigName = "Fallback"; - private const int MaxRecursions = 50; - - private readonly ILog log; - private readonly IRepositoryStore repositoryStore; - - public BranchConfigurationCalculator(ILog log, IRepositoryStore repositoryStore) - { - this.log = log.NotNull(); - this.repositoryStore = repositoryStore.NotNull(); - } - - /// - /// Gets the for the current commit. - /// - public BranchConfig GetBranchConfiguration(IBranch targetBranch, ICommit? currentCommit, Config configuration, IList? excludedInheritBranches = null) => - GetBranchConfigurationInternal(0, targetBranch, currentCommit, configuration, excludedInheritBranches); - - private BranchConfig GetBranchConfigurationInternal(int recursions, IBranch targetBranch, ICommit? currentCommit, Config configuration, IList? excludedInheritBranches = null) - { - if (recursions >= MaxRecursions) - { - throw new InfiniteLoopProtectionException($"Inherited branch configuration caused {recursions} recursions. Aborting!"); - } - - var matchingBranches = configuration.ForBranch(targetBranch); - - if (matchingBranches == null) - { - this.log.Info($"No branch configuration found for branch {targetBranch}, falling back to default configuration"); - - matchingBranches = BranchConfig.CreateDefaultBranchConfig(FallbackConfigName) - .Apply(new BranchConfig - { - Regex = "", - VersioningMode = configuration.VersioningMode, - Increment = configuration.Increment ?? IncrementStrategy.Inherit - }); - } - - if (matchingBranches.Increment == IncrementStrategy.Inherit) - { - matchingBranches = InheritBranchConfiguration(recursions, targetBranch, matchingBranches, currentCommit, configuration, excludedInheritBranches); - if (matchingBranches.Name.IsEquivalentTo(FallbackConfigName) && matchingBranches.Increment == IncrementStrategy.Inherit) - { - // We tried, and failed to inherit, just fall back to patch - matchingBranches.Increment = IncrementStrategy.Patch; - } - } - - return matchingBranches; - - } - - // TODO I think we need to take a fresh approach to this.. it's getting really complex with heaps of edge cases - private BranchConfig InheritBranchConfiguration(int recursions, IBranch targetBranch, BranchConfig branchConfiguration, ICommit? currentCommit, Config configuration, IList? excludedInheritBranches) - { - using (this.log.IndentLog("Attempting to inherit branch configuration from parent branch")) - { - recursions += 1; - - var excludedBranches = new[] { targetBranch }; - // Check if we are a merge commit. If so likely we are a pull request - if (currentCommit != null) - { - var parentCount = currentCommit.Parents.Count(); - if (parentCount == 2) - { - excludedBranches = CalculateWhenMultipleParents(currentCommit, ref targetBranch, excludedBranches); - } - } - - excludedInheritBranches ??= this.repositoryStore.GetExcludedInheritBranches(configuration).ToList(); - - excludedBranches = excludedBranches.Where(b => excludedInheritBranches.All(bte => !b.Equals(bte))).ToArray(); - // Add new excluded branches. - foreach (var excludedBranch in excludedBranches) - { - excludedInheritBranches.Add(excludedBranch); - } - var branchesToEvaluate = this.repositoryStore.ExcludingBranches(excludedInheritBranches) - .Distinct(new LocalRemoteBranchEqualityComparer()) - .ToList(); - - var branchPoint = this.repositoryStore - .FindCommitBranchWasBranchedFrom(targetBranch, configuration, excludedInheritBranches.ToArray()); - List possibleParents; - if (branchPoint == BranchCommit.Empty) - { - possibleParents = this.repositoryStore.GetBranchesContainingCommit(targetBranch.Tip, branchesToEvaluate) - // It fails to inherit Increment branch configuration if more than 1 parent; - // therefore no point to get more than 2 parents - .Take(2) - .ToList(); - } - else - { - var branches = this.repositoryStore.GetBranchesContainingCommit(branchPoint.Commit, branchesToEvaluate).ToList(); - if (branches.Count > 1) - { - var currentTipBranches = this.repositoryStore.GetBranchesContainingCommit(currentCommit, branchesToEvaluate).ToList(); - possibleParents = branches.Except(currentTipBranches).ToList(); - } - else - { - possibleParents = branches; - } - } - - this.log.Info("Found possible parent branches: " + string.Join(", ", possibleParents.Select(p => p.ToString()))); - - if (possibleParents.Count == 1) - { - var branchConfig = GetBranchConfigurationInternal(recursions, possibleParents[0], currentCommit, configuration, excludedInheritBranches); - // If we have resolved a fallback config we should not return that we have got config - if (branchConfig.Name != FallbackConfigName) - { - return new BranchConfig(branchConfiguration) - { - Increment = branchConfig.Increment, - PreventIncrementOfMergedBranchVersion = branchConfig.PreventIncrementOfMergedBranchVersion, - // If we are inheriting from develop then we should behave like develop - TracksReleaseBranches = branchConfig.TracksReleaseBranches - }; - } - } - - // If we fail to inherit it is probably because the branch has been merged and we can't do much. So we will fall back to develop's config - // if develop exists and main if not - var errorMessage = possibleParents.Count == 0 - ? "Failed to inherit Increment branch configuration, no branches found." - : "Failed to inherit Increment branch configuration, ended up with: " + string.Join(", ", possibleParents.Select(p => p.ToString())); - - var chosenBranch = this.repositoryStore.GetChosenBranch(configuration); - if (chosenBranch == null) - { - // TODO We should call the build server to generate this exception, each build server works differently - // for fetch issues and we could give better warnings. - throw new InvalidOperationException("Gitversion could not determine which branch to treat as the development branch (default is 'develop') nor release-able branch (default is 'main' or 'master'), either locally or remotely. Ensure the local clone and checkout match the requirements or considering using 'GitVersion Dynamic Repositories'"); - } - - this.log.Warning($"{errorMessage}{System.Environment.NewLine}Falling back to {chosenBranch} branch config"); - - // To prevent infinite loops, make sure that a new branch was chosen. - if (targetBranch.Equals(chosenBranch)) - { - var developOrMainConfig = - ChooseMainOrDevelopIncrementStrategyIfTheChosenBranchIsOneOfThem( - chosenBranch, branchConfiguration, configuration); - if (developOrMainConfig != null) - { - return developOrMainConfig; - } - - this.log.Warning("Fallback branch wants to inherit Increment branch configuration from itself. Using patch increment instead."); - return new BranchConfig(branchConfiguration) - { - Increment = IncrementStrategy.Patch - }; - } - - var inheritingBranchConfig = GetBranchConfigurationInternal(recursions, chosenBranch, currentCommit, configuration, excludedInheritBranches); - var configIncrement = inheritingBranchConfig.Increment; - if (inheritingBranchConfig.Name.IsEquivalentTo(FallbackConfigName) && configIncrement == IncrementStrategy.Inherit) - { - this.log.Warning("Fallback config inherits by default, dropping to patch increment"); - configIncrement = IncrementStrategy.Patch; - } - - return new BranchConfig(branchConfiguration) - { - Increment = configIncrement, - PreventIncrementOfMergedBranchVersion = inheritingBranchConfig.PreventIncrementOfMergedBranchVersion, - // If we are inheriting from develop then we should behave like develop - TracksReleaseBranches = inheritingBranchConfig.TracksReleaseBranches - }; - } - } - - private IBranch[] CalculateWhenMultipleParents(ICommit currentCommit, ref IBranch currentBranch, IBranch[] excludedBranches) - { - var parents = currentCommit.Parents.ToArray(); - var branches = this.repositoryStore.GetBranchesForCommit(parents[1]).ToList(); - if (branches.Count == 1) - { - var branch = branches[0]; - excludedBranches = new[] - { - currentBranch, - branch - }; - currentBranch = branch; - } - else if (branches.Count > 1) - { - currentBranch = branches.FirstOrDefault(b => b.Name.WithoutRemote == Config.MainBranchKey) ?? branches.First(); - } - else - { - var possibleTargetBranches = this.repositoryStore.GetBranchesForCommit(parents[0]).ToList(); - if (possibleTargetBranches.Count > 1) - { - currentBranch = possibleTargetBranches.FirstOrDefault(b => b.Name.WithoutRemote == Config.MainBranchKey) ?? possibleTargetBranches.First(); - } - else - { - currentBranch = possibleTargetBranches.FirstOrDefault() ?? currentBranch; - } - } - - this.log.Info($"HEAD is merge commit, this is likely a pull request using {currentBranch} as base"); - - return excludedBranches; - } - - - - private static BranchConfig? ChooseMainOrDevelopIncrementStrategyIfTheChosenBranchIsOneOfThem(IBranch chosenBranch, BranchConfig branchConfiguration, Config config) - { - BranchConfig? mainOrDevelopConfig = null; - var developBranchRegex = config.Branches[Config.DevelopBranchKey]?.Regex ?? Config.DevelopBranchRegex; - var mainBranchRegex = config.Branches[Config.MainBranchKey]?.Regex ?? Config.MainBranchRegex; - if (Regex.IsMatch(chosenBranch.Name.Friendly, developBranchRegex, RegexOptions.IgnoreCase)) - { - // Normally we would not expect this to happen but for safety we add a check - if (config.Branches[Config.DevelopBranchKey]?.Increment != - IncrementStrategy.Inherit) - { - mainOrDevelopConfig = new BranchConfig(branchConfiguration) - { - Increment = config.Branches[Config.DevelopBranchKey]?.Increment - }; - } - } - else if (Regex.IsMatch(chosenBranch.Name.Friendly, mainBranchRegex, RegexOptions.IgnoreCase)) - { - // Normally we would not expect this to happen but for safety we add a check - if (config.Branches[Config.MainBranchKey]?.Increment != - IncrementStrategy.Inherit) - { - mainOrDevelopConfig = new BranchConfig(branchConfiguration) - { - Increment = config.Branches[Config.DevelopBranchKey]?.Increment - }; - } - } - return mainOrDevelopConfig; - } - - private class LocalRemoteBranchEqualityComparer : IEqualityComparer - { - public bool Equals(IBranch? b1, IBranch? b2) - { - if (b1 == null && b2 == null) - return true; - if (b1 == null || b2 == null) - return false; - - return b1.Name.WithoutRemote.Equals(b2.Name.WithoutRemote); - } - - public int GetHashCode(IBranch b) => b.Name.WithoutRemote.GetHashCode(); - } -} diff --git a/src/GitVersion.Core/Configuration/ConfigExtensions.cs b/src/GitVersion.Core/Configuration/ConfigExtensions.cs index 3edf74d0a3..95e50bc0d4 100644 --- a/src/GitVersion.Core/Configuration/ConfigExtensions.cs +++ b/src/GitVersion.Core/Configuration/ConfigExtensions.cs @@ -54,10 +54,7 @@ public static BranchConfig GetFallbackBranchConfiguration(this Config configurat return result; } - internal static BranchConfig? ForBranch(this Config configuration, IBranch branch) - => ForBranch(configuration, branch.NotNull().Name.WithoutRemote); - - internal static BranchConfig? ForBranch(this Config configuration, string branchName) + private static BranchConfig? ForBranch(Config configuration, string branchName) { var matches = configuration.Branches .Where(b => b.Value?.Regex != null && Regex.IsMatch(branchName, b.Value.Regex, RegexOptions.IgnoreCase)) @@ -89,7 +86,7 @@ public static BranchConfig GetFallbackBranchConfiguration(this Config configurat public static string GetBranchSpecificTag(this EffectiveConfiguration configuration, ILog log, string? branchFriendlyName, string? branchNameOverride) { - var tagToUse = configuration.Tag ?? "{BranchName}"; + var tagToUse = configuration.Tag; if (tagToUse == "useBranchName") { tagToUse = "{BranchName}"; diff --git a/src/GitVersion.Core/Configuration/ConfigurationBuilder.cs b/src/GitVersion.Core/Configuration/ConfigurationBuilder.cs index ff106375d9..044e97829f 100644 --- a/src/GitVersion.Core/Configuration/ConfigurationBuilder.cs +++ b/src/GitVersion.Core/Configuration/ConfigurationBuilder.cs @@ -10,11 +10,11 @@ public class ConfigurationBuilder private readonly List overrides = new(); - public ConfigurationBuilder Add(Config config) + public ConfigurationBuilder Add(Config configuration) { - if (config == null) throw new ArgumentNullException(nameof(config)); + if (configuration == null) throw new ArgumentNullException(nameof(configuration)); - this.overrides.Add(config); + this.overrides.Add(configuration); return this; } @@ -127,6 +127,7 @@ private static void FinalizeBranchConfiguration(Config config, string name, Bran { if (name == Config.DevelopBranchKey) { + // Why this applies only on develop branch? I'm surprised that the value not coming from configuration. branchConfig.VersioningMode = config.VersioningMode == VersioningMode.Mainline ? VersioningMode.Mainline : VersioningMode.ContinuousDeployment; } else @@ -282,7 +283,7 @@ private static Config CreateDefaultConfiguration() AddBranchConfig(Config.HotfixBranchKey, new BranchConfig { - Increment = IncrementStrategy.Patch, + Increment = IncrementStrategy.Inherit, Regex = Config.HotfixBranchRegex, SourceBranches = new HashSet { Config.ReleaseBranchKey, @@ -291,11 +292,6 @@ private static Config CreateDefaultConfiguration() Config.HotfixBranchKey }, Tag = "beta", - PreventIncrementOfMergedBranchVersion = false, - TrackMergeTarget = false, - TracksReleaseBranches = false, - IsMainline = false, - IsReleaseBranch = false, PreReleaseWeight = 30000 }); @@ -316,10 +312,10 @@ private static Config CreateDefaultConfiguration() return config; - void AddBranchConfig(string name, BranchConfig overrides) + void AddBranchConfig(string name, BranchConfig branchConfiguration) { - var emptyBranchConfiguration = new BranchConfig() { Name = name }; - config.Branches[name] = emptyBranchConfiguration.Apply(overrides); + branchConfiguration.Name = name; + config.Branches[name] = branchConfiguration; } } } diff --git a/src/GitVersion.Core/Configuration/ConfigurationModule.cs b/src/GitVersion.Core/Configuration/ConfigurationModule.cs index cadd2afb47..1bc2b97666 100644 --- a/src/GitVersion.Core/Configuration/ConfigurationModule.cs +++ b/src/GitVersion.Core/Configuration/ConfigurationModule.cs @@ -12,6 +12,5 @@ public void RegisterTypes(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); } } diff --git a/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs b/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs index 5b0b969fba..aca3832ed7 100644 --- a/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs +++ b/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs @@ -11,7 +11,6 @@ public interface IRepositoryStore ICommit? FindMergeBase(ICommit commit, ICommit mainlineTip); ICommit? GetCurrentCommit(IBranch currentBranch, string? commitId); - ICommit GetBaseVersionSource(ICommit currentBranchTip); IEnumerable GetMainlineCommitLog(ICommit? baseVersionSource, ICommit? mainlineTip); IEnumerable GetMergeBaseCommits(ICommit? mergeCommit, ICommit? mergedHead, ICommit? findMergeBase); IEnumerable GetCommitLog(ICommit? baseVersionSource, ICommit? currentCommit); @@ -19,13 +18,11 @@ public interface IRepositoryStore IBranch GetTargetBranch(string? targetBranchName); IBranch? FindBranch(string? branchName); IBranch? FindMainBranch(Config configuration); - IBranch? GetChosenBranch(Config configuration); - IEnumerable GetBranchesForCommit(ICommit commit); - IEnumerable GetExcludedInheritBranches(Config configuration); IEnumerable GetReleaseBranches(IEnumerable> releaseBranchConfig); IEnumerable ExcludingBranches(IEnumerable branchesToExclude); IEnumerable GetBranchesContainingCommit(ICommit? commit, IEnumerable? branches = null, bool onlyTrackedBranches = false); - IDictionary> GetMainlineBranches(ICommit commit, Config configuration, IEnumerable>? mainlineBranchConfigs); + + IDictionary> GetMainlineBranches(ICommit commit, Config configuration); /// /// Find the commit where the given branch was branched from another branch. @@ -33,7 +30,16 @@ public interface IRepositoryStore /// BranchCommit FindCommitBranchWasBranchedFrom(IBranch? branch, Config configuration, params IBranch[] excludedBranches); - SemanticVersion GetCurrentCommitTaggedVersion(ICommit? commit, string? tagPrefix); + IEnumerable FindCommitBranchesWasBranchedFrom(IBranch branch, Config configuration, params IBranch[] excludedBranches); + + IEnumerable FindCommitBranchesWasBranchedFrom(IBranch branch, Config configuration, IEnumerable excludedBranches); + + IEnumerable GetSourceBranches(IBranch branch, Config configuration, params IBranch[] excludedBranches); + + IEnumerable GetSourceBranches(IBranch branch, Config configuration, IEnumerable excludedBranches); + + SemanticVersion? GetCurrentCommitTaggedVersion(ICommit? commit, string? tagPrefix); + IEnumerable GetVersionTagsOnBranch(IBranch branch, string? tagPrefixRegex); IEnumerable<(ITag Tag, SemanticVersion Semver, ICommit Commit)> GetValidVersionTags(string? tagPrefixRegex, DateTimeOffset? olderThan = null); diff --git a/src/GitVersion.Core/Core/MainlineBranchFinder.cs b/src/GitVersion.Core/Core/MainlineBranchFinder.cs index b4fed2e22b..687a178496 100644 --- a/src/GitVersion.Core/Core/MainlineBranchFinder.cs +++ b/src/GitVersion.Core/Core/MainlineBranchFinder.cs @@ -10,7 +10,7 @@ internal class MainlineBranchFinder { private readonly Config configuration; private readonly ILog log; - private readonly IEnumerable>? mainlineBranchConfigs; + private readonly List mainlineBranchConfigurations; private readonly IGitRepository repository; private readonly IRepositoryStore repositoryStore; @@ -18,13 +18,12 @@ internal class MainlineBranchFinder public MainlineBranchFinder(IRepositoryStore repositoryStore, IGitRepository repository, Config configuration, - IEnumerable>? mainlineBranchConfigs, ILog log) { this.repositoryStore = repositoryStore.NotNull(); this.repository = repository.NotNull(); this.configuration = configuration.NotNull(); - this.mainlineBranchConfigs = mainlineBranchConfigs; + mainlineBranchConfigurations = configuration.Branches.Select(e => e.Value).Where(b => b?.IsMainline == true).ToList(); this.log = log.NotNull(); } @@ -44,7 +43,7 @@ public IDictionary> FindMainlineBranches(ICommit commit) private bool BranchIsMainline(INamedReference branch) { var matcher = new MainlineConfigBranchMatcher(branch, this.log); - return this.mainlineBranchConfigs?.Any(matcher.IsMainline) == true; + return this.mainlineBranchConfigurations.Any(matcher.IsMainline) == true; } private class MainlineConfigBranchMatcher @@ -58,9 +57,8 @@ public MainlineConfigBranchMatcher(INamedReference branch, ILog log) this.log = log; } - public bool IsMainline(KeyValuePair mainlineBranchConfig) + public bool IsMainline(BranchConfig value) { - var (_, value) = mainlineBranchConfig; if (value?.Regex == null) return false; diff --git a/src/GitVersion.Core/Core/RepositoryStore.cs b/src/GitVersion.Core/Core/RepositoryStore.cs index 2ae5ed42a8..fade95d46f 100644 --- a/src/GitVersion.Core/Core/RepositoryStore.cs +++ b/src/GitVersion.Core/Core/RepositoryStore.cs @@ -1,6 +1,5 @@ using System.Text.RegularExpressions; using GitVersion.Common; -using GitVersion.Configuration; using GitVersion.Extensions; using GitVersion.Logging; using GitVersion.Model.Configuration; @@ -54,21 +53,6 @@ public RepositoryStore(ILog log, IGitRepository repository) return currentCommit; } - public ICommit GetBaseVersionSource(ICommit currentBranchTip) - { - try - { - var filter = new CommitFilter { IncludeReachableFrom = currentBranchTip }; - var commitCollection = this.repository.Commits.QueryBy(filter); - - return commitCollection.First(c => !c.Parents.Any()); - } - catch (Exception exception) - { - throw new GitVersionException($"Cannot find commit {currentBranchTip}. Please ensure that the repository is an unshallow clone with `git fetch --unshallow`.", exception); - } - } - public IEnumerable GetMainlineCommitLog(ICommit? baseVersionSource, ICommit? mainlineTip) { if (mainlineTip is null) @@ -137,29 +121,6 @@ public IBranch GetTargetBranch(string? targetBranchName) Regex.IsMatch(b.Name.Friendly, mainBranchRegex, RegexOptions.IgnoreCase)); } - public IBranch? GetChosenBranch(Config configuration) - { - var developBranchRegex = configuration.Branches[Config.DevelopBranchKey]?.Regex; - var mainBranchRegex = configuration.Branches[Config.MainBranchKey]?.Regex; - - if (mainBranchRegex == null || developBranchRegex == null) return null; - var chosenBranch = this.repository.Branches.FirstOrDefault(b => - Regex.IsMatch(b.Name.Friendly, developBranchRegex, RegexOptions.IgnoreCase) - || Regex.IsMatch(b.Name.Friendly, mainBranchRegex, RegexOptions.IgnoreCase)); - return chosenBranch; - } - - public IEnumerable GetBranchesForCommit(ICommit commit) - => this.repository.Branches.Where(b => !b.IsRemote && Equals(b.Tip, commit)).ToList(); - - public IEnumerable GetExcludedInheritBranches(Config configuration) - => this.repository.Branches.Where(b => - { - var branchConfiguration = configuration.ForBranch(b); - - return branchConfiguration == null || branchConfiguration.Increment == IncrementStrategy.Inherit; - }).ToList(); - public IEnumerable GetReleaseBranches(IEnumerable> releaseBranchConfig) => this.repository.Branches.Where(b => IsReleaseBranch(b, releaseBranchConfig)); @@ -171,12 +132,35 @@ public IEnumerable GetBranchesContainingCommit(ICommit? commit, IEnumer return branchesContainingCommitFinder.GetBranchesContainingCommit(commit, branches, onlyTrackedBranches); } - public IDictionary> GetMainlineBranches(ICommit commit, Config configuration, IEnumerable>? mainlineBranchConfigs) + public IDictionary> GetMainlineBranches(ICommit commit, Config configuration) { - var mainlineBranchFinder = new MainlineBranchFinder(this, this.repository, configuration, mainlineBranchConfigs, this.log); + var mainlineBranchFinder = new MainlineBranchFinder(this, this.repository, configuration, this.log); return mainlineBranchFinder.FindMainlineBranches(commit); } + public IEnumerable GetSourceBranches(IBranch branch, Config configuration, params IBranch[] excludedBranches) + => GetSourceBranches(branch, configuration, (IEnumerable)excludedBranches); + + public IEnumerable GetSourceBranches(IBranch branch, Config configuration, IEnumerable excludedBranches) + { + var referenceLookup = this.repository.Refs.ToLookup(r => r.TargetIdentifier); + + var returnedBranches = new HashSet(); + if (referenceLookup.Any()) + { + foreach (var branchCommit in FindCommitBranchesWasBranchedFrom(branch, configuration, excludedBranches)) + { + foreach (var item in referenceLookup[branchCommit.Commit.Sha] + .Where(r => r.Name.Friendly == branchCommit.Branch.Name.Friendly)) + { + if (returnedBranches.Add(branchCommit.Branch)) + { + yield return branchCommit.Branch; + } + } + } + } + } /// /// Find the commit where the given branch was branched from another branch. @@ -210,7 +194,32 @@ public BranchCommit FindCommitBranchWasBranchedFrom(IBranch? branch, Config conf } } - public SemanticVersion GetCurrentCommitTaggedVersion(ICommit? commit, string? tagPrefix) + public IEnumerable FindCommitBranchesWasBranchedFrom(IBranch branch, Config configuration, params IBranch[] excludedBranches) + => FindCommitBranchesWasBranchedFrom(branch, configuration, (IEnumerable)excludedBranches); + + public IEnumerable FindCommitBranchesWasBranchedFrom(IBranch branch, Config configuration, IEnumerable excludedBranches) + { + using (this.log.IndentLog($"Finding branches source of '{branch}'")) + { + if (branch.Tip == null) + { + this.log.Warning($"{branch} has no tip."); + yield break; + } + + DateTimeOffset? when = null; + var branchCommits = new MergeCommitFinder(this, configuration, excludedBranches, this.log) + .FindMergeCommitsFor(branch).ToList(); + foreach (var branchCommit in branchCommits) + { + if (when != null && branchCommit.Commit.When != when) break; + yield return branchCommit; + when = branchCommit.Commit.When; + } + } + } + + public SemanticVersion? GetCurrentCommitTaggedVersion(ICommit? commit, string? tagPrefix) => this.repository.Tags .SelectMany(t => GetCurrentCommitSemanticVersions(commit, tagPrefix, t)) .Max(); diff --git a/src/GitVersion.Core/Model/Configuration/BranchConfig.cs b/src/GitVersion.Core/Model/Configuration/BranchConfig.cs index 46e60ed892..a33aaf68f0 100644 --- a/src/GitVersion.Core/Model/Configuration/BranchConfig.cs +++ b/src/GitVersion.Core/Model/Configuration/BranchConfig.cs @@ -43,6 +43,33 @@ public BranchConfig(BranchConfig branchConfiguration) [YamlMember(Alias = "increment")] public IncrementStrategy? Increment { get; set; } + public BranchConfig Inherit(BranchConfig? parentConfig) + { + if (parentConfig is null) return this; + + var result = new BranchConfig(this); + + if (result.Increment is null || result.Increment == IncrementStrategy.Inherit) + { + result.Increment = parentConfig.Increment; + } + result.VersioningMode ??= parentConfig.VersioningMode; + result.Tag ??= parentConfig.Tag; + result.PreventIncrementOfMergedBranchVersion ??= parentConfig.PreventIncrementOfMergedBranchVersion; + result.TagNumberPattern ??= parentConfig.TagNumberPattern; + result.TrackMergeTarget ??= parentConfig.TrackMergeTarget; + result.CommitMessageIncrementing ??= parentConfig.CommitMessageIncrementing; + result.Regex ??= parentConfig.Regex; + result.SourceBranches ??= parentConfig.SourceBranches; + result.IsSourceBranchFor ??= parentConfig.IsSourceBranchFor; + result.TracksReleaseBranches ??= parentConfig.TracksReleaseBranches; + result.IsReleaseBranch ??= parentConfig.IsReleaseBranch; + result.IsMainline ??= parentConfig.IsMainline; + result.PreReleaseWeight ??= parentConfig.PreReleaseWeight; + + return result; + } + [YamlMember(Alias = "prevent-increment-of-merged-branch-version")] public bool? PreventIncrementOfMergedBranchVersion { get; set; } @@ -109,15 +136,4 @@ public BranchConfig Apply(BranchConfig overrides) overrides.MergeTo(this); return this; } - - public static BranchConfig CreateDefaultBranchConfig(string name) => new() - { - Name = name, - Tag = "useBranchName", - PreventIncrementOfMergedBranchVersion = false, - TrackMergeTarget = false, - TracksReleaseBranches = false, - IsReleaseBranch = false, - IsMainline = false - }; } diff --git a/src/GitVersion.Core/Model/Configuration/EffectiveConfiguration.cs b/src/GitVersion.Core/Model/Configuration/EffectiveConfiguration.cs index 355f3614cd..cf24aa7256 100644 --- a/src/GitVersion.Core/Model/Configuration/EffectiveConfiguration.cs +++ b/src/GitVersion.Core/Model/Configuration/EffectiveConfiguration.cs @@ -41,7 +41,7 @@ public EffectiveConfiguration(Config configuration, BranchConfig currentBranchCo AssemblyFileVersioningFormat = configuration.AssemblyFileVersioningFormat; VersioningMode = currentBranchConfig.VersioningMode.Value; TagPrefix = configuration.TagPrefix; - Tag = currentBranchConfig.Tag ?? @"{BranchName}"; + Tag = currentBranchConfig.Tag ?? string.Empty; NextVersion = configuration.NextVersion; Increment = currentBranchConfig.Increment.Value; BranchPrefixToTrim = currentBranchConfig.Regex; @@ -72,7 +72,7 @@ protected EffectiveConfiguration(AssemblyVersioningScheme assemblyVersioningSche string? assemblyFileVersioningFormat, VersioningMode versioningMode, string? tagPrefix, - string? tag, + string tag, string? nextVersion, IncrementStrategy increment, string? branchPrefixToTrim, @@ -144,7 +144,7 @@ protected EffectiveConfiguration(AssemblyVersioningScheme assemblyVersioningSche /// /// Tag to use when calculating SemVer /// - public string? Tag { get; } + public string Tag { get; } public string? NextVersion { get; } diff --git a/src/GitVersion.Core/Model/GitVersionContext.cs b/src/GitVersion.Core/Model/GitVersionContext.cs index 3d303072a6..f172642c33 100644 --- a/src/GitVersion.Core/Model/GitVersionContext.cs +++ b/src/GitVersion.Core/Model/GitVersionContext.cs @@ -11,7 +11,7 @@ public class GitVersionContext /// /// Contains the raw configuration, use Configuration for specific config based on the current GitVersion context. /// - public Config FullConfiguration { get; } + public Config Configuration { get; } public SemanticVersion? CurrentCommitTaggedVersion { get; } @@ -28,14 +28,14 @@ public GitVersionContext(IBranch currentBranch, ICommit? currentCommit, { CurrentBranch = currentBranch; CurrentCommit = currentCommit; - FullConfiguration = configuration; + Configuration = configuration; CurrentCommitTaggedVersion = currentCommitTaggedVersion; NumberOfUncommittedChanges = numberOfUncommittedChanges; } public EffectiveConfiguration GetEffectiveConfiguration(IBranch branch) { - BranchConfig branchConfiguration = FullConfiguration.GetBranchConfiguration(branch); - return new EffectiveConfiguration(FullConfiguration, branchConfiguration); + BranchConfig branchConfiguration = Configuration.GetBranchConfiguration(branch); + return new EffectiveConfiguration(Configuration, branchConfiguration); } } diff --git a/src/GitVersion.Core/PublicAPI.Shipped.txt b/src/GitVersion.Core/PublicAPI.Shipped.txt index 9f3cd6df99..fbe53e2f4c 100644 --- a/src/GitVersion.Core/PublicAPI.Shipped.txt +++ b/src/GitVersion.Core/PublicAPI.Shipped.txt @@ -147,23 +147,23 @@ GitVersion.CommitSortStrategies.Topological = 1 -> GitVersion.CommitSortStrategi GitVersion.Common.IRepositoryStore GitVersion.Common.IRepositoryStore.ExcludingBranches(System.Collections.Generic.IEnumerable! branchesToExclude) -> System.Collections.Generic.IEnumerable! GitVersion.Common.IRepositoryStore.FindBranch(string? branchName) -> GitVersion.IBranch? +GitVersion.Common.IRepositoryStore.FindCommitBranchesWasBranchedFrom(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration, params GitVersion.IBranch![]! excludedBranches) -> System.Collections.Generic.IEnumerable! +GitVersion.Common.IRepositoryStore.FindCommitBranchesWasBranchedFrom(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IEnumerable! excludedBranches) -> System.Collections.Generic.IEnumerable! GitVersion.Common.IRepositoryStore.FindCommitBranchWasBranchedFrom(GitVersion.IBranch? branch, GitVersion.Model.Configuration.Config! configuration, params GitVersion.IBranch![]! excludedBranches) -> GitVersion.BranchCommit GitVersion.Common.IRepositoryStore.FindMainBranch(GitVersion.Model.Configuration.Config! configuration) -> GitVersion.IBranch? GitVersion.Common.IRepositoryStore.FindMergeBase(GitVersion.IBranch? branch, GitVersion.IBranch? otherBranch) -> GitVersion.ICommit? GitVersion.Common.IRepositoryStore.FindMergeBase(GitVersion.ICommit! commit, GitVersion.ICommit! mainlineTip) -> GitVersion.ICommit? -GitVersion.Common.IRepositoryStore.GetBaseVersionSource(GitVersion.ICommit! currentBranchTip) -> GitVersion.ICommit! GitVersion.Common.IRepositoryStore.GetBranchesContainingCommit(GitVersion.ICommit? commit, System.Collections.Generic.IEnumerable? branches = null, bool onlyTrackedBranches = false) -> System.Collections.Generic.IEnumerable! -GitVersion.Common.IRepositoryStore.GetBranchesForCommit(GitVersion.ICommit! commit) -> System.Collections.Generic.IEnumerable! -GitVersion.Common.IRepositoryStore.GetChosenBranch(GitVersion.Model.Configuration.Config! configuration) -> GitVersion.IBranch? GitVersion.Common.IRepositoryStore.GetCommitLog(GitVersion.ICommit? baseVersionSource, GitVersion.ICommit? currentCommit) -> System.Collections.Generic.IEnumerable! GitVersion.Common.IRepositoryStore.GetCurrentCommit(GitVersion.IBranch! currentBranch, string? commitId) -> GitVersion.ICommit? -GitVersion.Common.IRepositoryStore.GetCurrentCommitTaggedVersion(GitVersion.ICommit? commit, string? tagPrefix) -> GitVersion.SemanticVersion! -GitVersion.Common.IRepositoryStore.GetExcludedInheritBranches(GitVersion.Model.Configuration.Config! configuration) -> System.Collections.Generic.IEnumerable! -GitVersion.Common.IRepositoryStore.GetMainlineBranches(GitVersion.ICommit! commit, GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IEnumerable>? mainlineBranchConfigs) -> System.Collections.Generic.IDictionary!>! +GitVersion.Common.IRepositoryStore.GetCurrentCommitTaggedVersion(GitVersion.ICommit? commit, string? tagPrefix) -> GitVersion.SemanticVersion? +GitVersion.Common.IRepositoryStore.GetMainlineBranches(GitVersion.ICommit! commit, GitVersion.Model.Configuration.Config! configuration) -> System.Collections.Generic.IDictionary!>! GitVersion.Common.IRepositoryStore.GetMainlineCommitLog(GitVersion.ICommit? baseVersionSource, GitVersion.ICommit? mainlineTip) -> System.Collections.Generic.IEnumerable! GitVersion.Common.IRepositoryStore.GetMergeBaseCommits(GitVersion.ICommit? mergeCommit, GitVersion.ICommit? mergedHead, GitVersion.ICommit? findMergeBase) -> System.Collections.Generic.IEnumerable! GitVersion.Common.IRepositoryStore.GetNumberOfUncommittedChanges() -> int GitVersion.Common.IRepositoryStore.GetReleaseBranches(System.Collections.Generic.IEnumerable>! releaseBranchConfig) -> System.Collections.Generic.IEnumerable! +GitVersion.Common.IRepositoryStore.GetSourceBranches(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration, params GitVersion.IBranch![]! excludedBranches) -> System.Collections.Generic.IEnumerable! +GitVersion.Common.IRepositoryStore.GetSourceBranches(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IEnumerable! excludedBranches) -> System.Collections.Generic.IEnumerable! GitVersion.Common.IRepositoryStore.GetTargetBranch(string? targetBranchName) -> GitVersion.IBranch! GitVersion.Common.IRepositoryStore.GetValidVersionTags(string? tagPrefixRegex, System.DateTimeOffset? olderThan = null) -> System.Collections.Generic.IEnumerable<(GitVersion.ITag! Tag, GitVersion.SemanticVersion! Semver, GitVersion.ICommit! Commit)>! GitVersion.Common.IRepositoryStore.GetVersionTagsOnBranch(GitVersion.IBranch! branch, string? tagPrefixRegex) -> System.Collections.Generic.IEnumerable! @@ -173,9 +173,6 @@ GitVersion.ConfigInfo.ConfigFile -> string? GitVersion.ConfigInfo.ConfigInfo() -> void GitVersion.ConfigInfo.OverrideConfig -> GitVersion.Model.Configuration.Config? GitVersion.ConfigInfo.ShowConfig -> bool -GitVersion.Configuration.BranchConfigurationCalculator -GitVersion.Configuration.BranchConfigurationCalculator.BranchConfigurationCalculator(GitVersion.Logging.ILog! log, GitVersion.Common.IRepositoryStore! repositoryStore) -> void -GitVersion.Configuration.BranchConfigurationCalculator.GetBranchConfiguration(GitVersion.IBranch! targetBranch, GitVersion.ICommit? currentCommit, GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IList? excludedInheritBranches = null) -> GitVersion.Model.Configuration.BranchConfig! GitVersion.Configuration.ConfigExtensions GitVersion.Configuration.ConfigFileLocator GitVersion.Configuration.ConfigFileLocator.ConfigFileLocator(GitVersion.IFileSystem! fileSystem, Microsoft.Extensions.Options.IOptions! options) -> void @@ -194,7 +191,7 @@ GitVersion.Configuration.ConfigProvider.Provide(string? workingDirectory, GitVer GitVersion.Configuration.ConfigSerializer GitVersion.Configuration.ConfigSerializer.ConfigSerializer() -> void GitVersion.Configuration.ConfigurationBuilder -GitVersion.Configuration.ConfigurationBuilder.Add(GitVersion.Model.Configuration.Config! config) -> GitVersion.Configuration.ConfigurationBuilder! +GitVersion.Configuration.ConfigurationBuilder.Add(GitVersion.Model.Configuration.Config! configuration) -> GitVersion.Configuration.ConfigurationBuilder! GitVersion.Configuration.ConfigurationBuilder.Build() -> GitVersion.Model.Configuration.Config! GitVersion.Configuration.ConfigurationBuilder.ConfigurationBuilder() -> void GitVersion.Configuration.ConfigurationException @@ -202,8 +199,6 @@ GitVersion.Configuration.ConfigurationException.ConfigurationException(string! m GitVersion.Configuration.ConfigurationModule GitVersion.Configuration.ConfigurationModule.ConfigurationModule() -> void GitVersion.Configuration.ConfigurationModule.RegisterTypes(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void -GitVersion.Configuration.IBranchConfigurationCalculator -GitVersion.Configuration.IBranchConfigurationCalculator.GetBranchConfiguration(GitVersion.IBranch! targetBranch, GitVersion.ICommit? currentCommit, GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IList? excludedInheritBranches = null) -> GitVersion.Model.Configuration.BranchConfig! GitVersion.Configuration.IConfigFileLocator GitVersion.Configuration.IConfigFileLocator.FilePath.get -> string! GitVersion.Configuration.IConfigFileLocator.GetConfigFilePath(string! workingDirectory) -> string? @@ -327,10 +322,10 @@ GitVersion.GitVersionCalculateTool GitVersion.GitVersionCalculateTool.CalculateVersionVariables() -> GitVersion.OutputVariables.VersionVariables! GitVersion.GitVersionCalculateTool.GitVersionCalculateTool(GitVersion.Logging.ILog! log, GitVersion.VersionCalculation.INextVersionCalculator! nextVersionCalculator, GitVersion.VersionCalculation.IVariableProvider! variableProvider, GitVersion.IGitPreparer! gitPreparer, GitVersion.VersionCalculation.Cache.IGitVersionCache! gitVersionCache, GitVersion.VersionCalculation.Cache.IGitVersionCacheKeyFactory! cacheKeyFactory, Microsoft.Extensions.Options.IOptions! options, System.Lazy! versionContext) -> void GitVersion.GitVersionContext +GitVersion.GitVersionContext.Configuration.get -> GitVersion.Model.Configuration.Config! GitVersion.GitVersionContext.CurrentBranch.get -> GitVersion.IBranch! GitVersion.GitVersionContext.CurrentCommit.get -> GitVersion.ICommit? GitVersion.GitVersionContext.CurrentCommitTaggedVersion.get -> GitVersion.SemanticVersion? -GitVersion.GitVersionContext.FullConfiguration.get -> GitVersion.Model.Configuration.Config! GitVersion.GitVersionContext.GetEffectiveConfiguration(GitVersion.IBranch! branch) -> GitVersion.Model.Configuration.EffectiveConfiguration! GitVersion.GitVersionContext.GitVersionContext(GitVersion.IBranch! currentBranch, GitVersion.ICommit? currentCommit, GitVersion.Model.Configuration.Config! configuration, GitVersion.SemanticVersion? currentCommitTaggedVersion, int numberOfUncommittedChanges) -> void GitVersion.GitVersionContext.IsCurrentCommitTagged.get -> bool @@ -584,6 +579,7 @@ GitVersion.Model.Configuration.BranchConfig.CommitMessageIncrementing.get -> Git GitVersion.Model.Configuration.BranchConfig.CommitMessageIncrementing.set -> void GitVersion.Model.Configuration.BranchConfig.Increment.get -> GitVersion.IncrementStrategy? GitVersion.Model.Configuration.BranchConfig.Increment.set -> void +GitVersion.Model.Configuration.BranchConfig.Inherit(GitVersion.Model.Configuration.BranchConfig? parentConfig) -> GitVersion.Model.Configuration.BranchConfig! GitVersion.Model.Configuration.BranchConfig.IsMainline.get -> bool? GitVersion.Model.Configuration.BranchConfig.IsMainline.set -> void GitVersion.Model.Configuration.BranchConfig.IsReleaseBranch.get -> bool? @@ -647,6 +643,8 @@ GitVersion.Model.Configuration.Config.NoBumpMessage.get -> string? GitVersion.Model.Configuration.Config.NoBumpMessage.set -> void GitVersion.Model.Configuration.Config.PatchVersionBumpMessage.get -> string? GitVersion.Model.Configuration.Config.PatchVersionBumpMessage.set -> void +GitVersion.Model.Configuration.Config.SemanticVersionFormat.get -> GitVersion.SemanticVersionFormat +GitVersion.Model.Configuration.Config.SemanticVersionFormat.set -> void GitVersion.Model.Configuration.Config.TagPrefix.get -> string? GitVersion.Model.Configuration.Config.TagPrefix.set -> void GitVersion.Model.Configuration.Config.TagPreReleaseWeight.get -> int? @@ -655,8 +653,6 @@ GitVersion.Model.Configuration.Config.UpdateBuildNumber.get -> bool? GitVersion.Model.Configuration.Config.UpdateBuildNumber.set -> void GitVersion.Model.Configuration.Config.VersioningMode.get -> GitVersion.VersionCalculation.VersioningMode? GitVersion.Model.Configuration.Config.VersioningMode.set -> void -GitVersion.Model.Configuration.Config.SemanticVersionFormat.get -> GitVersion.SemanticVersionFormat -GitVersion.Model.Configuration.Config.SemanticVersionFormat.set -> void GitVersion.Model.Configuration.EffectiveBranchConfiguration GitVersion.Model.Configuration.EffectiveBranchConfiguration.Branch.get -> GitVersion.IBranch! GitVersion.Model.Configuration.EffectiveBranchConfiguration.CreateNextVersion(GitVersion.VersionCalculation.BaseVersion! baseVersion, GitVersion.SemanticVersion! incrementedVersion) -> GitVersion.VersionCalculation.NextVersion! @@ -672,7 +668,7 @@ GitVersion.Model.Configuration.EffectiveConfiguration.BranchPrefixToTrim.get -> GitVersion.Model.Configuration.EffectiveConfiguration.CommitDateFormat.get -> string? GitVersion.Model.Configuration.EffectiveConfiguration.CommitMessageIncrementing.get -> GitVersion.VersionCalculation.CommitMessageIncrementMode GitVersion.Model.Configuration.EffectiveConfiguration.ContinuousDeploymentFallbackTag.get -> string? -GitVersion.Model.Configuration.EffectiveConfiguration.EffectiveConfiguration(GitVersion.Extensions.AssemblyVersioningScheme assemblyVersioningScheme, GitVersion.Extensions.AssemblyFileVersioningScheme assemblyFileVersioningScheme, string? assemblyInformationalFormat, string? assemblyVersioningFormat, string? assemblyFileVersioningFormat, GitVersion.VersionCalculation.VersioningMode versioningMode, string? tagPrefix, string? tag, string? nextVersion, GitVersion.IncrementStrategy increment, string? branchPrefixToTrim, bool preventIncrementOfMergedBranchVersion, string? tagNumberPattern, string? continuousDeploymentFallbackTag, bool trackMergeTarget, string? majorVersionBumpMessage, string? minorVersionBumpMessage, string? patchVersionBumpMessage, string? noBumpMessage, GitVersion.VersionCalculation.CommitMessageIncrementMode commitMessageIncrementing, System.Collections.Generic.IEnumerable! versionFilters, bool tracksReleaseBranches, bool isReleaseBranch, bool isMainline, string? commitDateFormat, bool updateBuildNumber, GitVersion.SemanticVersionFormat semanticVersionFormat, int preReleaseWeight, int tagPreReleaseWeight) -> void +GitVersion.Model.Configuration.EffectiveConfiguration.EffectiveConfiguration(GitVersion.Extensions.AssemblyVersioningScheme assemblyVersioningScheme, GitVersion.Extensions.AssemblyFileVersioningScheme assemblyFileVersioningScheme, string? assemblyInformationalFormat, string? assemblyVersioningFormat, string? assemblyFileVersioningFormat, GitVersion.VersionCalculation.VersioningMode versioningMode, string? tagPrefix, string! tag, string? nextVersion, GitVersion.IncrementStrategy increment, string? branchPrefixToTrim, bool preventIncrementOfMergedBranchVersion, string? tagNumberPattern, string? continuousDeploymentFallbackTag, bool trackMergeTarget, string? majorVersionBumpMessage, string? minorVersionBumpMessage, string? patchVersionBumpMessage, string? noBumpMessage, GitVersion.VersionCalculation.CommitMessageIncrementMode commitMessageIncrementing, System.Collections.Generic.IEnumerable! versionFilters, bool tracksReleaseBranches, bool isReleaseBranch, bool isMainline, string? commitDateFormat, bool updateBuildNumber, GitVersion.SemanticVersionFormat semanticVersionFormat, int preReleaseWeight, int tagPreReleaseWeight) -> void GitVersion.Model.Configuration.EffectiveConfiguration.EffectiveConfiguration(GitVersion.Model.Configuration.Config! configuration, GitVersion.Model.Configuration.BranchConfig! currentBranchConfig) -> void GitVersion.Model.Configuration.EffectiveConfiguration.Increment.get -> GitVersion.IncrementStrategy GitVersion.Model.Configuration.EffectiveConfiguration.IsMainline.get -> bool @@ -684,7 +680,9 @@ GitVersion.Model.Configuration.EffectiveConfiguration.NoBumpMessage.get -> strin GitVersion.Model.Configuration.EffectiveConfiguration.PatchVersionBumpMessage.get -> string? GitVersion.Model.Configuration.EffectiveConfiguration.PreReleaseWeight.get -> int GitVersion.Model.Configuration.EffectiveConfiguration.PreventIncrementOfMergedBranchVersion.get -> bool -GitVersion.Model.Configuration.EffectiveConfiguration.Tag.get -> string? +GitVersion.Model.Configuration.EffectiveConfiguration.SemanticVersionFormat.get -> GitVersion.SemanticVersionFormat +GitVersion.Model.Configuration.EffectiveConfiguration.SemanticVersionFormat.set -> void +GitVersion.Model.Configuration.EffectiveConfiguration.Tag.get -> string! GitVersion.Model.Configuration.EffectiveConfiguration.TagNumberPattern.get -> string? GitVersion.Model.Configuration.EffectiveConfiguration.TagPrefix.get -> string? GitVersion.Model.Configuration.EffectiveConfiguration.TagPreReleaseWeight.get -> int @@ -693,8 +691,6 @@ GitVersion.Model.Configuration.EffectiveConfiguration.TracksReleaseBranches.get GitVersion.Model.Configuration.EffectiveConfiguration.UpdateBuildNumber.get -> bool GitVersion.Model.Configuration.EffectiveConfiguration.VersionFilters.get -> System.Collections.Generic.IEnumerable! GitVersion.Model.Configuration.EffectiveConfiguration.VersioningMode.get -> GitVersion.VersionCalculation.VersioningMode -GitVersion.Model.Configuration.EffectiveConfiguration.SemanticVersionFormat.get -> GitVersion.SemanticVersionFormat -GitVersion.Model.Configuration.EffectiveConfiguration.SemanticVersionFormat.set -> void GitVersion.Model.Configuration.IgnoreConfig GitVersion.Model.Configuration.IgnoreConfig.Before.get -> System.DateTimeOffset? GitVersion.Model.Configuration.IgnoreConfig.Before.set -> void @@ -820,36 +816,34 @@ GitVersion.RepositoryInfo.TargetUrl -> string? GitVersion.RepositoryStore GitVersion.RepositoryStore.ExcludingBranches(System.Collections.Generic.IEnumerable! branchesToExclude) -> System.Collections.Generic.IEnumerable! GitVersion.RepositoryStore.FindBranch(string? branchName) -> GitVersion.IBranch? +GitVersion.RepositoryStore.FindCommitBranchesWasBranchedFrom(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration, params GitVersion.IBranch![]! excludedBranches) -> System.Collections.Generic.IEnumerable! +GitVersion.RepositoryStore.FindCommitBranchesWasBranchedFrom(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IEnumerable! excludedBranches) -> System.Collections.Generic.IEnumerable! GitVersion.RepositoryStore.FindCommitBranchWasBranchedFrom(GitVersion.IBranch? branch, GitVersion.Model.Configuration.Config! configuration, params GitVersion.IBranch![]! excludedBranches) -> GitVersion.BranchCommit GitVersion.RepositoryStore.FindMainBranch(GitVersion.Model.Configuration.Config! configuration) -> GitVersion.IBranch? GitVersion.RepositoryStore.FindMergeBase(GitVersion.IBranch? branch, GitVersion.IBranch? otherBranch) -> GitVersion.ICommit? GitVersion.RepositoryStore.FindMergeBase(GitVersion.ICommit! commit, GitVersion.ICommit! mainlineTip) -> GitVersion.ICommit? -GitVersion.RepositoryStore.GetBaseVersionSource(GitVersion.ICommit! currentBranchTip) -> GitVersion.ICommit! GitVersion.RepositoryStore.GetBranchesContainingCommit(GitVersion.ICommit? commit, System.Collections.Generic.IEnumerable? branches = null, bool onlyTrackedBranches = false) -> System.Collections.Generic.IEnumerable! -GitVersion.RepositoryStore.GetBranchesForCommit(GitVersion.ICommit! commit) -> System.Collections.Generic.IEnumerable! -GitVersion.RepositoryStore.GetChosenBranch(GitVersion.Model.Configuration.Config! configuration) -> GitVersion.IBranch? GitVersion.RepositoryStore.GetCommitLog(GitVersion.ICommit? baseVersionSource, GitVersion.ICommit? currentCommit) -> System.Collections.Generic.IEnumerable! GitVersion.RepositoryStore.GetCurrentCommit(GitVersion.IBranch! currentBranch, string? commitId) -> GitVersion.ICommit? -GitVersion.RepositoryStore.GetCurrentCommitTaggedVersion(GitVersion.ICommit? commit, string? tagPrefix) -> GitVersion.SemanticVersion! -GitVersion.RepositoryStore.GetExcludedInheritBranches(GitVersion.Model.Configuration.Config! configuration) -> System.Collections.Generic.IEnumerable! -GitVersion.RepositoryStore.GetMainlineBranches(GitVersion.ICommit! commit, GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IEnumerable>? mainlineBranchConfigs) -> System.Collections.Generic.IDictionary!>! +GitVersion.RepositoryStore.GetCurrentCommitTaggedVersion(GitVersion.ICommit? commit, string? tagPrefix) -> GitVersion.SemanticVersion? +GitVersion.RepositoryStore.GetMainlineBranches(GitVersion.ICommit! commit, GitVersion.Model.Configuration.Config! configuration) -> System.Collections.Generic.IDictionary!>! GitVersion.RepositoryStore.GetMainlineCommitLog(GitVersion.ICommit? baseVersionSource, GitVersion.ICommit? mainlineTip) -> System.Collections.Generic.IEnumerable! GitVersion.RepositoryStore.GetMergeBaseCommits(GitVersion.ICommit? mergeCommit, GitVersion.ICommit? mergedHead, GitVersion.ICommit? findMergeBase) -> System.Collections.Generic.IEnumerable! GitVersion.RepositoryStore.GetNumberOfUncommittedChanges() -> int GitVersion.RepositoryStore.GetReleaseBranches(System.Collections.Generic.IEnumerable>! releaseBranchConfig) -> System.Collections.Generic.IEnumerable! +GitVersion.RepositoryStore.GetSourceBranches(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration, params GitVersion.IBranch![]! excludedBranches) -> System.Collections.Generic.IEnumerable! +GitVersion.RepositoryStore.GetSourceBranches(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IEnumerable! excludedBranches) -> System.Collections.Generic.IEnumerable! GitVersion.RepositoryStore.GetTargetBranch(string? targetBranchName) -> GitVersion.IBranch! GitVersion.RepositoryStore.GetValidVersionTags(string? tagPrefixRegex, System.DateTimeOffset? olderThan = null) -> System.Collections.Generic.IEnumerable<(GitVersion.ITag! Tag, GitVersion.SemanticVersion! Semver, GitVersion.ICommit! Commit)>! GitVersion.RepositoryStore.GetVersionTagsOnBranch(GitVersion.IBranch! branch, string? tagPrefixRegex) -> System.Collections.Generic.IEnumerable! GitVersion.RepositoryStore.IsCommitOnBranch(GitVersion.ICommit? baseVersionSource, GitVersion.IBranch! branch, GitVersion.ICommit! firstMatchingCommit) -> bool GitVersion.RepositoryStore.RepositoryStore(GitVersion.Logging.ILog! log, GitVersion.IGitRepository! repository) -> void GitVersion.SemanticVersion -GitVersion.SemanticVersionFormat -GitVersion.SemanticVersionFormat.Loose = 1 -> GitVersion.SemanticVersionFormat -GitVersion.SemanticVersionFormat.Strict = 0 -> GitVersion.SemanticVersionFormat GitVersion.SemanticVersion.BuildMetaData -> GitVersion.SemanticVersionBuildMetaData? GitVersion.SemanticVersion.CompareTo(GitVersion.SemanticVersion! value) -> int GitVersion.SemanticVersion.CompareTo(GitVersion.SemanticVersion? value, bool includePrerelease) -> int GitVersion.SemanticVersion.Equals(GitVersion.SemanticVersion? obj) -> bool +GitVersion.SemanticVersion.HasPreReleaseTagWithLabel.get -> bool GitVersion.SemanticVersion.IncrementVersion(GitVersion.VersionField incrementStrategy) -> GitVersion.SemanticVersion! GitVersion.SemanticVersion.IsEmpty() -> bool GitVersion.SemanticVersion.Major -> long @@ -876,6 +870,9 @@ GitVersion.SemanticVersionBuildMetaData.ToString(string! format) -> string! GitVersion.SemanticVersionBuildMetaData.ToString(string? format, System.IFormatProvider? formatProvider) -> string! GitVersion.SemanticVersionBuildMetaData.UncommittedChanges -> long GitVersion.SemanticVersionBuildMetaData.VersionSourceSha -> string? +GitVersion.SemanticVersionFormat +GitVersion.SemanticVersionFormat.Loose = 1 -> GitVersion.SemanticVersionFormat +GitVersion.SemanticVersionFormat.Strict = 0 -> GitVersion.SemanticVersionFormat GitVersion.SemanticVersionFormatValues GitVersion.SemanticVersionFormatValues.AssemblyFileSemVer.get -> string? GitVersion.SemanticVersionFormatValues.AssemblySemVer.get -> string? @@ -931,9 +928,6 @@ GitVersion.VersionCalculation.BaseVersion.BranchNameOverride.get -> string? GitVersion.VersionCalculation.BaseVersion.SemanticVersion.get -> GitVersion.SemanticVersion! GitVersion.VersionCalculation.BaseVersion.ShouldIncrement.get -> bool GitVersion.VersionCalculation.BaseVersion.Source.get -> string! -GitVersion.VersionCalculation.BaseVersionCalculator -GitVersion.VersionCalculation.BaseVersionCalculator.BaseVersionCalculator(GitVersion.Logging.ILog! log, GitVersion.Common.IRepositoryStore! repositoryStore, GitVersion.Configuration.IBranchConfigurationCalculator! branchConfigurationCalculator, GitVersion.VersionCalculation.IIncrementStrategyFinder! incrementStrategyFinder, System.Lazy! versionContext, System.Collections.Generic.IEnumerable! strategies) -> void -GitVersion.VersionCalculation.BaseVersionCalculator.GetBaseVersion() -> (GitVersion.VersionCalculation.BaseVersion!, GitVersion.Model.Configuration.EffectiveBranchConfiguration!) GitVersion.VersionCalculation.Cache.GitVersionCache GitVersion.VersionCalculation.Cache.GitVersionCache.GetCacheDirectory() -> string! GitVersion.VersionCalculation.Cache.GitVersionCache.GitVersionCache(GitVersion.IFileSystem! fileSystem, GitVersion.Logging.ILog! log, GitVersion.IGitRepositoryInfo! repositoryInfo) -> void @@ -954,10 +948,12 @@ GitVersion.VersionCalculation.CommitMessageIncrementMode.Enabled = 0 -> GitVersi GitVersion.VersionCalculation.CommitMessageIncrementMode.MergeMessageOnly = 2 -> GitVersion.VersionCalculation.CommitMessageIncrementMode GitVersion.VersionCalculation.ConfigNextVersionVersionStrategy GitVersion.VersionCalculation.ConfigNextVersionVersionStrategy.ConfigNextVersionVersionStrategy(System.Lazy! versionContext) -> void +GitVersion.VersionCalculation.EffectiveBranchConfigurationFinder +GitVersion.VersionCalculation.EffectiveBranchConfigurationFinder.EffectiveBranchConfigurationFinder(GitVersion.Logging.ILog! log, GitVersion.Common.IRepositoryStore! repositoryStore) -> void GitVersion.VersionCalculation.FallbackVersionStrategy -GitVersion.VersionCalculation.FallbackVersionStrategy.FallbackVersionStrategy(GitVersion.Common.IRepositoryStore! repositoryStore, System.Lazy! versionContext) -> void -GitVersion.VersionCalculation.IBaseVersionCalculator -GitVersion.VersionCalculation.IBaseVersionCalculator.GetBaseVersion() -> (GitVersion.VersionCalculation.BaseVersion!, GitVersion.Model.Configuration.EffectiveBranchConfiguration!) +GitVersion.VersionCalculation.FallbackVersionStrategy.FallbackVersionStrategy() -> void +GitVersion.VersionCalculation.IEffectiveBranchConfigurationFinder +GitVersion.VersionCalculation.IEffectiveBranchConfigurationFinder.GetConfigurations(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration) -> System.Collections.Generic.IEnumerable! GitVersion.VersionCalculation.IIncrementStrategyFinder GitVersion.VersionCalculation.IIncrementStrategyFinder.DetermineIncrementedField(GitVersion.GitVersionContext! context, GitVersion.VersionCalculation.BaseVersion! baseVersion, GitVersion.Model.Configuration.EffectiveConfiguration! configuration) -> GitVersion.VersionField GitVersion.VersionCalculation.IIncrementStrategyFinder.GetIncrementForCommits(GitVersion.Model.Configuration.Config! configuration, System.Collections.Generic.IEnumerable! commits) -> GitVersion.VersionField? @@ -977,21 +973,21 @@ GitVersion.VersionCalculation.IVersionFilter.Exclude(GitVersion.VersionCalculati GitVersion.VersionCalculation.IVersionStrategy GitVersion.VersionCalculation.IVersionStrategy.GetBaseVersions(GitVersion.Model.Configuration.EffectiveBranchConfiguration! configuration) -> System.Collections.Generic.IEnumerable! GitVersion.VersionCalculation.MergeMessageVersionStrategy -GitVersion.VersionCalculation.MergeMessageVersionStrategy.MergeMessageVersionStrategy(GitVersion.Logging.ILog! log, System.Lazy! versionContext) -> void +GitVersion.VersionCalculation.MergeMessageVersionStrategy.MergeMessageVersionStrategy(GitVersion.Logging.ILog! log, System.Lazy! versionContext, GitVersion.Common.IRepositoryStore! repositoryStore) -> void GitVersion.VersionCalculation.MinDateVersionFilter GitVersion.VersionCalculation.MinDateVersionFilter.Exclude(GitVersion.VersionCalculation.BaseVersion! version, out string? reason) -> bool GitVersion.VersionCalculation.MinDateVersionFilter.MinDateVersionFilter(System.DateTimeOffset minimum) -> void GitVersion.VersionCalculation.NextVersion GitVersion.VersionCalculation.NextVersion.BaseVersion.get -> GitVersion.VersionCalculation.BaseVersion! -GitVersion.VersionCalculation.NextVersion.BaseVersion.set -> void GitVersion.VersionCalculation.NextVersion.Branch.get -> GitVersion.IBranch! +GitVersion.VersionCalculation.NextVersion.CompareTo(GitVersion.VersionCalculation.NextVersion! other) -> int GitVersion.VersionCalculation.NextVersion.Configuration.get -> GitVersion.Model.Configuration.EffectiveConfiguration! +GitVersion.VersionCalculation.NextVersion.Equals(GitVersion.VersionCalculation.NextVersion! other) -> bool GitVersion.VersionCalculation.NextVersion.IncrementedVersion.get -> GitVersion.SemanticVersion! GitVersion.VersionCalculation.NextVersion.NextVersion(GitVersion.SemanticVersion! incrementedVersion, GitVersion.VersionCalculation.BaseVersion! baseVersion, GitVersion.IBranch! branch, GitVersion.Model.Configuration.EffectiveConfiguration! configuration) -> void GitVersion.VersionCalculation.NextVersion.NextVersion(GitVersion.SemanticVersion! incrementedVersion, GitVersion.VersionCalculation.BaseVersion! baseVersion, GitVersion.Model.Configuration.EffectiveBranchConfiguration! configuration) -> void GitVersion.VersionCalculation.NextVersionCalculator -GitVersion.VersionCalculation.NextVersionCalculator.FindVersion() -> GitVersion.VersionCalculation.NextVersion! -GitVersion.VersionCalculation.NextVersionCalculator.NextVersionCalculator(GitVersion.Logging.ILog! log, GitVersion.VersionCalculation.IBaseVersionCalculator! baseVersionCalculator, GitVersion.VersionCalculation.IMainlineVersionCalculator! mainlineVersionCalculator, GitVersion.Common.IRepositoryStore! repositoryStore, GitVersion.VersionCalculation.IIncrementStrategyFinder! incrementStrategyFinder, System.Lazy! versionContext) -> void +GitVersion.VersionCalculation.NextVersionCalculator.NextVersionCalculator(GitVersion.Logging.ILog! log, GitVersion.VersionCalculation.IMainlineVersionCalculator! mainlineVersionCalculator, GitVersion.Common.IRepositoryStore! repositoryStore, System.Lazy! versionContext, System.Collections.Generic.IEnumerable! versionStrategies, GitVersion.VersionCalculation.IEffectiveBranchConfigurationFinder! effectiveBranchConfigurationFinder, GitVersion.VersionCalculation.IIncrementStrategyFinder! incrementStrategyFinder) -> void GitVersion.VersionCalculation.ShaVersionFilter GitVersion.VersionCalculation.ShaVersionFilter.Exclude(GitVersion.VersionCalculation.BaseVersion! version, out string? reason) -> bool GitVersion.VersionCalculation.ShaVersionFilter.ShaVersionFilter(System.Collections.Generic.IEnumerable! shas) -> void @@ -1240,8 +1236,9 @@ override GitVersion.SemanticVersionPreReleaseTag.GetHashCode() -> int override GitVersion.SemanticVersionPreReleaseTag.ToString() -> string! override GitVersion.VersionCalculation.BaseVersion.ToString() -> string! override GitVersion.VersionCalculation.ConfigNextVersionVersionStrategy.GetBaseVersions(GitVersion.Model.Configuration.EffectiveBranchConfiguration! configuration) -> System.Collections.Generic.IEnumerable! -override GitVersion.VersionCalculation.FallbackVersionStrategy.GetBaseVersions(GitVersion.Model.Configuration.EffectiveBranchConfiguration! configuration) -> System.Collections.Generic.IEnumerable! override GitVersion.VersionCalculation.MergeMessageVersionStrategy.GetBaseVersions(GitVersion.Model.Configuration.EffectiveBranchConfiguration! configuration) -> System.Collections.Generic.IEnumerable! +override GitVersion.VersionCalculation.NextVersion.Equals(object! other) -> bool +override GitVersion.VersionCalculation.NextVersion.GetHashCode() -> int override GitVersion.VersionCalculation.NextVersion.ToString() -> string! override GitVersion.VersionCalculation.TaggedCommitVersionStrategy.GetBaseVersions(GitVersion.Model.Configuration.EffectiveBranchConfiguration! configuration) -> System.Collections.Generic.IEnumerable! override GitVersion.VersionCalculation.TaggedCommitVersionStrategy.VersionTaggedCommit.ToString() -> string! @@ -1334,7 +1331,6 @@ static GitVersion.Logging.LogExtensions.Warning(this GitVersion.Logging.ILog! lo static GitVersion.Logging.LogExtensions.Warning(this GitVersion.Logging.ILog! log, GitVersion.Logging.Verbosity verbosity, string! format, params object![]! args) -> void static GitVersion.Logging.LogExtensions.Warning(this GitVersion.Logging.ILog! log, string! format, params object![]! args) -> void static GitVersion.Logging.LogExtensions.Write(this GitVersion.Logging.ILog! log, GitVersion.Logging.LogLevel level, string! format, params object![]! args) -> void -static GitVersion.Model.Configuration.BranchConfig.CreateDefaultBranchConfig(string! name) -> GitVersion.Model.Configuration.BranchConfig! static GitVersion.OutputVariables.VersionVariables.AvailableVariables.get -> System.Collections.Generic.IEnumerable! static GitVersion.OutputVariables.VersionVariables.FromFile(string! filePath, GitVersion.IFileSystem! fileSystem) -> GitVersion.OutputVariables.VersionVariables! static GitVersion.OutputVariables.VersionVariables.FromJson(string! json) -> GitVersion.OutputVariables.VersionVariables! @@ -1362,6 +1358,12 @@ static GitVersion.SemanticVersionPreReleaseTag.operator ==(GitVersion.SemanticVe static GitVersion.SemanticVersionPreReleaseTag.operator >(GitVersion.SemanticVersionPreReleaseTag? left, GitVersion.SemanticVersionPreReleaseTag? right) -> bool static GitVersion.SemanticVersionPreReleaseTag.operator >=(GitVersion.SemanticVersionPreReleaseTag? left, GitVersion.SemanticVersionPreReleaseTag? right) -> bool static GitVersion.SemanticVersionPreReleaseTag.Parse(string? preReleaseTag) -> GitVersion.SemanticVersionPreReleaseTag! +static GitVersion.VersionCalculation.NextVersion.operator !=(GitVersion.VersionCalculation.NextVersion! left, GitVersion.VersionCalculation.NextVersion! right) -> bool +static GitVersion.VersionCalculation.NextVersion.operator <(GitVersion.VersionCalculation.NextVersion! left, GitVersion.VersionCalculation.NextVersion! right) -> bool +static GitVersion.VersionCalculation.NextVersion.operator <=(GitVersion.VersionCalculation.NextVersion! left, GitVersion.VersionCalculation.NextVersion! right) -> bool +static GitVersion.VersionCalculation.NextVersion.operator ==(GitVersion.VersionCalculation.NextVersion! left, GitVersion.VersionCalculation.NextVersion! right) -> bool +static GitVersion.VersionCalculation.NextVersion.operator >(GitVersion.VersionCalculation.NextVersion! left, GitVersion.VersionCalculation.NextVersion! right) -> bool +static GitVersion.VersionCalculation.NextVersion.operator >=(GitVersion.VersionCalculation.NextVersion! left, GitVersion.VersionCalculation.NextVersion! right) -> bool static readonly GitVersion.BranchCommit.Empty -> GitVersion.BranchCommit static readonly GitVersion.Helpers.StringComparerUtils.IgnoreCaseComparer -> System.StringComparer! static readonly GitVersion.Helpers.StringComparerUtils.OsDependentComparer -> System.StringComparer! @@ -1377,4 +1379,7 @@ virtual GitVersion.BuildAgents.BuildAgentBase.ShouldCleanUpRemotes() -> bool virtual GitVersion.BuildAgents.BuildAgentBase.WriteIntegration(System.Action! writer, GitVersion.OutputVariables.VersionVariables! variables, bool updateBuildNumber = true) -> void virtual GitVersion.Model.Configuration.IgnoreConfig.IsEmpty.get -> bool virtual GitVersion.Model.Configuration.IgnoreConfig.ToFilters() -> System.Collections.Generic.IEnumerable! +virtual GitVersion.VersionCalculation.EffectiveBranchConfigurationFinder.GetConfigurations(GitVersion.IBranch! branch, GitVersion.Model.Configuration.Config! configuration) -> System.Collections.Generic.IEnumerable! +virtual GitVersion.VersionCalculation.FallbackVersionStrategy.GetBaseVersions(GitVersion.Model.Configuration.EffectiveBranchConfiguration! configuration) -> System.Collections.Generic.IEnumerable! +virtual GitVersion.VersionCalculation.NextVersionCalculator.FindVersion() -> GitVersion.VersionCalculation.NextVersion! virtual GitVersion.VersionCalculation.TaggedCommitVersionStrategy.FormatSource(GitVersion.VersionCalculation.TaggedCommitVersionStrategy.VersionTaggedCommit! version) -> string! diff --git a/src/GitVersion.Core/VersionCalculation/Abstractions/IBaseVersionCalculator.cs b/src/GitVersion.Core/VersionCalculation/Abstractions/IBaseVersionCalculator.cs deleted file mode 100644 index 7ed1497cd6..0000000000 --- a/src/GitVersion.Core/VersionCalculation/Abstractions/IBaseVersionCalculator.cs +++ /dev/null @@ -1,8 +0,0 @@ -using GitVersion.Model.Configuration; - -namespace GitVersion.VersionCalculation; - -public interface IBaseVersionCalculator -{ - (BaseVersion, EffectiveBranchConfiguration) GetBaseVersion(); -} diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculator.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculator.cs deleted file mode 100644 index c7af4273c3..0000000000 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculator.cs +++ /dev/null @@ -1,197 +0,0 @@ -using GitVersion.Common; -using GitVersion.Configuration; -using GitVersion.Extensions; -using GitVersion.Logging; -using GitVersion.Model.Configuration; - -namespace GitVersion.VersionCalculation; - -public class BaseVersionCalculator : IBaseVersionCalculator -{ - private readonly ILog log; - private readonly IRepositoryStore repositoryStore; - private readonly IBranchConfigurationCalculator branchConfigurationCalculator; - private readonly IIncrementStrategyFinder incrementStrategyFinder; - private readonly IVersionStrategy[] strategies; - private readonly Lazy versionContext; - private GitVersionContext context => this.versionContext.Value; - - public BaseVersionCalculator(ILog log, IRepositoryStore repositoryStore, - IBranchConfigurationCalculator branchConfigurationCalculator, IIncrementStrategyFinder incrementStrategyFinder, Lazy versionContext, IEnumerable strategies) - { - this.log = log.NotNull(); - this.repositoryStore = repositoryStore.NotNull(); - this.branchConfigurationCalculator = branchConfigurationCalculator.NotNull(); - this.incrementStrategyFinder = incrementStrategyFinder.NotNull(); - this.strategies = strategies.ToArray(); - this.versionContext = versionContext.NotNull(); - } - - public (BaseVersion, EffectiveBranchConfiguration) GetBaseVersion() - { - using (this.log.IndentLog("Calculating base versions")) - { - if (this.context.CurrentBranch.Tip == null) - throw new GitVersionException("No commits found on the current branch."); - - var currentBranchConfig = this.branchConfigurationCalculator.GetBranchConfiguration( - targetBranch: this.context.CurrentBranch, - currentCommit: this.context.CurrentCommit, - configuration: this.context.FullConfiguration - ); - var effectiveConfiguration = new EffectiveConfiguration(this.context.FullConfiguration, currentBranchConfig); - var effectiveBranchConfiguration = new EffectiveBranchConfiguration(this.context.CurrentBranch, effectiveConfiguration); - - var allVersions = new List(); - foreach (var strategy in this.strategies) - { - var baseVersions = GetBaseVersions(strategy, effectiveBranchConfiguration).ToList(); - allVersions.AddRange(baseVersions); - } - - var versions = allVersions - .Select(baseVersion => new Versions { IncrementedVersion = MaybeIncrement(baseVersion, effectiveConfiguration), Version = baseVersion }) - .ToList(); - - FixTheBaseVersionSourceOfMergeMessageStrategyIfReleaseBranchWasMergedAndDeleted(versions); - - if (context.FullConfiguration.VersioningMode == VersioningMode.Mainline) - { - versions = versions - .Where(b => b.IncrementedVersion.PreReleaseTag?.HasTag() != true) - .ToList(); - } - - var maxVersion = versions.Aggregate((v1, v2) => v1.IncrementedVersion > v2.IncrementedVersion ? v1 : v2); - var matchingVersionsOnceIncremented = versions - .Where(b => b.Version.BaseVersionSource != null && b.IncrementedVersion == maxVersion.IncrementedVersion) - .ToList(); - BaseVersion baseVersionWithOldestSource; - - if (matchingVersionsOnceIncremented.Any()) - { - static Versions CompareVersions(Versions versions1, Versions version2) - { - if (versions1.Version.BaseVersionSource == null) - { - return version2; - } - if (version2.Version.BaseVersionSource == null) - { - return versions1; - } - - return versions1.Version.BaseVersionSource.When < version2.Version.BaseVersionSource.When ? versions1 : version2; - } - - var oldest = matchingVersionsOnceIncremented.Aggregate(CompareVersions); - baseVersionWithOldestSource = oldest.Version; - maxVersion = oldest; - this.log.Info($"Found multiple base versions which will produce the same SemVer ({maxVersion.IncrementedVersion}), taking oldest source for commit counting ({baseVersionWithOldestSource.Source})"); - } - else - { - baseVersionWithOldestSource = versions - .Where(v => v.Version.BaseVersionSource != null) - .OrderByDescending(v => v.IncrementedVersion) - .ThenByDescending(v => v.Version.BaseVersionSource?.When) - .First() - .Version; - } - - if (baseVersionWithOldestSource.BaseVersionSource == null) - throw new Exception("Base version should not be null"); - - var calculatedBase = new BaseVersion( - maxVersion.Version.Source, - maxVersion.Version.ShouldIncrement, - maxVersion.Version.SemanticVersion, - baseVersionWithOldestSource.BaseVersionSource, - maxVersion.Version.BranchNameOverride); - - this.log.Info($"Base version used: {calculatedBase}"); - - return new(calculatedBase, effectiveBranchConfiguration); - } - } - - private SemanticVersion MaybeIncrement(BaseVersion baseVersion, EffectiveConfiguration configuration) - { - var incrementStrategy = this.incrementStrategyFinder.DetermineIncrementedField( - context: context, - baseVersion: baseVersion, - configuration: configuration - ); - return incrementStrategy == VersionField.None - ? baseVersion.SemanticVersion - : baseVersion.SemanticVersion.IncrementVersion(incrementStrategy); - } - - private IEnumerable GetBaseVersions(IVersionStrategy strategy, EffectiveBranchConfiguration configuration) - { - foreach (var version in strategy.GetBaseVersions(configuration)) - { - if (version == null) continue; - - this.log.Info(version.ToString()); - if (strategy is FallbackVersionStrategy || IncludeVersion(version)) - { - yield return version; - } - } - } - private bool IncludeVersion(BaseVersion version) - { - foreach (var filter in context.FullConfiguration.Ignore.ToFilters()) - { - if (filter.Exclude(version, out var reason)) - { - if (reason != null) - { - this.log.Info(reason); - } - return false; - } - } - return true; - } - - private void FixTheBaseVersionSourceOfMergeMessageStrategyIfReleaseBranchWasMergedAndDeleted(IEnumerable baseVersions) - { - if (ReleaseBranchExistsInRepo()) return; - - foreach (var baseVersion in baseVersions) - { - if (baseVersion.Version.Source.Contains(MergeMessageVersionStrategy.MergeMessageStrategyPrefix) - && baseVersion.Version.Source.Contains("Merge branch") - && baseVersion.Version.Source.Contains("release")) - { - if (baseVersion.Version.BaseVersionSource != null) - { - var parents = baseVersion.Version.BaseVersionSource.Parents.ToList(); - baseVersion.Version = new BaseVersion( - baseVersion.Version.Source, - baseVersion.Version.ShouldIncrement, - baseVersion.Version.SemanticVersion, - this.repositoryStore.FindMergeBase(parents[0], parents[1]), - baseVersion.Version.BranchNameOverride); - } - } - } - } - - private bool ReleaseBranchExistsInRepo() - { - var releaseBranchConfig = context.FullConfiguration.GetReleaseBranchConfig(); - var releaseBranches = this.repositoryStore.GetReleaseBranches(releaseBranchConfig); - return releaseBranches.Any(); - } - - private class Versions - { - public SemanticVersion IncrementedVersion { get; set; } - public BaseVersion Version { get; set; } - - public override string ToString() => $"{Version} | {IncrementedVersion}"; - } -} diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/ConfigNextVersionVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/ConfigNextVersionVersionStrategy.cs index f0945ecb35..895e451c17 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/ConfigNextVersionVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/ConfigNextVersionVersionStrategy.cs @@ -16,10 +16,10 @@ public ConfigNextVersionVersionStrategy(Lazy versionContext) public override IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) { - var nextVersion = Context.FullConfiguration.NextVersion; + var nextVersion = Context.Configuration.NextVersion; if (!nextVersion.IsNullOrEmpty() && !Context.IsCurrentCommitTagged) { - var semanticVersion = SemanticVersion.Parse(nextVersion, Context.FullConfiguration.TagPrefix); + var semanticVersion = SemanticVersion.Parse(nextVersion, Context.Configuration.TagPrefix); yield return new BaseVersion("NextVersion in GitVersion configuration file", false, semanticVersion, null, null); } } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs index e1e7f5ad46..81b57f4322 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs @@ -1,4 +1,3 @@ -using GitVersion.Common; using GitVersion.Model.Configuration; namespace GitVersion.VersionCalculation; @@ -8,22 +7,10 @@ namespace GitVersion.VersionCalculation; /// BaseVersionSource is the "root" commit reachable from the current commit. /// Does not increment. /// -public class FallbackVersionStrategy : VersionStrategyBase +public class FallbackVersionStrategy : IVersionStrategy { - private readonly IRepositoryStore repositoryStore; - - public FallbackVersionStrategy(IRepositoryStore repositoryStore, Lazy versionContext) : base(versionContext) => this.repositoryStore = repositoryStore; - - public override IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) + public virtual IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) { - var currentBranchTip = Context.CurrentBranch.Tip; - if (currentBranchTip == null) - { - throw new GitVersionException("No commits found on the current branch."); - } - - var baseVersionSource = this.repositoryStore.GetBaseVersionSource(currentBranchTip); - - yield return new BaseVersion("Fallback base version", false, new SemanticVersion(minor: 1), baseVersionSource, null); + yield return new BaseVersion("Fallback base version", true, new SemanticVersion(), null, null); } } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs index 12ae7565eb..6c106330e6 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; +using GitVersion.Common; using GitVersion.Configuration; using GitVersion.Extensions; using GitVersion.Logging; @@ -15,9 +16,13 @@ namespace GitVersion.VersionCalculation; public class MergeMessageVersionStrategy : VersionStrategyBase { private readonly ILog log; + private readonly IRepositoryStore repositoryStore; - public MergeMessageVersionStrategy(ILog log, Lazy versionContext) - : base(versionContext) => this.log = log.NotNull(); + public MergeMessageVersionStrategy(ILog log, Lazy versionContext, IRepositoryStore repositoryStore) : base(versionContext) + { + this.log = log.NotNull(); + this.repositoryStore = repositoryStore.NotNull(); + } public override IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) { @@ -30,14 +35,32 @@ public override IEnumerable GetBaseVersions(EffectiveBranchConfigur { if (TryParse(c, Context, out var mergeMessage) && mergeMessage.Version != null && - Context.FullConfiguration.IsReleaseBranch(TrimRemote(mergeMessage.MergedBranch))) + Context.Configuration.IsReleaseBranch(TrimRemote(mergeMessage.MergedBranch))) { this.log.Info($"Found commit [{Context.CurrentCommit}] matching merge message format: {mergeMessage.FormatName}"); var shouldIncrement = !configuration.Value.PreventIncrementOfMergedBranchVersion; - return new[] + + var message = c.Message.Trim(); + + var baseVersionSource = c; + + if (shouldIncrement) { - new BaseVersion($"{MergeMessageStrategyPrefix} '{c.Message.Trim()}'", shouldIncrement, mergeMessage.Version, c, null) - }; + var parents = c.Parents.ToArray(); + if (parents.Length == 2 && message.Contains("Merge branch") && message.Contains("release")) + { + baseVersionSource = this.repositoryStore.FindMergeBase(parents[0], parents[1]); + } + } + + var baseVersion = new BaseVersion( + source: $"{MergeMessageStrategyPrefix} '{message}'", + shouldIncrement: shouldIncrement, + semanticVersion: mergeMessage.Version, + baseVersionSource: baseVersionSource, + branchNameOverride: null + ); + return new[] { baseVersion }; } return Enumerable.Empty(); }) @@ -61,7 +84,7 @@ private static bool TryParse(ICommit mergeCommit, GitVersionContext context, [No return null; } - var mergeMessage = new MergeMessage(mergeCommit.Message, context.FullConfiguration); + var mergeMessage = new MergeMessage(mergeCommit.Message, context.Configuration); return mergeMessage; } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs index 9157b4a2a8..8695402dce 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs @@ -23,7 +23,7 @@ internal IEnumerable GetTaggedVersions(IBranch currentBranch, DateT { if (currentBranch is null) return Enumerable.Empty(); - var versionTags = this.repositoryStore.GetValidVersionTags(Context.FullConfiguration.TagPrefix, olderThan); + var versionTags = this.repositoryStore.GetValidVersionTags(Context.Configuration.TagPrefix, olderThan); var versionTagsByCommit = versionTags.ToLookup(vt => vt.Item3.Id.Sha); var commitsOnBranch = currentBranch.Commits; if (commitsOnBranch == null) diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs index 2053c2aaa8..8bdb686f69 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs @@ -41,7 +41,7 @@ public override IEnumerable GetBaseVersions(EffectiveBranchConfigur private IEnumerable MainTagsVersions() { - var configuration = Context.FullConfiguration; + var configuration = Context.Configuration; var mainBranch = this.repositoryStore.FindMainBranch(configuration); return mainBranch != null @@ -51,7 +51,7 @@ private IEnumerable MainTagsVersions() private IEnumerable ReleaseBranchBaseVersions() { - var releaseBranchConfig = Context.FullConfiguration.GetReleaseBranchConfig(); + var releaseBranchConfig = Context.Configuration.GetReleaseBranchConfig(); if (!releaseBranchConfig.Any()) return Array.Empty(); @@ -77,12 +77,6 @@ private IEnumerable GetReleaseVersion(IBranch releaseBranch) { // Find the commit where the child branch was created. var baseSource = this.repositoryStore.FindMergeBase(releaseBranch, Context.CurrentBranch); - if (Equals(baseSource, Context.CurrentCommit)) - { - // Ignore the branch if it has no commits. - return Array.Empty(); - } - var configuration = Context.GetEffectiveConfiguration(releaseBranch); return this.releaseVersionStrategy .GetBaseVersions(new(releaseBranch, configuration)) diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs index 85fb716945..1061eaa215 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs @@ -20,12 +20,12 @@ public VersionInBranchNameVersionStrategy(IRepositoryStore repositoryStore, Lazy public override IEnumerable GetBaseVersions(EffectiveBranchConfiguration configuration) { string nameWithoutOrigin = NameWithoutOrigin(configuration.Branch); - if (Context.FullConfiguration.IsReleaseBranch(nameWithoutOrigin)) + if (Context.Configuration.IsReleaseBranch(nameWithoutOrigin)) { - var versionInBranch = GetVersionInBranch(configuration.Branch.Name.Friendly, Context.FullConfiguration.TagPrefix); + var versionInBranch = GetVersionInBranch(configuration.Branch.Name.Friendly, Context.Configuration.TagPrefix); if (versionInBranch != null) { - var commitBranchWasBranchedFrom = this.repositoryStore.FindCommitBranchWasBranchedFrom(configuration.Branch, Context.FullConfiguration); + var commitBranchWasBranchedFrom = this.repositoryStore.FindCommitBranchWasBranchedFrom(configuration.Branch, Context.Configuration); var branchNameOverride = Context.CurrentBranch.Name.Friendly.RegexReplace("[-/]" + versionInBranch.Item1, string.Empty); yield return new BaseVersion("Version in branch name", false, versionInBranch.Item2, commitBranchWasBranchedFrom.Commit, branchNameOverride); } diff --git a/src/GitVersion.Core/VersionCalculation/EffectiveBranchConfigurationFinder.cs b/src/GitVersion.Core/VersionCalculation/EffectiveBranchConfigurationFinder.cs new file mode 100644 index 0000000000..f5cce1ee0e --- /dev/null +++ b/src/GitVersion.Core/VersionCalculation/EffectiveBranchConfigurationFinder.cs @@ -0,0 +1,77 @@ +using GitVersion.Common; +using GitVersion.Configuration; +using GitVersion.Extensions; +using GitVersion.Logging; +using GitVersion.Model.Configuration; + +namespace GitVersion.VersionCalculation; + +public class EffectiveBranchConfigurationFinder : IEffectiveBranchConfigurationFinder +{ + private readonly ILog log; + private readonly IRepositoryStore repositoryStore; + + public EffectiveBranchConfigurationFinder(ILog log, IRepositoryStore repositoryStore) + { + this.log = log.NotNull(); + this.repositoryStore = repositoryStore.NotNull(); + } + + public virtual IEnumerable GetConfigurations(IBranch branch, Config configuration) + { + branch.NotNull(); + configuration.NotNull(); + + return GetEffectiveConfigurationsRecursive(branch, configuration, null, new()); + } + + private IEnumerable GetEffectiveConfigurationsRecursive( + IBranch branch, Config configuration, BranchConfig? childBranchConfiguration, HashSet traversedBranches) + { + if (!traversedBranches.Add(branch)) yield break; // This should never happen!! But it is good to have a circuit breaker. + + var branchConfiguration = configuration.GetBranchConfiguration(branch); + if (childBranchConfiguration != null) + { + branchConfiguration = childBranchConfiguration.Inherit(branchConfiguration); + } + + var sourceBranches = Array.Empty(); + if (branchConfiguration.Increment == IncrementStrategy.Inherit) + { + // At this point we need to check if source branches are available. + sourceBranches = this.repositoryStore.GetSourceBranches(branch, configuration, traversedBranches).ToArray(); + + if (sourceBranches.Length == 0) + { + // Because the actual branch is marked with the inherit increment strategy we need to either skip the iteration or go further + // while inheriting from the fallback branch configuration. This behavior is configurable via the increment settings of the configuration. + var fallbackBranchConfiguration = configuration.GetFallbackBranchConfiguration(); + var skipTraversingOfOrphanedBranches = fallbackBranchConfiguration.Increment == null + || fallbackBranchConfiguration.Increment == IncrementStrategy.Inherit; + this.log.Info( + $"An orphaned branch '{branch}' has been detected and will be skipped={skipTraversingOfOrphanedBranches}." + ); + if (skipTraversingOfOrphanedBranches) yield break; + } + } + + if (branchConfiguration.Increment == IncrementStrategy.Inherit && sourceBranches.Any()) + { + foreach (var sourceBranch in sourceBranches) + { + foreach (var effectiveConfiguration + in GetEffectiveConfigurationsRecursive(sourceBranch, configuration, branchConfiguration, traversedBranches)) + { + yield return effectiveConfiguration; + } + } + } + else + { + var fallbackBranchConfiguration = configuration.GetFallbackBranchConfiguration(); + branchConfiguration = branchConfiguration.Inherit(fallbackBranchConfiguration); + yield return new(branch, new EffectiveConfiguration(configuration, branchConfiguration)); + } + } +} diff --git a/src/GitVersion.Core/VersionCalculation/IEffectiveBranchConfigurationFinder.cs b/src/GitVersion.Core/VersionCalculation/IEffectiveBranchConfigurationFinder.cs new file mode 100644 index 0000000000..dc5acaa59c --- /dev/null +++ b/src/GitVersion.Core/VersionCalculation/IEffectiveBranchConfigurationFinder.cs @@ -0,0 +1,9 @@ +using GitVersion.Model.Configuration; + +namespace GitVersion.VersionCalculation +{ + public interface IEffectiveBranchConfigurationFinder + { + IEnumerable GetConfigurations(IBranch branch, Config configuration); + } +} diff --git a/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs b/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs index de0e7b5443..45c141c925 100644 --- a/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs +++ b/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs @@ -53,7 +53,7 @@ public VersionField DetermineIncrementedField(GitVersionContext context, BaseVer return defaultIncrement; } - return (VersionField)commitMessageIncrement; + return commitMessageIncrement ?? VersionField.None; } public VersionField? GetIncrementForCommits(Config configuration, IEnumerable commits) @@ -97,7 +97,7 @@ public VersionField DetermineIncrementedField(GitVersionContext context, BaseVer commits = commits.Where(c => c.Parents.Count() > 1); } - return GetIncrementForCommits(context.FullConfiguration, commits); + return GetIncrementForCommits(context.Configuration, commits); } private static Regex TryGetRegexOrDefault(string? messageRegex, Regex defaultRegex) => diff --git a/src/GitVersion.Core/VersionCalculation/MainlineVersionCalculator.cs b/src/GitVersion.Core/VersionCalculation/MainlineVersionCalculator.cs index fd1c9d8b15..6fdf1b28fc 100644 --- a/src/GitVersion.Core/VersionCalculation/MainlineVersionCalculator.cs +++ b/src/GitVersion.Core/VersionCalculation/MainlineVersionCalculator.cs @@ -52,7 +52,7 @@ public SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion) var mainlineCommitLog = this.repositoryStore.GetMainlineCommitLog(baseVersion.BaseVersionSource, mainlineTip).ToList(); var directCommits = new List(mainlineCommitLog.Count); - var nextVersion = context.FullConfiguration.NextVersion; + var nextVersion = context.Configuration.NextVersion; if (nextVersion.IsNullOrEmpty()) { // Scans commit log in reverse, aggregating merge commits @@ -79,16 +79,28 @@ public SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion) mainlineVersion = mainlineVersion.IncrementVersion(branchIncrement); } - return mainlineVersion; } } public SemanticVersionBuildMetaData CreateVersionBuildMetaData(ICommit? baseVersionSource) { - var commitLog = this.repositoryStore.GetCommitLog(baseVersionSource, context.CurrentCommit); - var commitsSinceTag = commitLog.Count(); - this.log.Info($"{commitsSinceTag} commits found between {baseVersionSource} and {context.CurrentCommit}"); + int commitsSinceTag = 0; + if (context.CurrentCommit != null) + { + var commitLogs = this.repositoryStore.GetCommitLog(baseVersionSource, context.CurrentCommit); + + var ignore = context.Configuration.Ignore; + if (!ignore.IsEmpty) + { + var shasToIgnore = new HashSet(ignore.ShAs); + commitLogs = commitLogs + .Where(c => ignore.Before is null || c.When > ignore.Before && !shasToIgnore.Contains(c.Sha)); + } + commitsSinceTag = commitLogs.Count(); + + this.log.Info($"{commitsSinceTag} commits found between {baseVersionSource} and {context.CurrentCommit}"); + } var shortSha = context.CurrentCommit?.Id.ToString(7); return new SemanticVersionBuildMetaData( @@ -123,17 +135,23 @@ private SemanticVersion AggregateMergeCommitIncrement(ICommit commit, List b.Value?.IsMainline == true).ToList(); + if (context.Configuration.Branches.TryGetValue(context.CurrentBranch.Name.Friendly, out var branchConfig) + && branchConfig.IsMainline == true) + { + return context.CurrentBranch; + } + + IDictionary>? mainlineBranches = null; - IDictionary> mainlineBranches = new Dictionary>(); if (context.CurrentCommit != null) { - mainlineBranches = this.repositoryStore.GetMainlineBranches(context.CurrentCommit, context.FullConfiguration, mainlineBranchConfigs); + mainlineBranches = this.repositoryStore.GetMainlineBranches(context.CurrentCommit, context.Configuration); } + mainlineBranches ??= new Dictionary>(); if (!mainlineBranches.Any()) { - var mainlineBranchConfigsString = string.Join(", ", mainlineBranchConfigs.Where(x => x.Value != null).Select(b => b.Value?.Name)); + var mainlineBranchConfigsString = string.Join(", ", context.Configuration.Branches.Where(b => b.Value?.IsMainline == true).Select(b => b.Value.Name)); throw new WarningException($"No branches can be found matching the commit {context.CurrentCommit?.Sha} in the configured Mainline branches: {mainlineBranchConfigsString}"); } @@ -259,8 +277,8 @@ private SemanticVersion IncrementForEachCommit(IEnumerable directCommit { foreach (var directCommit in directCommits) { - var directCommitIncrement = this.incrementStrategyFinder.GetIncrementForCommits(context.FullConfiguration, new[] { directCommit }) - ?? FindDefaultIncrementForBranch(context, mainline.Name.Friendly); + var directCommitIncrement = this.incrementStrategyFinder.GetIncrementForCommits(context.Configuration, new[] { directCommit }) + ?? FindDefaultIncrementForBranch(context, mainline.Name.Friendly); mainlineVersion = mainlineVersion.IncrementVersion(directCommitIncrement); this.log.Info($"Direct commit on main {directCommit} incremented base versions {directCommitIncrement}, now {mainlineVersion}"); } @@ -272,18 +290,19 @@ private VersionField FindMessageIncrement(ICommit? mergeCommit, ICommit? mergedH { var commits = this.repositoryStore.GetMergeBaseCommits(mergeCommit, mergedHead, findMergeBase); commitLog.RemoveAll(c => commits.Any(c1 => c1.Sha == c.Sha)); - return this.incrementStrategyFinder.GetIncrementForCommits(context.FullConfiguration, commits) ?? TryFindIncrementFromMergeMessage(mergeCommit); + return this.incrementStrategyFinder.GetIncrementForCommits(context.Configuration, commits) + ?? TryFindIncrementFromMergeMessage(mergeCommit); } private VersionField TryFindIncrementFromMergeMessage(ICommit? mergeCommit) { if (mergeCommit != null) { - var mergeMessage = new MergeMessage(mergeCommit.Message, context.FullConfiguration); - var config = context.FullConfiguration.GetBranchConfiguration(mergeMessage.MergedBranch); - if (config?.Increment != null && config.Increment != IncrementStrategy.Inherit) + var mergeMessage = new MergeMessage(mergeCommit.Message, context.Configuration); + var configuration = context.Configuration.GetBranchConfiguration(mergeMessage.MergedBranch); + if (configuration.Increment != null && configuration.Increment != IncrementStrategy.Inherit) { - return config.Increment.Value.ToVersionField(); + return configuration.Increment.Value.ToVersionField(); } } @@ -293,12 +312,15 @@ private VersionField TryFindIncrementFromMergeMessage(ICommit? mergeCommit) private static VersionField FindDefaultIncrementForBranch(GitVersionContext context, string? branchName = null) { - var config = context.FullConfiguration.GetBranchConfiguration(branchName ?? context.CurrentBranch.Name.WithoutRemote); - if (config?.Increment != null && config.Increment != IncrementStrategy.Inherit) + var configuration = context.Configuration.GetBranchConfiguration(branchName ?? context.CurrentBranch.Name.WithoutRemote); + if (configuration.Increment != null && configuration.Increment != IncrementStrategy.Inherit) { - return config.Increment.Value.ToVersionField(); + return configuration.Increment.Value.ToVersionField(); } + // TODO: Hardcoded fallback values are not so good. It might be better to get this information either from the fallback or the unknown + // branch configuration settings I have introduced. We should think about it: This is a cooking machine... the ingredients are coming from the user. ;) + // Fallback to patch return VersionField.Patch; } diff --git a/src/GitVersion.Core/VersionCalculation/NextVersion.cs b/src/GitVersion.Core/VersionCalculation/NextVersion.cs index 4d23c16e0d..32eb366cb4 100644 --- a/src/GitVersion.Core/VersionCalculation/NextVersion.cs +++ b/src/GitVersion.Core/VersionCalculation/NextVersion.cs @@ -3,9 +3,9 @@ namespace GitVersion.VersionCalculation; -public class NextVersion +public class NextVersion : IComparable, IEquatable { - public BaseVersion BaseVersion { get; set; } + public BaseVersion BaseVersion { get; } public SemanticVersion IncrementedVersion { get; } @@ -26,5 +26,25 @@ public NextVersion(SemanticVersion incrementedVersion, BaseVersion baseVersion, Branch = branch.NotNull(); } + public int CompareTo(NextVersion other) => IncrementedVersion.CompareTo(other.IncrementedVersion); + + public static bool operator ==(NextVersion left, NextVersion right) => left.CompareTo(right) == 0; + + public static bool operator !=(NextVersion left, NextVersion right) => left.CompareTo(right) != 0; + + public static bool operator <(NextVersion left, NextVersion right) => left.CompareTo(right) < 0; + + public static bool operator <=(NextVersion left, NextVersion right) => left.CompareTo(right) <= 0; + + public static bool operator >(NextVersion left, NextVersion right) => left.CompareTo(right) > 0; + + public static bool operator >=(NextVersion left, NextVersion right) => left.CompareTo(right) >= 0; + + public bool Equals(NextVersion other) => this == other; + + public override bool Equals(object other) => other is NextVersion nextVersion && Equals(nextVersion); + public override string ToString() => $"{BaseVersion} | {IncrementedVersion}"; + + public override int GetHashCode() => ToString().GetHashCode(); } diff --git a/src/GitVersion.Core/VersionCalculation/NextVersionCalculator.cs b/src/GitVersion.Core/VersionCalculation/NextVersionCalculator.cs index 93d38e3799..87f6f234d3 100644 --- a/src/GitVersion.Core/VersionCalculation/NextVersionCalculator.cs +++ b/src/GitVersion.Core/VersionCalculation/NextVersionCalculator.cs @@ -9,31 +9,34 @@ namespace GitVersion.VersionCalculation; public class NextVersionCalculator : INextVersionCalculator { private readonly ILog log; - private readonly IBaseVersionCalculator baseVersionCalculator; private readonly IMainlineVersionCalculator mainlineVersionCalculator; private readonly IRepositoryStore repositoryStore; - private readonly IIncrementStrategyFinder incrementStrategyFinder; private readonly Lazy versionContext; + private readonly IVersionStrategy[] versionStrategies; + private readonly IEffectiveBranchConfigurationFinder effectiveBranchConfigurationFinder; + private readonly IIncrementStrategyFinder incrementStrategyFinder; private GitVersionContext Context => this.versionContext.Value; - public NextVersionCalculator( - ILog log, - IBaseVersionCalculator baseVersionCalculator, - IMainlineVersionCalculator mainlineVersionCalculator, - IRepositoryStore repositoryStore, - IIncrementStrategyFinder incrementStrategyFinder, - Lazy versionContext) + public NextVersionCalculator(ILog log, + IMainlineVersionCalculator mainlineVersionCalculator, + IRepositoryStore repositoryStore, + Lazy versionContext, + IEnumerable versionStrategies, + IEffectiveBranchConfigurationFinder effectiveBranchConfigurationFinder, + IIncrementStrategyFinder incrementStrategyFinder) { this.log = log.NotNull(); - this.baseVersionCalculator = baseVersionCalculator.NotNull(); this.mainlineVersionCalculator = mainlineVersionCalculator.NotNull(); this.repositoryStore = repositoryStore.NotNull(); this.incrementStrategyFinder = incrementStrategyFinder.NotNull(); this.versionContext = versionContext.NotNull(); + this.versionStrategies = versionStrategies.NotNull().ToArray(); + this.effectiveBranchConfigurationFinder = effectiveBranchConfigurationFinder.NotNull(); + this.incrementStrategyFinder = incrementStrategyFinder.NotNull(); } - public NextVersion FindVersion() + public virtual NextVersion FindVersion() { this.log.Info($"Running against branch: {Context.CurrentBranch} ({Context.CurrentCommit?.ToString() ?? "-"})"); if (Context.IsCurrentCommitTagged) @@ -45,45 +48,50 @@ public NextVersion FindVersion() EnsureHeadIsNotDetached(Context); } + // TODO: It is totally unimportant that the current commit has been tagged or not IMO. We can make a double check actually if the result + // is the same or make it configurable but each run should be deterministic.Even if the development process goes on the tagged commit + // should always calculating the same result. Otherwise something is wrong with the configuration or someone messed up the branching history. + SemanticVersion? taggedSemanticVersion = null; if (Context.IsCurrentCommitTagged) { // Will always be 0, don't bother with the +0 on tags - var semanticVersionBuildMetaData = this.mainlineVersionCalculator.CreateVersionBuildMetaData(Context.CurrentCommit); + var semanticVersionBuildMetaData = this.mainlineVersionCalculator.CreateVersionBuildMetaData(Context.CurrentCommit!); semanticVersionBuildMetaData.CommitsSinceTag = null; - var semanticVersion = new SemanticVersion(Context.CurrentCommitTaggedVersion) { BuildMetaData = semanticVersionBuildMetaData }; taggedSemanticVersion = semanticVersion; } - var (baseVersion, configuration) = this.baseVersionCalculator.GetBaseVersion(); - baseVersion.SemanticVersion.BuildMetaData = this.mainlineVersionCalculator.CreateVersionBuildMetaData(baseVersion.BaseVersionSource); + // + + var nextVersion = Calculate(Context.CurrentBranch, Context.Configuration); + nextVersion.BaseVersion.SemanticVersion.BuildMetaData = this.mainlineVersionCalculator.CreateVersionBuildMetaData(nextVersion.BaseVersion.BaseVersionSource); SemanticVersion semver; - if (Context.FullConfiguration.VersioningMode == VersioningMode.Mainline) + if (Context.Configuration.VersioningMode == VersioningMode.Mainline) { - semver = this.mainlineVersionCalculator.FindMainlineModeVersion(baseVersion); + semver = this.mainlineVersionCalculator.FindMainlineModeVersion(nextVersion.BaseVersion); } else { - if (taggedSemanticVersion?.BuildMetaData == null || (taggedSemanticVersion.BuildMetaData?.Sha != baseVersion.SemanticVersion.BuildMetaData.Sha)) + if (taggedSemanticVersion?.BuildMetaData == null || (taggedSemanticVersion.BuildMetaData?.Sha != nextVersion.BaseVersion.SemanticVersion.BuildMetaData.Sha)) { - semver = PerformIncrement(baseVersion, configuration.Value); - semver.BuildMetaData = this.mainlineVersionCalculator.CreateVersionBuildMetaData(baseVersion.BaseVersionSource); + semver = nextVersion.IncrementedVersion; + semver.BuildMetaData = this.mainlineVersionCalculator.CreateVersionBuildMetaData(nextVersion.BaseVersion.BaseVersionSource); } else { - semver = baseVersion.SemanticVersion; + semver = nextVersion.BaseVersion.SemanticVersion; } } - var hasPreReleaseTag = semver.PreReleaseTag?.HasTag() == true; - var tag = configuration.Value.Tag; + var hasPreReleaseTag = semver.HasPreReleaseTagWithLabel; + var tag = nextVersion.Configuration.Tag; var branchConfigHasPreReleaseTagConfigured = !tag.IsNullOrEmpty(); var preReleaseTagDoesNotMatchConfiguration = hasPreReleaseTag && branchConfigHasPreReleaseTagConfigured && semver.PreReleaseTag?.Name != tag; if (semver.PreReleaseTag?.HasTag() != true && branchConfigHasPreReleaseTagConfigured || preReleaseTagDoesNotMatchConfiguration) { - UpdatePreReleaseTag(configuration.Value, semver, baseVersion.BranchNameOverride); + UpdatePreReleaseTag(new(nextVersion.Branch, nextVersion.Configuration), semver, nextVersion.BaseVersion.BranchNameOverride); } if (taggedSemanticVersion != null) @@ -100,31 +108,23 @@ public NextVersion FindVersion() } } - var incrementedVersion = taggedSemanticVersion ?? semver; - return new(incrementedVersion, baseVersion, configuration); + return new(taggedSemanticVersion ?? semver, nextVersion.BaseVersion, new(nextVersion.Branch, nextVersion.Configuration)); } - private SemanticVersion PerformIncrement(BaseVersion baseVersion, EffectiveConfiguration configuration) + private void UpdatePreReleaseTag(EffectiveBranchConfiguration configuration, SemanticVersion semanticVersion, string? branchNameOverride) { - var semver = baseVersion.SemanticVersion; - var increment = this.incrementStrategyFinder.DetermineIncrementedField(Context, baseVersion, configuration); - semver = semver.IncrementVersion(increment); - return semver; - } - - private void UpdatePreReleaseTag(EffectiveConfiguration configuration, SemanticVersion semanticVersion, string? branchNameOverride) - { - var tagToUse = configuration.GetBranchSpecificTag(this.log, Context.CurrentBranch.Name.Friendly, branchNameOverride); + var tagToUse = configuration.Value.GetBranchSpecificTag(this.log, Context.CurrentBranch.Name.Friendly, branchNameOverride); long? number = null; + // TODO: Please update the pre release-tag in the IVersionStrategy implementation. var lastTag = this.repositoryStore - .GetVersionTagsOnBranch(Context.CurrentBranch, Context.FullConfiguration.TagPrefix) + .GetVersionTagsOnBranch(Context.CurrentBranch, Context.Configuration.TagPrefix) .FirstOrDefault(v => v.PreReleaseTag?.Name?.IsEquivalentTo(tagToUse) == true); - if (lastTag != null && MajorMinorPatchEqual(lastTag, semanticVersion) && lastTag.PreReleaseTag?.HasTag() == true) + if (lastTag != null && MajorMinorPatchEqual(lastTag, semanticVersion) && lastTag.HasPreReleaseTagWithLabel) { - number = lastTag.PreReleaseTag.Number + 1; + number = lastTag.PreReleaseTag!.Number + 1; } number ??= 1; @@ -146,4 +146,141 @@ private static void EnsureHeadIsNotDetached(GitVersionContext context) } private static bool MajorMinorPatchEqual(SemanticVersion lastTag, SemanticVersion baseVersion) => lastTag.Major == baseVersion.Major && lastTag.Minor == baseVersion.Minor && lastTag.Patch == baseVersion.Patch; + + private NextVersion Calculate(IBranch branch, Config configuration) + { + using (log.IndentLog("Calculating the base versions")) + { + var nextVersions = GetNextVersions(branch, configuration).ToArray(); + var maxVersion = nextVersions.Max(); + + var matchingVersionsOnceIncremented = nextVersions + .Where(v => v.BaseVersion.BaseVersionSource != null && v.IncrementedVersion == maxVersion.IncrementedVersion) + .ToList(); + ICommit? latestBaseVersionSource; + + if (matchingVersionsOnceIncremented.Any()) + { + static NextVersion CompareVersions( + NextVersion versions1, + NextVersion version2) + { + if (versions1.BaseVersion.BaseVersionSource == null) + { + return version2; + } + if (version2.BaseVersion.BaseVersionSource == null) + { + return versions1; + } + + return versions1.BaseVersion.BaseVersionSource.When + < version2.BaseVersion.BaseVersionSource.When ? versions1 : version2; + } + + var latestVersion = matchingVersionsOnceIncremented.Aggregate(CompareVersions); + latestBaseVersionSource = latestVersion.BaseVersion.BaseVersionSource; + maxVersion = latestVersion; + log.Info($"Found multiple base versions which will produce the same SemVer ({maxVersion.IncrementedVersion})," + + $" taking oldest source for commit counting ({latestVersion.BaseVersion.Source})"); + } + else + { + IEnumerable filteredVersions = nextVersions; + if (!maxVersion.IncrementedVersion.HasPreReleaseTagWithLabel) + { + // If the maximal version has no pre-release tag defined than we want to determine just the latest previous + // base source which are not coming from pre-release tag. + filteredVersions = filteredVersions.Where(v => !v.BaseVersion.SemanticVersion.HasPreReleaseTagWithLabel); + } + + var version = filteredVersions + .Where(v => v.BaseVersion.BaseVersionSource != null) + .OrderByDescending(v => v.IncrementedVersion) + .ThenByDescending(v => v.BaseVersion.BaseVersionSource!.When) + .FirstOrDefault(); + + version ??= filteredVersions.Where(v => v.BaseVersion.BaseVersionSource == null) + .OrderByDescending(v => v.IncrementedVersion) + .First(); + latestBaseVersionSource = version.BaseVersion.BaseVersionSource; + } + + var calculatedBase = new BaseVersion( + maxVersion.BaseVersion.Source, + maxVersion.BaseVersion.ShouldIncrement, + maxVersion.BaseVersion.SemanticVersion, + latestBaseVersionSource, + maxVersion.BaseVersion.BranchNameOverride + ); + + log.Info($"Base version used: {calculatedBase}"); + + var nextVersion = new NextVersion(maxVersion.IncrementedVersion, calculatedBase, maxVersion.Branch, maxVersion.Configuration); + + return nextVersion; + } + } + + private IEnumerable GetNextVersions(IBranch branch, Config configuration) + { + if (branch.Tip == null) + throw new GitVersionException("No commits found on the current branch."); + + bool atLeastOneBaseVersionReturned = false; + + foreach (var effectiveBranchConfiguration in effectiveBranchConfigurationFinder.GetConfigurations(branch, configuration)) + { + foreach (var versionStrategy in this.versionStrategies) + { + foreach (var baseVersion in versionStrategy.GetBaseVersions(effectiveBranchConfiguration)) + { + log.Info(baseVersion.ToString()); + if (IncludeVersion(baseVersion, configuration.Ignore)) + { + var incrementStrategy = incrementStrategyFinder.DetermineIncrementedField( + context: Context, + baseVersion: baseVersion, + configuration: effectiveBranchConfiguration.Value + ); + var incrementedVersion = incrementStrategy == VersionField.None + ? baseVersion.SemanticVersion + : baseVersion.SemanticVersion.IncrementVersion(incrementStrategy); + + if (configuration.VersioningMode == VersioningMode.Mainline) + { + if (!(incrementedVersion.PreReleaseTag?.HasTag() != true)) + { + continue; + } + } + + yield return effectiveBranchConfiguration.CreateNextVersion(baseVersion, incrementedVersion); + atLeastOneBaseVersionReturned = true; + } + } + } + } + + if (!atLeastOneBaseVersionReturned) + { + throw new GitVersionException("No base versions determined on the current branch."); + } + } + + private bool IncludeVersion(BaseVersion baseVersion, IgnoreConfig ignoreConfiguration) + { + foreach (var versionFilter in ignoreConfiguration.ToFilters()) + { + if (versionFilter.Exclude(baseVersion, out var reason)) + { + if (reason != null) + { + log.Info(reason); + } + return false; + } + } + return true; + } } diff --git a/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersion.cs b/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersion.cs index 19a29257f3..7918fe5e6d 100644 --- a/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersion.cs +++ b/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersion.cs @@ -24,6 +24,8 @@ public class SemanticVersion : IFormattable, IComparable, IEqua public SemanticVersionPreReleaseTag? PreReleaseTag; public SemanticVersionBuildMetaData? BuildMetaData; + public bool HasPreReleaseTagWithLabel => PreReleaseTag?.HasTag() == true; + public SemanticVersion(long major = 0, long minor = 0, long patch = 0) { this.Major = major; diff --git a/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersionBuildMetaData.cs b/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersionBuildMetaData.cs index 520b302843..30c11d3ff1 100644 --- a/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersionBuildMetaData.cs +++ b/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersionBuildMetaData.cs @@ -80,7 +80,7 @@ public string ToString(string? format, IFormatProvider? formatProvider) format = format.ToLower(); return format.ToLower() switch { - "b" => this.CommitsSinceTag.ToString(), + "b" => $"{this.CommitsSinceTag}", "s" => $"{this.CommitsSinceTag}{(this.Sha.IsNullOrEmpty() ? null : ".Sha." + this.Sha)}".TrimStart('.'), "f" => $"{this.CommitsSinceTag}{(this.Branch.IsNullOrEmpty() ? null : ".Branch." + FormatMetaDataPart(this.Branch))}{(this.Sha.IsNullOrEmpty() ? null : ".Sha." + this.Sha)}{(this.OtherMetaData.IsNullOrEmpty() ? null : "." + FormatMetaDataPart(this.OtherMetaData))}".TrimStart('.'), _ => throw new FormatException($"Unknown format '{format}'.") diff --git a/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersionPreReleaseTag.cs b/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersionPreReleaseTag.cs index 759eead96d..b199f683db 100644 --- a/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersionPreReleaseTag.cs +++ b/src/GitVersion.Core/VersionCalculation/SemanticVersioning/SemanticVersionPreReleaseTag.cs @@ -33,7 +33,9 @@ public SemanticVersionPreReleaseTag(SemanticVersionPreReleaseTag? preReleaseTag) } public string? Name { get; set; } + public long? Number { get; set; } + public bool? PromotedFromCommits { get; set; } public override bool Equals(object? obj) => Equals(obj as SemanticVersionPreReleaseTag); diff --git a/src/GitVersion.Core/VersionCalculation/VariableProvider.cs b/src/GitVersion.Core/VersionCalculation/VariableProvider.cs index 793c30ff8a..20cfbf1f9e 100644 --- a/src/GitVersion.Core/VersionCalculation/VariableProvider.cs +++ b/src/GitVersion.Core/VersionCalculation/VariableProvider.cs @@ -31,6 +31,8 @@ public VersionVariables GetVariablesFor(SemanticVersion semanticVersion, Effecti semanticVersion.PreReleaseTag.Name = config.GetBranchSpecificTag(this.log, semanticVersion.BuildMetaData?.Branch, null); if (semanticVersion.PreReleaseTag.Name.IsNullOrEmpty()) { + // TODO: Why do we manipulating the semantic version here in the VariableProvider? The method name is GET not MANIPULATE. + // What is about the separation of concern and single-responsibility principle? semanticVersion.PreReleaseTag.Name = config.ContinuousDeploymentFallbackTag; } } @@ -46,6 +48,8 @@ public VersionVariables GetVariablesFor(SemanticVersion semanticVersion, Effecti var numberGroup = match.Groups["number"]; if (numberGroup.Success && semanticVersion.PreReleaseTag != null) { + // TODO: Why do we manipulating the semantic version here in the VariableProvider? The method name is GET not MANIPULATE. + // What is about the separation of concern and single-responsibility principle? semanticVersion.PreReleaseTag.Name += numberGroup.Value; } } @@ -53,6 +57,8 @@ public VersionVariables GetVariablesFor(SemanticVersion semanticVersion, Effecti if (isContinuousDeploymentMode || appendTagNumberPattern || config.VersioningMode == VersioningMode.Mainline) { + // TODO: Why do we manipulating the semantic version here in the VariableProvider? The method name is GET not MANIPULATE. + // What is about the separation of concern and single-responsibility principle? PromoteNumberOfCommitsToTagNumber(semanticVersion); } diff --git a/src/GitVersion.Core/VersionCalculation/VersionCalculationModule.cs b/src/GitVersion.Core/VersionCalculation/VersionCalculationModule.cs index 4b6f93e908..20d803f68b 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionCalculationModule.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionCalculationModule.cs @@ -10,9 +10,9 @@ public void RegisterTypes(IServiceCollection services) services.AddModule(new VersionStrategyModule()); services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); } }