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: 5 additions & 3 deletions src/GitVersion.LibGit2Sharp/Git/Branch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ internal sealed class Branch : IBranch

private readonly LibGit2Sharp.Branch innerBranch;

internal Branch(LibGit2Sharp.Branch branch, LibGit2Sharp.Diff diff)
internal Branch(LibGit2Sharp.Branch branch, LibGit2Sharp.Diff diff, GitRepository repo)
{
diff.NotNull();
repo.NotNull();
this.innerBranch = branch.NotNull();
Name = new(branch.CanonicalName);

var commit = this.innerBranch.Tip;
Tip = commit is null ? null : new Commit(commit, diff);
Tip = commit is null ? null : repo.GetOrCreate(commit, diff);

var commits = this.innerBranch.Commits;
Commits = new CommitCollection(commits, diff);
Commits = new CommitCollection(commits, diff, repo);
}

public ReferenceName Name { get; }
Expand Down
8 changes: 5 additions & 3 deletions src/GitVersion.LibGit2Sharp/Git/BranchCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ internal sealed class BranchCollection : IBranchCollection
private readonly LibGit2Sharp.BranchCollection innerCollection;
private readonly Lazy<IReadOnlyCollection<IBranch>> branches;
private readonly Diff diff;
private readonly GitRepository repo;

internal BranchCollection(LibGit2Sharp.BranchCollection collection, Diff diff)
internal BranchCollection(LibGit2Sharp.BranchCollection collection, Diff diff, GitRepository repo)
{
this.innerCollection = collection.NotNull();
this.branches = new Lazy<IReadOnlyCollection<IBranch>>(() => [.. this.innerCollection.Select(branch => new Branch(branch, diff))]);
this.branches = new Lazy<IReadOnlyCollection<IBranch>>(() => [.. this.innerCollection.Select(branch => repo.GetOrCreate(branch, diff))]);
this.diff = diff.NotNull();
this.repo = repo.NotNull();
}

public IEnumerator<IBranch> GetEnumerator()
Expand All @@ -27,7 +29,7 @@ public IBranch? this[string name]
{
name = name.NotNull();
var branch = this.innerCollection[name];
return branch is null ? null : new Branch(branch, this.diff);
return branch is null ? null : this.repo.GetOrCreate(branch, this.diff);
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/GitVersion.LibGit2Sharp/Git/Commit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ internal sealed class Commit : GitObject, ICommit
private readonly LibGit2Sharp.Commit innerCommit;
private readonly LibGit2Sharp.Diff repoDiff;

internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff) : base(innerCommit)
internal Commit(LibGit2Sharp.Commit innerCommit, LibGit2Sharp.Diff repoDiff, GitRepository repo) : base(innerCommit)
{
repoDiff.NotNull();
repo.NotNull();
this.innerCommit = innerCommit.NotNull();
this.parentsLazy = new(() => innerCommit.Parents.Select(parent => new Commit(parent, repoDiff)).ToList());
this.parentsLazy = new(() => innerCommit.Parents.Select(parent => repo.GetOrCreate(parent, repoDiff)).ToList());
When = innerCommit.Committer.When;
this.repoDiff = repoDiff;
}
Expand Down
8 changes: 5 additions & 3 deletions src/GitVersion.LibGit2Sharp/Git/CommitCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ internal sealed class CommitCollection : ICommitCollection
private readonly ICommitLog innerCollection;
private readonly Lazy<IReadOnlyCollection<ICommit>> commits;
private readonly Diff diff;
private readonly GitRepository repo;

internal CommitCollection(ICommitLog collection, Diff diff)
internal CommitCollection(ICommitLog collection, Diff diff, GitRepository repo)
{
this.innerCollection = collection.NotNull();
this.commits = new Lazy<IReadOnlyCollection<ICommit>>(() => [.. this.innerCollection.Select(commit => new Commit(commit, diff))]);
this.commits = new Lazy<IReadOnlyCollection<ICommit>>(() => [.. this.innerCollection.Select(commit => repo.GetOrCreate(commit, diff))]);
this.diff = diff.NotNull();
this.repo = repo.NotNull();
}

public IEnumerator<ICommit> GetEnumerator()
Expand All @@ -36,7 +38,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);
return new CommitCollection(commitLog, this.diff, this.repo);

static object? GetReacheableFrom(object? item) =>
item switch
Expand Down
35 changes: 30 additions & 5 deletions src/GitVersion.LibGit2Sharp/Git/GitRepository.cs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the rename from GitCache to GitRepository? I don't feel like the provided functionality is quite that of a repository, which is often used as an abstraction over a database. 🤔

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using GitVersion.Extensions;
using GitVersion.Helpers;
using LibGit2Sharp;
Expand All @@ -8,6 +9,10 @@ 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 IRepository RepositoryInstance
{
get
Expand All @@ -20,12 +25,12 @@ private IRepository RepositoryInstance
public string WorkingDirectory => RepositoryInstance.Info.WorkingDirectory;
public bool IsHeadDetached => RepositoryInstance.Info.IsHeadDetached;
public bool IsShallow => RepositoryInstance.Info.IsShallow;
public IBranch Head => new Branch(RepositoryInstance.Head, RepositoryInstance.Diff);
public IBranch Head => GetOrCreate(RepositoryInstance.Head, RepositoryInstance.Diff);

public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff);
public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags, RepositoryInstance.Diff, this);
public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs);
public IBranchCollection Branches => new BranchCollection(RepositoryInstance.Branches, RepositoryInstance.Diff);
public ICommitCollection Commits => new CommitCollection(RepositoryInstance.Commits, RepositoryInstance.Diff);
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 void DiscoverRepository(string? gitDirectory)
Expand All @@ -48,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 : new Commit(mergeBase, RepositoryInstance.Diff);
return mergeBase == null ? null : GetOrCreate(mergeBase, RepositoryInstance.Diff);
});
}

Expand All @@ -58,6 +63,26 @@ 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}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for not discovering this earlier. It's not very important, but will improve debugging if the format of the key matches what one would expect to find used in Git and GitHub a bit more than this.

Suggested change
var cacheKey = $"{innerBranch.CanonicalName}|{innerBranch.Tip.Sha}|{innerBranch.RemoteName}";
var cacheKey = $"{innerBranch.RemoteName}/{innerBranch.CanonicalName}@{innerBranch.Tip.Sha}|";

return cachedBranches.GetOrAdd(cacheKey, new Branch(innerBranch, repoDiff, this));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't lazy initialization here be even more optimal, avoiding the construction of a Branch instance altogether if it already exists?

Suggested change
return cachedBranches.GetOrAdd(cacheKey, new Branch(innerBranch, repoDiff, this));
return cachedBranches.GetOrAdd(cacheKey, () => new Branch(innerBranch, repoDiff, this));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

}

public Commit GetOrCreate(LibGit2Sharp.Commit innerCommit, Diff repoDiff) =>
cachedCommits.GetOrAdd(innerCommit.Sha, new Commit(innerCommit, repoDiff, this));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var cacheKey = $"{innerTag.CanonicalName}|{innerTag.Target.Sha}";
var cacheKey = $"{innerTag.CanonicalName}@{innerTag.Target.Sha}";

return cachedTags.GetOrAdd(cacheKey, new Tag(innerTag, repoDiff, this));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return cachedTags.GetOrAdd(cacheKey, new Tag(innerTag, repoDiff, this));
return cachedTags.GetOrAdd(cacheKey, () => new Tag(innerTag, repoDiff, this));

}

public void Dispose()
{
if (this.repositoryLazy is { IsValueCreated: true }) RepositoryInstance.Dispose();
Expand Down
6 changes: 4 additions & 2 deletions src/GitVersion.LibGit2Sharp/Git/Tag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ internal sealed class Tag : ITag
private readonly LibGit2Sharp.Tag innerTag;
private readonly Diff diff;
private readonly Lazy<ICommit?> commitLazy;
private readonly GitRepository repo;

internal Tag(LibGit2Sharp.Tag tag, Diff diff)
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();
Name = new(this.innerTag.CanonicalName);
}

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

return target is LibGit2Sharp.Commit commit ? new Commit(commit, this.diff) : null;
return target is LibGit2Sharp.Commit commit ? this.repo.GetOrCreate(commit, this.diff) : null;
}

public override bool Equals(object? obj) => Equals(obj as ITag);
Expand Down
8 changes: 5 additions & 3 deletions src/GitVersion.LibGit2Sharp/Git/TagCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ internal sealed class TagCollection : ITagCollection
{
private readonly Lazy<IReadOnlyCollection<ITag>> tags;

internal TagCollection(LibGit2Sharp.TagCollection collection, LibGit2Sharp.Diff diff)
internal TagCollection(LibGit2Sharp.TagCollection collection, LibGit2Sharp.Diff diff, GitRepository repo)
{
collection = collection.NotNull();
this.tags = new Lazy<IReadOnlyCollection<ITag>>(() => [.. collection.Select(tag => new Tag(tag, diff))]);
collection.NotNull();
diff.NotNull();
repo.NotNull();
this.tags = new Lazy<IReadOnlyCollection<ITag>>(() => [.. collection.Select(tag => repo.GetOrCreate(tag, diff))]);
}

public IEnumerator<ITag> GetEnumerator()
Expand Down