diff --git a/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs b/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs index 64188e3b8c..ce9c392228 100644 --- a/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs +++ b/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs @@ -43,5 +43,7 @@ public interface IRepositoryStore VersionField? DetermineIncrementedField(BaseVersion baseVersion, GitVersionContext context); int GetNumberOfUncommittedChanges(); + + public IGitRepository Repository { get; } } } diff --git a/src/GitVersion.Core/Core/GitVersionContextFactory.cs b/src/GitVersion.Core/Core/GitVersionContextFactory.cs index d6af78eb92..abe99fd359 100644 --- a/src/GitVersion.Core/Core/GitVersionContextFactory.cs +++ b/src/GitVersion.Core/Core/GitVersionContextFactory.cs @@ -42,7 +42,7 @@ public GitVersionContext Create(GitVersionOptions? gitVersionOptions) var currentCommitTaggedVersion = this.repositoryStore.GetCurrentCommitTaggedVersion(currentCommit, effectiveConfiguration); var numberOfUncommittedChanges = this.repositoryStore.GetNumberOfUncommittedChanges(); - return new GitVersionContext(currentBranch, currentCommit, configuration, effectiveConfiguration, currentCommitTaggedVersion, numberOfUncommittedChanges); + return new GitVersionContext(currentBranch, currentCommit, configuration, effectiveConfiguration, currentCommitTaggedVersion, numberOfUncommittedChanges, this.repositoryStore.Repository); } } } diff --git a/src/GitVersion.Core/Core/RepositoryStore.cs b/src/GitVersion.Core/Core/RepositoryStore.cs index c819bbe8af..4875b1af21 100644 --- a/src/GitVersion.Core/Core/RepositoryStore.cs +++ b/src/GitVersion.Core/Core/RepositoryStore.cs @@ -491,5 +491,6 @@ private ICommit GetForwardMerge(ICommit? commitToFindCommonBase, ICommit? findMe public ICommit FindMergeBase(ICommit commit, ICommit mainlineTip) => this.repository.FindMergeBase(commit, mainlineTip); public int GetNumberOfUncommittedChanges() => this.repository.GetNumberOfUncommittedChanges(); + public IGitRepository Repository => this.repository; } } diff --git a/src/GitVersion.Core/Git/IGitRepository.cs b/src/GitVersion.Core/Git/IGitRepository.cs index 376f463e89..e54fdb1363 100644 --- a/src/GitVersion.Core/Git/IGitRepository.cs +++ b/src/GitVersion.Core/Git/IGitRepository.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace GitVersion { public interface IGitRepository @@ -14,5 +16,7 @@ public interface IGitRepository ICommit FindMergeBase(ICommit commit, ICommit otherCommit); int GetNumberOfUncommittedChanges(); + + IEnumerable DiffPathChanges(ICommit commitFrom, ICommit commitTo); } } diff --git a/src/GitVersion.Core/Model/Configuration/IgnoreConfig.cs b/src/GitVersion.Core/Model/Configuration/IgnoreConfig.cs index 3db1b4b2ff..8aae729ddf 100644 --- a/src/GitVersion.Core/Model/Configuration/IgnoreConfig.cs +++ b/src/GitVersion.Core/Model/Configuration/IgnoreConfig.cs @@ -8,7 +8,11 @@ namespace GitVersion.Model.Configuration { public class IgnoreConfig { - public IgnoreConfig() => ShAs = Enumerable.Empty(); + public IgnoreConfig() + { + ShAs = Enumerable.Empty(); + PathFilters = new PathFilterConfig(); + } [YamlMember(Alias = "commits-before")] public DateTimeOffset? Before { get; set; } @@ -20,10 +24,17 @@ public class IgnoreConfig public virtual bool IsEmpty => Before == null && (ShAs == null || ShAs.Any() == false); + [YamlMember(Alias = "paths")] + public PathFilterConfig PathFilters { get; set; } + public virtual IEnumerable ToFilters() { if (ShAs.Any()) yield return new ShaVersionFilter(ShAs); if (Before.HasValue) yield return new MinDateVersionFilter(Before.Value); + foreach (var filter in PathFilters.ToFilters()) + { + yield return filter; + } } } } diff --git a/src/GitVersion.Core/Model/Configuration/PathFilterConfig.cs b/src/GitVersion.Core/Model/Configuration/PathFilterConfig.cs new file mode 100644 index 0000000000..9f6a8f3099 --- /dev/null +++ b/src/GitVersion.Core/Model/Configuration/PathFilterConfig.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using GitVersion.VersionCalculation; +using YamlDotNet.Serialization; + +namespace GitVersion.Model.Configuration +{ + public class PathFilterConfig + { + public PathFilterConfig() + { + Include = Enumerable.Empty(); + Exclude = Enumerable.Empty(); + } + + [YamlMember(Alias = "exclude")] + public IEnumerable Exclude { get; set; } + + [YamlMember(Alias = "include")] + public IEnumerable Include { get; set; } + + public virtual IEnumerable ToFilters() + { + if (Include.Any()) yield return new PathFilter(Include, PathFilter.PathFilterMode.Inclusive); + if (Exclude.Any()) yield return new PathFilter(Exclude, PathFilter.PathFilterMode.Exclusive); + } + } +} diff --git a/src/GitVersion.Core/Model/GitVersionContext.cs b/src/GitVersion.Core/Model/GitVersionContext.cs index 06d9ea4b2c..53da6fb5d8 100644 --- a/src/GitVersion.Core/Model/GitVersionContext.cs +++ b/src/GitVersion.Core/Model/GitVersionContext.cs @@ -20,12 +20,14 @@ public class GitVersionContext public int NumberOfUncommittedChanges { get; } + public IGitRepository Repository { get; } + public GitVersionContext() { } public GitVersionContext(IBranch currentBranch, ICommit? currentCommit, - Config configuration, EffectiveConfiguration effectiveConfiguration, SemanticVersion currentCommitTaggedVersion, int numberOfUncommittedChanges) + Config configuration, EffectiveConfiguration effectiveConfiguration, SemanticVersion currentCommitTaggedVersion, int numberOfUncommittedChanges, IGitRepository repository) { CurrentCommit = currentCommit; CurrentBranch = currentBranch; @@ -36,6 +38,7 @@ public GitVersionContext(IBranch currentBranch, ICommit? currentCommit, CurrentCommitTaggedVersion = currentCommitTaggedVersion; NumberOfUncommittedChanges = numberOfUncommittedChanges; + Repository = repository; } } } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculator.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculator.cs index 8f4243c77a..f3f3dea429 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculator.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculator.cs @@ -4,6 +4,7 @@ using GitVersion.Common; using GitVersion.Configuration; using GitVersion.Logging; +using Polly; namespace GitVersion.VersionCalculation { @@ -78,6 +79,7 @@ public BaseVersion GetBaseVersion() throw new Exception("Base version should not be null"); var calculatedBase = new BaseVersion( + this.versionContext.Value, maxVersion.Version!.Source, maxVersion.Version.ShouldIncrement, maxVersion.Version.SemanticVersion, baseVersionWithOldestSource.BaseVersionSource, maxVersion.Version.BranchNameOverride); @@ -126,6 +128,7 @@ private void FixTheBaseVersionSourceOfMergeMessageStrategyIfReleaseBranchWasMerg { var parents = baseVersion.Version.BaseVersionSource!.Parents.ToList(); baseVersion.Version = new BaseVersion( + this.versionContext.Value, baseVersion.Version.Source, baseVersion.Version.ShouldIncrement, baseVersion.Version.SemanticVersion, diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/BaseVersion.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/BaseVersion.cs index 651a8b2457..4ae68acae6 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/BaseVersion.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/BaseVersion.cs @@ -2,15 +2,18 @@ namespace GitVersion.VersionCalculation { public class BaseVersion { - public BaseVersion(string source, bool shouldIncrement, SemanticVersion semanticVersion, ICommit? baseVersionSource, string? branchNameOverride) + public BaseVersion(GitVersionContext context, string source, bool shouldIncrement, SemanticVersion semanticVersion, ICommit? baseVersionSource, string? branchNameOverride) { Source = source; ShouldIncrement = shouldIncrement; SemanticVersion = semanticVersion; BaseVersionSource = baseVersionSource; BranchNameOverride = branchNameOverride; + Context = context; } + public GitVersionContext Context { get;} + public string Source { get; } public bool ShouldIncrement { get; } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/ConfigNextVersionVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/ConfigNextVersionVersionStrategy.cs index 3b29998c61..16e91ff464 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/ConfigNextVersionVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/ConfigNextVersionVersionStrategy.cs @@ -21,7 +21,7 @@ public override IEnumerable GetVersions() if (nextVersion.IsNullOrEmpty() || Context.IsCurrentCommitTagged) yield break; var semanticVersion = SemanticVersion.Parse(nextVersion, Context.Configuration?.GitTagPrefix); - yield return new BaseVersion("NextVersion in GitVersion configuration file", false, semanticVersion, null, null); + yield return new BaseVersion(Context, "NextVersion in GitVersion configuration file", false, semanticVersion, null, null); } } } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs index b425240a16..2fdc2c5fc4 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/FallbackVersionStrategy.cs @@ -24,7 +24,7 @@ public override IEnumerable GetVersions() var baseVersionSource = this.repositoryStore.GetBaseVersionSource(currentBranchTip); - yield return new BaseVersion("Fallback base version", false, new SemanticVersion(minor: 1), baseVersionSource, null); + yield return new BaseVersion(Context, "Fallback base version", false, new SemanticVersion(minor: 1), baseVersionSource, null); } } } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs index 05e4453cbf..7c408e2f0a 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/MergeMessageVersionStrategy.cs @@ -35,7 +35,7 @@ public override IEnumerable GetVersions() var shouldIncrement = Context.Configuration?.PreventIncrementForMergedBranchVersion != true; return new[] { - new BaseVersion($"{MergeMessageStrategyPrefix} '{c.Message.Trim()}'", shouldIncrement, mergeMessage.Version, c, null) + new BaseVersion(Context, $"{MergeMessageStrategyPrefix} '{c.Message.Trim()}'", shouldIncrement, mergeMessage.Version, c, null) }; } return Enumerable.Empty(); diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs index 2fe72ffcc7..ceeb2c80a6 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs @@ -36,7 +36,7 @@ internal IEnumerable GetTaggedVersions(IBranch? currentBranch, Date private BaseVersion CreateBaseVersion(GitVersionContext context, VersionTaggedCommit version) { var shouldUpdateVersion = version.Commit.Sha != context.CurrentCommit?.Sha; - var baseVersion = new BaseVersion(FormatSource(version), shouldUpdateVersion, version.SemVer, version.Commit, null); + var baseVersion = new BaseVersion(Context, FormatSource(version), shouldUpdateVersion, version.SemVer, version.Commit, null); return baseVersion; } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs index cd7cc0b587..13d0c4a904 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/TrackReleaseBranchesVersionStrategy.cs @@ -70,7 +70,8 @@ private IEnumerable ReleaseBranchBaseVersions() // Need to drop branch overrides and give a bit more context about // where this version came from var source1 = "Release branch exists -> " + baseVersion.Source; - return new BaseVersion(source1, + return new BaseVersion(Context, + source1, baseVersion.ShouldIncrement, baseVersion.SemanticVersion, baseVersion.BaseVersionSource, @@ -95,7 +96,7 @@ private IEnumerable GetReleaseVersion(GitVersionContext context, IB return this.releaseVersionStrategy .GetVersions(tagPrefixRegex, releaseBranch) - .Select(b => new BaseVersion(b.Source, true, b.SemanticVersion, baseSource, b.BranchNameOverride)); + .Select(b => new BaseVersion(Context, b.Source, true, b.SemanticVersion, baseSource, b.BranchNameOverride)); } } } diff --git a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs index 1d2a7a3c0c..753a971a58 100644 --- a/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs +++ b/src/GitVersion.Core/VersionCalculation/BaseVersionCalculators/VersionInBranchNameVersionStrategy.cs @@ -38,7 +38,7 @@ internal IEnumerable GetVersions(string? tagPrefixRegex, IBranch? c { var commitBranchWasBranchedFrom = this.repositoryStore.FindCommitBranchWasBranchedFrom(currentBranch, Context.FullConfiguration); var branchNameOverride = branchName.RegexReplace("[-/]" + versionInBranch.Item1, string.Empty); - yield return new BaseVersion("Version in branch name", false, versionInBranch.Item2, commitBranchWasBranchedFrom.Commit, branchNameOverride); + yield return new BaseVersion(Context, "Version in branch name", false, versionInBranch.Item2, commitBranchWasBranchedFrom.Commit, branchNameOverride); } } diff --git a/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs b/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs index 5283db5796..ce13451d01 100644 --- a/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs +++ b/src/GitVersion.Core/VersionCalculation/IncrementStrategyFinder.cs @@ -59,7 +59,10 @@ public class IncrementStrategyFinder : IIncrementStrategyFinder var patchRegex = TryGetRegexOrDefault(context.Configuration?.PatchVersionBumpMessage, DefaultPatchPatternRegex); var none = TryGetRegexOrDefault(context.Configuration?.NoBumpMessage, DefaultNoBumpPatternRegex); + var pathFilters = context.Configuration.VersionFilters.OfType(); + var increments = commits + .Where(c => !pathFilters.Any(f => f.Exclude(c, context, out _))) .Select(c => GetIncrementFromCommit(c, majorRegex, minorRegex, patchRegex, none)) .Where(v => v != null) .Select(v => v!.Value) diff --git a/src/GitVersion.Core/VersionCalculation/PathFilter.cs b/src/GitVersion.Core/VersionCalculation/PathFilter.cs new file mode 100644 index 0000000000..17ec438e08 --- /dev/null +++ b/src/GitVersion.Core/VersionCalculation/PathFilter.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GitVersion.VersionCalculation +{ + public class PathFilter : IVersionFilter + { + private readonly static Dictionary> patchsCache = new Dictionary>(); + + public enum PathFilterMode { Inclusive, Exclusive } + + private readonly IEnumerable paths; + private readonly PathFilterMode mode; + + public PathFilter(IEnumerable paths, PathFilterMode mode = PathFilterMode.Inclusive) + { + this.paths = paths ?? throw new ArgumentNullException(nameof(paths)); + this.mode = mode; + } + + public bool Exclude(BaseVersion version, out string reason) + { + if (version == null) throw new ArgumentNullException(nameof(version)); + + reason = null; + if (version.Source.StartsWith("Fallback") || version.Source.StartsWith("Git tag") || version.Source.StartsWith("NextVersion")) return false; + + return Exclude(version.BaseVersionSource, version.Context, out reason); + } + + public bool Exclude(ICommit commit, GitVersionContext context, out string reason) + { + if (commit == null) throw new ArgumentNullException(nameof(commit)); + + reason = null; + + var match = new System.Text.RegularExpressions.Regex($"^({context.Configuration.GitTagPrefix}).*$", System.Text.RegularExpressions.RegexOptions.Compiled); + + + + IEnumerable patch = null; + if (!patchsCache.ContainsKey(commit.Sha)) + { + //if (!context.Repository.Tags.Any(t => t.Target.Sha == commit.Sha && match.IsMatch(t.FriendlyName))) + //{ + // Tree commitTree = commit.Tree; // Main Tree + // Tree parentCommitTree = commit.Parents.FirstOrDefault()?.Tree; // Secondary Tree + // patch = context.Repository.Diff.Compare(parentCommitTree, commitTree); // Difference + //} + //patchsCache[commit.Sha] = patch; + + if (!context.Repository.Tags.Any(t => t.TargetSha == commit.Sha && match.IsMatch(t.Name.Friendly))) + + patch = context.Repository.DiffPathChanges(commit.Parents.FirstOrDefault(), commit); + } + patchsCache[commit.Sha] = patch; + + + patch = patchsCache[commit.Sha]; + if (patch != null) + { + switch (mode) + { + case PathFilterMode.Inclusive: + if (!paths.Any(path => patch.Any(p => p.StartsWith(path, StringComparison.OrdinalIgnoreCase)))) + { + reason = "Source was ignored due to commit path is not present"; + return true; + } + break; + case PathFilterMode.Exclusive: + if (paths.Any(path => patch.All(p => p.StartsWith(path, StringComparison.OrdinalIgnoreCase)))) + { + reason = "Source was ignored due to commit path excluded"; + return true; + } + break; + } + } + + + return false; + } + } +} diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index b9a6b6e76c..427b4e075f 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -198,6 +198,17 @@ public void Fetch(string remote, IEnumerable refSpecs, AuthenticationInf RepositoryExtensions.RunSafe(() => Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage)); + public IEnumerable DiffPathChanges(ICommit commitFrom, ICommit commitTo) + { + var cFrom = this.repositoryInstance.Commits.Single(c => c.Sha == commitFrom.Sha); + var cTo = this.repositoryInstance.Commits.Single(c => c.Sha == commitTo.Sha); + + + var patch = this.repositoryInstance.Diff.Compare(cTo.Parents.FirstOrDefault()?.Tree, cFrom.Tree); + + return patch.Select(p => p.Path); + } + internal static string Discover(string? path) => Repository.Discover(path); private static FetchOptions GetFetchOptions(AuthenticationInfo auth) =>