aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--repository.go29
-rw-r--r--repository_test.go82
-rw-r--r--storage/seekable/internal/gitdir/gitdir.go2
-rw-r--r--storage/seekable/storage.go46
4 files changed, 147 insertions, 12 deletions
diff --git a/repository.go b/repository.go
index c05afdb..866f9ff 100644
--- a/repository.go
+++ b/repository.go
@@ -218,3 +218,32 @@ func (r *Repository) Object(h core.Hash) (Object, error) {
return nil, core.ErrInvalidType
}
}
+
+// Head returns the hash of the HEAD of the repository or the head of a
+// remote, if one is passed.
+func (r *Repository) Head(remote string) (core.Hash, error) {
+ if remote == "" {
+ return r.localHead()
+ }
+
+ return r.remoteHead(remote)
+}
+
+func (r *Repository) remoteHead(remote string) (core.Hash, error) {
+ rem, ok := r.Remotes[remote]
+ if !ok {
+ return core.ZeroHash, fmt.Errorf("unable to find remote %q", remote)
+ }
+
+ return rem.Head()
+}
+
+func (r *Repository) localHead() (core.Hash, error) {
+ storage, ok := r.Storage.(*seekable.ObjectStorage)
+ if !ok {
+ return core.ZeroHash,
+ fmt.Errorf("cannot retrieve local head: no local data found")
+ }
+
+ return storage.Head()
+}
diff --git a/repository_test.go b/repository_test.go
index 7d4abb5..80a765c 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -13,19 +13,26 @@ import (
. "gopkg.in/check.v1"
)
-var dirFixtures = [...]struct {
+var dirFixturesInit = [...]struct {
name string
tgz string
+ head string
}{
{
name: "binrels",
tgz: "storage/seekable/internal/gitdir/fixtures/alcortesm-binary-relations.tgz",
+ head: "c44b5176e99085c8fe36fa27b045590a7b9d34c9",
},
}
+type dirFixture struct {
+ path string
+ head core.Hash
+}
+
type SuiteRepository struct {
- repos map[string]*Repository
- dirFixturePaths map[string]string
+ repos map[string]*Repository
+ dirFixtures map[string]dirFixture
}
var _ = Suite(&SuiteRepository{})
@@ -33,22 +40,25 @@ var _ = Suite(&SuiteRepository{})
func (s *SuiteRepository) SetUpSuite(c *C) {
s.repos = unpackFixtures(c, tagFixtures, treeWalkerFixtures)
- s.dirFixturePaths = make(map[string]string, len(dirFixtures))
- for _, fix := range dirFixtures {
+ s.dirFixtures = make(map[string]dirFixture, len(dirFixturesInit))
+ for _, fix := range dirFixturesInit {
com := Commentf("fixture name = %s\n", fix.name)
path, err := tgz.Extract(fix.tgz)
c.Assert(err, IsNil, com)
- s.dirFixturePaths[fix.name] = path
+ s.dirFixtures[fix.name] = dirFixture{
+ path: path,
+ head: core.NewHash(fix.head),
+ }
}
}
func (s *SuiteRepository) TearDownSuite(c *C) {
- for name, path := range s.dirFixturePaths {
- err := os.RemoveAll(path)
+ for name, fix := range s.dirFixtures {
+ err := os.RemoveAll(fix.path)
c.Assert(err, IsNil, Commentf("cannot delete tmp dir for fixture %s: %s\n",
- name, path))
+ name, fix.path))
}
}
@@ -66,9 +76,9 @@ func (s *SuiteRepository) TestNewRepositoryWithAuth(c *C) {
}
func (s *SuiteRepository) TestNewRepositoryFromFS(c *C) {
- for name, path := range s.dirFixturePaths {
+ for name, fix := range s.dirFixtures {
fs := fs.NewOS()
- gitPath := fs.Join(path, ".git/")
+ gitPath := fs.Join(fix.path, ".git/")
com := Commentf("dir fixture %q → %q\n", name, gitPath)
repo, err := NewRepositoryFromFS(fs, gitPath)
c.Assert(err, IsNil, com)
@@ -205,3 +215,53 @@ func (s *SuiteRepository) TestCommitIterClosePanic(c *C) {
c.Assert(err, IsNil)
commits.Close()
}
+
+func (s *SuiteRepository) TestHeadFromFs(c *C) {
+ for name, fix := range s.dirFixtures {
+ fs := fs.NewOS()
+ gitPath := fs.Join(fix.path, ".git/")
+ com := Commentf("dir fixture %q → %q\n", name, gitPath)
+ repo, err := NewRepositoryFromFS(fs, gitPath)
+ c.Assert(err, IsNil, com)
+
+ head, err := repo.Head("")
+ c.Assert(err, IsNil)
+
+ c.Assert(head, Equals, fix.head)
+ }
+}
+
+func (s *SuiteRepository) TestHeadFromRemote(c *C) {
+ r, err := NewRepository(RepositoryFixture, nil)
+ c.Assert(err, IsNil)
+
+ upSrv := &MockGitUploadPackService{}
+ r.Remotes[DefaultRemoteName].upSrv = upSrv
+ err = r.Remotes[DefaultRemoteName].Connect()
+ c.Assert(err, IsNil)
+
+ info, err := upSrv.Info()
+ c.Assert(err, IsNil)
+ expected := info.Head
+
+ obtained, err := r.Head(DefaultRemoteName)
+ c.Assert(err, IsNil)
+
+ c.Assert(obtained, Equals, expected)
+}
+
+func (s *SuiteRepository) TestHeadErrors(c *C) {
+ r, err := NewRepository(RepositoryFixture, nil)
+ c.Assert(err, IsNil)
+
+ upSrv := &MockGitUploadPackService{}
+ r.Remotes[DefaultRemoteName].upSrv = upSrv
+
+ remote := "not found"
+ _, err = r.Head(remote)
+ c.Assert(err, ErrorMatches, fmt.Sprintf("unable to find remote %q", remote))
+
+ remote = ""
+ _, err = r.Head(remote)
+ c.Assert(err, ErrorMatches, "cannot retrieve local head: no local data found")
+}
diff --git a/storage/seekable/internal/gitdir/gitdir.go b/storage/seekable/internal/gitdir/gitdir.go
index bfdf030..d3149ee 100644
--- a/storage/seekable/internal/gitdir/gitdir.go
+++ b/storage/seekable/internal/gitdir/gitdir.go
@@ -126,7 +126,7 @@ func (d *GitDir) Packfile() (fs.FS, string, error) {
return nil, "", ErrPackfileNotFound
}
-// Packfile returns the path of the idx file (really, it returns the
+// Idxfile returns the path of the idx file (really, it returns the
// path of the first file in the "objects/pack/" directory with an
// ".idx" extension.
func (d *GitDir) Idxfile() (fs.FS, string, error) {
diff --git a/storage/seekable/storage.go b/storage/seekable/storage.go
index 8d75700..db436c8 100644
--- a/storage/seekable/storage.go
+++ b/storage/seekable/storage.go
@@ -3,6 +3,7 @@ package seekable
import (
"fmt"
"os"
+ "strings"
"gopkg.in/src-d/go-git.v3/core"
"gopkg.in/src-d/go-git.v3/formats/packfile"
@@ -150,3 +151,48 @@ func (s *ObjectStorage) Iter(t core.ObjectType) (core.ObjectIter, error) {
return core.NewObjectSliceIter(objects), nil
}
+
+const (
+ headErrPrefix = "cannot get HEAD reference:"
+ symrefCapability = "symref"
+ headRefPrefix = "HEAD:"
+)
+
+// Head returns the hash of the HEAD reference
+func (s *ObjectStorage) Head() (core.Hash, error) {
+ cap, err := s.dir.Capabilities()
+ if err != nil {
+ return core.ZeroHash, fmt.Errorf("%s %s", headErrPrefix, err)
+ }
+
+ ok := cap.Supports(symrefCapability)
+ if !ok {
+ return core.ZeroHash,
+ fmt.Errorf("%s symref capability not supported", headErrPrefix)
+ }
+
+ symrefs := cap.Get(symrefCapability)
+ var headRef string
+ for _, ref := range symrefs.Values {
+ if strings.HasPrefix(ref, headRefPrefix) {
+ headRef = strings.TrimPrefix(ref, headRefPrefix)
+ }
+ }
+ if headRef == "" {
+ return core.ZeroHash, fmt.Errorf("%s HEAD reference not found",
+ headErrPrefix)
+ }
+
+ refs, err := s.dir.Refs()
+ if err != nil {
+ return core.ZeroHash, fmt.Errorf("%s %s", headErrPrefix, err)
+ }
+
+ head, ok := refs[headRef]
+ if !ok {
+ return core.ZeroHash, fmt.Errorf("%s reference %q not found",
+ headErrPrefix, headRef)
+ }
+
+ return head, nil
+}