Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit c337614

Browse files
committed
remote: add the last 100 commits for each ref in haves list
If the local ref is not an ancestor of the remote ref being fetched, then when we send an UploadPack request with that local ref as one of the Haves, the remote will not recognize it, and will think we are asking for the entire history of the repo, even if there's a common ancestor. To do this right, we need to support the multi-ack protocol so we can negotiate a common commit. That's hard though; this is a quick fix just to include the previous 100 commits for each local ref in the Haves list, and hope that one of them is the common commit.
1 parent 30a99a2 commit c337614

File tree

2 files changed

+58
-4
lines changed

2 files changed

+58
-4
lines changed

remote.go

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (storer.ReferenceSt
284284

285285
req.Wants, err = getWants(r.s, refs)
286286
if len(req.Wants) > 0 {
287-
req.Haves, err = getHaves(localRefs)
287+
req.Haves, err = getHaves(localRefs, remoteRefs, r.s)
288288
if err != nil {
289289
return nil, err
290290
}
@@ -490,8 +490,29 @@ func (r *Remote) references() ([]*plumbing.Reference, error) {
490490
return localRefs, nil
491491
}
492492

493-
func getHaves(localRefs []*plumbing.Reference) ([]plumbing.Hash, error) {
493+
func getHaves(
494+
localRefs []*plumbing.Reference,
495+
remoteRefs storer.ReferenceStorer,
496+
s storage.Storer,
497+
) ([]plumbing.Hash, error) {
494498
haves := map[plumbing.Hash]bool{}
499+
500+
// Build a map of all the remote references, to avoid loading too
501+
// many parent commits for references we know don't need to be
502+
// transferred.
503+
stopEarlyRefs := map[plumbing.Hash]bool{}
504+
iter, err := remoteRefs.IterReferences()
505+
if err != nil {
506+
return nil, err
507+
}
508+
err = iter.ForEach(func(ref *plumbing.Reference) error {
509+
stopEarlyRefs[ref.Hash()] = true
510+
return nil
511+
})
512+
if err != nil {
513+
return nil, err
514+
}
515+
495516
for _, ref := range localRefs {
496517
if haves[ref.Hash()] == true {
497518
continue
@@ -501,7 +522,36 @@ func getHaves(localRefs []*plumbing.Reference) ([]plumbing.Hash, error) {
501522
continue
502523
}
503524

504-
haves[ref.Hash()] = true
525+
// No need to load the commit if we know the remote already
526+
// has this hash.
527+
if stopEarlyRefs[ref.Hash()] {
528+
haves[ref.Hash()] = true
529+
continue
530+
}
531+
532+
commit, err := object.GetCommit(s, ref.Hash())
533+
if err != nil {
534+
haves[ref.Hash()] = true
535+
continue
536+
}
537+
538+
// Until go-git supports proper commit negotiation during an
539+
// upload pack request, include up to 100 commits from the
540+
// history of each ref.
541+
walker := object.NewCommitPreorderIter(commit, haves, nil)
542+
const maxToVisit = 100
543+
toVisit := maxToVisit
544+
err = walker.ForEach(func(c *object.Commit) error {
545+
haves[c.Hash] = true
546+
toVisit--
547+
if toVisit == 0 || stopEarlyRefs[c.Hash] {
548+
return storer.ErrStop
549+
}
550+
return nil
551+
})
552+
if err != nil {
553+
return nil, err
554+
}
505555
}
506556

507557
var result []plumbing.Hash

remote_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,10 @@ func (s *RemoteSuite) TestPushWrongRemoteName(c *C) {
625625
}
626626

627627
func (s *RemoteSuite) TestGetHaves(c *C) {
628+
f := fixtures.Basic().One()
629+
sto, err := filesystem.NewStorage(f.DotGit())
630+
c.Assert(err, IsNil)
631+
628632
var localRefs = []*plumbing.Reference{
629633
plumbing.NewReferenceFromStrings(
630634
"foo",
@@ -640,7 +644,7 @@ func (s *RemoteSuite) TestGetHaves(c *C) {
640644
),
641645
}
642646

643-
l, err := getHaves(localRefs)
647+
l, err := getHaves(localRefs, memory.NewStorage(), sto)
644648
c.Assert(err, IsNil)
645649
c.Assert(l, HasLen, 2)
646650
}

0 commit comments

Comments
 (0)