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