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

Commit efe9ecf

Browse files
authored
Merge pull request #269 from smola/remove-reference
plumbing/storer: add RemoveReference
2 parents f5a9c7e + 53385d6 commit efe9ecf

File tree

6 files changed

+206
-14
lines changed

6 files changed

+206
-14
lines changed

plumbing/storer/reference.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type ReferenceStorer interface {
1818
SetReference(*plumbing.Reference) error
1919
Reference(plumbing.ReferenceName) (*plumbing.Reference, error)
2020
IterReferences() (ReferenceIter, error)
21+
RemoveReference(plumbing.ReferenceName) error
2122
}
2223

2324
// ReferenceIter is a generic closable interface for iterating over references

storage/filesystem/internal/dotgit/dotgit.go

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import (
55
"bufio"
66
"errors"
77
"fmt"
8-
"io/ioutil"
8+
stdioutil "io/ioutil"
99
"os"
1010
"strings"
1111

1212
"srcd.works/go-git.v4/plumbing"
13+
"srcd.works/go-git.v4/utils/ioutil"
1314

1415
"srcd.works/go-billy.v1"
1516
)
@@ -21,6 +22,8 @@ const (
2122
indexPath = "index"
2223
shallowPath = "shallow"
2324

25+
tmpPackedRefsPrefix = "._packed-refs"
26+
2427
objectsPath = "objects"
2528
packPath = "pack"
2629
refsPath = "refs"
@@ -269,6 +272,21 @@ func (d *DotGit) Ref(name plumbing.ReferenceName) (*plumbing.Reference, error) {
269272
return nil, plumbing.ErrReferenceNotFound
270273
}
271274

275+
// RemoveRef removes a reference by name.
276+
func (d *DotGit) RemoveRef(name plumbing.ReferenceName) error {
277+
path := d.fs.Join(".", name.String())
278+
_, err := d.fs.Stat(path)
279+
if err == nil {
280+
return d.fs.Remove(path)
281+
}
282+
283+
if err != nil && !os.IsNotExist(err) {
284+
return err
285+
}
286+
287+
return d.rewritePackedRefsWithoutRef(name)
288+
}
289+
272290
func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference) (err error) {
273291
f, err := d.fs.Open(packedRefsPath)
274292
if err != nil {
@@ -277,12 +295,7 @@ func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference) (err error)
277295
}
278296
return err
279297
}
280-
281-
defer func() {
282-
if errClose := f.Close(); err == nil {
283-
err = errClose
284-
}
285-
}()
298+
defer ioutil.CheckClose(f, &err)
286299

287300
s := bufio.NewScanner(f)
288301
for s.Scan() {
@@ -299,8 +312,64 @@ func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference) (err error)
299312
return s.Err()
300313
}
301314

315+
func (d *DotGit) rewritePackedRefsWithoutRef(name plumbing.ReferenceName) (err error) {
316+
f, err := d.fs.Open(packedRefsPath)
317+
if err != nil {
318+
if os.IsNotExist(err) {
319+
return nil
320+
}
321+
322+
return err
323+
}
324+
defer ioutil.CheckClose(f, &err)
325+
326+
// Creating the temp file in the same directory as the target file
327+
// improves our chances for rename operation to be atomic.
328+
tmp, err := d.fs.TempFile("", tmpPackedRefsPrefix)
329+
if err != nil {
330+
return err
331+
}
332+
333+
tmpPath := tmp.Filename()
334+
defer ioutil.CheckClose(tmp, &err)
335+
defer d.fs.Remove(tmpPath)
336+
337+
s := bufio.NewScanner(f)
338+
found := false
339+
for s.Scan() {
340+
line := s.Text()
341+
ref, err := d.processLine(line)
342+
if err != nil {
343+
return err
344+
}
345+
346+
if ref != nil && ref.Name() == name {
347+
found = true
348+
continue
349+
}
350+
351+
if _, err := fmt.Fprintln(tmp, line); err != nil {
352+
return err
353+
}
354+
}
355+
356+
if err := s.Err(); err != nil {
357+
return err
358+
}
359+
360+
if !found {
361+
return nil
362+
}
363+
364+
return d.fs.Rename(tmpPath, packedRefsPath)
365+
}
366+
302367
// process lines from a packed-refs file
303368
func (d *DotGit) processLine(line string) (*plumbing.Reference, error) {
369+
if len(line) == 0 {
370+
return nil, nil
371+
}
372+
304373
switch line[0] {
305374
case '#': // comment - ignore
306375
return nil, nil
@@ -374,14 +443,9 @@ func (d *DotGit) readReferenceFile(refsPath, refFile string) (ref *plumbing.Refe
374443
if err != nil {
375444
return nil, err
376445
}
446+
defer ioutil.CheckClose(f, &err)
377447

378-
defer func() {
379-
if errClose := f.Close(); err == nil {
380-
err = errClose
381-
}
382-
}()
383-
384-
b, err := ioutil.ReadAll(f)
448+
b, err := stdioutil.ReadAll(f)
385449
if err != nil {
386450
return nil, err
387451
}

storage/filesystem/internal/dotgit/dotgit_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotgit
22

33
import (
4+
"bufio"
45
"io/ioutil"
56
"os"
67
"path/filepath"
@@ -108,6 +109,96 @@ func (s *SuiteDotGit) TestRefsFromReferenceFile(c *C) {
108109

109110
}
110111

112+
func (s *SuiteDotGit) TestRemoveRefFromReferenceFile(c *C) {
113+
fs := fixtures.Basic().ByTag(".git").One().DotGit()
114+
dir := New(fs)
115+
116+
name := plumbing.ReferenceName("refs/remotes/origin/HEAD")
117+
err := dir.RemoveRef(name)
118+
c.Assert(err, IsNil)
119+
120+
refs, err := dir.Refs()
121+
c.Assert(err, IsNil)
122+
123+
ref := findReference(refs, string(name))
124+
c.Assert(ref, IsNil)
125+
}
126+
127+
func (s *SuiteDotGit) TestRemoveRefFromPackedRefs(c *C) {
128+
fs := fixtures.Basic().ByTag(".git").One().DotGit()
129+
dir := New(fs)
130+
131+
name := plumbing.ReferenceName("refs/remotes/origin/master")
132+
err := dir.RemoveRef(name)
133+
c.Assert(err, IsNil)
134+
135+
b, err := ioutil.ReadFile(filepath.Join(fs.Base(), packedRefsPath))
136+
c.Assert(err, IsNil)
137+
138+
c.Assert(string(b), Equals, ""+
139+
"# pack-refs with: peeled fully-peeled \n"+
140+
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 refs/heads/master\n"+
141+
"e8d3ffab552895c19b9fcf7aa264d277cde33881 refs/remotes/origin/branch\n")
142+
}
143+
144+
func (s *SuiteDotGit) TestRemoveRefNonExistent(c *C) {
145+
fs := fixtures.Basic().ByTag(".git").One().DotGit()
146+
dir := New(fs)
147+
148+
packedRefs := filepath.Join(fs.Base(), packedRefsPath)
149+
before, err := ioutil.ReadFile(packedRefs)
150+
c.Assert(err, IsNil)
151+
152+
name := plumbing.ReferenceName("refs/heads/nonexistent")
153+
err = dir.RemoveRef(name)
154+
c.Assert(err, IsNil)
155+
156+
after, err := ioutil.ReadFile(packedRefs)
157+
c.Assert(err, IsNil)
158+
159+
c.Assert(string(before), Equals, string(after))
160+
}
161+
162+
func (s *SuiteDotGit) TestRemoveRefInvalidPackedRefs(c *C) {
163+
fs := fixtures.Basic().ByTag(".git").One().DotGit()
164+
dir := New(fs)
165+
166+
packedRefs := filepath.Join(fs.Base(), packedRefsPath)
167+
brokenContent := "BROKEN STUFF REALLY BROKEN"
168+
169+
err := ioutil.WriteFile(packedRefs, []byte(brokenContent), os.FileMode(0755))
170+
c.Assert(err, IsNil)
171+
172+
name := plumbing.ReferenceName("refs/heads/nonexistent")
173+
err = dir.RemoveRef(name)
174+
c.Assert(err, NotNil)
175+
176+
after, err := ioutil.ReadFile(filepath.Join(fs.Base(), packedRefsPath))
177+
c.Assert(err, IsNil)
178+
179+
c.Assert(brokenContent, Equals, string(after))
180+
}
181+
182+
func (s *SuiteDotGit) TestRemoveRefInvalidPackedRefs2(c *C) {
183+
fs := fixtures.Basic().ByTag(".git").One().DotGit()
184+
dir := New(fs)
185+
186+
packedRefs := filepath.Join(fs.Base(), packedRefsPath)
187+
brokenContent := strings.Repeat("a", bufio.MaxScanTokenSize*2)
188+
189+
err := ioutil.WriteFile(packedRefs, []byte(brokenContent), os.FileMode(0755))
190+
c.Assert(err, IsNil)
191+
192+
name := plumbing.ReferenceName("refs/heads/nonexistent")
193+
err = dir.RemoveRef(name)
194+
c.Assert(err, NotNil)
195+
196+
after, err := ioutil.ReadFile(filepath.Join(fs.Base(), packedRefsPath))
197+
c.Assert(err, IsNil)
198+
199+
c.Assert(brokenContent, Equals, string(after))
200+
}
201+
111202
func (s *SuiteDotGit) TestRefsFromHEADFile(c *C) {
112203
fs := fixtures.Basic().ByTag(".git").One().DotGit()
113204
dir := New(fs)

storage/filesystem/reference.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,7 @@ func (r *ReferenceStorage) IterReferences() (storer.ReferenceIter, error) {
2626

2727
return storer.NewReferenceSliceIter(refs), nil
2828
}
29+
30+
func (r *ReferenceStorage) RemoveReference(n plumbing.ReferenceName) error {
31+
return r.dir.RemoveRef(n)
32+
}

storage/memory/storage.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ func (r ReferenceStorage) IterReferences() (storer.ReferenceIter, error) {
217217
return storer.NewReferenceSliceIter(refs), nil
218218
}
219219

220+
func (r ReferenceStorage) RemoveReference(n plumbing.ReferenceName) error {
221+
delete(r, n)
222+
return nil
223+
}
224+
220225
type ShallowStorage []plumbing.Hash
221226

222227
func (s *ShallowStorage) SetShallow(commits []plumbing.Hash) error {

storage/test/storage_suite.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,33 @@ func (s *BaseStorageSuite) TestSetReferenceAndGetReference(c *C) {
242242
c.Assert(e.Hash().String(), Equals, "bc9968d75e48de59f0870ffb71f5e160bbbdcf52")
243243
}
244244

245+
func (s *BaseStorageSuite) TestRemoveReference(c *C) {
246+
err := s.Storer.SetReference(
247+
plumbing.NewReferenceFromStrings("foo", "bc9968d75e48de59f0870ffb71f5e160bbbdcf52"),
248+
)
249+
c.Assert(err, IsNil)
250+
251+
err = s.Storer.RemoveReference(plumbing.ReferenceName("foo"))
252+
c.Assert(err, IsNil)
253+
254+
_, err = s.Storer.Reference(plumbing.ReferenceName("foo"))
255+
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
256+
}
257+
258+
func (s *BaseStorageSuite) TestRemoveReferenceNonExistent(c *C) {
259+
err := s.Storer.SetReference(
260+
plumbing.NewReferenceFromStrings("foo", "bc9968d75e48de59f0870ffb71f5e160bbbdcf52"),
261+
)
262+
c.Assert(err, IsNil)
263+
264+
err = s.Storer.RemoveReference(plumbing.ReferenceName("nonexistent"))
265+
c.Assert(err, IsNil)
266+
267+
e, err := s.Storer.Reference(plumbing.ReferenceName("foo"))
268+
c.Assert(err, IsNil)
269+
c.Assert(e.Hash().String(), Equals, "bc9968d75e48de59f0870ffb71f5e160bbbdcf52")
270+
}
271+
245272
func (s *BaseStorageSuite) TestGetReferenceNotFound(c *C) {
246273
r, err := s.Storer.Reference(plumbing.ReferenceName("bar"))
247274
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)

0 commit comments

Comments
 (0)