Merge pull request #706 from antham/resolve-commit-sha1

Resolve full commit sha
diff --git a/remote.go b/remote.go
index 666c9f5..4b86955 100644
--- a/remote.go
+++ b/remote.go
@@ -976,9 +976,24 @@
 }
 
 func (r *Remote) updateShallow(o *FetchOptions, resp *packp.UploadPackResponse) error {
-	if o.Depth == 0 {
+	if o.Depth == 0 || len(resp.Shallows) == 0 {
 		return nil
 	}
 
-	return r.s.SetShallow(resp.Shallows)
+	shallows, err := r.s.Shallow()
+	if err != nil {
+		return err
+	}
+
+outer:
+	for _, s := range resp.Shallows {
+		for _, oldS := range shallows {
+			if s == oldS {
+				continue outer
+			}
+		}
+		shallows = append(shallows, s)
+	}
+
+	return r.s.SetShallow(shallows)
 }
diff --git a/remote_test.go b/remote_test.go
index e586e7a..82ec1fc 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -9,6 +9,7 @@
 
 	"gopkg.in/src-d/go-git.v4/config"
 	"gopkg.in/src-d/go-git.v4/plumbing"
+	"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
 	"gopkg.in/src-d/go-git.v4/plumbing/storer"
 	"gopkg.in/src-d/go-git.v4/storage"
 	"gopkg.in/src-d/go-git.v4/storage/filesystem"
@@ -741,3 +742,54 @@
 		c.Assert(found, Equals, true)
 	}
 }
+
+func (s *RemoteSuite) TestUpdateShallows(c *C) {
+	hashes := []plumbing.Hash{
+		plumbing.NewHash("0000000000000000000000000000000000000001"),
+		plumbing.NewHash("0000000000000000000000000000000000000002"),
+		plumbing.NewHash("0000000000000000000000000000000000000003"),
+		plumbing.NewHash("0000000000000000000000000000000000000004"),
+		plumbing.NewHash("0000000000000000000000000000000000000005"),
+		plumbing.NewHash("0000000000000000000000000000000000000006"),
+	}
+
+	tests := []struct {
+		hashes []plumbing.Hash
+		result []plumbing.Hash
+	}{
+		// add to empty shallows
+		{hashes[0:2], hashes[0:2]},
+		// add new hashes
+		{hashes[2:4], hashes[0:4]},
+		// add some hashes already in shallow list
+		{hashes[2:6], hashes[0:6]},
+		// add all hashes
+		{hashes[0:6], hashes[0:6]},
+		// add empty list
+		{nil, hashes[0:6]},
+	}
+
+	remote := newRemote(memory.NewStorage(), &config.RemoteConfig{
+		Name: DefaultRemoteName,
+	})
+
+	shallows, err := remote.s.Shallow()
+	c.Assert(err, IsNil)
+	c.Assert(len(shallows), Equals, 0)
+
+	resp := new(packp.UploadPackResponse)
+	o := &FetchOptions{
+		Depth: 1,
+	}
+
+	for _, t := range tests {
+		resp.Shallows = t.hashes
+		err = remote.updateShallow(o, resp)
+		c.Assert(err, IsNil)
+
+		shallow, err := remote.s.Shallow()
+		c.Assert(err, IsNil)
+		c.Assert(len(shallow), Equals, len(t.result))
+		c.Assert(shallow, DeepEquals, t.result)
+	}
+}
diff --git a/repository_test.go b/repository_test.go
index 3e94db9..b78fbb7 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -1625,3 +1625,66 @@
 
 	return c.Run()
 }
+
+func (s *RepositorySuite) TestBrokenMultipleShallowFetch(c *C) {
+	r, _ := Init(memory.NewStorage(), nil)
+	_, err := r.CreateRemote(&config.RemoteConfig{
+		Name: DefaultRemoteName,
+		URLs: []string{s.GetBasicLocalRepositoryURL()},
+	})
+	c.Assert(err, IsNil)
+
+	c.Assert(r.Fetch(&FetchOptions{
+		Depth:    2,
+		RefSpecs: []config.RefSpec{config.RefSpec("refs/heads/master:refs/heads/master")},
+	}), IsNil)
+
+	shallows, err := r.Storer.Shallow()
+	c.Assert(err, IsNil)
+	c.Assert(len(shallows), Equals, 1)
+
+	ref, err := r.Reference("refs/heads/master", true)
+	c.Assert(err, IsNil)
+	cobj, err := r.CommitObject(ref.Hash())
+	c.Assert(err, IsNil)
+	c.Assert(cobj, NotNil)
+	err = object.NewCommitPreorderIter(cobj, nil, nil).ForEach(func(c *object.Commit) error {
+		for _, ph := range c.ParentHashes {
+			for _, h := range shallows {
+				if ph == h {
+					return storer.ErrStop
+				}
+			}
+		}
+
+		return nil
+	})
+	c.Assert(err, IsNil)
+
+	c.Assert(r.Fetch(&FetchOptions{
+		Depth:    5,
+		RefSpecs: []config.RefSpec{config.RefSpec("refs/heads/*:refs/heads/*")},
+	}), IsNil)
+
+	shallows, err = r.Storer.Shallow()
+	c.Assert(err, IsNil)
+	c.Assert(len(shallows), Equals, 3)
+
+	ref, err = r.Reference("refs/heads/master", true)
+	c.Assert(err, IsNil)
+	cobj, err = r.CommitObject(ref.Hash())
+	c.Assert(err, IsNil)
+	c.Assert(cobj, NotNil)
+	err = object.NewCommitPreorderIter(cobj, nil, nil).ForEach(func(c *object.Commit) error {
+		for _, ph := range c.ParentHashes {
+			for _, h := range shallows {
+				if ph == h {
+					return storer.ErrStop
+				}
+			}
+		}
+
+		return nil
+	})
+	c.Assert(err, IsNil)
+}
diff --git a/storage/filesystem/internal/dotgit/dotgit.go b/storage/filesystem/internal/dotgit/dotgit.go
index 6f0f1a5..52b621c 100644
--- a/storage/filesystem/internal/dotgit/dotgit.go
+++ b/storage/filesystem/internal/dotgit/dotgit.go
@@ -162,8 +162,11 @@
 
 		n := f.Name()
 		h := plumbing.NewHash(n[5 : len(n)-5]) //pack-(hash).pack
+		if h.IsZero() {
+			// Ignore files with badly-formatted names.
+			continue
+		}
 		packs = append(packs, h)
-
 	}
 
 	return packs, nil
@@ -255,7 +258,12 @@
 			}
 
 			for _, o := range d {
-				err = fun(plumbing.NewHash(base + o.Name()))
+				h := plumbing.NewHash(base + o.Name())
+				if h.IsZero() {
+					// Ignore files with badly-formatted names.
+					continue
+				}
+				err = fun(h)
 				if err != nil {
 					return err
 				}
diff --git a/storage/filesystem/internal/dotgit/dotgit_test.go b/storage/filesystem/internal/dotgit/dotgit_test.go
index 2c43295..7733eef 100644
--- a/storage/filesystem/internal/dotgit/dotgit_test.go
+++ b/storage/filesystem/internal/dotgit/dotgit_test.go
@@ -151,6 +151,7 @@
 }
 
 func BenchmarkRefMultipleTimes(b *testing.B) {
+	fixtures.Init()
 	fs := fixtures.Basic().ByTag(".git").One().DotGit()
 	refname := plumbing.ReferenceName("refs/remotes/origin/branch")
 
@@ -418,7 +419,7 @@
 	return nil
 }
 
-func (s *SuiteDotGit) TestObjectsPack(c *C) {
+func (s *SuiteDotGit) TestObjectPacks(c *C) {
 	f := fixtures.Basic().ByTag(".git").One()
 	fs := f.DotGit()
 	dir := New(fs)
@@ -427,6 +428,18 @@
 	c.Assert(err, IsNil)
 	c.Assert(hashes, HasLen, 1)
 	c.Assert(hashes[0], Equals, f.PackfileHash)
+
+	// Make sure that a random file in the pack directory doesn't
+	// break everything.
+	badFile, err := fs.Create("objects/pack/OOPS_THIS_IS_NOT_RIGHT.pack")
+	c.Assert(err, IsNil)
+	err = badFile.Close()
+	c.Assert(err, IsNil)
+
+	hashes2, err := dir.ObjectPacks()
+	c.Assert(err, IsNil)
+	c.Assert(hashes2, HasLen, 1)
+	c.Assert(hashes[0], Equals, hashes2[0])
 }
 
 func (s *SuiteDotGit) TestObjectPack(c *C) {
diff --git a/storage/filesystem/shallow.go b/storage/filesystem/shallow.go
index 4b2e2dc..173767c 100644
--- a/storage/filesystem/shallow.go
+++ b/storage/filesystem/shallow.go
@@ -41,6 +41,8 @@
 		return nil, err
 	}
 
+	defer ioutil.CheckClose(f, &err)
+
 	var hash []plumbing.Hash
 
 	scn := bufio.NewScanner(f)