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

Commit 5413c7a

Browse files
alcortesmmcuadros
authored andcommitted
Repository head (#61)
* add Repository.Head() tests * add head support for remote repos and more tests * add head support for local repos * clean code * remove dead code
1 parent dc1e2bd commit 5413c7a

File tree

4 files changed

+147
-12
lines changed

4 files changed

+147
-12
lines changed

repository.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,32 @@ func (r *Repository) Object(h core.Hash) (Object, error) {
218218
return nil, core.ErrInvalidType
219219
}
220220
}
221+
222+
// Head returns the hash of the HEAD of the repository or the head of a
223+
// remote, if one is passed.
224+
func (r *Repository) Head(remote string) (core.Hash, error) {
225+
if remote == "" {
226+
return r.localHead()
227+
}
228+
229+
return r.remoteHead(remote)
230+
}
231+
232+
func (r *Repository) remoteHead(remote string) (core.Hash, error) {
233+
rem, ok := r.Remotes[remote]
234+
if !ok {
235+
return core.ZeroHash, fmt.Errorf("unable to find remote %q", remote)
236+
}
237+
238+
return rem.Head()
239+
}
240+
241+
func (r *Repository) localHead() (core.Hash, error) {
242+
storage, ok := r.Storage.(*seekable.ObjectStorage)
243+
if !ok {
244+
return core.ZeroHash,
245+
fmt.Errorf("cannot retrieve local head: no local data found")
246+
}
247+
248+
return storage.Head()
249+
}

repository_test.go

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,52 @@ import (
1313
. "gopkg.in/check.v1"
1414
)
1515

16-
var dirFixtures = [...]struct {
16+
var dirFixturesInit = [...]struct {
1717
name string
1818
tgz string
19+
head string
1920
}{
2021
{
2122
name: "binrels",
2223
tgz: "storage/seekable/internal/gitdir/fixtures/alcortesm-binary-relations.tgz",
24+
head: "c44b5176e99085c8fe36fa27b045590a7b9d34c9",
2325
},
2426
}
2527

28+
type dirFixture struct {
29+
path string
30+
head core.Hash
31+
}
32+
2633
type SuiteRepository struct {
27-
repos map[string]*Repository
28-
dirFixturePaths map[string]string
34+
repos map[string]*Repository
35+
dirFixtures map[string]dirFixture
2936
}
3037

3138
var _ = Suite(&SuiteRepository{})
3239

3340
func (s *SuiteRepository) SetUpSuite(c *C) {
3441
s.repos = unpackFixtures(c, tagFixtures, treeWalkerFixtures)
3542

36-
s.dirFixturePaths = make(map[string]string, len(dirFixtures))
37-
for _, fix := range dirFixtures {
43+
s.dirFixtures = make(map[string]dirFixture, len(dirFixturesInit))
44+
for _, fix := range dirFixturesInit {
3845
com := Commentf("fixture name = %s\n", fix.name)
3946

4047
path, err := tgz.Extract(fix.tgz)
4148
c.Assert(err, IsNil, com)
4249

43-
s.dirFixturePaths[fix.name] = path
50+
s.dirFixtures[fix.name] = dirFixture{
51+
path: path,
52+
head: core.NewHash(fix.head),
53+
}
4454
}
4555
}
4656

4757
func (s *SuiteRepository) TearDownSuite(c *C) {
48-
for name, path := range s.dirFixturePaths {
49-
err := os.RemoveAll(path)
58+
for name, fix := range s.dirFixtures {
59+
err := os.RemoveAll(fix.path)
5060
c.Assert(err, IsNil, Commentf("cannot delete tmp dir for fixture %s: %s\n",
51-
name, path))
61+
name, fix.path))
5262
}
5363
}
5464

@@ -66,9 +76,9 @@ func (s *SuiteRepository) TestNewRepositoryWithAuth(c *C) {
6676
}
6777

6878
func (s *SuiteRepository) TestNewRepositoryFromFS(c *C) {
69-
for name, path := range s.dirFixturePaths {
79+
for name, fix := range s.dirFixtures {
7080
fs := fs.NewOS()
71-
gitPath := fs.Join(path, ".git/")
81+
gitPath := fs.Join(fix.path, ".git/")
7282
com := Commentf("dir fixture %q → %q\n", name, gitPath)
7383
repo, err := NewRepositoryFromFS(fs, gitPath)
7484
c.Assert(err, IsNil, com)
@@ -205,3 +215,53 @@ func (s *SuiteRepository) TestCommitIterClosePanic(c *C) {
205215
c.Assert(err, IsNil)
206216
commits.Close()
207217
}
218+
219+
func (s *SuiteRepository) TestHeadFromFs(c *C) {
220+
for name, fix := range s.dirFixtures {
221+
fs := fs.NewOS()
222+
gitPath := fs.Join(fix.path, ".git/")
223+
com := Commentf("dir fixture %q → %q\n", name, gitPath)
224+
repo, err := NewRepositoryFromFS(fs, gitPath)
225+
c.Assert(err, IsNil, com)
226+
227+
head, err := repo.Head("")
228+
c.Assert(err, IsNil)
229+
230+
c.Assert(head, Equals, fix.head)
231+
}
232+
}
233+
234+
func (s *SuiteRepository) TestHeadFromRemote(c *C) {
235+
r, err := NewRepository(RepositoryFixture, nil)
236+
c.Assert(err, IsNil)
237+
238+
upSrv := &MockGitUploadPackService{}
239+
r.Remotes[DefaultRemoteName].upSrv = upSrv
240+
err = r.Remotes[DefaultRemoteName].Connect()
241+
c.Assert(err, IsNil)
242+
243+
info, err := upSrv.Info()
244+
c.Assert(err, IsNil)
245+
expected := info.Head
246+
247+
obtained, err := r.Head(DefaultRemoteName)
248+
c.Assert(err, IsNil)
249+
250+
c.Assert(obtained, Equals, expected)
251+
}
252+
253+
func (s *SuiteRepository) TestHeadErrors(c *C) {
254+
r, err := NewRepository(RepositoryFixture, nil)
255+
c.Assert(err, IsNil)
256+
257+
upSrv := &MockGitUploadPackService{}
258+
r.Remotes[DefaultRemoteName].upSrv = upSrv
259+
260+
remote := "not found"
261+
_, err = r.Head(remote)
262+
c.Assert(err, ErrorMatches, fmt.Sprintf("unable to find remote %q", remote))
263+
264+
remote = ""
265+
_, err = r.Head(remote)
266+
c.Assert(err, ErrorMatches, "cannot retrieve local head: no local data found")
267+
}

storage/seekable/internal/gitdir/gitdir.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func (d *GitDir) Packfile() (fs.FS, string, error) {
126126
return nil, "", ErrPackfileNotFound
127127
}
128128

129-
// Packfile returns the path of the idx file (really, it returns the
129+
// Idxfile returns the path of the idx file (really, it returns the
130130
// path of the first file in the "objects/pack/" directory with an
131131
// ".idx" extension.
132132
func (d *GitDir) Idxfile() (fs.FS, string, error) {

storage/seekable/storage.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package seekable
33
import (
44
"fmt"
55
"os"
6+
"strings"
67

78
"gopkg.in/src-d/go-git.v3/core"
89
"gopkg.in/src-d/go-git.v3/formats/packfile"
@@ -150,3 +151,48 @@ func (s *ObjectStorage) Iter(t core.ObjectType) (core.ObjectIter, error) {
150151

151152
return core.NewObjectSliceIter(objects), nil
152153
}
154+
155+
const (
156+
headErrPrefix = "cannot get HEAD reference:"
157+
symrefCapability = "symref"
158+
headRefPrefix = "HEAD:"
159+
)
160+
161+
// Head returns the hash of the HEAD reference
162+
func (s *ObjectStorage) Head() (core.Hash, error) {
163+
cap, err := s.dir.Capabilities()
164+
if err != nil {
165+
return core.ZeroHash, fmt.Errorf("%s %s", headErrPrefix, err)
166+
}
167+
168+
ok := cap.Supports(symrefCapability)
169+
if !ok {
170+
return core.ZeroHash,
171+
fmt.Errorf("%s symref capability not supported", headErrPrefix)
172+
}
173+
174+
symrefs := cap.Get(symrefCapability)
175+
var headRef string
176+
for _, ref := range symrefs.Values {
177+
if strings.HasPrefix(ref, headRefPrefix) {
178+
headRef = strings.TrimPrefix(ref, headRefPrefix)
179+
}
180+
}
181+
if headRef == "" {
182+
return core.ZeroHash, fmt.Errorf("%s HEAD reference not found",
183+
headErrPrefix)
184+
}
185+
186+
refs, err := s.dir.Refs()
187+
if err != nil {
188+
return core.ZeroHash, fmt.Errorf("%s %s", headErrPrefix, err)
189+
}
190+
191+
head, ok := refs[headRef]
192+
if !ok {
193+
return core.ZeroHash, fmt.Errorf("%s reference %q not found",
194+
headErrPrefix, headRef)
195+
}
196+
197+
return head, nil
198+
}

0 commit comments

Comments
 (0)