From 6f58131ffcbaab8aaa732944fc95a7b4054b1ae6 Mon Sep 17 00:00:00 2001 From: 9swampy Date: Sat, 21 Jun 2025 14:52:47 +0100 Subject: [PATCH 1/5] Facilitate running tests with master as the default main branch. --- src/GitVersion.Core.Tests/Helpers/TestBase.cs | 2 +- .../IntegrationTests/GitflowScenarios.cs | 36 ++++++++++--------- .../Fixtures/BaseGitFlowRepositoryFixture.cs | 4 +-- .../Fixtures/EmptyRepositoryFixture.cs | 2 +- .../Fixtures/RemoteRepositoryFixture.cs | 2 +- .../Fixtures/RepositoryFixtureBase.cs | 26 +++++++++----- 6 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/GitVersion.Core.Tests/Helpers/TestBase.cs b/src/GitVersion.Core.Tests/Helpers/TestBase.cs index cd25bd8a27..ef5e0fdb53 100644 --- a/src/GitVersion.Core.Tests/Helpers/TestBase.cs +++ b/src/GitVersion.Core.Tests/Helpers/TestBase.cs @@ -7,7 +7,7 @@ namespace GitVersion.Core.Tests.Helpers; public class TestBase { - public const string MainBranch = "main"; + public const string MainBranch = RepositoryFixtureBase.MainBranch; protected static IServiceProvider ConfigureServices(Action? overrideServices = null) { diff --git a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs index a466855b43..a8a650adcf 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs @@ -1,3 +1,4 @@ +using GitVersion.Configuration; using GitVersion.Core.Tests.Helpers; namespace GitVersion.Core.Tests.IntegrationTests; @@ -14,69 +15,70 @@ public void GitflowComplexExample() const string release1Branch = "release/1.1.0"; const string release2Branch = "release/1.2.0"; const string hotfixBranch = "hotfix/hf"; + var configuration = GitFlowConfigurationBuilder.New.Build(); using var fixture = new BaseGitFlowRepositoryFixture("1.0.0"); - fixture.AssertFullSemver("1.1.0-alpha.1"); + fixture.AssertFullSemver("1.1.0-alpha.1", configuration); // Feature 1 fixture.BranchTo(feature1Branch); fixture.MakeACommit("added feature 1"); - fixture.AssertFullSemver("1.1.0-f1.1+2"); + fixture.AssertFullSemver("1.1.0-f1.1+2", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(feature1Branch); fixture.Repository.Branches.Remove(fixture.Repository.Branches[feature1Branch]); - fixture.AssertFullSemver("1.1.0-alpha.3"); + fixture.AssertFullSemver("1.1.0-alpha.3", configuration); // Release 1.1.0 fixture.BranchTo(release1Branch); fixture.MakeACommit("release stabilization"); - fixture.AssertFullSemver("1.1.0-beta.1+4"); + fixture.AssertFullSemver("1.1.0-beta.1+4", configuration); fixture.Checkout(MainBranch); fixture.MergeNoFF(release1Branch); - fixture.AssertFullSemver("1.1.0-5"); + fixture.AssertFullSemver("1.1.0-5", configuration); fixture.ApplyTag("1.1.0"); - fixture.AssertFullSemver("1.1.0"); + fixture.AssertFullSemver("1.1.0", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(release1Branch); fixture.Repository.Branches.Remove(fixture.Repository.Branches[release1Branch]); - fixture.AssertFullSemver("1.2.0-alpha.1"); + fixture.AssertFullSemver("1.2.0-alpha.1", configuration); // Feature 2 fixture.BranchTo(feature2Branch); fixture.MakeACommit("added feature 2"); - fixture.AssertFullSemver("1.2.0-f2.1+2"); + fixture.AssertFullSemver("1.2.0-f2.1+2", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(feature2Branch); fixture.Repository.Branches.Remove(fixture.Repository.Branches[feature2Branch]); - fixture.AssertFullSemver("1.2.0-alpha.3"); + fixture.AssertFullSemver("1.2.0-alpha.3", configuration); // Release 1.2.0 fixture.BranchTo(release2Branch); fixture.MakeACommit("release stabilization"); - fixture.AssertFullSemver("1.2.0-beta.1+8"); + fixture.AssertFullSemver("1.2.0-beta.1+8", configuration); fixture.Checkout(MainBranch); fixture.MergeNoFF(release2Branch); - fixture.AssertFullSemver("1.2.0-5"); + fixture.AssertFullSemver("1.2.0-5", configuration); fixture.ApplyTag("1.2.0"); - fixture.AssertFullSemver("1.2.0"); + fixture.AssertFullSemver("1.2.0", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(release2Branch); fixture.Repository.Branches.Remove(fixture.Repository.Branches[release2Branch]); - fixture.AssertFullSemver("1.3.0-alpha.1"); + fixture.AssertFullSemver("1.3.0-alpha.1", configuration); // Hotfix fixture.Checkout(MainBranch); fixture.BranchTo(hotfixBranch); fixture.MakeACommit("added hotfix"); - fixture.AssertFullSemver("1.2.1-beta.1+1"); + fixture.AssertFullSemver("1.2.1-beta.1+1", configuration); fixture.Checkout(MainBranch); fixture.MergeNoFF(hotfixBranch); - fixture.AssertFullSemver("1.2.1-2"); + fixture.AssertFullSemver("1.2.1-2", configuration); fixture.ApplyTag("1.2.1"); - fixture.AssertFullSemver("1.2.1"); + fixture.AssertFullSemver("1.2.1", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(hotfixBranch); fixture.Repository.Branches.Remove(fixture.Repository.Branches[hotfixBranch]); - fixture.AssertFullSemver("1.3.0-alpha.2"); + fixture.AssertFullSemver("1.3.0-alpha.2", configuration); } } diff --git a/src/GitVersion.Testing/Fixtures/BaseGitFlowRepositoryFixture.cs b/src/GitVersion.Testing/Fixtures/BaseGitFlowRepositoryFixture.cs index ebf88bf939..31d9e7de7f 100644 --- a/src/GitVersion.Testing/Fixtures/BaseGitFlowRepositoryFixture.cs +++ b/src/GitVersion.Testing/Fixtures/BaseGitFlowRepositoryFixture.cs @@ -12,7 +12,7 @@ public class BaseGitFlowRepositoryFixture : EmptyRepositoryFixture /// Creates a repo with a develop branch off main which is a single commit ahead of main branch /// Main will be tagged with the initial version before branching develop /// - public BaseGitFlowRepositoryFixture(string initialVersion, string branchName = "main") : + public BaseGitFlowRepositoryFixture(string initialVersion, string branchName = MainBranch) : this(r => r.MakeATaggedCommit(initialVersion), branchName) { } @@ -21,7 +21,7 @@ public BaseGitFlowRepositoryFixture(string initialVersion, string branchName = " /// Creates a repo with a develop branch off main which is a single commit ahead of main /// The initial setup actions will be performed before branching develop /// - public BaseGitFlowRepositoryFixture(Action initialMainAction, string branchName = "main") : + public BaseGitFlowRepositoryFixture(Action initialMainAction, string branchName = MainBranch) : base(branchName) => SetupRepo(initialMainAction); private void SetupRepo(Action initialMainAction) diff --git a/src/GitVersion.Testing/Fixtures/EmptyRepositoryFixture.cs b/src/GitVersion.Testing/Fixtures/EmptyRepositoryFixture.cs index b864b31247..955205b118 100644 --- a/src/GitVersion.Testing/Fixtures/EmptyRepositoryFixture.cs +++ b/src/GitVersion.Testing/Fixtures/EmptyRepositoryFixture.cs @@ -1,3 +1,3 @@ namespace GitVersion.Testing; -public class EmptyRepositoryFixture(string branchName = "main") : RepositoryFixtureBase(path => CreateNewRepository(path, branchName)); +public class EmptyRepositoryFixture(string branchName = RepositoryFixtureBase.MainBranch) : RepositoryFixtureBase(path => CreateNewRepository(path, branchName)); diff --git a/src/GitVersion.Testing/Fixtures/RemoteRepositoryFixture.cs b/src/GitVersion.Testing/Fixtures/RemoteRepositoryFixture.cs index 2312d47f62..71245172ab 100644 --- a/src/GitVersion.Testing/Fixtures/RemoteRepositoryFixture.cs +++ b/src/GitVersion.Testing/Fixtures/RemoteRepositoryFixture.cs @@ -12,7 +12,7 @@ public class RemoteRepositoryFixture : RepositoryFixtureBase public RemoteRepositoryFixture(Func builder) : base(builder) => LocalRepositoryFixture = CloneRepository(); - public RemoteRepositoryFixture(string branchName = "main") + public RemoteRepositoryFixture(string branchName = MainBranch) : this(path => CreateNewRepository(path, branchName, 5)) { } diff --git a/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs b/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs index be177bbf0a..cdfa36b8f3 100644 --- a/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs +++ b/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs @@ -9,6 +9,9 @@ namespace GitVersion.Testing; /// public abstract class RepositoryFixtureBase : IDisposable { + public const string MainBranch = "master"; + public const bool DeleteOnDispose = true; + protected RepositoryFixtureBase(Func repositoryBuilder) : this(repositoryBuilder(FileSystemHelper.Path.GetRepositoryTempPath())) { @@ -47,16 +50,23 @@ protected virtual void Dispose(bool disposing) Repository.Dispose(); var directoryPath = FileSystemHelper.Path.GetFileName(RepositoryPath); - try + if (DeleteOnDispose) { - Console.WriteLine("Cleaning up repository path at {0}", directoryPath); - FileSystemHelper.Directory.DeleteDirectory(RepositoryPath); - Console.WriteLine("Cleaned up repository path at {0}", directoryPath); + try + { + Console.WriteLine("Cleaning up repository path at {0}", directoryPath); + FileSystemHelper.Directory.DeleteDirectory(RepositoryPath); + Console.WriteLine("Cleaned up repository path at {0}", directoryPath); + } + catch (Exception e) + { + Console.WriteLine("Failed to clean up repository path at {0}. Received exception: {1}", directoryPath, e.Message); + // throw; + } } - catch (Exception e) + else { - Console.WriteLine("Failed to clean up repository path at {0}. Received exception: {1}", directoryPath, e.Message); - // throw; + Console.WriteLine("Leaving repository path at {0} intact", directoryPath); } this.SequenceDiagram.End(); @@ -73,7 +83,7 @@ public void Remove(string branch) SequenceDiagram.Destroy(branch); } - public static void Init(string path, string branchName = "main") => GitTestExtensions.ExecuteGitCmd($"init {path} -b {branchName}", "."); + public static void Init(string path, string branchName = MainBranch) => GitTestExtensions.ExecuteGitCmd($"init {path} -b {branchName}", "."); public string MakeATaggedCommit(string tag) { From 7a8ef765f5bd58e58a71828ca0e6827e130fe33a Mon Sep 17 00:00:00 2001 From: 9swampy Date: Sat, 21 Jun 2025 18:49:12 +0100 Subject: [PATCH 2/5] Document unexpected behaviour by extending GitflowComplexExample. --- .../GitRepositoryTestingExtensions.cs | 4 +- .../IntegrationTests/GitflowScenarios.cs | 94 ++++++++++++++++--- .../Fixtures/BaseGitFlowRepositoryFixture.cs | 10 +- .../Fixtures/EmptyRepositoryFixture.cs | 2 +- .../Fixtures/RepositoryFixtureBase.cs | 11 ++- src/GitVersion.Testing/GitTestExtensions.cs | 4 +- 6 files changed, 96 insertions(+), 29 deletions(-) diff --git a/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs b/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs index 1dd6e242c8..9de9fdedb0 100644 --- a/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs +++ b/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs @@ -144,12 +144,12 @@ public static void WriteVersionVariables(this RepositoryFixtureBase fixture, str } public static void AssertFullSemver(this RepositoryFixtureBase fixture, string fullSemver, - IGitVersionConfiguration? configuration = null, IRepository? repository = null, string? commitId = null, bool onlyTrackedBranches = true, string? targetBranch = null) + IGitVersionConfiguration? configuration = null, IRepository? repository = null, string? commitId = null, bool onlyTrackedBranches = true, string? targetBranch = null, string? customMessage = null) { repository ??= fixture.Repository; var variables = GetVersion(fixture, configuration, repository, commitId, onlyTrackedBranches, targetBranch); - variables.FullSemVer.ShouldBe(fullSemver); + variables.FullSemVer.ShouldBe(fullSemver, customMessage); if (commitId == null) { fixture.SequenceDiagram.NoteOver(fullSemver, repository.Head.FriendlyName, color: "#D3D3D3"); diff --git a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs index a8a650adcf..513e144c1c 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs @@ -1,5 +1,7 @@ using GitVersion.Configuration; using GitVersion.Core.Tests.Helpers; +using GitVersion.Helpers; +using LibGit2Sharp; namespace GitVersion.Core.Tests.IntegrationTests; @@ -9,24 +11,29 @@ public class GitflowScenarios : TestBase [Test] public void GitflowComplexExample() { + var keepBranches = true; const string developBranch = "develop"; const string feature1Branch = "feature/f1"; const string feature2Branch = "feature/f2"; const string release1Branch = "release/1.1.0"; const string release2Branch = "release/1.2.0"; const string hotfixBranch = "hotfix/hf"; + var configuration = GitFlowConfigurationBuilder.New.Build(); - using var fixture = new BaseGitFlowRepositoryFixture("1.0.0"); - fixture.AssertFullSemver("1.1.0-alpha.1", configuration); + using var fixture = new BaseGitFlowRepositoryFixture(initialMainAction, deleteOnDispose: false); + var fullSemver = "1.1.0-alpha.1"; + fixture.AssertFullSemver(fullSemver, configuration); // Feature 1 fixture.BranchTo(feature1Branch); - fixture.MakeACommit("added feature 1"); - fixture.AssertFullSemver("1.1.0-f1.1+2", configuration); + + fixture.MakeACommit($"added feature 1 >> {fullSemver}"); + fullSemver = "1.1.0-f1.1+2"; + fixture.AssertFullSemver(fullSemver, configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(feature1Branch); - fixture.Repository.Branches.Remove(fixture.Repository.Branches[feature1Branch]); + if (!keepBranches) fixture.Repository.Branches.Remove(fixture.Repository.Branches[feature1Branch]); fixture.AssertFullSemver("1.1.0-alpha.3", configuration); // Release 1.1.0 @@ -45,17 +52,19 @@ public void GitflowComplexExample() // Feature 2 fixture.BranchTo(feature2Branch); - fixture.MakeACommit("added feature 2"); - fixture.AssertFullSemver("1.2.0-f2.1+2", configuration); + fullSemver = "1.2.0-f2.1+2"; + fixture.MakeACommit($"added feature 2 >> {fullSemver}"); + fixture.AssertFullSemver(fullSemver, configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(feature2Branch); - fixture.Repository.Branches.Remove(fixture.Repository.Branches[feature2Branch]); + if (!keepBranches) fixture.Repository.Branches.Remove(fixture.Repository.Branches[feature2Branch]); fixture.AssertFullSemver("1.2.0-alpha.3", configuration); // Release 1.2.0 fixture.BranchTo(release2Branch); - fixture.MakeACommit("release stabilization"); - fixture.AssertFullSemver("1.2.0-beta.1+8", configuration); + fullSemver = "1.2.0-beta.1+8"; + fixture.MakeACommit($"release stabilization >> {fullSemver}"); + fixture.AssertFullSemver(fullSemver, configuration); fixture.Checkout(MainBranch); fixture.MergeNoFF(release2Branch); fixture.AssertFullSemver("1.2.0-5", configuration); @@ -63,14 +72,15 @@ public void GitflowComplexExample() fixture.AssertFullSemver("1.2.0", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(release2Branch); - fixture.Repository.Branches.Remove(fixture.Repository.Branches[release2Branch]); + if (!keepBranches) fixture.Repository.Branches.Remove(fixture.Repository.Branches[release2Branch]); fixture.AssertFullSemver("1.3.0-alpha.1", configuration); // Hotfix fixture.Checkout(MainBranch); fixture.BranchTo(hotfixBranch); - fixture.MakeACommit("added hotfix"); - fixture.AssertFullSemver("1.2.1-beta.1+1", configuration); + fullSemver = "1.2.1-beta.1+1"; + fixture.MakeACommit($"added hotfix >> {fullSemver}"); + fixture.AssertFullSemver(fullSemver, configuration); fixture.Checkout(MainBranch); fixture.MergeNoFF(hotfixBranch); fixture.AssertFullSemver("1.2.1-2", configuration); @@ -78,7 +88,63 @@ public void GitflowComplexExample() fixture.AssertFullSemver("1.2.1", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(hotfixBranch); - fixture.Repository.Branches.Remove(fixture.Repository.Branches[hotfixBranch]); + if (!keepBranches) fixture.Repository.Branches.Remove(fixture.Repository.Branches[hotfixBranch]); fixture.AssertFullSemver("1.3.0-alpha.2", configuration); + + fixture.Checkout(feature2Branch); + fixture.AssertFullSemver( + "1.3.0-f2.1+0", + configuration, + customMessage: + "Feature branches use inherited versioning (increment: inherit), " + System.Environment.NewLine + + "and your config inherits from develop." + System.Environment.NewLine + System.Environment.NewLine + + "GitVersion uses the merge base between the feature and develop to determine the version." + System.Environment.NewLine + System.Environment.NewLine + + "As develop progresses (e.g., by releasing 1.2.0), rebuilding old feature branches can" + System.Environment.NewLine + + "produce different versions."); + + fullSemver = "1.3.0-f2.1+1"; + fixture.MakeACommit( + "feature 2 additional commit after original feature has been merged to develop " + System.Environment.NewLine + + $"and release/1.2.0 has already happened >> {fullSemver}" + + "Problem #1: 1.3.0-f2.1+0 is what I observe when I run dotnet-gitversion 6.3.0 but in the repo the assertion is 1.3.0-f2.1+1" + + "After rebase 1.3.0-f2.1+3 is both what the test asserts and what I observe when I run dotnet-gitversion 6.3.0." + + "Problem #2: I expected to get the same before and after the rebase." + + "" + + "Whether my expectations are correct or not could we at least build upon the documentation I have started to add " + + "as an explanation of observed behaviour. I'm happy to translate an explanation in to test " + + "documentation if you confirm it would be accepted on PR." + ); + + var identity = new Identity( + fixture.Repository.Head.Tip.Committer.Name, + fixture.Repository.Head.Tip.Committer.Email); + fixture.AssertFullSemver(fullSemver, configuration); + var rebaseResult = fixture.Repository.Rebase.Start( + fixture.Repository.Branches[feature2Branch], + fixture.Repository.Branches[developBranch], + fixture.Repository.Branches[developBranch], + identity, + new RebaseOptions()); + while (rebaseResult != null && rebaseResult.Status != RebaseStatus.Complete) + { + rebaseResult = fixture.Repository.Rebase.Continue(identity, new RebaseOptions()); + } + + fixture.AssertFullSemver(fullSemver, configuration, customMessage: "I expected to get the same before and after the rebase."); + + void initialMainAction(IRepository r) + { + if (configuration is GitVersionConfiguration concreteConfig) + { + var yaml = new ConfigurationSerializer().Serialize(concreteConfig); + const string fileName = "GitVersion.yml"; + var filePath = FileSystemHelper.Path.Combine(r.Info.Path, "..", fileName); + File.WriteAllText(filePath, yaml); + r.Index.Add(fileName); + r.Index.Write(); + } + + r.MakeATaggedCommit("1.0.0", $"Initial commit on {MainBranch}"); + } } } diff --git a/src/GitVersion.Testing/Fixtures/BaseGitFlowRepositoryFixture.cs b/src/GitVersion.Testing/Fixtures/BaseGitFlowRepositoryFixture.cs index 31d9e7de7f..efdc4b95f3 100644 --- a/src/GitVersion.Testing/Fixtures/BaseGitFlowRepositoryFixture.cs +++ b/src/GitVersion.Testing/Fixtures/BaseGitFlowRepositoryFixture.cs @@ -12,8 +12,8 @@ public class BaseGitFlowRepositoryFixture : EmptyRepositoryFixture /// Creates a repo with a develop branch off main which is a single commit ahead of main branch /// Main will be tagged with the initial version before branching develop /// - public BaseGitFlowRepositoryFixture(string initialVersion, string branchName = MainBranch) : - this(r => r.MakeATaggedCommit(initialVersion), branchName) + public BaseGitFlowRepositoryFixture(string initialVersion, string branchName = MainBranch, bool deleteOnDispose = true) : + this(r => r.MakeATaggedCommit(initialVersion), branchName, deleteOnDispose) { } @@ -21,8 +21,8 @@ public BaseGitFlowRepositoryFixture(string initialVersion, string branchName = M /// Creates a repo with a develop branch off main which is a single commit ahead of main /// The initial setup actions will be performed before branching develop /// - public BaseGitFlowRepositoryFixture(Action initialMainAction, string branchName = MainBranch) : - base(branchName) => SetupRepo(initialMainAction); + public BaseGitFlowRepositoryFixture(Action initialMainAction, string branchName = MainBranch, bool deleteOnDispose = true) : + base(branchName, deleteOnDispose) => SetupRepo(initialMainAction); private void SetupRepo(Action initialMainAction) { @@ -33,6 +33,6 @@ private void SetupRepo(Action initialMainAction) initialMainAction(Repository); Commands.Checkout(Repository, Repository.CreateBranch("develop")); - Repository.MakeACommit(); + Repository.MakeACommit("First commit on new branch 'develop'"); } } diff --git a/src/GitVersion.Testing/Fixtures/EmptyRepositoryFixture.cs b/src/GitVersion.Testing/Fixtures/EmptyRepositoryFixture.cs index 955205b118..bf394da67b 100644 --- a/src/GitVersion.Testing/Fixtures/EmptyRepositoryFixture.cs +++ b/src/GitVersion.Testing/Fixtures/EmptyRepositoryFixture.cs @@ -1,3 +1,3 @@ namespace GitVersion.Testing; -public class EmptyRepositoryFixture(string branchName = RepositoryFixtureBase.MainBranch) : RepositoryFixtureBase(path => CreateNewRepository(path, branchName)); +public class EmptyRepositoryFixture(string branchName = RepositoryFixtureBase.MainBranch, bool deleteOnDispose = true) : RepositoryFixtureBase(path => CreateNewRepository(path, branchName), deleteOnDispose); diff --git a/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs b/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs index cdfa36b8f3..213f0f14cc 100644 --- a/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs +++ b/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs @@ -10,19 +10,20 @@ namespace GitVersion.Testing; public abstract class RepositoryFixtureBase : IDisposable { public const string MainBranch = "master"; - public const bool DeleteOnDispose = true; + private readonly bool deleteOnDispose; - protected RepositoryFixtureBase(Func repositoryBuilder) - : this(repositoryBuilder(FileSystemHelper.Path.GetRepositoryTempPath())) + protected RepositoryFixtureBase(Func repositoryBuilder, bool deleteOnDispose = true) + : this(repositoryBuilder(FileSystemHelper.Path.GetRepositoryTempPath()), deleteOnDispose) { } - protected RepositoryFixtureBase(Repository repository) + protected RepositoryFixtureBase(Repository repository, bool deleteOnDispose = true) { SequenceDiagram = new(); Repository = repository.ShouldNotBeNull(); Repository.Config.Set("user.name", "Test"); Repository.Config.Set("user.email", "test@email.com"); + this.deleteOnDispose = deleteOnDispose; } public Repository Repository { get; } @@ -50,7 +51,7 @@ protected virtual void Dispose(bool disposing) Repository.Dispose(); var directoryPath = FileSystemHelper.Path.GetFileName(RepositoryPath); - if (DeleteOnDispose) + if (deleteOnDispose) { try { diff --git a/src/GitVersion.Testing/GitTestExtensions.cs b/src/GitVersion.Testing/GitTestExtensions.cs index be1a0518cf..28f11143e1 100644 --- a/src/GitVersion.Testing/GitTestExtensions.cs +++ b/src/GitVersion.Testing/GitTestExtensions.cs @@ -38,9 +38,9 @@ private static Commit CreateFileAndCommit(this IRepository repository, string re Generate.SignatureNow(), Generate.SignatureNow()); } - public static Tag MakeATaggedCommit(this IRepository repository, string tag) + public static Tag MakeATaggedCommit(this IRepository repository, string tag, string? commitMessage = null) { - var commit = repository.MakeACommit(); + var commit = repository.MakeACommit(commitMessage); var existingTag = repository.Tags.SingleOrDefault(t => t.FriendlyName == tag); return existingTag ?? repository.Tags.Add(tag, commit); } From 110abbcc4a0122effbde011048a5fe8cf0e89de6 Mon Sep 17 00:00:00 2001 From: 9swampy Date: Sat, 21 Jun 2025 19:29:02 +0100 Subject: [PATCH 3/5] Put default branch back to main. --- CommitsSinceVersionSource.txt | 207 ++++++++++++++++++ src/GitVersion.Core.Tests/Helpers/TestBase.cs | 2 +- .../IntegrationTests/GitflowScenarios.cs | 10 +- .../Fixtures/RepositoryFixtureBase.cs | 2 +- 4 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 CommitsSinceVersionSource.txt diff --git a/CommitsSinceVersionSource.txt b/CommitsSinceVersionSource.txt new file mode 100644 index 0000000000..d807d974aa --- /dev/null +++ b/CommitsSinceVersionSource.txt @@ -0,0 +1,207 @@ + + +@startuml +skinparam participantMargin 10 +skinparam noteMargin 10 +skinparam shadowing false + +participant main +participant develop +participant feature_f1 +participant release_1_1_0 +participant feature_f2 +participant release_1_2_0 +participant hotfix_hf + +note right of main #LightBlue +📝 Commit: A +Version Source: A (initial commit) +Commits beyond Version Source: none +Version: 1.0.0 +end note + +main --> develop: 🌿 branch develop from A + +note right of develop #LightSkyBlue +📝 Commit: B +Version Source: A (already merged to develop) +Commits beyond Version Source: B +Version: 1.1.0-alpha.1 +end note + +develop --> feature_f1: 🌿 branch feature/f1 from B + +note right of feature_f1 #LightGreen +📝 Commit: C +Version Source: B (already merged to develop) +Commits beyond Version Source: C +Version: 1.1.0-f1.1+2 +end note + +feature_f1 --> develop: 🔀 E (merge f1 → develop) + +note right of develop #Khaki +📝 Commit: E +Version Source: E (develop tip) +Commits beyond Version Source: none +Version: 1.1.0-alpha.3 +end note + +develop --> release_1_1_0: 🌿 branch release/1.1.0 from E + +note right of release_1_1_0 #PaleGreen +📝 Commit: F +Version Source: E (already merged to develop) +Commits beyond Version Source: F +Version: 1.1.0-beta.1+4 +end note + +release_1_1_0 --> main: 🔀 G (merge release/1.1.0 → main) + +note right of main #Gold +📝 Commit: G +🏷 Tag: 1.1.0 +Version Source: G (main tip) +Commits beyond Version Source: none +Version: 1.1.0-5 +end note + +main -> main: 🏷 tag 1.1.0 + +note right of main #Gold +Version: 1.1.0 +end note + +release_1_1_0 --> develop: 🔀 H (merge release/1.1.0 → develop) + +note right of develop #Khaki +📝 Commit: H +Version Source: H (develop tip) +Commits beyond Version Source: none +Version: 1.2.0-alpha.1 +end note + +develop --> feature_f2: 🌿 branch feature/f2 from H + +note right of feature_f2 #LightGreen +📝 Commit: I +Version Source: H (already merged to develop) +Commits beyond Version Source: I +Version: 1.2.0-f2.1+2 +end note + +feature_f2 -> feature_f2: Commit 'feature 2 additional commit (J)' + +note right of feature_f2 #LightGreen +📝 Commit: J +Version Source: H (already merged to develop) +Commits beyond Version Source: J +Version: 1.3.0-f2.1+0 +end note + +feature_f2 --> develop: 🔀 K (merge f2 → develop) + +note right of develop #Khaki +📝 Commit: K +Version Source: K (develop tip) +Commits beyond Version Source: none +Version: 1.2.0-alpha.3 +end note + +develop --> release_1_2_0: 🌿 branch release/1.2.0 from K + +note right of release_1_2_0 #PaleGreen +📝 Commit: L +Version Source: K (already merged to develop) +Commits beyond Version Source: L +Beta parent: B.. E,F,G,H,I,J,K +Version: 1.2.0-beta.1+8 +end note + +release_1_2_0 --> main: 🔀 M (merge release/1.2.0 → main) + +note right of main #Gold +📝 Commit: M +🏷 Tag: 1.2.0 +Version Source: M (main tip) +Commits beyond Version Source: none +Version: 1.2.0-5 +end note + +main -> main: 🏷 tag 1.2.0 + +note right of main #Gold +Version: 1.2.0 +end note + +release_1_2_0 --> develop: 🔀 N (merge release/1.2.0 → develop) + +note right of develop #Khaki +📝 Commit: N +Version Source: N (develop tip) +Commits beyond Version Source: none +Version: 1.3.0-alpha.1 +end note + +main --> hotfix_hf: 🌿 branch hotfix/hf from M + +note right of hotfix_hf #LightCoral +📝 Commit: O +Version Source: M (already merged to main) +Commits beyond Version Source: O +Version: 1.2.1-beta.1+1 +end note + +hotfix_hf --> main: 🔀 P (merge hotfix → main) + +note right of main #Gold +📝 Commit: P +🏷 Tag: 1.2.1 +Version Source: P (main tip) +Commits beyond Version Source: none +Version: 1.2.1-2 +end note + +main -> main: 🏷 tag 1.2.1 + +note right of main #Gold +Version: 1.2.1 +end note + +hotfix_hf --> develop: 🔀 Q (merge hotfix → develop) + +note right of develop #Khaki +📝 Commit: Q +Version Source: Q (develop tip) +Commits beyond Version Source: none +Version: 1.3.0-alpha.2 +end note + +note right of feature_f2 #Orange +⬅ Returned to feature/f2 at Commit J +Version Source: J (already merged to develop) +Commits beyond Version Source: none +Version recalculates to: 1.3.0-f2.1+0 +end note + +note right of feature_f2 #LightGreen +Version: 1.3.0-f2.1+0 +end note + +note right of feature_f2 #LightGreen +📝 Commit: R +Version Source: J (already merged to develop) +Commits beyond Version Source: R +Version: 1.3.0-f2.1+1 +end note + +feature_f2 --> develop: --> rebase R onto develop tip Q + +note right of feature_f2 #Thistle +📝 Commit: R′ +Version Source: Q (develop tip) +Commits beyond Version Source: R′ +Version: 1.3.0-f2.1+1 (after rebase) +end note + +@enduml diff --git a/src/GitVersion.Core.Tests/Helpers/TestBase.cs b/src/GitVersion.Core.Tests/Helpers/TestBase.cs index ef5e0fdb53..cd25bd8a27 100644 --- a/src/GitVersion.Core.Tests/Helpers/TestBase.cs +++ b/src/GitVersion.Core.Tests/Helpers/TestBase.cs @@ -7,7 +7,7 @@ namespace GitVersion.Core.Tests.Helpers; public class TestBase { - public const string MainBranch = RepositoryFixtureBase.MainBranch; + public const string MainBranch = "main"; protected static IServiceProvider ConfigureServices(Action? overrideServices = null) { diff --git a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs index 513e144c1c..ad9a9901e9 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs @@ -72,7 +72,10 @@ public void GitflowComplexExample() fixture.AssertFullSemver("1.2.0", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(release2Branch); - if (!keepBranches) fixture.Repository.Branches.Remove(fixture.Repository.Branches[release2Branch]); + if (!keepBranches) + { + fixture.Repository.Branches.Remove(fixture.Repository.Branches[release2Branch]); + } fixture.AssertFullSemver("1.3.0-alpha.1", configuration); // Hotfix @@ -88,7 +91,10 @@ public void GitflowComplexExample() fixture.AssertFullSemver("1.2.1", configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(hotfixBranch); - if (!keepBranches) fixture.Repository.Branches.Remove(fixture.Repository.Branches[hotfixBranch]); + if (!keepBranches) + { + fixture.Repository.Branches.Remove(fixture.Repository.Branches[hotfixBranch]); + } fixture.AssertFullSemver("1.3.0-alpha.2", configuration); fixture.Checkout(feature2Branch); diff --git a/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs b/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs index 213f0f14cc..c8ae3140d5 100644 --- a/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs +++ b/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs @@ -9,7 +9,7 @@ namespace GitVersion.Testing; /// public abstract class RepositoryFixtureBase : IDisposable { - public const string MainBranch = "master"; + public const string MainBranch = "main"; private readonly bool deleteOnDispose; protected RepositoryFixtureBase(Func repositoryBuilder, bool deleteOnDispose = true) From 0e5a247ab7e46d85a69a2019ef8fb1cbaf11cd41 Mon Sep 17 00:00:00 2001 From: 9swampy Date: Mon, 30 Jun 2025 12:06:15 +0100 Subject: [PATCH 4/5] Add CommitsSinceVersionSourceList to VariablesProvider. --- .../GitRepositoryTestingExtensions.cs | 23 ++++++++++++ .../Helpers/TestableGitVersionVariables.cs | 1 + .../IntegrationTests/GitflowScenarios.cs | 1 + .../VersionCalculation/VersionSourceTests.cs | 5 +++ .../OutputVariables/GitVersionVariables.cs | 3 ++ src/GitVersion.Core/PublicAPI.Shipped.txt | 6 ++- src/GitVersion.Core/PublicAPI.Unshipped.txt | 4 +- .../SemVer/SemanticVersionBuildMetaData.cs | 37 +++++++++++++++++-- .../SemanticVersionFormatValues.cs | 2 + .../VersionCalculation/VariableProvider.cs | 1 + .../ContinuousDeliveryVersionCalculator.cs | 1 + .../VersionCalculatorBase.cs | 4 +- 12 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs b/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs index 9de9fdedb0..bee581792e 100644 --- a/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs +++ b/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs @@ -156,6 +156,29 @@ public static void AssertFullSemver(this RepositoryFixtureBase fixture, string f } } + public static void AssertCommitsSinceVersionSource(this RepositoryFixtureBase fixture, int commitsSinceVersionSourceExpected, + IGitVersionConfiguration? configuration = null, IRepository? repository = null, string? commitId = null, bool onlyTrackedBranches = true, string? targetBranch = null, string? customMessage = null) + { + repository ??= fixture.Repository; + + var variables = GetVersion(fixture, configuration, repository, commitId, onlyTrackedBranches, targetBranch); + variables.CommitsSinceVersionSource.ShouldBe(commitsSinceVersionSourceExpected.ToString(), customMessage); + if (commitId == null) + { + var message = new StringBuilder($"CommitsSinceVersionSource:{commitsSinceVersionSourceExpected}"); + if (variables.CommitsSinceVersionSourceList != null) + { + foreach (var sha in variables.CommitsSinceVersionSourceList) + { + message.Append(System.Environment.NewLine); + message.Append($"- {sha}"); + } + } + + fixture.SequenceDiagram.NoteOver(message.ToString(), repository.Head.FriendlyName, color: "#D3D3D3"); + } + } + /// /// Simulates running on build server /// diff --git a/src/GitVersion.Core.Tests/Helpers/TestableGitVersionVariables.cs b/src/GitVersion.Core.Tests/Helpers/TestableGitVersionVariables.cs index 5670ca86a3..0237f5b4ec 100644 --- a/src/GitVersion.Core.Tests/Helpers/TestableGitVersionVariables.cs +++ b/src/GitVersion.Core.Tests/Helpers/TestableGitVersionVariables.cs @@ -26,4 +26,5 @@ internal record TestableGitVersionVariables() : GitVersionVariables("", "", "", "", + "", ""); diff --git a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs index ad9a9901e9..9993e80398 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs @@ -89,6 +89,7 @@ public void GitflowComplexExample() fixture.AssertFullSemver("1.2.1-2", configuration); fixture.ApplyTag("1.2.1"); fixture.AssertFullSemver("1.2.1", configuration); + fixture.AssertCommitsSinceVersionSource(2, configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(hotfixBranch); if (!keepBranches) diff --git a/src/GitVersion.Core.Tests/VersionCalculation/VersionSourceTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/VersionSourceTests.cs index f4618e6d40..3d5fa698d0 100644 --- a/src/GitVersion.Core.Tests/VersionCalculation/VersionSourceTests.cs +++ b/src/GitVersion.Core.Tests/VersionCalculation/VersionSourceTests.cs @@ -24,6 +24,7 @@ public void VersionSourceSha() var semanticVersion = nextVersionCalculator.FindVersion(); semanticVersion.BuildMetaData.VersionSourceSha.ShouldBeNull(); + semanticVersion.BuildMetaData.CommitsSinceVersionSourceList.Length.ShouldBe((int)semanticVersion.BuildMetaData.CommitsSinceVersionSource); semanticVersion.BuildMetaData.CommitsSinceVersionSource.ShouldBe(3); } @@ -39,6 +40,7 @@ public void VersionSourceShaOneCommit() var semanticVersion = nextVersionCalculator.FindVersion(); semanticVersion.BuildMetaData.VersionSourceSha.ShouldBeNull(); + semanticVersion.BuildMetaData.CommitsSinceVersionSourceList.Length.ShouldBe((int)semanticVersion.BuildMetaData.CommitsSinceVersionSource); semanticVersion.BuildMetaData.CommitsSinceVersionSource.ShouldBe(1); } @@ -58,6 +60,9 @@ public void VersionSourceShaUsingTag() var semanticVersion = nextVersionCalculator.FindVersion(); semanticVersion.BuildMetaData.VersionSourceSha.ShouldBe(secondCommitSha); +#if DEBUG + semanticVersion.BuildMetaData.CommitsSinceVersionSourceList.Length.ShouldBe((int)semanticVersion.BuildMetaData.CommitsSinceVersionSource); +#endif semanticVersion.BuildMetaData.CommitsSinceVersionSource.ShouldBe(1); } diff --git a/src/GitVersion.Core/OutputVariables/GitVersionVariables.cs b/src/GitVersion.Core/OutputVariables/GitVersionVariables.cs index 53a8b55172..b378a6e748 100644 --- a/src/GitVersion.Core/OutputVariables/GitVersionVariables.cs +++ b/src/GitVersion.Core/OutputVariables/GitVersionVariables.cs @@ -24,6 +24,7 @@ public record GitVersionVariables(string Major, string? CommitDate, string? VersionSourceSha, string? CommitsSinceVersionSource, + string? CommitsSinceVersionSourceList, string? UncommittedChanges) : IEnumerable> { internal static readonly List AvailableVariables = @@ -52,6 +53,7 @@ public record GitVersionVariables(string Major, nameof(CommitDate), nameof(VersionSourceSha), nameof(CommitsSinceVersionSource), + nameof(CommitsSinceVersionSourceList), nameof(UncommittedChanges) ]; @@ -81,6 +83,7 @@ public record GitVersionVariables(string Major, { nameof(CommitDate), CommitDate }, { nameof(VersionSourceSha), VersionSourceSha }, { nameof(CommitsSinceVersionSource), CommitsSinceVersionSource }, + { nameof(CommitsSinceVersionSourceList), CommitsSinceVersionSourceList }, { nameof(UncommittedChanges), UncommittedChanges } }; diff --git a/src/GitVersion.Core/PublicAPI.Shipped.txt b/src/GitVersion.Core/PublicAPI.Shipped.txt index 2b3133a9e8..7d55a5ccb2 100644 --- a/src/GitVersion.Core/PublicAPI.Shipped.txt +++ b/src/GitVersion.Core/PublicAPI.Shipped.txt @@ -426,6 +426,8 @@ GitVersion.OutputVariables.GitVersionVariables.CommitDate.get -> string? GitVersion.OutputVariables.GitVersionVariables.CommitDate.init -> void GitVersion.OutputVariables.GitVersionVariables.CommitsSinceVersionSource.get -> string? GitVersion.OutputVariables.GitVersionVariables.CommitsSinceVersionSource.init -> void +GitVersion.OutputVariables.GitVersionVariables.CommitsSinceVersionSourceList.get -> string? +GitVersion.OutputVariables.GitVersionVariables.CommitsSinceVersionSourceList.init -> void GitVersion.OutputVariables.GitVersionVariables.EscapedBranchName.get -> string? GitVersion.OutputVariables.GitVersionVariables.EscapedBranchName.init -> void GitVersion.OutputVariables.GitVersionVariables.FullBuildMetaData.get -> string? @@ -433,7 +435,7 @@ GitVersion.OutputVariables.GitVersionVariables.FullBuildMetaData.init -> void GitVersion.OutputVariables.GitVersionVariables.FullSemVer.get -> string! GitVersion.OutputVariables.GitVersionVariables.FullSemVer.init -> void GitVersion.OutputVariables.GitVersionVariables.GetEnumerator() -> System.Collections.Generic.IEnumerator>! -GitVersion.OutputVariables.GitVersionVariables.GitVersionVariables(string! Major, string! Minor, string! Patch, string? BuildMetaData, string? FullBuildMetaData, string? BranchName, string? EscapedBranchName, string? Sha, string? ShortSha, string! MajorMinorPatch, string! SemVer, string! FullSemVer, string? AssemblySemVer, string? AssemblySemFileVer, string? PreReleaseTag, string? PreReleaseTagWithDash, string? PreReleaseLabel, string? PreReleaseLabelWithDash, string? PreReleaseNumber, string! WeightedPreReleaseNumber, string? InformationalVersion, string? CommitDate, string? VersionSourceSha, string? CommitsSinceVersionSource, string? UncommittedChanges) -> void +GitVersion.OutputVariables.GitVersionVariables.GitVersionVariables(string! Major, string! Minor, string! Patch, string? BuildMetaData, string? FullBuildMetaData, string? BranchName, string? EscapedBranchName, string? Sha, string? ShortSha, string! MajorMinorPatch, string! SemVer, string! FullSemVer, string? AssemblySemVer, string? AssemblySemFileVer, string? PreReleaseTag, string? PreReleaseTagWithDash, string? PreReleaseLabel, string? PreReleaseLabelWithDash, string? PreReleaseNumber, string! WeightedPreReleaseNumber, string? InformationalVersion, string? CommitDate, string? VersionSourceSha, string? CommitsSinceVersionSource, string? CommitsSinceVersionSourceList, string? UncommittedChanges) -> void GitVersion.OutputVariables.GitVersionVariables.InformationalVersion.get -> string? GitVersion.OutputVariables.GitVersionVariables.InformationalVersion.init -> void GitVersion.OutputVariables.GitVersionVariables.Major.get -> string! @@ -520,6 +522,8 @@ GitVersion.SemanticVersionBuildMetaData.CommitsSinceTag.get -> long? GitVersion.SemanticVersionBuildMetaData.CommitsSinceTag.init -> void GitVersion.SemanticVersionBuildMetaData.CommitsSinceVersionSource.get -> long GitVersion.SemanticVersionBuildMetaData.CommitsSinceVersionSource.init -> void +GitVersion.SemanticVersionBuildMetaData.CommitsSinceVersionSourceList.get -> string[] +GitVersion.SemanticVersionBuildMetaData.CommitsSinceVersionSourceList.init -> void GitVersion.SemanticVersionBuildMetaData.Equals(GitVersion.SemanticVersionBuildMetaData? other) -> bool GitVersion.SemanticVersionBuildMetaData.OtherMetaData.get -> string? GitVersion.SemanticVersionBuildMetaData.OtherMetaData.init -> void diff --git a/src/GitVersion.Core/PublicAPI.Unshipped.txt b/src/GitVersion.Core/PublicAPI.Unshipped.txt index 505df687a4..d77a85acd0 100644 --- a/src/GitVersion.Core/PublicAPI.Unshipped.txt +++ b/src/GitVersion.Core/PublicAPI.Unshipped.txt @@ -11,10 +11,12 @@ GitVersion.Git.AuthenticationInfo.AuthenticationInfo() -> void GitVersion.Git.AuthenticationInfo.AuthenticationInfo(GitVersion.Git.AuthenticationInfo! original) -> void GitVersion.Git.CommitFilter.CommitFilter() -> void GitVersion.Git.CommitFilter.CommitFilter(GitVersion.Git.CommitFilter! original) -> void -GitVersion.OutputVariables.GitVersionVariables.Deconstruct(out string! Major, out string! Minor, out string! Patch, out string? BuildMetaData, out string? FullBuildMetaData, out string? BranchName, out string? EscapedBranchName, out string? Sha, out string? ShortSha, out string! MajorMinorPatch, out string! SemVer, out string! FullSemVer, out string? AssemblySemVer, out string? AssemblySemFileVer, out string? PreReleaseTag, out string? PreReleaseTagWithDash, out string? PreReleaseLabel, out string? PreReleaseLabelWithDash, out string? PreReleaseNumber, out string! WeightedPreReleaseNumber, out string? InformationalVersion, out string? CommitDate, out string? VersionSourceSha, out string? CommitsSinceVersionSource, out string? UncommittedChanges) -> void +GitVersion.OutputVariables.GitVersionVariables.Deconstruct(out string! Major, out string! Minor, out string! Patch, out string? BuildMetaData, out string? FullBuildMetaData, out string? BranchName, out string? EscapedBranchName, out string? Sha, out string? ShortSha, out string! MajorMinorPatch, out string! SemVer, out string! FullSemVer, out string? AssemblySemVer, out string? AssemblySemFileVer, out string? PreReleaseTag, out string? PreReleaseTagWithDash, out string? PreReleaseLabel, out string? PreReleaseLabelWithDash, out string? PreReleaseNumber, out string! WeightedPreReleaseNumber, out string? InformationalVersion, out string? CommitDate, out string? VersionSourceSha, out string? CommitsSinceVersionSource, out string? CommitsSinceVersionSourceList, out string? UncommittedChanges) -> void GitVersion.OutputVariables.GitVersionVariables.GitVersionVariables(GitVersion.OutputVariables.GitVersionVariables! original) -> void GitVersion.RepositoryInfo.RepositoryInfo() -> void GitVersion.RepositoryInfo.RepositoryInfo(GitVersion.RepositoryInfo! original) -> void +GitVersion.SemanticVersionBuildMetaData.CommitsSinceVersionSourceList.get -> string![]! +GitVersion.SemanticVersionFormatValues.CommitsSinceVersionSourceList.get -> string? GitVersion.SemanticVersionWithTag.$() -> GitVersion.SemanticVersionWithTag! GitVersion.SemanticVersionWithTag.Deconstruct(out GitVersion.SemanticVersion! Value, out GitVersion.Git.ITag! Tag) -> void GitVersion.SemanticVersionWithTag.Equals(GitVersion.SemanticVersionWithTag? other) -> bool diff --git a/src/GitVersion.Core/SemVer/SemanticVersionBuildMetaData.cs b/src/GitVersion.Core/SemVer/SemanticVersionBuildMetaData.cs index 88f2fc658a..14daa65f3e 100644 --- a/src/GitVersion.Core/SemVer/SemanticVersionBuildMetaData.cs +++ b/src/GitVersion.Core/SemVer/SemanticVersionBuildMetaData.cs @@ -26,7 +26,17 @@ public class SemanticVersionBuildMetaData : IFormattable, IEquatable semver.BuildMetaData.CommitsSinceVersionSource.ToString(CultureInfo.InvariantCulture); + public string? CommitsSinceVersionSourceList => string.Join(", ", semver.BuildMetaData.CommitsSinceVersionSourceList ?? []).ToString(CultureInfo.InvariantCulture); + public string UncommittedChanges => semver.BuildMetaData.UncommittedChanges.ToString(CultureInfo.InvariantCulture); } diff --git a/src/GitVersion.Core/VersionCalculation/VariableProvider.cs b/src/GitVersion.Core/VersionCalculation/VariableProvider.cs index 0cb26fa7a7..9f35b84c41 100644 --- a/src/GitVersion.Core/VersionCalculation/VariableProvider.cs +++ b/src/GitVersion.Core/VersionCalculation/VariableProvider.cs @@ -63,6 +63,7 @@ public GitVersionVariables GetVariablesFor( semverFormatValues.CommitDate, semverFormatValues.VersionSourceSha, semverFormatValues.CommitsSinceVersionSource, + semverFormatValues.CommitsSinceVersionSourceList, semverFormatValues.UncommittedChanges ); } diff --git a/src/GitVersion.Core/VersionCalculation/VersionCalculators/ContinuousDeliveryVersionCalculator.cs b/src/GitVersion.Core/VersionCalculation/VersionCalculators/ContinuousDeliveryVersionCalculator.cs index 91098dae31..5c67205b87 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionCalculators/ContinuousDeliveryVersionCalculator.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionCalculators/ContinuousDeliveryVersionCalculator.cs @@ -35,6 +35,7 @@ private SemanticVersion CalculateInternal(SemanticVersion semanticVersion, IComm BuildMetaData = new SemanticVersionBuildMetaData(buildMetaData) { CommitsSinceVersionSource = buildMetaData.CommitsSinceTag!.Value, + CommitsSinceVersionSourceList = buildMetaData.CommitsSinceVersionSourceList, CommitsSinceTag = null } }; diff --git a/src/GitVersion.Core/VersionCalculation/VersionCalculators/VersionCalculatorBase.cs b/src/GitVersion.Core/VersionCalculation/VersionCalculators/VersionCalculatorBase.cs index db6c3eb135..f321c15ab4 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionCalculators/VersionCalculatorBase.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionCalculators/VersionCalculatorBase.cs @@ -33,7 +33,9 @@ protected SemanticVersionBuildMetaData CreateVersionBuildMetaData(ICommit? baseV commitSha: Context.CurrentCommit.Sha, commitShortSha: shortSha, commitDate: Context.CurrentCommit.When, - numberOfUnCommittedChanges: Context.NumberOfUncommittedChanges + numberOfUnCommittedChanges: Context.NumberOfUncommittedChanges, + otherMetadata: null, + commitSinceTagList: [.. commitLogs.Select(c => c.Sha)] ); } } From 2f23dd0aad5d0cc6e2549cef92950b4e48d36b19 Mon Sep 17 00:00:00 2001 From: 9swampy Date: Tue, 1 Jul 2025 00:48:33 +0100 Subject: [PATCH 5/5] Add CommitLabelGenerator and additional documentation to tests. --- .../Core/RepositoryStoreTests.cs | 23 +++++ .../GitRepositoryTestingExtensions.cs | 26 +++-- .../Helpers/CommitLabelGeneratorTests.cs | 94 +++++++++++++++++++ .../IntegrationTests/GitflowScenarios.cs | 79 +++++++++++----- .../Fixtures/SequenceDiagram.cs | 5 + .../Helpers/CommitLabelGenerator.cs | 87 +++++++++++++++++ 6 files changed, 287 insertions(+), 27 deletions(-) create mode 100644 src/GitVersion.Core.Tests/Helpers/CommitLabelGeneratorTests.cs create mode 100644 src/GitVersion.Testing/Helpers/CommitLabelGenerator.cs diff --git a/src/GitVersion.Core.Tests/Core/RepositoryStoreTests.cs b/src/GitVersion.Core.Tests/Core/RepositoryStoreTests.cs index 9e58ad8bcb..fb606b1e2b 100644 --- a/src/GitVersion.Core.Tests/Core/RepositoryStoreTests.cs +++ b/src/GitVersion.Core.Tests/Core/RepositoryStoreTests.cs @@ -86,33 +86,56 @@ public void FindsCorrectMergeBaseForForwardMergeMovesOn() //*91bf945 58 minutes ago(main) using var fixture = new EmptyRepositoryFixture(); fixture.MakeACommit("initial"); + fixture.AssertFullSemver("0.0.1-1"); + fixture.AssertCommitsSinceVersionSource(1); fixture.BranchTo("develop"); + fixture.AssertFullSemver("0.1.0-alpha.1"); var fixtureRepository = fixture.Repository.ToGitRepository(); var expectedReleaseMergeBase = fixtureRepository.Head.Tip; + fixture.SequenceDiagram.NoteOver(string.Join(System.Environment.NewLine, ("Expected Release Merge Base" + System.Environment.NewLine + System.Environment.NewLine + + "This is the first common ancestor of both develop and release, from release's perspective.").SplitIntoLines(30)), "main", "develop"); // Create release from develop fixture.BranchTo("release-2.0.0"); + fixture.AssertFullSemver("2.0.0-beta.1+1"); // Make some commits on release fixture.MakeACommit("release 1"); + fixture.AssertCommitsSinceVersionSource(2); + fixture.AssertFullSemver("2.0.0-beta.1+2"); fixture.MakeACommit("release 2"); + fixture.AssertCommitsSinceVersionSource(3); + fixture.AssertFullSemver("2.0.0-beta.1+3"); + fixture.SequenceDiagram.NoteOver(string.Join(System.Environment.NewLine, ("Expected Develop Merge Base" + System.Environment.NewLine + System.Environment.NewLine + + "This is a common ancestor from develop's perspective because it is aware of the merge from release." + System.Environment.NewLine + + "It is NOT an common ancestor from release's perspective because release is NOT aware of the merge to develop.").SplitIntoLines(30)), "release-2.0.0"); var expectedDevelopMergeBase = fixtureRepository.Head.Tip; // First forward merge release to develop fixture.Checkout("develop"); fixture.MergeNoFF("release-2.0.0"); + fixture.AssertFullSemver("2.1.0-alpha.3"); + fixture.AssertCommitsSinceVersionSource(3); // Make some new commit on release fixture.Checkout("release-2.0.0"); fixture.MakeACommit("release 3 - after first merge"); + fixture.AssertFullSemver("2.0.0-beta.1+4"); + fixture.AssertCommitsSinceVersionSource(4); // Make new commit on develop fixture.Checkout("develop"); + fixture.AssertFullSemver("2.1.0-alpha.3"); // Checkout to release (no new commits) fixture.MakeACommit("develop after merge"); + fixture.AssertFullSemver("2.1.0-alpha.4"); + fixture.AssertCommitsSinceVersionSource(4); // Checkout to release (no new commits) fixture.Checkout("release-2.0.0"); + fixture.SequenceDiagram.NoteOver("Checkout release-2.0.0 again", "release-2.0.0"); + fixture.AssertFullSemver("2.0.0-beta.1+4"); + fixture.AssertCommitsSinceVersionSource(4); var develop = fixtureRepository.FindBranch("develop"); var release = fixtureRepository.FindBranch("release-2.0.0"); diff --git a/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs b/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs index bee581792e..97fad3e742 100644 --- a/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs +++ b/src/GitVersion.Core.Tests/Extensions/GitRepositoryTestingExtensions.cs @@ -165,17 +165,31 @@ public static void AssertCommitsSinceVersionSource(this RepositoryFixtureBase fi variables.CommitsSinceVersionSource.ShouldBe(commitsSinceVersionSourceExpected.ToString(), customMessage); if (commitId == null) { - var message = new StringBuilder($"CommitsSinceVersionSource:{commitsSinceVersionSourceExpected}"); - if (variables.CommitsSinceVersionSourceList != null) + var message = new StringBuilder(); + var versionSourceLabel = fixture.SequenceDiagram.GetOrAddSourceLabel(variables.VersionSourceSha); + message.AppendLine($" Commit: {fixture.SequenceDiagram.GetOrAddLabel(variables.Sha)}"); + message.Append($" Version source: {versionSourceLabel}"); + if (commitsSinceVersionSourceExpected != 0 && variables.CommitsSinceVersionSourceList != null) { - foreach (var sha in variables.CommitsSinceVersionSourceList) + bool isFirst = true; + foreach (var sha in variables.CommitsSinceVersionSourceList.Split(", ").Reverse()) { - message.Append(System.Environment.NewLine); - message.Append($"- {sha}"); + if (isFirst) + { + message.Append(System.Environment.NewLine); + message.Append($"Commits since version source: {variables.CommitsSinceVersionSource} - "); + isFirst = false; + } + else + { + message.Append(", "); + } + + message.Append($"{fixture.SequenceDiagram.GetOrAddLabel(sha)}"); } } - fixture.SequenceDiagram.NoteOver(message.ToString(), repository.Head.FriendlyName, color: "#D3D3D3"); + fixture.SequenceDiagram.NoteOver(message.ToString(), repository.Head.FriendlyName, color: "#A9B7C6"); } } diff --git a/src/GitVersion.Core.Tests/Helpers/CommitLabelGeneratorTests.cs b/src/GitVersion.Core.Tests/Helpers/CommitLabelGeneratorTests.cs new file mode 100644 index 0000000000..0343ad7d04 --- /dev/null +++ b/src/GitVersion.Core.Tests/Helpers/CommitLabelGeneratorTests.cs @@ -0,0 +1,94 @@ +namespace GitVersion.Testing.Helpers; + +[TestFixture] +public class CommitLabelGeneratorTests +{ + [TestCase("")] + [TestCase(" ")] + public void GetOrAdd_ShouldThrow_WhenKeyIsEmptyOrWhitespace(string key) + { + var sut = new CommitLabelGenerator(); + var ex = Should.Throw(() => sut.GetOrAdd(key)); + ex.Message.ShouldContain("SHA cannot be empty or whitespace"); + ex.ParamName.ShouldBe("key"); + } + + [Test] + public void GetOrAdd_ShouldReturnNA_WhenKeyIsNA() + { + var sut = new CommitLabelGenerator(); + sut.GetOrAdd("N/A").ShouldBe("N/A"); + } + + [Test] + public void GetOrAdd_ShouldAssignLabels_Sequentially() + { + var sut = new CommitLabelGenerator(); + + sut.GetOrAdd("sha1").ShouldBe("A"); + sut.GetOrAdd("sha2").ShouldBe("B"); + sut.GetOrAdd("sha3").ShouldBe("C"); + } + + [Test] + public void GetOrAdd_ShouldReturnSameLabel_OnRepeatedCalls() + { + var sut = new CommitLabelGenerator(); + sut.GetOrAdd("sha1").ShouldBe(sut.GetOrAdd("sha1")); + } + + [TestCase("")] + [TestCase(" ")] + public void GetOrAddRoot_ShouldThrow_WhenKeyIsEmptyOrWhitespace(string key) + { + var sut = new CommitLabelGenerator(); + var ex = Should.Throw(() => sut.GetOrAddRoot(key)); + ex.Message.ShouldContain("Version source SHA cannot be empty or whitespace"); + ex.ParamName.ShouldBe("versionSourceSha"); + } + + [Test] + public void GetOrAddRoot_ShouldReturnNA_WhenKeyIsNA() + { + var sut = new CommitLabelGenerator(); + sut.GetOrAddRoot("N/A").ShouldBe("N/A"); + } + + [Test] + public void GetOrAddRoot_ShouldAssignRootLabels_Sequentially() + { + var sut = new CommitLabelGenerator(); + + sut.GetOrAddRoot("rootsha1").ShouldBe("RootA"); + sut.GetOrAddRoot("rootsha2").ShouldBe("RootB"); + sut.GetOrAddRoot("rootsha3").ShouldBe("RootC"); + } + + [Test] + public void GetOrAddRoot_ShouldReturnSameLabel_OnRepeatedCalls() + { + var sut = new CommitLabelGenerator(); + sut.GetOrAddRoot("rootsha1").ShouldBe(sut.GetOrAddRoot("rootsha1")); + } + + [Test] + public void GetOrAddRoot_And_GetOrAdd_ShouldMaintainSequenceLabels() + { + var sut = new CommitLabelGenerator(); + + sut.GetOrAddRoot("sha1").ShouldBe("RootA"); + sut.GetOrAdd("sha2").ShouldBe("B"); + sut.GetOrAddRoot("sha3").ShouldBe("RootC"); + } + + [Test] + public void GetOrAddRoot_And_GetOrAdd_ShouldAlign() + { + var sut = new CommitLabelGenerator(); + + sut.GetOrAddRoot("sha1").ShouldBe("RootA"); + sut.GetOrAdd("sha1").ShouldBe("RootA"); + sut.GetOrAdd("sha2").ShouldBe("B"); + sut.GetOrAddRoot("sha2").ShouldBe("B"); + } +} diff --git a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs index 9993e80398..28e8f8f7e6 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/GitflowScenarios.cs @@ -1,13 +1,23 @@ using GitVersion.Configuration; using GitVersion.Core.Tests.Helpers; using GitVersion.Helpers; +using GitVersion.Logging; using LibGit2Sharp; +using Microsoft.Extensions.DependencyInjection; namespace GitVersion.Core.Tests.IntegrationTests; [TestFixture] public class GitflowScenarios : TestBase { + private readonly ILog log; + + public GitflowScenarios() + { + var sp = ConfigureServices(); + this.log = sp.GetRequiredService(); + } + [Test] public void GitflowComplexExample() { @@ -21,9 +31,10 @@ public void GitflowComplexExample() var configuration = GitFlowConfigurationBuilder.New.Build(); - using var fixture = new BaseGitFlowRepositoryFixture(initialMainAction, deleteOnDispose: false); + using var fixture = new BaseGitFlowRepositoryFixture(InitialMainAction, deleteOnDispose: false); var fullSemver = "1.1.0-alpha.1"; fixture.AssertFullSemver(fullSemver, configuration); + fixture.AssertCommitsSinceVersionSource(1, configuration); // Feature 1 fixture.BranchTo(feature1Branch); @@ -31,45 +42,56 @@ public void GitflowComplexExample() fixture.MakeACommit($"added feature 1 >> {fullSemver}"); fullSemver = "1.1.0-f1.1+2"; fixture.AssertFullSemver(fullSemver, configuration); + fixture.AssertCommitsSinceVersionSource(2, configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(feature1Branch); if (!keepBranches) fixture.Repository.Branches.Remove(fixture.Repository.Branches[feature1Branch]); fixture.AssertFullSemver("1.1.0-alpha.3", configuration); + fixture.AssertCommitsSinceVersionSource(3, configuration); // Release 1.1.0 fixture.BranchTo(release1Branch); fixture.MakeACommit("release stabilization"); fixture.AssertFullSemver("1.1.0-beta.1+4", configuration); + fixture.AssertCommitsSinceVersionSource(4, configuration); fixture.Checkout(MainBranch); fixture.MergeNoFF(release1Branch); fixture.AssertFullSemver("1.1.0-5", configuration); + fixture.AssertCommitsSinceVersionSource(5, configuration); fixture.ApplyTag("1.1.0"); fixture.AssertFullSemver("1.1.0", configuration); + fixture.AssertCommitsSinceVersionSource(0, configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(release1Branch); fixture.Repository.Branches.Remove(fixture.Repository.Branches[release1Branch]); fixture.AssertFullSemver("1.2.0-alpha.1", configuration); + fixture.AssertCommitsSinceVersionSource(1, configuration); // Feature 2 fixture.BranchTo(feature2Branch); fullSemver = "1.2.0-f2.1+2"; fixture.MakeACommit($"added feature 2 >> {fullSemver}"); fixture.AssertFullSemver(fullSemver, configuration); + fixture.AssertCommitsSinceVersionSource(2, configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(feature2Branch); if (!keepBranches) fixture.Repository.Branches.Remove(fixture.Repository.Branches[feature2Branch]); fixture.AssertFullSemver("1.2.0-alpha.3", configuration); + fixture.AssertCommitsSinceVersionSource(3, configuration); // Release 1.2.0 fixture.BranchTo(release2Branch); fullSemver = "1.2.0-beta.1+8"; fixture.MakeACommit($"release stabilization >> {fullSemver}"); fixture.AssertFullSemver(fullSemver, configuration); + fixture.AssertCommitsSinceVersionSource(8, configuration); fixture.Checkout(MainBranch); fixture.MergeNoFF(release2Branch); fixture.AssertFullSemver("1.2.0-5", configuration); + fixture.AssertCommitsSinceVersionSource(5, configuration); fixture.ApplyTag("1.2.0"); fixture.AssertFullSemver("1.2.0", configuration); + fixture.AssertCommitsSinceVersionSource(0, configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(release2Branch); if (!keepBranches) @@ -77,6 +99,7 @@ public void GitflowComplexExample() fixture.Repository.Branches.Remove(fixture.Repository.Branches[release2Branch]); } fixture.AssertFullSemver("1.3.0-alpha.1", configuration); + fixture.AssertCommitsSinceVersionSource(1, configuration); // Hotfix fixture.Checkout(MainBranch); @@ -84,12 +107,14 @@ public void GitflowComplexExample() fullSemver = "1.2.1-beta.1+1"; fixture.MakeACommit($"added hotfix >> {fullSemver}"); fixture.AssertFullSemver(fullSemver, configuration); + fixture.AssertCommitsSinceVersionSource(1, configuration); fixture.Checkout(MainBranch); fixture.MergeNoFF(hotfixBranch); fixture.AssertFullSemver("1.2.1-2", configuration); + fixture.AssertCommitsSinceVersionSource(2, configuration); fixture.ApplyTag("1.2.1"); fixture.AssertFullSemver("1.2.1", configuration); - fixture.AssertCommitsSinceVersionSource(2, configuration); + fixture.AssertCommitsSinceVersionSource(0, configuration); fixture.Checkout(developBranch); fixture.MergeNoFF(hotfixBranch); if (!keepBranches) @@ -97,35 +122,42 @@ public void GitflowComplexExample() fixture.Repository.Branches.Remove(fixture.Repository.Branches[hotfixBranch]); } fixture.AssertFullSemver("1.3.0-alpha.2", configuration); + fixture.AssertCommitsSinceVersionSource(2, configuration); fixture.Checkout(feature2Branch); - fixture.AssertFullSemver( - "1.3.0-f2.1+0", - configuration, - customMessage: - "Feature branches use inherited versioning (increment: inherit), " + System.Environment.NewLine + - "and your config inherits from develop." + System.Environment.NewLine + System.Environment.NewLine + + fixture.SequenceDiagram.NoteOver($"Checkout {feature2Branch}", feature2Branch); + fixture.AssertFullSemver("1.3.0-f2.1+0", configuration); + fixture.SequenceDiagram.NoteOver( + string.Join(System.Environment.NewLine, ("Feature branches are configured to inherit version (increment: inherit)." + System.Environment.NewLine + System.Environment.NewLine + "GitVersion uses the merge base between the feature and develop to determine the version." + System.Environment.NewLine + System.Environment.NewLine + - "As develop progresses (e.g., by releasing 1.2.0), rebuilding old feature branches can" + System.Environment.NewLine + - "produce different versions."); + "As develop progresses (e.g., by releasing 1.2.0 & 1.2.1), rebuilding old feature branches can produce different versions." + System.Environment.NewLine + System.Environment.NewLine + + "Here we've checked out commit H again and now it's it's own VersionSource and produces 1.3.0-f2.1+0").SplitIntoLines(60)), feature2Branch); + fixture.AssertCommitsSinceVersionSource(0, configuration); fullSemver = "1.3.0-f2.1+1"; fixture.MakeACommit( "feature 2 additional commit after original feature has been merged to develop " + System.Environment.NewLine + - $"and release/1.2.0 has already happened >> {fullSemver}" + - "Problem #1: 1.3.0-f2.1+0 is what I observe when I run dotnet-gitversion 6.3.0 but in the repo the assertion is 1.3.0-f2.1+1" + - "After rebase 1.3.0-f2.1+3 is both what the test asserts and what I observe when I run dotnet-gitversion 6.3.0." + - "Problem #2: I expected to get the same before and after the rebase." + - "" + - "Whether my expectations are correct or not could we at least build upon the documentation I have started to add " + - "as an explanation of observed behaviour. I'm happy to translate an explanation in to test " + - "documentation if you confirm it would be accepted on PR." + $"and release/1.2.0 has already happened >> {fullSemver}" ); + fixture.AssertFullSemver(fullSemver, configuration); + fixture.AssertCommitsSinceVersionSource(1, configuration); + fixture.SequenceDiagram.NoteOver( + string.Join(System.Environment.NewLine, ($"We committed again to {feature2Branch}." + System.Environment.NewLine + System.Environment.NewLine + + "Why is the VersionSource no longer H but has instead jumped to N?" + System.Environment.NewLine + System.Environment.NewLine + + $"I expected this to produce {fullSemver} and it does.").SplitIntoLines(60)), feature2Branch); + + var gitRepository = fixture.Repository.ToGitRepository(); + var gitRepoMetadataProvider = new RepositoryStore(this.log, gitRepository); + // H can't be it's own ancestor, so merge base is G + fixture.SequenceDiagram.GetOrAddLabel(gitRepoMetadataProvider.FindMergeBase(gitRepository.Branches[feature2Branch], gitRepository.Branches[developBranch]).Sha).ShouldBe("G"); + fixture.SequenceDiagram.GetOrAddLabel(gitRepoMetadataProvider.FindMergeBase(gitRepository.Branches[feature2Branch], gitRepository.Branches[MainBranch]).Sha).ShouldBe("G"); + // Why is H it's own VersionSource though if after committing with H as the ancestor we get N as the VersionSource? + + fixture.SequenceDiagram.NoteOver($"Now we rebase {feature2Branch} onto {developBranch}", feature2Branch); var identity = new Identity( fixture.Repository.Head.Tip.Committer.Name, fixture.Repository.Head.Tip.Committer.Email); - fixture.AssertFullSemver(fullSemver, configuration); var rebaseResult = fixture.Repository.Rebase.Start( fixture.Repository.Branches[feature2Branch], fixture.Repository.Branches[developBranch], @@ -137,9 +169,14 @@ public void GitflowComplexExample() rebaseResult = fixture.Repository.Rebase.Continue(identity, new RebaseOptions()); } - fixture.AssertFullSemver(fullSemver, configuration, customMessage: "I expected to get the same before and after the rebase."); + fullSemver = "1.3.0-f2.1+3"; + fixture.AssertFullSemver(fullSemver, configuration); + fixture.AssertCommitsSinceVersionSource(3, configuration); + fixture.SequenceDiagram.NoteOver( + string.Join(System.Environment.NewLine, $"Post rebase the VersionSource is again N - the last commit on {MainBranch}." + System.Environment.NewLine + System.Environment.NewLine + + $"I expected this to produce 1.3.0-f2.1+1 and have a VersionSource of O with self as one commit since VersionSource. Instead VersionSource of N produces {fullSemver}, with a count traversal that includes both L and O!".SplitIntoLines(60)), feature2Branch); - void initialMainAction(IRepository r) + void InitialMainAction(IRepository r) { if (configuration is GitVersionConfiguration concreteConfig) { diff --git a/src/GitVersion.Testing/Fixtures/SequenceDiagram.cs b/src/GitVersion.Testing/Fixtures/SequenceDiagram.cs index 35e73d21cf..6338968241 100644 --- a/src/GitVersion.Testing/Fixtures/SequenceDiagram.cs +++ b/src/GitVersion.Testing/Fixtures/SequenceDiagram.cs @@ -10,6 +10,7 @@ public class SequenceDiagram { private readonly Dictionary participants = []; private readonly StringBuilder diagramBuilder; + private readonly CommitLabelGenerator commitLabelGenerator = new(); /// /// Initializes a new instance of the class. @@ -125,4 +126,8 @@ public void BranchToFromTag(string branchName, string fromTag, string onBranch, /// returns the plantUML representation of the Sequence Diagram /// public string GetDiagram() => this.diagramBuilder.ToString(); + + public string GetOrAddLabel(string? key) => commitLabelGenerator.GetOrAdd(key ?? "N/A"); + + public string GetOrAddSourceLabel(string? key) => commitLabelGenerator.GetOrAddRoot(key ?? "N/A"); } diff --git a/src/GitVersion.Testing/Helpers/CommitLabelGenerator.cs b/src/GitVersion.Testing/Helpers/CommitLabelGenerator.cs new file mode 100644 index 0000000000..1b1a7818e3 --- /dev/null +++ b/src/GitVersion.Testing/Helpers/CommitLabelGenerator.cs @@ -0,0 +1,87 @@ +namespace GitVersion.Testing.Helpers; + +/// +/// Generates unique uppercase letter labels (A, B, ..., Z, AA, AB, etc.) for unique keys. +/// +public class CommitLabelGenerator +{ + private readonly Dictionary lookup = new(); + + /// + /// Gets the label assigned to the specified key, creating a new one if not already assigned. + /// + public string GetOrAdd(string key) + { + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("SHA cannot be empty or whitespace.", nameof(key)); + } + + if (key == "N/A") + { + return "N/A"; + } + + if (lookup.TryGetValue(key, out var existing)) + { + return existing; + } + + var label = GetNextLabel(); + lookup[key] = label; + return label; + } + + public string GetOrAddRoot(string? versionSourceSha) + { + if (string.IsNullOrWhiteSpace(versionSourceSha)) + { + throw new ArgumentException("Version source SHA cannot be empty or whitespace.", nameof(versionSourceSha)); + } + + if (versionSourceSha == "N/A") + { + return "N/A"; + } + + return GetOrAddRootPrivate(versionSourceSha); + } + + private string GetOrAddRootPrivate(string key) + { + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("SHA cannot be empty or whitespace.", nameof(key)); + } + + if (key == "N/A") + { + return "N/A"; + } + + if (lookup.TryGetValue(key, out var existing)) + { + return existing; + } + + var label = GetNextRootLabel(); + lookup[key] = label; + return label; + } + + private string GetNextRootLabel() => "Root" + GetNextLabel(); + + private string GetNextLabel() + { + var index = lookup.Count; + var label = string.Empty; + + do + { + label = (char)('A' + (index % 26)) + label; + index = (index / 26) - 1; + } while (index >= 0); + + return label; + } +}