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

Commit 5e100bd

Browse files
committed
Added support for annotated tags
1 parent f23141a commit 5e100bd

File tree

5 files changed

+297
-1
lines changed

5 files changed

+297
-1
lines changed

repository.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,26 @@ func (r *Repository) Commits() *CommitIter {
110110
return NewCommitIter(r, r.Storage.Iter(core.CommitObject))
111111
}
112112

113+
// Tag returns a tag with the given hash.
114+
func (r *Repository) Tag(h core.Hash) (*Tag, error) {
115+
obj, err := r.Storage.Get(h)
116+
if err != nil {
117+
if err == core.ObjectNotFoundErr {
118+
return nil, ObjectNotFoundErr
119+
}
120+
return nil, err
121+
}
122+
123+
tag := &Tag{r: r}
124+
return tag, tag.Decode(obj)
125+
}
126+
127+
// Tags returns a TagIter that can step through all of the annotated tags
128+
// in the repository.
129+
func (r *Repository) Tags() *TagIter {
130+
return NewTagIter(r, r.Storage.Iter(core.TagObject))
131+
}
132+
113133
// Tree return the tree with the given hash
114134
func (r *Repository) Tree(h core.Hash) (*Tree, error) {
115135
obj, err := r.Storage.Get(h)

repository_test.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
package git
22

33
import (
4+
"fmt"
5+
46
"gopkg.in/src-d/go-git.v3/clients/http"
57
"gopkg.in/src-d/go-git.v3/core"
68

79
. "gopkg.in/check.v1"
810
)
911

10-
type SuiteRepository struct{}
12+
type SuiteRepository struct {
13+
repos map[string]*Repository
14+
}
1115

1216
var _ = Suite(&SuiteRepository{})
1317

18+
func (s *SuiteRepository) SetUpTest(c *C) {
19+
s.repos = unpackFixtures(c, tagFixtures)
20+
}
21+
1422
func (s *SuiteRepository) TestNewRepository(c *C) {
1523
r, err := NewRepository(RepositoryFixture, nil)
1624
c.Assert(err, IsNil)
@@ -77,6 +85,28 @@ func (s *SuiteRepository) TestCommits(c *C) {
7785
c.Assert(count, Equals, 8)
7886
}
7987

88+
func (s *SuiteRepository) TestTag(c *C) {
89+
for i, t := range tagTests {
90+
r, ok := s.repos[t.repo]
91+
c.Assert(ok, Equals, true)
92+
k := 0
93+
for hash, expected := range t.tags {
94+
tag, err := r.Tag(core.NewHash(hash))
95+
c.Assert(err, IsNil)
96+
testTagExpected(c, tag, expected, fmt.Sprintf("subtest %d, tag %d: ", i, k))
97+
k++
98+
}
99+
}
100+
}
101+
102+
func (s *SuiteRepository) TestTags(c *C) {
103+
for i, t := range tagTests {
104+
r, ok := s.repos[t.repo]
105+
c.Assert(ok, Equals, true)
106+
testTagIter(c, r.Tags(), t.tags, fmt.Sprintf("subtest %d, ", i))
107+
}
108+
}
109+
80110
func (s *SuiteRepository) TestCommitIterClosePanic(c *C) {
81111
r, err := NewRepository(RepositoryFixture, nil)
82112
r.Remotes["origin"].upSrv = &MockGitUploadPackService{}

storage/memory/storage.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type ObjectStorage struct {
1414
Commits map[core.Hash]core.Object
1515
Trees map[core.Hash]core.Object
1616
Blobs map[core.Hash]core.Object
17+
Tags map[core.Hash]core.Object
1718
}
1819

1920
// NewObjectStorage returns a new empty ObjectStorage
@@ -23,6 +24,7 @@ func NewObjectStorage() *ObjectStorage {
2324
Commits: make(map[core.Hash]core.Object, 0),
2425
Trees: make(map[core.Hash]core.Object, 0),
2526
Blobs: make(map[core.Hash]core.Object, 0),
27+
Tags: make(map[core.Hash]core.Object, 0),
2628
}
2729
}
2830

@@ -43,6 +45,8 @@ func (o *ObjectStorage) Set(obj core.Object) (core.Hash, error) {
4345
o.Trees[h] = o.Objects[h]
4446
case core.BlobObject:
4547
o.Blobs[h] = o.Objects[h]
48+
case core.TagObject:
49+
o.Tags[h] = o.Objects[h]
4650
default:
4751
return h, ErrUnsupportedObjectType
4852
}
@@ -70,6 +74,8 @@ func (o *ObjectStorage) Iter(t core.ObjectType) core.ObjectIter {
7074
series = flattenObjectMap(o.Trees)
7175
case core.BlobObject:
7276
series = flattenObjectMap(o.Blobs)
77+
case core.TagObject:
78+
series = flattenObjectMap(o.Tags)
7379
}
7480
return core.NewObjectSliceIter(series)
7581
}

tag.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package git
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"fmt"
7+
"io"
8+
"io/ioutil"
9+
10+
"gopkg.in/src-d/go-git.v3/core"
11+
)
12+
13+
// Tag represents an annotated tag object. It points to a single git object of
14+
// any type, but tags typically are applied to commit or blob objects. It
15+
// provides a reference that associates the target with a tag name. It also
16+
// contains meta-information about the tag, including the tagger, tag date and
17+
// message.
18+
//
19+
// https://git-scm.com/book/en/v2/Git-Internals-Git-References#Tags
20+
type Tag struct {
21+
Hash core.Hash
22+
Type core.ObjectType
23+
Name string
24+
Tagger Signature
25+
Message string
26+
27+
object core.Hash
28+
r *Repository
29+
}
30+
31+
// Decode transforms a core.Object into a Tag struct.
32+
func (t *Tag) Decode(o core.Object) error {
33+
if o.Type() != core.TagObject {
34+
return ErrUnsupportedObject
35+
}
36+
37+
t.Hash = o.Hash()
38+
39+
r := bufio.NewReader(o.Reader())
40+
for {
41+
line, err := r.ReadSlice('\n')
42+
if err != nil && err != io.EOF {
43+
return err
44+
}
45+
46+
line = bytes.TrimSpace(line)
47+
if len(line) == 0 {
48+
break // Start of message
49+
}
50+
51+
split := bytes.SplitN(line, []byte{' '}, 2)
52+
switch string(split[0]) {
53+
case "object":
54+
t.object = core.NewHash(string(split[1]))
55+
case "type":
56+
t.Type, err = core.ParseObjectType(string(split[1]))
57+
if err != nil {
58+
return err
59+
}
60+
case "tag":
61+
t.Name = string(split[1])
62+
case "tagger":
63+
t.Tagger.Decode(split[1])
64+
}
65+
66+
if err == io.EOF {
67+
return nil
68+
}
69+
}
70+
71+
data, err := ioutil.ReadAll(r)
72+
if err != nil {
73+
return err
74+
}
75+
t.Message = string(data)
76+
77+
return nil
78+
}
79+
80+
// Commit returns the commit pointed to by the tag. If the tag points to a
81+
// different type of object an error will be returned.
82+
func (t *Tag) Commit() (*Commit, error) {
83+
return t.r.Commit(t.object)
84+
}
85+
86+
// Tree returns the tree pointed to by the tag. If the tag points to a
87+
// different type of object an error will be returned.
88+
func (t *Tag) Tree() (*Tree, error) {
89+
// TODO: If the tag is of type commit, follow the commit to its tree?
90+
return t.r.Tree(t.object)
91+
}
92+
93+
// TODO: Add support for retrieving blobs? We don't have a type for that
94+
// currently.
95+
96+
// Object returns the object pointed to by the tag.
97+
func (t *Tag) Object() (core.Object, error) {
98+
return t.r.Storage.Get(t.object)
99+
}
100+
101+
// String returns the meta information contained in the tag as a formatted
102+
// string.
103+
func (t *Tag) String() string {
104+
return fmt.Sprintf(
105+
"%s %s\nObject: %s\nType: %s\nTag: %s\nTagger: %s\nDate: %s\n",
106+
core.TagObject, t.Hash, t.object, t.Type, t.Name, t.Tagger.String(), t.Tagger.When,
107+
)
108+
}
109+
110+
// TagIter provides an iterator for a set of tags.
111+
type TagIter struct {
112+
core.ObjectIter
113+
r *Repository
114+
}
115+
116+
// NewTagIter returns a new TagIter for the given Repository and ObjectIter.
117+
func NewTagIter(r *Repository, iter core.ObjectIter) *TagIter {
118+
return &TagIter{iter, r}
119+
}
120+
121+
// Next moves the iterator to the next tag and returns a pointer to it. If it
122+
// has reached the end of the set it will return io.EOF.
123+
func (iter *TagIter) Next() (*Tag, error) {
124+
obj, err := iter.ObjectIter.Next()
125+
if err != nil {
126+
return nil, err
127+
}
128+
129+
tag := &Tag{r: iter.r}
130+
return tag, tag.Decode(obj)
131+
}
132+
133+
// Close releases any resources used by the iterator.
134+
func (iter *TagIter) Close() {
135+
}

0 commit comments

Comments
 (0)