aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2017-11-15 18:33:41 +0200
committerJeremy Stribling <strib@alum.mit.edu>2017-11-29 11:01:18 -0800
commitfae438980c3e17cb04f84ce92b99cd3c835e3e18 (patch)
tree49897b1fef2644f332015a7d7f3333c3e6c935e8
parent3f0b1ff37b64108cfed1b57ea4ae1f1566592905 (diff)
downloadgo-git-fae438980c3e17cb04f84ce92b99cd3c835e3e18.tar.gz
Support for repacking objects
-rw-r--r--plumbing/storer/object.go5
-rw-r--r--plumbing/storer/object_test.go4
-rw-r--r--repository.go62
-rw-r--r--storage/filesystem/internal/dotgit/dotgit.go34
-rw-r--r--storage/filesystem/object.go8
-rw-r--r--storage/memory/storage.go7
6 files changed, 107 insertions, 13 deletions
diff --git a/plumbing/storer/object.go b/plumbing/storer/object.go
index bd34be8..29e0090 100644
--- a/plumbing/storer/object.go
+++ b/plumbing/storer/object.go
@@ -52,6 +52,11 @@ type EncodedObjectStorer interface {
LooseObjectTime(plumbing.Hash) (time.Time, error)
// DeleteLooseObject deletes a loose object if it exists.
DeleteLooseObject(plumbing.Hash) error
+ // ObjectPacks returns hashes of object packs if the underlying
+ // implementation has pack files.
+ ObjectPacks() ([]plumbing.Hash, error)
+ // DeleteObjectPackAndIndex deletes an object pack and the corresponding index file if they exist.
+ DeleteObjectPackAndIndex(plumbing.Hash) error
}
// DeltaObjectStorer is an EncodedObjectStorer that can return delta
diff --git a/plumbing/storer/object_test.go b/plumbing/storer/object_test.go
index f8b1f73..68e8e10 100644
--- a/plumbing/storer/object_test.go
+++ b/plumbing/storer/object_test.go
@@ -161,3 +161,7 @@ func (o *MockObjectStorage) LooseObjectTime(plumbing.Hash) (time.Time, error) {
func (o *MockObjectStorage) DeleteLooseObject(plumbing.Hash) error {
return plumbing.ErrObjectNotFound
}
+
+func (o *MockObjectStorage) ObjectPacks() ([]plumbing.Hash, error) {
+ return nil, nil
+}
diff --git a/repository.go b/repository.go
index b159ff0..51cd077 100644
--- a/repository.go
+++ b/repository.go
@@ -12,6 +12,7 @@ import (
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/internal/revision"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage"
@@ -1011,3 +1012,64 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err
return &commit.Hash, nil
}
+
+func (r *Repository) RepackObjects() (err error) {
+ // Get the existing object packs.
+ hs, err := r.Storer.ObjectPacks()
+ if err != nil {
+ return err
+ }
+
+ // Create a new pack.
+ nh, err := r.createNewObjectPack()
+ if err != nil {
+ return err
+ }
+
+ // Delete old packs.
+ for _, h := range hs {
+ // Skip if new hash is the same as an old one.
+ if h == nh {
+ continue
+ }
+ err = r.Storer.DeleteObjectPackAndIndex(h)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// createNewObjectPack is a helper for RepackObjects taking care
+// of creating a new pack. It is used so the the PackfileWriter
+// deferred close has the right scope.
+func (r *Repository) createNewObjectPack() (h plumbing.Hash, err error) {
+ pfw, ok := r.Storer.(storer.PackfileWriter)
+ if !ok {
+ return h, fmt.Errorf("Repository storer is not a storer.PackfileWriter")
+ }
+ wc, err := pfw.PackfileWriter(nil)
+ if err != nil {
+ return h, err
+ }
+ defer ioutil.CheckClose(wc, &err)
+ var objs []plumbing.Hash
+ iter, err := r.Storer.IterEncodedObjects(plumbing.AnyObject)
+ if err != nil {
+ return h, err
+ }
+ err = iter.ForEach(func(obj plumbing.EncodedObject) error {
+ objs = append(objs, obj.Hash())
+ return nil
+ })
+ if err != nil {
+ return h, err
+ }
+ enc := packfile.NewEncoder(wc, r.Storer, false)
+ h, err = enc.Encode(objs, 10, nil)
+ if err != nil {
+ return h, err
+ }
+ return h, err
+}
diff --git a/storage/filesystem/internal/dotgit/dotgit.go b/storage/filesystem/internal/dotgit/dotgit.go
index 83fe159..09688c1 100644
--- a/storage/filesystem/internal/dotgit/dotgit.go
+++ b/storage/filesystem/internal/dotgit/dotgit.go
@@ -166,11 +166,12 @@ func (d *DotGit) ObjectPacks() ([]plumbing.Hash, error) {
return packs, nil
}
-// ObjectPack returns a fs.File of the given packfile
-func (d *DotGit) ObjectPack(hash plumbing.Hash) (billy.File, error) {
- file := d.fs.Join(objectsPath, packPath, fmt.Sprintf("pack-%s.pack", hash.String()))
+func (d *DotGit) objectPackPath(hash plumbing.Hash, extension string) string {
+ return d.fs.Join(objectsPath, packPath, fmt.Sprintf("pack-%s.%s", hash.String(), extension))
+}
- pack, err := d.fs.Open(file)
+func (d *DotGit) objectPackOpen(hash plumbing.Hash, extension string) (billy.File, error) {
+ pack, err := d.fs.Open(d.objectPackPath(hash, extension))
if err != nil {
if os.IsNotExist(err) {
return nil, ErrPackfileNotFound
@@ -182,19 +183,26 @@ func (d *DotGit) ObjectPack(hash plumbing.Hash) (billy.File, error) {
return pack, nil
}
+// ObjectPack returns a fs.File of the given packfile
+func (d *DotGit) ObjectPack(hash plumbing.Hash) (billy.File, error) {
+ return d.objectPackOpen(hash, `pack`)
+}
+
// ObjectPackIdx returns a fs.File of the index file for a given packfile
func (d *DotGit) ObjectPackIdx(hash plumbing.Hash) (billy.File, error) {
- file := d.fs.Join(objectsPath, packPath, fmt.Sprintf("pack-%s.idx", hash.String()))
- idx, err := d.fs.Open(file)
- if err != nil {
- if os.IsNotExist(err) {
- return nil, ErrPackfileNotFound
- }
+ return d.objectPackOpen(hash, `idx`)
+}
- return nil, err
+func (d *DotGit) DeleteObjectPackAndIndex(hash plumbing.Hash) error {
+ err := d.fs.Remove(d.objectPackPath(hash, `pack`))
+ if err != nil {
+ return err
}
-
- return idx, nil
+ err = d.fs.Remove(d.objectPackPath(hash, `idx`))
+ if err != nil {
+ return err
+ }
+ return nil
}
// NewObject return a writer for a new object file.
diff --git a/storage/filesystem/object.go b/storage/filesystem/object.go
index 9b2790f..f235204 100644
--- a/storage/filesystem/object.go
+++ b/storage/filesystem/object.go
@@ -503,3 +503,11 @@ func (s *ObjectStorage) LooseObjectTime(hash plumbing.Hash) (time.Time, error) {
func (s *ObjectStorage) DeleteLooseObject(hash plumbing.Hash) error {
return s.dir.ObjectDelete(hash)
}
+
+func (s *ObjectStorage) ObjectPacks() ([]plumbing.Hash, error) {
+ return s.dir.ObjectPacks()
+}
+
+func (s *ObjectStorage) DeleteObjectPackAndIndex(h plumbing.Hash) error {
+ return s.dir.DeleteObjectPackAndIndex(h)
+}
diff --git a/storage/memory/storage.go b/storage/memory/storage.go
index bfcad34..5049036 100644
--- a/storage/memory/storage.go
+++ b/storage/memory/storage.go
@@ -170,6 +170,13 @@ func (o *ObjectStorage) ForEachObjectHash(fun func(plumbing.Hash) error) error {
return nil
}
+func (o *ObjectStorage) ObjectPacks() ([]plumbing.Hash, error) {
+ return nil, nil
+}
+func (o *ObjectStorage) DeleteObjectPackAndIndex(plumbing.Hash) error {
+ return errNotSupported
+}
+
var errNotSupported = fmt.Errorf("Not supported")
func (s *ObjectStorage) LooseObjectTime(hash plumbing.Hash) (time.Time, error) {