aboutsummaryrefslogtreecommitdiffstats
path: root/storage/filesystem/internal/dotgit/dotgit.go
diff options
context:
space:
mode:
authorJeremy Stribling <strib@alum.mit.edu>2017-10-31 15:15:58 -0700
committerJeremy Stribling <strib@alum.mit.edu>2017-11-29 10:32:55 -0800
commit026d7c48163a9d246820c84693673a13f42f9145 (patch)
tree7cf86e1095be31a205c53224d7a648d71cf76a1c /storage/filesystem/internal/dotgit/dotgit.go
parent7ced03216a47327d64f68c750114a96cfcbae38b (diff)
downloadgo-git-026d7c48163a9d246820c84693673a13f42f9145.tar.gz
filesystem: implement PackRefs()
Currently this implementation is only valid for kbfsgit, since it assumes some things about the filesystem not being updated during the packing, and about conflict resolution rules. In the future, it would be nice to replace this with a more general one, and move this kbfsgit-optimized implementation into kbfsgit. Issue: KBFS-2517
Diffstat (limited to 'storage/filesystem/internal/dotgit/dotgit.go')
-rw-r--r--storage/filesystem/internal/dotgit/dotgit.go115
1 files changed, 115 insertions, 0 deletions
diff --git a/storage/filesystem/internal/dotgit/dotgit.go b/storage/filesystem/internal/dotgit/dotgit.go
index 1cb97bd..29e2525 100644
--- a/storage/filesystem/internal/dotgit/dotgit.go
+++ b/storage/filesystem/internal/dotgit/dotgit.go
@@ -576,6 +576,121 @@ func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference,
return d.readReferenceFrom(f, name)
}
+func (d *DotGit) CountLooseRefs() (int, error) {
+ var refs []*plumbing.Reference
+ var seen = make(map[plumbing.ReferenceName]bool)
+ if err := d.addRefsFromRefDir(&refs, seen); err != nil {
+ return 0, err
+ }
+
+ return len(refs), nil
+}
+
+// PackRefs packs all loose refs into the packed-refs file.
+//
+// This implementation only works under the assumption that the view
+// of the file system won't be updated during this operation, which is
+// true for kbfsgit after the Lock() operation is complete (and before
+// the Unlock()/Close() of the locked file). If another process
+// concurrently updates one of the loose refs we delete, then KBFS
+// conflict resolution would just end up ignoring our delete. Also
+// note that deleting a ref requires locking packed-refs, so a ref
+// deleted by the user shouldn't be revived by ref-packing.
+//
+// The strategy would not work on a general file system though,
+// without locking each loose reference and checking it again before
+// deleting the file, because otherwise an updated reference could
+// sneak in and then be deleted by the packed-refs process.
+// Alternatively, every ref update could also lock packed-refs, so
+// only one lock is required during ref-packing. But that would
+// worsen performance in the common case.
+//
+// TODO: before trying to get this merged upstream, move it into a
+// custom kbfsgit Storer implementation, and rewrite this function to
+// work correctly on a general filesystem.
+func (d *DotGit) PackRefs() (err error) {
+ // Lock packed-refs, and create it if it doesn't exist yet.
+ f, err := d.fs.Open(packedRefsPath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ f, err = d.fs.Create(packedRefsPath)
+ if err != nil {
+ return err
+ }
+ } else {
+ return err
+ }
+ }
+ defer ioutil.CheckClose(f, &err)
+
+ err = f.Lock()
+ if err != nil {
+ return err
+ }
+
+ // Gather all refs using addRefsFromRefDir and addRefsFromPackedRefs.
+ var refs []*plumbing.Reference
+ var seen = make(map[plumbing.ReferenceName]bool)
+ if err := d.addRefsFromRefDir(&refs, seen); err != nil {
+ return err
+ }
+ if len(refs) == 0 {
+ // Nothing to do!
+ return nil
+ }
+ numLooseRefs := len(refs)
+ if err := d.addRefsFromPackedRefs(&refs, seen); err != nil {
+ return err
+ }
+
+ // Write them all to a new temp packed-refs file.
+ tmp, err := d.fs.TempFile("", tmpPackedRefsPrefix)
+ if err != nil {
+ return err
+ }
+ doCloseTmp := true
+ defer func() {
+ if doCloseTmp {
+ ioutil.CheckClose(tmp, &err)
+ }
+ }()
+ for _, ref := range refs {
+ _, err := tmp.Write([]byte(ref.String() + "\n"))
+ if err != nil {
+ return err
+ }
+ }
+
+ // Rename the temp packed-refs file.
+ doCloseTmp = false
+ if err := tmp.Close(); err != nil {
+ return err
+ }
+ err = d.fs.Rename(tmp.Name(), packedRefsPath)
+ if err != nil {
+ return err
+ }
+
+ // Delete all the loose refs, while still holding the packed-refs
+ // lock.
+ for _, ref := range refs[:numLooseRefs] {
+ path := d.fs.Join(".", ref.Name().String())
+ err = d.fs.Remove(path)
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ }
+
+ // Update packed-refs cache.
+ d.cachedPackedRefs = make(refCache)
+ for _, ref := range refs {
+ d.cachedPackedRefs[ref.Name()] = ref
+ }
+ d.packedRefsLastMod = time.Now()
+
+ return nil
+}
+
// Module return a billy.Filesystem poiting to the module folder
func (d *DotGit) Module(name string) (billy.Filesystem, error) {
return d.fs.Chroot(d.fs.Join(modulePath, name))