Skip to content

Commit

Permalink
git: Fix fetching after shallow clone. Fixes go-git#305
Browse files Browse the repository at this point in the history
Signed-off-by: Arieh Schneier <15041913+AriehSchneier@users.noreply.github.com>
  • Loading branch information
AriehSchneier committed May 12, 2023
1 parent dc2b346 commit 254c9e4
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 32 deletions.
21 changes: 20 additions & 1 deletion remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,7 @@ func getHavesFromRef(
remoteRefs map[plumbing.Hash]bool,
s storage.Storer,
haves map[plumbing.Hash]bool,
shallows *map[plumbing.Hash]struct{},
) error {
h := ref.Hash()
if haves[h] {
Expand Down Expand Up @@ -864,6 +865,21 @@ func getHavesFromRef(
toVisit := maxHavesToVisitPerRef
return walker.ForEach(func(c *object.Commit) error {
haves[c.Hash] = true

// initialise the shallows map
if *shallows == nil {
shallowList, _ := s.Shallow()
*shallows = make(map[plumbing.Hash]struct{}, len(shallowList))
for _, sh := range shallowList {
(*shallows)[sh] = struct{}{}
}
}

// If this is a shallow reference don't try to iterate further
if _, ok := (*shallows)[c.Hash]; ok {
return storer.ErrStop
}

toVisit--
// If toVisit starts out at 0 (indicating there is no
// max), then it will be negative here and we won't stop
Expand All @@ -890,6 +906,9 @@ func getHaves(
return nil, err
}

// create a map of all the shallow references, but only populate on first use
var shallows map[plumbing.Hash]struct{}

for _, ref := range localRefs {
if haves[ref.Hash()] {
continue
Expand All @@ -899,7 +918,7 @@ func getHaves(
continue
}

err = getHavesFromRef(ref, remoteRefs, s, haves)
err = getHavesFromRef(ref, remoteRefs, s, haves, &shallows)
if err != nil {
return nil, err
}
Expand Down
102 changes: 71 additions & 31 deletions remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1386,48 +1386,22 @@ func (s *RemoteSuite) TestPushRequireRemoteRefs(c *C) {
c.Assert(newRef, Not(DeepEquals), oldRef)
}

func (s *RemoteSuite) TestCanPushShasToReference(c *C) {
d, err := ioutil.TempDir("", "TestCanPushShasToReference")
func commitNewFile(c *C, repo *Repository, fileName string) plumbing.Hash {
wt, err := repo.Worktree()
c.Assert(err, IsNil)
if err != nil {
return
}
defer os.RemoveAll(d)

// remote currently forces a plain path for path based remotes inside the PushContext function.
// This makes it impossible, in the current state to use memfs.
// For the sake of readability, use the same osFS everywhere and use plain git repositories on temporary files
remote, err := PlainInit(filepath.Join(d, "remote"), true)
fd, err := wt.Filesystem.Create(fileName)
c.Assert(err, IsNil)
c.Assert(remote, NotNil)

repo, err := PlainInit(filepath.Join(d, "repo"), false)
_, err = fd.Write([]byte("# test file"))
c.Assert(err, IsNil)
c.Assert(repo, NotNil)

fd, err := os.Create(filepath.Join(d, "repo", "README.md"))
c.Assert(err, IsNil)
if err != nil {
return
}
_, err = fd.WriteString("# test repo")
c.Assert(err, IsNil)
if err != nil {
return
}
err = fd.Close()
c.Assert(err, IsNil)
if err != nil {
return
}

wt, err := repo.Worktree()
_, err = wt.Add(fileName)
c.Assert(err, IsNil)
if err != nil {
return
}

wt.Add("README.md")
sha, err := wt.Commit("test commit", &CommitOptions{
Author: &object.Signature{
Name: "test",
Expand All @@ -1441,9 +1415,30 @@ func (s *RemoteSuite) TestCanPushShasToReference(c *C) {
},
})
c.Assert(err, IsNil)

return sha
}

func (s *RemoteSuite) TestCanPushShasToReference(c *C) {
d, err := ioutil.TempDir("", "TestCanPushShasToReference")
c.Assert(err, IsNil)
if err != nil {
return
}
defer os.RemoveAll(d)

// remote currently forces a plain path for path based remotes inside the PushContext function.
// This makes it impossible, in the current state to use memfs.
// For the sake of readability, use the same osFS everywhere and use plain git repositories on temporary files
remote, err := PlainInit(filepath.Join(d, "remote"), true)
c.Assert(err, IsNil)
c.Assert(remote, NotNil)

repo, err := PlainInit(filepath.Join(d, "repo"), false)
c.Assert(err, IsNil)
c.Assert(repo, NotNil)

sha := commitNewFile(c, repo, "README.md")

gitremote, err := repo.CreateRemote(&config.RemoteConfig{
Name: "local",
Expand Down Expand Up @@ -1473,3 +1468,48 @@ func (s *RemoteSuite) TestCanPushShasToReference(c *C) {
}
c.Assert(ref.Hash().String(), Equals, sha.String())
}

func (s *RemoteSuite) TestFetchAfterShallowClone(c *C) {
tempDir, clean := s.TemporalDir()
defer clean()
remoteUrl := filepath.Join(tempDir, "remote")
repoDir := filepath.Join(tempDir, "repo")

// Create a new repo and add more than 1 commit (so we can have a shallow commit)
remote, err := PlainInit(remoteUrl, false)
c.Assert(err, IsNil)
c.Assert(remote, NotNil)

_ = commitNewFile(c, remote, "File1")
_ = commitNewFile(c, remote, "File2")

// Clone the repo with a depth of 1
repo, err := PlainClone(repoDir, false, &CloneOptions{
URL: remoteUrl,
Depth: 1,
Tags: NoTags,
SingleBranch: true,
ReferenceName: "master",
})
c.Assert(err, IsNil)

// Add a new commit to the origin
sha3 := commitNewFile(c, remote, "NewFile")

// Try fetch with depth of 1 again (note, we need to ensure no remote branch remains pointing at the old commit)
r, err := repo.Remote(DefaultRemoteName)
c.Assert(err, IsNil)
s.testFetch(c, r, &FetchOptions{
Depth: 1,
Tags: NoTags,

RefSpecs: []config.RefSpec{
"+refs/heads/master:refs/heads/master",
"+refs/heads/master:refs/remotes/origin/master",
},
}, []*plumbing.Reference{
plumbing.NewReferenceFromStrings("refs/heads/master", sha3.String()),
plumbing.NewReferenceFromStrings("refs/remotes/origin/master", sha3.String()),
plumbing.NewSymbolicReference("HEAD", "refs/heads/master"),
})
}

0 comments on commit 254c9e4

Please sign in to comment.