Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

repository: allow push from shallow repositories #489

Merged
merged 2 commits into from
Jul 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions plumbing/object/commit_walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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,
}
}

Expand All @@ -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
}
}

Expand Down
57 changes: 52 additions & 5 deletions plumbing/object/commit_walker_test.go
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
})
Expand All @@ -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
})
Expand All @@ -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])
}
Expand Down
39 changes: 19 additions & 20 deletions plumbing/revlist/revlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
}
}
Expand All @@ -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)
Expand All @@ -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:
Expand All @@ -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
Expand Down
12 changes: 11 additions & 1 deletion remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
45 changes: 45 additions & 0 deletions repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down