Skip to content

Commit df996c4

Browse files
author
Pascal van Buijtene
committed
Reduce memory usage by caching git objects
1 parent 9939c56 commit df996c4

File tree

9 files changed

+67
-21
lines changed

9 files changed

+67
-21
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: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@ 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
{
15+
diff.NotNull();
16+
gitCache.NotNull();
1517
this.innerBranch = branch.NotNull();
1618
Name = new(branch.CanonicalName);
1719

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

2123
var commits = this.innerBranch.Commits;
22-
Commits = new CommitCollection(commits, diff);
24+
Commits = new CommitCollection(commits, diff, gitCache);
2325
}
2426

2527
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.NotNull();
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 : this.gitCache.GetOrCreate(branch, this.diff);
3133
}
3234
}
3335

src/GitVersion.LibGit2Sharp/Git/Commit.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ 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
{
19+
repoDiff.NotNull();
20+
gitCache.NotNull();
1921
this.innerCommit = innerCommit.NotNull();
20-
this.parentsLazy = new(() => innerCommit.Parents.Select(parent => new Commit(parent, repoDiff)).ToList());
22+
this.parentsLazy = new(() => innerCommit.Parents.Select(parent => gitCache.GetOrCreate(parent, repoDiff)).ToList());
2123
When = innerCommit.Committer.When;
2224
this.repoDiff = repoDiff;
2325
}

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.NotNull();
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: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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, Tag> cachedTags = new();
10+
11+
public Branch GetOrCreate(LibGit2Sharp.Branch innerBranch, LibGit2Sharp.Diff repoDiff)
12+
{
13+
if (innerBranch.Tip is null)
14+
{
15+
return new Branch(innerBranch, repoDiff, this);
16+
}
17+
18+
var cacheKey = $"{innerBranch.CanonicalName}|{innerBranch.Tip.Sha}|{innerBranch.RemoteName}";
19+
return cachedBranches.GetOrAdd(cacheKey, new Branch(innerBranch, repoDiff, this));
20+
}
21+
22+
public Commit GetOrCreate(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff) =>
23+
cachedCommits.GetOrAdd(innerCommit.Sha, new Commit(innerCommit, repoDiff, this));
24+
25+
public Tag GetOrCreate(LibGit2Sharp.Tag innerTag, LibGit2Sharp.Diff repoDiff)
26+
{
27+
var cacheKey = $"{innerTag.CanonicalName}|{innerTag.Target.Sha}";
28+
return cachedTags.GetOrAdd(cacheKey, new Tag(innerTag, repoDiff, this));
29+
}
30+
}

src/GitVersion.LibGit2Sharp/Git/GitRepository.cs

Lines changed: 6 additions & 5 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
{
@@ -20,12 +21,12 @@ private IRepository RepositoryInstance
2021
public string WorkingDirectory => RepositoryInstance.Info.WorkingDirectory;
2122
public bool IsHeadDetached => RepositoryInstance.Info.IsHeadDetached;
2223
public bool IsShallow => RepositoryInstance.Info.IsShallow;
23-
public IBranch Head => new Branch(RepositoryInstance.Head, RepositoryInstance.Diff);
24+
public IBranch Head => this.gitCache.GetOrCreate(RepositoryInstance.Head, RepositoryInstance.Diff);
2425

25-
public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff);
26+
public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this.gitCache);
2627
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);
28+
public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff, this.gitCache);
29+
public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff, this.gitCache);
2930
public IRemoteCollection Remotes => new RemoteCollection(RepositoryInstance.Network.Remotes);
3031

3132
public void DiscoverRepository(string? gitDirectory)
@@ -48,7 +49,7 @@ public void DiscoverRepository(string? gitDirectory)
4849
var first = (Commit)commit;
4950
var second = (Commit)otherCommit;
5051
var mergeBase = RepositoryInstance.ObjectDatabase.FindMergeBase(first, second);
51-
return mergeBase == null ? null : new Commit(mergeBase, RepositoryInstance.Diff);
52+
return mergeBase == null ? null : this.gitCache.GetOrCreate(mergeBase, RepositoryInstance.Diff);
5253
});
5354
}
5455

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.NotNull();
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);

src/GitVersion.LibGit2Sharp/Git/TagCollection.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ internal sealed class TagCollection : ITagCollection
66
{
77
private readonly Lazy<IReadOnlyCollection<ITag>> tags;
88

9-
internal TagCollection(LibGit2Sharp.TagCollection collection, LibGit2Sharp.Diff diff)
9+
internal TagCollection(LibGit2Sharp.TagCollection collection, LibGit2Sharp.Diff diff, GitCache gitCache)
1010
{
11-
collection = collection.NotNull();
12-
this.tags = new Lazy<IReadOnlyCollection<ITag>>(() => [.. collection.Select(tag => new Tag(tag, diff))]);
11+
collection.NotNull();
12+
diff.NotNull();
13+
gitCache.NotNull();
14+
this.tags = new Lazy<IReadOnlyCollection<ITag>>(() => [.. collection.Select(tag => gitCache.GetOrCreate(tag, diff))]);
1315
}
1416

1517
public IEnumerator<ITag> GetEnumerator()

0 commit comments

Comments
 (0)