Skip to content

Commit 33f7107

Browse files
author
Pascal van Buijtene
committed
Reduce memory usage by caching git objects on their sha hash
1 parent b874c3f commit 33f7107

File tree

11 files changed

+73
-29
lines changed

11 files changed

+73
-29
lines changed

new-cli/GitVersion.Core.Libgit2Sharp/GitVersion.Core.Libgit2Sharp.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
<Compile Include="..\..\src\GitVersion.LibGit2Sharp\Git\CommitCollection.cs">
2222
<Link>Git\CommitCollection.cs</Link>
2323
</Compile>
24+
<Compile Include="..\..\src\GitVersion.LibGit2Sharp\Git\GitCache.cs">
25+
<Link>Git\GitCache.cs</Link>
26+
</Compile>
2427
<Compile Include="..\..\src\GitVersion.LibGit2Sharp\Git\GitObject.cs">
2528
<Link>Git\GitObject.cs</Link>
2629
</Compile>

src/GitVersion.LibGit2Sharp/Git/Branch.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ internal sealed class Branch : IBranch
1010

1111
private readonly LibGit2Sharp.Branch innerBranch;
1212

13-
internal Branch(LibGit2Sharp.Branch branch, LibGit2Sharp.Diff diff)
13+
internal Branch(LibGit2Sharp.Branch branch, LibGit2Sharp.Diff diff, GitCache gitCache)
1414
{
1515
this.innerBranch = branch.NotNull();
1616
Name = new(branch.CanonicalName);
1717

1818
var commit = this.innerBranch.Tip;
19-
Tip = commit is null ? null : new Commit(commit, diff);
19+
Tip = commit is null ? null : gitCache.GetOrCreate(commit, diff);
2020

2121
var commits = this.innerBranch.Commits;
22-
Commits = new CommitCollection(commits, diff);
22+
Commits = new CommitCollection(commits, diff, gitCache);
2323
}
2424

2525
public ReferenceName Name { get; }

src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ internal sealed class BranchCollection : IBranchCollection
88
private readonly LibGit2Sharp.BranchCollection innerCollection;
99
private readonly Lazy<IReadOnlyCollection<IBranch>> branches;
1010
private readonly Diff diff;
11+
private readonly GitCache gitCache;
1112

12-
internal BranchCollection(LibGit2Sharp.BranchCollection collection, Diff diff)
13+
internal BranchCollection(LibGit2Sharp.BranchCollection collection, Diff diff, GitCache gitCache)
1314
{
1415
this.innerCollection = collection.NotNull();
15-
this.branches = new Lazy<IReadOnlyCollection<IBranch>>(() => [.. this.innerCollection.Select(branch => new Branch(branch, diff))]);
16+
this.branches = new Lazy<IReadOnlyCollection<IBranch>>(() => [.. this.innerCollection.Select(branch => gitCache.GetOrCreate(branch, diff))]);
1617
this.diff = diff.NotNull();
18+
this.gitCache = gitCache;
1719
}
1820

1921
public IEnumerator<IBranch> GetEnumerator()
@@ -27,7 +29,7 @@ public IBranch? this[string name]
2729
{
2830
name = name.NotNull();
2931
var branch = this.innerCollection[name];
30-
return branch is null ? null : new Branch(branch, this.diff);
32+
return branch is null ? null : gitCache.GetOrCreate(branch, this.diff);
3133
}
3234
}
3335

src/GitVersion.LibGit2Sharp/Git/Commit.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ internal sealed class Commit : GitObject, ICommit
1414
private readonly LibGit2Sharp.Commit innerCommit;
1515
private readonly LibGit2Sharp.Diff repoDiff;
1616

17-
internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff) : base(innerCommit)
17+
internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, GitCache gitCache) : base(innerCommit)
1818
{
1919
this.innerCommit = innerCommit.NotNull();
20-
this.parentsLazy = new(() => innerCommit.Parents.Select(parent => new Commit(parent, repoDiff)).ToList());
20+
this.parentsLazy = new(() => innerCommit.Parents.Select(parent => gitCache.GetOrCreate(parent, repoDiff)).ToList());
2121
When = innerCommit.Committer.When;
2222
this.repoDiff = repoDiff;
2323
}

src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ internal sealed class CommitCollection : ICommitCollection
88
private readonly ICommitLog innerCollection;
99
private readonly Lazy<IReadOnlyCollection<ICommit>> commits;
1010
private readonly Diff diff;
11+
private readonly GitCache gitCache;
1112

12-
internal CommitCollection(ICommitLog collection, Diff diff)
13+
internal CommitCollection(ICommitLog collection, Diff diff, GitCache gitCache)
1314
{
1415
this.innerCollection = collection.NotNull();
15-
this.commits = new Lazy<IReadOnlyCollection<ICommit>>(() => [.. this.innerCollection.Select(commit => new Commit(commit, diff))]);
16+
this.commits = new Lazy<IReadOnlyCollection<ICommit>>(() => [.. this.innerCollection.Select(commit => gitCache.GetOrCreate(commit, diff))]);
1617
this.diff = diff.NotNull();
18+
this.gitCache = gitCache;
1719
}
1820

1921
public IEnumerator<ICommit> GetEnumerator()
@@ -36,7 +38,7 @@ public IEnumerable<ICommit> QueryBy(CommitFilter commitFilter)
3638
SortBy = (LibGit2Sharp.CommitSortStrategies)commitFilter.SortBy
3739
};
3840
var commitLog = ((IQueryableCommitLog)this.innerCollection).QueryBy(filter);
39-
return new CommitCollection(commitLog, this.diff);
41+
return new CommitCollection(commitLog, this.diff, this.gitCache);
4042

4143
static object? GetReacheableFrom(object? item) =>
4244
item switch
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Collections.Concurrent;
2+
3+
namespace GitVersion.Git;
4+
5+
internal sealed class GitCache
6+
{
7+
private static readonly ConcurrentDictionary<string, Branch> _cachedBranches = new();
8+
private static readonly ConcurrentDictionary<string, Commit> _cachedCommits = new();
9+
private static readonly ConcurrentDictionary<string, Reference> _cachedReferences = new();
10+
private static readonly ConcurrentDictionary<string, Remote> _cachedRemotes = new();
11+
private static readonly ConcurrentDictionary<string, Tag> _cachedTags = new();
12+
13+
public Branch GetOrCreate(LibGit2Sharp.Branch innerBranch, LibGit2Sharp.Diff repoDiff) =>
14+
_cachedBranches.GetOrAdd(innerBranch.CanonicalName, new Branch(innerBranch, repoDiff, this));
15+
public Commit GetOrCreate(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff) =>
16+
_cachedCommits.GetOrAdd(innerCommit.Sha, new Commit(innerCommit, repoDiff, this));
17+
public Reference GetOrCreate(LibGit2Sharp.Reference innerReference) =>
18+
_cachedReferences.GetOrAdd(innerReference.CanonicalName, new Reference(innerReference));
19+
public Remote GetOrCreate(LibGit2Sharp.Remote innerRemote) =>
20+
_cachedRemotes.GetOrAdd(innerRemote.Name, new Remote(innerRemote));
21+
public Tag GetOrCreate(LibGit2Sharp.Tag innerTag, LibGit2Sharp.Diff repoDiff) =>
22+
_cachedTags.GetOrAdd(innerTag.CanonicalName, new Tag(innerTag, repoDiff, this));
23+
}

src/GitVersion.LibGit2Sharp/Git/GitRepository.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace GitVersion.Git;
77
internal sealed partial class GitRepository
88
{
99
private Lazy<IRepository>? repositoryLazy;
10+
private readonly GitCache gitCache = new();
1011

1112
private IRepository RepositoryInstance
1213
{
@@ -16,17 +17,18 @@ private IRepository RepositoryInstance
1617
return lazy.Value;
1718
}
1819
}
20+
1921
public string Path => RepositoryInstance.Info.Path;
2022
public string WorkingDirectory => RepositoryInstance.Info.WorkingDirectory;
2123
public bool IsHeadDetached => RepositoryInstance.Info.IsHeadDetached;
2224
public bool IsShallow => RepositoryInstance.Info.IsShallow;
23-
public IBranch Head => new Branch(RepositoryInstance.Head, RepositoryInstance.Diff);
25+
public IBranch Head => gitCache.GetOrCreate(RepositoryInstance.Head, RepositoryInstance.Diff);
2426

25-
public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff);
26-
public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs);
27-
public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff);
28-
public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff);
29-
public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes);
27+
public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this.gitCache);
28+
public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs, this.gitCache);
29+
public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff, this.gitCache);
30+
public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff, this.gitCache);
31+
public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes, this.gitCache);
3032

3133
public void DiscoverRepository(string? gitDirectory)
3234
{
@@ -48,7 +50,7 @@ public void DiscoverRepository(string? gitDirectory)
4850
var first = (Commit)commit;
4951
var second = (Commit)otherCommit;
5052
var mergeBase = RepositoryInstance.ObjectDatabase.FindMergeBase(first, second);
51-
return mergeBase == null ? null : new Commit(mergeBase, RepositoryInstance.Diff);
53+
return mergeBase == null ? null : this.gitCache.GetOrCreate(mergeBase, RepositoryInstance.Diff);
5254
});
5355
}
5456

src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@ namespace GitVersion.Git;
55
internal sealed class ReferenceCollection : IReferenceCollection
66
{
77
private readonly LibGit2Sharp.ReferenceCollection innerCollection;
8+
private readonly GitCache gitCache;
89
private IReadOnlyCollection<IReference>? references;
910

10-
internal ReferenceCollection(LibGit2Sharp.ReferenceCollection collection) => this.innerCollection = collection.NotNull();
11+
internal ReferenceCollection(LibGit2Sharp.ReferenceCollection collection, GitCache gitCache)
12+
{
13+
this.innerCollection = collection.NotNull();
14+
this.gitCache = gitCache;
15+
}
1116

1217
public IEnumerator<IReference> GetEnumerator()
1318
{
14-
this.references ??= [.. this.innerCollection.Select(reference => new Reference(reference))];
19+
this.references ??= [.. this.innerCollection.Select(reference => this.gitCache.GetOrCreate(reference))];
1520
return this.references.GetEnumerator();
1621
}
1722

@@ -30,13 +35,13 @@ public IReference? this[string name]
3035
get
3136
{
3237
var reference = this.innerCollection[name];
33-
return reference is null ? null : new Reference(reference);
38+
return reference is null ? null : gitCache.GetOrCreate(reference);
3439
}
3540
}
3641

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

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

41-
public IEnumerable<IReference> FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => new Reference(reference));
46+
public IEnumerable<IReference> FromGlob(string prefix) => this.innerCollection.FromGlob(prefix).Select(reference => this.gitCache.GetOrCreate(reference));
4247
}

src/GitVersion.LibGit2Sharp/Git/RemoteCollection.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@ namespace GitVersion.Git;
55
internal sealed class RemoteCollection : IRemoteCollection
66
{
77
private readonly LibGit2Sharp.RemoteCollection innerCollection;
8+
private readonly GitCache gitCache;
89
private IReadOnlyCollection<IRemote>? remotes;
910

10-
internal RemoteCollection(LibGit2Sharp.RemoteCollection collection) => this.innerCollection = collection.NotNull();
11+
internal RemoteCollection(LibGit2Sharp.RemoteCollection collection, GitCache gitCache)
12+
{
13+
this.innerCollection = collection.NotNull();
14+
this.gitCache = gitCache;
15+
}
1116

1217
public IEnumerator<IRemote> GetEnumerator()
1318
{
14-
this.remotes ??= [.. this.innerCollection.Select(reference => new Remote(reference))];
19+
this.remotes ??= [.. this.innerCollection.Select(reference => this.gitCache.GetOrCreate(reference))];
1520
return this.remotes.GetEnumerator();
1621
}
1722

@@ -22,7 +27,7 @@ public IRemote? this[string name]
2227
get
2328
{
2429
var remote = this.innerCollection[name];
25-
return remote is null ? null : new Remote(remote);
30+
return remote is null ? null : this.gitCache.GetOrCreate(remote);
2631
}
2732
}
2833

src/GitVersion.LibGit2Sharp/Git/Tag.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ internal sealed class Tag : ITag
1111
private readonly LibGit2Sharp.Tag innerTag;
1212
private readonly Diff diff;
1313
private readonly Lazy<ICommit?> commitLazy;
14+
private readonly GitCache gitCache;
1415

15-
internal Tag(LibGit2Sharp.Tag tag, Diff diff)
16+
internal Tag(LibGit2Sharp.Tag tag, Diff diff, GitCache gitCache)
1617
{
1718
this.innerTag = tag.NotNull();
1819
this.commitLazy = new(PeeledTargetCommit);
1920
this.diff = diff.NotNull();
21+
this.gitCache = gitCache;
2022
Name = new(this.innerTag.CanonicalName);
2123
}
2224

@@ -35,7 +37,7 @@ internal Tag(LibGit2Sharp.Tag tag, Diff diff)
3537
target = annotation.Target;
3638
}
3739

38-
return target is LibGit2Sharp.Commit commit ? new Commit(commit, this.diff) : null;
40+
return target is LibGit2Sharp.Commit commit ? this.gitCache.GetOrCreate(commit, this.diff) : null;
3941
}
4042

4143
public override bool Equals(object? obj) => Equals(obj as ITag);

0 commit comments

Comments
 (0)