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