77//
88////////////////////////////////////////////////////////////////////////////////////////
99
10+ using System ;
1011using System . Collections . Generic ;
1112using System . Linq ;
1213using System . Threading ;
1314using System . Threading . Tasks ;
15+ using GitReader ;
1416using GitReader . IO ;
1517using GitReader . Primitive ;
1618
1719namespace RelaxVersioner ;
1820
1921internal 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