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






               
              
 
                                       

 
                         

                   





                                                                             
                           



                           

                           


                           
                                        
                               
                                                                   


                   
                                                    
                                        
                                                                              

 




                                                        
                                                                      
                                                                       

                                                            
                                  

 


                                                                               
                                                 





                                                                        
                                                   




                                                        
                                                    



                                           
                         




                                 
                                      

                                    

















                                                                   
                                                                       
                                      
                                                                                             

















                                                            
                                                                            


         
                                                        
                        

                       

 



                                                                             

                                                                     

 

                                                                                



                                                 

         
                                    


                                         




















                                                                      
package git

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"sort"

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

// Hash hash of an object
type Hash core.Hash

// Commit points to a single tree, marking it as what the project looked like
// at a certain point in time. It contains meta-information about that point
// in time, such as a timestamp, the author of the changes since the last
// commit, a pointer to the previous commit(s), etc.
// http://schacon.github.io/gitbook/1_the_git_object_model.html
type Commit struct {
	Hash      core.Hash
	Author    Signature
	Committer Signature
	Message   string

	tree    core.Hash
	parents []core.Hash
	r       *Repository
}

// Tree returns the Tree from the commit
func (c *Commit) Tree() *Tree {
	tree, _ := c.r.Tree(c.tree) // FIXME: Return error as well?
	return tree
}

// Parents return a CommitIter to the parent Commits
func (c *Commit) Parents() *CommitIter {
	return NewCommitIter(c.r, core.NewObjectLookupIter(c.r.os, c.parents))
}

// NumParents returns the number of parents in a commit.
func (c *Commit) NumParents() int {
	return len(c.parents)
}

// File returns the file with the specified "path" in the commit and a
// nil error if the file exists. If the file does not exist, it returns
// a nil file and the ErrFileNotFound error.
func (c *Commit) File(path string) (file *File, err error) {
	return c.Tree().File(path)
}

// ID returns the object ID of the commit. The returned value will always match
// the current value of Commit.Hash.
//
// ID is present to fulfill the Object interface.
func (c *Commit) ID() core.Hash {
	return c.Hash
}

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

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

	c.Hash = o.Hash()

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

	r := bufio.NewReader(reader)

	var message bool
	for {
		line, err := r.ReadSlice('\n')
		if err != nil && err != io.EOF {
			return err
		}

		line = bytes.TrimSpace(line)
		if !message {
			if len(line) == 0 {
				message = true
				continue
			}

			split := bytes.SplitN(line, []byte{' '}, 2)
			switch string(split[0]) {
			case "tree":
				c.tree = core.NewHash(string(split[1]))
			case "parent":
				c.parents = append(c.parents, core.NewHash(string(split[1])))
			case "author":
				c.Author.Decode(split[1])
			case "committer":
				c.Committer.Decode(split[1])
			}
		} else {
			c.Message += string(line) + "\n"
		}

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

func (c *Commit) String() string {
	return fmt.Sprintf(
		"%s %s\nAuthor: %s\nDate:   %s\n",
		core.CommitObject, c.Hash, c.Author.String(), c.Author.When,
	)
}

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

// NewCommitIter returns a CommitIter for the given repository and underlying
// object iterator.
//
// The returned CommitIter will automatically skip over non-commit objects.
func NewCommitIter(r *Repository, iter core.ObjectIter) *CommitIter {
	return &CommitIter{iter, r}
}

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

	commit := &Commit{r: iter.r}
	return commit, commit.Decode(obj)
}

type commitSorterer struct {
	l []*Commit
}

func (s commitSorterer) Len() int {
	return len(s.l)
}

func (s commitSorterer) Less(i, j int) bool {
	return s.l[i].Committer.When.Before(s.l[j].Committer.When)
}

func (s commitSorterer) Swap(i, j int) {
	s.l[i], s.l[j] = s.l[j], s.l[i]
}

// SortCommits sort a commit list by commit date, from older to newer.
func SortCommits(l []*Commit) {
	s := &commitSorterer{l}
	sort.Sort(s)
}