55 "errors"
66 "fmt"
77 "io"
8+ "strings"
89 "time"
910
1011 "github.com/go-git/go-billy/v5/osfs"
@@ -225,6 +226,77 @@ func (r *Remote) useRefDeltas(ar *packp.AdvRefs) bool {
225226 return ! ar .Capabilities .Supports (capability .OFSDelta )
226227}
227228
229+ func (r * Remote ) addReachableTags (localRefs []* plumbing.Reference , remoteRefs storer.ReferenceStorer , req * packp.ReferenceUpdateRequest ) error {
230+ tags := make (map [plumbing.Reference ]struct {})
231+ // get a list of all tags locally
232+ for _ , ref := range localRefs {
233+ if strings .HasPrefix (string (ref .Name ()), "refs/tags" ) {
234+ tags [* ref ] = struct {}{}
235+ }
236+ }
237+
238+ remoteRefIter , err := remoteRefs .IterReferences ()
239+ if err != nil {
240+ return err
241+ }
242+
243+ // remove any that are already on the remote
244+ if err := remoteRefIter .ForEach (func (reference * plumbing.Reference ) error {
245+ if _ , ok := tags [* reference ]; ok {
246+ delete (tags , * reference )
247+ }
248+
249+ return nil
250+ }); err != nil {
251+ return err
252+ }
253+
254+ for tag , _ := range tags {
255+ tagObject , err := object .GetObject (r .s , tag .Hash ())
256+ var tagCommit * object.Commit
257+ if err != nil {
258+ return fmt .Errorf ("get tag object: %w\n " , err )
259+ }
260+
261+ if tagObject .Type () != plumbing .TagObject {
262+ continue
263+ }
264+
265+ annotatedTag , ok := tagObject .(* object.Tag )
266+ if ! ok {
267+ return errors .New ("could not get annotated tag object" )
268+ }
269+
270+ tagCommit , err = object .GetCommit (r .s , annotatedTag .Target )
271+ if err != nil {
272+ return fmt .Errorf ("get annotated tag commit: %w\n " , err )
273+ }
274+
275+ // only include tags that are reachable from one of the refs
276+ // already being pushed
277+ for _ , cmd := range req .Commands {
278+ if tag .Name () == cmd .Name {
279+ continue
280+ }
281+
282+ if strings .HasPrefix (cmd .Name .String (), "refs/tags" ) {
283+ continue
284+ }
285+
286+ c , err := object .GetCommit (r .s , cmd .New )
287+ if err != nil {
288+ return fmt .Errorf ("get commit %v: %w" , cmd .Name , err )
289+ }
290+
291+ if isAncestor , err := tagCommit .IsAncestor (c ); err == nil && isAncestor {
292+ req .Commands = append (req .Commands , & packp.Command {Name : tag .Name (), New : tag .Hash ()})
293+ }
294+ }
295+ }
296+
297+ return nil
298+ }
299+
228300func (r * Remote ) newReferenceUpdateRequest (
229301 o * PushOptions ,
230302 localRefs []* plumbing.Reference ,
@@ -246,6 +318,12 @@ func (r *Remote) newReferenceUpdateRequest(
246318 return nil , err
247319 }
248320
321+ if o .FollowTags {
322+ if err := r .addReachableTags (localRefs , remoteRefs , req ); err != nil {
323+ return nil , err
324+ }
325+ }
326+
249327 return req , nil
250328}
251329
0 commit comments