aboutsummaryrefslogtreecommitdiffstats
path: root/storage/filesystem/internal/dotgit
diff options
context:
space:
mode:
Diffstat (limited to 'storage/filesystem/internal/dotgit')
-rw-r--r--storage/filesystem/internal/dotgit/dotgit.go92
-rw-r--r--storage/filesystem/internal/dotgit/dotgit_test.go91
2 files changed, 169 insertions, 14 deletions
diff --git a/storage/filesystem/internal/dotgit/dotgit.go b/storage/filesystem/internal/dotgit/dotgit.go
index accf9ca..6646e18 100644
--- a/storage/filesystem/internal/dotgit/dotgit.go
+++ b/storage/filesystem/internal/dotgit/dotgit.go
@@ -5,11 +5,12 @@ import (
"bufio"
"errors"
"fmt"
- "io/ioutil"
+ stdioutil "io/ioutil"
"os"
"strings"
"srcd.works/go-git.v4/plumbing"
+ "srcd.works/go-git.v4/utils/ioutil"
"srcd.works/go-billy.v1"
)
@@ -21,6 +22,8 @@ const (
indexPath = "index"
shallowPath = "shallow"
+ tmpPackedRefsPrefix = "._packed-refs"
+
objectsPath = "objects"
packPath = "pack"
refsPath = "refs"
@@ -269,6 +272,21 @@ func (d *DotGit) Ref(name plumbing.ReferenceName) (*plumbing.Reference, error) {
return nil, plumbing.ErrReferenceNotFound
}
+// RemoveRef removes a reference by name.
+func (d *DotGit) RemoveRef(name plumbing.ReferenceName) error {
+ path := d.fs.Join(".", name.String())
+ _, err := d.fs.Stat(path)
+ if err == nil {
+ return d.fs.Remove(path)
+ }
+
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ }
+
+ return d.rewritePackedRefsWithoutRef(name)
+}
+
func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference) (err error) {
f, err := d.fs.Open(packedRefsPath)
if err != nil {
@@ -277,12 +295,7 @@ func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference) (err error)
}
return err
}
-
- defer func() {
- if errClose := f.Close(); err == nil {
- err = errClose
- }
- }()
+ defer ioutil.CheckClose(f, &err)
s := bufio.NewScanner(f)
for s.Scan() {
@@ -299,8 +312,64 @@ func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference) (err error)
return s.Err()
}
+func (d *DotGit) rewritePackedRefsWithoutRef(name plumbing.ReferenceName) (err error) {
+ f, err := d.fs.Open(packedRefsPath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+
+ return err
+ }
+ defer ioutil.CheckClose(f, &err)
+
+ // Creating the temp file in the same directory as the target file
+ // improves our chances for rename operation to be atomic.
+ tmp, err := d.fs.TempFile("", tmpPackedRefsPrefix)
+ if err != nil {
+ return err
+ }
+
+ tmpPath := tmp.Filename()
+ defer ioutil.CheckClose(tmp, &err)
+ defer d.fs.Remove(tmpPath)
+
+ s := bufio.NewScanner(f)
+ found := false
+ for s.Scan() {
+ line := s.Text()
+ ref, err := d.processLine(line)
+ if err != nil {
+ return err
+ }
+
+ if ref != nil && ref.Name() == name {
+ found = true
+ continue
+ }
+
+ if _, err := fmt.Fprintln(tmp, line); err != nil {
+ return err
+ }
+ }
+
+ if err := s.Err(); err != nil {
+ return err
+ }
+
+ if !found {
+ return nil
+ }
+
+ return d.fs.Rename(tmpPath, packedRefsPath)
+}
+
// process lines from a packed-refs file
func (d *DotGit) processLine(line string) (*plumbing.Reference, error) {
+ if len(line) == 0 {
+ return nil, nil
+ }
+
switch line[0] {
case '#': // comment - ignore
return nil, nil
@@ -374,14 +443,9 @@ func (d *DotGit) readReferenceFile(refsPath, refFile string) (ref *plumbing.Refe
if err != nil {
return nil, err
}
+ defer ioutil.CheckClose(f, &err)
- defer func() {
- if errClose := f.Close(); err == nil {
- err = errClose
- }
- }()
-
- b, err := ioutil.ReadAll(f)
+ b, err := stdioutil.ReadAll(f)
if err != nil {
return nil, err
}
diff --git a/storage/filesystem/internal/dotgit/dotgit_test.go b/storage/filesystem/internal/dotgit/dotgit_test.go
index a335e5f..226b299 100644
--- a/storage/filesystem/internal/dotgit/dotgit_test.go
+++ b/storage/filesystem/internal/dotgit/dotgit_test.go
@@ -1,6 +1,7 @@
package dotgit
import (
+ "bufio"
"io/ioutil"
"os"
"path/filepath"
@@ -108,6 +109,96 @@ func (s *SuiteDotGit) TestRefsFromReferenceFile(c *C) {
}
+func (s *SuiteDotGit) TestRemoveRefFromReferenceFile(c *C) {
+ fs := fixtures.Basic().ByTag(".git").One().DotGit()
+ dir := New(fs)
+
+ name := plumbing.ReferenceName("refs/remotes/origin/HEAD")
+ err := dir.RemoveRef(name)
+ c.Assert(err, IsNil)
+
+ refs, err := dir.Refs()
+ c.Assert(err, IsNil)
+
+ ref := findReference(refs, string(name))
+ c.Assert(ref, IsNil)
+}
+
+func (s *SuiteDotGit) TestRemoveRefFromPackedRefs(c *C) {
+ fs := fixtures.Basic().ByTag(".git").One().DotGit()
+ dir := New(fs)
+
+ name := plumbing.ReferenceName("refs/remotes/origin/master")
+ err := dir.RemoveRef(name)
+ c.Assert(err, IsNil)
+
+ b, err := ioutil.ReadFile(filepath.Join(fs.Base(), packedRefsPath))
+ c.Assert(err, IsNil)
+
+ c.Assert(string(b), Equals, ""+
+ "# pack-refs with: peeled fully-peeled \n"+
+ "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 refs/heads/master\n"+
+ "e8d3ffab552895c19b9fcf7aa264d277cde33881 refs/remotes/origin/branch\n")
+}
+
+func (s *SuiteDotGit) TestRemoveRefNonExistent(c *C) {
+ fs := fixtures.Basic().ByTag(".git").One().DotGit()
+ dir := New(fs)
+
+ packedRefs := filepath.Join(fs.Base(), packedRefsPath)
+ before, err := ioutil.ReadFile(packedRefs)
+ c.Assert(err, IsNil)
+
+ name := plumbing.ReferenceName("refs/heads/nonexistent")
+ err = dir.RemoveRef(name)
+ c.Assert(err, IsNil)
+
+ after, err := ioutil.ReadFile(packedRefs)
+ c.Assert(err, IsNil)
+
+ c.Assert(string(before), Equals, string(after))
+}
+
+func (s *SuiteDotGit) TestRemoveRefInvalidPackedRefs(c *C) {
+ fs := fixtures.Basic().ByTag(".git").One().DotGit()
+ dir := New(fs)
+
+ packedRefs := filepath.Join(fs.Base(), packedRefsPath)
+ brokenContent := "BROKEN STUFF REALLY BROKEN"
+
+ err := ioutil.WriteFile(packedRefs, []byte(brokenContent), os.FileMode(0755))
+ c.Assert(err, IsNil)
+
+ name := plumbing.ReferenceName("refs/heads/nonexistent")
+ err = dir.RemoveRef(name)
+ c.Assert(err, NotNil)
+
+ after, err := ioutil.ReadFile(filepath.Join(fs.Base(), packedRefsPath))
+ c.Assert(err, IsNil)
+
+ c.Assert(brokenContent, Equals, string(after))
+}
+
+func (s *SuiteDotGit) TestRemoveRefInvalidPackedRefs2(c *C) {
+ fs := fixtures.Basic().ByTag(".git").One().DotGit()
+ dir := New(fs)
+
+ packedRefs := filepath.Join(fs.Base(), packedRefsPath)
+ brokenContent := strings.Repeat("a", bufio.MaxScanTokenSize*2)
+
+ err := ioutil.WriteFile(packedRefs, []byte(brokenContent), os.FileMode(0755))
+ c.Assert(err, IsNil)
+
+ name := plumbing.ReferenceName("refs/heads/nonexistent")
+ err = dir.RemoveRef(name)
+ c.Assert(err, NotNil)
+
+ after, err := ioutil.ReadFile(filepath.Join(fs.Base(), packedRefsPath))
+ c.Assert(err, IsNil)
+
+ c.Assert(brokenContent, Equals, string(after))
+}
+
func (s *SuiteDotGit) TestRefsFromHEADFile(c *C) {
fs := fixtures.Basic().ByTag(".git").One().DotGit()
dir := New(fs)