Skip to content

Commit 9c2cfbd

Browse files
committed
Create TrunkBasedVersionStrategy implementation (see #3601 for more details)
1 parent 5e71ec2 commit 9c2cfbd

32 files changed

+1846
-118
lines changed

src/GitVersion.Core.Tests/VersionCalculation/SemanticVersionTests.cs

Lines changed: 1380 additions & 0 deletions
Large diffs are not rendered by default.

src/GitVersion.Core.Tests/VersionCalculation/Strategies/ConfigNextVersionBaseVersionStrategyTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void ConfigNextVersionTest(string nextVersion, string expectedVersion, Se
3333

3434
baseVersion.ShouldNotBeNull();
3535
baseVersion.ShouldIncrement.ShouldBe(false);
36-
baseVersion.SemanticVersion.ToString().ShouldBe(expectedVersion);
36+
baseVersion.GetSemanticVersion().ToString().ShouldBe(expectedVersion);
3737
}
3838

3939
[TestCase("0.1", SemanticVersionFormat.Strict)]

src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ private static void AssertMergeMessage(string message, string? expectedVersion,
177177
else
178178
{
179179
baseVersion.ShouldNotBeNull();
180-
baseVersion.SemanticVersion.ToString().ShouldBe(expectedVersion);
180+
baseVersion.GetSemanticVersion().ToString().ShouldBe(expectedVersion);
181181
}
182182
}
183183

src/GitVersion.Core.Tests/VersionCalculation/Strategies/VersionInBranchNameBaseVersionStrategyTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public void CanTakeVersionFromNameOfReleaseBranch(string branchName, string expe
2828
strategy.ShouldNotBeNull();
2929
var baseVersion = strategy.GetBaseVersions(effectiveBranchConfiguration).Single();
3030

31-
baseVersion.SemanticVersion.ToString().ShouldBe(expectedBaseVersion);
31+
baseVersion.GetSemanticVersion().ToString().ShouldBe(expectedBaseVersion);
3232
}
3333

3434
[TestCase("origin/hotfix-2.0.0")]
@@ -90,7 +90,7 @@ public void CanTakeVersionFromNameOfConfiguredReleaseBranch(string branchName, s
9090
strategy.ShouldNotBeNull();
9191
var baseVersion = strategy.GetBaseVersions(effectiveBranchConfiguration).Single();
9292

93-
baseVersion.SemanticVersion.ToString().ShouldBe(expectedBaseVersion);
93+
baseVersion.GetSemanticVersion().ToString().ShouldBe(expectedBaseVersion);
9494
}
9595

9696
[TestCase("origin", "release-2.0.0", "2.0.0")]
@@ -114,7 +114,7 @@ public void CanTakeVersionFromNameOfRemoteReleaseBranch(string origin, string br
114114
strategy.ShouldNotBeNull();
115115
var baseVersion = strategy.GetBaseVersions(effectiveBranchConfiguration).Single();
116116

117-
baseVersion.SemanticVersion.ToString().ShouldBe(expectedBaseVersion);
117+
baseVersion.GetSemanticVersion().ToString().ShouldBe(expectedBaseVersion);
118118
}
119119

120120
private static IVersionStrategy GetVersionStrategy(IGitRepository repository,

src/GitVersion.Core/Configuration/EffectiveBranchConfiguration.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using GitVersion.Extensions;
2+
using GitVersion.VersionCalculation;
23

34
namespace GitVersion.Configuration;
45

@@ -7,4 +8,12 @@ public record EffectiveBranchConfiguration(EffectiveConfiguration Value, IBranch
78
public IBranch Branch { get; } = Branch.NotNull();
89

910
public EffectiveConfiguration Value { get; } = Value.NotNull();
11+
12+
public NextVersion CreateNextVersion(BaseVersion baseVersion, SemanticVersion incrementedVersion)
13+
{
14+
incrementedVersion.NotNull();
15+
baseVersion.NotNull();
16+
17+
return new NextVersion(incrementedVersion, baseVersion, this);
18+
}
1019
}

src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ public interface IRepositoryStore
1616
IEnumerable<ICommit> GetCommitLog(ICommit? baseVersionSource, ICommit? currentCommit);
1717

1818
IBranch GetTargetBranch(string? targetBranchName);
19-
IBranch? FindBranch(string? branchName);
19+
IBranch? FindBranch(ReferenceName branchName);
20+
IBranch? FindBranch(string branchName);
2021
IBranch? FindMainBranch(IGitVersionConfiguration configuration);
2122
IEnumerable<IBranch> FindMainlineBranches(IGitVersionConfiguration configuration);
2223
IEnumerable<IBranch> GetReleaseBranches(IEnumerable<KeyValuePair<string, IBranchConfiguration>> releaseBranchConfig);
2324
IEnumerable<IBranch> ExcludingBranches(IEnumerable<IBranch> branchesToExclude);
24-
IEnumerable<IBranch> GetBranchesContainingCommit(ICommit? commit, IEnumerable<IBranch>? branches = null, bool onlyTrackedBranches = false);
25+
IEnumerable<IBranch> GetBranchesContainingCommit(ICommit commit, IEnumerable<IBranch>? branches = null, bool onlyTrackedBranches = false);
2526

2627
IDictionary<string, List<IBranch>> GetMainlineBranches(ICommit commit, IGitVersionConfiguration configuration);
2728

@@ -50,4 +51,9 @@ public interface IRepositoryStore
5051
bool IsCommitOnBranch(ICommit? baseVersionSource, IBranch branch, ICommit firstMatchingCommit);
5152

5253
int GetNumberOfUncommittedChanges();
54+
55+
IEnumerable<SemanticVersionWithTag> GetSemanticVersions(
56+
IGitVersionConfiguration configuration, IBranch currentBranch, ICommit currentCommit,
57+
bool trackMergeTarget, bool tracksReleaseBranches
58+
);
5359
}

src/GitVersion.Core/Core/BranchesContainingCommitFinder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ public BranchesContainingCommitFinder(IGitRepository repository, ILog log)
1414
this.log = log.NotNull();
1515
}
1616

17-
public IEnumerable<IBranch> GetBranchesContainingCommit(ICommit? commit, IEnumerable<IBranch>? branches = null, bool onlyTrackedBranches = false)
17+
public IEnumerable<IBranch> GetBranchesContainingCommit(ICommit commit, IEnumerable<IBranch>? branches = null, bool onlyTrackedBranches = false)
1818
{
19-
commit = commit.NotNull();
19+
commit.NotNull();
2020
branches ??= this.repository.Branches.ToList();
2121

2222
// TODO Should we cache this?

src/GitVersion.Core/Core/GitVersionContextFactory.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public GitVersionContext Create(GitVersionOptions gitVersionOptions)
2323
var currentBranch = this.repositoryStore.GetTargetBranch(gitVersionOptions.RepositoryInfo.TargetBranch) ?? throw new InvalidOperationException("Need a branch to operate on");
2424
var currentCommit = this.repositoryStore.GetCurrentCommit(currentBranch, gitVersionOptions.RepositoryInfo.CommitId);
2525

26+
if (currentCommit is null) throw new GitVersionException("No commits found on the current branch.");
27+
2628
var overrideConfiguration = this.options.Value.ConfigurationInfo.OverrideConfiguration;
2729
var configuration = this.configurationProvider.Provide(overrideConfiguration);
2830
if (currentBranch.IsDetachedHead)

src/GitVersion.Core/Core/RepositoryStore.cs

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ public IBranch GetTargetBranch(string? targetBranchName)
104104
return desiredBranch;
105105
}
106106

107-
public IBranch? FindBranch(string? branchName) => this.repository.Branches.FirstOrDefault(x => x.Name.EquivalentTo(branchName));
107+
public IBranch? FindBranch(ReferenceName branchName) => this.repository.Branches.FirstOrDefault(x => x.Name.Equals(branchName));
108+
109+
public IBranch? FindBranch(string branchName) => this.repository.Branches.FirstOrDefault(x => x.Name.EquivalentTo(branchName));
108110

109111
public IBranch? FindMainBranch(IGitVersionConfiguration configuration)
110112
{
@@ -143,7 +145,7 @@ private static bool IsReleaseBranch(INamedReference branch, IEnumerable<KeyValue
143145

144146
public IEnumerable<IBranch> ExcludingBranches(IEnumerable<IBranch> branchesToExclude) => this.repository.Branches.ExcludeBranches(branchesToExclude);
145147

146-
public IEnumerable<IBranch> GetBranchesContainingCommit(ICommit? commit, IEnumerable<IBranch>? branches = null, bool onlyTrackedBranches = false)
148+
public IEnumerable<IBranch> GetBranchesContainingCommit(ICommit commit, IEnumerable<IBranch>? branches = null, bool onlyTrackedBranches = false)
147149
{
148150
var branchesContainingCommitFinder = new BranchesContainingCommitFinder(this.repository, this.log);
149151
return branchesContainingCommitFinder.GetBranchesContainingCommit(commit, branches, onlyTrackedBranches);
@@ -268,7 +270,7 @@ public IEnumerable<SemanticVersion> GetVersionTagsOnBranch(IBranch branch, strin
268270
var semanticVersions = GetTaggedSemanticVersions(tagPrefix, format);
269271
var tagsBySha = semanticVersions.Where(t => t.Tag.TargetSha != null).ToLookup(t => t.Tag.TargetSha, t => t);
270272

271-
var versionTags = (branch.Commits?.SelectMany(c => tagsBySha[c.Sha].Select(t => t))
273+
var versionTags = (branch.Commits.SelectMany(c => tagsBySha[c.Sha].Select(t => t))
272274
?? Enumerable.Empty<SemanticVersionWithTag>()).ToList();
273275

274276
this.taggedSemanticVersionsOnBranchCache.Add(branch, versionTags);
@@ -316,7 +318,7 @@ public IReadOnlyList<SemanticVersionWithTag> GetTaggedSemanticVersionsOnBranch(
316318
var semanticVersions = GetTaggedSemanticVersions(tagPrefix, format);
317319
var tagsBySha = semanticVersions.Where(t => t.Tag.TargetSha != null).ToLookup(t => t.Tag.TargetSha, t => t);
318320

319-
var versionTags = (branch.Commits?.SelectMany(c => tagsBySha[c.Sha].Select(t => t))
321+
var versionTags = (branch.Commits.SelectMany(c => tagsBySha[c.Sha].Select(t => t))
320322
?? Enumerable.Empty<SemanticVersionWithTag>()).ToList();
321323

322324
this.taggedSemanticVersionsOnBranchCache.Add(branch, versionTags);
@@ -355,4 +357,93 @@ private IEnumerable<SemanticVersion> GetCurrentCommitSemanticVersions(ICommit? c
355357
? new[] { version }
356358
: Array.Empty<SemanticVersion>();
357359
}
360+
361+
public IEnumerable<SemanticVersionWithTag> GetSemanticVersions(IGitVersionConfiguration configuration,
362+
IBranch currentBranch, ICommit currentCommit, bool trackMergeTarget, bool tracksReleaseBranches)
363+
{
364+
var olderThan = currentCommit?.When;
365+
366+
IEnumerable<SemanticVersionWithTag> GetSemanticVersions()
367+
{
368+
var semanticVersions = GetTaggedSemanticVersions(
369+
configuration.TagPrefix, configuration.SemanticVersionFormat
370+
).ToList();
371+
var semanticVersionsByCommitLazy = new Lazy<ILookup<string, SemanticVersionWithTag>>(
372+
() => semanticVersions.ToLookup(element => element.Tag.Commit.Id.Sha)
373+
);
374+
375+
foreach (var semanticVersion in GetSemanticVersionsCommon(currentBranch, semanticVersionsByCommitLazy))
376+
{
377+
if (semanticVersion.Tag.Commit.When <= olderThan) yield return semanticVersion;
378+
}
379+
380+
if (trackMergeTarget)
381+
{
382+
foreach (var semanticVersion in GetSemanticVersionsTrackMergeTarget(currentBranch, semanticVersionsByCommitLazy))
383+
{
384+
if (semanticVersion.Tag.Commit.When <= olderThan) yield return semanticVersion;
385+
}
386+
}
387+
388+
if (tracksReleaseBranches)
389+
{
390+
foreach (var semanticVersion in GetSemanticVersionsTracksReleaseBranches(configuration, semanticVersionsByCommitLazy))
391+
{
392+
yield return semanticVersion;
393+
}
394+
}
395+
}
396+
397+
var alreadyReturnedValues = new HashSet<SemanticVersionWithTag>();
398+
399+
foreach (var semanticVersion in GetSemanticVersions())
400+
{
401+
if (alreadyReturnedValues.Add(semanticVersion))
402+
yield return semanticVersion;
403+
}
404+
}
405+
406+
private static IEnumerable<SemanticVersionWithTag> GetSemanticVersionsCommon(
407+
IBranch currentBranch, Lazy<ILookup<string, SemanticVersionWithTag>> semanticVersionsByCommitLazy)
408+
{
409+
foreach (var commit in currentBranch.Commits)
410+
{
411+
foreach (var semanticVersion in semanticVersionsByCommitLazy.Value[commit.Id.Sha])
412+
{
413+
yield return semanticVersion;
414+
}
415+
}
416+
}
417+
418+
private static IEnumerable<SemanticVersionWithTag> GetSemanticVersionsTrackMergeTarget(
419+
IBranch currentBranch, Lazy<ILookup<string, SemanticVersionWithTag>> semanticVersionsByCommitLazy)
420+
{
421+
var commitsOnCurrentBranch = currentBranch.Commits.ToArray();
422+
if (!commitsOnCurrentBranch.Any()) yield break;
423+
424+
var shaHashSet = new HashSet<string>(commitsOnCurrentBranch.Select(element => element.Id.Sha));
425+
foreach (var semanticVersion in semanticVersionsByCommitLazy.Value.SelectMany(element => element))
426+
{
427+
var parentCommits = semanticVersion.Tag.Commit.Parents ?? Array.Empty<ICommit>();
428+
if (parentCommits.Any(element => shaHashSet.Contains(element.Id.Sha)))
429+
{
430+
yield return semanticVersion;
431+
}
432+
}
433+
}
434+
435+
private IEnumerable<SemanticVersionWithTag> GetSemanticVersionsTracksReleaseBranches(
436+
IGitVersionConfiguration configuration, Lazy<ILookup<string, SemanticVersionWithTag>> semanticVersionsByCommitLazy)
437+
{
438+
foreach (var mainBranch in FindMainlineBranches(configuration))
439+
{
440+
foreach (var commit in mainBranch.Commits?.ToArray() ?? Array.Empty<ICommit>())
441+
{
442+
foreach (var semanticVersion in semanticVersionsByCommitLazy.Value[commit.Id.Sha])
443+
{
444+
yield return semanticVersion;
445+
}
446+
}
447+
}
448+
}
358449
}

src/GitVersion.Core/Extensions/ConfigurationExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ public static EffectiveConfiguration GetEffectiveConfiguration(this IGitVersionC
1818
return new(configuration, branchConfiguration);
1919
}
2020

21+
public static IBranchConfiguration GetBranchConfiguration(this IGitVersionConfiguration configuration, IBranch branch)
22+
=> GetBranchConfiguration(configuration, branch.NotNull().Name);
23+
2124
public static IBranchConfiguration GetBranchConfiguration(this IGitVersionConfiguration configuration, ReferenceName branchName)
2225
{
2326
var branchConfiguration = GetBranchConfigurations(configuration, branchName.WithoutOrigin).FirstOrDefault();

0 commit comments

Comments
 (0)