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) 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)
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) newReferenceUpdateRequest(
171 o *PushOptions,
172 localRefs []*plumbing.Reference,
173 remoteRefs storer.ReferenceStorer,
174 ar *packp.AdvRefs,
175) (*packp.ReferenceUpdateRequest, error) {
176 req := packp.NewReferenceUpdateRequestFromCapabilities(ar.Capabilities)
177
178 if o.Progress != nil {
179 req.Progress = o.Progress
180 if ar.Capabilities.Supports(capability.Sideband64k) {
181 req.Capabilities.Set(capability.Sideband64k)
182 } else if ar.Capabilities.Supports(capability.Sideband) {
183 req.Capabilities.Set(capability.Sideband)
184 }
185 }
186
187 if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req); err != nil {
188 return nil, err
189 }
190
191 return req, nil
192}
193
194func (r *Remote) updateRemoteReferenceStorage(
195 req *packp.ReferenceUpdateRequest,
196 result *packp.ReportStatus,
197) error {
198
199 for _, spec := range r.c.Fetch {
200 for _, c := range req.Commands {
201 if !spec.Match(c.Name) {
202 continue
203 }
204
205 local := spec.Dst(c.Name)
206 ref := plumbing.NewHashReference(local, c.New)
207 switch c.Action() {
208 case packp.Create, packp.Update:
209 if err := r.s.SetReference(ref); err != nil {
210 return err
211 }
212 case packp.Delete:
213 if err := r.s.RemoveReference(local); err != nil {
214 return err
215 }
216 }
217 }
218 }
219
220 return nil
221}
222
223// FetchContext fetches references along with the objects necessary to complete
224// their histories.
225//
226// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
227// no changes to be fetched, or an error.
228//
229// The provided Context must be non-nil. If the context expires before the
230// operation is complete, an error is returned. The context only affects to the
231// transport operations.
232func (r *Remote) FetchContext(ctx context.Context, o *FetchOptions) error {
233 _, err := r.fetch(ctx, o)
234 return err
235}
236
237// Fetch fetches references along with the objects necessary to complete their
238// histories.
239//
240// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
241// no changes to be fetched, or an error.
242func (r *Remote) Fetch(o *FetchOptions) error {
243 return r.FetchContext(context.Background(), o)
244}
245
246func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (storer.ReferenceStorer, error) {
247 if o.RemoteName == "" {
248 o.RemoteName = r.c.Name
249 }
250
251 if err := o.Validate(); err != nil {
252 return nil, err
253 }
254
255 if len(o.RefSpecs) == 0 {
256 o.RefSpecs = r.c.Fetch
257 }
258
259 s, err := newUploadPackSession(r.c.URLs[0], o.Auth)
260 if err != nil {
261 return nil, err
262 }
263
264 defer ioutil.CheckClose(s, &err)
265
266 ar, err := s.AdvertisedReferences()
267 if err != nil {
268 return nil, err
269 }
270
271 req, err := r.newUploadPackRequest(o, ar)
272 if err != nil {
273 return nil, err
274 }
275
276 remoteRefs, err := ar.AllReferences()
277 if err != nil {
278 return nil, err
279 }
280
281 localRefs, err := r.references()
282 if err != nil {
283 return nil, err
284 }
285
286 refs, err := calculateRefs(o.RefSpecs, remoteRefs, o.Tags)
287 if err != nil {
288 return nil, err
289 }
290
291 req.Wants, err = getWants(r.s, refs)
292 if len(req.Wants) > 0 {
293 req.Haves, err = getHaves(localRefs, remoteRefs, r.s)
294 if err != nil {
295 return nil, err
296 }
297
298 if err := r.fetchPack(ctx, o, s, req); err != nil {
299 return nil, err
300 }
301 }
302
303 updated, err := r.updateLocalReferenceStorage(o.RefSpecs, refs, remoteRefs, o.Tags, o.Force)
304 if err != nil {
305 return nil, err
306 }
307
308 if !updated {
309 return remoteRefs, NoErrAlreadyUpToDate
310 }
311
312 return remoteRefs, nil
313}
314
315func newUploadPackSession(url string, auth transport.AuthMethod) (transport.UploadPackSession, error) {
316 c, ep, err := newClient(url)
317 if err != nil {
318 return nil, err
319 }
320
321 return c.NewUploadPackSession(ep, auth)
322}
323
324func newSendPackSession(url string, auth transport.AuthMethod) (transport.ReceivePackSession, error) {
325 c, ep, err := newClient(url)
326 if err != nil {
327 return nil, err
328 }
329
330 return c.NewReceivePackSession(ep, auth)
331}
332
333func newClient(url string) (transport.Transport, *transport.Endpoint, error) {
334 ep, err := transport.NewEndpoint(url)
335 if err != nil {
336 return nil, nil, err
337 }
338
339 c, err := client.NewClient(ep)
340 if err != nil {
341 return nil, nil, err
342 }
343
344 return c, ep, err
345}
346
347func (r *Remote) fetchPack(ctx context.Context, o *FetchOptions, s transport.UploadPackSession,
348 req *packp.UploadPackRequest) (err error) {
349
350 reader, err := s.UploadPack(ctx, req)
351 if err != nil {
352 return err
353 }
354
355 defer ioutil.CheckClose(reader, &err)
356
357 if err := r.updateShallow(o, reader); err != nil {
358 return err
359 }
360
361 if err = packfile.UpdateObjectStorage(r.s,
362 buildSidebandIfSupported(req.Capabilities, reader, o.Progress),
363 ); err != nil {
364 return err
365 }
366
367 return err
368}
369
370func (r *Remote) addReferencesToUpdate(
371 refspecs []config.RefSpec,
372 localRefs []*plumbing.Reference,
373 remoteRefs storer.ReferenceStorer,
374 req *packp.ReferenceUpdateRequest) error {
375 for _, rs := range refspecs {
376 if rs.IsDelete() {
377 if err := r.deleteReferences(rs, remoteRefs, req); err != nil {
378 return err
379 }
380 } else {
381 if err := r.addOrUpdateReferences(rs, localRefs, remoteRefs, req); err != nil {
382 return err
383 }
384 }
385 }
386
387 return nil
388}
389
390func (r *Remote) addOrUpdateReferences(
391 rs config.RefSpec,
392 localRefs []*plumbing.Reference,
393 remoteRefs storer.ReferenceStorer,
394 req *packp.ReferenceUpdateRequest,
395) error {
396 for _, ref := range localRefs {
397 err := r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req)
398 if err != nil {
399 return err
400 }
401 }
402
403 return nil
404}
405
406func (r *Remote) deleteReferences(rs config.RefSpec,
407 remoteRefs storer.ReferenceStorer, req *packp.ReferenceUpdateRequest) error {
408 iter, err := remoteRefs.IterReferences()
409 if err != nil {
410 return err
411 }
412
413 return iter.ForEach(func(ref *plumbing.Reference) error {
414 if ref.Type() != plumbing.HashReference {
415 return nil
416 }
417
418 if rs.Dst("") != ref.Name() {
419 return nil
420 }
421
422 cmd := &packp.Command{
423 Name: ref.Name(),
424 Old: ref.Hash(),
425 New: plumbing.ZeroHash,
426 }
427 req.Commands = append(req.Commands, cmd)
428 return nil
429 })
430}
431
432func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
433 remoteRefs storer.ReferenceStorer, localRef *plumbing.Reference,
434 req *packp.ReferenceUpdateRequest) error {
435
436 if localRef.Type() != plumbing.HashReference {
437 return nil
438 }
439
440 if !rs.Match(localRef.Name()) {
441 return nil
442 }
443
444 cmd := &packp.Command{
445 Name: rs.Dst(localRef.Name()),
446 Old: plumbing.ZeroHash,
447 New: localRef.Hash(),
448 }
449
450 remoteRef, err := remoteRefs.Reference(cmd.Name)
451 if err == nil {
452 if remoteRef.Type() != plumbing.HashReference {
453 //TODO: check actual git behavior here
454 return nil
455 }
456
457 cmd.Old = remoteRef.Hash()
458 } else if err != plumbing.ErrReferenceNotFound {
459 return err
460 }
461
462 if cmd.Old == cmd.New {
463 return nil
464 }
465
466 if !rs.IsForceUpdate() {
467 if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil {
468 return err
469 }
470 }
471
472 req.Commands = append(req.Commands, cmd)
473 return nil
474}
475
476func (r *Remote) references() ([]*plumbing.Reference, error) {
477 var localRefs []*plumbing.Reference
478 iter, err := r.s.IterReferences()
479 if err != nil {
480 return nil, err
481 }
482
483 for {
484 ref, err := iter.Next()
485 if err == io.EOF {
486 break
487 }
488
489 if err != nil {
490 return nil, err
491 }
492
493 localRefs = append(localRefs, ref)
494 }
495
496 return localRefs, nil
497}
498
499func getRemoteRefsFromStorer(remoteRefStorer storer.ReferenceStorer) (
500 map[plumbing.Hash]bool, error) {
501 remoteRefs := map[plumbing.Hash]bool{}
502 iter, err := remoteRefStorer.IterReferences()
503 if err != nil {
504 return nil, err
505 }
506 err = iter.ForEach(func(ref *plumbing.Reference) error {
507 if ref.Type() != plumbing.HashReference {
508 return nil
509 }
510 remoteRefs[ref.Hash()] = true
511 return nil
512 })
513 if err != nil {
514 return nil, err
515 }
516 return remoteRefs, nil
517}
518
519// getHavesFromRef populates the given `haves` map with the given
520// reference, and up to `maxHavesToVisitPerRef` ancestor commits.
521func getHavesFromRef(
522 ref *plumbing.Reference,
523 remoteRefs map[plumbing.Hash]bool,
524 s storage.Storer,
525 haves map[plumbing.Hash]bool,
526) error {
527 h := ref.Hash()
528 if haves[h] {
529 return nil
530 }
531
532 // No need to load the commit if we know the remote already
533 // has this hash.
534 if remoteRefs[h] {
535 haves[h] = true
536 return nil
537 }
538
539 commit, err := object.GetCommit(s, h)
540 if err != nil {
541 // Ignore the error if this isn't a commit.
542 haves[ref.Hash()] = true
543 return nil
544 }
545
546 // Until go-git supports proper commit negotiation during an
547 // upload pack request, include up to `maxHavesToVisitPerRef`
548 // commits from the history of each ref.
549 walker := object.NewCommitPreorderIter(commit, haves, nil)
550 toVisit := maxHavesToVisitPerRef
551 return walker.ForEach(func(c *object.Commit) error {
552 haves[c.Hash] = true
553 toVisit--
554 // If toVisit starts out at 0 (indicating there is no
555 // max), then it will be negative here and we won't stop
556 // early.
557 if toVisit == 0 || remoteRefs[c.Hash] {
558 return storer.ErrStop
559 }
560 return nil
561 })
562}
563
564func getHaves(
565 localRefs []*plumbing.Reference,
566 remoteRefStorer storer.ReferenceStorer,
567 s storage.Storer,
568) ([]plumbing.Hash, error) {
569 haves := map[plumbing.Hash]bool{}
570
571 // Build a map of all the remote references, to avoid loading too
572 // many parent commits for references we know don't need to be
573 // transferred.
574 remoteRefs, err := getRemoteRefsFromStorer(remoteRefStorer)
575 if err != nil {
576 return nil, err
577 }
578
579 for _, ref := range localRefs {
580 if haves[ref.Hash()] {
581 continue
582 }
583
584 if ref.Type() != plumbing.HashReference {
585 continue
586 }
587
588 err = getHavesFromRef(ref, remoteRefs, s, haves)
589 if err != nil {
590 return nil, err
591 }
592 }
593
594 var result []plumbing.Hash
595 for h := range haves {
596 result = append(result, h)
597 }
598
599 return result, nil
600}
601
602const refspecTag = "+refs/tags/*:refs/tags/*"
603
604func calculateRefs(
605 spec []config.RefSpec,
606 remoteRefs storer.ReferenceStorer,
607 tagMode TagMode,
608) (memory.ReferenceStorage, error) {
609 if tagMode == AllTags {
610 spec = append(spec, refspecTag)
611 }
612
613 iter, err := remoteRefs.IterReferences()
614 if err != nil {
615 return nil, err
616 }
617
618 refs := make(memory.ReferenceStorage)
619 return refs, iter.ForEach(func(ref *plumbing.Reference) error {
620 if !config.MatchAny(spec, ref.Name()) {
621 return nil
622 }
623
624 if ref.Type() == plumbing.SymbolicReference {
625 target, err := storer.ResolveReference(remoteRefs, ref.Name())
626 if err != nil {
627 return err
628 }
629
630 ref = plumbing.NewHashReference(ref.Name(), target.Hash())
631 }
632
633 if ref.Type() != plumbing.HashReference {
634 return nil
635 }
636
637 return refs.SetReference(ref)
638 })
639}
640
641func getWants(localStorer storage.Storer, refs memory.ReferenceStorage) ([]plumbing.Hash, error) {
642 wants := map[plumbing.Hash]bool{}
643 for _, ref := range refs {
644 hash := ref.Hash()
645 exists, err := objectExists(localStorer, ref.Hash())
646 if err != nil {
647 return nil, err
648 }
649
650 if !exists {
651 wants[hash] = true
652 }
653 }
654
655 var result []plumbing.Hash
656 for h := range wants {
657 result = append(result, h)
658 }
659
660 return result, nil
661}
662
663func objectExists(s storer.EncodedObjectStorer, h plumbing.Hash) (bool, error) {
664 _, err := s.EncodedObject(plumbing.AnyObject, h)
665 if err == plumbing.ErrObjectNotFound {
666 return false, nil
667 }
668
669 return true, err
670}
671
672func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.ReferenceStorer, cmd *packp.Command) error {
673 if cmd.Old == plumbing.ZeroHash {
674 _, err := remoteRefs.Reference(cmd.Name)
675 if err == plumbing.ErrReferenceNotFound {
676 return nil
677 }
678
679 if err != nil {
680 return err
681 }
682
683 return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
684 }
685
686 ff, err := isFastForward(s, cmd.Old, cmd.New)
687 if err != nil {
688 return err
689 }
690
691 if !ff {
692 return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
693 }
694
695 return nil
696}
697
698func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) {
699 c, err := object.GetCommit(s, new)
700 if err != nil {
701 return false, err
702 }
703
704 found := false
705 iter := object.NewCommitPreorderIter(c, nil, nil)
706 err = iter.ForEach(func(c *object.Commit) error {
707 if c.Hash != old {
708 return nil
709 }
710
711 found = true
712 return storer.ErrStop
713 })
714 return found, err
715}
716
717func (r *Remote) newUploadPackRequest(o *FetchOptions,
718 ar *packp.AdvRefs) (*packp.UploadPackRequest, error) {
719
720 req := packp.NewUploadPackRequestFromCapabilities(ar.Capabilities)
721
722 if o.Depth != 0 {
723 req.Depth = packp.DepthCommits(o.Depth)
724 if err := req.Capabilities.Set(capability.Shallow); err != nil {
725 return nil, err
726 }
727 }
728
729 if o.Progress == nil && ar.Capabilities.Supports(capability.NoProgress) {
730 if err := req.Capabilities.Set(capability.NoProgress); err != nil {
731 return nil, err
732 }
733 }
734
735 isWildcard := true
736 for _, s := range o.RefSpecs {
737 if !s.IsWildcard() {
738 isWildcard = false
739 break
740 }
741 }
742
743 if isWildcard && o.Tags == TagFollowing && ar.Capabilities.Supports(capability.IncludeTag) {
744 if err := req.Capabilities.Set(capability.IncludeTag); err != nil {
745 return nil, err
746 }
747 }
748
749 return req, nil
750}
751
752func buildSidebandIfSupported(l *capability.List, reader io.Reader, p sideband.Progress) io.Reader {
753 var t sideband.Type
754
755 switch {
756 case l.Supports(capability.Sideband):
757 t = sideband.Sideband
758 case l.Supports(capability.Sideband64k):
759 t = sideband.Sideband64k
760 default:
761 return reader
762 }
763
764 d := sideband.NewDemuxer(t, reader)
765 d.Progress = p
766
767 return d
768}
769
770func (r *Remote) updateLocalReferenceStorage(
771 specs []config.RefSpec,
772 fetchedRefs, remoteRefs memory.ReferenceStorage,
773 tagMode TagMode,
774 force bool,
775) (updated bool, err error) {
776 isWildcard := true
777 forceNeeded := false
778
779 for _, spec := range specs {
780 if !spec.IsWildcard() {
781 isWildcard = false
782 }
783
784 for _, ref := range fetchedRefs {
785 if !spec.Match(ref.Name()) {
786 continue
787 }
788
789 if ref.Type() != plumbing.HashReference {
790 continue
791 }
792
793 localName := spec.Dst(ref.Name())
794 old, _ := storer.ResolveReference(r.s, localName)
795 new := plumbing.NewHashReference(localName, ref.Hash())
796
797 // If the ref exists locally as a branch and force is not specified,
798 // only update if the new ref is an ancestor of the old
799 if old != nil && old.Name().IsBranch() && !force && !spec.IsForceUpdate() {
800 ff, err := isFastForward(r.s, old.Hash(), new.Hash())
801 if err != nil {
802 return updated, err
803 }
804
805 if !ff {
806 forceNeeded = true
807 continue
808 }
809 }
810
811 refUpdated, err := checkAndUpdateReferenceStorerIfNeeded(r.s, new, old)
812 if err != nil {
813 return updated, err
814 }
815
816 if refUpdated {
817 updated = true
818 }
819 }
820 }
821
822 if tagMode == NoTags {
823 return updated, nil
824 }
825
826 tags := fetchedRefs
827 if isWildcard {
828 tags = remoteRefs
829 }
830 tagUpdated, err := r.buildFetchedTags(tags)
831 if err != nil {
832 return updated, err
833 }
834
835 if tagUpdated {
836 updated = true
837 }
838
839 if err == nil && forceNeeded {
840 err = ErrForceNeeded
841 }
842
843 return
844}
845
846func (r *Remote) buildFetchedTags(refs memory.ReferenceStorage) (updated bool, err error) {
847 for _, ref := range refs {
848 if !ref.Name().IsTag() {
849 continue
850 }
851
852 _, err := r.s.EncodedObject(plumbing.AnyObject, ref.Hash())
853 if err == plumbing.ErrObjectNotFound {
854 continue
855 }
856
857 if err != nil {
858 return false, err
859 }
860
861 refUpdated, err := updateReferenceStorerIfNeeded(r.s, ref)
862 if err != nil {
863 return updated, err
864 }
865
866 if refUpdated {
867 updated = true
868 }
869 }
870
871 return
872}
873
874// List the references on the remote repository.
875func (r *Remote) List(o *ListOptions) ([]*plumbing.Reference, error) {
876 s, err := newUploadPackSession(r.c.URLs[0], o.Auth)
877 if err != nil {
878 return nil, err
879 }
880
881 defer ioutil.CheckClose(s, &err)
882
883 ar, err := s.AdvertisedReferences()
884 if err != nil {
885 return nil, err
886 }
887
888 allRefs, err := ar.AllReferences()
889 if err != nil {
890 return nil, err
891 }
892
893 refs, err := allRefs.IterReferences()
894 if err != nil {
895 return nil, err
896 }
897
898 var resultRefs []*plumbing.Reference
899 refs.ForEach(func(ref *plumbing.Reference) error {
900 resultRefs = append(resultRefs, ref)
901 return nil
902 })
903
904 return resultRefs, nil
905}
906
907func objectsToPush(commands []*packp.Command) []plumbing.Hash {
908 var objects []plumbing.Hash
909 for _, cmd := range commands {
910 if cmd.New == plumbing.ZeroHash {
911 continue
912 }
913
914 objects = append(objects, cmd.New)
915 }
916 return objects
917}
918
919func referencesToHashes(refs storer.ReferenceStorer) ([]plumbing.Hash, error) {
920 iter, err := refs.IterReferences()
921 if err != nil {
922 return nil, err
923 }
924
925 var hs []plumbing.Hash
926 err = iter.ForEach(func(ref *plumbing.Reference) error {
927 if ref.Type() != plumbing.HashReference {
928 return nil
929 }
930
931 hs = append(hs, ref.Hash())
932 return nil
933 })
934 if err != nil {
935 return nil, err
936 }
937
938 return hs, nil
939}
940
941func pushHashes(
942 ctx context.Context,
943 sess transport.ReceivePackSession,
944 s storage.Storer,
945 req *packp.ReferenceUpdateRequest,
946 hs []plumbing.Hash,
947) (*packp.ReportStatus, error) {
948
949 rd, wr := io.Pipe()
950 req.Packfile = rd
951 config, err := s.Config()
952 if err != nil {
953 return nil, err
954 }
955 done := make(chan error)
956 go func() {
957 e := packfile.NewEncoder(wr, s, false)
958 if _, err := e.Encode(hs, config.Pack.Window); err != nil {
959 done <- wr.CloseWithError(err)
960 return
961 }
962
963 done <- wr.Close()
964 }()
965
966 rs, err := sess.ReceivePack(ctx, req)
967 if err != nil {
968 return nil, err
969 }
970
971 if err := <-done; err != nil {
972 return nil, err
973 }
974
975 return rs, nil
976}
977
978func (r *Remote) updateShallow(o *FetchOptions, resp *packp.UploadPackResponse) error {
979 if o.Depth == 0 {
980 return nil
981 }
982
983 return r.s.SetShallow(resp.Shallows)
984}