aboutsummaryrefslogblamecommitdiffstats
path: root/tag.go
blob: 4d2253ae31f286f029083011633bd7b7047cecd0 (plain) (tree)















































































                                                                              
                                                                  
                                         


                                                


                                   


                                                                             

                                                                             











                                                   

 







                                                                      







































                                                                                                   
                    
 
package git

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"

	"gopkg.in/src-d/go-git.v3/core"
)

// Tag represents an annotated tag object. It points to a single git object of
// any type, but tags typically are applied to commit or blob objects. It
// provides a reference that associates the target with a tag name. It also
// contains meta-information about the tag, including the tagger, tag date and
// message.
//
// https://git-scm.com/book/en/v2/Git-Internals-Git-References#Tags
type Tag struct {
	Hash    core.Hash
	Type    core.ObjectType
	Name    string
	Tagger  Signature
	Message string

	object core.Hash
	r      *Repository
}

// Decode transforms a core.Object into a Tag struct.
func (t *Tag) Decode(o core.Object) error {
	if o.Type() != core.TagObject {
		return ErrUnsupportedObject
	}

	t.Hash = o.Hash()

	r := bufio.NewReader(o.Reader())
	for {
		line, err := r.ReadSlice('\n')
		if err != nil && err != io.EOF {
			return err
		}

		line = bytes.TrimSpace(line)
		if len(line) == 0 {
			break // Start of message
		}

		split := bytes.SplitN(line, []byte{' '}, 2)
		switch string(split[0]) {
		case "object":
			t.object = core.NewHash(string(split[1]))
		case "type":
			t.Type, err = core.ParseObjectType(string(split[1]))
			if err != nil {
				return err
			}
		case "tag":
			t.Name = string(split[1])
		case "tagger":
			t.Tagger.Decode(split[1])
		}

		if err == io.EOF {
			return nil
		}
	}

	data, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}
	t.Message = string(data)

	return nil
}

// Commit returns the commit pointed to by the tag. If the tag points to a
// different type of object ErrUnsupportedObject will be returned.
func (t *Tag) Commit() (*Commit, error) {
	if t.Type != core.CommitObject {
		return nil, ErrUnsupportedObject
	}
	return t.r.Commit(t.object)
}

// Tree returns the tree pointed to by the tag. If the tag points to a commit
// object the tree of that commit will be returned. If the tag does not point
// to a commit or tree object ErrUnsupportedObject will be returned.
func (t *Tag) Tree() (*Tree, error) {
	// TODO: If the tag is of type commit, follow the commit to its tree?
	switch t.Type {
	case core.CommitObject:
		commit, err := t.r.Commit(t.object)
		if err != nil {
			return nil, err
		}
		return commit.Tree(), nil
	case core.TreeObject:
		return t.r.Tree(t.object)
	default:
		return nil, ErrUnsupportedObject
	}
}

// Blob returns the blob pointed to by the tag. If the tag points to a
// different type of object ErrUnsupportedObject will be returned.
func (t *Tag) Blob() (*Blob, error) {
	if t.Type != core.BlobObject {
		return nil, ErrUnsupportedObject
	}
	return t.r.Blob(t.object)
}

// Object returns the object pointed to by the tag.
func (t *Tag) Object() (core.Object, error) {
	return t.r.Storage.Get(t.object)
}

// String returns the meta information contained in the tag as a formatted
// string.
func (t *Tag) String() string {
	return fmt.Sprintf(
		"%s %s\nObject: %s\nType: %s\nTag: %s\nTagger: %s\nDate:   %s\n",
		core.TagObject, t.Hash, t.object, t.Type, t.Name, t.Tagger.String(), t.Tagger.When,
	)
}

// TagIter provides an iterator for a set of tags.
type TagIter struct {
	core.ObjectIter
	r *Repository
}

// NewTagIter returns a new TagIter for the given Repository and ObjectIter.
func NewTagIter(r *Repository, iter core.ObjectIter) *TagIter {
	return &TagIter{iter, r}
}

// Next moves the iterator to the next tag and returns a pointer to it. If it
// has reached the end of the set it will return io.EOF.
func (iter *TagIter) Next() (*Tag, error) {
	obj, err := iter.ObjectIter.Next()
	if err != nil {
		return nil, err
	}

	tag := &Tag{r: iter.r}
	return tag, tag.Decode(obj)
}

// Close releases any resources used by the iterator.
func (iter *TagIter) Close() {
	iter.Close()
}