From f828c84747271a770bcd8e7e0929f90ae0d296f8 Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Wed, 8 Oct 2025 07:15:31 +0200 Subject: [PATCH 01/11] improves branch/tag cache keys Simplifies branch/tag cache keys by including the remote name and SHA. --- src/GitVersion.LibGit2Sharp/Git/GitRepository.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 115bceb44c..39063573c8 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -70,17 +70,17 @@ public Branch GetOrCreate(LibGit2Sharp.Branch innerBranch, Diff repoDiff) return new Branch(innerBranch, repoDiff, this); } - var cacheKey = $"{innerBranch.CanonicalName}|{innerBranch.Tip.Sha}|{innerBranch.RemoteName}"; - return cachedBranches.GetOrAdd(cacheKey, new Branch(innerBranch, repoDiff, this)); + var cacheKey = $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}@{innerBranch.Tip.Sha}"; + return cachedBranches.GetOrAdd(cacheKey, _ => new Branch(innerBranch, repoDiff, this)); } public Commit GetOrCreate(LibGit2Sharp.Commit innerCommit, Diff repoDiff) => - cachedCommits.GetOrAdd(innerCommit.Sha, new Commit(innerCommit, repoDiff, this)); + cachedCommits.GetOrAdd(innerCommit.Sha, _ => new Commit(innerCommit, repoDiff, this)); public Tag GetOrCreate(LibGit2Sharp.Tag innerTag, Diff repoDiff) { - var cacheKey = $"{innerTag.CanonicalName}|{innerTag.Target.Sha}"; - return cachedTags.GetOrAdd(cacheKey, new Tag(innerTag, repoDiff, this)); + var cacheKey = $"{innerTag.CanonicalName}@{innerTag.Target.Sha}"; + return cachedTags.GetOrAdd(cacheKey, _ => new Tag(innerTag, repoDiff, this)); } public void Dispose() From f9e44b044920d097ecdf35d9e2ee66727a632591 Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Wed, 8 Oct 2025 07:45:30 +0200 Subject: [PATCH 02/11] code formatting --- src/GitVersion.Core/Helpers/FileSystemHelper.cs | 6 +++--- src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs | 3 +-- src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs | 3 +-- src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs | 4 ++-- src/GitVersion.LibGit2Sharp/Git/TagCollection.cs | 3 +-- src/GitVersion.Output/TemplateManager.cs | 4 ++-- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/GitVersion.Core/Helpers/FileSystemHelper.cs b/src/GitVersion.Core/Helpers/FileSystemHelper.cs index bdd9ccfc90..4b331170d0 100644 --- a/src/GitVersion.Core/Helpers/FileSystemHelper.cs +++ b/src/GitVersion.Core/Helpers/FileSystemHelper.cs @@ -88,14 +88,14 @@ public static string GetTempPath() public static string GetDirectoryName(string? path) { - ArgumentNullException.ThrowIfNull(path, nameof(path)); + ArgumentNullException.ThrowIfNull(path); return fileSystem.Path.GetDirectoryName(path)!; } public static string GetFileName(string? path) { - ArgumentNullException.ThrowIfNull(path, nameof(path)); + ArgumentNullException.ThrowIfNull(path); return fileSystem.Path.GetFileName(path); } @@ -116,7 +116,7 @@ public static string Combine(string? path1, string? path2) public static string Combine(string? path1) { - ArgumentNullException.ThrowIfNull(path1, nameof(path1)); + ArgumentNullException.ThrowIfNull(path1); return fileSystem.Path.Combine(path1); } diff --git a/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs b/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs index b8b1df839d..335224f568 100644 --- a/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs @@ -18,8 +18,7 @@ internal BranchCollection(LibGit2Sharp.BranchCollection collection, Diff diff, G this.repo = repo.NotNull(); } - public IEnumerator GetEnumerator() - => this.branches.Value.GetEnumerator(); + public IEnumerator GetEnumerator() => this.branches.Value.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs b/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs index 05d9940f94..279d7527f5 100644 --- a/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs @@ -18,8 +18,7 @@ internal CommitCollection(ICommitLog collection, Diff diff, GitRepository repo) this.repo = repo.NotNull(); } - public IEnumerator GetEnumerator() - => this.commits.Value.GetEnumerator(); + public IEnumerator GetEnumerator() => this.commits.Value.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs index 2aa74a2140..d7031033ce 100644 --- a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs @@ -15,6 +15,8 @@ public IEnumerator GetEnumerator() return this.references.GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(string name, string canonicalRefNameOrObject, bool allowOverwrite = false) => this.innerCollection.Add(name, canonicalRefNameOrObject, allowOverwrite); public void UpdateTarget(IReference directRef, IObjectId targetId) @@ -23,8 +25,6 @@ public void UpdateTarget(IReference directRef, IObjectId targetId) this.references = null; } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public IReference? this[string name] { get diff --git a/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs b/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs index ec339e28b3..925ef49c2f 100644 --- a/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs @@ -14,8 +14,7 @@ internal TagCollection(LibGit2Sharp.TagCollection collection, LibGit2Sharp.Diff this.tags = new Lazy>(() => [.. collection.Select(tag => repo.GetOrCreate(tag, diff))]); } - public IEnumerator GetEnumerator() - => this.tags.Value.GetEnumerator(); + public IEnumerator GetEnumerator() => this.tags.Value.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } diff --git a/src/GitVersion.Output/TemplateManager.cs b/src/GitVersion.Output/TemplateManager.cs index 863b41c290..6da666c691 100644 --- a/src/GitVersion.Output/TemplateManager.cs +++ b/src/GitVersion.Output/TemplateManager.cs @@ -16,7 +16,7 @@ internal class TemplateManager(TemplateType templateType) public string? GetTemplateFor(string? fileExtension) { - ArgumentNullException.ThrowIfNull(fileExtension, nameof(fileExtension)); + ArgumentNullException.ThrowIfNull(fileExtension); string? result = null; @@ -30,7 +30,7 @@ internal class TemplateManager(TemplateType templateType) public string? GetAddFormatFor(string? fileExtension) { - ArgumentNullException.ThrowIfNull(fileExtension, nameof(fileExtension)); + ArgumentNullException.ThrowIfNull(fileExtension); string? result = null; From 501a75fee080094187c426fba46a370d3c579373 Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Wed, 8 Oct 2025 08:00:40 +0200 Subject: [PATCH 03/11] caches git objects Renames `GetOrCreate` to `GetOrWrap` to better reflect its caching behavior. --- src/GitVersion.LibGit2Sharp/Git/Branch.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs | 4 ++-- src/GitVersion.LibGit2Sharp/Git/Commit.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/GitRepository.cs | 10 +++++----- src/GitVersion.LibGit2Sharp/Git/Tag.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/TagCollection.cs | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/GitVersion.LibGit2Sharp/Git/Branch.cs b/src/GitVersion.LibGit2Sharp/Git/Branch.cs index bfa286cad8..7cd5cce70e 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Branch.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Branch.cs @@ -18,7 +18,7 @@ internal Branch(LibGit2Sharp.Branch branch, LibGit2Sharp.Diff diff, GitRepositor Name = new(branch.CanonicalName); var commit = this.innerBranch.Tip; - Tip = commit is null ? null : repo.GetOrCreate(commit, diff); + Tip = commit is null ? null : repo.GetOrWrap(commit, diff); var commits = this.innerBranch.Commits; Commits = new CommitCollection(commits, diff, repo); diff --git a/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs b/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs index 335224f568..8fd144815d 100644 --- a/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs @@ -13,7 +13,7 @@ internal sealed class BranchCollection : IBranchCollection internal BranchCollection(LibGit2Sharp.BranchCollection collection, Diff diff, GitRepository repo) { this.innerCollection = collection.NotNull(); - this.branches = new Lazy>(() => [.. this.innerCollection.Select(branch => repo.GetOrCreate(branch, diff))]); + this.branches = new Lazy>(() => [.. this.innerCollection.Select(branch => repo.GetOrWrap(branch, diff))]); this.diff = diff.NotNull(); this.repo = repo.NotNull(); } @@ -28,7 +28,7 @@ public IBranch? this[string name] { name = name.NotNull(); var branch = this.innerCollection[name]; - return branch is null ? null : this.repo.GetOrCreate(branch, this.diff); + return branch is null ? null : this.repo.GetOrWrap(branch, this.diff); } } diff --git a/src/GitVersion.LibGit2Sharp/Git/Commit.cs b/src/GitVersion.LibGit2Sharp/Git/Commit.cs index 60bb853dd7..6e7ca2c6d6 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Commit.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Commit.cs @@ -19,7 +19,7 @@ internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, Git repoDiff.NotNull(); repo.NotNull(); this.innerCommit = innerCommit.NotNull(); - this.parentsLazy = new(() => innerCommit.Parents.Select(parent => repo.GetOrCreate(parent, repoDiff)).ToList()); + this.parentsLazy = new(() => innerCommit.Parents.Select(parent => repo.GetOrWrap(parent, repoDiff)).ToList()); When = innerCommit.Committer.When; this.repoDiff = repoDiff; } diff --git a/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs b/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs index 279d7527f5..4c4607815e 100644 --- a/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs @@ -13,7 +13,7 @@ internal sealed class CommitCollection : ICommitCollection internal CommitCollection(ICommitLog collection, Diff diff, GitRepository repo) { this.innerCollection = collection.NotNull(); - this.commits = new Lazy>(() => [.. this.innerCollection.Select(commit => repo.GetOrCreate(commit, diff))]); + this.commits = new Lazy>(() => [.. this.innerCollection.Select(commit => repo.GetOrWrap(commit, diff))]); this.diff = diff.NotNull(); this.repo = repo.NotNull(); } diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 39063573c8..49280c0726 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -25,7 +25,7 @@ private IRepository RepositoryInstance public string WorkingDirectory => RepositoryInstance.Info.WorkingDirectory; public bool IsHeadDetached => RepositoryInstance.Info.IsHeadDetached; public bool IsShallow => RepositoryInstance.Info.IsShallow; - public IBranch Head => GetOrCreate(RepositoryInstance.Head, RepositoryInstance.Diff); + public IBranch Head => GetOrWrap(RepositoryInstance.Head, RepositoryInstance.Diff); public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this); public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs); @@ -53,7 +53,7 @@ public void DiscoverRepository(string? gitDirectory) var first = (Commit)commit; var second = (Commit)otherCommit; var mergeBase = RepositoryInstance.ObjectDatabase.FindMergeBase(first, second); - return mergeBase == null ? null : GetOrCreate(mergeBase, RepositoryInstance.Diff); + return mergeBase == null ? null : GetOrWrap(mergeBase, RepositoryInstance.Diff); }); } @@ -63,7 +63,7 @@ public int UncommittedChangesCount() return retryAction.Execute(GetUncommittedChangesCountInternal); } - public Branch GetOrCreate(LibGit2Sharp.Branch innerBranch, Diff repoDiff) + public Branch GetOrWrap(LibGit2Sharp.Branch innerBranch, Diff repoDiff) { if (innerBranch.Tip is null) { @@ -74,10 +74,10 @@ public Branch GetOrCreate(LibGit2Sharp.Branch innerBranch, Diff repoDiff) return cachedBranches.GetOrAdd(cacheKey, _ => new Branch(innerBranch, repoDiff, this)); } - public Commit GetOrCreate(LibGit2Sharp.Commit innerCommit, Diff repoDiff) => + public Commit GetOrWrap(LibGit2Sharp.Commit innerCommit, Diff repoDiff) => cachedCommits.GetOrAdd(innerCommit.Sha, _ => new Commit(innerCommit, repoDiff, this)); - public Tag GetOrCreate(LibGit2Sharp.Tag innerTag, Diff repoDiff) + public Tag GetOrWrap(LibGit2Sharp.Tag innerTag, Diff repoDiff) { var cacheKey = $"{innerTag.CanonicalName}@{innerTag.Target.Sha}"; return cachedTags.GetOrAdd(cacheKey, _ => new Tag(innerTag, repoDiff, this)); diff --git a/src/GitVersion.LibGit2Sharp/Git/Tag.cs b/src/GitVersion.LibGit2Sharp/Git/Tag.cs index 89fb6d777a..0fe66a2c7d 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Tag.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Tag.cs @@ -37,7 +37,7 @@ internal Tag(LibGit2Sharp.Tag tag, Diff diff, GitRepository repo) target = annotation.Target; } - return target is LibGit2Sharp.Commit commit ? this.repo.GetOrCreate(commit, this.diff) : null; + return target is LibGit2Sharp.Commit commit ? this.repo.GetOrWrap(commit, this.diff) : null; } public override bool Equals(object? obj) => Equals(obj as ITag); diff --git a/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs b/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs index 925ef49c2f..64ce7d7c1c 100644 --- a/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs @@ -11,7 +11,7 @@ internal TagCollection(LibGit2Sharp.TagCollection collection, LibGit2Sharp.Diff collection.NotNull(); diff.NotNull(); repo.NotNull(); - this.tags = new Lazy>(() => [.. collection.Select(tag => repo.GetOrCreate(tag, diff))]); + this.tags = new Lazy>(() => [.. collection.Select(tag => repo.GetOrWrap(tag, diff))]); } public IEnumerator GetEnumerator() => this.tags.Value.GetEnumerator(); From 4a981adba46e8a61f1eb10c25d442388d381d6a1 Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Wed, 8 Oct 2025 10:28:55 +0200 Subject: [PATCH 04/11] replace IGitObject with ICommit in method signatures --- .../MergeMessageBaseVersionStrategyTests.cs | 2 -- .../Core/Abstractions/IRepositoryStore.cs | 2 +- .../Core/BranchesContainingCommitFinder.cs | 4 +-- src/GitVersion.Core/Core/RepositoryStore.cs | 2 +- src/GitVersion.Core/Git/ICommit.cs | 6 ++++- src/GitVersion.Core/Git/IGitObject.cs | 7 ----- src/GitVersion.Core/PublicAPI.Shipped.txt | 4 --- src/GitVersion.Core/PublicAPI.Unshipped.txt | 3 +++ src/GitVersion.LibGit2Sharp/Git/Commit.cs | 8 ++++-- src/GitVersion.LibGit2Sharp/Git/GitObject.cs | 26 ------------------- 10 files changed, 18 insertions(+), 46 deletions(-) delete mode 100644 src/GitVersion.Core/Git/IGitObject.cs delete mode 100644 src/GitVersion.LibGit2Sharp/Git/GitObject.cs diff --git a/src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs b/src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs index 1f03f3a9ea..d38ae71969 100644 --- a/src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs +++ b/src/GitVersion.Core.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs @@ -199,8 +199,6 @@ private class MockCommit : ICommit { public bool Equals(ICommit? other) => throw new NotImplementedException(); public int CompareTo(ICommit? other) => throw new NotImplementedException(); - public bool Equals(IGitObject? other) => throw new NotImplementedException(); - public int CompareTo(IGitObject? other) => throw new NotImplementedException(); public IObjectId Id => throw new NotImplementedException(); public string Sha => throw new NotImplementedException(); public IReadOnlyList Parents => throw new NotImplementedException(); diff --git a/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs b/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs index e3a2f260af..18fc7edd92 100644 --- a/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs +++ b/src/GitVersion.Core/Core/Abstractions/IRepositoryStore.cs @@ -22,7 +22,7 @@ public interface IRepositoryStore IReadOnlyList GetCommitLog(ICommit? baseVersionSource, ICommit currentCommit, IIgnoreConfiguration ignore); IReadOnlyList GetCommitsReacheableFromHead(ICommit? headCommit, IIgnoreConfiguration ignore); - IReadOnlyList GetCommitsReacheableFrom(IGitObject commit, IBranch branch); + IReadOnlyList GetCommitsReacheableFrom(ICommit commit, IBranch branch); IBranch GetTargetBranch(string? targetBranchName); IBranch? FindBranch(ReferenceName branchName); diff --git a/src/GitVersion.Core/Core/BranchesContainingCommitFinder.cs b/src/GitVersion.Core/Core/BranchesContainingCommitFinder.cs index fb5f8b6d68..d078714e98 100644 --- a/src/GitVersion.Core/Core/BranchesContainingCommitFinder.cs +++ b/src/GitVersion.Core/Core/BranchesContainingCommitFinder.cs @@ -21,7 +21,7 @@ public IEnumerable GetBranchesContainingCommit(ICommit commit, IEnumera return InnerGetBranchesContainingCommit(commit, branches, onlyTrackedBranches); } - private IEnumerable InnerGetBranchesContainingCommit(IGitObject commit, IEnumerable branches, bool onlyTrackedBranches) + private IEnumerable InnerGetBranchesContainingCommit(ICommit commit, IEnumerable branches, bool onlyTrackedBranches) { using (log.IndentLog($"Getting branches containing the commit '{commit.Id}'.")) { @@ -63,6 +63,6 @@ private IEnumerable InnerGetBranchesContainingCommit(IGitObject commit, private static bool IncludeTrackedBranches(IBranch branch, bool includeOnlyTracked) => (includeOnlyTracked && branch.IsTracking) || !includeOnlyTracked; - private static bool BranchTipIsNullOrCommit(IBranch branch, IGitObject commit) + private static bool BranchTipIsNullOrCommit(IBranch branch, ICommit commit) => branch.Tip == null || branch.Tip.Sha == commit.Sha; } diff --git a/src/GitVersion.Core/Core/RepositoryStore.cs b/src/GitVersion.Core/Core/RepositoryStore.cs index ee018444fb..401cae566a 100644 --- a/src/GitVersion.Core/Core/RepositoryStore.cs +++ b/src/GitVersion.Core/Core/RepositoryStore.cs @@ -231,7 +231,7 @@ public IReadOnlyList GetCommitsReacheableFromHead(ICommit? headCommit, return [.. ignore.Filter(commits)]; } - public IReadOnlyList GetCommitsReacheableFrom(IGitObject commit, IBranch branch) + public IReadOnlyList GetCommitsReacheableFrom(ICommit commit, IBranch branch) { var filter = new CommitFilter { IncludeReachableFrom = branch }; diff --git a/src/GitVersion.Core/Git/ICommit.cs b/src/GitVersion.Core/Git/ICommit.cs index 8d3b225655..7c6900c1bb 100644 --- a/src/GitVersion.Core/Git/ICommit.cs +++ b/src/GitVersion.Core/Git/ICommit.cs @@ -1,9 +1,13 @@ namespace GitVersion.Git; -public interface ICommit : IEquatable, IComparable, IGitObject +public interface ICommit : IEquatable, IComparable { IReadOnlyList Parents { get; } + IObjectId Id { get; } + + string Sha { get; } + DateTimeOffset When { get; } string Message { get; } diff --git a/src/GitVersion.Core/Git/IGitObject.cs b/src/GitVersion.Core/Git/IGitObject.cs deleted file mode 100644 index 1965547a30..0000000000 --- a/src/GitVersion.Core/Git/IGitObject.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GitVersion.Git; - -public interface IGitObject : IEquatable, IComparable -{ - IObjectId Id { get; } - string Sha { get; } -} diff --git a/src/GitVersion.Core/PublicAPI.Shipped.txt b/src/GitVersion.Core/PublicAPI.Shipped.txt index 53c7d65518..f8cc9b01d7 100644 --- a/src/GitVersion.Core/PublicAPI.Shipped.txt +++ b/src/GitVersion.Core/PublicAPI.Shipped.txt @@ -28,7 +28,6 @@ GitVersion.Common.IRepositoryStore.FindMergeBase(GitVersion.Git.IBranch? branch, GitVersion.Common.IRepositoryStore.FindMergeBase(GitVersion.Git.ICommit! commit, GitVersion.Git.ICommit! mainlineTip) -> GitVersion.Git.ICommit? GitVersion.Common.IRepositoryStore.GetBranchesContainingCommit(GitVersion.Git.ICommit! commit, System.Collections.Generic.IEnumerable? branches = null, bool onlyTrackedBranches = false) -> System.Collections.Generic.IEnumerable! GitVersion.Common.IRepositoryStore.GetCommitLog(GitVersion.Git.ICommit? baseVersionSource, GitVersion.Git.ICommit! currentCommit, GitVersion.Configuration.IIgnoreConfiguration! ignore) -> System.Collections.Generic.IReadOnlyList! -GitVersion.Common.IRepositoryStore.GetCommitsReacheableFrom(GitVersion.Git.IGitObject! commit, GitVersion.Git.IBranch! branch) -> System.Collections.Generic.IReadOnlyList! GitVersion.Common.IRepositoryStore.GetCommitsReacheableFromHead(GitVersion.Git.ICommit? headCommit, GitVersion.Configuration.IIgnoreConfiguration! ignore) -> System.Collections.Generic.IReadOnlyList! GitVersion.Common.IRepositoryStore.GetCurrentCommit(GitVersion.Git.IBranch! currentBranch, string? commitId, GitVersion.Configuration.IIgnoreConfiguration! ignore) -> GitVersion.Git.ICommit? GitVersion.Common.IRepositoryStore.GetForwardMerge(GitVersion.Git.ICommit? commitToFindCommonBase, GitVersion.Git.ICommit? findMergeBase) -> GitVersion.Git.ICommit? @@ -227,9 +226,6 @@ GitVersion.Git.ICommit.When.get -> System.DateTimeOffset GitVersion.Git.ICommitCollection GitVersion.Git.ICommitCollection.GetCommitsPriorTo(System.DateTimeOffset olderThan) -> System.Collections.Generic.IEnumerable! GitVersion.Git.ICommitCollection.QueryBy(GitVersion.Git.CommitFilter! commitFilter) -> System.Collections.Generic.IEnumerable! -GitVersion.Git.IGitObject -GitVersion.Git.IGitObject.Id.get -> GitVersion.Git.IObjectId! -GitVersion.Git.IGitObject.Sha.get -> string! GitVersion.Git.IGitRepository GitVersion.Git.IGitRepository.Branches.get -> GitVersion.Git.IBranchCollection! GitVersion.Git.IGitRepository.Commits.get -> GitVersion.Git.ICommitCollection! diff --git a/src/GitVersion.Core/PublicAPI.Unshipped.txt b/src/GitVersion.Core/PublicAPI.Unshipped.txt index 7dc5c58110..19cd7e2e08 100644 --- a/src/GitVersion.Core/PublicAPI.Unshipped.txt +++ b/src/GitVersion.Core/PublicAPI.Unshipped.txt @@ -1 +1,4 @@ #nullable enable +GitVersion.Common.IRepositoryStore.GetCommitsReacheableFrom(GitVersion.Git.ICommit! commit, GitVersion.Git.IBranch! branch) -> System.Collections.Generic.IReadOnlyList! +GitVersion.Git.ICommit.Id.get -> GitVersion.Git.IObjectId! +GitVersion.Git.ICommit.Sha.get -> string! diff --git a/src/GitVersion.LibGit2Sharp/Git/Commit.cs b/src/GitVersion.LibGit2Sharp/Git/Commit.cs index 6e7ca2c6d6..d85fc32a1c 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Commit.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Commit.cs @@ -4,7 +4,7 @@ namespace GitVersion.Git; -internal sealed class Commit : GitObject, ICommit +internal sealed class Commit : ICommit { private static readonly ConcurrentDictionary> pathsCache = new(); private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Id); @@ -14,12 +14,14 @@ internal sealed class Commit : GitObject, ICommit private readonly LibGit2Sharp.Commit innerCommit; private readonly LibGit2Sharp.Diff repoDiff; - internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, GitRepository repo) : base(innerCommit) + internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, GitRepository repo) { repoDiff.NotNull(); repo.NotNull(); this.innerCommit = innerCommit.NotNull(); this.parentsLazy = new(() => innerCommit.Parents.Select(parent => repo.GetOrWrap(parent, repoDiff)).ToList()); + Id = new ObjectId(innerCommit.Id); + Sha = innerCommit.Sha; When = innerCommit.Committer.When; this.repoDiff = repoDiff; } @@ -27,6 +29,8 @@ internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, Git public int CompareTo(ICommit? other) => comparerHelper.Compare(this, other); public bool Equals(ICommit? other) => equalityHelper.Equals(this, other); public IReadOnlyList Parents => this.parentsLazy.Value; + public IObjectId Id { get; } + public string Sha { get; } public DateTimeOffset When { get; } public string Message => this.innerCommit.Message; public IReadOnlyList DiffPaths diff --git a/src/GitVersion.LibGit2Sharp/Git/GitObject.cs b/src/GitVersion.LibGit2Sharp/Git/GitObject.cs deleted file mode 100644 index 850168393c..0000000000 --- a/src/GitVersion.LibGit2Sharp/Git/GitObject.cs +++ /dev/null @@ -1,26 +0,0 @@ -using GitVersion.Extensions; -using GitVersion.Helpers; - -namespace GitVersion.Git; - -internal class GitObject : IGitObject -{ - private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Id); - private static readonly LambdaKeyComparer comparerHelper = new(x => x.Sha); - - internal GitObject(LibGit2Sharp.GitObject innerGitObject) - { - innerGitObject = innerGitObject.NotNull(); - Id = new ObjectId(innerGitObject.Id); - Sha = innerGitObject.Sha; - } - - public int CompareTo(IGitObject? other) => comparerHelper.Compare(this, other); - public bool Equals(IGitObject? other) => equalityHelper.Equals(this, other); - public override bool Equals(object? obj) => Equals(obj as IGitObject); - public override int GetHashCode() => equalityHelper.GetHashCode(this); - public override string ToString() => Id.ToString(7); - - public IObjectId Id { get; } - public string Sha { get; } -} From e88c5816d19fad393d6cae764ffe05a8a1fdbc4f Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Thu, 9 Oct 2025 15:15:51 +0200 Subject: [PATCH 05/11] restructures libgit2sharp project refactors the project to improve code organization and maintainability. --- .../GitVersion.Core.Libgit2Sharp.csproj | 56 ++----------------- 1 file changed, 4 insertions(+), 52 deletions(-) diff --git a/new-cli/GitVersion.Core.Libgit2Sharp/GitVersion.Core.Libgit2Sharp.csproj b/new-cli/GitVersion.Core.Libgit2Sharp/GitVersion.Core.Libgit2Sharp.csproj index 4e8f95ddc5..a42d02992f 100644 --- a/new-cli/GitVersion.Core.Libgit2Sharp/GitVersion.Core.Libgit2Sharp.csproj +++ b/new-cli/GitVersion.Core.Libgit2Sharp/GitVersion.Core.Libgit2Sharp.csproj @@ -5,61 +5,13 @@ - + - - Git\Branch.cs - - - Git\BranchCollection.cs - - - Git\Commit.cs - - - Git\CommitCollection.cs - - - Git\GitObject.cs - - - Git\GitRepository.cs - - - Git\ObjectId.cs - - - Git\Reference.cs - - - Git\ReferenceCollection.cs - - - Git\RefSpec.cs - - - Git\RefSpecCollection.cs - - - Git\Remote.cs - - - Git\RemoteCollection.cs - - - Git\RepositoryExtensions.cs - - - Git\Tag.cs - - - Git\TagCollection.cs - - - Git\TreeChanges.cs - + + + From 482ef9ca245fd373a633803a357b7225e2792bc3 Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Sun, 12 Oct 2025 15:51:36 +0200 Subject: [PATCH 06/11] Convert git wrapper classes to readonly structs Replaces several internal classes with readonly structs to improve performance by avoiding unnecessary heap allocations. Updates related code to ensure compatibility with the new struct-based implementation. --- src/GitVersion.LibGit2Sharp/Git/Branch.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/Commit.cs | 4 +- src/GitVersion.LibGit2Sharp/Git/ObjectId.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/RefSpec.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/Reference.cs | 47 +++++++++---------- .../Git/ReferenceCollection.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/Remote.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/Tag.cs | 6 +-- .../Git/TreeChanges.cs | 2 +- 9 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/GitVersion.LibGit2Sharp/Git/Branch.cs b/src/GitVersion.LibGit2Sharp/Git/Branch.cs index 7cd5cce70e..a2191e851f 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Branch.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Branch.cs @@ -3,7 +3,7 @@ namespace GitVersion.Git; -internal sealed class Branch : IBranch +internal readonly struct Branch : IBranch { private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Name.Canonical); private static readonly LambdaKeyComparer comparerHelper = new(x => x.Name.Canonical); diff --git a/src/GitVersion.LibGit2Sharp/Git/Commit.cs b/src/GitVersion.LibGit2Sharp/Git/Commit.cs index d85fc32a1c..77c44ca87f 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Commit.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Commit.cs @@ -4,7 +4,7 @@ namespace GitVersion.Git; -internal sealed class Commit : ICommit +internal readonly struct Commit : ICommit { private static readonly ConcurrentDictionary> pathsCache = new(); private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Id); @@ -19,7 +19,7 @@ internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, Git repoDiff.NotNull(); repo.NotNull(); this.innerCommit = innerCommit.NotNull(); - this.parentsLazy = new(() => innerCommit.Parents.Select(parent => repo.GetOrWrap(parent, repoDiff)).ToList()); + this.parentsLazy = new(() => [.. innerCommit.Parents.Select(parent => repo.GetOrWrap(parent, repoDiff))]); Id = new ObjectId(innerCommit.Id); Sha = innerCommit.Sha; When = innerCommit.Committer.When; diff --git a/src/GitVersion.LibGit2Sharp/Git/ObjectId.cs b/src/GitVersion.LibGit2Sharp/Git/ObjectId.cs index 0d924f59af..171614b3bd 100644 --- a/src/GitVersion.LibGit2Sharp/Git/ObjectId.cs +++ b/src/GitVersion.LibGit2Sharp/Git/ObjectId.cs @@ -3,7 +3,7 @@ namespace GitVersion.Git; -internal sealed class ObjectId : IObjectId +internal readonly struct ObjectId : IObjectId { private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Sha); private static readonly LambdaKeyComparer comparerHelper = new(x => x.Sha); diff --git a/src/GitVersion.LibGit2Sharp/Git/RefSpec.cs b/src/GitVersion.LibGit2Sharp/Git/RefSpec.cs index b32e388e90..dc3880bdd5 100644 --- a/src/GitVersion.LibGit2Sharp/Git/RefSpec.cs +++ b/src/GitVersion.LibGit2Sharp/Git/RefSpec.cs @@ -3,7 +3,7 @@ namespace GitVersion.Git; -internal class RefSpec : IRefSpec +internal readonly struct RefSpec : IRefSpec { private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Specification); private static readonly LambdaKeyComparer comparerHelper = new(x => x.Specification); diff --git a/src/GitVersion.LibGit2Sharp/Git/Reference.cs b/src/GitVersion.LibGit2Sharp/Git/Reference.cs index 7afe5a3919..104e21cb1a 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Reference.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Reference.cs @@ -2,32 +2,31 @@ using GitVersion.Helpers; using LibGit2Sharp; -namespace GitVersion.Git -{ - internal sealed class Reference : IReference - { - private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Name.Canonical); - private static readonly LambdaKeyComparer comparerHelper = new(x => x.Name.Canonical); - internal readonly LibGit2Sharp.Reference innerReference; +namespace GitVersion.Git; - internal Reference(LibGit2Sharp.Reference reference) - { - this.innerReference = reference.NotNull(); - Name = new ReferenceName(reference.CanonicalName); +internal readonly struct Reference : IReference +{ + private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Name.Canonical); + private static readonly LambdaKeyComparer comparerHelper = new(x => x.Name.Canonical); + internal readonly LibGit2Sharp.Reference innerReference; - if (reference is DirectReference) - ReferenceTargetId = new ObjectId(reference.TargetIdentifier); - } + internal Reference(LibGit2Sharp.Reference reference) + { + this.innerReference = reference.NotNull(); + Name = new ReferenceName(reference.CanonicalName); - public ReferenceName Name { get; } - public IObjectId? ReferenceTargetId { get; } - public int CompareTo(IReference? other) => comparerHelper.Compare(this, other); - public override bool Equals(object? obj) => Equals(obj as IReference); - public bool Equals(IReference? other) => equalityHelper.Equals(this, other); - public override int GetHashCode() => equalityHelper.GetHashCode(this); - public override string ToString() => Name.ToString(); - public string TargetIdentifier => this.innerReference.TargetIdentifier; - public static implicit operator LibGit2Sharp.Reference(Reference d) - => d.NotNull().innerReference; + if (reference is DirectReference) + ReferenceTargetId = new ObjectId(reference.TargetIdentifier); } + + public ReferenceName Name { get; } + public IObjectId? ReferenceTargetId { get; } + public int CompareTo(IReference? other) => comparerHelper.Compare(this, other); + public override bool Equals(object? obj) => Equals(obj as IReference); + public bool Equals(IReference? other) => equalityHelper.Equals(this, other); + public override int GetHashCode() => equalityHelper.GetHashCode(this); + public override string ToString() => Name.ToString(); + public string TargetIdentifier => this.innerReference.TargetIdentifier; + public static implicit operator LibGit2Sharp.Reference(Reference d) + => d.innerReference; } diff --git a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs index d7031033ce..022577d718 100644 --- a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs @@ -38,5 +38,5 @@ public IReference? this[string name] public IReference? Head => this["HEAD"]; - public IEnumerable FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => new Reference(reference)); + public IEnumerable FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => (IReference)new Reference(reference)); } diff --git a/src/GitVersion.LibGit2Sharp/Git/Remote.cs b/src/GitVersion.LibGit2Sharp/Git/Remote.cs index 9b409824e7..5d130ef338 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Remote.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Remote.cs @@ -3,7 +3,7 @@ namespace GitVersion.Git; -internal sealed class Remote : IRemote +internal readonly struct Remote : IRemote { private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Name); private static readonly LambdaKeyComparer comparerHelper = new(x => x.Name); diff --git a/src/GitVersion.LibGit2Sharp/Git/Tag.cs b/src/GitVersion.LibGit2Sharp/Git/Tag.cs index 0fe66a2c7d..58cf55f720 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Tag.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Tag.cs @@ -4,7 +4,7 @@ namespace GitVersion.Git; -internal sealed class Tag : ITag +internal readonly struct Tag : ITag { private static readonly LambdaEqualityHelper equalityHelper = new(x => x.Name.Canonical); private static readonly LambdaKeyComparer comparerHelper = new(x => x.Name.Canonical); @@ -16,9 +16,9 @@ internal sealed class Tag : ITag internal Tag(LibGit2Sharp.Tag tag, Diff diff, GitRepository repo) { this.innerTag = tag.NotNull(); - this.commitLazy = new(PeeledTargetCommit); this.diff = diff.NotNull(); this.repo = repo.NotNull(); + this.commitLazy = new(PeeledTargetCommit); Name = new(this.innerTag.CanonicalName); } @@ -28,7 +28,7 @@ internal Tag(LibGit2Sharp.Tag tag, Diff diff, GitRepository repo) public string TargetSha => this.innerTag.Target.Sha; public ICommit Commit => this.commitLazy.Value.NotNull(); - private Commit? PeeledTargetCommit() + private ICommit? PeeledTargetCommit() { var target = this.innerTag.Target; diff --git a/src/GitVersion.LibGit2Sharp/Git/TreeChanges.cs b/src/GitVersion.LibGit2Sharp/Git/TreeChanges.cs index 9ee9385a7e..d0e2714a5e 100644 --- a/src/GitVersion.LibGit2Sharp/Git/TreeChanges.cs +++ b/src/GitVersion.LibGit2Sharp/Git/TreeChanges.cs @@ -1,6 +1,6 @@ namespace GitVersion.Git; -internal sealed class TreeChanges(LibGit2Sharp.TreeChanges innerTreeChanges) : ITreeChanges +internal readonly struct TreeChanges(LibGit2Sharp.TreeChanges innerTreeChanges) : ITreeChanges { private readonly LibGit2Sharp.TreeChanges innerTreeChanges = innerTreeChanges ?? throw new ArgumentNullException(nameof(innerTreeChanges)); From 314d06c531fd9dc2461f15453495ab051cfc8666 Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Sun, 12 Oct 2025 15:57:08 +0200 Subject: [PATCH 07/11] organizes solution structure moves schema and testing projects to misc folder. --- src/GitVersion.slnx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/GitVersion.slnx b/src/GitVersion.slnx index b8001cb1b0..11c8908bea 100644 --- a/src/GitVersion.slnx +++ b/src/GitVersion.slnx @@ -1,4 +1,8 @@ + + + + @@ -34,6 +38,4 @@ - - \ No newline at end of file From 42794f97c0a2a0edc4f92daa1c27e579991669eb Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Mon, 13 Oct 2025 12:41:12 +0200 Subject: [PATCH 08/11] caches git objects Uses lazy initialization to cache references and remotes. --- .../Git/GitRepository.cs | 34 +++++++++++-------- .../Git/RefSpecCollection.cs | 6 ++-- .../Git/ReferenceCollection.cs | 26 +++++++------- src/GitVersion.LibGit2Sharp/Git/Remote.cs | 11 ++++-- .../Git/RemoteCollection.cs | 26 ++++++-------- 5 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 49280c0726..a479725418 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -12,6 +12,9 @@ internal sealed partial class GitRepository private readonly ConcurrentDictionary cachedBranches = new(); private readonly ConcurrentDictionary cachedCommits = new(); private readonly ConcurrentDictionary cachedTags = new(); + private readonly ConcurrentDictionary cachedRemotes = new(); + private readonly ConcurrentDictionary cachedReferences = new(); + private readonly ConcurrentDictionary cachedRefSpecs = new(); private IRepository RepositoryInstance { @@ -28,10 +31,10 @@ private IRepository RepositoryInstance public IBranch Head => GetOrWrap(RepositoryInstance.Head, RepositoryInstance.Diff); public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this); - public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs); + public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs, this); public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff, this); public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff, this); - public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes); + public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes, this); public void DiscoverRepository(string? gitDirectory) { @@ -65,23 +68,26 @@ public int UncommittedChangesCount() public Branch GetOrWrap(LibGit2Sharp.Branch innerBranch, Diff repoDiff) { - if (innerBranch.Tip is null) - { - return new Branch(innerBranch, repoDiff, this); - } - - var cacheKey = $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}@{innerBranch.Tip.Sha}"; + var cacheKey = innerBranch.Tip is null + ? $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}" + : $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}@{innerBranch.Tip.Sha}"; return cachedBranches.GetOrAdd(cacheKey, _ => new Branch(innerBranch, repoDiff, this)); } - public Commit GetOrWrap(LibGit2Sharp.Commit innerCommit, Diff repoDiff) => - cachedCommits.GetOrAdd(innerCommit.Sha, _ => new Commit(innerCommit, repoDiff, this)); + public Commit GetOrWrap(LibGit2Sharp.Commit innerCommit, Diff repoDiff) + => cachedCommits.GetOrAdd(innerCommit.Sha, _ => new Commit(innerCommit, repoDiff, this)); public Tag GetOrWrap(LibGit2Sharp.Tag innerTag, Diff repoDiff) - { - var cacheKey = $"{innerTag.CanonicalName}@{innerTag.Target.Sha}"; - return cachedTags.GetOrAdd(cacheKey, _ => new Tag(innerTag, repoDiff, this)); - } + => cachedTags.GetOrAdd(innerTag.CanonicalName, _ => new Tag(innerTag, repoDiff, this)); + + public Remote GetOrWrap(LibGit2Sharp.Remote innerRemote) + => cachedRemotes.GetOrAdd(innerRemote.Name, _ => new Remote(innerRemote, this)); + + public Reference GetOrWrap(LibGit2Sharp.Reference innerReference) + => cachedReferences.GetOrAdd(innerReference.CanonicalName, _ => new Reference(innerReference)); + + public RefSpec GetOrWrap(LibGit2Sharp.RefSpec innerRefSpec) + => cachedRefSpecs.GetOrAdd(innerRefSpec.Specification, _ => new RefSpec(innerRefSpec)); public void Dispose() { diff --git a/src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs b/src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs index ed67a52333..bc69fa541d 100644 --- a/src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs @@ -6,10 +6,10 @@ internal sealed class RefSpecCollection : IRefSpecCollection { private readonly Lazy> refSpecs; - internal RefSpecCollection(LibGit2Sharp.RefSpecCollection collection) + internal RefSpecCollection(LibGit2Sharp.RefSpecCollection innerCollection, GitRepository repo) { - collection = collection.NotNull(); - this.refSpecs = new Lazy>(() => [.. collection.Select(tag => new RefSpec(tag))]); + innerCollection = innerCollection.NotNull(); + this.refSpecs = new Lazy>(() => [.. innerCollection.Select(repo.GetOrWrap)]); } public IEnumerator GetEnumerator() => this.refSpecs.Value.GetEnumerator(); diff --git a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs index 022577d718..deaed6adad 100644 --- a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs @@ -5,32 +5,32 @@ namespace GitVersion.Git; internal sealed class ReferenceCollection : IReferenceCollection { private readonly LibGit2Sharp.ReferenceCollection innerCollection; - private IReadOnlyCollection? references; + private readonly GitRepository repo; + private readonly Lazy> references; - internal ReferenceCollection(LibGit2Sharp.ReferenceCollection collection) => this.innerCollection = collection.NotNull(); - - public IEnumerator GetEnumerator() + internal ReferenceCollection(LibGit2Sharp.ReferenceCollection collection, GitRepository repo) { - this.references ??= [.. this.innerCollection.Select(reference => new Reference(reference))]; - return this.references.GetEnumerator(); + this.innerCollection = collection.NotNull(); + this.repo = repo.NotNull(); + this.references = new Lazy>(() => [.. this.innerCollection.Select(repo.GetOrWrap)]); } + public IEnumerator GetEnumerator() => this.references.Value.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public void Add(string name, string canonicalRefNameOrObject, bool allowOverwrite = false) => this.innerCollection.Add(name, canonicalRefNameOrObject, allowOverwrite); + public void Add(string name, string canonicalRefNameOrObject, bool allowOverwrite = false) + => this.innerCollection.Add(name, canonicalRefNameOrObject, allowOverwrite); public void UpdateTarget(IReference directRef, IObjectId targetId) - { - RepositoryExtensions.RunSafe(() => this.innerCollection.UpdateTarget((Reference)directRef, (ObjectId)targetId)); - this.references = null; - } + => RepositoryExtensions.RunSafe(() => this.innerCollection.UpdateTarget((Reference)directRef, (ObjectId)targetId)); public IReference? this[string name] { get { var reference = this.innerCollection[name]; - return reference is null ? null : new Reference(reference); + return reference is null ? null : this.repo.GetOrWrap(reference); } } @@ -38,5 +38,5 @@ public IReference? this[string name] public IReference? Head => this["HEAD"]; - public IEnumerable FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => (IReference)new Reference(reference)); + public IEnumerable FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => (IReference)this.repo.GetOrWrap(reference)); } diff --git a/src/GitVersion.LibGit2Sharp/Git/Remote.cs b/src/GitVersion.LibGit2Sharp/Git/Remote.cs index 5d130ef338..5a976e90f2 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Remote.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Remote.cs @@ -9,22 +9,27 @@ namespace GitVersion.Git; private static readonly LambdaKeyComparer comparerHelper = new(x => x.Name); private readonly LibGit2Sharp.Remote innerRemote; + private readonly GitRepository repo; - internal Remote(LibGit2Sharp.Remote remote) => this.innerRemote = remote.NotNull(); + internal Remote(LibGit2Sharp.Remote remote, GitRepository repo) + { + this.innerRemote = remote.NotNull(); + this.repo = repo.NotNull(); + } public int CompareTo(IRemote? other) => comparerHelper.Compare(this, other); public bool Equals(IRemote? other) => equalityHelper.Equals(this, other); public string Name => this.innerRemote.Name; public string Url => this.innerRemote.Url; - public IEnumerable RefSpecs + private IEnumerable RefSpecs { get { var refSpecs = this.innerRemote.RefSpecs; return refSpecs is null ? [] - : new RefSpecCollection((LibGit2Sharp.RefSpecCollection)refSpecs); + : new RefSpecCollection((LibGit2Sharp.RefSpecCollection)refSpecs, repo); } } diff --git a/src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs b/src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs index b328c56875..0b782edec6 100644 --- a/src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs @@ -5,16 +5,18 @@ namespace GitVersion.Git; internal sealed class RemoteCollection : IRemoteCollection { private readonly LibGit2Sharp.RemoteCollection innerCollection; - private IReadOnlyCollection? remotes; + private readonly GitRepository repo; + private readonly Lazy> remotes; - internal RemoteCollection(LibGit2Sharp.RemoteCollection collection) => this.innerCollection = collection.NotNull(); - - public IEnumerator GetEnumerator() + internal RemoteCollection(LibGit2Sharp.RemoteCollection collection, GitRepository repo) { - this.remotes ??= [.. this.innerCollection.Select(reference => new Remote(reference))]; - return this.remotes.GetEnumerator(); + this.innerCollection = collection.NotNull(); + this.repo = repo.NotNull(); + this.remotes = new Lazy>(() => [.. this.innerCollection.Select(repo.GetOrWrap)]); } + public IEnumerator GetEnumerator() => this.remotes.Value.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public IRemote? this[string name] @@ -22,19 +24,13 @@ public IRemote? this[string name] get { var remote = this.innerCollection[name]; - return remote is null ? null : new Remote(remote); + return remote is null ? null : this.repo.GetOrWrap(remote); } } public void Remove(string remoteName) - { - this.innerCollection.Remove(remoteName); - this.remotes = null; - } + => this.innerCollection.Remove(remoteName); public void Update(string remoteName, string refSpec) - { - this.innerCollection.Update(remoteName, r => r.FetchRefSpecs.Add(refSpec)); - this.remotes = null; - } + => this.innerCollection.Update(remoteName, r => r.FetchRefSpecs.Add(refSpec)); } From 06225f852f3ea24cc5b220984e0fd17ce7aab4e7 Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Tue, 14 Oct 2025 07:48:18 +0200 Subject: [PATCH 09/11] migrates caching to dedicated class Introduces `GitRepositoryCache` to centralize caching logic for branches, commits, tags, references, and remotes. Updates related classes to use `GitRepositoryCache` instead of directly maintaining individual cache dictionaries. --- src/GitVersion.LibGit2Sharp/Git/Branch.cs | 8 ++-- .../Git/BranchCollection.cs | 10 ++-- src/GitVersion.LibGit2Sharp/Git/Commit.cs | 6 +-- .../Git/CommitCollection.cs | 10 ++-- .../Git/GitRepository.cs | 46 ++++--------------- .../Git/GitRepositoryCache.cs | 37 +++++++++++++++ .../Git/RefSpecCollection.cs | 4 +- .../Git/ReferenceCollection.cs | 12 ++--- src/GitVersion.LibGit2Sharp/Git/Remote.cs | 8 ++-- .../Git/RemoteCollection.cs | 10 ++-- src/GitVersion.LibGit2Sharp/Git/Tag.cs | 8 ++-- .../Git/TagCollection.cs | 6 +-- 12 files changed, 86 insertions(+), 79 deletions(-) create mode 100644 src/GitVersion.LibGit2Sharp/Git/GitRepositoryCache.cs diff --git a/src/GitVersion.LibGit2Sharp/Git/Branch.cs b/src/GitVersion.LibGit2Sharp/Git/Branch.cs index a2191e851f..e4342e7e66 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Branch.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Branch.cs @@ -10,18 +10,18 @@ namespace GitVersion.Git; private readonly LibGit2Sharp.Branch innerBranch; - internal Branch(LibGit2Sharp.Branch branch, LibGit2Sharp.Diff diff, GitRepository repo) + internal Branch(LibGit2Sharp.Branch branch, LibGit2Sharp.Diff diff, GitRepositoryCache repositoryCache) { diff.NotNull(); - repo.NotNull(); + repositoryCache.NotNull(); this.innerBranch = branch.NotNull(); Name = new(branch.CanonicalName); var commit = this.innerBranch.Tip; - Tip = commit is null ? null : repo.GetOrWrap(commit, diff); + Tip = commit is null ? null : repositoryCache.GetOrWrap(commit, diff); var commits = this.innerBranch.Commits; - Commits = new CommitCollection(commits, diff, repo); + Commits = new CommitCollection(commits, diff, repositoryCache); } public ReferenceName Name { get; } diff --git a/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs b/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs index 8fd144815d..5154ee5191 100644 --- a/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs @@ -8,14 +8,14 @@ internal sealed class BranchCollection : IBranchCollection private readonly LibGit2Sharp.BranchCollection innerCollection; private readonly Lazy> branches; private readonly Diff diff; - private readonly GitRepository repo; + private readonly GitRepositoryCache repositoryCache; - internal BranchCollection(LibGit2Sharp.BranchCollection collection, Diff diff, GitRepository repo) + internal BranchCollection(LibGit2Sharp.BranchCollection collection, Diff diff, GitRepositoryCache repositoryCache) { this.innerCollection = collection.NotNull(); - this.branches = new Lazy>(() => [.. this.innerCollection.Select(branch => repo.GetOrWrap(branch, diff))]); + this.branches = new Lazy>(() => [.. this.innerCollection.Select(branch => repositoryCache.GetOrWrap(branch, diff))]); this.diff = diff.NotNull(); - this.repo = repo.NotNull(); + this.repositoryCache = repositoryCache.NotNull(); } public IEnumerator GetEnumerator() => this.branches.Value.GetEnumerator(); @@ -28,7 +28,7 @@ public IBranch? this[string name] { name = name.NotNull(); var branch = this.innerCollection[name]; - return branch is null ? null : this.repo.GetOrWrap(branch, this.diff); + return branch is null ? null : this.repositoryCache.GetOrWrap(branch, this.diff); } } diff --git a/src/GitVersion.LibGit2Sharp/Git/Commit.cs b/src/GitVersion.LibGit2Sharp/Git/Commit.cs index 77c44ca87f..91ecd861a7 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Commit.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Commit.cs @@ -14,12 +14,12 @@ namespace GitVersion.Git; private readonly LibGit2Sharp.Commit innerCommit; private readonly LibGit2Sharp.Diff repoDiff; - internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, GitRepository repo) + internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, GitRepositoryCache repositoryCache) { repoDiff.NotNull(); - repo.NotNull(); + repositoryCache.NotNull(); this.innerCommit = innerCommit.NotNull(); - this.parentsLazy = new(() => [.. innerCommit.Parents.Select(parent => repo.GetOrWrap(parent, repoDiff))]); + this.parentsLazy = new(() => [.. innerCommit.Parents.Select(parent => repositoryCache.GetOrWrap(parent, repoDiff))]); Id = new ObjectId(innerCommit.Id); Sha = innerCommit.Sha; When = innerCommit.Committer.When; diff --git a/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs b/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs index 4c4607815e..2ab085e8c7 100644 --- a/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs @@ -8,14 +8,14 @@ internal sealed class CommitCollection : ICommitCollection private readonly ICommitLog innerCollection; private readonly Lazy> commits; private readonly Diff diff; - private readonly GitRepository repo; + private readonly GitRepositoryCache repositoryCache; - internal CommitCollection(ICommitLog collection, Diff diff, GitRepository repo) + internal CommitCollection(ICommitLog collection, Diff diff, GitRepositoryCache repositoryCache) { this.innerCollection = collection.NotNull(); - this.commits = new Lazy>(() => [.. this.innerCollection.Select(commit => repo.GetOrWrap(commit, diff))]); + this.commits = new Lazy>(() => [.. this.innerCollection.Select(commit => repositoryCache.GetOrWrap(commit, diff))]); this.diff = diff.NotNull(); - this.repo = repo.NotNull(); + this.repositoryCache = repositoryCache.NotNull(); } public IEnumerator GetEnumerator() => this.commits.Value.GetEnumerator(); @@ -37,7 +37,7 @@ public IEnumerable QueryBy(CommitFilter commitFilter) SortBy = (LibGit2Sharp.CommitSortStrategies)commitFilter.SortBy }; var commitLog = ((IQueryableCommitLog)this.innerCollection).QueryBy(filter); - return new CommitCollection(commitLog, this.diff, this.repo); + return new CommitCollection(commitLog, this.diff, this.repositoryCache); static object? GetReacheableFrom(object? item) => item switch diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index a479725418..ffc042c8b1 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -1,4 +1,3 @@ -using System.Collections.Concurrent; using GitVersion.Extensions; using GitVersion.Helpers; using LibGit2Sharp; @@ -8,13 +7,7 @@ namespace GitVersion.Git; internal sealed partial class GitRepository { private Lazy? repositoryLazy; - - private readonly ConcurrentDictionary cachedBranches = new(); - private readonly ConcurrentDictionary cachedCommits = new(); - private readonly ConcurrentDictionary cachedTags = new(); - private readonly ConcurrentDictionary cachedRemotes = new(); - private readonly ConcurrentDictionary cachedReferences = new(); - private readonly ConcurrentDictionary cachedRefSpecs = new(); + private readonly GitRepositoryCache repositoryCache = new(); private IRepository RepositoryInstance { @@ -28,13 +21,13 @@ private IRepository RepositoryInstance public string WorkingDirectory => RepositoryInstance.Info.WorkingDirectory; public bool IsHeadDetached => RepositoryInstance.Info.IsHeadDetached; public bool IsShallow => RepositoryInstance.Info.IsShallow; - public IBranch Head => GetOrWrap(RepositoryInstance.Head, RepositoryInstance.Diff); + public IBranch Head => this.repositoryCache.GetOrWrap(RepositoryInstance.Head, RepositoryInstance.Diff); - public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this); - public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs, this); - public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff, this); - public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff, this); - public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes, this); + public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this.repositoryCache); + public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs, this.repositoryCache); + public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff, this.repositoryCache); + public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff, this.repositoryCache); + public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes, this.repositoryCache); public void DiscoverRepository(string? gitDirectory) { @@ -56,7 +49,7 @@ public void DiscoverRepository(string? gitDirectory) var first = (Commit)commit; var second = (Commit)otherCommit; var mergeBase = RepositoryInstance.ObjectDatabase.FindMergeBase(first, second); - return mergeBase == null ? null : GetOrWrap(mergeBase, RepositoryInstance.Diff); + return mergeBase == null ? null : this.repositoryCache.GetOrWrap(mergeBase, RepositoryInstance.Diff); }); } @@ -66,29 +59,6 @@ public int UncommittedChangesCount() return retryAction.Execute(GetUncommittedChangesCountInternal); } - public Branch GetOrWrap(LibGit2Sharp.Branch innerBranch, Diff repoDiff) - { - var cacheKey = innerBranch.Tip is null - ? $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}" - : $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}@{innerBranch.Tip.Sha}"; - return cachedBranches.GetOrAdd(cacheKey, _ => new Branch(innerBranch, repoDiff, this)); - } - - public Commit GetOrWrap(LibGit2Sharp.Commit innerCommit, Diff repoDiff) - => cachedCommits.GetOrAdd(innerCommit.Sha, _ => new Commit(innerCommit, repoDiff, this)); - - public Tag GetOrWrap(LibGit2Sharp.Tag innerTag, Diff repoDiff) - => cachedTags.GetOrAdd(innerTag.CanonicalName, _ => new Tag(innerTag, repoDiff, this)); - - public Remote GetOrWrap(LibGit2Sharp.Remote innerRemote) - => cachedRemotes.GetOrAdd(innerRemote.Name, _ => new Remote(innerRemote, this)); - - public Reference GetOrWrap(LibGit2Sharp.Reference innerReference) - => cachedReferences.GetOrAdd(innerReference.CanonicalName, _ => new Reference(innerReference)); - - public RefSpec GetOrWrap(LibGit2Sharp.RefSpec innerRefSpec) - => cachedRefSpecs.GetOrAdd(innerRefSpec.Specification, _ => new RefSpec(innerRefSpec)); - public void Dispose() { if (this.repositoryLazy is { IsValueCreated: true }) RepositoryInstance.Dispose(); diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepositoryCache.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepositoryCache.cs new file mode 100644 index 0000000000..8d8eba006b --- /dev/null +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepositoryCache.cs @@ -0,0 +1,37 @@ +using System.Collections.Concurrent; +using LibGit2Sharp; + +namespace GitVersion.Git; + +internal class GitRepositoryCache +{ + private readonly ConcurrentDictionary cachedBranches = new(); + private readonly ConcurrentDictionary cachedCommits = new(); + private readonly ConcurrentDictionary cachedTags = new(); + private readonly ConcurrentDictionary cachedRemotes = new(); + private readonly ConcurrentDictionary cachedReferences = new(); + private readonly ConcurrentDictionary cachedRefSpecs = new(); + + public Branch GetOrWrap(LibGit2Sharp.Branch innerBranch, Diff repoDiff) + { + var cacheKey = innerBranch.Tip is null + ? $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}" + : $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}@{innerBranch.Tip.Sha}"; + return cachedBranches.GetOrAdd(cacheKey, _ => new Branch(innerBranch, repoDiff, this)); + } + + public Commit GetOrWrap(LibGit2Sharp.Commit innerCommit, Diff repoDiff) + => cachedCommits.GetOrAdd(innerCommit.Sha, _ => new Commit(innerCommit, repoDiff, this)); + + public Tag GetOrWrap(LibGit2Sharp.Tag innerTag, Diff repoDiff) + => cachedTags.GetOrAdd(innerTag.CanonicalName, _ => new Tag(innerTag, repoDiff, this)); + + public Remote GetOrWrap(LibGit2Sharp.Remote innerRemote) + => cachedRemotes.GetOrAdd(innerRemote.Name, _ => new Remote(innerRemote, this)); + + public Reference GetOrWrap(LibGit2Sharp.Reference innerReference) + => cachedReferences.GetOrAdd(innerReference.CanonicalName, _ => new Reference(innerReference)); + + public RefSpec GetOrWrap(LibGit2Sharp.RefSpec innerRefSpec) + => cachedRefSpecs.GetOrAdd(innerRefSpec.Specification, _ => new RefSpec(innerRefSpec)); +} diff --git a/src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs b/src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs index bc69fa541d..3ae987a10f 100644 --- a/src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs @@ -6,10 +6,10 @@ internal sealed class RefSpecCollection : IRefSpecCollection { private readonly Lazy> refSpecs; - internal RefSpecCollection(LibGit2Sharp.RefSpecCollection innerCollection, GitRepository repo) + internal RefSpecCollection(LibGit2Sharp.RefSpecCollection innerCollection, GitRepositoryCache repositoryCache) { innerCollection = innerCollection.NotNull(); - this.refSpecs = new Lazy>(() => [.. innerCollection.Select(repo.GetOrWrap)]); + this.refSpecs = new Lazy>(() => [.. innerCollection.Select(repositoryCache.GetOrWrap)]); } public IEnumerator GetEnumerator() => this.refSpecs.Value.GetEnumerator(); diff --git a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs index deaed6adad..69fc164d77 100644 --- a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs @@ -5,14 +5,14 @@ namespace GitVersion.Git; internal sealed class ReferenceCollection : IReferenceCollection { private readonly LibGit2Sharp.ReferenceCollection innerCollection; - private readonly GitRepository repo; + private readonly GitRepositoryCache repositoryCache; private readonly Lazy> references; - internal ReferenceCollection(LibGit2Sharp.ReferenceCollection collection, GitRepository repo) + internal ReferenceCollection(LibGit2Sharp.ReferenceCollection collection, GitRepositoryCache repositoryCache) { this.innerCollection = collection.NotNull(); - this.repo = repo.NotNull(); - this.references = new Lazy>(() => [.. this.innerCollection.Select(repo.GetOrWrap)]); + this.repositoryCache = repositoryCache.NotNull(); + this.references = new Lazy>(() => [.. this.innerCollection.Select(repositoryCache.GetOrWrap)]); } public IEnumerator GetEnumerator() => this.references.Value.GetEnumerator(); @@ -30,7 +30,7 @@ public IReference? this[string name] get { var reference = this.innerCollection[name]; - return reference is null ? null : this.repo.GetOrWrap(reference); + return reference is null ? null : this.repositoryCache.GetOrWrap(reference); } } @@ -38,5 +38,5 @@ public IReference? this[string name] public IReference? Head => this["HEAD"]; - public IEnumerable FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => (IReference)this.repo.GetOrWrap(reference)); + public IEnumerable FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => (IReference)this.repositoryCache.GetOrWrap(reference)); } diff --git a/src/GitVersion.LibGit2Sharp/Git/Remote.cs b/src/GitVersion.LibGit2Sharp/Git/Remote.cs index 5a976e90f2..33bce70838 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Remote.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Remote.cs @@ -9,12 +9,12 @@ namespace GitVersion.Git; private static readonly LambdaKeyComparer comparerHelper = new(x => x.Name); private readonly LibGit2Sharp.Remote innerRemote; - private readonly GitRepository repo; + private readonly GitRepositoryCache repositoryCache; - internal Remote(LibGit2Sharp.Remote remote, GitRepository repo) + internal Remote(LibGit2Sharp.Remote remote, GitRepositoryCache repositoryCache) { this.innerRemote = remote.NotNull(); - this.repo = repo.NotNull(); + this.repositoryCache = repositoryCache.NotNull(); } public int CompareTo(IRemote? other) => comparerHelper.Compare(this, other); @@ -29,7 +29,7 @@ private IEnumerable RefSpecs var refSpecs = this.innerRemote.RefSpecs; return refSpecs is null ? [] - : new RefSpecCollection((LibGit2Sharp.RefSpecCollection)refSpecs, repo); + : new RefSpecCollection((LibGit2Sharp.RefSpecCollection)refSpecs, this.repositoryCache); } } diff --git a/src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs b/src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs index 0b782edec6..f46e7e386f 100644 --- a/src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs @@ -5,14 +5,14 @@ namespace GitVersion.Git; internal sealed class RemoteCollection : IRemoteCollection { private readonly LibGit2Sharp.RemoteCollection innerCollection; - private readonly GitRepository repo; + private readonly GitRepositoryCache repositoryCache; private readonly Lazy> remotes; - internal RemoteCollection(LibGit2Sharp.RemoteCollection collection, GitRepository repo) + internal RemoteCollection(LibGit2Sharp.RemoteCollection collection, GitRepositoryCache repositoryCache) { this.innerCollection = collection.NotNull(); - this.repo = repo.NotNull(); - this.remotes = new Lazy>(() => [.. this.innerCollection.Select(repo.GetOrWrap)]); + this.repositoryCache = repositoryCache.NotNull(); + this.remotes = new Lazy>(() => [.. this.innerCollection.Select(repositoryCache.GetOrWrap)]); } public IEnumerator GetEnumerator() => this.remotes.Value.GetEnumerator(); @@ -24,7 +24,7 @@ public IRemote? this[string name] get { var remote = this.innerCollection[name]; - return remote is null ? null : this.repo.GetOrWrap(remote); + return remote is null ? null : this.repositoryCache.GetOrWrap(remote); } } diff --git a/src/GitVersion.LibGit2Sharp/Git/Tag.cs b/src/GitVersion.LibGit2Sharp/Git/Tag.cs index 58cf55f720..d3d07ab731 100644 --- a/src/GitVersion.LibGit2Sharp/Git/Tag.cs +++ b/src/GitVersion.LibGit2Sharp/Git/Tag.cs @@ -11,13 +11,13 @@ namespace GitVersion.Git; private readonly LibGit2Sharp.Tag innerTag; private readonly Diff diff; private readonly Lazy commitLazy; - private readonly GitRepository repo; + private readonly GitRepositoryCache repositoryCache; - internal Tag(LibGit2Sharp.Tag tag, Diff diff, GitRepository repo) + internal Tag(LibGit2Sharp.Tag tag, Diff diff, GitRepositoryCache repositoryCache) { this.innerTag = tag.NotNull(); this.diff = diff.NotNull(); - this.repo = repo.NotNull(); + this.repositoryCache = repositoryCache.NotNull(); this.commitLazy = new(PeeledTargetCommit); Name = new(this.innerTag.CanonicalName); } @@ -37,7 +37,7 @@ internal Tag(LibGit2Sharp.Tag tag, Diff diff, GitRepository repo) target = annotation.Target; } - return target is LibGit2Sharp.Commit commit ? this.repo.GetOrWrap(commit, this.diff) : null; + return target is LibGit2Sharp.Commit commit ? this.repositoryCache.GetOrWrap(commit, this.diff) : null; } public override bool Equals(object? obj) => Equals(obj as ITag); diff --git a/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs b/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs index 64ce7d7c1c..8ff39d4b4a 100644 --- a/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/TagCollection.cs @@ -6,12 +6,12 @@ internal sealed class TagCollection : ITagCollection { private readonly Lazy> tags; - internal TagCollection(LibGit2Sharp.TagCollection collection, LibGit2Sharp.Diff diff, GitRepository repo) + internal TagCollection(LibGit2Sharp.TagCollection collection, LibGit2Sharp.Diff diff, GitRepositoryCache repositoryCache) { collection.NotNull(); diff.NotNull(); - repo.NotNull(); - this.tags = new Lazy>(() => [.. collection.Select(tag => repo.GetOrWrap(tag, diff))]); + repositoryCache.NotNull(); + this.tags = new Lazy>(() => [.. collection.Select(tag => repositoryCache.GetOrWrap(tag, diff))]); } public IEnumerator GetEnumerator() => this.tags.Value.GetEnumerator(); From af5d0e3c6338c97c5896cfd6de62cd9fe75a923f Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Tue, 14 Oct 2025 07:58:59 +0200 Subject: [PATCH 10/11] renames Refs to References Replaces `Refs` with `References` across the codebase for better clarity and consistency. Updates all method calls, variable names, and interfaces to reflect this change. --- .../Core/RepositoryExtensionsTests.cs | 2 +- src/GitVersion.Core/Core/GitPreparer.cs | 18 +++++++++--------- src/GitVersion.Core/Core/RepositoryStore.cs | 2 +- src/GitVersion.Core/Git/IGitRepository.cs | 2 +- src/GitVersion.Core/PublicAPI.Shipped.txt | 1 - src/GitVersion.Core/PublicAPI.Unshipped.txt | 1 + .../Git/GitRepository.cs | 2 +- .../Git/GitRepository.mutating.cs | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/GitVersion.Core.Tests/Core/RepositoryExtensionsTests.cs b/src/GitVersion.Core.Tests/Core/RepositoryExtensionsTests.cs index 47258223f8..5e9addaab6 100644 --- a/src/GitVersion.Core.Tests/Core/RepositoryExtensionsTests.cs +++ b/src/GitVersion.Core.Tests/Core/RepositoryExtensionsTests.cs @@ -43,7 +43,7 @@ private static IRemote MockRemote(IGitRepository repository) references["develop"].Returns(reference); references.MockCollectionReturn(reference); - repository.Refs.Returns(references); + repository.References.Returns(references); repository.Head.Returns(branch); repository.Branches.Returns(branches); return remote; diff --git a/src/GitVersion.Core/Core/GitPreparer.cs b/src/GitVersion.Core/Core/GitPreparer.cs index ec34ecd4c4..52281942de 100644 --- a/src/GitVersion.Core/Core/GitPreparer.cs +++ b/src/GitVersion.Core/Core/GitPreparer.cs @@ -218,7 +218,7 @@ To disable this error set an environmental variable called IGNORE_NORMALISATION_ private void EnsureHeadIsAttachedToBranch(string? currentBranchName, AuthenticationInfo authentication) { - var headSha = this.repository.Refs.Head?.TargetIdentifier; + var headSha = this.repository.References.Head?.TargetIdentifier; if (!this.repository.IsHeadDetached) { this.log.Info($"HEAD points at branch '{headSha}'."); @@ -226,7 +226,7 @@ private void EnsureHeadIsAttachedToBranch(string? currentBranchName, Authenticat } this.log.Info($"HEAD is detached and points at commit '{headSha}'."); - var localRefs = this.repository.Refs.FromGlob("*").Select(r => $"{r.Name.Canonical} ({r.TargetIdentifier})"); + var localRefs = this.repository.References.FromGlob("*").Select(r => $"{r.Name.Canonical} ({r.TargetIdentifier})"); this.log.Info($"Local Refs:{FileSystemHelper.Path.NewLine}" + string.Join(FileSystemHelper.Path.NewLine, localRefs)); // In order to decide whether a fake branch is required or not, first check to see if any local branches have the same commit SHA of the head SHA. @@ -331,7 +331,7 @@ private void CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(string remoteName var prefix = $"refs/remotes/{remoteName}/"; var remoteHeadCanonicalName = $"{prefix}HEAD"; var headReferenceName = ReferenceName.Parse(remoteHeadCanonicalName); - var remoteTrackingReferences = this.repository.Refs + var remoteTrackingReferences = this.repository.References .FromGlob(prefix + "*") .Where(r => !r.Name.Equals(headReferenceName)); @@ -344,7 +344,7 @@ private void CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(string remoteName // We do not want to touch our current branch if (this.repository.Head.Name.EquivalentTo(branchName)) continue; - var localRef = this.repository.Refs[localReferenceName]; + var localRef = this.repository.References[localReferenceName]; if (localRef != null) { if (localRef.TargetIdentifier == remoteTrackingReference.TargetIdentifier) @@ -356,13 +356,13 @@ private void CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(string remoteName if (remoteRefTipId != null) { this.log.Info($"Updating local ref '{localRef.Name.Canonical}' to point at {remoteRefTipId}."); - this.retryAction.Execute(() => this.repository.Refs.UpdateTarget(localRef, remoteRefTipId)); + this.retryAction.Execute(() => this.repository.References.UpdateTarget(localRef, remoteRefTipId)); } continue; } this.log.Info($"Creating local branch from remote tracking '{remoteTrackingReference.Name.Canonical}'."); - this.repository.Refs.Add(localReferenceName.Canonical, remoteTrackingReference.TargetIdentifier, true); + this.repository.References.Add(localReferenceName.Canonical, remoteTrackingReference.TargetIdentifier, true); var branch = this.repository.Branches[branchName]; if (branch != null) @@ -406,17 +406,17 @@ public void EnsureLocalBranchExistsForCurrentBranch(IRemote remote, string? curr this.log.Info(isLocalBranch ? $"Creating local branch {referenceName}" : $"Creating local branch {referenceName} pointing at {repoTipId}"); - this.repository.Refs.Add(localCanonicalName, repoTipId.Sha); + this.repository.References.Add(localCanonicalName, repoTipId.Sha); } else { this.log.Info(isLocalBranch ? $"Updating local branch {referenceName} to point at {repoTipId}" : $"Updating local branch {referenceName} to match ref {currentBranch}"); - var localRef = this.repository.Refs[localCanonicalName]; + var localRef = this.repository.References[localCanonicalName]; if (localRef != null) { - this.retryAction.Execute(() => this.repository.Refs.UpdateTarget(localRef, repoTipId)); + this.retryAction.Execute(() => this.repository.References.UpdateTarget(localRef, repoTipId)); } } } diff --git a/src/GitVersion.Core/Core/RepositoryStore.cs b/src/GitVersion.Core/Core/RepositoryStore.cs index 401cae566a..50b5512a07 100644 --- a/src/GitVersion.Core/Core/RepositoryStore.cs +++ b/src/GitVersion.Core/Core/RepositoryStore.cs @@ -111,7 +111,7 @@ public IEnumerable GetSourceBranches( { var returnedBranches = new HashSet(); - var referenceLookup = this.repository.Refs.ToLookup(r => r.TargetIdentifier); + var referenceLookup = this.repository.References.ToLookup(r => r.TargetIdentifier); var commitBranches = FindCommitBranchesBranchedFrom(branch, configuration, excludedBranches).ToHashSet(); diff --git a/src/GitVersion.Core/Git/IGitRepository.cs b/src/GitVersion.Core/Git/IGitRepository.cs index bfad989068..7653744b77 100644 --- a/src/GitVersion.Core/Git/IGitRepository.cs +++ b/src/GitVersion.Core/Git/IGitRepository.cs @@ -10,7 +10,7 @@ public interface IGitRepository : IDisposable IBranch Head { get; } ITagCollection Tags { get; } - IReferenceCollection Refs { get; } + IReferenceCollection References { get; } IBranchCollection Branches { get; } ICommitCollection Commits { get; } IRemoteCollection Remotes { get; } diff --git a/src/GitVersion.Core/PublicAPI.Shipped.txt b/src/GitVersion.Core/PublicAPI.Shipped.txt index f8cc9b01d7..77671fc14b 100644 --- a/src/GitVersion.Core/PublicAPI.Shipped.txt +++ b/src/GitVersion.Core/PublicAPI.Shipped.txt @@ -235,7 +235,6 @@ GitVersion.Git.IGitRepository.Head.get -> GitVersion.Git.IBranch! GitVersion.Git.IGitRepository.IsHeadDetached.get -> bool GitVersion.Git.IGitRepository.IsShallow.get -> bool GitVersion.Git.IGitRepository.Path.get -> string! -GitVersion.Git.IGitRepository.Refs.get -> GitVersion.Git.IReferenceCollection! GitVersion.Git.IGitRepository.Remotes.get -> GitVersion.Git.IRemoteCollection! GitVersion.Git.IGitRepository.Tags.get -> GitVersion.Git.ITagCollection! GitVersion.Git.IGitRepository.UncommittedChangesCount() -> int diff --git a/src/GitVersion.Core/PublicAPI.Unshipped.txt b/src/GitVersion.Core/PublicAPI.Unshipped.txt index 19cd7e2e08..b2836fa006 100644 --- a/src/GitVersion.Core/PublicAPI.Unshipped.txt +++ b/src/GitVersion.Core/PublicAPI.Unshipped.txt @@ -2,3 +2,4 @@ GitVersion.Common.IRepositoryStore.GetCommitsReacheableFrom(GitVersion.Git.ICommit! commit, GitVersion.Git.IBranch! branch) -> System.Collections.Generic.IReadOnlyList! GitVersion.Git.ICommit.Id.get -> GitVersion.Git.IObjectId! GitVersion.Git.ICommit.Sha.get -> string! +GitVersion.Git.IGitRepository.References.get -> GitVersion.Git.IReferenceCollection! diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index ffc042c8b1..99aa932119 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -24,10 +24,10 @@ private IRepository RepositoryInstance public IBranch Head => this.repositoryCache.GetOrWrap(RepositoryInstance.Head, RepositoryInstance.Diff); public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this.repositoryCache); - public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs, this.repositoryCache); public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff, this.repositoryCache); public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff, this.repositoryCache); public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes, this.repositoryCache); + public IReferenceCollection References => new ReferenceCollection(RepositoryInstance.Refs, this.repositoryCache); public void DiscoverRepository(string? gitDirectory) { diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.mutating.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.mutating.cs index b3b8f919aa..022fdf8c11 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.mutating.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.mutating.cs @@ -73,7 +73,7 @@ public void CreateBranchForPullRequestBranch(AuthenticationInfo auth) => Reposit var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); this.log.Info($"Creating fake local branch '{fakeBranchName}'."); - Refs.Add(fakeBranchName, headTipSha); + References.Add(fakeBranchName, headTipSha); this.log.Info($"Checking local branch '{fakeBranchName}' out."); Checkout(fakeBranchName); From 468ac8d7b4b272096efd87db795857f7e7ab2631 Mon Sep 17 00:00:00 2001 From: Artur Stolear Date: Tue, 14 Oct 2025 08:02:49 +0200 Subject: [PATCH 11/11] cache git collections with lazy initialization Replaces direct instantiations of Tags, Branches, Commits, Remotes, and References with lazy initialization. This avoids redundant object creation and improves performance by deferring instantiation until the collection is accessed. --- .../Git/GitRepository.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 99aa932119..b4ef655370 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -23,11 +23,20 @@ private IRepository RepositoryInstance public bool IsShallow => RepositoryInstance.Info.IsShallow; public IBranch Head => this.repositoryCache.GetOrWrap(RepositoryInstance.Head, RepositoryInstance.Diff); - public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this.repositoryCache); - public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff, this.repositoryCache); - public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff, this.repositoryCache); - public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes, this.repositoryCache); - public IReferenceCollection References => new ReferenceCollection(RepositoryInstance.Refs, this.repositoryCache); + private ITagCollection? tags; + public ITagCollection Tags => this.tags ??= new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this.repositoryCache); + + private IBranchCollection? branches; + public IBranchCollection Branches => this.branches ??= new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff, this.repositoryCache); + + private ICommitCollection? commits; + public ICommitCollection Commits => this.commits ??= new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff, this.repositoryCache); + + private IRemoteCollection? remotes; + public IRemoteCollection Remotes => this.remotes ??= new RemoteCollection(RepositoryInstance.Network.Remotes, this.repositoryCache); + + private IReferenceCollection? references; + public IReferenceCollection References => this.references ??= new ReferenceCollection(RepositoryInstance.Refs, this.repositoryCache); public void DiscoverRepository(string? gitDirectory) {