Skip to content

Commit d3c7400

Browse files
authored
Merge pull request #485 from mcuadros/fetch-tags
remote: fetch, correct behavior on tags
2 parents 046b15e + cbdb258 commit d3c7400

File tree

3 files changed

+205
-109
lines changed

3 files changed

+205
-109
lines changed

options.go

+15
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ func (o *PullOptions) Validate() error {
103103
return nil
104104
}
105105

106+
type TagFetchMode int
107+
108+
var (
109+
// TagFollowing any tag that points into the histories being fetched is also
110+
// fetched. TagFollowing requires a server with `include-tag` capability
111+
// in order to fetch the annotated tags objects.
112+
TagFollowing TagFetchMode = 0
113+
// AllTags fetch all tags from the remote (i.e., fetch remote tags
114+
// refs/tags/* into local tags with the same name)
115+
AllTags TagFetchMode = 1
116+
)
117+
106118
// FetchOptions describes how a fetch should be performed
107119
type FetchOptions struct {
108120
// Name of the remote to fetch from. Defaults to origin.
@@ -117,6 +129,9 @@ type FetchOptions struct {
117129
// stored, if nil nothing is stored and the capability (if supported)
118130
// no-progress, is sent to the server to avoid send this information.
119131
Progress sideband.Progress
132+
// Tags describe how the tags will be fetched from the remote repository,
133+
// by default is TagFollowing.
134+
Tags TagFetchMode
120135
}
121136

122137
// Validate validates the fields and sets the default values.

remote.go

+83-56
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func (r *Remote) Push(o *PushOptions) (err error) {
134134
return rs.Error()
135135
}
136136

137-
func (r *Remote) fetch(o *FetchOptions) (refs storer.ReferenceStorer, err error) {
137+
func (r *Remote) fetch(o *FetchOptions) (storer.ReferenceStorer, error) {
138138
if o.RemoteName == "" {
139139
o.RemoteName = r.c.Name
140140
}
@@ -169,7 +169,12 @@ func (r *Remote) fetch(o *FetchOptions) (refs storer.ReferenceStorer, err error)
169169
return nil, err
170170
}
171171

172-
req.Wants, err = getWants(o.RefSpecs, r.s, remoteRefs)
172+
refs, err := calculateRefs(o.RefSpecs, remoteRefs, o.Tags)
173+
if err != nil {
174+
return nil, err
175+
}
176+
177+
req.Wants, err = getWants(r.s, refs)
173178
if len(req.Wants) > 0 {
174179
req.Haves, err = getHaves(r.s)
175180
if err != nil {
@@ -181,14 +186,15 @@ func (r *Remote) fetch(o *FetchOptions) (refs storer.ReferenceStorer, err error)
181186
}
182187
}
183188

184-
err = r.updateLocalReferenceStorage(o.RefSpecs, remoteRefs)
185-
if err != nil && err != NoErrAlreadyUpToDate {
189+
updated, err := r.updateLocalReferenceStorage(o.RefSpecs, refs, remoteRefs)
190+
if err != nil {
186191
return nil, err
187192
}
188193

189-
if len(req.Wants) == 0 {
190-
return remoteRefs, err
194+
if !updated {
195+
return remoteRefs, NoErrAlreadyUpToDate
191196
}
197+
192198
return remoteRefs, nil
193199
}
194200

@@ -382,56 +388,52 @@ func getHaves(localRefs storer.ReferenceStorer) ([]plumbing.Hash, error) {
382388
return result, nil
383389
}
384390

385-
func getWants(
386-
spec []config.RefSpec, localStorer storage.Storer, remoteRefs storer.ReferenceStorer,
387-
) ([]plumbing.Hash, error) {
388-
wantTags := true
389-
for _, s := range spec {
390-
if !s.IsWildcard() {
391-
wantTags = false
392-
break
393-
}
394-
}
395-
391+
func calculateRefs(spec []config.RefSpec,
392+
remoteRefs storer.ReferenceStorer,
393+
tags TagFetchMode,
394+
) (memory.ReferenceStorage, error) {
396395
iter, err := remoteRefs.IterReferences()
397396
if err != nil {
398397
return nil, err
399398
}
400399

401-
wants := map[plumbing.Hash]bool{}
402-
err = iter.ForEach(func(ref *plumbing.Reference) error {
400+
refs := make(memory.ReferenceStorage, 0)
401+
return refs, iter.ForEach(func(ref *plumbing.Reference) error {
403402
if !config.MatchAny(spec, ref.Name()) {
404-
if !ref.IsTag() || !wantTags {
403+
if !ref.IsTag() || tags != AllTags {
405404
return nil
406405
}
407406
}
408407

409408
if ref.Type() == plumbing.SymbolicReference {
410-
ref, err = storer.ResolveReference(remoteRefs, ref.Name())
409+
target, err := storer.ResolveReference(remoteRefs, ref.Name())
411410
if err != nil {
412411
return err
413412
}
413+
414+
ref = plumbing.NewHashReference(ref.Name(), target.Hash())
414415
}
415416

416417
if ref.Type() != plumbing.HashReference {
417418
return nil
418419
}
419420

420-
hash := ref.Hash()
421+
return refs.SetReference(ref)
422+
})
423+
}
421424

422-
exists, err := objectExists(localStorer, hash)
425+
func getWants(localStorer storage.Storer, refs memory.ReferenceStorage) ([]plumbing.Hash, error) {
426+
wants := map[plumbing.Hash]bool{}
427+
for _, ref := range refs {
428+
hash := ref.Hash()
429+
exists, err := objectExists(localStorer, ref.Hash())
423430
if err != nil {
424-
return err
431+
return nil, err
425432
}
426433

427434
if !exists {
428435
wants[hash] = true
429436
}
430-
431-
return nil
432-
})
433-
if err != nil {
434-
return nil, err
435437
}
436438

437439
var result []plumbing.Hash
@@ -513,6 +515,19 @@ func (r *Remote) newUploadPackRequest(o *FetchOptions,
513515
}
514516
}
515517

518+
isWildcard := true
519+
for _, s := range o.RefSpecs {
520+
if !s.IsWildcard() {
521+
isWildcard = false
522+
}
523+
}
524+
525+
if isWildcard && o.Tags == TagFollowing && ar.Capabilities.Supports(capability.IncludeTag) {
526+
if err := req.Capabilities.Set(capability.IncludeTag); err != nil {
527+
return nil, err
528+
}
529+
}
530+
516531
return req, nil
517532
}
518533

@@ -534,10 +549,17 @@ func buildSidebandIfSupported(l *capability.List, reader io.Reader, p sideband.P
534549
return d
535550
}
536551

537-
func (r *Remote) updateLocalReferenceStorage(specs []config.RefSpec, refs memory.ReferenceStorage) error {
538-
updated := false
552+
func (r *Remote) updateLocalReferenceStorage(
553+
specs []config.RefSpec,
554+
fetchedRefs, remoteRefs memory.ReferenceStorage,
555+
) (updated bool, err error) {
556+
isWildcard := true
539557
for _, spec := range specs {
540-
for _, ref := range refs {
558+
if !spec.IsWildcard() {
559+
isWildcard = false
560+
}
561+
562+
for _, ref := range fetchedRefs {
541563
if !spec.Match(ref.Name()) {
542564
continue
543565
}
@@ -546,33 +568,36 @@ func (r *Remote) updateLocalReferenceStorage(specs []config.RefSpec, refs memory
546568
continue
547569
}
548570

549-
name := spec.Dst(ref.Name())
550-
sref, err := r.s.Reference(name)
551-
if err != nil && err != plumbing.ErrReferenceNotFound {
552-
return err
571+
new := plumbing.NewHashReference(spec.Dst(ref.Name()), ref.Hash())
572+
573+
refUpdated, err := updateReferenceStorerIfNeeded(r.s, new)
574+
if err != nil {
575+
return updated, err
553576
}
554-
if err == plumbing.ErrReferenceNotFound || sref.Hash() != ref.Hash() {
555-
n := plumbing.NewHashReference(name, ref.Hash())
556-
if err := r.s.SetReference(n); err != nil {
557-
return err
558-
}
577+
578+
if refUpdated {
559579
updated = true
560580
}
561581
}
562582
}
563583

564-
if err := r.buildFetchedTags(refs); err != nil {
565-
return err
584+
tags := fetchedRefs
585+
if isWildcard {
586+
tags = remoteRefs
587+
}
588+
tagUpdated, err := r.buildFetchedTags(tags)
589+
if err != nil {
590+
return updated, err
566591
}
567592

568-
if !updated {
569-
return NoErrAlreadyUpToDate
593+
if tagUpdated {
594+
updated = true
570595
}
571-
return nil
596+
597+
return
572598
}
573599

574-
func (r *Remote) buildFetchedTags(refs memory.ReferenceStorage) error {
575-
updated := false
600+
func (r *Remote) buildFetchedTags(refs memory.ReferenceStorage) (updated bool, err error) {
576601
for _, ref := range refs {
577602
if !ref.IsTag() {
578603
continue
@@ -584,18 +609,20 @@ func (r *Remote) buildFetchedTags(refs memory.ReferenceStorage) error {
584609
}
585610

586611
if err != nil {
587-
return err
612+
return false, err
588613
}
589614

590-
if err = r.s.SetReference(ref); err != nil {
591-
return err
615+
refUpdated, err := updateReferenceStorerIfNeeded(r.s, ref)
616+
if err != nil {
617+
return updated, err
618+
}
619+
620+
if refUpdated {
621+
updated = true
592622
}
593-
updated = true
594-
}
595-
if !updated {
596-
return NoErrAlreadyUpToDate
597623
}
598-
return nil
624+
625+
return
599626
}
600627

601628
func objectsToPush(commands []*packp.Command) ([]plumbing.Hash, error) {

0 commit comments

Comments
 (0)