aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--blame.go4
-rw-r--r--options.go8
-rw-r--r--plumbing/object/commit.go65
-rw-r--r--plumbing/object/commit_test.go10
-rw-r--r--plumbing/object/commit_walker.go169
-rw-r--r--plumbing/object/commit_walker_test.go10
-rw-r--r--plumbing/revlist/revlist.go23
-rw-r--r--references.go28
-rw-r--r--references_test.go2
-rw-r--r--repository.go71
-rw-r--r--repository_test.go75
11 files changed, 307 insertions, 158 deletions
diff --git a/blame.go b/blame.go
index 2f0843d..99025fd 100644
--- a/blame.go
+++ b/blame.go
@@ -142,11 +142,11 @@ type blame struct {
graph [][]*object.Commit
}
-// calculte the history of a file "path", starting from commit "from", sorted by commit date.
+// calculate the history of a file "path", starting from commit "from", sorted by commit date.
func (b *blame) fillRevs() error {
var err error
- b.revs, err = References(b.fRev, b.path)
+ b.revs, err = references(b.fRev, b.path)
if err != nil {
return err
}
diff --git a/options.go b/options.go
index 1047d7e..d033654 100644
--- a/options.go
+++ b/options.go
@@ -177,3 +177,11 @@ type SubmoduleUpdateOptions struct {
// submodules (and so on). Until the SubmoduleRescursivity is reached.
RecurseSubmodules SubmoduleRescursivity
}
+
+// LogOptions describes how a log action should be performed.
+type LogOptions struct {
+ // When the From option is set the log will only contain commits
+ // reachable from it. If this option is not set, HEAD will be used as
+ // the default From.
+ From plumbing.Hash
+}
diff --git a/plumbing/object/commit.go b/plumbing/object/commit.go
index 7507bc7..ffbb9f9 100644
--- a/plumbing/object/commit.go
+++ b/plumbing/object/commit.go
@@ -5,7 +5,6 @@ import (
"bytes"
"fmt"
"io"
- "sort"
"strings"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -64,7 +63,7 @@ func (c *Commit) Tree() (*Tree, error) {
}
// Parents return a CommitIter to the parent Commits.
-func (c *Commit) Parents() *CommitIter {
+func (c *Commit) Parents() CommitIter {
return NewCommitIter(c.s,
storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, c.parents),
)
@@ -163,19 +162,6 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
}
}
-// History returns a slice with the previous commits in the history of this
-// commit, sorted in reverse chronological order.
-func (c *Commit) History() ([]*Commit, error) {
- var commits []*Commit
- err := WalkCommitHistory(c, func(commit *Commit) error {
- commits = append(commits, commit)
- return nil
- })
-
- ReverseSortCommits(commits)
- return commits, err
-}
-
// Encode transforms a Commit into a plumbing.EncodedObject.
func (b *Commit) Encode(o plumbing.EncodedObject) error {
o.SetType(plumbing.CommitObject)
@@ -231,24 +217,31 @@ func indent(t string) string {
return strings.Join(output, "\n")
}
-// CommitIter provides an iterator for a set of commits.
-type CommitIter struct {
+// CommitIter is a generic closable interface for iterating over commits.
+type CommitIter interface {
+ Next() (*Commit, error)
+ ForEach(func(*Commit) error) error
+ Close()
+}
+
+// storerCommitIter provides an iterator from commits in an EncodedObjectStorer.
+type storerCommitIter struct {
storer.EncodedObjectIter
s storer.EncodedObjectStorer
}
// NewCommitIter takes a storer.EncodedObjectStorer and a
-// storer.EncodedObjectIter and returns a *CommitIter that iterates over all
+// storer.EncodedObjectIter and returns a CommitIter that iterates over all
// commits contained in the storer.EncodedObjectIter.
//
// Any non-commit object returned by the storer.EncodedObjectIter is skipped.
-func NewCommitIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *CommitIter {
- return &CommitIter{iter, s}
+func NewCommitIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) CommitIter {
+ return &storerCommitIter{iter, s}
}
// Next moves the iterator to the next commit and returns a pointer to it. If
// there are no more commits, it returns io.EOF.
-func (iter *CommitIter) Next() (*Commit, error) {
+func (iter *storerCommitIter) Next() (*Commit, error) {
obj, err := iter.EncodedObjectIter.Next()
if err != nil {
return nil, err
@@ -260,7 +253,7 @@ func (iter *CommitIter) Next() (*Commit, error) {
// ForEach call the cb function for each commit contained on this iter until
// an error appends 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 *CommitIter) ForEach(cb func(*Commit) error) error {
+func (iter *storerCommitIter) ForEach(cb func(*Commit) error) error {
return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error {
c, err := DecodeCommit(iter.s, obj)
if err != nil {
@@ -271,30 +264,6 @@ func (iter *CommitIter) ForEach(cb func(*Commit) error) error {
})
}
-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 sorts a commit list by commit date, from older to newer.
-func SortCommits(l []*Commit) {
- s := &commitSorterer{l}
- sort.Sort(s)
-}
-
-// ReverseSortCommits sorts a commit list by commit date, from newer to older.
-func ReverseSortCommits(l []*Commit) {
- s := &commitSorterer{l}
- sort.Sort(sort.Reverse(s))
+func (iter *storerCommitIter) Close() {
+ iter.EncodedObjectIter.Close()
}
diff --git a/plumbing/object/commit_test.go b/plumbing/object/commit_test.go
index 8b4ee2a..c1f49db 100644
--- a/plumbing/object/commit_test.go
+++ b/plumbing/object/commit_test.go
@@ -62,6 +62,8 @@ func (s *SuiteCommit) TestParents(c *C) {
c.Assert(err, IsNil)
c.Assert(output, DeepEquals, expected)
+
+ i.Close()
}
func (s *SuiteCommit) TestCommitEncodeDecodeIdempotent(c *C) {
@@ -110,14 +112,6 @@ func (s *SuiteCommit) TestNumParents(c *C) {
c.Assert(s.Commit.NumParents(), Equals, 2)
}
-func (s *SuiteCommit) TestHistory(c *C) {
- commits, err := s.Commit.History()
- c.Assert(err, IsNil)
- c.Assert(commits, HasLen, 5)
- c.Assert(commits[0].Hash.String(), Equals, s.Commit.Hash.String())
- c.Assert(commits[len(commits)-1].Hash.String(), Equals, "b029517f6300c2da0f4b651b8642506cd6aaf45d")
-}
-
func (s *SuiteCommit) TestString(c *C) {
c.Assert(s.Commit.String(), Equals, ""+
"commit 1669dce138d9b841a518c64b10914d88f5e488ea\n"+
diff --git a/plumbing/object/commit_walker.go b/plumbing/object/commit_walker.go
index 681ea5e..3316fab 100644
--- a/plumbing/object/commit_walker.go
+++ b/plumbing/object/commit_walker.go
@@ -4,93 +4,150 @@ import (
"io"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
)
-type commitWalker struct {
+type commitPreIterator struct {
seen map[plumbing.Hash]bool
- stack []*CommitIter
+ stack []CommitIter
start *Commit
- cb func(*Commit) error
}
-// WalkCommitHistory walks the commit history, starting at the given commit and
-// visiting its parents in pre-order. The given callback will be called for each
-// visited commit. Each commit will be visited only once. If the callback returns
-// an error, walking will stop and will return the error. Other errors might be
-// returned if the history cannot be traversed (e.g. missing objects).
-func WalkCommitHistory(c *Commit, cb func(*Commit) error) error {
- w := &commitWalker{
+// NewCommitPreIterator returns a CommitIter that walks the commit history,
+// starting at the given commit and visiting its parents in pre-order.
+// The given callback will be called for each visited commit. Each commit will
+// be visited only once. If the callback returns an error, walking will stop
+// and will return the error. Other errors might be returned if the history
+// cannot be traversed (e.g. missing objects).
+func NewCommitPreIterator(c *Commit) CommitIter {
+ return &commitPreIterator{
seen: make(map[plumbing.Hash]bool),
- stack: make([]*CommitIter, 0),
+ stack: make([]CommitIter, 0),
start: c,
- cb: cb,
}
-
- return w.walk()
}
-func (w *commitWalker) walk() error {
- var commit *Commit
+func (w *commitPreIterator) Next() (*Commit, error) {
+ var c *Commit
+ for {
+ if w.start != nil {
+ c = w.start
+ w.start = nil
+ } else {
+ current := len(w.stack) - 1
+ if current < 0 {
+ return nil, io.EOF
+ }
- if w.start != nil {
- commit = w.start
- w.start = nil
- } else {
- current := len(w.stack) - 1
- if current < 0 {
- return nil
+ var err error
+ c, err = w.stack[current].Next()
+ if err == io.EOF {
+ w.stack = w.stack[:current]
+ continue
+ }
+
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // check and update seen
+ if w.seen[c.Hash] {
+ continue
}
- var err error
- commit, err = w.stack[current].Next()
+ w.seen[c.Hash] = true
+ if c.NumParents() > 0 {
+ w.stack = append(w.stack, c.Parents())
+ }
+
+ return c, nil
+ }
+}
+
+func (w *commitPreIterator) ForEach(cb func(*Commit) error) error {
+ for {
+ c, err := w.Next()
if err == io.EOF {
- w.stack = w.stack[:current]
- return w.walk()
+ break
+ }
+ if err != nil {
+ return err
}
+ err = cb(c)
+ if err == storer.ErrStop {
+ break
+ }
if err != nil {
return err
}
}
- // check and update seen
- if w.seen[commit.Hash] {
- return w.walk()
- }
+ return nil
+}
- w.seen[commit.Hash] = true
- if commit.NumParents() > 0 {
- w.stack = append(w.stack, commit.Parents())
- }
+func (w *commitPreIterator) Close() {}
- if err := w.cb(commit); err != nil {
- return err
- }
+type commitPostIterator struct {
+ stack []*Commit
+ seen map[plumbing.Hash]bool
+}
- return w.walk()
+// NewCommitPostIterator returns a CommitIter that walks the commit
+// history like WalkCommitHistory but in post-order. This means that after
+// walking a merge commit, the merged commit will be walked before the base
+// it was merged on. This can be useful if you wish to see the history in
+// chronological order.
+func NewCommitPostIterator(c *Commit) CommitIter {
+ return &commitPostIterator{
+ stack: []*Commit{c},
+ seen: make(map[plumbing.Hash]bool),
+ }
}
-// WalkCommitHistoryPost walks the commit history like WalkCommitHistory
-// but in post-order. This means that after walking a merge commit, the
-// merged commit will be walked before the base it was merged on. This
-// can be useful if you wish to see the history in chronological order.
-func WalkCommitHistoryPost(c *Commit, cb func(*Commit) error) error {
- stack := []*Commit{c}
- seen := make(map[plumbing.Hash]bool)
- for len(stack) > 0 {
- c := stack[len(stack)-1]
- stack = stack[:len(stack)-1]
- if seen[c.Hash] {
- continue
+func (w *commitPostIterator) Next() (*Commit, error) {
+ for {
+ if len(w.stack) == 0 {
+ return nil, io.EOF
}
- seen[c.Hash] = true
- if err := cb(c); err != nil {
- return err
+
+ c := w.stack[len(w.stack)-1]
+ w.stack = w.stack[:len(w.stack)-1]
+ if w.seen[c.Hash] {
+ continue
}
- c.Parents().ForEach(func(pcm *Commit) error {
- stack = append(stack, pcm)
+ w.seen[c.Hash] = true
+
+ err := c.Parents().ForEach(func(pcm *Commit) error {
+ w.stack = append(w.stack, pcm)
return nil
})
+
+ return c, err
+ }
+}
+
+func (w *commitPostIterator) ForEach(cb func(*Commit) error) error {
+ for {
+ c, err := w.Next()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return err
+ }
+
+ err = cb(c)
+ if err == storer.ErrStop {
+ break
+ }
+ if err != nil {
+ return err
+ }
}
+
return nil
}
+
+func (w *commitPostIterator) Close() {}
diff --git a/plumbing/object/commit_walker_test.go b/plumbing/object/commit_walker_test.go
index aa54de0..7d04bce 100644
--- a/plumbing/object/commit_walker_test.go
+++ b/plumbing/object/commit_walker_test.go
@@ -8,11 +8,12 @@ type CommitWalkerSuite struct {
var _ = Suite(&CommitWalkerSuite{})
-func (s *CommitWalkerSuite) TestWalkHistory(c *C) {
+func (s *CommitWalkerSuite) TestCommitPreIterator(c *C) {
commit := s.commit(c, s.Fixture.Head)
var commits []*Commit
- WalkCommitHistory(commit, func(c *Commit) error {
+ wIter := NewCommitPreIterator(commit)
+ wIter.ForEach(func(c *Commit) error {
commits = append(commits, c)
return nil
})
@@ -34,11 +35,12 @@ func (s *CommitWalkerSuite) TestWalkHistory(c *C) {
}
}
-func (s *CommitWalkerSuite) TestWalkHistoryPost(c *C) {
+func (s *CommitWalkerSuite) TestCommitPostIterator(c *C) {
commit := s.commit(c, s.Fixture.Head)
var commits []*Commit
- WalkCommitHistoryPost(commit, func(c *Commit) error {
+ wIter := NewCommitPostIterator(commit)
+ wIter.ForEach(func(c *Commit) error {
commits = append(commits, c)
return nil
})
diff --git a/plumbing/revlist/revlist.go b/plumbing/revlist/revlist.go
index eb9afaf..fbd1bd9 100644
--- a/plumbing/revlist/revlist.go
+++ b/plumbing/revlist/revlist.go
@@ -82,20 +82,21 @@ func reachableObjects(
commit *object.Commit,
seen map[plumbing.Hash]bool,
cb func(h plumbing.Hash)) error {
- return object.WalkCommitHistory(commit, func(commit *object.Commit) error {
- if seen[commit.Hash] {
- return nil
- }
+ return object.NewCommitPreIterator(commit).
+ ForEach(func(commit *object.Commit) error {
+ if seen[commit.Hash] {
+ return nil
+ }
- cb(commit.Hash)
+ cb(commit.Hash)
- tree, err := commit.Tree()
- if err != nil {
- return err
- }
+ tree, err := commit.Tree()
+ if err != nil {
+ return err
+ }
- return iterateCommitTrees(seen, tree, cb)
- })
+ return iterateCommitTrees(seen, tree, cb)
+ })
}
// iterateCommitTrees iterate all reachable trees from the given commit
diff --git a/references.go b/references.go
index 957d741..fc81103 100644
--- a/references.go
+++ b/references.go
@@ -2,6 +2,7 @@ package git
import (
"io"
+ "sort"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
@@ -23,19 +24,42 @@ import (
// - Cherry-picks are not detected unless there are no commits between them and
// therefore can appear repeated in the list. (see git path-id for hints on how
// to fix this).
-func References(c *object.Commit, path string) ([]*object.Commit, error) {
+func references(c *object.Commit, path string) ([]*object.Commit, error) {
var result []*object.Commit
seen := make(map[plumbing.Hash]struct{}, 0)
if err := walkGraph(&result, &seen, c, path); err != nil {
return nil, err
}
- object.SortCommits(result)
+ // TODO result should be returned without ordering
+ sortCommits(result)
// for merges of identical cherry-picks
return removeComp(path, result, equivalent)
}
+type commitSorterer struct {
+ l []*object.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 sorts a commit list by commit date, from older to newer.
+func sortCommits(l []*object.Commit) {
+ s := &commitSorterer{l}
+ sort.Sort(s)
+}
+
// Recursive traversal of the commit graph, generating a linear history of the
// path.
func walkGraph(result *[]*object.Commit, seen *map[plumbing.Hash]struct{}, current *object.Commit, path string) error {
diff --git a/references_test.go b/references_test.go
index e901ec0..22efc4d 100644
--- a/references_test.go
+++ b/references_test.go
@@ -294,7 +294,7 @@ func (s *ReferencesSuite) TestRevList(c *C) {
commit, err := r.CommitObject(plumbing.NewHash(t.commit))
c.Assert(err, IsNil)
- revs, err := References(commit, t.path)
+ revs, err := references(commit, t.path)
c.Assert(err, IsNil)
c.Assert(len(revs), Equals, len(t.revs))
diff --git a/repository.go b/repository.go
index 9e325e4..d9a1d7e 100644
--- a/repository.go
+++ b/repository.go
@@ -608,6 +608,26 @@ func (r *Repository) Push(o *PushOptions) error {
return remote.Push(o)
}
+// Log returns the commit history from the given LogOptions.
+func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) {
+ h := o.From
+ if o.From == plumbing.ZeroHash {
+ head, err := r.Head()
+ if err != nil {
+ return nil, err
+ }
+
+ h = head.Hash()
+ }
+
+ commit, err := r.CommitObject(h)
+ if err != nil {
+ return nil, err
+ }
+
+ return object.NewCommitPreIterator(commit), nil
+}
+
// Tags returns all the References from Tags. This method returns all the tag
// types, lightweight, and annotated ones.
func (r *Repository) Tags() (storer.ReferenceIter, error) {
@@ -671,7 +691,7 @@ func (r *Repository) CommitObject(h plumbing.Hash) (*object.Commit, error) {
}
// CommitObjects returns an unsorted CommitIter with all the commits in the repository.
-func (r *Repository) CommitObjects() (*object.CommitIter, error) {
+func (r *Repository) CommitObjects() (object.CommitIter, error) {
iter, err := r.Storer.IterEncodedObjects(plumbing.CommitObject)
if err != nil {
return nil, err
@@ -838,29 +858,28 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err
commit = c
}
case revision.CaretReg:
- history, err := commit.History()
-
- if err != nil {
- return &plumbing.ZeroHash, err
- }
+ history := object.NewCommitPreIterator(commit)
re := item.(revision.CaretReg).Regexp
negate := item.(revision.CaretReg).Negate
var c *object.Commit
- for i := 0; i < len(history); i++ {
- if !negate && re.MatchString(history[i].Message) {
- c = history[i]
-
- break
+ err := history.ForEach(func(hc *object.Commit) error {
+ if !negate && re.MatchString(hc.Message) {
+ c = hc
+ return storer.ErrStop
}
- if negate && !re.MatchString(history[i].Message) {
- c = history[i]
-
- break
+ if negate && !re.MatchString(hc.Message) {
+ c = hc
+ return storer.ErrStop
}
+
+ return nil
+ })
+ if err != nil {
+ return &plumbing.ZeroHash, err
}
if c == nil {
@@ -869,21 +888,21 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err
commit = c
case revision.AtDate:
- history, err := commit.History()
-
- if err != nil {
- return &plumbing.ZeroHash, err
- }
+ history := object.NewCommitPreIterator(commit)
date := item.(revision.AtDate).Date
- var c *object.Commit
- for i := 0; i < len(history); i++ {
- if date.Equal(history[i].Committer.When.UTC()) || history[i].Committer.When.UTC().Before(date) {
- c = history[i]
-
- break
+ var c *object.Commit
+ err := history.ForEach(func(hc *object.Commit) error {
+ if date.Equal(hc.Committer.When.UTC()) || hc.Committer.When.UTC().Before(date) {
+ c = hc
+ return storer.ErrStop
}
+
+ return nil
+ })
+ if err != nil {
+ return &plumbing.ZeroHash, err
}
if c == nil {
diff --git a/repository_test.go b/repository_test.go
index 352ff21..77bfde2 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -3,6 +3,7 @@ package git
import (
"bytes"
"fmt"
+ "io"
"io/ioutil"
"os"
"os/exec"
@@ -688,6 +689,80 @@ func (s *RepositorySuite) TestPushNonExistentRemote(c *C) {
c.Assert(err, ErrorMatches, ".*remote not found.*")
}
+func (s *RepositorySuite) TestLog(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ c.Assert(err, IsNil)
+
+ cIter, err := r.Log(&LogOptions{
+ plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
+ })
+
+ c.Assert(err, IsNil)
+
+ commitOrder := []plumbing.Hash{
+ plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
+ plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
+ }
+
+ for _, o := range commitOrder {
+ commit, err := cIter.Next()
+ c.Assert(err, IsNil)
+ c.Assert(commit.Hash, Equals, o)
+ }
+ _, err = cIter.Next()
+ c.Assert(err, Equals, io.EOF)
+}
+
+func (s *RepositorySuite) TestLogHead(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ c.Assert(err, IsNil)
+
+ cIter, err := r.Log(&LogOptions{})
+
+ c.Assert(err, IsNil)
+
+ commitOrder := []plumbing.Hash{
+ plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
+ plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294"),
+ plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"),
+ plumbing.NewHash("1669dce138d9b841a518c64b10914d88f5e488ea"),
+ plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
+ plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
+ plumbing.NewHash("a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69"),
+ plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
+ }
+
+ for _, o := range commitOrder {
+ commit, err := cIter.Next()
+ c.Assert(err, IsNil)
+ c.Assert(commit.Hash, Equals, o)
+ }
+ _, err = cIter.Next()
+ c.Assert(err, Equals, io.EOF)
+}
+
+func (s *RepositorySuite) TestLogError(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ c.Assert(err, IsNil)
+
+ _, err = r.Log(&LogOptions{
+ plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ })
+ c.Assert(err, NotNil)
+}
+
func (s *RepositorySuite) TestCommit(c *C) {
r, _ := Init(memory.NewStorage(), nil)
err := r.clone(&CloneOptions{