blob: 47805ba1732895694e8551a77319c9875b98829b [file] [log] [blame] [edit]
package commit_cache
import (
"encoding/json"
"fmt"
"io"
"sync"
"time"
"github.com/golang/glog"
"skia.googlesource.com/buildbot.git/go/gitinfo"
)
/*
Utilities for caching commit data.
*/
// CommitCache is a struct used for caching commit data. Stores ALL commits in
// the repository.
type CommitCache struct {
branchHeads []*gitinfo.GitBranch
commits []*gitinfo.LongCommit
repo *gitinfo.GitInfo
mutex sync.RWMutex
}
// New creates and returns a new CommitCache which watches the given repo.
// The initial Update will load ALL commits from the repository, so expect
// this to be slow.
func New(repo *gitinfo.GitInfo) (*CommitCache, error) {
// Initially load the past week's worth of commits.
c := CommitCache{
repo: repo,
}
if err := c.Update(); err != nil {
return nil, err
}
return &c, nil
}
// Update syncs the source code repository and loads any new commits.
func (c *CommitCache) Update() error {
glog.Info("Reloading commits.")
c.mutex.Lock()
defer c.mutex.Unlock()
if err := c.repo.Update(true, true); err != nil {
return fmt.Errorf("Failed to update the repo: %v", err)
}
from := time.Time{}
if len(c.commits) > 0 {
from = c.commits[len(c.commits)-1].Timestamp
}
newCommitHashes := c.repo.From(from)
glog.Infof("Processing %d new commits.", len(newCommitHashes))
newCommits := make([]*gitinfo.LongCommit, len(newCommitHashes))
if len(newCommitHashes) > 0 {
for i, h := range newCommitHashes {
d, err := c.repo.Details(h)
if err != nil {
return fmt.Errorf("Failed to obtain commit details for %s: %v", h, err)
}
newCommits[i] = d
}
}
branchHeads, err := c.repo.GetBranches()
if err != nil {
return fmt.Errorf("Failed to read branch information from the repo: %v", err)
}
// Update the cached values all at once at at the end.
c.branchHeads = branchHeads
c.commits = append(c.commits, newCommits...)
return nil
}
// NumCommits returns the number of commits contained in the cache.
func (c *CommitCache) NumCommits() int {
return len(c.commits)
}
// asJson writes the given commit range along with the branch heads in JSON
// format to the given Writer.
func (c *CommitCache) asJson(w io.Writer, startIdx, endIdx int) error {
c.mutex.RLock()
defer c.mutex.RUnlock()
data := struct {
Commits []*gitinfo.LongCommit `json:"commits"`
BranchHeads []*gitinfo.GitBranch `json:"branch_heads"`
StartIdx int `json:"startIdx"`
EndIdx int `json:"endIdx"`
}{
Commits: c.commits[startIdx:endIdx],
BranchHeads: c.branchHeads,
StartIdx: startIdx,
EndIdx: endIdx,
}
return json.NewEncoder(w).Encode(&data)
}
// LastNAsJson writes the last N commits along with the branch heads in JSON
// format to the given Writer.
func (c *CommitCache) LastNAsJson(w io.Writer, n int) error {
c.mutex.RLock()
end := len(c.commits)
c.mutex.RUnlock()
start := end - n
if start < 0 {
start = 0
}
return c.asJson(w, start, end)
}
// RangeAsJson writes the given range of commits along with the branch heads
// in JSON format to the given Writer.
func (c *CommitCache) RangeAsJson(w io.Writer, startIdx, endIdx int) error {
if startIdx < 0 || startIdx > len(c.commits) {
return fmt.Errorf("startIdx is out of range [0, %d]: %d", len(c.commits), startIdx)
}
if endIdx < 0 || endIdx > len(c.commits) {
return fmt.Errorf("endIdx is out of range [0, %d]: %d", len(c.commits), endIdx)
}
if endIdx < startIdx {
return fmt.Errorf("endIdx < startIdx: %d, %d", endIdx, startIdx)
}
return c.asJson(w, startIdx, endIdx)
}