aboutsummaryrefslogblamecommitdiffstats
path: root/tag.go
blob: 0a8014b40ac6dd4391133854cc249207640ce0f6 (plain) (tree)
1
2
3
4
5
6
7
8
9








                   
                                       









                                                                              









                                  


                                                                               
                                                 





                                                                     
                                                   

                                      


                                                     
                                                 





                                           



                                 
                                      

                                    













                                                           
                                                                 
                            
                                                                                  






















                                                 







                                              
 




                                                                              
 


                                                 
 


                                                       
 


                                                          
 


                  
                                                                          
                                                                  
                                         
                                              

                                                
                                   

 


                                                                             
                                     
                             
                               
                                                   


                                       
                                    
                             
                                         


                                                

 


                                                                      
                                            

                                                
                                 
 

                                                   
                                        
                                                 




                                                                          
                            
 
                           

                                                                                            
                                               








                                                  



                                                                       














                                                                             


                                                                          
                                                                          









                                                                    










                                        
package git

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

	"gopkg.in/src-d/go-git.v4/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
	Name       string
	Tagger     Signature
	Message    string
	TargetType core.ObjectType
	Target     core.Hash

	r *Repository
}

// ID returns the object ID of the tag, not the object that the tag references.
// The returned value will always match the current value of Tag.Hash.
//
// ID is present to fulfill the Object interface.
func (t *Tag) ID() core.Hash {
	return t.Hash
}

// Type returns the type of object. It always returns core.TagObject.
//
// Type is present to fulfill the Object interface.
func (t *Tag) Type() core.ObjectType {
	return core.TagObject
}

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

	t.Hash = o.Hash()

	reader, err := o.Reader()
	if err != nil {
		return err
	}
	defer checkClose(reader, &err)

	r := bufio.NewReader(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.Target = core.NewHash(string(split[1]))
		case "type":
			t.TargetType, 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
}

// Encode transforms a Tag into a core.Object.
func (t *Tag) Encode(o core.Object) error {
	o.SetType(core.TagObject)
	w, err := o.Writer()
	if err != nil {
		return err
	}
	defer checkClose(w, &err)

	if _, err = fmt.Fprintf(w,
		"object %s\ntype %s\ntag %s\ntagger ",
		t.Target.String(), t.TargetType.Bytes(), t.Name); err != nil {
		return err
	}

	if err = t.Tagger.Encode(w); err != nil {
		return err
	}

	if _, err = fmt.Fprint(w, "\n\n"); err != nil {
		return err
	}

	if _, err = fmt.Fprint(w, t.Message); err != nil {
		return err
	}

	return err
}

// 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.TargetType != core.CommitObject {
		return nil, ErrUnsupportedObject
	}
	return t.r.Commit(t.Target)
}

// 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) {
	switch t.TargetType {
	case core.CommitObject:
		commit, err := t.r.Commit(t.Target)
		if err != nil {
			return nil, err
		}
		return commit.Tree()
	case core.TreeObject:
		return t.r.Tree(t.Target)
	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.TargetType != core.BlobObject {
		return nil, ErrUnsupportedObject
	}
	return t.r.Blob(t.Target)
}

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

// String returns the meta information contained in the tag as a formatted
// string.
func (t *Tag) String() string {
	obj, _ := t.Object()

	return fmt.Sprintf(
		"%s %s\nTagger: %s\nDate:   %s\n\n%s\n%s",
		core.TagObject, t.Name, t.Tagger.String(), t.Tagger.When.Format(DateFormat),
		t.Message, objectAsString(obj),
	)
}

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

// NewTagIter returns a TagIter for the given repository and underlying
// object iterator.
//
// The returned TagIter will automatically skip over non-tag objects.
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)
}

// ForEach call the cb function for each tag contained on this iter until
// an error happends or the end of the iter is reached. If ErrStop is sent
// the iteration is stop but no error is returned. The iterator is closed.
func (iter *TagIter) ForEach(cb func(*Tag) error) error {
	return iter.ObjectIter.ForEach(func(obj core.Object) error {
		tag := &Tag{r: iter.r}
		if err := tag.Decode(obj); err != nil {
			return err
		}

		return cb(tag)
	})
}

func objectAsString(obj Object) string {
	switch o := obj.(type) {
	case *Commit:
		return o.String()
	case *Tag:
		return o.String()
	}

	return ""
}