diff --git a/src/GitVersionCore.Tests/IntegrationTests/DocumentationSamples.cs b/src/GitVersionCore.Tests/IntegrationTests/DocumentationSamples.cs index 7f07b14bc3..d61baa7d12 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/DocumentationSamples.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/DocumentationSamples.cs @@ -63,9 +63,9 @@ public void GitFlowPullRequestBranch() // Open Pull Request fixture.BranchTo("pull/2/merge", "pr"); fixture.SequenceDiagram.Activate("pull/2/merge"); - fixture.AssertFullSemver("1.3.0-PullRequest.2+1"); + fixture.AssertFullSemver("1.3.0-PullRequest0002.1"); fixture.MakeACommit(); - fixture.AssertFullSemver("1.3.0-PullRequest.2+2"); + fixture.AssertFullSemver("1.3.0-PullRequest0002.2"); // Merge into develop fixture.Checkout("develop"); @@ -365,9 +365,9 @@ public void GitHubFlowPullRequestBranch() // Open Pull Request fixture.BranchTo("pull/2/merge", "pr"); fixture.SequenceDiagram.Activate("pull/2/merge"); - fixture.AssertFullSemver("1.2.1-PullRequest.2+1"); + fixture.AssertFullSemver("1.2.1-PullRequest0002.1"); fixture.MakeACommit(); - fixture.AssertFullSemver("1.2.1-PullRequest.2+2"); + fixture.AssertFullSemver("1.2.1-PullRequest0002.2"); // Merge into master fixture.Checkout("master"); diff --git a/src/GitVersionCore.Tests/IntegrationTests/HotfixBranchScenarios.cs b/src/GitVersionCore.Tests/IntegrationTests/HotfixBranchScenarios.cs index 34db00ecd9..34234dbd36 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/HotfixBranchScenarios.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/HotfixBranchScenarios.cs @@ -103,8 +103,8 @@ public void PatchOlderReleaseExample() fixture.Repository.MakeACommit(); fixture.AssertFullSemver("1.1.1-fix.1+3"); - fixture.Repository.CreatePullRequestRef("feature/fix", "hotfix-1.1.1", normalise: true); - fixture.AssertFullSemver("1.1.1-PullRequest.2+4"); + fixture.Repository.CreatePullRequestRef("feature/fix", "hotfix-1.1.1", normalise: true, prNumber: 8); + fixture.AssertFullSemver("1.1.1-PullRequest0008.4"); fixture.Repository.Checkout("hotfix-1.1.1"); fixture.Repository.MergeNoFF("feature/fix", Generate.SignatureNow()); fixture.AssertFullSemver("1.1.1-beta.1+4"); diff --git a/src/GitVersionCore.Tests/IntegrationTests/MainlineDevelopmentMode.cs b/src/GitVersionCore.Tests/IntegrationTests/MainlineDevelopmentMode.cs index 6650600401..bf6806a170 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/MainlineDevelopmentMode.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/MainlineDevelopmentMode.cs @@ -6,6 +6,7 @@ using GitVersionCore.Tests; using LibGit2Sharp; using NUnit.Framework; +using LibGitExtensions = GitTools.LibGitExtensions; public class MainlineDevelopmentMode { @@ -24,18 +25,20 @@ public void MergedFeatureBranchesToMasterImpliesRelease() fixture.BranchTo("feature/foo", "foo"); fixture.MakeACommit("2"); - fixture.AssertFullSemver(config, "1.0.1-foo.1+1"); + fixture.AssertFullSemver(config, "1.0.1-foo.1"); + fixture.MakeACommit("2.1"); + fixture.AssertFullSemver(config, "1.0.1-foo.2"); fixture.Checkout("master"); fixture.MergeNoFF("feature/foo"); - fixture.AssertFullSemver(config, "1.0.1+2"); + fixture.AssertFullSemver(config, "1.0.1"); fixture.BranchTo("feature/foo2", "foo2"); fixture.MakeACommit("3 +semver: minor"); - fixture.AssertFullSemver(config, "1.1.0-foo2.1+3"); + fixture.AssertFullSemver(config, "1.1.0-foo2.1"); fixture.Checkout("master"); fixture.MergeNoFF("feature/foo2"); - fixture.AssertFullSemver(config, "1.1.0+4"); + fixture.AssertFullSemver(config, "1.1.0"); fixture.BranchTo("feature/foo3", "foo3"); fixture.MakeACommit("4"); @@ -48,41 +51,82 @@ public void MergedFeatureBranchesToMasterImpliesRelease() { AmendPreviousCommit = true }); - fixture.AssertFullSemver(config, "1.2.0+6"); + fixture.AssertFullSemver(config, "1.2.0"); fixture.BranchTo("feature/foo4", "foo4"); fixture.MakeACommit("5 +semver: major"); - fixture.AssertFullSemver(config, "2.0.0-foo4.1+7"); + fixture.AssertFullSemver(config, "2.0.0-foo4.1"); fixture.Checkout("master"); fixture.MergeNoFF("feature/foo4"); - fixture.AssertFullSemver(config, "2.0.0+8"); + fixture.AssertFullSemver(config, "2.0.0"); // We should evaluate any commits not included in merge commit calculations for direct commit/push or squash to merge commits fixture.MakeACommit("6 +semver: major"); - fixture.AssertFullSemver(config, "3.0.0+9"); + fixture.AssertFullSemver(config, "3.0.0"); fixture.MakeACommit("7 +semver: minor"); - fixture.AssertFullSemver(config, "3.1.0+10"); + fixture.AssertFullSemver(config, "3.1.0"); fixture.MakeACommit("8"); - fixture.AssertFullSemver(config, "3.1.1+11"); + fixture.AssertFullSemver(config, "3.1.1"); // Finally verify that the merge commits still function properly fixture.BranchTo("feature/foo5", "foo5"); fixture.MakeACommit("9 +semver: minor"); - fixture.AssertFullSemver(config, "3.2.0-foo5.1+12"); + fixture.AssertFullSemver(config, "3.2.0-foo5.1"); fixture.Checkout("master"); fixture.MergeNoFF("feature/foo5"); - fixture.AssertFullSemver(config, "3.2.0+13"); + fixture.AssertFullSemver(config, "3.2.0"); // One more direct commit for good measure fixture.MakeACommit("10 +semver: minor"); - fixture.AssertFullSemver(config, "3.3.0+14"); + fixture.AssertFullSemver(config, "3.3.0"); // And we can commit without bumping semver fixture.MakeACommit("11 +semver: none"); - fixture.AssertFullSemver(config, "3.3.0+15"); + fixture.AssertFullSemver(config, "3.3.0"); Console.WriteLine(fixture.SequenceDiagram.GetDiagram()); } } - // Write test which has a forward merge into a feature branch + + [Test] + public void VerifyPullRequestsActLikeContinuousDelivery() + { + using (var fixture = new EmptyRepositoryFixture()) + { + fixture.Repository.MakeACommit("1"); + fixture.MakeATaggedCommit("1.0.0"); + fixture.MakeACommit(); + fixture.AssertFullSemver(config, "1.0.1"); + + fixture.BranchTo("feature/foo", "foo"); + fixture.MakeACommit(); + fixture.MakeACommit(); + fixture.Repository.CreatePullRequestRef("feature/foo", "master", normalise: true, prNumber: 8); + fixture.AssertFullSemver(config, "1.0.2-PullRequest0008.3"); + } + } + + [Test] + [Ignore("Not passing yet")] + public void VerifyForwardMerge() + { + using (var fixture = new EmptyRepositoryFixture()) + { + fixture.Repository.MakeACommit("1"); + fixture.MakeATaggedCommit("1.0.0"); + fixture.MakeACommit(); // 1.0.1 + + fixture.BranchTo("feature/foo", "foo"); + fixture.MakeACommit(); + fixture.MakeACommit(); + fixture.AssertFullSemver(config, "1.0.2-foo.2"); + + fixture.Checkout("master"); + fixture.MakeACommit(); + fixture.AssertFullSemver(config, "1.0.2"); + fixture.Checkout("feature/foo"); + fixture.MergeNoFF("master"); + fixture.AssertFullSemver(config, "1.0.3-foo.3"); + } + } } static class CommitExtensions diff --git a/src/GitVersionCore.Tests/IntegrationTests/PullRequestScenarios.cs b/src/GitVersionCore.Tests/IntegrationTests/PullRequestScenarios.cs index c7f6d4f592..58a5c95def 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/PullRequestScenarios.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/PullRequestScenarios.cs @@ -19,7 +19,7 @@ public void CanCalculatePullRequestChanges() fixture.Repository.CreatePullRequestRef("feature/Foo", "master", normalise: true); fixture.Repository.DumpGraph(); - fixture.AssertFullSemver("0.1.1-PullRequest.2+2"); + fixture.AssertFullSemver("0.1.1-PullRequest0002.2"); } } @@ -37,7 +37,7 @@ public void CanCalculatePullRequestChangesInheritingConfig() fixture.Repository.CreatePullRequestRef("feature/Foo", "develop", 44, normalise: true); fixture.Repository.DumpGraph(); - fixture.AssertFullSemver("0.2.0-PullRequest.44+3"); + fixture.AssertFullSemver("0.2.0-PullRequest0044.3"); } } @@ -54,7 +54,7 @@ public void CanCalculatePullRequestChangesFromRemoteRepo() fixture.Repository.CreatePullRequestRef("feature/Foo", "master", normalise: true); fixture.Repository.DumpGraph(); - fixture.AssertFullSemver("0.1.1-PullRequest.2+2"); + fixture.AssertFullSemver("0.1.1-PullRequest0002.2"); } } @@ -71,7 +71,7 @@ public void CanCalculatePullRequestChangesInheritingConfigFromRemoteRepo() fixture.Repository.CreatePullRequestRef("feature/Foo", "develop", normalise: true); - fixture.AssertFullSemver("0.2.0-PullRequest.2+3"); + fixture.AssertFullSemver("0.2.0-PullRequest0002.3"); } } @@ -89,7 +89,7 @@ public void CanCalculatePullRequestChangesWhenThereAreMultipleMergeCandidates() fixture.Repository.CreatePullRequestRef("feature/Foo", "develop", normalise: true); - fixture.AssertFullSemver("0.2.0-PullRequest.2+3"); + fixture.AssertFullSemver("0.2.0-PullRequest0002.3"); } } @@ -106,7 +106,7 @@ public void CalculatesCorrectVersionAfterReleaseBranchMergedToMaster() fixture.Repository.CreatePullRequestRef("release/2.0.0", "master", normalise: true); - fixture.AssertFullSemver("2.0.0-PullRequest.2+0"); + fixture.AssertFullSemver("2.0.0-PullRequest0002.0"); } } } \ No newline at end of file diff --git a/src/GitVersionCore/OutputVariables/VariableProvider.cs b/src/GitVersionCore/OutputVariables/VariableProvider.cs index 6e0eb13a18..0f893d5b51 100644 --- a/src/GitVersionCore/OutputVariables/VariableProvider.cs +++ b/src/GitVersionCore/OutputVariables/VariableProvider.cs @@ -9,7 +9,8 @@ public static class VariableProvider { public static VersionVariables GetVariablesFor(SemanticVersion semanticVersion, EffectiveConfiguration config, bool isCurrentCommitTagged) { - if (config.VersioningMode == VersioningMode.ContinuousDeployment && !isCurrentCommitTagged) + var isContinuousDeploymentMode = config.VersioningMode == VersioningMode.ContinuousDeployment && !isCurrentCommitTagged; + if (isContinuousDeploymentMode) { semanticVersion = new SemanticVersion(semanticVersion); // Continuous Deployment always requires a pre-release tag unless the commit is tagged @@ -21,22 +22,23 @@ public static VersionVariables GetVariablesFor(SemanticVersion semanticVersion, semanticVersion.PreReleaseTag.Name = config.ContinuousDeploymentFallbackTag; } } + } - // Evaluate tag number pattern and append to prerelease tag, preserving build metadata - if (!string.IsNullOrEmpty(config.TagNumberPattern)) + // Evaluate tag number pattern and append to prerelease tag, preserving build metadata + var appendTagNumberPattern = !string.IsNullOrEmpty(config.TagNumberPattern) && semanticVersion.PreReleaseTag.HasTag(); + if (appendTagNumberPattern) + { + var match = Regex.Match(semanticVersion.BuildMetaData.Branch, config.TagNumberPattern); + var numberGroup = match.Groups["number"]; + if (numberGroup.Success) { - var match = Regex.Match(semanticVersion.BuildMetaData.Branch, config.TagNumberPattern); - var numberGroup = match.Groups["number"]; - if (numberGroup.Success) - { - semanticVersion.PreReleaseTag.Name += numberGroup.Value.PadLeft(config.BuildMetaDataPadding, '0'); - } + semanticVersion.PreReleaseTag.Name += numberGroup.Value.PadLeft(config.BuildMetaDataPadding, '0'); } + } - // For continuous deployment the commits since tag gets promoted to the pre-release number - semanticVersion.PreReleaseTag.Number = semanticVersion.BuildMetaData.CommitsSinceTag; - semanticVersion.BuildMetaData.CommitsSinceVersionSource = semanticVersion.BuildMetaData.CommitsSinceTag ?? 0; - semanticVersion.BuildMetaData.CommitsSinceTag = null; + if (isContinuousDeploymentMode || appendTagNumberPattern || config.VersioningMode == VersioningMode.Mainline) + { + PromoteNumberOfCommitsToTagNumber(semanticVersion); } var semverFormatValues = new SemanticVersionFormatValues(semanticVersion, config); @@ -87,5 +89,13 @@ public static VersionVariables GetVariablesFor(SemanticVersion semanticVersion, return variables; } + + static void PromoteNumberOfCommitsToTagNumber(SemanticVersion semanticVersion) + { + // For continuous deployment the commits since tag gets promoted to the pre-release number + semanticVersion.PreReleaseTag.Number = semanticVersion.BuildMetaData.CommitsSinceTag; + semanticVersion.BuildMetaData.CommitsSinceVersionSource = semanticVersion.BuildMetaData.CommitsSinceTag ?? 0; + semanticVersion.BuildMetaData.CommitsSinceTag = null; + } } } \ No newline at end of file diff --git a/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs index 2498e173de..f00d69f238 100644 --- a/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs @@ -44,16 +44,20 @@ public SemanticVersion FindVersion(GitVersionContext context) } var baseVersion = baseVersionFinder.GetBaseVersion(context); - var semver = context.Configuration.VersioningMode == VersioningMode.Mainline ? - FindMainlineModeVersion(baseVersion, context) : - PerformIncrement(context, baseVersion); + SemanticVersion semver; + if (context.Configuration.VersioningMode == VersioningMode.Mainline) + semver = FindMainlineModeVersion(baseVersion, context); + else + { + semver = PerformIncrement(context, baseVersion); + semver.BuildMetaData = metaDataCalculator.Create(baseVersion.BaseVersionSource, context); + } if (!semver.PreReleaseTag.HasTag() && !string.IsNullOrEmpty(context.Configuration.Tag)) { UpdatePreReleaseTag(context, semver, baseVersion.BranchNameOverride); } - semver.BuildMetaData = metaDataCalculator.Create(baseVersion.BaseVersionSource, context); if (taggedSemanticVersion != null) { @@ -76,7 +80,7 @@ private static SemanticVersion PerformIncrement(GitVersionContext context, BaseV return semver; } - private SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion, GitVersionContext context) + SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion, GitVersionContext context) { if (baseVersion.SemanticVersion.PreReleaseTag.HasTag()) { @@ -121,18 +125,26 @@ private SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion, GitVers if (context.CurrentBranch.FriendlyName != "master") { var mergedHead = context.CurrentCommit; - var findMergeBase = context.Repository.ObjectDatabase.FindMergeBase(context.CurrentCommit, context.Repository.FindBranch("master").Tip); + var second = context.Repository.FindBranch("master").Tip; + var findMergeBase = context.Repository.ObjectDatabase.FindMergeBase(context.CurrentCommit, second); Logger.WriteInfo(string.Format("Current branch ({0}) was branch from {1}", context.CurrentBranch.FriendlyName, findMergeBase)); - var branchIncrement = FindMessageIncrement(context, findMergeBase, mergedHead, findMergeBase, directCommits); + var branchIncrement = FindMessageIncrement(context, null, mergedHead, findMergeBase, directCommits); + // This will increment for any direct commits on master mainlineVersion = IncrementForEachCommit(context, directCommits, mainlineVersion); - Logger.WriteInfo(string.Format("Performing {0} increment for current branch ", branchIncrement)); - mainlineVersion = mainlineVersion.IncrementVersion(branchIncrement); + mainlineVersion.BuildMetaData = metaDataCalculator.Create(findMergeBase, context); + // Only increment if head is not a merge commit, ensures PR's and forward merges end up correct. + if (mergedHead.Parents.Count() == 1) + { + Logger.WriteInfo(string.Format("Performing {0} increment for current branch ", branchIncrement)); + mainlineVersion = mainlineVersion.IncrementVersion(branchIncrement); + } } else { // If we are on master, make sure no commits get left behind mainlineVersion = IncrementForEachCommit(context, directCommits, mainlineVersion); + mainlineVersion.BuildMetaData = metaDataCalculator.Create(baseVersion.BaseVersionSource, context); } return mainlineVersion; @@ -162,7 +174,9 @@ private static VersionField FindMessageIncrement( IncludeReachableFrom = mergedHead, ExcludeReachableFrom = findMergeBase }; - var commits = new[] { mergeCommit }.Union(context.Repository.Commits.QueryBy(filter)).ToList(); + var commits = mergeCommit == null ? + context.Repository.Commits.QueryBy(filter).ToList() : + new[] { mergeCommit }.Union(context.Repository.Commits.QueryBy(filter)).ToList(); commitLog.RemoveAll(c => commits.Any(c1 => c1.Sha == c.Sha)); return IncrementStrategyFinder.GetIncrementForCommits(context, commits) ?? VersionField.Patch; } @@ -180,22 +194,12 @@ void UpdatePreReleaseTag(GitVersionContext context, SemanticVersion semanticVers var tagToUse = GetBranchSpecificTag(context.Configuration, context.CurrentBranch.FriendlyName, branchNameOverride); int? number = null; - if (!string.IsNullOrEmpty(context.Configuration.TagNumberPattern)) - { - var match = Regex.Match(context.CurrentBranch.CanonicalName, context.Configuration.TagNumberPattern); - var numberGroup = match.Groups["number"]; - if (numberGroup.Success) - { - number = int.Parse(numberGroup.Value); - } - } var lastTag = context.CurrentBranch .GetVersionTagsOnBranch(context.Repository, context.Configuration.GitTagPrefix) .FirstOrDefault(v => v.PreReleaseTag.Name == tagToUse); - if (number == null && - lastTag != null && + if (lastTag != null && MajorMinorPatchEqual(lastTag, semanticVersion) && lastTag.PreReleaseTag.HasTag()) { diff --git a/src/GitVersionExe.Tests/PullRequestInTeamCityTest.cs b/src/GitVersionExe.Tests/PullRequestInTeamCityTest.cs index 2a8ee24d23..060ab92c6f 100644 --- a/src/GitVersionExe.Tests/PullRequestInTeamCityTest.cs +++ b/src/GitVersionExe.Tests/PullRequestInTeamCityTest.cs @@ -42,7 +42,7 @@ public void GivenARemoteWithATagOnMaster_AndAPullRequestWithTwoCommits_AndBuildI var result = GitVersionHelper.ExecuteIn(fixture.RepositoryPath, isTeamCity: true); result.ExitCode.ShouldBe(0); - result.OutputVariables.FullSemVer.ShouldBe("1.0.4-PullRequest.5+3"); + result.OutputVariables.FullSemVer.ShouldBe("1.0.4-PullRequest0005.3"); // Cleanup repository files DirectoryHelper.DeleteDirectory(remoteRepositoryPath);