fork of go-git with some jj specific features
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.16.0 1537 lines 37 kB view raw
1package git 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "strings" 9 "time" 10 11 "github.com/go-git/go-billy/v5/osfs" 12 13 "github.com/go-git/go-git/v5/config" 14 "github.com/go-git/go-git/v5/internal/url" 15 "github.com/go-git/go-git/v5/plumbing" 16 "github.com/go-git/go-git/v5/plumbing/cache" 17 "github.com/go-git/go-git/v5/plumbing/format/packfile" 18 "github.com/go-git/go-git/v5/plumbing/object" 19 "github.com/go-git/go-git/v5/plumbing/protocol/packp" 20 "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability" 21 "github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband" 22 "github.com/go-git/go-git/v5/plumbing/revlist" 23 "github.com/go-git/go-git/v5/plumbing/storer" 24 "github.com/go-git/go-git/v5/plumbing/transport" 25 "github.com/go-git/go-git/v5/plumbing/transport/client" 26 "github.com/go-git/go-git/v5/storage" 27 "github.com/go-git/go-git/v5/storage/filesystem" 28 "github.com/go-git/go-git/v5/storage/memory" 29 "github.com/go-git/go-git/v5/utils/ioutil" 30) 31 32var ( 33 NoErrAlreadyUpToDate = errors.New("already up-to-date") 34 ErrDeleteRefNotSupported = errors.New("server does not support delete-refs") 35 ErrForceNeeded = errors.New("some refs were not updated") 36 ErrExactSHA1NotSupported = errors.New("server does not support exact SHA1 refspec") 37 ErrEmptyUrls = errors.New("URLs cannot be empty") 38) 39 40type NoMatchingRefSpecError struct { 41 refSpec config.RefSpec 42} 43 44func (e NoMatchingRefSpecError) Error() string { 45 return fmt.Sprintf("couldn't find remote ref %q", e.refSpec.Src()) 46} 47 48func (e NoMatchingRefSpecError) Is(target error) bool { 49 _, ok := target.(NoMatchingRefSpecError) 50 return ok 51} 52 53const ( 54 // This describes the maximum number of commits to walk when 55 // computing the haves to send to a server, for each ref in the 56 // repo containing this remote, when not using the multi-ack 57 // protocol. Setting this to 0 means there is no limit. 58 maxHavesToVisitPerRef = 100 59 60 // peeledSuffix is the suffix used to build peeled reference names. 61 peeledSuffix = "^{}" 62) 63 64// Remote represents a connection to a remote repository. 65type Remote struct { 66 c *config.RemoteConfig 67 s storage.Storer 68} 69 70// NewRemote creates a new Remote. 71// The intended purpose is to use the Remote for tasks such as listing remote references (like using git ls-remote). 72// Otherwise Remotes should be created via the use of a Repository. 73func NewRemote(s storage.Storer, c *config.RemoteConfig) *Remote { 74 return &Remote{s: s, c: c} 75} 76 77// Config returns the RemoteConfig object used to instantiate this Remote. 78func (r *Remote) Config() *config.RemoteConfig { 79 return r.c 80} 81 82func (r *Remote) String() string { 83 var fetch, push string 84 if len(r.c.URLs) > 0 { 85 fetch = r.c.URLs[0] 86 push = r.c.URLs[len(r.c.URLs)-1] 87 } 88 89 return fmt.Sprintf("%s\t%s (fetch)\n%[1]s\t%[3]s (push)", r.c.Name, fetch, push) 90} 91 92// Push performs a push to the remote. Returns NoErrAlreadyUpToDate if the 93// remote was already up-to-date. 94func (r *Remote) Push(o *PushOptions) error { 95 return r.PushContext(context.Background(), o) 96} 97 98// PushContext performs a push to the remote. Returns NoErrAlreadyUpToDate if 99// the remote was already up-to-date. 100// 101// The provided Context must be non-nil. If the context expires before the 102// operation is complete, an error is returned. The context only affects the 103// transport operations. 104func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) { 105 if err := o.Validate(); err != nil { 106 return err 107 } 108 109 if o.RemoteName != r.c.Name { 110 return fmt.Errorf("remote names don't match: %s != %s", o.RemoteName, r.c.Name) 111 } 112 113 if o.RemoteURL == "" && len(r.c.URLs) > 0 { 114 o.RemoteURL = r.c.URLs[len(r.c.URLs)-1] 115 } 116 117 s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions) 118 if err != nil { 119 return err 120 } 121 122 defer ioutil.CheckClose(s, &err) 123 124 ar, err := s.AdvertisedReferencesContext(ctx) 125 if err != nil { 126 return err 127 } 128 129 remoteRefs, err := ar.AllReferences() 130 if err != nil { 131 return err 132 } 133 134 if err := r.checkRequireRemoteRefs(o.RequireRemoteRefs, remoteRefs); err != nil { 135 return err 136 } 137 138 isDelete := false 139 allDelete := true 140 for _, rs := range o.RefSpecs { 141 if rs.IsDelete() { 142 isDelete = true 143 } else { 144 allDelete = false 145 } 146 if isDelete && !allDelete { 147 break 148 } 149 } 150 151 if isDelete && !ar.Capabilities.Supports(capability.DeleteRefs) { 152 return ErrDeleteRefNotSupported 153 } 154 155 if o.Force { 156 for i := 0; i < len(o.RefSpecs); i++ { 157 rs := &o.RefSpecs[i] 158 if !rs.IsForceUpdate() && !rs.IsDelete() { 159 o.RefSpecs[i] = config.RefSpec("+" + rs.String()) 160 } 161 } 162 } 163 164 localRefs, err := r.references() 165 if err != nil { 166 return err 167 } 168 169 req, err := r.newReferenceUpdateRequest(o, localRefs, remoteRefs, ar) 170 if err != nil { 171 return err 172 } 173 174 if len(req.Commands) == 0 { 175 return NoErrAlreadyUpToDate 176 } 177 178 objects := objectsToPush(req.Commands) 179 180 haves, err := referencesToHashes(remoteRefs) 181 if err != nil { 182 return err 183 } 184 185 stop, err := r.s.Shallow() 186 if err != nil { 187 return err 188 } 189 190 // if we have shallow we should include this as part of the objects that 191 // we are aware. 192 haves = append(haves, stop...) 193 194 var hashesToPush []plumbing.Hash 195 // Avoid the expensive revlist operation if we're only doing deletes. 196 if !allDelete { 197 if url.IsLocalEndpoint(o.RemoteURL) { 198 // If we're are pushing to a local repo, it might be much 199 // faster to use a local storage layer to get the commits 200 // to ignore, when calculating the object revlist. 201 localStorer := filesystem.NewStorage( 202 osfs.New(o.RemoteURL), cache.NewObjectLRUDefault()) 203 hashesToPush, err = revlist.ObjectsWithStorageForIgnores( 204 r.s, localStorer, objects, haves) 205 } else { 206 hashesToPush, err = revlist.Objects(r.s, objects, haves) 207 } 208 if err != nil { 209 return err 210 } 211 } 212 213 if len(hashesToPush) == 0 { 214 allDelete = true 215 for _, command := range req.Commands { 216 if command.Action() != packp.Delete { 217 allDelete = false 218 break 219 } 220 } 221 } 222 223 rs, err := pushHashes(ctx, s, r.s, req, hashesToPush, r.useRefDeltas(ar), allDelete) 224 if err != nil { 225 return err 226 } 227 228 if rs != nil { 229 if err = rs.Error(); err != nil { 230 return err 231 } 232 } 233 234 return r.updateRemoteReferenceStorage(req) 235} 236 237func (r *Remote) useRefDeltas(ar *packp.AdvRefs) bool { 238 return !ar.Capabilities.Supports(capability.OFSDelta) 239} 240 241func (r *Remote) addReachableTags(localRefs []*plumbing.Reference, remoteRefs storer.ReferenceStorer, req *packp.ReferenceUpdateRequest) error { 242 tags := make(map[plumbing.Reference]struct{}) 243 // get a list of all tags locally 244 for _, ref := range localRefs { 245 if strings.HasPrefix(string(ref.Name()), "refs/tags") { 246 tags[*ref] = struct{}{} 247 } 248 } 249 250 remoteRefIter, err := remoteRefs.IterReferences() 251 if err != nil { 252 return err 253 } 254 255 // remove any that are already on the remote 256 if err := remoteRefIter.ForEach(func(reference *plumbing.Reference) error { 257 delete(tags, *reference) 258 return nil 259 }); err != nil { 260 return err 261 } 262 263 for tag := range tags { 264 tagObject, err := object.GetObject(r.s, tag.Hash()) 265 var tagCommit *object.Commit 266 if err != nil { 267 return fmt.Errorf("get tag object: %w", err) 268 } 269 270 if tagObject.Type() != plumbing.TagObject { 271 continue 272 } 273 274 annotatedTag, ok := tagObject.(*object.Tag) 275 if !ok { 276 return errors.New("could not get annotated tag object") 277 } 278 279 tagCommit, err = object.GetCommit(r.s, annotatedTag.Target) 280 if err != nil { 281 return fmt.Errorf("get annotated tag commit: %w", err) 282 } 283 284 // only include tags that are reachable from one of the refs 285 // already being pushed 286 for _, cmd := range req.Commands { 287 if tag.Name() == cmd.Name { 288 continue 289 } 290 291 if strings.HasPrefix(cmd.Name.String(), "refs/tags") { 292 continue 293 } 294 295 c, err := object.GetCommit(r.s, cmd.New) 296 if err != nil { 297 return fmt.Errorf("get commit %v: %w", cmd.Name, err) 298 } 299 300 if isAncestor, err := tagCommit.IsAncestor(c); err == nil && isAncestor { 301 req.Commands = append(req.Commands, &packp.Command{Name: tag.Name(), New: tag.Hash()}) 302 } 303 } 304 } 305 306 return nil 307} 308 309func (r *Remote) newReferenceUpdateRequest( 310 o *PushOptions, 311 localRefs []*plumbing.Reference, 312 remoteRefs storer.ReferenceStorer, 313 ar *packp.AdvRefs, 314) (*packp.ReferenceUpdateRequest, error) { 315 req := packp.NewReferenceUpdateRequestFromCapabilities(ar.Capabilities) 316 317 if o.Progress != nil { 318 req.Progress = o.Progress 319 if ar.Capabilities.Supports(capability.Sideband64k) { 320 _ = req.Capabilities.Set(capability.Sideband64k) 321 } else if ar.Capabilities.Supports(capability.Sideband) { 322 _ = req.Capabilities.Set(capability.Sideband) 323 } 324 } 325 326 if ar.Capabilities.Supports(capability.PushOptions) { 327 _ = req.Capabilities.Set(capability.PushOptions) 328 for k, v := range o.Options { 329 req.Options = append(req.Options, &packp.Option{Key: k, Value: v}) 330 } 331 } 332 333 if o.Atomic && ar.Capabilities.Supports(capability.Atomic) { 334 _ = req.Capabilities.Set(capability.Atomic) 335 } 336 337 if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req, o.Prune, o.ForceWithLease); err != nil { 338 339 return nil, err 340 } 341 342 if o.FollowTags { 343 if err := r.addReachableTags(localRefs, remoteRefs, req); err != nil { 344 return nil, err 345 } 346 } 347 348 return req, nil 349} 350 351func (r *Remote) updateRemoteReferenceStorage( 352 req *packp.ReferenceUpdateRequest, 353) error { 354 355 for _, spec := range r.c.Fetch { 356 for _, c := range req.Commands { 357 if !spec.Match(c.Name) { 358 continue 359 } 360 361 local := spec.Dst(c.Name) 362 ref := plumbing.NewHashReference(local, c.New) 363 switch c.Action() { 364 case packp.Create, packp.Update: 365 if err := r.s.SetReference(ref); err != nil { 366 return err 367 } 368 case packp.Delete: 369 if err := r.s.RemoveReference(local); err != nil { 370 return err 371 } 372 } 373 } 374 } 375 376 return nil 377} 378 379// FetchContext fetches references along with the objects necessary to complete 380// their histories. 381// 382// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are 383// no changes to be fetched, or an error. 384// 385// The provided Context must be non-nil. If the context expires before the 386// operation is complete, an error is returned. The context only affects the 387// transport operations. 388func (r *Remote) FetchContext(ctx context.Context, o *FetchOptions) error { 389 _, err := r.fetch(ctx, o) 390 return err 391} 392 393// Fetch fetches references along with the objects necessary to complete their 394// histories. 395// 396// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are 397// no changes to be fetched, or an error. 398func (r *Remote) Fetch(o *FetchOptions) error { 399 return r.FetchContext(context.Background(), o) 400} 401 402func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.ReferenceStorer, err error) { 403 if o.RemoteName == "" { 404 o.RemoteName = r.c.Name 405 } 406 407 if err = o.Validate(); err != nil { 408 return nil, err 409 } 410 411 if len(o.RefSpecs) == 0 { 412 o.RefSpecs = r.c.Fetch 413 } 414 415 if o.RemoteURL == "" { 416 o.RemoteURL = r.c.URLs[0] 417 } 418 419 s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions) 420 if err != nil { 421 return nil, err 422 } 423 424 defer ioutil.CheckClose(s, &err) 425 426 ar, err := s.AdvertisedReferencesContext(ctx) 427 if err != nil { 428 return nil, err 429 } 430 431 req, err := r.newUploadPackRequest(o, ar) 432 if err != nil { 433 return nil, err 434 } 435 436 if err := r.isSupportedRefSpec(o.RefSpecs, ar); err != nil { 437 return nil, err 438 } 439 440 remoteRefs, err := ar.AllReferences() 441 if err != nil { 442 return nil, err 443 } 444 445 localRefs, err := r.references() 446 if err != nil { 447 return nil, err 448 } 449 450 refs, specToRefs, err := calculateRefs(o.RefSpecs, remoteRefs, o.Tags) 451 if err != nil { 452 return nil, err 453 } 454 455 if !req.Depth.IsZero() { 456 req.Shallows, err = r.s.Shallow() 457 if err != nil { 458 return nil, fmt.Errorf("existing checkout is not shallow") 459 } 460 } 461 462 req.Wants, err = getWants(r.s, refs, o.Depth) 463 if len(req.Wants) > 0 { 464 req.Haves, err = getHaves(localRefs, remoteRefs, r.s, o.Depth) 465 if err != nil { 466 return nil, err 467 } 468 469 if err = r.fetchPack(ctx, o, s, req); err != nil { 470 return nil, err 471 } 472 } 473 474 var updatedPrune bool 475 if o.Prune { 476 updatedPrune, err = r.pruneRemotes(o.RefSpecs, localRefs, remoteRefs) 477 if err != nil { 478 return nil, err 479 } 480 } 481 482 updated, err := r.updateLocalReferenceStorage(o.RefSpecs, refs, remoteRefs, specToRefs, o.Tags, o.Force) 483 if err != nil { 484 return nil, err 485 } 486 487 if !updated { 488 updated, err = depthChanged(req.Shallows, r.s) 489 if err != nil { 490 return nil, fmt.Errorf("error checking depth change: %v", err) 491 } 492 } 493 494 if !updated && !updatedPrune { 495 // No references updated, but may have fetched new objects, check if we now have any of our wants 496 for _, hash := range req.Wants { 497 exists, _ := objectExists(r.s, hash) 498 if exists { 499 updated = true 500 break 501 } 502 } 503 504 if !updated { 505 return remoteRefs, NoErrAlreadyUpToDate 506 } 507 } 508 509 return remoteRefs, nil 510} 511 512func depthChanged(before []plumbing.Hash, s storage.Storer) (bool, error) { 513 after, err := s.Shallow() 514 if err != nil { 515 return false, err 516 } 517 518 if len(before) != len(after) { 519 return true, nil 520 } 521 522 bm := make(map[plumbing.Hash]bool, len(before)) 523 for _, b := range before { 524 bm[b] = true 525 } 526 for _, a := range after { 527 if _, ok := bm[a]; !ok { 528 return true, nil 529 } 530 } 531 532 return false, nil 533} 534 535func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) { 536 c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts) 537 if err != nil { 538 return nil, err 539 } 540 541 return c.NewUploadPackSession(ep, auth) 542} 543 544func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) { 545 c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts) 546 if err != nil { 547 return nil, err 548 } 549 550 return c.NewReceivePackSession(ep, auth) 551} 552 553func newClient(url string, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) { 554 ep, err := transport.NewEndpoint(url) 555 if err != nil { 556 return nil, nil, err 557 } 558 ep.InsecureSkipTLS = insecure 559 ep.ClientCert = clientCert 560 ep.ClientKey = clientKey 561 ep.CaBundle = caBundle 562 ep.Proxy = proxyOpts 563 564 c, err := client.NewClient(ep) 565 if err != nil { 566 return nil, nil, err 567 } 568 569 return c, ep, err 570} 571 572func (r *Remote) fetchPack(ctx context.Context, o *FetchOptions, s transport.UploadPackSession, 573 req *packp.UploadPackRequest) (err error) { 574 575 reader, err := s.UploadPack(ctx, req) 576 if err != nil { 577 if errors.Is(err, transport.ErrEmptyUploadPackRequest) { 578 // XXX: no packfile provided, everything is up-to-date. 579 return nil 580 } 581 return err 582 } 583 584 defer ioutil.CheckClose(reader, &err) 585 586 if err = r.updateShallow(o, reader); err != nil { 587 return err 588 } 589 590 if err = packfile.UpdateObjectStorage(r.s, 591 buildSidebandIfSupported(req.Capabilities, reader, o.Progress), 592 ); err != nil { 593 return err 594 } 595 596 return err 597} 598 599func (r *Remote) pruneRemotes(specs []config.RefSpec, localRefs []*plumbing.Reference, remoteRefs memory.ReferenceStorage) (bool, error) { 600 var updatedPrune bool 601 for _, spec := range specs { 602 rev := spec.Reverse() 603 for _, ref := range localRefs { 604 if !rev.Match(ref.Name()) { 605 continue 606 } 607 _, err := remoteRefs.Reference(rev.Dst(ref.Name())) 608 if errors.Is(err, plumbing.ErrReferenceNotFound) { 609 updatedPrune = true 610 err := r.s.RemoveReference(ref.Name()) 611 if err != nil { 612 return false, err 613 } 614 } 615 } 616 } 617 return updatedPrune, nil 618} 619 620func (r *Remote) addReferencesToUpdate( 621 refspecs []config.RefSpec, 622 localRefs []*plumbing.Reference, 623 remoteRefs storer.ReferenceStorer, 624 req *packp.ReferenceUpdateRequest, 625 prune bool, 626 forceWithLease *ForceWithLease, 627) error { 628 // This references dictionary will be used to search references by name. 629 refsDict := make(map[string]*plumbing.Reference) 630 for _, ref := range localRefs { 631 refsDict[ref.Name().String()] = ref 632 } 633 634 for _, rs := range refspecs { 635 if rs.IsDelete() { 636 if err := r.deleteReferences(rs, remoteRefs, refsDict, req, false); err != nil { 637 return err 638 } 639 } else { 640 err := r.addOrUpdateReferences(rs, localRefs, refsDict, remoteRefs, req, forceWithLease) 641 if err != nil { 642 return err 643 } 644 645 if prune { 646 if err := r.deleteReferences(rs, remoteRefs, refsDict, req, true); err != nil { 647 return err 648 } 649 } 650 } 651 } 652 653 return nil 654} 655 656func (r *Remote) addOrUpdateReferences( 657 rs config.RefSpec, 658 localRefs []*plumbing.Reference, 659 refsDict map[string]*plumbing.Reference, 660 remoteRefs storer.ReferenceStorer, 661 req *packp.ReferenceUpdateRequest, 662 forceWithLease *ForceWithLease, 663) error { 664 // If it is not a wildcard refspec we can directly search for the reference 665 // in the references dictionary. 666 if !rs.IsWildcard() { 667 ref, ok := refsDict[rs.Src()] 668 if !ok { 669 commit, err := object.GetCommit(r.s, plumbing.NewHash(rs.Src())) 670 if err == nil { 671 return r.addCommit(rs, remoteRefs, commit.Hash, req) 672 } 673 return nil 674 } 675 676 return r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req, forceWithLease) 677 } 678 679 for _, ref := range localRefs { 680 err := r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req, forceWithLease) 681 if err != nil { 682 return err 683 } 684 } 685 686 return nil 687} 688 689func (r *Remote) deleteReferences(rs config.RefSpec, 690 remoteRefs storer.ReferenceStorer, 691 refsDict map[string]*plumbing.Reference, 692 req *packp.ReferenceUpdateRequest, 693 prune bool) error { 694 iter, err := remoteRefs.IterReferences() 695 if err != nil { 696 return err 697 } 698 699 return iter.ForEach(func(ref *plumbing.Reference) error { 700 if ref.Type() != plumbing.HashReference { 701 return nil 702 } 703 704 if prune { 705 rs := rs.Reverse() 706 if !rs.Match(ref.Name()) { 707 return nil 708 } 709 710 if _, ok := refsDict[rs.Dst(ref.Name()).String()]; ok { 711 return nil 712 } 713 } else if rs.Dst("") != ref.Name() { 714 return nil 715 } 716 717 cmd := &packp.Command{ 718 Name: ref.Name(), 719 Old: ref.Hash(), 720 New: plumbing.ZeroHash, 721 } 722 req.Commands = append(req.Commands, cmd) 723 return nil 724 }) 725} 726 727func (r *Remote) addCommit(rs config.RefSpec, 728 remoteRefs storer.ReferenceStorer, localCommit plumbing.Hash, 729 req *packp.ReferenceUpdateRequest) error { 730 731 if rs.IsWildcard() { 732 return errors.New("can't use wildcard together with hash refspecs") 733 } 734 735 cmd := &packp.Command{ 736 Name: rs.Dst(""), 737 Old: plumbing.ZeroHash, 738 New: localCommit, 739 } 740 remoteRef, err := remoteRefs.Reference(cmd.Name) 741 if err == nil { 742 if remoteRef.Type() != plumbing.HashReference { 743 // TODO: check actual git behavior here 744 return nil 745 } 746 747 cmd.Old = remoteRef.Hash() 748 } else if err != plumbing.ErrReferenceNotFound { 749 return err 750 } 751 if cmd.Old == cmd.New { 752 return nil 753 } 754 if !rs.IsForceUpdate() { 755 if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil { 756 return err 757 } 758 } 759 760 req.Commands = append(req.Commands, cmd) 761 return nil 762} 763 764func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec, 765 remoteRefs storer.ReferenceStorer, localRef *plumbing.Reference, 766 req *packp.ReferenceUpdateRequest, forceWithLease *ForceWithLease) error { 767 768 if localRef.Type() != plumbing.HashReference { 769 return nil 770 } 771 772 if !rs.Match(localRef.Name()) { 773 return nil 774 } 775 776 cmd := &packp.Command{ 777 Name: rs.Dst(localRef.Name()), 778 Old: plumbing.ZeroHash, 779 New: localRef.Hash(), 780 } 781 782 remoteRef, err := remoteRefs.Reference(cmd.Name) 783 if err == nil { 784 if remoteRef.Type() != plumbing.HashReference { 785 // TODO: check actual git behavior here 786 return nil 787 } 788 789 cmd.Old = remoteRef.Hash() 790 } else if err != plumbing.ErrReferenceNotFound { 791 return err 792 } 793 794 if cmd.Old == cmd.New { 795 return nil 796 } 797 798 if forceWithLease != nil { 799 if err = r.checkForceWithLease(localRef, cmd, forceWithLease); err != nil { 800 return err 801 } 802 } else if !rs.IsForceUpdate() { 803 if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil { 804 return err 805 } 806 } 807 808 req.Commands = append(req.Commands, cmd) 809 return nil 810} 811 812func (r *Remote) checkForceWithLease(localRef *plumbing.Reference, cmd *packp.Command, forceWithLease *ForceWithLease) error { 813 remotePrefix := fmt.Sprintf("refs/remotes/%s/", r.Config().Name) 814 815 ref, err := storer.ResolveReference( 816 r.s, 817 plumbing.ReferenceName(remotePrefix+strings.Replace(localRef.Name().String(), "refs/heads/", "", -1))) 818 if err != nil { 819 return err 820 } 821 822 if forceWithLease.RefName.String() == "" || (forceWithLease.RefName == cmd.Name) { 823 expectedOID := ref.Hash() 824 825 if !forceWithLease.Hash.IsZero() { 826 expectedOID = forceWithLease.Hash 827 } 828 829 if cmd.Old != expectedOID { 830 return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String()) 831 } 832 } 833 834 return nil 835} 836 837func (r *Remote) references() ([]*plumbing.Reference, error) { 838 var localRefs []*plumbing.Reference 839 840 iter, err := r.s.IterReferences() 841 if err != nil { 842 return nil, err 843 } 844 845 for { 846 ref, err := iter.Next() 847 if err == io.EOF { 848 break 849 } 850 851 if err != nil { 852 return nil, err 853 } 854 855 localRefs = append(localRefs, ref) 856 } 857 858 return localRefs, nil 859} 860 861func getRemoteRefsFromStorer(remoteRefStorer storer.ReferenceStorer) ( 862 map[plumbing.Hash]bool, error) { 863 remoteRefs := map[plumbing.Hash]bool{} 864 iter, err := remoteRefStorer.IterReferences() 865 if err != nil { 866 return nil, err 867 } 868 err = iter.ForEach(func(ref *plumbing.Reference) error { 869 if ref.Type() != plumbing.HashReference { 870 return nil 871 } 872 remoteRefs[ref.Hash()] = true 873 return nil 874 }) 875 if err != nil { 876 return nil, err 877 } 878 return remoteRefs, nil 879} 880 881// getHavesFromRef populates the given `haves` map with the given 882// reference, and up to `maxHavesToVisitPerRef` ancestor commits. 883func getHavesFromRef( 884 ref *plumbing.Reference, 885 remoteRefs map[plumbing.Hash]bool, 886 s storage.Storer, 887 haves map[plumbing.Hash]bool, 888 depth int, 889) error { 890 h := ref.Hash() 891 if haves[h] { 892 return nil 893 } 894 895 commit, err := object.GetCommit(s, h) 896 if err != nil { 897 if !errors.Is(err, plumbing.ErrObjectNotFound) { 898 // Ignore the error if this isn't a commit. 899 haves[ref.Hash()] = true 900 } 901 return nil 902 } 903 904 // Until go-git supports proper commit negotiation during an 905 // upload pack request, include up to `maxHavesToVisitPerRef` 906 // commits from the history of each ref. 907 walker := object.NewCommitPreorderIter(commit, haves, nil) 908 toVisit := maxHavesToVisitPerRef 909 // But only need up to the requested depth 910 if depth > 0 && depth < maxHavesToVisitPerRef { 911 toVisit = depth 912 } 913 // It is safe to ignore any error here as we are just trying to find the references that we already have 914 // An example of a legitimate failure is we have a shallow clone and don't have the previous commit(s) 915 _ = walker.ForEach(func(c *object.Commit) error { 916 haves[c.Hash] = true 917 toVisit-- 918 // If toVisit starts out at 0 (indicating there is no 919 // max), then it will be negative here and we won't stop 920 // early. 921 if toVisit == 0 || remoteRefs[c.Hash] { 922 return storer.ErrStop 923 } 924 return nil 925 }) 926 927 return nil 928} 929 930func getHaves( 931 localRefs []*plumbing.Reference, 932 remoteRefStorer storer.ReferenceStorer, 933 s storage.Storer, 934 depth int, 935) ([]plumbing.Hash, error) { 936 haves := map[plumbing.Hash]bool{} 937 938 // Build a map of all the remote references, to avoid loading too 939 // many parent commits for references we know don't need to be 940 // transferred. 941 remoteRefs, err := getRemoteRefsFromStorer(remoteRefStorer) 942 if err != nil { 943 return nil, err 944 } 945 946 for _, ref := range localRefs { 947 if haves[ref.Hash()] { 948 continue 949 } 950 951 if ref.Type() != plumbing.HashReference { 952 continue 953 } 954 955 err = getHavesFromRef(ref, remoteRefs, s, haves, depth) 956 if err != nil { 957 return nil, err 958 } 959 } 960 961 var result []plumbing.Hash 962 for h := range haves { 963 result = append(result, h) 964 } 965 966 return result, nil 967} 968 969const refspecAllTags = "+refs/tags/*:refs/tags/*" 970 971func calculateRefs( 972 spec []config.RefSpec, 973 remoteRefs storer.ReferenceStorer, 974 tagMode TagMode, 975) (memory.ReferenceStorage, [][]*plumbing.Reference, error) { 976 if tagMode == AllTags { 977 spec = append(spec, refspecAllTags) 978 } 979 980 refs := make(memory.ReferenceStorage) 981 // list of references matched for each spec 982 specToRefs := make([][]*plumbing.Reference, len(spec)) 983 for i := range spec { 984 var err error 985 specToRefs[i], err = doCalculateRefs(spec[i], remoteRefs, refs) 986 if err != nil { 987 return nil, nil, err 988 } 989 } 990 991 return refs, specToRefs, nil 992} 993 994func doCalculateRefs( 995 s config.RefSpec, 996 remoteRefs storer.ReferenceStorer, 997 refs memory.ReferenceStorage, 998) ([]*plumbing.Reference, error) { 999 var refList []*plumbing.Reference 1000 1001 if s.IsExactSHA1() { 1002 ref := plumbing.NewHashReference(s.Dst(""), plumbing.NewHash(s.Src())) 1003 1004 refList = append(refList, ref) 1005 return refList, refs.SetReference(ref) 1006 } 1007 1008 var matched bool 1009 onMatched := func(ref *plumbing.Reference) error { 1010 if ref.Type() == plumbing.SymbolicReference { 1011 target, err := storer.ResolveReference(remoteRefs, ref.Name()) 1012 if err != nil { 1013 return err 1014 } 1015 1016 ref = plumbing.NewHashReference(ref.Name(), target.Hash()) 1017 } 1018 1019 if ref.Type() != plumbing.HashReference { 1020 return nil 1021 } 1022 1023 matched = true 1024 refList = append(refList, ref) 1025 return refs.SetReference(ref) 1026 } 1027 1028 var ret error 1029 if s.IsWildcard() { 1030 iter, err := remoteRefs.IterReferences() 1031 if err != nil { 1032 return nil, err 1033 } 1034 ret = iter.ForEach(func(ref *plumbing.Reference) error { 1035 if !s.Match(ref.Name()) { 1036 return nil 1037 } 1038 1039 return onMatched(ref) 1040 }) 1041 } else { 1042 var resolvedRef *plumbing.Reference 1043 src := s.Src() 1044 resolvedRef, ret = expand_ref(remoteRefs, plumbing.ReferenceName(src)) 1045 if ret == nil { 1046 ret = onMatched(resolvedRef) 1047 } 1048 } 1049 1050 if !matched && !s.IsWildcard() { 1051 return nil, NoMatchingRefSpecError{refSpec: s} 1052 } 1053 1054 return refList, ret 1055} 1056 1057func getWants(localStorer storage.Storer, refs memory.ReferenceStorage, depth int) ([]plumbing.Hash, error) { 1058 // If depth is anything other than 1 and the repo has shallow commits then just because we have the commit 1059 // at the reference doesn't mean that we don't still need to fetch the parents 1060 shallow := false 1061 if depth != 1 { 1062 if s, _ := localStorer.Shallow(); len(s) > 0 { 1063 shallow = true 1064 } 1065 } 1066 1067 wants := map[plumbing.Hash]bool{} 1068 for _, ref := range refs { 1069 hash := ref.Hash() 1070 exists, err := objectExists(localStorer, ref.Hash()) 1071 if err != nil { 1072 return nil, err 1073 } 1074 1075 if !exists || shallow { 1076 wants[hash] = true 1077 } 1078 } 1079 1080 var result []plumbing.Hash 1081 for h := range wants { 1082 result = append(result, h) 1083 } 1084 1085 return result, nil 1086} 1087 1088func objectExists(s storer.EncodedObjectStorer, h plumbing.Hash) (bool, error) { 1089 _, err := s.EncodedObject(plumbing.AnyObject, h) 1090 if err == plumbing.ErrObjectNotFound { 1091 return false, nil 1092 } 1093 1094 return true, err 1095} 1096 1097func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.ReferenceStorer, cmd *packp.Command) error { 1098 if cmd.Old == plumbing.ZeroHash { 1099 _, err := remoteRefs.Reference(cmd.Name) 1100 if err == plumbing.ErrReferenceNotFound { 1101 return nil 1102 } 1103 1104 if err != nil { 1105 return err 1106 } 1107 1108 return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String()) 1109 } 1110 1111 ff, err := isFastForward(s, cmd.Old, cmd.New, nil) 1112 if err != nil { 1113 return err 1114 } 1115 1116 if !ff { 1117 return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String()) 1118 } 1119 1120 return nil 1121} 1122 1123func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash, earliestShallow *plumbing.Hash) (bool, error) { 1124 c, err := object.GetCommit(s, new) 1125 if err != nil { 1126 return false, err 1127 } 1128 1129 parentsToIgnore := []plumbing.Hash{} 1130 if earliestShallow != nil { 1131 earliestCommit, err := object.GetCommit(s, *earliestShallow) 1132 if err != nil { 1133 return false, err 1134 } 1135 1136 parentsToIgnore = earliestCommit.ParentHashes 1137 } 1138 1139 found := false 1140 // stop iterating at the earliest shallow commit, ignoring its parents 1141 // note: when pull depth is smaller than the number of new changes on the remote, this fails due to missing parents. 1142 // as far as i can tell, without the commits in-between the shallow pull and the earliest shallow, there's no 1143 // real way of telling whether it will be a fast-forward merge. 1144 iter := object.NewCommitPreorderIter(c, nil, parentsToIgnore) 1145 err = iter.ForEach(func(c *object.Commit) error { 1146 if c.Hash != old { 1147 return nil 1148 } 1149 1150 found = true 1151 return storer.ErrStop 1152 }) 1153 return found, err 1154} 1155 1156func (r *Remote) newUploadPackRequest(o *FetchOptions, 1157 ar *packp.AdvRefs) (*packp.UploadPackRequest, error) { 1158 1159 req := packp.NewUploadPackRequestFromCapabilities(ar.Capabilities) 1160 1161 if o.Depth != 0 { 1162 req.Depth = packp.DepthCommits(o.Depth) 1163 if err := req.Capabilities.Set(capability.Shallow); err != nil { 1164 return nil, err 1165 } 1166 } 1167 1168 if o.Progress == nil && ar.Capabilities.Supports(capability.NoProgress) { 1169 if err := req.Capabilities.Set(capability.NoProgress); err != nil { 1170 return nil, err 1171 } 1172 } 1173 1174 isWildcard := true 1175 for _, s := range o.RefSpecs { 1176 if !s.IsWildcard() { 1177 isWildcard = false 1178 break 1179 } 1180 } 1181 1182 if isWildcard && o.Tags == TagFollowing && ar.Capabilities.Supports(capability.IncludeTag) { 1183 if err := req.Capabilities.Set(capability.IncludeTag); err != nil { 1184 return nil, err 1185 } 1186 } 1187 1188 return req, nil 1189} 1190 1191func (r *Remote) isSupportedRefSpec(refs []config.RefSpec, ar *packp.AdvRefs) error { 1192 var containsIsExact bool 1193 for _, ref := range refs { 1194 if ref.IsExactSHA1() { 1195 containsIsExact = true 1196 } 1197 } 1198 1199 if !containsIsExact { 1200 return nil 1201 } 1202 1203 if ar.Capabilities.Supports(capability.AllowReachableSHA1InWant) || 1204 ar.Capabilities.Supports(capability.AllowTipSHA1InWant) { 1205 return nil 1206 } 1207 1208 return ErrExactSHA1NotSupported 1209} 1210 1211func buildSidebandIfSupported(l *capability.List, reader io.Reader, p sideband.Progress) io.Reader { 1212 var t sideband.Type 1213 1214 switch { 1215 case l.Supports(capability.Sideband): 1216 t = sideband.Sideband 1217 case l.Supports(capability.Sideband64k): 1218 t = sideband.Sideband64k 1219 default: 1220 return reader 1221 } 1222 1223 d := sideband.NewDemuxer(t, reader) 1224 d.Progress = p 1225 1226 return d 1227} 1228 1229func (r *Remote) updateLocalReferenceStorage( 1230 specs []config.RefSpec, 1231 fetchedRefs, remoteRefs memory.ReferenceStorage, 1232 specToRefs [][]*plumbing.Reference, 1233 tagMode TagMode, 1234 force bool, 1235) (updated bool, err error) { 1236 isWildcard := true 1237 forceNeeded := false 1238 1239 for i, spec := range specs { 1240 if !spec.IsWildcard() { 1241 isWildcard = false 1242 } 1243 1244 for _, ref := range specToRefs[i] { 1245 if ref.Type() != plumbing.HashReference { 1246 continue 1247 } 1248 1249 localName := spec.Dst(ref.Name()) 1250 // If localName doesn't start with "refs/" then treat as a branch. 1251 if !strings.HasPrefix(localName.String(), "refs/") { 1252 localName = plumbing.NewBranchReferenceName(localName.String()) 1253 } 1254 old, _ := storer.ResolveReference(r.s, localName) 1255 new := plumbing.NewHashReference(localName, ref.Hash()) 1256 1257 // If the ref exists locally as a non-tag and force is not 1258 // specified, only update if the new ref is an ancestor of the old 1259 if old != nil && !old.Name().IsTag() && !force && !spec.IsForceUpdate() { 1260 ff, err := isFastForward(r.s, old.Hash(), new.Hash(), nil) 1261 if err != nil { 1262 return updated, err 1263 } 1264 1265 if !ff { 1266 forceNeeded = true 1267 continue 1268 } 1269 } 1270 1271 refUpdated, err := checkAndUpdateReferenceStorerIfNeeded(r.s, new, old) 1272 if err != nil { 1273 return updated, err 1274 } 1275 1276 if refUpdated { 1277 updated = true 1278 } 1279 } 1280 } 1281 1282 if tagMode == NoTags { 1283 return updated, nil 1284 } 1285 1286 tags := fetchedRefs 1287 if isWildcard { 1288 tags = remoteRefs 1289 } 1290 tagUpdated, err := r.buildFetchedTags(tags) 1291 if err != nil { 1292 return updated, err 1293 } 1294 1295 if tagUpdated { 1296 updated = true 1297 } 1298 1299 if forceNeeded { 1300 err = ErrForceNeeded 1301 } 1302 1303 return 1304} 1305 1306func (r *Remote) buildFetchedTags(refs memory.ReferenceStorage) (updated bool, err error) { 1307 for _, ref := range refs { 1308 if !ref.Name().IsTag() { 1309 continue 1310 } 1311 1312 _, err := r.s.EncodedObject(plumbing.AnyObject, ref.Hash()) 1313 if err == plumbing.ErrObjectNotFound { 1314 continue 1315 } 1316 1317 if err != nil { 1318 return false, err 1319 } 1320 1321 refUpdated, err := updateReferenceStorerIfNeeded(r.s, ref) 1322 if err != nil { 1323 return updated, err 1324 } 1325 1326 if refUpdated { 1327 updated = true 1328 } 1329 } 1330 1331 return 1332} 1333 1334// List the references on the remote repository. 1335// The provided Context must be non-nil. If the context expires before the 1336// operation is complete, an error is returned. The context only affects to the 1337// transport operations. 1338func (r *Remote) ListContext(ctx context.Context, o *ListOptions) (rfs []*plumbing.Reference, err error) { 1339 return r.list(ctx, o) 1340} 1341 1342func (r *Remote) List(o *ListOptions) (rfs []*plumbing.Reference, err error) { 1343 timeout := o.Timeout 1344 // Default to the old hardcoded 10s value if a timeout is not explicitly set. 1345 if timeout == 0 { 1346 timeout = 10 1347 } 1348 if timeout < 0 { 1349 return nil, fmt.Errorf("invalid timeout: %d", timeout) 1350 } 1351 ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) 1352 defer cancel() 1353 return r.ListContext(ctx, o) 1354} 1355 1356func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Reference, err error) { 1357 if r.c == nil || len(r.c.URLs) == 0 { 1358 return nil, ErrEmptyUrls 1359 } 1360 1361 s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions) 1362 if err != nil { 1363 return nil, err 1364 } 1365 1366 defer ioutil.CheckClose(s, &err) 1367 1368 ar, err := s.AdvertisedReferencesContext(ctx) 1369 if err != nil { 1370 return nil, err 1371 } 1372 1373 allRefs, err := ar.AllReferences() 1374 if err != nil { 1375 return nil, err 1376 } 1377 1378 refs, err := allRefs.IterReferences() 1379 if err != nil { 1380 return nil, err 1381 } 1382 1383 var resultRefs []*plumbing.Reference 1384 if o.PeelingOption == AppendPeeled || o.PeelingOption == IgnorePeeled { 1385 err = refs.ForEach(func(ref *plumbing.Reference) error { 1386 resultRefs = append(resultRefs, ref) 1387 return nil 1388 }) 1389 if err != nil { 1390 return nil, err 1391 } 1392 } 1393 1394 if o.PeelingOption == AppendPeeled || o.PeelingOption == OnlyPeeled { 1395 for k, v := range ar.Peeled { 1396 resultRefs = append(resultRefs, plumbing.NewReferenceFromStrings(k+"^{}", v.String())) 1397 } 1398 } 1399 1400 return resultRefs, nil 1401} 1402 1403func objectsToPush(commands []*packp.Command) []plumbing.Hash { 1404 objects := make([]plumbing.Hash, 0, len(commands)) 1405 for _, cmd := range commands { 1406 if cmd.New == plumbing.ZeroHash { 1407 continue 1408 } 1409 objects = append(objects, cmd.New) 1410 } 1411 return objects 1412} 1413 1414func referencesToHashes(refs storer.ReferenceStorer) ([]plumbing.Hash, error) { 1415 iter, err := refs.IterReferences() 1416 if err != nil { 1417 return nil, err 1418 } 1419 1420 var hs []plumbing.Hash 1421 err = iter.ForEach(func(ref *plumbing.Reference) error { 1422 if ref.Type() != plumbing.HashReference { 1423 return nil 1424 } 1425 1426 hs = append(hs, ref.Hash()) 1427 return nil 1428 }) 1429 if err != nil { 1430 return nil, err 1431 } 1432 1433 return hs, nil 1434} 1435 1436func pushHashes( 1437 ctx context.Context, 1438 sess transport.ReceivePackSession, 1439 s storage.Storer, 1440 req *packp.ReferenceUpdateRequest, 1441 hs []plumbing.Hash, 1442 useRefDeltas bool, 1443 allDelete bool, 1444) (*packp.ReportStatus, error) { 1445 rd, wr := io.Pipe() 1446 1447 config, err := s.Config() 1448 if err != nil { 1449 return nil, err 1450 } 1451 1452 // Set buffer size to 1 so the error message can be written when 1453 // ReceivePack fails. Otherwise the goroutine will be blocked writing 1454 // to the channel. 1455 done := make(chan error, 1) 1456 1457 if !allDelete { 1458 req.Packfile = rd 1459 go func() { 1460 e := packfile.NewEncoder(wr, s, useRefDeltas) 1461 if _, err := e.Encode(hs, config.Pack.Window); err != nil { 1462 done <- wr.CloseWithError(err) 1463 return 1464 } 1465 1466 done <- wr.Close() 1467 }() 1468 } else { 1469 close(done) 1470 } 1471 1472 rs, err := sess.ReceivePack(ctx, req) 1473 if err != nil { 1474 // close the pipe to unlock encode write 1475 _ = rd.Close() 1476 return nil, err 1477 } 1478 1479 if err := <-done; err != nil { 1480 return nil, err 1481 } 1482 1483 return rs, nil 1484} 1485 1486func (r *Remote) updateShallow(o *FetchOptions, resp *packp.UploadPackResponse) error { 1487 if o.Depth == 0 || len(resp.Shallows) == 0 { 1488 return nil 1489 } 1490 1491 shallows, err := r.s.Shallow() 1492 if err != nil { 1493 return err 1494 } 1495 1496outer: 1497 for _, s := range resp.Shallows { 1498 for _, oldS := range shallows { 1499 if s == oldS { 1500 continue outer 1501 } 1502 } 1503 shallows = append(shallows, s) 1504 } 1505 1506 return r.s.SetShallow(shallows) 1507} 1508 1509func (r *Remote) checkRequireRemoteRefs(requires []config.RefSpec, remoteRefs storer.ReferenceStorer) error { 1510 for _, require := range requires { 1511 if require.IsWildcard() { 1512 return fmt.Errorf("wildcards not supported in RequireRemoteRefs, got %s", require.String()) 1513 } 1514 1515 name := require.Dst("") 1516 remote, err := remoteRefs.Reference(name) 1517 if err != nil { 1518 return fmt.Errorf("remote ref %s required to be %s but is absent", name.String(), require.Src()) 1519 } 1520 1521 var requireHash string 1522 if require.IsExactSHA1() { 1523 requireHash = require.Src() 1524 } else { 1525 target, err := storer.ResolveReference(remoteRefs, plumbing.ReferenceName(require.Src())) 1526 if err != nil { 1527 return fmt.Errorf("could not resolve ref %s in RequireRemoteRefs", require.Src()) 1528 } 1529 requireHash = target.Hash().String() 1530 } 1531 1532 if remote.Hash().String() != requireHash { 1533 return fmt.Errorf("remote ref %s required to be %s but is %s", name.String(), requireHash, remote.Hash().String()) 1534 } 1535 } 1536 return nil 1537}