Skip to content

Commit ad02e01

Browse files
committed
feat: Improved annotated tag looking up.
Related: kekyo/screw-up@2370649
1 parent 3cbbe4b commit ad02e01

File tree

1 file changed

+78
-5
lines changed

1 file changed

+78
-5
lines changed

RelaxVersioner.Core/Analyzer.cs

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,82 @@
77
//
88
////////////////////////////////////////////////////////////////////////////////////////
99

10+
using System;
1011
using System.Collections.Generic;
1112
using System.Linq;
1213
using System.Threading;
1314
using System.Threading.Tasks;
15+
using GitReader;
1416
using GitReader.IO;
1517
using GitReader.Primitive;
1618

1719
namespace RelaxVersioner;
1820

1921
internal static class Analyzer
2022
{
23+
// Tag cache for optimized tag lookups
24+
private sealed class TagCache
25+
{
26+
private readonly Dictionary<Hash, List<PrimitiveTag>> commitToTags;
27+
28+
private TagCache(Dictionary<Hash, List<PrimitiveTag>> commitToTags)
29+
{
30+
this.commitToTags = commitToTags;
31+
}
32+
33+
public static async Task<TagCache> BuildAsync(
34+
PrimitiveRepository repository,
35+
CancellationToken ct)
36+
{
37+
var cache = new Dictionary<Hash, List<PrimitiveTag>>();
38+
var allTagReferences = await repository.GetTagReferencesAsync(ct);
39+
40+
// Process all tags in parallel to build commit->tags mapping
41+
await LooseConcurrentScope.Default.WhenAll(
42+
allTagReferences.Select(async tagRef =>
43+
{
44+
var tag = await repository.GetTagAsync(tagRef, ct);
45+
46+
// Determine the actual commit hash for this tag
47+
Hash commitHash;
48+
if (tag.Type == ObjectTypes.Commit)
49+
{
50+
// Lightweight tag or annotated tag pointing to commit
51+
commitHash = tag.Hash;
52+
}
53+
else if (tagRef.CommitHash.HasValue)
54+
{
55+
// Annotated tag with peeled-tag info
56+
commitHash = tagRef.CommitHash.Value;
57+
}
58+
else
59+
{
60+
// Annotated tag pointing to non-commit object
61+
// Try to resolve through the tag object
62+
commitHash = tagRef.ObjectOrCommitHash;
63+
}
64+
65+
lock (cache)
66+
{
67+
if (!cache.TryGetValue(commitHash, out var list))
68+
{
69+
list = new List<PrimitiveTag>();
70+
cache[commitHash] = list;
71+
}
72+
list.Add(tag);
73+
}
74+
}));
75+
76+
return new TagCache(cache);
77+
}
78+
79+
public PrimitiveTag[] GetTagsForCommit(Hash commitHash)
80+
{
81+
return commitToTags.TryGetValue(commitHash, out var tags) ?
82+
tags.ToArray() : Array.Empty<PrimitiveTag>();
83+
}
84+
}
85+
2186
private readonly struct ScheduledCommit
2287
{
2388
public readonly PrimitiveCommit Commit;
@@ -72,7 +137,9 @@ private static Version IncrementLastVersionComponent(
72137
private static async Task<Version> LookupVersionLabelRecursiveAsync(
73138
PrimitiveRepository repository,
74139
PrimitiveCommit commit,
75-
Dictionary<PrimitiveCommit, Version> reached, CancellationToken ct)
140+
Dictionary<PrimitiveCommit, Version> reached,
141+
TagCache tagCache,
142+
CancellationToken ct)
76143
{
77144
var scheduledStack = new Stack<ScheduledCommit>();
78145
var version = Version.Default;
@@ -91,7 +158,8 @@ private static async Task<Version> LookupVersionLabelRecursiveAsync(
91158
}
92159

93160
// Detected mostly larger version tag.
94-
var candidates = (await repository.GetRelatedTagsAsync(commit, ct)).
161+
// Use cached tags for O(1) lookup instead of scanning all tags
162+
var candidates = tagCache.GetTagsForCommit(commit.Hash).
95163
Select(tag => Version.TryParse(tag.Name, out var v) ? v : null!).
96164
Where(v => v?.ComponentCount >= 2). // "1.2" or more.
97165
OrderByDescending(v => v).
@@ -130,7 +198,7 @@ private static async Task<Version> LookupVersionLabelRecursiveAsync(
130198
{
131199
for (var index = 1; index < pcs.Length; index++)
132200
{
133-
var v = await LookupVersionLabelRecursiveAsync(repository, pcs[index], reached, ct);
201+
var v = await LookupVersionLabelRecursiveAsync(repository, pcs[index], reached, tagCache, ct);
134202
if (v.CompareTo(version) > 0)
135203
{
136204
version = v;
@@ -149,8 +217,13 @@ private static async Task<Version> RunLookupVersionLabelAsync(
149217
PrimitiveRepository repository,
150218
PrimitiveReference branch, CancellationToken ct)
151219
{
152-
var headCommit = await repository.GetCommitAsync(branch, ct);
153-
return await LookupVersionLabelRecursiveAsync(repository, headCommit!.Value, new(), ct);
220+
// Build tag cache once for all lookups
221+
var (headCommit, tagCache) = await LooseConcurrentScope.Default.Join(
222+
repository.GetCommitAsync(branch, ct),
223+
TagCache.BuildAsync(repository, ct));
224+
225+
return await LookupVersionLabelRecursiveAsync(
226+
repository, headCommit!.Value, new(), tagCache, ct);
154227
}
155228

156229
public static async Task<Version> LookupVersionLabelAsync(

0 commit comments

Comments
 (0)