diff --git a/docs/input/docs/workflows/GitFlow/v1.yml b/docs/input/docs/workflows/GitFlow/v1.yml index 114ecf8e4f..dd44250ac5 100644 --- a/docs/input/docs/workflows/GitFlow/v1.yml +++ b/docs/input/docs/workflows/GitFlow/v1.yml @@ -148,6 +148,7 @@ branches: is-main-branch: false ignore: sha: [] + paths: [] mode: ContinuousDelivery label: '{BranchName}' increment: Inherit diff --git a/docs/input/docs/workflows/GitHubFlow/v1.yml b/docs/input/docs/workflows/GitHubFlow/v1.yml index bc0452231a..be7da3a729 100644 --- a/docs/input/docs/workflows/GitHubFlow/v1.yml +++ b/docs/input/docs/workflows/GitHubFlow/v1.yml @@ -97,6 +97,7 @@ branches: is-main-branch: false ignore: sha: [] + paths: [] mode: ContinuousDelivery label: '{BranchName}' increment: Inherit diff --git a/docs/input/docs/workflows/TrunkBased/preview1.yml b/docs/input/docs/workflows/TrunkBased/preview1.yml index 83d231527f..c261444d9f 100644 --- a/docs/input/docs/workflows/TrunkBased/preview1.yml +++ b/docs/input/docs/workflows/TrunkBased/preview1.yml @@ -82,6 +82,7 @@ branches: pre-release-weight: 30000 ignore: sha: [] + paths: [] mode: ContinuousDelivery label: '{BranchName}' increment: Inherit diff --git a/schemas/6.1/GitVersion.configuration.json b/schemas/6.1/GitVersion.configuration.json index a186b94b48..8ce044db95 100644 --- a/schemas/6.1/GitVersion.configuration.json +++ b/schemas/6.1/GitVersion.configuration.json @@ -189,6 +189,13 @@ "null" ] }, + "paths": { + "description": "A sequence of file paths to be excluded from the version calculations.", + "type": "array", + "items": { + "type": "string" + } + }, "sha": { "description": "A sequence of SHAs to be excluded from the version calculations.", "$ref": "#/$defs/hashSetOfString" diff --git a/src/GitVersion.Configuration.Tests/Configuration/ConfigurationProviderTests.CanWriteOutEffectiveConfiguration.approved.txt b/src/GitVersion.Configuration.Tests/Configuration/ConfigurationProviderTests.CanWriteOutEffectiveConfiguration.approved.txt index 114ecf8e4f..dd44250ac5 100644 --- a/src/GitVersion.Configuration.Tests/Configuration/ConfigurationProviderTests.CanWriteOutEffectiveConfiguration.approved.txt +++ b/src/GitVersion.Configuration.Tests/Configuration/ConfigurationProviderTests.CanWriteOutEffectiveConfiguration.approved.txt @@ -148,6 +148,7 @@ branches: is-main-branch: false ignore: sha: [] + paths: [] mode: ContinuousDelivery label: '{BranchName}' increment: Inherit diff --git a/src/GitVersion.Configuration.Tests/Workflows/approved/GitFlow/v1.yml b/src/GitVersion.Configuration.Tests/Workflows/approved/GitFlow/v1.yml index 114ecf8e4f..dd44250ac5 100644 --- a/src/GitVersion.Configuration.Tests/Workflows/approved/GitFlow/v1.yml +++ b/src/GitVersion.Configuration.Tests/Workflows/approved/GitFlow/v1.yml @@ -148,6 +148,7 @@ branches: is-main-branch: false ignore: sha: [] + paths: [] mode: ContinuousDelivery label: '{BranchName}' increment: Inherit diff --git a/src/GitVersion.Configuration.Tests/Workflows/approved/GitHubFlow/v1.yml b/src/GitVersion.Configuration.Tests/Workflows/approved/GitHubFlow/v1.yml index bc0452231a..be7da3a729 100644 --- a/src/GitVersion.Configuration.Tests/Workflows/approved/GitHubFlow/v1.yml +++ b/src/GitVersion.Configuration.Tests/Workflows/approved/GitHubFlow/v1.yml @@ -97,6 +97,7 @@ branches: is-main-branch: false ignore: sha: [] + paths: [] mode: ContinuousDelivery label: '{BranchName}' increment: Inherit diff --git a/src/GitVersion.Configuration.Tests/Workflows/approved/TrunkBased/preview1.yml b/src/GitVersion.Configuration.Tests/Workflows/approved/TrunkBased/preview1.yml index 83d231527f..c261444d9f 100644 --- a/src/GitVersion.Configuration.Tests/Workflows/approved/TrunkBased/preview1.yml +++ b/src/GitVersion.Configuration.Tests/Workflows/approved/TrunkBased/preview1.yml @@ -82,6 +82,7 @@ branches: pre-release-weight: 30000 ignore: sha: [] + paths: [] mode: ContinuousDelivery label: '{BranchName}' increment: Inherit diff --git a/src/GitVersion.Configuration/IgnoreConfiguration.cs b/src/GitVersion.Configuration/IgnoreConfiguration.cs index 9ec97e2b0a..489148831a 100644 --- a/src/GitVersion.Configuration/IgnoreConfiguration.cs +++ b/src/GitVersion.Configuration/IgnoreConfiguration.cs @@ -1,3 +1,4 @@ +using System.Collections.ObjectModel; using GitVersion.Configuration.Attributes; namespace GitVersion.Configuration; @@ -22,4 +23,11 @@ public string? BeforeString [JsonPropertyName("sha")] [JsonPropertyDescription("A sequence of SHAs to be excluded from the version calculations.")] public HashSet Shas { get; init; } = []; + + [JsonIgnore] + IReadOnlyCollection IIgnoreConfiguration.Paths => Paths; + + [JsonPropertyName("paths")] + [JsonPropertyDescription("A sequence of file paths to be excluded from the version calculations.")] + public Collection Paths { get; init; } = []; } diff --git a/src/GitVersion.Core.Tests/VersionCalculation/PathFilterTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/PathFilterTests.cs new file mode 100644 index 0000000000..12d2540e4c --- /dev/null +++ b/src/GitVersion.Core.Tests/VersionCalculation/PathFilterTests.cs @@ -0,0 +1,11 @@ +using GitVersion.Core.Tests.Helpers; +using GitVersion.VersionCalculation; + +namespace GitVersion.Core.Tests; + +[TestFixture] +public class PathFilterTests : TestBase +{ + [Test] + public void VerifyNullGuard() => Should.Throw(() => new PathFilter(null!, null!, null!)); +} diff --git a/src/GitVersion.Core/Configuration/EffectiveConfiguration.cs b/src/GitVersion.Core/Configuration/EffectiveConfiguration.cs index bd16f1f2ba..d7baa0df16 100644 --- a/src/GitVersion.Core/Configuration/EffectiveConfiguration.cs +++ b/src/GitVersion.Core/Configuration/EffectiveConfiguration.cs @@ -1,4 +1,5 @@ using GitVersion.Extensions; +using GitVersion.Git; using GitVersion.VersionCalculation; namespace GitVersion.Configuration; @@ -12,6 +13,8 @@ public record EffectiveConfiguration public EffectiveConfiguration( IGitVersionConfiguration configuration, IBranchConfiguration branchConfiguration, + IGitRepository? repository = null, + Lazy? versionContext = null, EffectiveConfiguration? fallbackConfiguration = null) { configuration.NotNull(); @@ -67,7 +70,7 @@ public EffectiveConfiguration( PatchVersionBumpMessage = configuration.PatchVersionBumpMessage; NoBumpMessage = configuration.NoBumpMessage; CommitMessageIncrementing = branchConfiguration.CommitMessageIncrementing.Value; - VersionFilters = configuration.Ignore.ToFilters(); + VersionFilters = versionContext != null && repository != null ? configuration.Ignore.ToFilters(repository, versionContext.Value) : []; Ignore = configuration.Ignore; TracksReleaseBranches = branchConfiguration.TracksReleaseBranches ?? false; IsReleaseBranch = branchConfiguration.IsReleaseBranch ?? false; diff --git a/src/GitVersion.Core/Configuration/IIgnoreConfiguration.cs b/src/GitVersion.Core/Configuration/IIgnoreConfiguration.cs index f836d52915..d94c778fb1 100644 --- a/src/GitVersion.Core/Configuration/IIgnoreConfiguration.cs +++ b/src/GitVersion.Core/Configuration/IIgnoreConfiguration.cs @@ -6,5 +6,7 @@ public interface IIgnoreConfiguration IReadOnlySet Shas { get; } - bool IsEmpty => Before == null && Shas.Count == 0; + IReadOnlyCollection Paths { get; } + + bool IsEmpty => Before == null && Shas.Count == 0 && Paths.Count == 0; } diff --git a/src/GitVersion.Core/Extensions/ConfigurationExtensions.cs b/src/GitVersion.Core/Extensions/ConfigurationExtensions.cs index 65b6d0cb67..2f3dc18236 100644 --- a/src/GitVersion.Core/Extensions/ConfigurationExtensions.cs +++ b/src/GitVersion.Core/Extensions/ConfigurationExtensions.cs @@ -25,7 +25,7 @@ public static EffectiveConfiguration GetEffectiveConfiguration( { fallbackConfiguration = parentConfiguration; } - return new EffectiveConfiguration(configuration, branchConfiguration, fallbackConfiguration); + return new EffectiveConfiguration(configuration, branchConfiguration, fallbackConfiguration: fallbackConfiguration); } public static IBranchConfiguration GetBranchConfiguration(this IGitVersionConfiguration configuration, IBranch branch) @@ -38,12 +38,13 @@ public static IBranchConfiguration GetBranchConfiguration(this IGitVersionConfig return branchConfiguration; } - public static IEnumerable ToFilters(this IIgnoreConfiguration source) + public static IEnumerable ToFilters(this IIgnoreConfiguration source, IGitRepository repository, GitVersionContext versionContext) { source.NotNull(); if (source.Shas.Count != 0) yield return new ShaVersionFilter(source.Shas); if (source.Before.HasValue) yield return new MinDateVersionFilter(source.Before.Value); + if (source.Paths.Count != 0) yield return new PathFilter(repository, versionContext, source.Paths); } private static IEnumerable GetBranchConfigurations(IGitVersionConfiguration configuration, string branchName) diff --git a/src/GitVersion.Core/Git/IGitRepository.cs b/src/GitVersion.Core/Git/IGitRepository.cs index bfad989068..1984f52a47 100644 --- a/src/GitVersion.Core/Git/IGitRepository.cs +++ b/src/GitVersion.Core/Git/IGitRepository.cs @@ -16,6 +16,7 @@ public interface IGitRepository : IDisposable IRemoteCollection Remotes { get; } ICommit? FindMergeBase(ICommit commit, ICommit otherCommit); + IReadOnlyList? FindPatchPaths(ICommit commit, string? tagPrefix); int UncommittedChangesCount(); void DiscoverRepository(string? gitDirectory); } diff --git a/src/GitVersion.Core/PublicAPI.Shipped.txt b/src/GitVersion.Core/PublicAPI.Shipped.txt index 8feb99e5cc..bde2e4919c 100644 --- a/src/GitVersion.Core/PublicAPI.Shipped.txt +++ b/src/GitVersion.Core/PublicAPI.Shipped.txt @@ -65,7 +65,6 @@ GitVersion.Configuration.EffectiveConfiguration.AssemblyVersioningScheme.get -> GitVersion.Configuration.EffectiveConfiguration.CommitDateFormat.get -> string? GitVersion.Configuration.EffectiveConfiguration.CommitMessageIncrementing.get -> GitVersion.VersionCalculation.CommitMessageIncrementMode GitVersion.Configuration.EffectiveConfiguration.DeploymentMode.get -> GitVersion.VersionCalculation.DeploymentMode -GitVersion.Configuration.EffectiveConfiguration.EffectiveConfiguration(GitVersion.Configuration.IGitVersionConfiguration! configuration, GitVersion.Configuration.IBranchConfiguration! branchConfiguration, GitVersion.Configuration.EffectiveConfiguration? fallbackConfiguration = null) -> void GitVersion.Configuration.EffectiveConfiguration.Ignore.get -> GitVersion.Configuration.IIgnoreConfiguration! GitVersion.Configuration.EffectiveConfiguration.Increment.get -> GitVersion.IncrementStrategy GitVersion.Configuration.EffectiveConfiguration.IsMainBranch.get -> bool @@ -88,7 +87,6 @@ GitVersion.Configuration.EffectiveConfiguration.TrackMergeMessage.get -> bool GitVersion.Configuration.EffectiveConfiguration.TrackMergeTarget.get -> bool GitVersion.Configuration.EffectiveConfiguration.TracksReleaseBranches.get -> bool GitVersion.Configuration.EffectiveConfiguration.UpdateBuildNumber.get -> bool -GitVersion.Configuration.EffectiveConfiguration.VersionFilters.get -> System.Collections.Generic.IEnumerable! GitVersion.Configuration.EffectiveConfiguration.VersionInBranchPattern.get -> string? GitVersion.Configuration.EffectiveConfiguration.VersionStrategy.get -> GitVersion.VersionCalculation.VersionStrategies GitVersion.Configuration.IBranchConfiguration @@ -602,7 +600,6 @@ GitVersion.Settings.OnlyTrackedBranches -> bool GitVersion.VersionCalculation.BaseVersion GitVersion.VersionCalculation.BaseVersion.BaseVersion() -> void GitVersion.VersionCalculation.BaseVersion.BaseVersion(GitVersion.VersionCalculation.BaseVersionOperand! Operand) -> void -GitVersion.VersionCalculation.BaseVersion.BaseVersion(string! source, GitVersion.SemanticVersion! semanticVersion, GitVersion.Git.ICommit? baseVersionSource = null) -> void GitVersion.VersionCalculation.BaseVersion.BaseVersionSource.get -> GitVersion.Git.ICommit? GitVersion.VersionCalculation.BaseVersion.GetIncrementedVersion() -> GitVersion.SemanticVersion! GitVersion.VersionCalculation.BaseVersion.Operand.get -> GitVersion.VersionCalculation.BaseVersionOperand! @@ -614,7 +611,6 @@ GitVersion.VersionCalculation.BaseVersion.ShouldIncrement.get -> bool GitVersion.VersionCalculation.BaseVersion.Source.get -> string! GitVersion.VersionCalculation.BaseVersionOperand GitVersion.VersionCalculation.BaseVersionOperand.BaseVersionOperand() -> void -GitVersion.VersionCalculation.BaseVersionOperand.BaseVersionOperand(string! Source, GitVersion.SemanticVersion! SemanticVersion, GitVersion.Git.ICommit? BaseVersionSource = null) -> void GitVersion.VersionCalculation.BaseVersionOperand.BaseVersionSource.get -> GitVersion.Git.ICommit? GitVersion.VersionCalculation.BaseVersionOperand.BaseVersionSource.init -> void GitVersion.VersionCalculation.BaseVersionOperand.SemanticVersion.get -> GitVersion.SemanticVersion! diff --git a/src/GitVersion.Core/PublicAPI.Unshipped.txt b/src/GitVersion.Core/PublicAPI.Unshipped.txt index 57bf801923..2ef5f789ae 100644 --- a/src/GitVersion.Core/PublicAPI.Unshipped.txt +++ b/src/GitVersion.Core/PublicAPI.Unshipped.txt @@ -1,3 +1,20 @@ #nullable enable +GitVersion.Configuration.EffectiveConfiguration.EffectiveConfiguration(GitVersion.Configuration.IGitVersionConfiguration! configuration, GitVersion.Configuration.IBranchConfiguration! branchConfiguration, GitVersion.Git.IGitRepository? repository = null, System.Lazy? versionContext = null, GitVersion.Configuration.EffectiveConfiguration? fallbackConfiguration = null) -> void +GitVersion.Configuration.EffectiveConfiguration.VersionFilters.get -> System.Collections.Generic.IEnumerable! +GitVersion.Configuration.IIgnoreConfiguration.Paths.get -> System.Collections.Generic.IReadOnlyCollection! GitVersion.Extensions.FileSystemExtensions +GitVersion.Git.IGitRepository.FindPatchPaths(GitVersion.Git.ICommit! commit, string? tagPrefix) -> System.Collections.Generic.IReadOnlyList? +GitVersion.VersionCalculation.BaseVersion.BaseVersion(string! source, GitVersion.SemanticVersion! semanticVersion, GitVersion.Git.ICommit? baseVersionSource = null, GitVersion.VersionCalculation.VersionIncrementSourceType sourceType = GitVersion.VersionCalculation.VersionIncrementSourceType.Tree) -> void +GitVersion.VersionCalculation.BaseVersion.SourceType.get -> GitVersion.VersionCalculation.VersionIncrementSourceType +GitVersion.VersionCalculation.BaseVersion.SourceType.init -> void +GitVersion.VersionCalculation.BaseVersionOperand.BaseVersionOperand(string! Source, GitVersion.SemanticVersion! SemanticVersion, GitVersion.Git.ICommit? BaseVersionSource = null, GitVersion.VersionCalculation.VersionIncrementSourceType SourceType = GitVersion.VersionCalculation.VersionIncrementSourceType.Tree) -> void +GitVersion.VersionCalculation.BaseVersionOperand.SourceType.get -> GitVersion.VersionCalculation.VersionIncrementSourceType +GitVersion.VersionCalculation.BaseVersionOperand.SourceType.init -> void +GitVersion.VersionCalculation.BaseVersionOperator.SourceType.get -> GitVersion.VersionCalculation.VersionIncrementSourceType +GitVersion.VersionCalculation.IBaseVersionIncrement.SourceType.get -> GitVersion.VersionCalculation.VersionIncrementSourceType +GitVersion.VersionCalculation.VersionIncrementSourceType +GitVersion.VersionCalculation.VersionIncrementSourceType.Fallback = 2 -> GitVersion.VersionCalculation.VersionIncrementSourceType +GitVersion.VersionCalculation.VersionIncrementSourceType.NextVersionConfig = 3 -> GitVersion.VersionCalculation.VersionIncrementSourceType +GitVersion.VersionCalculation.VersionIncrementSourceType.Tag = 1 -> GitVersion.VersionCalculation.VersionIncrementSourceType +GitVersion.VersionCalculation.VersionIncrementSourceType.Tree = 0 -> GitVersion.VersionCalculation.VersionIncrementSourceType static GitVersion.Extensions.FileSystemExtensions.GetLastDirectoryWrite(this System.IO.Abstractions.IFileSystem! fileSystem, string! path) -> long diff --git a/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs b/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs index d0f69f4bbb..4e4e8cb0cc 100644 --- a/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs +++ b/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs @@ -7,7 +7,11 @@ namespace GitVersion.VersionCalculation; -internal class IncrementStrategyFinder(IRepositoryStore repositoryStore, ITaggedSemanticVersionRepository taggedSemanticVersionRepository) +internal class IncrementStrategyFinder( + IRepositoryStore repositoryStore, + Lazy versionContext, + IGitRepository repository, + ITaggedSemanticVersionRepository taggedSemanticVersionRepository) : IIncrementStrategyFinder { private readonly Dictionary commitIncrementCache = []; @@ -51,15 +55,14 @@ public VersionField DetermineIncrementedField( var minorRegex = TryGetRegexOrDefault(configuration.MinorVersionBumpMessage, RegexPatterns.VersionCalculation.DefaultMinorRegex); var patchRegex = TryGetRegexOrDefault(configuration.PatchVersionBumpMessage, RegexPatterns.VersionCalculation.DefaultPatchRegex); var noBumpRegex = TryGetRegexOrDefault(configuration.NoBumpMessage, RegexPatterns.VersionCalculation.DefaultNoBumpRegex); + var pathFilters = configuration.Ignore.ToFilters(repository, versionContext.Value).OfType(); var increments = commits - .Select(c => GetIncrementFromCommit(c, majorRegex, minorRegex, patchRegex, noBumpRegex)) - .Where(v => v != null) - .ToList(); + .Select(c => (commit: c, increment: GetIncrementFromCommit(c, majorRegex, minorRegex, patchRegex, noBumpRegex))) + .Where(pair => pair.increment != null) + .OrderByDescending(pair => pair.increment); - return increments.Count != 0 - ? increments.Max() - : null; + return increments.FirstOrDefault(pair => !pathFilters.Any(f => f.Exclude(pair.commit, out _)), (null!, null)).increment; } private VersionField? FindCommitMessageIncrement( diff --git a/src/GitVersion.Core/VersionCalculation/PathFilter.cs b/src/GitVersion.Core/VersionCalculation/PathFilter.cs new file mode 100644 index 0000000000..566c899cf7 --- /dev/null +++ b/src/GitVersion.Core/VersionCalculation/PathFilter.cs @@ -0,0 +1,51 @@ +using System.Text.RegularExpressions; +using GitVersion.Git; + +namespace GitVersion.VersionCalculation; + +internal class PathFilter(IGitRepository repository, GitVersionContext context, IEnumerable paths) : IVersionFilter +{ + private readonly GitVersionContext context = context; + private readonly List pathsRegexes = paths.Select(path => new Regex(path, RegexOptions.IgnoreCase | RegexOptions.Compiled)).ToList(); + private readonly Dictionary pathMatchCache = []; + + public bool Exclude(IBaseVersion baseVersion, out string? reason) + { + ArgumentNullException.ThrowIfNull(baseVersion); + + reason = null; + if (baseVersion.SourceType != VersionIncrementSourceType.Tree) return false; + + return Exclude(baseVersion.BaseVersionSource, out reason); + } + + public bool Exclude(ICommit? commit, out string? reason) + { + reason = null; + + if (commit != null) + { + var patchPaths = repository.FindPatchPaths(commit, this.context.Configuration.TagPrefixPattern); + + if (patchPaths != null) + { + foreach (var path in patchPaths) + { + if (!pathMatchCache.TryGetValue(path, out var isMatch)) + { + isMatch = this.pathsRegexes.Any(regex => regex.IsMatch(path)); + pathMatchCache[path] = isMatch; + } + + if (isMatch) + { + reason = "Source was ignored due to commit path matching ignore regex"; + return true; + } + } + } + } + + return false; + } +} diff --git a/src/GitVersion.Core/VersionCalculation/VersionCalculators/NextVersionCalculator.cs b/src/GitVersion.Core/VersionCalculation/VersionCalculators/NextVersionCalculator.cs index 8e4ebc4640..02813daabc 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionCalculators/NextVersionCalculator.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionCalculators/NextVersionCalculator.cs @@ -11,6 +11,7 @@ namespace GitVersion.VersionCalculation; internal class NextVersionCalculator( ILog log, Lazy versionContext, + IGitRepository repository, IEnumerable deploymentModeCalculators, IEnumerable versionStrategies, IEffectiveBranchConfigurationFinder effectiveBranchConfigurationFinder, @@ -155,10 +156,10 @@ private SemanticVersion CalculateSemanticVersion( private NextVersion CalculateNextVersion(IBranch branch, IGitVersionConfiguration configuration) { - var nextVersions = GetNextVersions(branch, configuration); + var nextVersions = GetNextVersions(branch, configuration).OrderByDescending(v => v.IncrementedVersion); log.Separator(); - var maxVersion = nextVersions.Max() + var maxVersion = nextVersions.FirstOrDefault(v => ShouldIncludeVersion(configuration, v)) ?? throw new GitVersionException("No base versions determined on the current branch."); ICommit? latestBaseVersionSource; @@ -167,6 +168,7 @@ private NextVersion CalculateNextVersion(IBranch branch, IGitVersionConfiguratio .Where( element => element.BaseVersion.BaseVersionSource != null && element.IncrementedVersion == maxVersion.IncrementedVersion + && ShouldIncludeVersion(configuration, element) ).ToArray(); if (matchingVersionsOnceIncremented.Length > 1) { @@ -192,11 +194,11 @@ private NextVersion CalculateNextVersion(IBranch branch, IGitVersionConfiguratio .Where(v => v.BaseVersion.BaseVersionSource != null) .OrderByDescending(v => v.IncrementedVersion) .ThenByDescending(v => v.BaseVersion.BaseVersionSource?.When) - .FirstOrDefault(); + .FirstOrDefault(v => ShouldIncludeVersion(configuration, v)); version ??= versions.Where(v => v.BaseVersion.BaseVersionSource == null) .OrderByDescending(v => v.IncrementedVersion) - .First(); + .First(v => ShouldIncludeVersion(configuration, v)); latestBaseVersionSource = version.BaseVersion.BaseVersionSource; } @@ -216,6 +218,9 @@ private NextVersion CalculateNextVersion(IBranch branch, IGitVersionConfiguratio return new(maxVersion.IncrementedVersion, calculatedBase, maxVersion.BranchConfiguration); } + private bool ShouldIncludeVersion(IGitVersionConfiguration configuration, NextVersion version) => + configuration.Ignore.ToFilters(repository, this.Context).All(filter => !filter.Exclude(version.BaseVersion, out _)); + private static NextVersion CompareVersions(NextVersion version1, NextVersion version2) { if (version1.BaseVersion.BaseVersionSource == null) @@ -264,7 +269,8 @@ IEnumerable GetNextVersionsInternal() foreach (var baseVersion in versionStrategy.GetBaseVersions(effectiveBranchConfiguration)) { log.Info(baseVersion.ToString()); - if (IncludeVersion(baseVersion, configuration.Ignore)) + if (versionStrategy is not FallbackVersionStrategy || IncludeVersion(baseVersion, configuration.Ignore)) + //Defer the version inclusion check to the caller, for strategies other than FallbackVersionStrategy { atLeastOneBaseVersionReturned = true; @@ -284,7 +290,7 @@ IEnumerable GetNextVersionsInternal() private bool IncludeVersion(IBaseVersion baseVersion, IIgnoreConfiguration ignoreConfiguration) { - foreach (var versionFilter in ignoreConfiguration.ToFilters()) + foreach (var versionFilter in ignoreConfiguration.ToFilters(repository, Context)) { if (versionFilter.Exclude(baseVersion, out var reason)) { diff --git a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersion.cs b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersion.cs index 28a574b6a0..0ccc0d3220 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersion.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersion.cs @@ -10,13 +10,13 @@ public BaseVersion() : this(new BaseVersionOperand()) { } - public BaseVersion(string source, SemanticVersion semanticVersion, ICommit? baseVersionSource = null) - : this(new BaseVersionOperand(source, semanticVersion, baseVersionSource)) - { - } + public BaseVersion(string source, SemanticVersion semanticVersion, ICommit? baseVersionSource = null, VersionIncrementSourceType sourceType = VersionIncrementSourceType.Tree) + : this(new BaseVersionOperand(source, semanticVersion, baseVersionSource, sourceType)) => this.SourceType = sourceType; public string Source => (Operator?.Source).IsNullOrEmpty() ? Operand.Source : Operator.Source; + public VersionIncrementSourceType SourceType { get; init; } = VersionIncrementSourceType.Tree; + public SemanticVersion SemanticVersion => Operand.SemanticVersion; public ICommit? BaseVersionSource => Operator?.BaseVersionSource ?? Operand.BaseVersionSource; diff --git a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersionOperand.cs b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersionOperand.cs index 0a46368ba5..4878fee300 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersionOperand.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersionOperand.cs @@ -3,7 +3,7 @@ namespace GitVersion.VersionCalculation; -public sealed record BaseVersionOperand(string Source, SemanticVersion SemanticVersion, ICommit? BaseVersionSource = null) +public sealed record BaseVersionOperand(string Source, SemanticVersion SemanticVersion, ICommit? BaseVersionSource = null, VersionIncrementSourceType SourceType = VersionIncrementSourceType.Tree) : IBaseVersionIncrement { public BaseVersionOperand() : this(string.Empty, SemanticVersion.Empty) @@ -12,6 +12,8 @@ public BaseVersionOperand() : this(string.Empty, SemanticVersion.Empty) public string Source { get; init; } = Source.NotNull(); + public VersionIncrementSourceType SourceType { get; init; } = SourceType; + public SemanticVersion SemanticVersion { get; init; } = SemanticVersion.NotNull(); public override string ToString() diff --git a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersionOperator.cs b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersionOperator.cs index 767dd7b83f..4a34bb4c68 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersionOperator.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/BaseVersionOperator.cs @@ -6,6 +6,8 @@ public sealed record BaseVersionOperator : IBaseVersionIncrement { public string Source { get; init; } = string.Empty; + public VersionIncrementSourceType SourceType { get; } = VersionIncrementSourceType.Tree; + public ICommit? BaseVersionSource { get; init; } public VersionField Increment { get; init; } diff --git a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/ConfiguredNextVersionVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/ConfiguredNextVersionVersionStrategy.cs index 87645553ca..9253986704 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/ConfiguredNextVersionVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/ConfiguredNextVersionVersionStrategy.cs @@ -18,16 +18,16 @@ public IEnumerable GetBaseVersions(EffectiveBranchConfiguration con { configuration.NotNull(); - if (!Context.Configuration.VersionStrategy.HasFlag(VersionStrategies.ConfiguredNextVersion)) + if (!this.Context.Configuration.VersionStrategy.HasFlag(VersionStrategies.ConfiguredNextVersion)) yield break; - var nextVersion = Context.Configuration.NextVersion; + var nextVersion = this.Context.Configuration.NextVersion; if (!nextVersion.IsNullOrEmpty()) { var semanticVersion = SemanticVersion.Parse( - nextVersion, Context.Configuration.TagPrefixPattern, Context.Configuration.SemanticVersionFormat + nextVersion, this.Context.Configuration.TagPrefixPattern, this.Context.Configuration.SemanticVersionFormat ); - var label = configuration.Value.GetBranchSpecificLabel(Context.CurrentBranch.Name, null); + var label = configuration.Value.GetBranchSpecificLabel(this.Context.CurrentBranch.Name, null); if (semanticVersion.IsMatchForBranchSpecificLabel(label)) { @@ -42,7 +42,7 @@ public IEnumerable GetBaseVersions(EffectiveBranchConfiguration con }; } - yield return new BaseVersion("NextVersion in GitVersion configuration file", semanticVersion) + yield return new BaseVersion("NextVersion in GitVersion configuration file", semanticVersion, sourceType: VersionIncrementSourceType.NextVersionConfig) { Operator = operation }; diff --git a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/FallbacktVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/FallbacktVersionStrategy.cs index 31858d5a4d..7724a69117 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/FallbacktVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/FallbacktVersionStrategy.cs @@ -58,7 +58,8 @@ private IEnumerable GetBaseVersionsInternal(EffectiveBranchConfigur Increment = increment, ForceIncrement = false, Label = label - } + }, + SourceType = VersionIncrementSourceType.Fallback }; } } diff --git a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/IBaseVersionIncrement.cs b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/IBaseVersionIncrement.cs index 02f53b7cd1..9cf7acea9d 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/IBaseVersionIncrement.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/IBaseVersionIncrement.cs @@ -6,5 +6,7 @@ public interface IBaseVersionIncrement { string Source { get; } + VersionIncrementSourceType SourceType { get; } + ICommit? BaseVersionSource { get; } } diff --git a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/TaggedCommitVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/TaggedCommitVersionStrategy.cs index 4e08eef9c8..9d00de96ea 100644 --- a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/TaggedCommitVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/TaggedCommitVersionStrategy.cs @@ -78,7 +78,7 @@ private IEnumerable GetBaseVersionsInternal(EffectiveBranchConfigur semanticVersionTreshold = semanticVersion.Value.Increment(increment, null, forceIncrement: true); yield return new BaseVersion( - $"Git tag '{semanticVersion.Tag.Name.Friendly}'", semanticVersion.Value, baseVersionSource) + $"Git tag '{semanticVersion.Tag.Name.Friendly}'", semanticVersion.Value, baseVersionSource, VersionIncrementSourceType.Tag) { Operator = new BaseVersionOperator { diff --git a/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/VersionIncrementSourceType.cs b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/VersionIncrementSourceType.cs new file mode 100644 index 0000000000..25dc551e09 --- /dev/null +++ b/src/GitVersion.Core/VersionCalculation/VersionSearchStrategies/VersionIncrementSourceType.cs @@ -0,0 +1,9 @@ +namespace GitVersion.VersionCalculation; + +public enum VersionIncrementSourceType +{ + Tree, + Tag, + Fallback, + NextVersionConfig +} diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index aea0217adc..709ed3c303 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using GitVersion.Extensions; using GitVersion.Helpers; using LibGit2Sharp; @@ -7,6 +8,7 @@ namespace GitVersion.Git; internal sealed partial class GitRepository { private Lazy? repositoryLazy; + private readonly ConcurrentDictionary?> patchPathsCache = new(); private IRepository RepositoryInstance { @@ -53,6 +55,39 @@ public void DiscoverRepository(string? gitDirectory) }); } + public IReadOnlyList? FindPatchPaths(ICommit commit, string? tagPrefix) + { + ArgumentNullException.ThrowIfNull(commit); + + return patchPathsCache.GetOrAdd(commit.Sha, commitSha => + { + if (PatchPathsNeedsToBeDetermined(commitSha, tagPrefix)) + { + var innerCommit = this.RepositoryInstance.Commits.First(c => c.Sha == commitSha); + Tree commitTree = innerCommit.Tree; // Main Tree + Tree? parentCommitTree = innerCommit.Parents.FirstOrDefault()?.Tree; // Secondary Tree + Patch patch = this.RepositoryInstance.Diff.Compare(parentCommitTree, commitTree); // Difference + return patch.Select(element => element.Path).ToList(); + } + return null; + }); + } + + private bool PatchPathsNeedsToBeDetermined(string commitSha, string? tagPrefix) + { + if (!string.IsNullOrEmpty(tagPrefix)) + { + foreach (var tag in this.RepositoryInstance.Tags.Where(element => element.Target.Sha == commitSha)) + { + if (tag.FriendlyName.StartsWith(tagPrefix, StringComparison.InvariantCulture)) + { + return false; + } + } + } + return true; + } + public int UncommittedChangesCount() { var retryAction = new RetryAction();