package git
import (
"errors"
"fmt"
"gopkg.in/src-d/go-git.v3/clients/common"
"gopkg.in/src-d/go-git.v3/core"
"gopkg.in/src-d/go-git.v3/formats/packfile"
"gopkg.in/src-d/go-git.v3/storage/memory"
)
var (
ObjectNotFoundErr = errors.New("object not found")
)
const (
DefaultRemoteName = "origin"
)
type Repository struct {
Remotes map[string]*Remote
Storage core.ObjectStorage
URL string
}
// NewRepository creates a new repository setting remote as default remote
func NewRepository(url string, auth common.AuthMethod) (*Repository, error) {
var remote *Remote
var err error
if auth == nil {
remote, err = NewRemote(url)
} else {
remote, err = NewAuthenticatedRemote(url, auth)
}
if err != nil {
return nil, err
}
r := NewPlainRepository()
r.Remotes[DefaultRemoteName] = remote
r.URL = url
return r, nil
}
// NewPlainRepository creates a new repository without remotes
func NewPlainRepository() *Repository {
return &Repository{
Remotes: map[string]*Remote{},
Storage: memory.NewObjectStorage(),
}
}
// Pull connect and fetch the given branch from the given remote, the branch
// should be provided with the full path not only the abbreviation, eg.:
// "refs/heads/master"
func (r *Repository) Pull(remoteName, branch string) (err error) {
remote, ok := r.Remotes[remoteName]
if !ok {
return fmt.Errorf("unable to find remote %q", remoteName)
}
if err := remote.Connect(); err != nil {
return err
}
ref, err := remote.Ref(branch)
if err != nil {
return err
}
req := &common.GitUploadPackRequest{}
req.Want(ref)
// TODO: Provide "haves" for what's already in the repository's storage
reader, err := remote.Fetch(req)
if err != nil {
return err
}
defer checkClose(reader, &err)
pr := packfile.NewReader(reader)
if _, err = pr.Read(r.Storage); err != nil {
return err
}
return nil
}
// PullDefault like Pull but retrieve the default branch from the default remote
func (r *Repository) PullDefault() (err error) {
remote, ok := r.Remotes[DefaultRemoteName]
if !ok {
return fmt.Errorf("unable to find default remote %q", DefaultRemoteName)
}
return r.Pull(DefaultRemoteName, remote.DefaultBranch())
}
// Commit return the commit with the given hash
func (r *Repository) Commit(h core.Hash) (*Commit, error) {
obj, err := r.Storage.Get(h)
if err != nil {
if err == core.ObjectNotFoundErr {
return nil, ObjectNotFoundErr
}
return nil, err
}
commit := &Commit{r: r}
return commit, commit.Decode(obj)
}
// Commits decode the objects into commits
func (r *Repository) Commits() *CommitIter {
return NewCommitIter(r, r.Storage.Iter(core.CommitObject))
}
// Tree return the tree with the given hash
func (r *Repository) Tree(h core.Hash) (*Tree, error) {
obj, err := r.Storage.Get(h)
if err != nil {
if err == core.ObjectNotFoundErr {
return nil, ObjectNotFoundErr
}
return nil, err
}
tree := &Tree{r: r}
return tree, tree.Decode(obj)
}
// Blob returns the blob with the given hash
func (r *Repository) Blob(h core.Hash) (*Blob, error) {
obj, err := r.Storage.Get(h)
if err != nil {
if err == core.ObjectNotFoundErr {
return nil, ObjectNotFoundErr
}
return nil, err
}
blob := &Blob{}
return blob, blob.Decode(obj)
}
// Tag returns a tag with the given hash.
func (r *Repository) Tag(h core.Hash) (*Tag, error) {
obj, err := r.Storage.Get(h)
if err != nil {
if err == core.ObjectNotFoundErr {
return nil, ObjectNotFoundErr
}
return nil, err
}
tag := &Tag{r: r}
return tag, tag.Decode(obj)
}
// Tags returns a TagIter that can step through all of the annotated tags
// in the repository.
func (r *Repository) Tags() *TagIter {
return NewTagIter(r, r.Storage.Iter(core.TagObject))
}
// Object returns an object with the given hash.
func (r *Repository) Object(h core.Hash) (Object, error) {
obj, err := r.Storage.Get(h)
if err != nil {
if err == core.ObjectNotFoundErr {
return nil, ObjectNotFoundErr
}
return nil, err
}
switch obj.Type() {
case core.CommitObject:
commit := &Commit{r: r}
return commit, commit.Decode(obj)
case core.TreeObject:
tree := &Tree{r: r}
return tree, tree.Decode(obj)
case core.BlobObject:
blob := &Blob{}
return blob, blob.Decode(obj)
case core.TagObject:
tag := &Tag{r: r}
return tag, tag.Decode(obj)
default:
return nil, core.ErrInvalidType
}
}