Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/GitVersion.LibGit2Sharp/Git/Branch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ internal sealed class Branch : IBranch

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.GetOrCreate(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; }
Expand Down
13 changes: 6 additions & 7 deletions src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ internal sealed class BranchCollection : IBranchCollection
private readonly LibGit2Sharp.BranchCollection innerCollection;
private readonly Lazy<IReadOnlyCollection<IBranch>> 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<IReadOnlyCollection<IBranch>>(() => [.. this.innerCollection.Select(branch => repo.GetOrCreate(branch, diff))]);
this.branches = new Lazy<IReadOnlyCollection<IBranch>>(() => [.. this.innerCollection.Select(branch => repositoryCache.GetOrWrap(branch, diff))]);
this.diff = diff.NotNull();
this.repo = repo.NotNull();
this.repositoryCache = repositoryCache.NotNull();
}

public IEnumerator<IBranch> GetEnumerator()
=> this.branches.Value.GetEnumerator();
public IEnumerator<IBranch> GetEnumerator() => this.branches.Value.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

Expand All @@ -29,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.repositoryCache.GetOrWrap(branch, this.diff);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/GitVersion.LibGit2Sharp/Git/Commit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ internal sealed class Commit : ICommit
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.GetOrCreate(parent, repoDiff)).ToList());
this.parentsLazy = new(() => [.. innerCommit.Parents.Select(parent => repositoryCache.GetOrWrap(parent, repoDiff))]);
Id = new ObjectId(innerCommit.Id);
Sha = innerCommit.Sha;
When = innerCommit.Committer.When;
Expand Down
13 changes: 6 additions & 7 deletions src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ internal sealed class CommitCollection : ICommitCollection
private readonly ICommitLog innerCollection;
private readonly Lazy<IReadOnlyCollection<ICommit>> 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<IReadOnlyCollection<ICommit>>(() => [.. this.innerCollection.Select(commit => repo.GetOrCreate(commit, diff))]);
this.commits = new Lazy<IReadOnlyCollection<ICommit>>(() => [.. this.innerCollection.Select(commit => repositoryCache.GetOrWrap(commit, diff))]);
this.diff = diff.NotNull();
this.repo = repo.NotNull();
this.repositoryCache = repositoryCache.NotNull();
}

public IEnumerator<ICommit> GetEnumerator()
=> this.commits.Value.GetEnumerator();
public IEnumerator<ICommit> GetEnumerator() => this.commits.Value.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

Expand All @@ -38,7 +37,7 @@ public IEnumerable<ICommit> 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
Expand Down
48 changes: 16 additions & 32 deletions src/GitVersion.LibGit2Sharp/Git/GitRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Collections.Concurrent;
using GitVersion.Extensions;
using GitVersion.Helpers;
using LibGit2Sharp;
Expand All @@ -8,10 +7,7 @@ namespace GitVersion.Git;
internal sealed partial class GitRepository
{
private Lazy<IRepository>? repositoryLazy;

private readonly ConcurrentDictionary<string, Branch> cachedBranches = new();
private readonly ConcurrentDictionary<string, Commit> cachedCommits = new();
private readonly ConcurrentDictionary<string, Tag> cachedTags = new();
private readonly GitRepositoryCache repositoryCache = new();

private IRepository RepositoryInstance
{
Expand All @@ -25,13 +21,21 @@ 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 => this.repositoryCache.GetOrWrap(RepositoryInstance.Head, RepositoryInstance.Diff);

private ITagCollection? tags;
public ITagCollection Tags => this.tags ??= new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this.repositoryCache);

public IBranchCollection 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);

public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, 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 IReferenceCollection References => new ReferenceCollection(RepositoryInstance.Refs);
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)
{
Expand All @@ -53,7 +57,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 : this.repositoryCache.GetOrWrap(mergeBase, RepositoryInstance.Diff);
});
}

Expand All @@ -63,26 +67,6 @@ public int UncommittedChangesCount()
return retryAction.Execute(GetUncommittedChangesCountInternal);
}

public Branch GetOrCreate(LibGit2Sharp.Branch innerBranch, Diff repoDiff)
{
if (innerBranch.Tip is null)
{
return new Branch(innerBranch, repoDiff, this);
}

var cacheKey = $"{innerBranch.CanonicalName}|{innerBranch.Tip.Sha}|{innerBranch.RemoteName}";
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));

public Tag GetOrCreate(LibGit2Sharp.Tag innerTag, Diff repoDiff)
{
var cacheKey = $"{innerTag.CanonicalName}|{innerTag.Target.Sha}";
return cachedTags.GetOrAdd(cacheKey, new Tag(innerTag, repoDiff, this));
}

public void Dispose()
{
if (this.repositoryLazy is { IsValueCreated: true }) RepositoryInstance.Dispose();
Expand Down
37 changes: 37 additions & 0 deletions src/GitVersion.LibGit2Sharp/Git/GitRepositoryCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Collections.Concurrent;
using LibGit2Sharp;

namespace GitVersion.Git;

internal class GitRepositoryCache
{
private readonly ConcurrentDictionary<string, Branch> cachedBranches = new();
private readonly ConcurrentDictionary<string, Commit> cachedCommits = new();
private readonly ConcurrentDictionary<string, Tag> cachedTags = new();
private readonly ConcurrentDictionary<string, Remote> cachedRemotes = new();
private readonly ConcurrentDictionary<string, Reference> cachedReferences = new();
private readonly ConcurrentDictionary<string, RefSpec> 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));
}
6 changes: 3 additions & 3 deletions src/GitVersion.LibGit2Sharp/Git/RefSpecCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ internal sealed class RefSpecCollection : IRefSpecCollection
{
private readonly Lazy<IReadOnlyCollection<IRefSpec>> refSpecs;

internal RefSpecCollection(LibGit2Sharp.RefSpecCollection collection)
internal RefSpecCollection(LibGit2Sharp.RefSpecCollection innerCollection, GitRepositoryCache repositoryCache)
{
collection = collection.NotNull();
this.refSpecs = new Lazy<IReadOnlyCollection<IRefSpec>>(() => [.. collection.Select(tag => new RefSpec(tag))]);
innerCollection = innerCollection.NotNull();
this.refSpecs = new Lazy<IReadOnlyCollection<IRefSpec>>(() => [.. innerCollection.Select(repositoryCache.GetOrWrap)]);
}

public IEnumerator<IRefSpec> GetEnumerator() => this.refSpecs.Value.GetEnumerator();
Expand Down
42 changes: 27 additions & 15 deletions src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,17 @@ namespace GitVersion.Git;
internal sealed class ReferenceCollection : IReferenceCollection
{
private readonly LibGit2Sharp.ReferenceCollection innerCollection;
private IReadOnlyCollection<IReference>? references;
private readonly GitRepositoryCache repositoryCache;
private Lazy<IReadOnlyCollection<IReference>> references = null!;

internal ReferenceCollection(LibGit2Sharp.ReferenceCollection collection) => this.innerCollection = collection.NotNull();

public IEnumerator<IReference> GetEnumerator()
internal ReferenceCollection(LibGit2Sharp.ReferenceCollection collection, GitRepositoryCache repositoryCache)
{
this.references ??= [.. this.innerCollection.Select(reference => new Reference(reference))];
return this.references.GetEnumerator();
this.innerCollection = collection.NotNull();
this.repositoryCache = repositoryCache.NotNull();
InitializeReferencesLazy();
}

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;
}
public IEnumerator<IReference> GetEnumerator() => this.references.Value.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

Expand All @@ -30,13 +24,31 @@ 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.repositoryCache.GetOrWrap(reference);
}
}

public IReference? this[ReferenceName referenceName] => this[referenceName.Canonical];

public IReference? Head => this["HEAD"];

public IEnumerable<IReference> FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => new Reference(reference));
public IEnumerable<IReference> FromGlob(string prefix)
=> this.innerCollection.FromGlob(prefix).Select(reference => this.repositoryCache.GetOrWrap(reference));

public void Add(string name, string canonicalRefNameOrObject, bool allowOverwrite = false) =>
RepositoryExtensions.RunSafe(() =>
{
this.innerCollection.Add(name, canonicalRefNameOrObject, allowOverwrite);
InitializeReferencesLazy();
});

public void UpdateTarget(IReference directRef, IObjectId targetId) =>
RepositoryExtensions.RunSafe(() =>
{
this.innerCollection.UpdateTarget((Reference)directRef, (ObjectId)targetId);
InitializeReferencesLazy();
});

private void InitializeReferencesLazy()
=> this.references = new Lazy<IReadOnlyCollection<IReference>>(() => [.. this.innerCollection.Select(repositoryCache.GetOrWrap)]);
}
11 changes: 8 additions & 3 deletions src/GitVersion.LibGit2Sharp/Git/Remote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,27 @@ internal sealed class Remote : IRemote
private static readonly LambdaKeyComparer<IRemote, string> comparerHelper = new(x => x.Name);

private readonly LibGit2Sharp.Remote innerRemote;
private readonly GitRepositoryCache repositoryCache;

internal Remote(LibGit2Sharp.Remote remote) => this.innerRemote = remote.NotNull();
internal Remote(LibGit2Sharp.Remote remote, GitRepositoryCache repositoryCache)
{
this.innerRemote = remote.NotNull();
this.repositoryCache = repositoryCache.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<IRefSpec> RefSpecs
private IEnumerable<IRefSpec> RefSpecs
{
get
{
var refSpecs = this.innerRemote.RefSpecs;
return refSpecs is null
? []
: new RefSpecCollection((LibGit2Sharp.RefSpecCollection)refSpecs);
: new RefSpecCollection((LibGit2Sharp.RefSpecCollection)refSpecs, this.repositoryCache);
}
}

Expand Down
41 changes: 24 additions & 17 deletions src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,43 @@ namespace GitVersion.Git;
internal sealed class RemoteCollection : IRemoteCollection
{
private readonly LibGit2Sharp.RemoteCollection innerCollection;
private IReadOnlyCollection<IRemote>? remotes;
private readonly GitRepositoryCache repositoryCache;
private Lazy<IReadOnlyCollection<IRemote>> remotes = null!;

internal RemoteCollection(LibGit2Sharp.RemoteCollection collection) => this.innerCollection = collection.NotNull();

public IEnumerator<IRemote> GetEnumerator()
internal RemoteCollection(LibGit2Sharp.RemoteCollection collection, GitRepositoryCache repositoryCache)
{
this.remotes ??= [.. this.innerCollection.Select(reference => new Remote(reference))];
return this.remotes.GetEnumerator();
this.innerCollection = collection.NotNull();
this.repositoryCache = repositoryCache.NotNull();
InitializeRemotesLazy();
}

public IEnumerator<IRemote> GetEnumerator() => this.remotes.Value.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

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.repositoryCache.GetOrWrap(remote);
}
}

public void Remove(string remoteName)
{
this.innerCollection.Remove(remoteName);
this.remotes = null;
}
public void Remove(string remoteName) =>
RepositoryExtensions.RunSafe(() =>
{
this.innerCollection.Remove(remoteName);
InitializeRemotesLazy();
});

public void Update(string remoteName, string refSpec)
{
this.innerCollection.Update(remoteName, r => r.FetchRefSpecs.Add(refSpec));
this.remotes = null;
}
public void Update(string remoteName, string refSpec) =>
RepositoryExtensions.RunSafe(() =>
{
this.innerCollection.Update(remoteName, r => r.FetchRefSpecs.Add(refSpec));
InitializeRemotesLazy();
});

private void InitializeRemotesLazy()
=> this.remotes = new Lazy<IReadOnlyCollection<IRemote>>(() => [.. this.innerCollection.Select(repositoryCache.GetOrWrap)]);
}
Loading