Skip to content

Commit

Permalink
add Prune option to PushOptions
Browse files Browse the repository at this point in the history
Signed-off-by: Stanislav Seletskiy <[email protected]>
  • Loading branch information
seletskiy committed Jul 25, 2019
1 parent 60033b8 commit 5d9007f
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 6 deletions.
7 changes: 7 additions & 0 deletions config/refspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 127,13 @@ func (s RefSpec) Dst(n plumbing.ReferenceName) plumbing.ReferenceName {
return plumbing.ReferenceName(dst[0:wd] match dst[wd 1:])
}

func (s RefSpec) Reverse() RefSpec {
spec := string(s)
separator := strings.Index(spec, refSpecSeparator)

return RefSpec(spec[separator 1:] refSpecSeparator spec[:separator])
}

func (s RefSpec) String() string {
return string(s)
}
Expand Down
9 changes: 9 additions & 0 deletions config/refspec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 116,15 @@ func (s *RefSpecSuite) TestRefSpecDstBlob(c *C) {
"refs/remotes/origin/foo",
)
}

func (s *RefSpecSuite) TestRefSpecReverse(c *C) {
spec := RefSpec("refs/heads/*:refs/remotes/origin/*")
c.Assert(
spec.Reverse(), Equals,
RefSpec("refs/remotes/origin/*:refs/heads/*"),
)
}

func (s *RefSpecSuite) TestMatchAny(c *C) {
specs := []RefSpec{
"refs/heads/bar:refs/remotes/origin/foo",
Expand Down
3 changes: 3 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 186,9 @@ type PushOptions struct {
// Progress is where the human readable information sent by the server is
// stored, if nil nothing is stored.
Progress sideband.Progress
// Prune specify that remote refs that match given RefSpecs and that do
// not exist locally will be removed.
Prune bool
}

// Validate validates the fields and sets the default values.
Expand Down
31 changes: 26 additions & 5 deletions remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 201,7 @@ func (r *Remote) newReferenceUpdateRequest(
}
}

if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req); err != nil {
if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req, o.Prune); err != nil {
return nil, err
}

Expand Down Expand Up @@ -389,6 389,7 @@ func (r *Remote) addReferencesToUpdate(
localRefs []*plumbing.Reference,
remoteRefs storer.ReferenceStorer,
req *packp.ReferenceUpdateRequest,
prune bool,
) error {
// This references dictionary will be used to search references by name.
refsDict := make(map[string]*plumbing.Reference)
Expand All @@ -398,14 399,20 @@ func (r *Remote) addReferencesToUpdate(

for _, rs := range refspecs {
if rs.IsDelete() {
if err := r.deleteReferences(rs, remoteRefs, req); err != nil {
if err := r.deleteReferences(rs, remoteRefs, refsDict, req, false); err != nil {
return err
}
} else {
err := r.addOrUpdateReferences(rs, localRefs, refsDict, remoteRefs, req)
if err != nil {
return err
}

if prune {
if err := r.deleteReferences(rs, remoteRefs, refsDict, req, true); err != nil {
return err
}
}
}
}

Expand Down Expand Up @@ -441,7 448,10 @@ func (r *Remote) addOrUpdateReferences(
}

func (r *Remote) deleteReferences(rs config.RefSpec,
remoteRefs storer.ReferenceStorer, req *packp.ReferenceUpdateRequest) error {
remoteRefs storer.ReferenceStorer,
refsDict map[string]*plumbing.Reference,
req *packp.ReferenceUpdateRequest,
prune bool) error {
iter, err := remoteRefs.IterReferences()
if err != nil {
return err
Expand All @@ -452,8 462,19 @@ func (r *Remote) deleteReferences(rs config.RefSpec,
return nil
}

if rs.Dst("") != ref.Name() {
return nil
if prune {
rs := rs.Reverse()
if !rs.Match(ref.Name()) {
return nil
}

if _, ok := refsDict[rs.Dst(ref.Name()).String()]; ok {
return nil
}
} else {
if rs.Dst("") != ref.Name() {
return nil
}
}

cmd := &packp.Command{
Expand Down
59 changes: 58 additions & 1 deletion remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 21,7 @@ import (

. "gopkg.in/check.v1"
"gopkg.in/src-d/go-billy.v4/osfs"
"gopkg.in/src-d/go-git-fixtures.v3"
fixtures "gopkg.in/src-d/go-git-fixtures.v3"
)

type RemoteSuite struct {
Expand Down Expand Up @@ -583,6 583,63 @@ func (s *RemoteSuite) TestPushForce(c *C) {
c.Assert(newRef, Not(DeepEquals), oldRef)
}

func (s *RemoteSuite) TestPushPrune(c *C) {
fs := fixtures.Basic().One().DotGit()
url := c.MkDir()
server, err := PlainClone(url, true, &CloneOptions{
URL: fs.Root(),
})
c.Assert(err, IsNil)

r, err := PlainClone(c.MkDir(), true, &CloneOptions{
URL: url,
})
c.Assert(err, IsNil)

tag, err := r.Reference(plumbing.ReferenceName("refs/tags/v1.0.0"), true)
c.Assert(err, IsNil)

err = r.DeleteTag("v1.0.0")
c.Assert(err, IsNil)

remote, err := r.Remote(DefaultRemoteName)
c.Assert(err, IsNil)

ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true)
c.Assert(err, IsNil)

err = remote.Push(&PushOptions{
RefSpecs: []config.RefSpec{
config.RefSpec("refs/heads/*:refs/heads/*"),
},
Prune: true,
})
c.Assert(err, Equals, NoErrAlreadyUpToDate)

AssertReferences(c, server, map[string]string{
"refs/tags/v1.0.0": tag.Hash().String(),
})

err = remote.Push(&PushOptions{
RefSpecs: []config.RefSpec{
config.RefSpec("*:*"),
},
Prune: true,
})
c.Assert(err, IsNil)

AssertReferences(c, server, map[string]string{
"refs/remotes/origin/master": ref.Hash().String(),
})

AssertReferences(c, server, map[string]string{
"refs/remotes/origin/master": ref.Hash().String(),
})

ref, err = server.Reference(plumbing.ReferenceName("refs/tags/v1.0.0"), true)
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
}

func (s *RemoteSuite) TestPushNewReference(c *C) {
fs := fixtures.Basic().One().DotGit()
url := c.MkDir()
Expand Down

0 comments on commit 5d9007f

Please sign in to comment.