From 5a22bce9fc92c8ae15bea372b11fbea6e959b4a0 Mon Sep 17 00:00:00 2001 From: Jonathon Wyza Date: Wed, 18 Mar 2026 06:57:42 -0500 Subject: [PATCH 1/4] Adds the ability to allow for shallow clones with a command line argument --- .../Configuration/AppSettings.cs | 2 + source/OctoVersion.Core/OctoVersionRunner.cs | 2 +- .../VersionCalculatorFactory.cs | 25 +- .../ConfigurationBootstrapperFixture.cs | 29 +++ .../WhenCalculatingVersionFromShallowClone.cs | 216 ++++++++++++++++++ 5 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 source/OctoVersion.Tests/WhenCalculatingVersionFromShallowClone.cs diff --git a/source/OctoVersion.Core/Configuration/AppSettings.cs b/source/OctoVersion.Core/Configuration/AppSettings.cs index ebb915e..62e32fd 100644 --- a/source/OctoVersion.Core/Configuration/AppSettings.cs +++ b/source/OctoVersion.Core/Configuration/AppSettings.cs @@ -37,6 +37,8 @@ public class AppSettings : IAppSettings, IValidatableObject public string? OutputJsonFile { get; set; } + public bool AllowShallowClone { get; set; } + public void ApplyDefaultsIfRequired() { if (!NonPreReleaseTags.Any()) diff --git a/source/OctoVersion.Core/OctoVersionRunner.cs b/source/OctoVersion.Core/OctoVersionRunner.cs index 7fdaf18..6f8b7b3 100644 --- a/source/OctoVersion.Core/OctoVersionRunner.cs +++ b/source/OctoVersion.Core/OctoVersionRunner.cs @@ -68,7 +68,7 @@ public void Run(out OctoVersionInfo versionInfo) ? currentDirectory : appSettings.RepositoryPath; - var versionCalculatorFactory = new VersionCalculatorFactory(repositorySearchPath); + var versionCalculatorFactory = new VersionCalculatorFactory(repositorySearchPath, appSettings.AllowShallowClone); var calculator = versionCalculatorFactory.Create(); var version = calculator.GetVersion(); var currentSha = calculator.CurrentCommitHash; diff --git a/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs b/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs index a4ec6e1..fd2b128 100644 --- a/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs +++ b/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs @@ -12,20 +12,27 @@ public class VersionCalculatorFactory { readonly ILogger _logger = Log.ForContext(); readonly Repository _repository; + readonly bool _allowShallowClone; - public VersionCalculatorFactory(string repositorySearchPath) + public VersionCalculatorFactory(string repositorySearchPath, bool allowShallowClone = false) { var gitRepositoryPath = Repository.Discover(repositorySearchPath); if (gitRepositoryPath == null) throw new RepositoryNotFoundException("Unable to resolve Git repository path."); _logger.Debug("Located Git repository in {GitRepositoryPath}", gitRepositoryPath); _repository = new Repository(gitRepositoryPath); + _allowShallowClone = allowShallowClone; } public VersionCalculator Create() { if (_repository.Info.IsShallow) - throw new RepositoryIsShallowCloneException("This repository is a shallow clone; it does not contain enough history to resolve the version correctly."); + { + if (_allowShallowClone) + _logger.Warning("This repository is a shallow clone. Version calculation may be inaccurate if the commit history does not reach a commit containing version information."); + else + throw new RepositoryIsShallowCloneException("This repository is a shallow clone; it does not contain enough history to resolve the version correctly."); + } Commit[] allCommits; using (_logger.BeginTimedOperation("Loading commits")) @@ -52,7 +59,19 @@ public VersionCalculator Create() var simpleCommit = commits[commit.Sha]; foreach (var parent in commit.Parents) { - var simpleParent = commits[parent.Sha]; + if (!commits.TryGetValue(parent.Sha, out var simpleParent)) + { + if (_allowShallowClone) + { + // In a shallow clone, boundary commits may reference parents outside the available history. Skip them rather than blowing up with a KeyNotFoundException. + continue; + } + else + { + throw new KeyNotFoundException("Unable to find parent commit with hash " + parent.Sha); + } + } + simpleCommit.AddParent(simpleParent); } } diff --git a/source/OctoVersion.Tests/ConfigurationBootstrapperFixture.cs b/source/OctoVersion.Tests/ConfigurationBootstrapperFixture.cs index f0ea542..a43e0a5 100644 --- a/source/OctoVersion.Tests/ConfigurationBootstrapperFixture.cs +++ b/source/OctoVersion.Tests/ConfigurationBootstrapperFixture.cs @@ -154,4 +154,33 @@ public static IEnumerable NonPreReleaseTagsEnvironmentVariableTestCase new[] { "main", "release" } }; } + + [Fact] + public void AllowShallowClone_DefaultsToFalse() + { + var args = new[] { "--CurrentBranch", "main" }; + var (appSettings, _) = ConfigurationBootstrapper.Bootstrap(args); + appSettings.AllowShallowClone.ShouldBe(false); + } + + [Theory] + [InlineData("true", true)] + [InlineData("false", false)] + public void WhenAllowShallowCloneIsPassedViaCommandLine(string argValue, bool expectedValue) + { + var args = new[] { "--CurrentBranch", "main", "--AllowShallowClone", argValue }; + var (appSettings, _) = ConfigurationBootstrapper.Bootstrap(args); + appSettings.AllowShallowClone.ShouldBe(expectedValue); + } + + [Theory] + [InlineData("true", true)] + [InlineData("false", false)] + public void WhenAllowShallowCloneIsPassedViaEnvironmentVariable(string envValue, bool expectedValue) + { + Environment.SetEnvironmentVariable("OCTOVERSION_CurrentBranch", "main"); + Environment.SetEnvironmentVariable("OCTOVERSION_AllowShallowClone", envValue); + var (appSettings, _) = ConfigurationBootstrapper.Bootstrap(); + appSettings.AllowShallowClone.ShouldBe(expectedValue); + } } \ No newline at end of file diff --git a/source/OctoVersion.Tests/WhenCalculatingVersionFromShallowClone.cs b/source/OctoVersion.Tests/WhenCalculatingVersionFromShallowClone.cs new file mode 100644 index 0000000..c564ea3 --- /dev/null +++ b/source/OctoVersion.Tests/WhenCalculatingVersionFromShallowClone.cs @@ -0,0 +1,216 @@ +using System; +using OctoVersion.Core.VersionNumberCalculation; +using Shouldly; +using Xunit; + +namespace OctoVersion.Tests; + +/// +/// Tests for VersionCalculator when the commit graph has been truncated, as happens +/// when VersionCalculatorFactory silently skips parents that fall outside a shallow +/// clone's available history. +/// +public class WhenCalculatingVersionFromShallowClone +{ + static SimpleCommit MakeCommit(string hash, DateTimeOffset timestamp, string message = "normal commit") + { + return new SimpleCommit(hash, message, timestamp, false, false); + } + + static SimpleCommit MakeMajorBumpCommit(string hash, DateTimeOffset timestamp) + { + return new SimpleCommit(hash, "+semver: major", timestamp, true, false); + } + + static SimpleCommit MakeMinorBumpCommit(string hash, DateTimeOffset timestamp) + { + return new SimpleCommit(hash, "+semver: minor", timestamp, false, true); + } + + [Fact] + public void WhenShallowBoundaryHasVersionTag_VersionIsCalculatedRelativeToTag() + { + // Simulates: + // C (no parents — shallow boundary, tagged 2.0.0) + // B (parent: C) + // A (parent: B) ← HEAD + // Expected: HEAD = 2.0.2 + + var baseTime = new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero); + + var commitC = MakeCommit("ccc", baseTime); + var commitB = MakeCommit("bbb", baseTime.AddMinutes(1)); + var commitA = MakeCommit("aaa", baseTime.AddMinutes(2)); + + commitC.TagWith(new SimpleVersion(2, 0, 0)); + commitB.AddParent(commitC); + commitA.AddParent(commitB); + + var calculator = new VersionCalculator(new[] { commitA, commitB, commitC }, commitA.Hash); + var version = calculator.GetVersion(); + + version.Major.ShouldBe(2); + version.Minor.ShouldBe(0); + version.Patch.ShouldBe(2); + } + + [Fact] + public void WhenShallowBoundaryHasNoTag_VersionCountsFromZero() + { + // Simulates: + // B (no parents — shallow boundary, no tag) + // A (parent: B) ← HEAD + // The shallow boundary commit itself counts as 0.0.1, so HEAD = 0.0.2 + + var baseTime = new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero); + + var commitB = MakeCommit("bbb", baseTime); + var commitA = MakeCommit("aaa", baseTime.AddMinutes(1)); + + commitA.AddParent(commitB); + + var calculator = new VersionCalculator(new[] { commitA, commitB }, commitA.Hash); + var version = calculator.GetVersion(); + + version.Major.ShouldBe(0); + version.Minor.ShouldBe(0); + version.Patch.ShouldBe(2); + } + + [Fact] + public void WhenShallowBoundaryIsTheCurrentCommit_VersionIsCalculatedFromZero() + { + // Simulates a very shallow clone — the HEAD commit itself is the boundary (no parents). + // A (no parents — shallow boundary) ← HEAD + // Expected: 0.0.1 + + var commitA = MakeCommit("aaa", new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero)); + + var calculator = new VersionCalculator(new[] { commitA }, commitA.Hash); + var version = calculator.GetVersion(); + + version.Major.ShouldBe(0); + version.Minor.ShouldBe(0); + version.Patch.ShouldBe(1); + } + + [Fact] + public void WhenMergeCommitHasOneParentCutByShallowClone_VersionUsesRemainingParent() + { + // Simulates a merge where one branch was deeper than the shallow depth and its + // root was stripped. The factory's TryGetValue skip means only one parent is linked. + // + // C (no parents — shallow boundary, tagged 3.0.0) + // B (parent: C) + // A (parent: B — the other merge parent was outside shallow history) ← HEAD + + var baseTime = new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero); + + var commitC = MakeCommit("ccc", baseTime); + var commitB = MakeCommit("bbb", baseTime.AddMinutes(1)); + var commitA = MakeCommit("aaa", baseTime.AddMinutes(2)); + + commitC.TagWith(new SimpleVersion(3, 0, 0)); + commitB.AddParent(commitC); + // commitA is a merge commit, but its other parent (e.g. "ddd") was outside the + // shallow history so the factory skipped it — only commitB is linked. + commitA.AddParent(commitB); + + var calculator = new VersionCalculator(new[] { commitA, commitB, commitC }, commitA.Hash); + var version = calculator.GetVersion(); + + version.Major.ShouldBe(3); + version.Minor.ShouldBe(0); + version.Patch.ShouldBe(2); + } + + [Fact] + public void WhenVersionTagExistsWithinShallowHistory_TagTakesPrecedenceOverCountedCommits() + { + // Confirms that when a version tag exists somewhere in the available (truncated) + // history, it still wins over the commit-counting approach — shallow or not. + // + // D (no parents — shallow boundary) + // C (parent: D, tagged 1.5.0) + // B (parent: C) + // A (parent: B) ← HEAD + // Expected: HEAD = 1.5.2 + + var baseTime = new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero); + + var commitD = MakeCommit("ddd", baseTime); + var commitC = MakeCommit("ccc", baseTime.AddMinutes(1)); + var commitB = MakeCommit("bbb", baseTime.AddMinutes(2)); + var commitA = MakeCommit("aaa", baseTime.AddMinutes(3)); + + commitC.TagWith(new SimpleVersion(1, 5, 0)); + commitC.AddParent(commitD); + commitB.AddParent(commitC); + commitA.AddParent(commitB); + + var calculator = new VersionCalculator(new[] { commitA, commitB, commitC, commitD }, commitA.Hash); + var version = calculator.GetVersion(); + + version.Major.ShouldBe(1); + version.Minor.ShouldBe(5); + version.Patch.ShouldBe(2); + } + + [Fact] + public void WhenMajorBumpCommitIsAboveShallowBoundary_MajorVersionIsIncremented() + { + // Confirms that semver bump semantics still work correctly when the + // base commit chain starts from a shallow boundary. + // + // C (no parents — shallow boundary, tagged 1.0.0) + // B (parent: C, +semver: major) + // A (parent: B) ← HEAD + // Expected: B = 2.0.0, HEAD = 2.0.1 + + var baseTime = new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero); + + var commitC = MakeCommit("ccc", baseTime); + var commitB = MakeMajorBumpCommit("bbb", baseTime.AddMinutes(1)); + var commitA = MakeCommit("aaa", baseTime.AddMinutes(2)); + + commitC.TagWith(new SimpleVersion(1, 0, 0)); + commitB.AddParent(commitC); + commitA.AddParent(commitB); + + var calculator = new VersionCalculator(new[] { commitA, commitB, commitC }, commitA.Hash); + var version = calculator.GetVersion(); + + version.Major.ShouldBe(2); + version.Minor.ShouldBe(0); + version.Patch.ShouldBe(1); + } + + [Fact] + public void WhenMinorBumpCommitIsAboveShallowBoundary_MinorVersionIsIncremented() + { + // Confirms that minor version bumps work correctly when the + // base commit chain starts from a shallow boundary. + // + // C (no parents — shallow boundary, tagged 1.0.0) + // B (parent: C, +semver: minor) + // A (parent: B) ← HEAD + // Expected: B = 1.1.0, HEAD = 1.1.1 + + var baseTime = new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero); + + var commitC = MakeCommit("ccc", baseTime); + var commitB = MakeMinorBumpCommit("bbb", baseTime.AddMinutes(1)); + var commitA = MakeCommit("aaa", baseTime.AddMinutes(2)); + + commitC.TagWith(new SimpleVersion(1, 0, 0)); + commitB.AddParent(commitC); + commitA.AddParent(commitB); + + var calculator = new VersionCalculator(new[] { commitA, commitB, commitC }, commitA.Hash); + var version = calculator.GetVersion(); + + version.Major.ShouldBe(1); + version.Minor.ShouldBe(1); + version.Patch.ShouldBe(1); + } +} From 21ddf4f31254a6a6bd3945a375e1f21c95c5bd25 Mon Sep 17 00:00:00 2001 From: Jonathon Wyza Date: Wed, 18 Mar 2026 07:08:13 -0500 Subject: [PATCH 2/4] Add readme updates about sparse checkout support --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/README.md b/README.md index d96ed04..db89db0 100644 --- a/README.md +++ b/README.md @@ -256,3 +256,53 @@ Minor bumps can be achieved using either of the following within one of the comm ``` Patch bumps occur per-commit, and do not require specific messages to occur. + +## Using Shallow Clones + +For very large repositories with long histories it is often desirable to be able to use shallow clones. This is supported via the `--AllowShallowClone=true` argument, however it is critical to understand that there must be enough commits to find a previous version to use as the base. In a scenario where the tags for the previous version are always on your main branch's `HEAD` and you always branch from there, you can use something like the following: + +```bash +# Optionally, also use sparse checkout to only fetch some static, known, file that is small since the contents of the repo are unimportant for octoversion to run. (only in cases where you don't need to checkout the whole repo) +git sparse-checkout set reporoot + +DEPTH=20 +while true; do +git fetch --depth=$DEPTH --tags origin main featureBranch +if git describe --tags --abbrev=0 featureBranch 2>/dev/null; then + break +fi +DEPTH=$((DEPTH * 2)) +if [ $DEPTH -gt 1000 ]; then + echo "Reached maximum depth without finding a tag, stopping fetch" + exit 1 +fi +done +``` + +If using Github Actions, an example usage would look something like this: + +```yaml +- name: checkout repo source code with fast checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1 + persist-credentials: true + token: ${{ secrets.GITHUB_TOKEN }} + sparse-checkout: reporoot + +- name: fast-checkout checking out default branch to find common history + run: | + DEPTH=20 + while true; do + git fetch --depth=$DEPTH --tags origin ${{ github.event.repository.default_branch }} +${{ github.sha }}:${{ github.ref }} + if git describe --tags --abbrev=0 ${{ github.ref }} 2>/dev/null; then + break + fi + DEPTH=$((DEPTH * 2)) + if [ $DEPTH -gt 1000 ]; then + echo "Reached maximum depth without finding a tag, stopping fetch" + exit 1 + fi + done + +``` \ No newline at end of file From a196929989857d13d48bdb89b10a954cc51a4e7c Mon Sep 17 00:00:00 2001 From: Matt Richardson Date: Sat, 4 Apr 2026 15:36:54 +1100 Subject: [PATCH 3/4] Minor reformats --- README.md | 24 +++++++++++--- .../Configuration/AppSettings.cs | 3 +- .../VersionCalculatorFactory.cs | 23 ++++++------- .../WhenCalculatingVersionFromShallowClone.cs | 32 ++++++++----------- 4 files changed, 46 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index db89db0..cca78a6 100644 --- a/README.md +++ b/README.md @@ -257,12 +257,23 @@ Minor bumps can be achieved using either of the following within one of the comm Patch bumps occur per-commit, and do not require specific messages to occur. -## Using Shallow Clones +## Advanced Use Cases -For very large repositories with long histories it is often desirable to be able to use shallow clones. This is supported via the `--AllowShallowClone=true` argument, however it is critical to understand that there must be enough commits to find a previous version to use as the base. In a scenario where the tags for the previous version are always on your main branch's `HEAD` and you always branch from there, you can use something like the following: +### Using Shallow Clones + +For very large repositories with long histories it is often desirable to be able to use shallow clones. +This is supported via the `--AllowShallowClone=true` argument, however it is critical to understand that there must be enough commits to find a previous version to use as the base. +In a scenario where the tags for the previous version are always on your main branch's `HEAD` and you always branch from there. + +> [!WARNING] +> if you cannot guarantee that the tags will be present, the generated version is likely to be incorrect. + +
+ Example: Bash ```bash -# Optionally, also use sparse checkout to only fetch some static, known, file that is small since the contents of the repo are unimportant for octoversion to run. (only in cases where you don't need to checkout the whole repo) +# Optionally, also use sparse checkout to only fetch some static, known, file that is small since the contents of the repo are unimportant for octoversion to run. +# Only for cases where you don't need to checkout the whole repo git sparse-checkout set reporoot DEPTH=20 @@ -278,8 +289,10 @@ if [ $DEPTH -gt 1000 ]; then fi done ``` +
-If using Github Actions, an example usage would look something like this: +
+ Example: GitHub Actions ```yaml - name: checkout repo source code with fast checkout @@ -305,4 +318,5 @@ If using Github Actions, an example usage would look something like this: fi done -``` \ No newline at end of file +``` +
diff --git a/source/OctoVersion.Core/Configuration/AppSettings.cs b/source/OctoVersion.Core/Configuration/AppSettings.cs index 62e32fd..7c00f18 100644 --- a/source/OctoVersion.Core/Configuration/AppSettings.cs +++ b/source/OctoVersion.Core/Configuration/AppSettings.cs @@ -37,6 +37,7 @@ public class AppSettings : IAppSettings, IValidatableObject public string? OutputJsonFile { get; set; } + // defaults to false - throw an exception if a shallow clone is detected public bool AllowShallowClone { get; set; } public void ApplyDefaultsIfRequired() @@ -87,4 +88,4 @@ public IEnumerable Validate(ValidationContext validationContex if (string.IsNullOrWhiteSpace(CurrentBranch) && string.IsNullOrWhiteSpace(FullSemVer)) yield return new ValidationResult($"At least one of {nameof(CurrentBranch)} or {nameof(FullSemVer)} must be provided.", new[] { nameof(CurrentBranch), nameof(FullSemVer) }); } -} \ No newline at end of file +} diff --git a/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs b/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs index fd2b128..2bd3dd8 100644 --- a/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs +++ b/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs @@ -14,7 +14,12 @@ public class VersionCalculatorFactory readonly Repository _repository; readonly bool _allowShallowClone; - public VersionCalculatorFactory(string repositorySearchPath, bool allowShallowClone = false) + public VersionCalculatorFactory(string repositorySearchPath) + : this(repositorySearchPath, allowShallowClone: false) + { + } + + public VersionCalculatorFactory(string repositorySearchPath, bool allowShallowClone) { var gitRepositoryPath = Repository.Discover(repositorySearchPath); if (gitRepositoryPath == null) @@ -29,7 +34,7 @@ public VersionCalculator Create() if (_repository.Info.IsShallow) { if (_allowShallowClone) - _logger.Warning("This repository is a shallow clone. Version calculation may be inaccurate if the commit history does not reach a commit containing version information."); + _logger.Warning("This repository is a shallow clone - version calculation may be inaccurate if the commit history does not reach a commit containing version information"); else throw new RepositoryIsShallowCloneException("This repository is a shallow clone; it does not contain enough history to resolve the version correctly."); } @@ -61,15 +66,11 @@ public VersionCalculator Create() { if (!commits.TryGetValue(parent.Sha, out var simpleParent)) { - if (_allowShallowClone) - { - // In a shallow clone, boundary commits may reference parents outside the available history. Skip them rather than blowing up with a KeyNotFoundException. + // In a shallow clone, boundary commits may reference parents outside the available history. Skip them rather than blowing up with a KeyNotFoundException. + if (_allowShallowClone) continue; - } - else - { - throw new KeyNotFoundException("Unable to find parent commit with hash " + parent.Sha); - } + + throw new KeyNotFoundException("Unable to find parent commit with hash " + parent.Sha); } simpleCommit.AddParent(simpleParent); @@ -103,4 +104,4 @@ public VersionCalculator Create() var calculator = new VersionCalculator(commits.Values.ToArray(), currentCommitHash); return calculator; } -} \ No newline at end of file +} diff --git a/source/OctoVersion.Tests/WhenCalculatingVersionFromShallowClone.cs b/source/OctoVersion.Tests/WhenCalculatingVersionFromShallowClone.cs index c564ea3..645fe84 100644 --- a/source/OctoVersion.Tests/WhenCalculatingVersionFromShallowClone.cs +++ b/source/OctoVersion.Tests/WhenCalculatingVersionFromShallowClone.cs @@ -12,20 +12,14 @@ namespace OctoVersion.Tests; /// public class WhenCalculatingVersionFromShallowClone { - static SimpleCommit MakeCommit(string hash, DateTimeOffset timestamp, string message = "normal commit") - { - return new SimpleCommit(hash, message, timestamp, false, false); - } + static SimpleCommit MakeCommit(string hash, DateTimeOffset timestamp, string message = "normal commit") + => new(hash, message, timestamp, false, false); - static SimpleCommit MakeMajorBumpCommit(string hash, DateTimeOffset timestamp) - { - return new SimpleCommit(hash, "+semver: major", timestamp, true, false); - } + static SimpleCommit MakeMajorBumpCommit(string hash, DateTimeOffset timestamp) + => new(hash, "+semver: major", timestamp, true, false); - static SimpleCommit MakeMinorBumpCommit(string hash, DateTimeOffset timestamp) - { - return new SimpleCommit(hash, "+semver: minor", timestamp, false, true); - } + static SimpleCommit MakeMinorBumpCommit(string hash, DateTimeOffset timestamp) + => new(hash, "+semver: minor", timestamp, false, true); [Fact] public void WhenShallowBoundaryHasVersionTag_VersionIsCalculatedRelativeToTag() @@ -46,7 +40,7 @@ public void WhenShallowBoundaryHasVersionTag_VersionIsCalculatedRelativeToTag() commitB.AddParent(commitC); commitA.AddParent(commitB); - var calculator = new VersionCalculator(new[] { commitA, commitB, commitC }, commitA.Hash); + var calculator = new VersionCalculator([commitA, commitB, commitC], commitA.Hash); var version = calculator.GetVersion(); version.Major.ShouldBe(2); @@ -69,7 +63,7 @@ public void WhenShallowBoundaryHasNoTag_VersionCountsFromZero() commitA.AddParent(commitB); - var calculator = new VersionCalculator(new[] { commitA, commitB }, commitA.Hash); + var calculator = new VersionCalculator([commitA, commitB], commitA.Hash); var version = calculator.GetVersion(); version.Major.ShouldBe(0); @@ -86,7 +80,7 @@ public void WhenShallowBoundaryIsTheCurrentCommit_VersionIsCalculatedFromZero() var commitA = MakeCommit("aaa", new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero)); - var calculator = new VersionCalculator(new[] { commitA }, commitA.Hash); + var calculator = new VersionCalculator([commitA], commitA.Hash); var version = calculator.GetVersion(); version.Major.ShouldBe(0); @@ -116,7 +110,7 @@ public void WhenMergeCommitHasOneParentCutByShallowClone_VersionUsesRemainingPar // shallow history so the factory skipped it — only commitB is linked. commitA.AddParent(commitB); - var calculator = new VersionCalculator(new[] { commitA, commitB, commitC }, commitA.Hash); + var calculator = new VersionCalculator([commitA, commitB, commitC], commitA.Hash); var version = calculator.GetVersion(); version.Major.ShouldBe(3); @@ -148,7 +142,7 @@ public void WhenVersionTagExistsWithinShallowHistory_TagTakesPrecedenceOverCount commitB.AddParent(commitC); commitA.AddParent(commitB); - var calculator = new VersionCalculator(new[] { commitA, commitB, commitC, commitD }, commitA.Hash); + var calculator = new VersionCalculator([commitA, commitB, commitC, commitD], commitA.Hash); var version = calculator.GetVersion(); version.Major.ShouldBe(1); @@ -177,7 +171,7 @@ public void WhenMajorBumpCommitIsAboveShallowBoundary_MajorVersionIsIncremented( commitB.AddParent(commitC); commitA.AddParent(commitB); - var calculator = new VersionCalculator(new[] { commitA, commitB, commitC }, commitA.Hash); + var calculator = new VersionCalculator([commitA, commitB, commitC], commitA.Hash); var version = calculator.GetVersion(); version.Major.ShouldBe(2); @@ -206,7 +200,7 @@ public void WhenMinorBumpCommitIsAboveShallowBoundary_MinorVersionIsIncremented( commitB.AddParent(commitC); commitA.AddParent(commitB); - var calculator = new VersionCalculator(new[] { commitA, commitB, commitC }, commitA.Hash); + var calculator = new VersionCalculator([commitA, commitB, commitC], commitA.Hash); var version = calculator.GetVersion(); version.Major.ShouldBe(1); From 3bfe2c39103fe199f9d3629923507b32bdced97a Mon Sep 17 00:00:00 2001 From: Matt Richardson Date: Sat, 4 Apr 2026 15:38:26 +1100 Subject: [PATCH 4/4] Whitespace --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cca78a6..6f4e7ad 100644 --- a/README.md +++ b/README.md @@ -268,7 +268,7 @@ In a scenario where the tags for the previous version are always on your main br > [!WARNING] > if you cannot guarantee that the tags will be present, the generated version is likely to be incorrect. -
+
Example: Bash ```bash @@ -291,7 +291,7 @@ done ```
-
+
Example: GitHub Actions ```yaml