Skip to content

Commit

Permalink
Fix GitVersion calculates the wrong version after main is merged back…
Browse files Browse the repository at this point in the history
… to develop.
  • Loading branch information
HHobeck committed Feb 26, 2024
1 parent 2ad57ea commit 2e314a2
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 70 deletions.
84 changes: 84 additions & 0 deletions src/GitVersion.Core.Tests/IntegrationTests/OtherScenarios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,4 +1218,88 @@ public void EnsurePreventIncrementWhenCurrentCommitTaggedOnReleaseBranchAndIncre

fixture.AssertFullSemver(semVersion, configuration);
}

[Test]
public void EnsureVersionAfterMainIsMergedBackToDevelopIsCorrect()
{
using EmptyRepositoryFixture fixture = new("main");

fixture.MakeATaggedCommit("1.0.0");
fixture.BranchTo("develop");
fixture.MakeACommit("A");

// ✅ succeeds as expected
fixture.AssertFullSemver("1.1.0-alpha.1");

fixture.Checkout("main");
fixture.MakeACommit("B");
fixture.BranchTo("hotfix/just-a-hotfix");
fixture.MakeACommit("C +semver: major");
fixture.MergeTo("main", removeBranchAfterMerging: true);
fixture.Checkout("develop");
fixture.MakeACommit("D");
fixture.Checkout("main");
fixture.MakeACommit("E");
fixture.ApplyTag("1.0.1");

fixture.Checkout("develop");
fixture.MergeNoFF("main");

// ✅ succeeds as expected
fixture.AssertFullSemver("1.1.0-alpha.3");
}

[TestCase(false, "2.0.0-alpha.3")]
[TestCase(true, "2.0.0-alpha.1")]
public void EnsureVersionAfterMainIsMergedBackToDevelopIsCorrectForTrunkBased(bool applyTag, string semanticVersion)
{
var configuration = GitFlowConfigurationBuilder.New
.WithVersionStrategy(VersionStrategies.TrunkBased)
.Build();

using EmptyRepositoryFixture fixture = new("main");

fixture.MakeACommit("A");
fixture.ApplyTag("1.0.0");
fixture.BranchTo("develop");
fixture.MakeACommit("B +semver: major");

// ✅ succeeds as expected
fixture.AssertFullSemver("2.0.0-alpha.1", configuration);

fixture.Checkout("main");
fixture.MakeACommit("C");
if (applyTag) fixture.ApplyTag("1.0.1");
fixture.Checkout("develop");
fixture.MergeNoFF("main");

// ✅ succeeds as expected
fixture.AssertFullSemver(semanticVersion, configuration);
}

[TestCase(false, "2.0.0-alpha.3")]
[TestCase(true, "2.0.0-alpha.2")]
public void EnsureVersionAfterMainIsMergedBackToDevelopIsCorrectForGitFlow(bool applyTag, string semanticVersion)
{
var configuration = GitFlowConfigurationBuilder.New.Build();

using EmptyRepositoryFixture fixture = new("main");

fixture.MakeACommit("A");
fixture.ApplyTag("1.0.0");
fixture.BranchTo("develop");
fixture.MakeACommit("B +semver: major");

// ✅ succeeds as expected
fixture.AssertFullSemver("2.0.0-alpha.1", configuration);

fixture.Checkout("main");
fixture.MakeACommit("C");
if (applyTag) fixture.ApplyTag("1.0.1");
fixture.Checkout("develop");
fixture.MergeNoFF("main");

// ✅ succeeds as expected
fixture.AssertFullSemver(semanticVersion, configuration);
}
}
1 change: 0 additions & 1 deletion src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public interface IRepositoryStore

ICommit? FindMergeBase(ICommit commit, ICommit mainlineTip);
ICommit? GetCurrentCommit(IBranch currentBranch, string? commitId);
IEnumerable<ICommit> GetCommitLog(ICommit? baseVersionSource, ICommit? currentCommit);

IBranch GetTargetBranch(string? targetBranchName);
IBranch? FindBranch(ReferenceName branchName);
Expand Down
7 changes: 0 additions & 7 deletions src/GitVersion.Core/Core/RepositoryStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,6 @@ public IEnumerable<BranchCommit> FindCommitBranchesWasBranchedFrom(IBranch branc
}
}

public IEnumerable<ICommit> GetCommitLog(ICommit? baseVersionSource, ICommit? currentCommit)
{
var filter = new CommitFilter { IncludeReachableFrom = currentCommit, ExcludeReachableFrom = baseVersionSource, SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time };

return this.repository.Commits.QueryBy(filter);
}

public bool IsCommitOnBranch(ICommit? baseVersionSource, IBranch branch, ICommit firstMatchingCommit)
{
var filter = new CommitFilter { IncludeReachableFrom = branch, ExcludeReachableFrom = baseVersionSource, FirstParentOnly = true };
Expand Down
4 changes: 2 additions & 2 deletions src/GitVersion.Core/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ GitVersion.Common.IRepositoryStore.FindCommitBranchesWasBranchedFrom(GitVersion.
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.GetBranchesContainingCommit(GitVersion.ICommit! commit, System.Collections.Generic.IEnumerable<GitVersion.IBranch!>? branches = null, bool onlyTrackedBranches = false) -> System.Collections.Generic.IEnumerable<GitVersion.IBranch!>!
GitVersion.Common.IRepositoryStore.GetCommitLog(GitVersion.ICommit? baseVersionSource, GitVersion.ICommit? currentCommit) -> System.Collections.Generic.IEnumerable<GitVersion.ICommit!>!
GitVersion.Common.IRepositoryStore.GetCurrentCommit(GitVersion.IBranch! currentBranch, string? commitId) -> GitVersion.ICommit?
GitVersion.Common.IRepositoryStore.GetNumberOfUncommittedChanges() -> int
GitVersion.Common.IRepositoryStore.GetReleaseBranches(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string!, GitVersion.Configuration.IBranchConfiguration!>>! releaseBranchConfig) -> System.Collections.Generic.IEnumerable<GitVersion.IBranch!>!
Expand Down Expand Up @@ -690,11 +689,12 @@ GitVersion.VersionCalculation.DeploymentMode.ContinuousDelivery = 1 -> GitVersio
GitVersion.VersionCalculation.DeploymentMode.ContinuousDeployment = 2 -> GitVersion.VersionCalculation.DeploymentMode
GitVersion.VersionCalculation.DeploymentMode.ManualDeployment = 0 -> GitVersion.VersionCalculation.DeploymentMode
GitVersion.VersionCalculation.IDeploymentModeCalculator
GitVersion.VersionCalculation.IDeploymentModeCalculator.Calculate(GitVersion.SemanticVersion! semanticVersion, GitVersion.ICommit? baseVersionSource) -> GitVersion.SemanticVersion!
GitVersion.VersionCalculation.IDeploymentModeCalculator.Calculate(GitVersion.SemanticVersion! semanticVersion, GitVersion.ICommit? baseVersionSource, string? label) -> GitVersion.SemanticVersion!
GitVersion.VersionCalculation.IEffectiveBranchConfigurationFinder
GitVersion.VersionCalculation.IEffectiveBranchConfigurationFinder.GetConfigurations(GitVersion.IBranch! branch, GitVersion.Configuration.IGitVersionConfiguration! configuration) -> System.Collections.Generic.IEnumerable<GitVersion.Configuration.EffectiveBranchConfiguration!>!
GitVersion.VersionCalculation.IIncrementStrategyFinder
GitVersion.VersionCalculation.IIncrementStrategyFinder.DetermineIncrementedField(GitVersion.ICommit! currentCommit, GitVersion.VersionCalculation.BaseVersion! baseVersion, GitVersion.Configuration.EffectiveConfiguration! configuration, string? label) -> GitVersion.VersionField
GitVersion.VersionCalculation.IIncrementStrategyFinder.GetCommitHistory(string? tagPrefix, GitVersion.SemanticVersionFormat semanticVersionFormat, GitVersion.ICommit? baseVersionSource, GitVersion.ICommit! currentCommit, string? label) -> System.Collections.Generic.IReadOnlyCollection<GitVersion.ICommit!>!
GitVersion.VersionCalculation.IIncrementStrategyFinder.GetIncrementForcedByCommit(GitVersion.ICommit! commit, GitVersion.Configuration.EffectiveConfiguration! configuration) -> GitVersion.VersionField
GitVersion.VersionCalculation.IIncrementStrategyFinder.GetIncrementForCommits(string? majorVersionBumpMessage, string? minorVersionBumpMessage, string? patchVersionBumpMessage, string? noBumpMessage, GitVersion.ICommit![]! commits) -> GitVersion.VersionField?
GitVersion.VersionCalculation.IIncrementStrategyFinder.GetMergedCommits(GitVersion.ICommit! mergeCommit, int index) -> System.Collections.Generic.IEnumerable<GitVersion.ICommit!>!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ namespace GitVersion.VersionCalculation;

public interface IDeploymentModeCalculator
{
SemanticVersion Calculate(SemanticVersion semanticVersion, ICommit? baseVersionSource);
SemanticVersion Calculate(SemanticVersion semanticVersion, ICommit? baseVersionSource, string? label);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ ICommit[] commits
IEnumerable<ICommit> GetMergedCommits(ICommit mergeCommit, int index);

VersionField GetIncrementForcedByCommit(ICommit commit, EffectiveConfiguration configuration);

IReadOnlyCollection<ICommit> GetCommitHistory(
string? tagPrefix, SemanticVersionFormat semanticVersionFormat, ICommit? baseVersionSource, ICommit currentCommit, string? label);
}
75 changes: 50 additions & 25 deletions src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace GitVersion.VersionCalculation;

internal class IncrementStrategyFinder : IIncrementStrategyFinder
internal sealed class IncrementStrategyFinder : IIncrementStrategyFinder
{
public const string DefaultMajorPattern = @"\+semver:\s?(breaking|major)";
public const string DefaultMinorPattern = @"\+semver:\s?(feature|minor)";
Expand All @@ -23,13 +23,12 @@ internal class IncrementStrategyFinder : IIncrementStrategyFinder
private static readonly Regex DefaultPatchPatternRegex = new(DefaultPatchPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex DefaultNoBumpPatternRegex = new(DefaultNoBumpPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);

private readonly IGitRepository repository;
private readonly IGitRepository repository_;
private readonly ITaggedSemanticVersionRepository taggedSemanticVersionRepository;

public IncrementStrategyFinder(IGitRepository repository, ITaggedSemanticVersionRepository taggedSemanticVersionRepository)
{
this.repository = repository.NotNull();
this.repository = repository.NotNull();
this.repository_ = repository.NotNull();
this.taggedSemanticVersionRepository = taggedSemanticVersionRepository;
}

Expand Down Expand Up @@ -81,26 +80,21 @@ public VersionField DetermineIncrementedField(
}

private VersionField? FindCommitMessageIncrement(
EffectiveConfiguration configuration, ICommit? baseCommit, ICommit? currentCommit, string? label)
EffectiveConfiguration configuration, ICommit? baseVersionSource, ICommit currentCommit, string? label)
{
if (configuration.CommitMessageIncrementing == CommitMessageIncrementMode.Disabled)
{
return null;
}

//get tags with valid version - depends on configuration (see #3757)
var targetShas = new Lazy<IReadOnlySet<string>>(() =>
this.taggedSemanticVersionRepository.GetTaggedSemanticVersions(configuration.TagPrefix, configuration.SemanticVersionFormat)
.SelectMany(_ => _).Where(_ => _.Value.IsMatchForBranchSpecificLabel(label)).Select(_ => _.Tag.TargetSha).ToHashSet()
IEnumerable<ICommit> commits = GetCommitHistory(
tagPrefix: configuration.TagPrefix,
semanticVersionFormat: configuration.SemanticVersionFormat,
baseVersionSource: baseVersionSource,
currentCommit: currentCommit,
label: label
);

var commits = GetIntermediateCommits(baseCommit, currentCommit);
// consider commit messages since latest tag only (see #3071)
commits = commits
.Reverse()
.TakeWhile(x => !targetShas.Value.Contains(x.Sha))
.Reverse();

if (configuration.CommitMessageIncrementing == CommitMessageIncrementMode.MergeMessageOnly)
{
commits = commits.Where(c => c.Parents.Count() > 1);
Expand All @@ -120,6 +114,41 @@ private static Regex TryGetRegexOrDefault(string? messageRegex, Regex defaultReg
? defaultRegex
: CompiledRegexCache.GetOrAdd(messageRegex, pattern => new(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase));

public IReadOnlyCollection<ICommit> GetCommitHistory(
string? tagPrefix, SemanticVersionFormat semanticVersionFormat, ICommit? baseVersionSource, ICommit currentCommit, string? label)
{
var targetShas = new Lazy<IReadOnlySet<string>>(() =>
this.taggedSemanticVersionRepository.GetTaggedSemanticVersions(tagPrefix, semanticVersionFormat)
.SelectMany(_ => _).Where(_ => _.Value.IsMatchForBranchSpecificLabel(label)).Select(_ => _.Tag.TargetSha).ToHashSet()
);

var intermediateCommits = GetIntermediateCommits(baseVersionSource, currentCommit).ToArray();

var commitLog = intermediateCommits.ToDictionary(element => element.Id.Sha);

foreach (var item in intermediateCommits.Reverse())
{
if (!commitLog.ContainsKey(item.Sha)) continue;

if (targetShas.Value.Contains(item.Sha))
{
void RemoveCommitFromHistory(ICommit commit)
{
if (!commitLog.ContainsKey(commit.Sha)) return;

commitLog.Remove(commit.Sha);
foreach (var item in commit.Parents)
{
RemoveCommitFromHistory(item);
}
}
RemoveCommitFromHistory(item);
}
}

return commitLog.Values;
}

/// <summary>
/// Get the sequence of commits in a repository between a <paramref name="baseCommit"/> (exclusive)
/// and a particular <paramref name="headCommit"/> (inclusive)
Expand Down Expand Up @@ -155,7 +184,7 @@ private Dictionary<string, int> GetHeadCommitsMap(ICommit? headCommit) =>
/// </summary>
private ICommit[] GetHeadCommits(ICommit? headCommit) =>
this.headCommitsCache.GetOrAdd(headCommit?.Sha ?? "NULL", () =>
GetCommitsReacheableFromHead(repository, headCommit).ToArray());
GetCommitsReacheableFromHead(headCommit).ToArray());

private VersionField? GetIncrementFromCommit(ICommit commit, Regex majorRegex, Regex minorRegex, Regex patchRegex, Regex none) =>
this.commitIncrementCache.GetOrAdd(commit.Sha, () =>
Expand All @@ -170,19 +199,15 @@ private ICommit[] GetHeadCommits(ICommit? headCommit) =>
return null;
}

/// <summary>
/// Query a <paramref name="repo"/> for the sequence of commits from the beginning to a particular
/// <paramref name="headCommit"/> (inclusive)
/// </summary>
private static IEnumerable<ICommit> GetCommitsReacheableFromHead(IGitRepository repo, ICommit? headCommit)
private IEnumerable<ICommit> GetCommitsReacheableFromHead(ICommit? headCommit)
{
var filter = new CommitFilter
{
IncludeReachableFrom = headCommit,
SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Reverse
};

return repo.Commits.QueryBy(filter);
return repository_.Commits.QueryBy(filter);
}

public IEnumerable<ICommit> GetMergedCommits(ICommit mergeCommit, int index)
Expand All @@ -198,7 +223,7 @@ public IEnumerable<ICommit> GetMergedCommits(ICommit mergeCommit, int index)
ICommit mergedCommit = GetMergedHead(mergeCommit);
if (index == 0) (mergedCommit, baseCommit) = (baseCommit, mergedCommit);

ICommit findMergeBase = this.repository.FindMergeBase(baseCommit, mergedCommit)
ICommit findMergeBase = this.repository_.FindMergeBase(baseCommit, mergedCommit)
?? throw new InvalidOperationException("Cannot find the base commit of merged branch.");

return GetIntermediateCommits(findMergeBase, mergedCommit);
Expand All @@ -208,7 +233,7 @@ private static ICommit GetMergedHead(ICommit mergeCommit)
{
var parents = mergeCommit.Parents.Skip(1).ToList();
if (parents.Count > 1)
throw new NotSupportedException("Mainline development does not support more than one merge source in a single commit yet");
throw new NotSupportedException("GitVersion does not support more than one merge source in a single commit yet");
return parents.Single();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using GitVersion.Common;
using GitVersion.Logging;

namespace GitVersion.VersionCalculation;

internal sealed class ContinuousDeliveryVersionCalculator(ILog log, IRepositoryStore repositoryStore, Lazy<GitVersionContext> versionContext)
: VersionCalculatorBase(log, repositoryStore, versionContext), IDeploymentModeCalculator
internal sealed class ContinuousDeliveryVersionCalculator(
ILog log, IIncrementStrategyFinder incrementStrategyFinder, Lazy<GitVersionContext> versionContext)
: VersionCalculatorBase(log, incrementStrategyFinder, versionContext), IDeploymentModeCalculator
{
public SemanticVersion Calculate(SemanticVersion semanticVersion, ICommit? baseVersionSource)
public SemanticVersion Calculate(SemanticVersion semanticVersion, ICommit? baseVersionSource, string? label)
{
using (this.log.IndentLog("Using continuous delivery workflow to calculate the incremented version."))
{
Expand All @@ -16,13 +16,13 @@ public SemanticVersion Calculate(SemanticVersion semanticVersion, ICommit? baseV
throw new WarningException("Continuous delivery requires a pre-release tag.");
}

return CalculateInternal(semanticVersion, baseVersionSource);
return CalculateInternal(semanticVersion, baseVersionSource, label);
}
}

private SemanticVersion CalculateInternal(SemanticVersion semanticVersion, ICommit? baseVersionSource)
private SemanticVersion CalculateInternal(SemanticVersion semanticVersion, ICommit? baseVersionSource, string? label)
{
var buildMetaData = CreateVersionBuildMetaData(baseVersionSource);
var buildMetaData = CreateVersionBuildMetaData(baseVersionSource, label);

return new SemanticVersion(semanticVersion)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
using GitVersion.Common;
using GitVersion.Logging;

namespace GitVersion.VersionCalculation;

internal sealed class ContinuousDeploymentVersionCalculator(ILog log, IRepositoryStore repositoryStore, Lazy<GitVersionContext> versionContext)
: VersionCalculatorBase(log, repositoryStore, versionContext), IDeploymentModeCalculator
internal sealed class ContinuousDeploymentVersionCalculator(
ILog log, IIncrementStrategyFinder incrementStrategyFinder, Lazy<GitVersionContext> versionContext)
: VersionCalculatorBase(log, incrementStrategyFinder, versionContext), IDeploymentModeCalculator
{
public SemanticVersion Calculate(SemanticVersion semanticVersion, ICommit? baseVersionSource)
public SemanticVersion Calculate(SemanticVersion semanticVersion, ICommit? baseVersionSource, string? label)
{
using (this.log.IndentLog("Using continuous deployment workflow to calculate the incremented version."))
{
return CalculateInternal(semanticVersion, baseVersionSource);
return CalculateInternal(semanticVersion, baseVersionSource, label);
}
}

private SemanticVersion CalculateInternal(SemanticVersion semanticVersion, ICommit? baseVersionSource)
private SemanticVersion CalculateInternal(SemanticVersion semanticVersion, ICommit? baseVersionSource, string? label)
{
var buildMetaData = CreateVersionBuildMetaData(baseVersionSource);
var buildMetaData = CreateVersionBuildMetaData(baseVersionSource, label);

return new SemanticVersion(semanticVersion)
{
Expand Down
Loading

0 comments on commit 2e314a2

Please sign in to comment.