fork of go-git with some jj specific features
1package git
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io"
8
9 "gopkg.in/src-d/go-git.v4/config"
10 "gopkg.in/src-d/go-git.v4/plumbing"
11 "gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
12 "gopkg.in/src-d/go-git.v4/plumbing/object"
13 "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
14 "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
15 "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
16 "gopkg.in/src-d/go-git.v4/plumbing/revlist"
17 "gopkg.in/src-d/go-git.v4/plumbing/storer"
18 "gopkg.in/src-d/go-git.v4/plumbing/transport"
19 "gopkg.in/src-d/go-git.v4/plumbing/transport/client"
20 "gopkg.in/src-d/go-git.v4/storage"
21 "gopkg.in/src-d/go-git.v4/storage/memory"
22 "gopkg.in/src-d/go-git.v4/utils/ioutil"
23)
24
25var (
26 NoErrAlreadyUpToDate = errors.New("already up-to-date")
27 ErrDeleteRefNotSupported = errors.New("server does not support delete-refs")
28 ErrForceNeeded = errors.New("some refs were not updated")
29)
30
31const (
32 // This describes the maximum number of commits to walk when
33 // computing the haves to send to a server, for each ref in the
34 // repo containing this remote, when not using the multi-ack
35 // protocol. Setting this to 0 means there is no limit.
36 maxHavesToVisitPerRef = 100
37)
38
39// Remote represents a connection to a remote repository.
40type Remote struct {
41 c *config.RemoteConfig
42 s storage.Storer
43}
44
45func newRemote(s storage.Storer, c *config.RemoteConfig) *Remote {
46 return &Remote{s: s, c: c}
47}
48
49// Config returns the RemoteConfig object used to instantiate this Remote.
50func (r *Remote) Config() *config.RemoteConfig {
51 return r.c
52}
53
54func (r *Remote) String() string {
55 var fetch, push string
56 if len(r.c.URLs) > 0 {
57 fetch = r.c.URLs[0]
58 push = r.c.URLs[0]
59 }
60
61 return fmt.Sprintf("%s\t%s (fetch)\n%[1]s\t%[3]s (push)", r.c.Name, fetch, push)
62}
63
64// Push performs a push to the remote. Returns NoErrAlreadyUpToDate if the
65// remote was already up-to-date.
66func (r *Remote) Push(o *PushOptions) error {
67 return r.PushContext(context.Background(), o)
68}
69
70// PushContext performs a push to the remote. Returns NoErrAlreadyUpToDate if
71// the remote was already up-to-date.
72//
73// The provided Context must be non-nil. If the context expires before the
74// operation is complete, an error is returned. The context only affects to the
75// transport operations.
76func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
77 if err := o.Validate(); err != nil {
78 return err
79 }
80
81 if o.RemoteName != r.c.Name {
82 return fmt.Errorf("remote names don't match: %s != %s", o.RemoteName, r.c.Name)
83 }
84
85 s, err := newSendPackSession(r.c.URLs[0], o.Auth)
86 if err != nil {
87 return err
88 }
89
90 defer ioutil.CheckClose(s, &err)
91
92 ar, err := s.AdvertisedReferences()
93 if err != nil {
94 return err
95 }
96
97 remoteRefs, err := ar.AllReferences()
98 if err != nil {
99 return err
100 }
101
102 isDelete := false
103 allDelete := true
104 for _, rs := range o.RefSpecs {
105 if rs.IsDelete() {
106 isDelete = true
107 } else {
108 allDelete = false
109 }
110 if isDelete && !allDelete {
111 break
112 }
113 }
114
115 if isDelete && !ar.Capabilities.Supports(capability.DeleteRefs) {
116 return ErrDeleteRefNotSupported
117 }
118
119 localRefs, err := r.references()
120 if err != nil {
121 return err
122 }
123
124 req, err := r.newReferenceUpdateRequest(o, localRefs, remoteRefs, ar)
125 if err != nil {
126 return err
127 }
128
129 if len(req.Commands) == 0 {
130 return NoErrAlreadyUpToDate
131 }
132
133 objects := objectsToPush(req.Commands)
134
135 haves, err := referencesToHashes(remoteRefs)
136 if err != nil {
137 return err
138 }
139
140 stop, err := r.s.Shallow()
141 if err != nil {
142 return err
143 }
144
145 // if we have shallow we should include this as part of the objects that
146 // we are aware.
147 haves = append(haves, stop...)
148
149 var hashesToPush []plumbing.Hash
150 // Avoid the expensive revlist operation if we're only doing deletes.
151 if !allDelete {
152 hashesToPush, err = revlist.Objects(r.s, objects, haves)
153 if err != nil {
154 return err
155 }
156 }
157
158 rs, err := pushHashes(ctx, s, r.s, req, hashesToPush, r.useRefDeltas(ar))
159 if err != nil {
160 return err
161 }
162
163 if err = rs.Error(); err != nil {
164 return err
165 }
166
167 return r.updateRemoteReferenceStorage(req, rs)
168}
169
170func (r *Remote) useRefDeltas(ar *packp.AdvRefs) bool {
171 return !ar.Capabilities.Supports(capability.OFSDelta)
172}
173
174func (r *Remote) newReferenceUpdateRequest(
175 o *PushOptions,
176 localRefs []*plumbing.Reference,
177 remoteRefs storer.ReferenceStorer,
178 ar *packp.AdvRefs,
179) (*packp.ReferenceUpdateRequest, error) {
180 req := packp.NewReferenceUpdateRequestFromCapabilities(ar.Capabilities)
181
182 if o.Progress != nil {
183 req.Progress = o.Progress
184 if ar.Capabilities.Supports(capability.Sideband64k) {
185 req.Capabilities.Set(capability.Sideband64k)
186 } else if ar.Capabilities.Supports(capability.Sideband) {
187 req.Capabilities.Set(capability.Sideband)
188 }
189 }
190
191 if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req); err != nil {
192 return nil, err
193 }
194
195 return req, nil
196}
197
198func (r *Remote) updateRemoteReferenceStorage(
199 req *packp.ReferenceUpdateRequest,
200 result *packp.ReportStatus,
201) error {
202
203 for _, spec := range r.c.Fetch {
204 for _, c := range req.Commands {
205 if !spec.Match(c.Name) {
206 continue
207 }
208
209 local := spec.Dst(c.Name)
210 ref := plumbing.NewHashReference(local, c.New)
211 switch c.Action() {
212 case packp.Create, packp.Update:
213 if err := r.s.SetReference(ref); err != nil {
214 return err
215 }
216 case packp.Delete:
217 if err := r.s.RemoveReference(local); err != nil {
218 return err
219 }
220 }
221 }
222 }
223
224 return nil
225}
226
227// FetchContext fetches references along with the objects necessary to complete
228// their histories.
229//
230// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
231// no changes to be fetched, or an error.
232//
233// The provided Context must be non-nil. If the context expires before the
234// operation is complete, an error is returned. The context only affects to the
235// transport operations.
236func (r *Remote) FetchContext(ctx context.Context, o *FetchOptions) error {
237 _, err := r.fetch(ctx, o)
238 return err
239}
240
241// Fetch fetches references along with the objects necessary to complete their
242// histories.
243//
244// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
245// no changes to be fetched, or an error.
246func (r *Remote) Fetch(o *FetchOptions) error {
247 return r.FetchContext(context.Background(), o)
248}
249
250func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.ReferenceStorer, err error) {
251 if o.RemoteName == "" {
252 o.RemoteName = r.c.Name
253 }
254
255 if err = o.Validate(); err != nil {
256 return nil, err
257 }
258
259 if len(o.RefSpecs) == 0 {
260 o.RefSpecs = r.c.Fetch
261 }
262
263 s, err := newUploadPackSession(r.c.URLs[0], o.Auth)
264 if err != nil {
265 return nil, err
266 }
267
268 defer ioutil.CheckClose(s, &err)
269
270 ar, err := s.AdvertisedReferences()
271 if err != nil {
272 return nil, err
273 }
274
275 req, err := r.newUploadPackRequest(o, ar)
276 if err != nil {
277 return nil, err
278 }
279
280 remoteRefs, err := ar.AllReferences()
281 if err != nil {
282 return nil, err
283 }
284
285 localRefs, err := r.references()
286 if err != nil {
287 return nil, err
288 }
289
290 refs, err := calculateRefs(o.RefSpecs, remoteRefs, o.Tags)
291 if err != nil {
292 return nil, err
293 }
294
295 req.Wants, err = getWants(r.s, refs)
296 if len(req.Wants) > 0 {
297 req.Haves, err = getHaves(localRefs, remoteRefs, r.s)
298 if err != nil {
299 return nil, err
300 }
301
302 if err = r.fetchPack(ctx, o, s, req); err != nil {
303 return nil, err
304 }
305 }
306
307 updated, err := r.updateLocalReferenceStorage(o.RefSpecs, refs, remoteRefs, o.Tags, o.Force)
308 if err != nil {
309 return nil, err
310 }
311
312 if !updated {
313 return remoteRefs, NoErrAlreadyUpToDate
314 }
315
316 return remoteRefs, nil
317}
318
319func newUploadPackSession(url string, auth transport.AuthMethod) (transport.UploadPackSession, error) {
320 c, ep, err := newClient(url)
321 if err != nil {
322 return nil, err
323 }
324
325 return c.NewUploadPackSession(ep, auth)
326}
327
328func newSendPackSession(url string, auth transport.AuthMethod) (transport.ReceivePackSession, error) {
329 c, ep, err := newClient(url)
330 if err != nil {
331 return nil, err
332 }
333
334 return c.NewReceivePackSession(ep, auth)
335}
336
337func newClient(url string) (transport.Transport, *transport.Endpoint, error) {
338 ep, err := transport.NewEndpoint(url)
339 if err != nil {
340 return nil, nil, err
341 }
342
343 c, err := client.NewClient(ep)
344 if err != nil {
345 return nil, nil, err
346 }
347
348 return c, ep, err
349}
350
351func (r *Remote) fetchPack(ctx context.Context, o *FetchOptions, s transport.UploadPackSession,
352 req *packp.UploadPackRequest) (err error) {
353
354 reader, err := s.UploadPack(ctx, req)
355 if err != nil {
356 return err
357 }
358
359 defer ioutil.CheckClose(reader, &err)
360
361 if err = r.updateShallow(o, reader); err != nil {
362 return err
363 }
364
365 if err = packfile.UpdateObjectStorage(r.s,
366 buildSidebandIfSupported(req.Capabilities, reader, o.Progress),
367 ); err != nil {
368 return err
369 }
370
371 return err
372}
373
374func (r *Remote) addReferencesToUpdate(
375 refspecs []config.RefSpec,
376 localRefs []*plumbing.Reference,
377 remoteRefs storer.ReferenceStorer,
378 req *packp.ReferenceUpdateRequest,
379) error {
380 // This references dictionary will be used to search references by name.
381 refsDict := make(map[string]*plumbing.Reference)
382 for _, ref := range localRefs {
383 refsDict[ref.Name().String()] = ref
384 }
385
386 for _, rs := range refspecs {
387 if rs.IsDelete() {
388 if err := r.deleteReferences(rs, remoteRefs, req); err != nil {
389 return err
390 }
391 } else {
392 err := r.addOrUpdateReferences(rs, localRefs, refsDict, remoteRefs, req)
393 if err != nil {
394 return err
395 }
396 }
397 }
398
399 return nil
400}
401
402func (r *Remote) addOrUpdateReferences(
403 rs config.RefSpec,
404 localRefs []*plumbing.Reference,
405 refsDict map[string]*plumbing.Reference,
406 remoteRefs storer.ReferenceStorer,
407 req *packp.ReferenceUpdateRequest,
408) error {
409 // If it is not a wilcard refspec we can directly search for the reference
410 // in the references dictionary.
411 if !rs.IsWildcard() {
412 ref, ok := refsDict[rs.Src()]
413 if !ok {
414 return nil
415 }
416
417 return r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req)
418 }
419
420 for _, ref := range localRefs {
421 err := r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req)
422 if err != nil {
423 return err
424 }
425 }
426
427 return nil
428}
429
430func (r *Remote) deleteReferences(rs config.RefSpec,
431 remoteRefs storer.ReferenceStorer, req *packp.ReferenceUpdateRequest) error {
432 iter, err := remoteRefs.IterReferences()
433 if err != nil {
434 return err
435 }
436
437 return iter.ForEach(func(ref *plumbing.Reference) error {
438 if ref.Type() != plumbing.HashReference {
439 return nil
440 }
441
442 if rs.Dst("") != ref.Name() {
443 return nil
444 }
445
446 cmd := &packp.Command{
447 Name: ref.Name(),
448 Old: ref.Hash(),
449 New: plumbing.ZeroHash,
450 }
451 req.Commands = append(req.Commands, cmd)
452 return nil
453 })
454}
455
456func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
457 remoteRefs storer.ReferenceStorer, localRef *plumbing.Reference,
458 req *packp.ReferenceUpdateRequest) error {
459
460 if localRef.Type() != plumbing.HashReference {
461 return nil
462 }
463
464 if !rs.Match(localRef.Name()) {
465 return nil
466 }
467
468 cmd := &packp.Command{
469 Name: rs.Dst(localRef.Name()),
470 Old: plumbing.ZeroHash,
471 New: localRef.Hash(),
472 }
473
474 remoteRef, err := remoteRefs.Reference(cmd.Name)
475 if err == nil {
476 if remoteRef.Type() != plumbing.HashReference {
477 //TODO: check actual git behavior here
478 return nil
479 }
480
481 cmd.Old = remoteRef.Hash()
482 } else if err != plumbing.ErrReferenceNotFound {
483 return err
484 }
485
486 if cmd.Old == cmd.New {
487 return nil
488 }
489
490 if !rs.IsForceUpdate() {
491 if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil {
492 return err
493 }
494 }
495
496 req.Commands = append(req.Commands, cmd)
497 return nil
498}
499
500func (r *Remote) references() ([]*plumbing.Reference, error) {
501 var localRefs []*plumbing.Reference
502 iter, err := r.s.IterReferences()
503 if err != nil {
504 return nil, err
505 }
506
507 for {
508 ref, err := iter.Next()
509 if err == io.EOF {
510 break
511 }
512
513 if err != nil {
514 return nil, err
515 }
516
517 localRefs = append(localRefs, ref)
518 }
519
520 return localRefs, nil
521}
522
523func getRemoteRefsFromStorer(remoteRefStorer storer.ReferenceStorer) (
524 map[plumbing.Hash]bool, error) {
525 remoteRefs := map[plumbing.Hash]bool{}
526 iter, err := remoteRefStorer.IterReferences()
527 if err != nil {
528 return nil, err
529 }
530 err = iter.ForEach(func(ref *plumbing.Reference) error {
531 if ref.Type() != plumbing.HashReference {
532 return nil
533 }
534 remoteRefs[ref.Hash()] = true
535 return nil
536 })
537 if err != nil {
538 return nil, err
539 }
540 return remoteRefs, nil
541}
542
543// getHavesFromRef populates the given `haves` map with the given
544// reference, and up to `maxHavesToVisitPerRef` ancestor commits.
545func getHavesFromRef(
546 ref *plumbing.Reference,
547 remoteRefs map[plumbing.Hash]bool,
548 s storage.Storer,
549 haves map[plumbing.Hash]bool,
550) error {
551 h := ref.Hash()
552 if haves[h] {
553 return nil
554 }
555
556 // No need to load the commit if we know the remote already
557 // has this hash.
558 if remoteRefs[h] {
559 haves[h] = true
560 return nil
561 }
562
563 commit, err := object.GetCommit(s, h)
564 if err != nil {
565 // Ignore the error if this isn't a commit.
566 haves[ref.Hash()] = true
567 return nil
568 }
569
570 // Until go-git supports proper commit negotiation during an
571 // upload pack request, include up to `maxHavesToVisitPerRef`
572 // commits from the history of each ref.
573 walker := object.NewCommitPreorderIter(commit, haves, nil)
574 toVisit := maxHavesToVisitPerRef
575 return walker.ForEach(func(c *object.Commit) error {
576 haves[c.Hash] = true
577 toVisit--
578 // If toVisit starts out at 0 (indicating there is no
579 // max), then it will be negative here and we won't stop
580 // early.
581 if toVisit == 0 || remoteRefs[c.Hash] {
582 return storer.ErrStop
583 }
584 return nil
585 })
586}
587
588func getHaves(
589 localRefs []*plumbing.Reference,
590 remoteRefStorer storer.ReferenceStorer,
591 s storage.Storer,
592) ([]plumbing.Hash, error) {
593 haves := map[plumbing.Hash]bool{}
594
595 // Build a map of all the remote references, to avoid loading too
596 // many parent commits for references we know don't need to be
597 // transferred.
598 remoteRefs, err := getRemoteRefsFromStorer(remoteRefStorer)
599 if err != nil {
600 return nil, err
601 }
602
603 for _, ref := range localRefs {
604 if haves[ref.Hash()] {
605 continue
606 }
607
608 if ref.Type() != plumbing.HashReference {
609 continue
610 }
611
612 err = getHavesFromRef(ref, remoteRefs, s, haves)
613 if err != nil {
614 return nil, err
615 }
616 }
617
618 var result []plumbing.Hash
619 for h := range haves {
620 result = append(result, h)
621 }
622
623 return result, nil
624}
625
626const refspecAllTags = "+refs/tags/*:refs/tags/*"
627
628func calculateRefs(
629 spec []config.RefSpec,
630 remoteRefs storer.ReferenceStorer,
631 tagMode TagMode,
632) (memory.ReferenceStorage, error) {
633 if tagMode == AllTags {
634 spec = append(spec, refspecAllTags)
635 }
636
637 refs := make(memory.ReferenceStorage)
638 for _, s := range spec {
639 if err := doCalculateRefs(s, remoteRefs, refs); err != nil {
640 return nil, err
641 }
642 }
643
644 return refs, nil
645}
646
647func doCalculateRefs(
648 s config.RefSpec,
649 remoteRefs storer.ReferenceStorer,
650 refs memory.ReferenceStorage,
651) error {
652 iter, err := remoteRefs.IterReferences()
653 if err != nil {
654 return err
655 }
656
657 var matched bool
658 err = iter.ForEach(func(ref *plumbing.Reference) error {
659 if !s.Match(ref.Name()) {
660 return nil
661 }
662
663 if ref.Type() == plumbing.SymbolicReference {
664 target, err := storer.ResolveReference(remoteRefs, ref.Name())
665 if err != nil {
666 return err
667 }
668
669 ref = plumbing.NewHashReference(ref.Name(), target.Hash())
670 }
671
672 if ref.Type() != plumbing.HashReference {
673 return nil
674 }
675
676 matched = true
677 if err := refs.SetReference(ref); err != nil {
678 return err
679 }
680
681 if !s.IsWildcard() {
682 return storer.ErrStop
683 }
684
685 return nil
686 })
687
688 if !matched && !s.IsWildcard() {
689 return fmt.Errorf("couldn't find remote ref %q", s.Src())
690 }
691
692 return err
693}
694
695func getWants(localStorer storage.Storer, refs memory.ReferenceStorage) ([]plumbing.Hash, error) {
696 wants := map[plumbing.Hash]bool{}
697 for _, ref := range refs {
698 hash := ref.Hash()
699 exists, err := objectExists(localStorer, ref.Hash())
700 if err != nil {
701 return nil, err
702 }
703
704 if !exists {
705 wants[hash] = true
706 }
707 }
708
709 var result []plumbing.Hash
710 for h := range wants {
711 result = append(result, h)
712 }
713
714 return result, nil
715}
716
717func objectExists(s storer.EncodedObjectStorer, h plumbing.Hash) (bool, error) {
718 _, err := s.EncodedObject(plumbing.AnyObject, h)
719 if err == plumbing.ErrObjectNotFound {
720 return false, nil
721 }
722
723 return true, err
724}
725
726func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.ReferenceStorer, cmd *packp.Command) error {
727 if cmd.Old == plumbing.ZeroHash {
728 _, err := remoteRefs.Reference(cmd.Name)
729 if err == plumbing.ErrReferenceNotFound {
730 return nil
731 }
732
733 if err != nil {
734 return err
735 }
736
737 return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
738 }
739
740 ff, err := isFastForward(s, cmd.Old, cmd.New)
741 if err != nil {
742 return err
743 }
744
745 if !ff {
746 return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
747 }
748
749 return nil
750}
751
752func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) {
753 c, err := object.GetCommit(s, new)
754 if err != nil {
755 return false, err
756 }
757
758 found := false
759 iter := object.NewCommitPreorderIter(c, nil, nil)
760 err = iter.ForEach(func(c *object.Commit) error {
761 if c.Hash != old {
762 return nil
763 }
764
765 found = true
766 return storer.ErrStop
767 })
768 return found, err
769}
770
771func (r *Remote) newUploadPackRequest(o *FetchOptions,
772 ar *packp.AdvRefs) (*packp.UploadPackRequest, error) {
773
774 req := packp.NewUploadPackRequestFromCapabilities(ar.Capabilities)
775
776 if o.Depth != 0 {
777 req.Depth = packp.DepthCommits(o.Depth)
778 if err := req.Capabilities.Set(capability.Shallow); err != nil {
779 return nil, err
780 }
781 }
782
783 if o.Progress == nil && ar.Capabilities.Supports(capability.NoProgress) {
784 if err := req.Capabilities.Set(capability.NoProgress); err != nil {
785 return nil, err
786 }
787 }
788
789 isWildcard := true
790 for _, s := range o.RefSpecs {
791 if !s.IsWildcard() {
792 isWildcard = false
793 break
794 }
795 }
796
797 if isWildcard && o.Tags == TagFollowing && ar.Capabilities.Supports(capability.IncludeTag) {
798 if err := req.Capabilities.Set(capability.IncludeTag); err != nil {
799 return nil, err
800 }
801 }
802
803 return req, nil
804}
805
806func buildSidebandIfSupported(l *capability.List, reader io.Reader, p sideband.Progress) io.Reader {
807 var t sideband.Type
808
809 switch {
810 case l.Supports(capability.Sideband):
811 t = sideband.Sideband
812 case l.Supports(capability.Sideband64k):
813 t = sideband.Sideband64k
814 default:
815 return reader
816 }
817
818 d := sideband.NewDemuxer(t, reader)
819 d.Progress = p
820
821 return d
822}
823
824func (r *Remote) updateLocalReferenceStorage(
825 specs []config.RefSpec,
826 fetchedRefs, remoteRefs memory.ReferenceStorage,
827 tagMode TagMode,
828 force bool,
829) (updated bool, err error) {
830 isWildcard := true
831 forceNeeded := false
832
833 for _, spec := range specs {
834 if !spec.IsWildcard() {
835 isWildcard = false
836 }
837
838 for _, ref := range fetchedRefs {
839 if !spec.Match(ref.Name()) {
840 continue
841 }
842
843 if ref.Type() != plumbing.HashReference {
844 continue
845 }
846
847 localName := spec.Dst(ref.Name())
848 old, _ := storer.ResolveReference(r.s, localName)
849 new := plumbing.NewHashReference(localName, ref.Hash())
850
851 // If the ref exists locally as a branch and force is not specified,
852 // only update if the new ref is an ancestor of the old
853 if old != nil && old.Name().IsBranch() && !force && !spec.IsForceUpdate() {
854 ff, err := isFastForward(r.s, old.Hash(), new.Hash())
855 if err != nil {
856 return updated, err
857 }
858
859 if !ff {
860 forceNeeded = true
861 continue
862 }
863 }
864
865 refUpdated, err := checkAndUpdateReferenceStorerIfNeeded(r.s, new, old)
866 if err != nil {
867 return updated, err
868 }
869
870 if refUpdated {
871 updated = true
872 }
873 }
874 }
875
876 if tagMode == NoTags {
877 return updated, nil
878 }
879
880 tags := fetchedRefs
881 if isWildcard {
882 tags = remoteRefs
883 }
884 tagUpdated, err := r.buildFetchedTags(tags)
885 if err != nil {
886 return updated, err
887 }
888
889 if tagUpdated {
890 updated = true
891 }
892
893 if err == nil && forceNeeded {
894 err = ErrForceNeeded
895 }
896
897 return
898}
899
900func (r *Remote) buildFetchedTags(refs memory.ReferenceStorage) (updated bool, err error) {
901 for _, ref := range refs {
902 if !ref.Name().IsTag() {
903 continue
904 }
905
906 _, err := r.s.EncodedObject(plumbing.AnyObject, ref.Hash())
907 if err == plumbing.ErrObjectNotFound {
908 continue
909 }
910
911 if err != nil {
912 return false, err
913 }
914
915 refUpdated, err := updateReferenceStorerIfNeeded(r.s, ref)
916 if err != nil {
917 return updated, err
918 }
919
920 if refUpdated {
921 updated = true
922 }
923 }
924
925 return
926}
927
928// List the references on the remote repository.
929func (r *Remote) List(o *ListOptions) (rfs []*plumbing.Reference, err error) {
930 s, err := newUploadPackSession(r.c.URLs[0], o.Auth)
931 if err != nil {
932 return nil, err
933 }
934
935 defer ioutil.CheckClose(s, &err)
936
937 ar, err := s.AdvertisedReferences()
938 if err != nil {
939 return nil, err
940 }
941
942 allRefs, err := ar.AllReferences()
943 if err != nil {
944 return nil, err
945 }
946
947 refs, err := allRefs.IterReferences()
948 if err != nil {
949 return nil, err
950 }
951
952 var resultRefs []*plumbing.Reference
953 refs.ForEach(func(ref *plumbing.Reference) error {
954 resultRefs = append(resultRefs, ref)
955 return nil
956 })
957
958 return resultRefs, nil
959}
960
961func objectsToPush(commands []*packp.Command) []plumbing.Hash {
962 var objects []plumbing.Hash
963 for _, cmd := range commands {
964 if cmd.New == plumbing.ZeroHash {
965 continue
966 }
967
968 objects = append(objects, cmd.New)
969 }
970 return objects
971}
972
973func referencesToHashes(refs storer.ReferenceStorer) ([]plumbing.Hash, error) {
974 iter, err := refs.IterReferences()
975 if err != nil {
976 return nil, err
977 }
978
979 var hs []plumbing.Hash
980 err = iter.ForEach(func(ref *plumbing.Reference) error {
981 if ref.Type() != plumbing.HashReference {
982 return nil
983 }
984
985 hs = append(hs, ref.Hash())
986 return nil
987 })
988 if err != nil {
989 return nil, err
990 }
991
992 return hs, nil
993}
994
995func pushHashes(
996 ctx context.Context,
997 sess transport.ReceivePackSession,
998 s storage.Storer,
999 req *packp.ReferenceUpdateRequest,
1000 hs []plumbing.Hash,
1001 useRefDeltas bool,
1002) (*packp.ReportStatus, error) {
1003
1004 rd, wr := io.Pipe()
1005 req.Packfile = rd
1006 config, err := s.Config()
1007 if err != nil {
1008 return nil, err
1009 }
1010 done := make(chan error)
1011 go func() {
1012 e := packfile.NewEncoder(wr, s, useRefDeltas)
1013 if _, err := e.Encode(hs, config.Pack.Window); err != nil {
1014 done <- wr.CloseWithError(err)
1015 return
1016 }
1017
1018 done <- wr.Close()
1019 }()
1020
1021 rs, err := sess.ReceivePack(ctx, req)
1022 if err != nil {
1023 return nil, err
1024 }
1025
1026 if err := <-done; err != nil {
1027 return nil, err
1028 }
1029
1030 return rs, nil
1031}
1032
1033func (r *Remote) updateShallow(o *FetchOptions, resp *packp.UploadPackResponse) error {
1034 if o.Depth == 0 || len(resp.Shallows) == 0 {
1035 return nil
1036 }
1037
1038 shallows, err := r.s.Shallow()
1039 if err != nil {
1040 return err
1041 }
1042
1043outer:
1044 for _, s := range resp.Shallows {
1045 for _, oldS := range shallows {
1046 if s == oldS {
1047 continue outer
1048 }
1049 }
1050 shallows = append(shallows, s)
1051 }
1052
1053 return r.s.SetShallow(shallows)
1054}