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

Commit 1edb992

Browse files
authored
Merge pull request #1165 from seletskiy/push-prune
Remote: add Prune option to PushOptions
2 parents 4a6d836 + 17ea92a commit 1edb992

File tree

5 files changed

+129
-16
lines changed

5 files changed

+129
-16
lines changed

Diff for: config/refspec.go

+7
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ func (s RefSpec) Dst(n plumbing.ReferenceName) plumbing.ReferenceName {
127127
return plumbing.ReferenceName(dst[0:wd] + match + dst[wd+1:])
128128
}
129129

130+
func (s RefSpec) Reverse() RefSpec {
131+
spec := string(s)
132+
separator := strings.Index(spec, refSpecSeparator)
133+
134+
return RefSpec(spec[separator+1:] + refSpecSeparator + spec[:separator])
135+
}
136+
130137
func (s RefSpec) String() string {
131138
return string(s)
132139
}

Diff for: config/refspec_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@ func (s *RefSpecSuite) TestRefSpecDstBlob(c *C) {
167167
)
168168
}
169169
}
170+
171+
func (s *RefSpecSuite) TestRefSpecReverse(c *C) {
172+
spec := RefSpec("refs/heads/*:refs/remotes/origin/*")
173+
c.Assert(
174+
spec.Reverse(), Equals,
175+
RefSpec("refs/remotes/origin/*:refs/heads/*"),
176+
)
177+
}
178+
170179
func (s *RefSpecSuite) TestMatchAny(c *C) {
171180
specs := []RefSpec{
172181
"refs/heads/bar:refs/remotes/origin/foo",

Diff for: options.go

+3
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ type PushOptions struct {
186186
// Progress is where the human readable information sent by the server is
187187
// stored, if nil nothing is stored.
188188
Progress sideband.Progress
189+
// Prune specify that remote refs that match given RefSpecs and that do
190+
// not exist locally will be removed.
191+
Prune bool
189192
}
190193

191194
// Validate validates the fields and sets the default values.

Diff for: remote.go

+52-15
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,17 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
171171
}
172172
}
173173

174-
rs, err := pushHashes(ctx, s, r.s, req, hashesToPush, r.useRefDeltas(ar))
174+
if len(hashesToPush) == 0 {
175+
allDelete = true
176+
for _, command := range req.Commands {
177+
if command.Action() != packp.Delete {
178+
allDelete = false
179+
break
180+
}
181+
}
182+
}
183+
184+
rs, err := pushHashes(ctx, s, r.s, req, hashesToPush, r.useRefDeltas(ar), allDelete)
175185
if err != nil {
176186
return err
177187
}
@@ -204,7 +214,7 @@ func (r *Remote) newReferenceUpdateRequest(
204214
}
205215
}
206216

207-
if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req); err != nil {
217+
if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req, o.Prune); err != nil {
208218
return nil, err
209219
}
210220

@@ -392,6 +402,7 @@ func (r *Remote) addReferencesToUpdate(
392402
localRefs []*plumbing.Reference,
393403
remoteRefs storer.ReferenceStorer,
394404
req *packp.ReferenceUpdateRequest,
405+
prune bool,
395406
) error {
396407
// This references dictionary will be used to search references by name.
397408
refsDict := make(map[string]*plumbing.Reference)
@@ -401,14 +412,20 @@ func (r *Remote) addReferencesToUpdate(
401412

402413
for _, rs := range refspecs {
403414
if rs.IsDelete() {
404-
if err := r.deleteReferences(rs, remoteRefs, req); err != nil {
415+
if err := r.deleteReferences(rs, remoteRefs, refsDict, req, false); err != nil {
405416
return err
406417
}
407418
} else {
408419
err := r.addOrUpdateReferences(rs, localRefs, refsDict, remoteRefs, req)
409420
if err != nil {
410421
return err
411422
}
423+
424+
if prune {
425+
if err := r.deleteReferences(rs, remoteRefs, refsDict, req, true); err != nil {
426+
return err
427+
}
428+
}
412429
}
413430
}
414431

@@ -444,7 +461,10 @@ func (r *Remote) addOrUpdateReferences(
444461
}
445462

446463
func (r *Remote) deleteReferences(rs config.RefSpec,
447-
remoteRefs storer.ReferenceStorer, req *packp.ReferenceUpdateRequest) error {
464+
remoteRefs storer.ReferenceStorer,
465+
refsDict map[string]*plumbing.Reference,
466+
req *packp.ReferenceUpdateRequest,
467+
prune bool) error {
448468
iter, err := remoteRefs.IterReferences()
449469
if err != nil {
450470
return err
@@ -455,8 +475,19 @@ func (r *Remote) deleteReferences(rs config.RefSpec,
455475
return nil
456476
}
457477

458-
if rs.Dst("") != ref.Name() {
459-
return nil
478+
if prune {
479+
rs := rs.Reverse()
480+
if !rs.Match(ref.Name()) {
481+
return nil
482+
}
483+
484+
if _, ok := refsDict[rs.Dst(ref.Name()).String()]; ok {
485+
return nil
486+
}
487+
} else {
488+
if rs.Dst("") != ref.Name() {
489+
return nil
490+
}
460491
}
461492

462493
cmd := &packp.Command{
@@ -1015,10 +1046,11 @@ func pushHashes(
10151046
req *packp.ReferenceUpdateRequest,
10161047
hs []plumbing.Hash,
10171048
useRefDeltas bool,
1049+
allDelete bool,
10181050
) (*packp.ReportStatus, error) {
10191051

10201052
rd, wr := io.Pipe()
1021-
req.Packfile = rd
1053+
10221054
config, err := s.Config()
10231055
if err != nil {
10241056
return nil, err
@@ -1029,15 +1061,20 @@ func pushHashes(
10291061
// to the channel.
10301062
done := make(chan error, 1)
10311063

1032-
go func() {
1033-
e := packfile.NewEncoder(wr, s, useRefDeltas)
1034-
if _, err := e.Encode(hs, config.Pack.Window); err != nil {
1035-
done <- wr.CloseWithError(err)
1036-
return
1037-
}
1064+
if !allDelete {
1065+
req.Packfile = rd
1066+
go func() {
1067+
e := packfile.NewEncoder(wr, s, useRefDeltas)
1068+
if _, err := e.Encode(hs, config.Pack.Window); err != nil {
1069+
done <- wr.CloseWithError(err)
1070+
return
1071+
}
10381072

1039-
done <- wr.Close()
1040-
}()
1073+
done <- wr.Close()
1074+
}()
1075+
} else {
1076+
close(done)
1077+
}
10411078

10421079
rs, err := sess.ReceivePack(ctx, req)
10431080
if err != nil {

Diff for: remote_test.go

+58-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121

2222
. "gopkg.in/check.v1"
2323
"gopkg.in/src-d/go-billy.v4/osfs"
24-
"gopkg.in/src-d/go-git-fixtures.v3"
24+
fixtures "gopkg.in/src-d/go-git-fixtures.v3"
2525
)
2626

2727
type RemoteSuite struct {
@@ -583,6 +583,63 @@ func (s *RemoteSuite) TestPushForce(c *C) {
583583
c.Assert(newRef, Not(DeepEquals), oldRef)
584584
}
585585

586+
func (s *RemoteSuite) TestPushPrune(c *C) {
587+
fs := fixtures.Basic().One().DotGit()
588+
url := c.MkDir()
589+
server, err := PlainClone(url, true, &CloneOptions{
590+
URL: fs.Root(),
591+
})
592+
c.Assert(err, IsNil)
593+
594+
r, err := PlainClone(c.MkDir(), true, &CloneOptions{
595+
URL: url,
596+
})
597+
c.Assert(err, IsNil)
598+
599+
tag, err := r.Reference(plumbing.ReferenceName("refs/tags/v1.0.0"), true)
600+
c.Assert(err, IsNil)
601+
602+
err = r.DeleteTag("v1.0.0")
603+
c.Assert(err, IsNil)
604+
605+
remote, err := r.Remote(DefaultRemoteName)
606+
c.Assert(err, IsNil)
607+
608+
ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true)
609+
c.Assert(err, IsNil)
610+
611+
err = remote.Push(&PushOptions{
612+
RefSpecs: []config.RefSpec{
613+
config.RefSpec("refs/heads/*:refs/heads/*"),
614+
},
615+
Prune: true,
616+
})
617+
c.Assert(err, Equals, NoErrAlreadyUpToDate)
618+
619+
AssertReferences(c, server, map[string]string{
620+
"refs/tags/v1.0.0": tag.Hash().String(),
621+
})
622+
623+
err = remote.Push(&PushOptions{
624+
RefSpecs: []config.RefSpec{
625+
config.RefSpec("*:*"),
626+
},
627+
Prune: true,
628+
})
629+
c.Assert(err, IsNil)
630+
631+
AssertReferences(c, server, map[string]string{
632+
"refs/remotes/origin/master": ref.Hash().String(),
633+
})
634+
635+
AssertReferences(c, server, map[string]string{
636+
"refs/remotes/origin/master": ref.Hash().String(),
637+
})
638+
639+
ref, err = server.Reference(plumbing.ReferenceName("refs/tags/v1.0.0"), true)
640+
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
641+
}
642+
586643
func (s *RemoteSuite) TestPushNewReference(c *C) {
587644
fs := fixtures.Basic().One().DotGit()
588645
url := c.MkDir()

0 commit comments

Comments
 (0)