fork of go-git with some jj specific features
at v5.2.0 26 kB view raw
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}