@@ -12,7 +12,9 @@ import (
1212 "net/url"
1313 "os"
1414 "path/filepath"
15+ "sort"
1516 "strings"
17+ "time"
1618
1719 "github.com/go-git/go-git/v5/plumbing"
1820
@@ -54,6 +56,8 @@ type pushService struct {
5456 force bool
5557 pushSSH bool
5658 gitURL string
59+ maxTags int
60+ includeBranches bool
5761}
5862
5963func (pushService * pushService ) createRepository () (* github.Repository , error ) {
@@ -175,6 +179,103 @@ func splitLargeRefSpecs(refSpecs []config.RefSpec) [][]config.RefSpec {
175179 return splitRefSpecs
176180}
177181
182+ // Returns the most recent tags up to maxTags limit. If maxTags is 0, returns all tags.
183+ func (pushService * pushService ) getAllowedTagsMap (gitRepository * git.Repository ) (map [string ]bool , error ) {
184+ allowedTags := make (map [string ]bool )
185+ if pushService .maxTags == 0 {
186+ tags , err := gitRepository .Tags ()
187+ if err != nil {
188+ return nil , errors .Wrap (err , "Error listing tags." )
189+ }
190+
191+ err = tags .ForEach (func (ref * plumbing.Reference ) error {
192+ allowedTags [ref .Name ().Short ()] = true
193+ return nil
194+ })
195+ return allowedTags , err
196+ }
197+
198+ type tagInfo struct {
199+ name string
200+ time time.Time
201+ }
202+
203+ var tagInfos []tagInfo
204+ tags , err := gitRepository .Tags ()
205+ if err != nil {
206+ return nil , errors .Wrap (err , "Error listing tags." )
207+ }
208+
209+ err = tags .ForEach (func (ref * plumbing.Reference ) error {
210+ var tagTime time.Time
211+
212+ // First try to get it as an annotated tag
213+ tagObj , err := gitRepository .TagObject (ref .Hash ())
214+ if err == nil {
215+ // It's an annotated tag, use the tag date
216+ tagTime = tagObj .Tagger .When
217+ } else {
218+ // It's a lightweight tag, get the commit it points to
219+ commit , err := gitRepository .CommitObject (ref .Hash ())
220+ if err != nil {
221+ // If we can't get the commit, use zero time (will be filtered out)
222+ tagTime = time.Time {}
223+ } else {
224+ tagTime = commit .Committer .When
225+ }
226+ }
227+
228+ tagInfos = append (tagInfos , tagInfo {
229+ name : ref .Name ().Short (),
230+ time : tagTime ,
231+ })
232+ return nil
233+ })
234+
235+ if err != nil {
236+ return nil , err
237+ }
238+
239+ sort .Slice (tagInfos , func (i , j int ) bool {
240+ return tagInfos [i ].time .After (tagInfos [j ].time ) // most recent first
241+ })
242+
243+ limit := pushService .maxTags
244+ if limit > len (tagInfos ) {
245+ limit = len (tagInfos )
246+ }
247+
248+ for i := 0 ; i < limit ; i ++ {
249+ allowedTags [tagInfos [i ].name ] = true
250+ }
251+
252+ return allowedTags , nil
253+ }
254+
255+ // Determines if a reference should be included based on filtering rules:
256+ // - For tags, only includes those in the allowedTags set.
257+ // - Always includes the main branch.
258+ // - For other branches, includes them only if includeBranches is enabled.
259+ func (pushService * pushService ) shouldIncludeReference (ref * plumbing.Reference , allowedTags map [string ]bool ) bool {
260+ refName := ref .Name ().String ()
261+
262+ if refName == "refs/heads/main" {
263+ return true
264+ }
265+
266+ if strings .HasPrefix (refName , "refs/tags/" ) {
267+ tagName := ref .Name ().Short ()
268+ return allowedTags [tagName ]
269+ }
270+
271+ if strings .HasPrefix (refName , "refs/heads/" ) {
272+ return pushService .includeBranches
273+ }
274+
275+ // No other refs are currently pulled, but include any that might in the future to be safe.
276+ return true
277+ }
278+
178279func (pushService * pushService ) pushGit (repository * github.Repository , initialPush bool ) error {
179280 remoteURL := pushService .gitURL
180281 if remoteURL == "" {
@@ -231,14 +332,22 @@ func (pushService *pushService) pushGit(repository *github.Repository, initialPu
231332 if err != nil {
232333 return errors .Wrap (err , "Error reading releases." )
233334 }
335+
336+ allowedTags , err := pushService .getAllowedTagsMap (gitRepository )
337+ if err != nil {
338+ return err
339+ }
340+
234341 initialRefSpecs := []config.RefSpec {}
235342 for _ , releasePathStat := range releasePathStats {
236- tagReferenceName := plumbing .NewTagReferenceName (releasePathStat .Name ())
237- _ , err := gitRepository .Reference (tagReferenceName , true )
238- if err != nil {
239- return errors .Wrapf (err , "Error finding local tag reference %s." , tagReferenceName )
343+ if allowedTags [releasePathStat .Name ()] {
344+ tagReferenceName := plumbing .NewTagReferenceName (releasePathStat .Name ())
345+ _ , err := gitRepository .Reference (tagReferenceName , true )
346+ if err != nil {
347+ return errors .Wrapf (err , "Error finding local tag reference %s." , tagReferenceName )
348+ }
349+ initialRefSpecs = append (initialRefSpecs , config .RefSpec ("+" + tagReferenceName .String ()+ ":" + tagReferenceName .String ()))
240350 }
241- initialRefSpecs = append (initialRefSpecs , config .RefSpec ("+" + tagReferenceName .String ()+ ":" + tagReferenceName .String ()))
242351 }
243352 refSpecBatches = append (refSpecBatches , splitLargeRefSpecs (initialRefSpecs )... )
244353 } else {
@@ -248,14 +357,22 @@ func (pushService *pushService) pushGit(repository *github.Repository, initialPu
248357 config .RefSpec (defaultBranchRefSpec ),
249358 },
250359 )
360+
361+ allowedTags , err := pushService .getAllowedTagsMap (gitRepository )
362+ if err != nil {
363+ return err
364+ }
365+
251366 nonDefaultRefSpecs := []config.RefSpec {}
252367 localReferences , err := gitRepository .References ()
253368 if err != nil {
254369 return errors .Wrap (err , "Error listing local references." )
255370 }
256371 localReferences .ForEach (func (ref * plumbing.Reference ) error {
257372 if ref .Name ().String () != defaultBranchRef && strings .HasPrefix (ref .Name ().String (), "refs/" ) {
258- nonDefaultRefSpecs = append (nonDefaultRefSpecs , config .RefSpec ("+" + ref .Name ().String ()+ ":" + ref .Name ().String ()))
373+ if pushService .shouldIncludeReference (ref , allowedTags ) {
374+ nonDefaultRefSpecs = append (nonDefaultRefSpecs , config .RefSpec ("+" + ref .Name ().String ()+ ":" + ref .Name ().String ()))
375+ }
259376 }
260377 return nil
261378 })
@@ -432,7 +549,7 @@ func (pushService *pushService) pushReleases() error {
432549 return nil
433550}
434551
435- func Push (ctx context.Context , cacheDirectory cachedirectory.CacheDirectory , destinationURL string , destinationToken string , destinationRepository string , actionsAdminUser string , force bool , pushSSH bool , gitURL string ) error {
552+ func Push (ctx context.Context , cacheDirectory cachedirectory.CacheDirectory , destinationURL string , destinationToken string , destinationRepository string , actionsAdminUser string , force bool , pushSSH bool , gitURL string , maxTags int , includeBranches bool ) error {
436553 err := cacheDirectory .CheckOrCreateVersionFile (false , version .Version ())
437554 if err != nil {
438555 return err
@@ -491,6 +608,8 @@ func Push(ctx context.Context, cacheDirectory cachedirectory.CacheDirectory, des
491608 force : force ,
492609 pushSSH : pushSSH ,
493610 gitURL : gitURL ,
611+ maxTags : maxTags ,
612+ includeBranches : includeBranches ,
494613 }
495614
496615 repository , err := pushService .createRepository ()
0 commit comments