diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2017-07-19 15:31:20 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-19 15:31:20 +0200 |
commit | f0fb843efcf56c3df9dbbb47aaafe3e9c4815147 (patch) | |
tree | 812f17007612d2b45431f458e3e49442db53a7fb | |
parent | 9775f829d6fb8026a2d73af89896a8f2cc5f7c50 (diff) | |
parent | d851b90f5e832fa1edeb0d408bd56ed6c1fd5e7a (diff) | |
download | go-git-f0fb843efcf56c3df9dbbb47aaafe3e9c4815147.tar.gz |
Merge pull request #489 from mcuadros/shallow-push
repository: allow push from shallow repositories
-rw-r--r-- | plumbing/object/commit_walker.go | 48 | ||||
-rw-r--r-- | plumbing/object/commit_walker_test.go | 57 | ||||
-rw-r--r-- | plumbing/revlist/revlist.go | 39 | ||||
-rw-r--r-- | remote.go | 12 | ||||
-rw-r--r-- | repository.go | 6 | ||||
-rw-r--r-- | repository_test.go | 45 |
6 files changed, 166 insertions, 41 deletions
diff --git a/plumbing/object/commit_walker.go b/plumbing/object/commit_walker.go index 8d2c6e8..797c17a 100644 --- a/plumbing/object/commit_walker.go +++ b/plumbing/object/commit_walker.go @@ -18,10 +18,16 @@ type commitPreIterator struct { // 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 NewCommitPreorderIter(c *Commit) CommitIter { +// cannot be traversed (e.g. missing objects). Ignore allows to skip some +// commits from being iterated. +func NewCommitPreorderIter(c *Commit, ignore []plumbing.Hash) CommitIter { + seen := make(map[plumbing.Hash]bool) + for _, h := range ignore { + seen[h] = true + } + return &commitPreIterator{ - seen: make(map[plumbing.Hash]bool), + seen: seen, stack: make([]CommitIter, 0), start: c, } @@ -51,20 +57,33 @@ func (w *commitPreIterator) Next() (*Commit, error) { } } - // check and update seen if w.seen[c.Hash] { continue } w.seen[c.Hash] = true + if c.NumParents() > 0 { - w.stack = append(w.stack, c.Parents()) + w.stack = append(w.stack, filteredParentIter(c, w.seen)) } return c, nil } } +func filteredParentIter(c *Commit, seen map[plumbing.Hash]bool) CommitIter { + var hashes []plumbing.Hash + for _, h := range c.ParentHashes { + if !seen[h] { + hashes = append(hashes, h) + } + } + + return NewCommitIter(c.s, + storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, hashes), + ) +} + func (w *commitPreIterator) ForEach(cb func(*Commit) error) error { for { c, err := w.Next() @@ -98,11 +117,16 @@ type commitPostIterator struct { // 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 NewCommitPostorderIter(c *Commit) CommitIter { +// chronological order. Ignore allows to skip some commits from being iterated. +func NewCommitPostorderIter(c *Commit, ignore []plumbing.Hash) CommitIter { + seen := make(map[plumbing.Hash]bool) + for _, h := range ignore { + seen[h] = true + } + return &commitPostIterator{ stack: []*Commit{c}, - seen: make(map[plumbing.Hash]bool), + seen: seen, } } @@ -114,17 +138,17 @@ func (w *commitPostIterator) Next() (*Commit, error) { c := w.stack[len(w.stack)-1] w.stack = w.stack[:len(w.stack)-1] + if w.seen[c.Hash] { continue } + w.seen[c.Hash] = true - err := c.Parents().ForEach(func(pcm *Commit) error { - w.stack = append(w.stack, pcm) + return c, c.Parents().ForEach(func(p *Commit) error { + w.stack = append(w.stack, p) return nil }) - - return c, err } } diff --git a/plumbing/object/commit_walker_test.go b/plumbing/object/commit_walker_test.go index 2a03057..48b504d 100644 --- a/plumbing/object/commit_walker_test.go +++ b/plumbing/object/commit_walker_test.go @@ -1,6 +1,10 @@ package object -import . "gopkg.in/check.v1" +import ( + "gopkg.in/src-d/go-git.v4/plumbing" + + . "gopkg.in/check.v1" +) type CommitWalkerSuite struct { BaseObjectsSuite @@ -12,8 +16,7 @@ func (s *CommitWalkerSuite) TestCommitPreIterator(c *C) { commit := s.commit(c, s.Fixture.Head) var commits []*Commit - wIter := NewCommitPreorderIter(commit) - wIter.ForEach(func(c *Commit) error { + NewCommitPreorderIter(commit, nil).ForEach(func(c *Commit) error { commits = append(commits, c) return nil }) @@ -35,12 +38,33 @@ func (s *CommitWalkerSuite) TestCommitPreIterator(c *C) { } } +func (s *CommitWalkerSuite) TestCommitPreIteratorWithIgnore(c *C) { + commit := s.commit(c, s.Fixture.Head) + + var commits []*Commit + NewCommitPreorderIter(commit, []plumbing.Hash{ + plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"), + }).ForEach(func(c *Commit) error { + commits = append(commits, c) + return nil + }) + + c.Assert(commits, HasLen, 2) + + expected := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "918c48b83bd081e863dbe1b80f8998f058cd8294", + } + for i, commit := range commits { + c.Assert(commit.Hash.String(), Equals, expected[i]) + } +} + func (s *CommitWalkerSuite) TestCommitPostIterator(c *C) { commit := s.commit(c, s.Fixture.Head) var commits []*Commit - wIter := NewCommitPostorderIter(commit) - wIter.ForEach(func(c *Commit) error { + NewCommitPostorderIter(commit, nil).ForEach(func(c *Commit) error { commits = append(commits, c) return nil }) @@ -57,6 +81,29 @@ func (s *CommitWalkerSuite) TestCommitPostIterator(c *C) { "b029517f6300c2da0f4b651b8642506cd6aaf45d", "35e85108805c84807bc66a02d91535e1e24b38b9", } + + for i, commit := range commits { + c.Assert(commit.Hash.String(), Equals, expected[i]) + } +} + +func (s *CommitWalkerSuite) TestCommitPostIteratorWithIgnore(c *C) { + commit := s.commit(c, s.Fixture.Head) + + var commits []*Commit + NewCommitPostorderIter(commit, []plumbing.Hash{ + plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"), + }).ForEach(func(c *Commit) error { + commits = append(commits, c) + return nil + }) + + c.Assert(commits, HasLen, 2) + + expected := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "918c48b83bd081e863dbe1b80f8998f058cd8294", + } for i, commit := range commits { c.Assert(commit.Hash.String(), Equals, expected[i]) } diff --git a/plumbing/revlist/revlist.go b/plumbing/revlist/revlist.go index 20bc99d..1bda2fa 100644 --- a/plumbing/revlist/revlist.go +++ b/plumbing/revlist/revlist.go @@ -16,11 +16,7 @@ import ( // the reachable objects from the given objects. Ignore param are object hashes // that we want to ignore on the result. All that objects must be accessible // from the object storer. -func Objects( - s storer.EncodedObjectStorer, - objects []plumbing.Hash, - ignore []plumbing.Hash) ([]plumbing.Hash, error) { - +func Objects(s storer.EncodedObjectStorer, objects, ignore []plumbing.Hash) ([]plumbing.Hash, error) { seen := hashListToSet(ignore) result := make(map[plumbing.Hash]bool) @@ -32,7 +28,7 @@ func Objects( } for _, h := range objects { - if err := processObject(s, h, seen, walkerFunc); err != nil { + if err := processObject(s, h, seen, ignore, walkerFunc); err != nil { return nil, err } } @@ -45,6 +41,7 @@ func processObject( s storer.EncodedObjectStorer, h plumbing.Hash, seen map[plumbing.Hash]bool, + ignore []plumbing.Hash, walkerFunc func(h plumbing.Hash), ) error { o, err := s.EncodedObject(plumbing.AnyObject, h) @@ -59,12 +56,12 @@ func processObject( switch do := do.(type) { case *object.Commit: - return reachableObjects(do, seen, walkerFunc) + return reachableObjects(do, seen, ignore, walkerFunc) case *object.Tree: return iterateCommitTrees(seen, do, walkerFunc) case *object.Tag: walkerFunc(do.Hash) - return processObject(s, do.Target, seen, walkerFunc) + return processObject(s, do.Target, seen, ignore, walkerFunc) case *object.Blob: walkerFunc(do.Hash) default: @@ -82,22 +79,24 @@ func processObject( func reachableObjects( commit *object.Commit, seen map[plumbing.Hash]bool, + ignore []plumbing.Hash, cb func(h plumbing.Hash)) error { - return object.NewCommitPreorderIter(commit). - ForEach(func(commit *object.Commit) error { - if seen[commit.Hash] { - return nil - } - cb(commit.Hash) + i := object.NewCommitPreorderIter(commit, ignore) + return i.ForEach(func(commit *object.Commit) error { + if seen[commit.Hash] { + return nil + } + + 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 @@ -104,6 +104,7 @@ func (r *Remote) Push(o *PushOptions) (err error) { req := packp.NewReferenceUpdateRequestFromCapabilities(ar.Capabilities) if err := r.addReferencesToUpdate(o.RefSpecs, remoteRefs, req); err != nil { + return err } @@ -121,6 +122,15 @@ func (r *Remote) Push(o *PushOptions) (err error) { return err } + stop, err := r.s.Shallow() + if err != nil { + return err + } + + // if we have shallow we should include this as part of the objects that + // we are aware. + haves = append(haves, stop...) + hashesToPush, err := revlist.Objects(r.s, objects, haves) if err != nil { return err @@ -486,7 +496,7 @@ func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, } found := false - iter := object.NewCommitPreorderIter(c) + iter := object.NewCommitPreorderIter(c, nil) return found, iter.ForEach(func(c *object.Commit) error { if c.Hash != old { return nil diff --git a/repository.go b/repository.go index 17f0dff..ec9f254 100644 --- a/repository.go +++ b/repository.go @@ -689,7 +689,7 @@ func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) { return nil, err } - return object.NewCommitPreorderIter(commit), nil + return object.NewCommitPreorderIter(commit, nil), nil } // Tags returns all the References from Tags. This method returns all the tag @@ -918,7 +918,7 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err commit = c } case revision.CaretReg: - history := object.NewCommitPreorderIter(commit) + history := object.NewCommitPreorderIter(commit, nil) re := item.(revision.CaretReg).Regexp negate := item.(revision.CaretReg).Negate @@ -948,7 +948,7 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err commit = c case revision.AtDate: - history := object.NewCommitPreorderIter(commit) + history := object.NewCommitPreorderIter(commit, nil) date := item.(revision.AtDate).Date diff --git a/repository_test.go b/repository_test.go index f622007..f31cd1d 100644 --- a/repository_test.go +++ b/repository_test.go @@ -20,6 +20,7 @@ import ( . "gopkg.in/check.v1" "gopkg.in/src-d/go-billy.v3/memfs" "gopkg.in/src-d/go-billy.v3/osfs" + "gopkg.in/src-d/go-billy.v3/util" ) type RepositorySuite struct { @@ -767,6 +768,50 @@ func (s *RepositorySuite) TestPushToEmptyRepository(c *C) { c.Assert(err, IsNil) } +func (s *RepositorySuite) TestPushDepth(c *C) { + dir, err := ioutil.TempDir("", "push-depth") + defer os.RemoveAll(dir) + + origin, err := PlainClone(c.MkDir(), true, &CloneOptions{ + URL: fixtures.Basic().One().DotGit().Root(), + }) + + c.Assert(err, IsNil) + fs := origin.Storer.(*filesystem.Storage).Filesystem() + + r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ + URL: fs.Root(), + Depth: 1, + }) + c.Assert(err, IsNil) + + err = util.WriteFile(r.wt, "foo", nil, 0755) + c.Assert(err, IsNil) + + w, err := r.Worktree() + c.Assert(err, IsNil) + + _, err = w.Add("foo") + c.Assert(err, IsNil) + + hash, err := w.Commit("foo", &CommitOptions{ + Author: defaultSignature(), + Committer: defaultSignature(), + }) + c.Assert(err, IsNil) + + err = r.Push(&PushOptions{}) + c.Assert(err, IsNil) + + remote, err := origin.Head() + c.Assert(err, IsNil) + c.Assert(remote.Hash(), Equals, hash) + + local, err := r.Head() + c.Assert(err, IsNil) + c.Assert(local.Hash(), Equals, remote.Hash()) +} + func (s *RepositorySuite) TestPushNonExistentRemote(c *C) { srcFs := fixtures.Basic().One().DotGit() sto, err := filesystem.NewStorage(srcFs) |