diff options
-rw-r--r-- | repository.go | 29 | ||||
-rw-r--r-- | repository_test.go | 82 | ||||
-rw-r--r-- | storage/seekable/internal/gitdir/gitdir.go | 2 | ||||
-rw-r--r-- | storage/seekable/storage.go | 46 |
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 +} |