diff options
Diffstat (limited to 'storage')
-rw-r--r-- | storage/filesystem/config_test.go | 4 | ||||
-rw-r--r-- | storage/filesystem/dotgit/dotgit.go | 77 | ||||
-rw-r--r-- | storage/filesystem/dotgit/dotgit_test.go | 35 | ||||
-rw-r--r-- | storage/filesystem/dotgit/writers_test.go | 2 | ||||
-rw-r--r-- | storage/filesystem/object.go | 31 | ||||
-rw-r--r-- | storage/filesystem/object_test.go | 17 | ||||
-rw-r--r-- | storage/memory/storage.go | 4 | ||||
-rw-r--r-- | storage/transactional/config_test.go | 3 | ||||
-rw-r--r-- | storage/transactional/index_test.go | 3 | ||||
-rw-r--r-- | storage/transactional/object_test.go | 3 | ||||
-rw-r--r-- | storage/transactional/reference.go | 2 | ||||
-rw-r--r-- | storage/transactional/reference_test.go | 3 | ||||
-rw-r--r-- | storage/transactional/shallow_test.go | 3 |
13 files changed, 173 insertions, 14 deletions
diff --git a/storage/filesystem/config_test.go b/storage/filesystem/config_test.go index fe84698..c092d14 100644 --- a/storage/filesystem/config_test.go +++ b/storage/filesystem/config_test.go @@ -4,12 +4,12 @@ import ( "io/ioutil" "os" + "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/storage/filesystem/dotgit" - "github.com/go-git/go-billy/v5/osfs" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) type ConfigSuite struct { diff --git a/storage/filesystem/dotgit/dotgit.go b/storage/filesystem/dotgit/dotgit.go index 3840ea7..6c386f7 100644 --- a/storage/filesystem/dotgit/dotgit.go +++ b/storage/filesystem/dotgit/dotgit.go @@ -3,12 +3,14 @@ package dotgit import ( "bufio" + "bytes" "errors" "fmt" "io" stdioutil "io/ioutil" "os" "path/filepath" + "sort" "strings" "time" @@ -88,7 +90,7 @@ type DotGit struct { incomingChecked bool incomingDirName string - objectList []plumbing.Hash + objectList []plumbing.Hash // sorted objectMap map[plumbing.Hash]struct{} packList []plumbing.Hash packMap map[plumbing.Hash]struct{} @@ -336,6 +338,53 @@ func (d *DotGit) NewObject() (*ObjectWriter, error) { return newObjectWriter(d.fs) } +// ObjectsWithPrefix returns the hashes of objects that have the given prefix. +func (d *DotGit) ObjectsWithPrefix(prefix []byte) ([]plumbing.Hash, error) { + // Handle edge cases. + if len(prefix) < 1 { + return d.Objects() + } else if len(prefix) > len(plumbing.ZeroHash) { + return nil, nil + } + + if d.options.ExclusiveAccess { + err := d.genObjectList() + if err != nil { + return nil, err + } + + // Rely on d.objectList being sorted. + // Figure out the half-open interval defined by the prefix. + first := sort.Search(len(d.objectList), func(i int) bool { + // Same as plumbing.HashSlice.Less. + return bytes.Compare(d.objectList[i][:], prefix) >= 0 + }) + lim := len(d.objectList) + if limPrefix, overflow := incBytes(prefix); !overflow { + lim = sort.Search(len(d.objectList), func(i int) bool { + // Same as plumbing.HashSlice.Less. + return bytes.Compare(d.objectList[i][:], limPrefix) >= 0 + }) + } + return d.objectList[first:lim], nil + } + + // This is the slow path. + var objects []plumbing.Hash + var n int + err := d.ForEachObjectHash(func(hash plumbing.Hash) error { + n++ + if bytes.HasPrefix(hash[:], prefix) { + objects = append(objects, hash) + } + return nil + }) + if err != nil { + return nil, err + } + return objects, nil +} + // Objects returns a slice with the hashes of objects found under the // .git/objects/ directory. func (d *DotGit) Objects() ([]plumbing.Hash, error) { @@ -427,12 +476,17 @@ func (d *DotGit) genObjectList() error { } d.objectMap = make(map[plumbing.Hash]struct{}) - return d.forEachObjectHash(func(h plumbing.Hash) error { + populate := func(h plumbing.Hash) error { d.objectList = append(d.objectList, h) d.objectMap[h] = struct{}{} return nil - }) + } + if err := d.forEachObjectHash(populate); err != nil { + return err + } + plumbing.HashesSort(d.objectList) + return nil } func (d *DotGit) hasObject(h plumbing.Hash) error { @@ -1115,3 +1169,20 @@ func isNum(b byte) bool { func isHexAlpha(b byte) bool { return b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F' } + +// incBytes increments a byte slice, which involves incrementing the +// right-most byte, and following carry leftward. +// It makes a copy so that the provided slice's underlying array is not modified. +// If the overall operation overflows (e.g. incBytes(0xff, 0xff)), the second return parameter indicates that. +func incBytes(in []byte) (out []byte, overflow bool) { + out = make([]byte, len(in)) + copy(out, in) + for i := len(out) - 1; i >= 0; i-- { + out[i]++ + if out[i] != 0 { + return // Didn't overflow. + } + } + overflow = true + return +} diff --git a/storage/filesystem/dotgit/dotgit_test.go b/storage/filesystem/dotgit/dotgit_test.go index 0a72aa6..237605f 100644 --- a/storage/filesystem/dotgit/dotgit_test.go +++ b/storage/filesystem/dotgit/dotgit_test.go @@ -2,6 +2,7 @@ package dotgit import ( "bufio" + "encoding/hex" "io/ioutil" "os" "path/filepath" @@ -591,6 +592,7 @@ func (s *SuiteDotGit) TestObjects(c *C) { dir := New(fs) testObjects(c, fs, dir) + testObjectsWithPrefix(c, fs, dir) } func (s *SuiteDotGit) TestObjectsExclusive(c *C) { @@ -598,6 +600,7 @@ func (s *SuiteDotGit) TestObjectsExclusive(c *C) { dir := NewWithOptions(fs, Options{ExclusiveAccess: true}) testObjects(c, fs, dir) + testObjectsWithPrefix(c, fs, dir) } func testObjects(c *C, fs billy.Filesystem, dir *DotGit) { @@ -609,6 +612,20 @@ func testObjects(c *C, fs billy.Filesystem, dir *DotGit) { c.Assert(hashes[2].String(), Equals, "03db8e1fbe133a480f2867aac478fd866686d69e") } +func testObjectsWithPrefix(c *C, fs billy.Filesystem, dir *DotGit) { + prefix, _ := hex.DecodeString("01d5") + hashes, err := dir.ObjectsWithPrefix(prefix) + c.Assert(err, IsNil) + c.Assert(hashes, HasLen, 1) + c.Assert(hashes[0].String(), Equals, "01d5fa556c33743006de7e76e67a2dfcd994ca04") + + // Empty prefix should yield all objects. + // (subset of testObjects) + hashes, err = dir.ObjectsWithPrefix(nil) + c.Assert(err, IsNil) + c.Assert(hashes, HasLen, 187) +} + func (s *SuiteDotGit) TestObjectsNoFolder(c *C) { tmp, err := ioutil.TempDir("", "dot-git") c.Assert(err, IsNil) @@ -835,3 +852,21 @@ type norwfs struct { func (f *norwfs) Capabilities() billy.Capability { return billy.Capabilities(f.Filesystem) &^ billy.ReadAndWriteCapability } + +func (s *SuiteDotGit) TestIncBytes(c *C) { + tests := []struct { + in []byte + out []byte + overflow bool + }{ + {[]byte{0}, []byte{1}, false}, + {[]byte{0xff}, []byte{0}, true}, + {[]byte{7, 0xff}, []byte{8, 0}, false}, + {[]byte{0xff, 0xff}, []byte{0, 0}, true}, + } + for _, test := range tests { + out, overflow := incBytes(test.in) + c.Assert(out, DeepEquals, test.out) + c.Assert(overflow, Equals, test.overflow) + } +} diff --git a/storage/filesystem/dotgit/writers_test.go b/storage/filesystem/dotgit/writers_test.go index 246d310..7147aec 100644 --- a/storage/filesystem/dotgit/writers_test.go +++ b/storage/filesystem/dotgit/writers_test.go @@ -13,8 +13,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/format/packfile" "github.com/go-git/go-billy/v5/osfs" + fixtures "github.com/go-git/go-git-fixtures/v4" . "gopkg.in/check.v1" - "github.com/go-git/go-git-fixtures/v4" ) func (s *SuiteDotGit) TestNewObjectPack(c *C) { diff --git a/storage/filesystem/object.go b/storage/filesystem/object.go index 7437174..0c25dad 100644 --- a/storage/filesystem/object.go +++ b/storage/filesystem/object.go @@ -1,6 +1,7 @@ package filesystem import ( + "bytes" "io" "os" "time" @@ -518,6 +519,36 @@ func (s *ObjectStorage) findObjectInPackfile(h plumbing.Hash) (plumbing.Hash, pl return plumbing.ZeroHash, plumbing.ZeroHash, -1 } +func (s *ObjectStorage) HashesWithPrefix(prefix []byte) ([]plumbing.Hash, error) { + hashes, err := s.dir.ObjectsWithPrefix(prefix) + if err != nil { + return nil, err + } + + // TODO: This could be faster with some idxfile changes, + // or diving into the packfile. + for _, index := range s.index { + ei, err := index.Entries() + if err != nil { + return nil, err + } + for { + e, err := ei.Next() + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + if bytes.HasPrefix(e.Hash[:], prefix) { + hashes = append(hashes, e.Hash) + } + } + ei.Close() + } + + return hashes, nil +} + // IterEncodedObjects returns an iterator for all the objects in the packfile // with the given type. func (s *ObjectStorage) IterEncodedObjects(t plumbing.ObjectType) (storer.EncodedObjectIter, error) { diff --git a/storage/filesystem/object_test.go b/storage/filesystem/object_test.go index 2d6f568..036420f 100644 --- a/storage/filesystem/object_test.go +++ b/storage/filesystem/object_test.go @@ -1,6 +1,7 @@ package filesystem import ( + "encoding/hex" "fmt" "io" "io/ioutil" @@ -332,6 +333,22 @@ func (s *FsSuite) TestGetFromObjectFileSharedCache(c *C) { c.Assert(err, Equals, plumbing.ErrObjectNotFound) } +func (s *FsSuite) TestHashesWithPrefix(c *C) { + // Same setup as TestGetFromObjectFile. + fs := fixtures.ByTag(".git").ByTag("unpacked").One().DotGit() + o := NewObjectStorage(dotgit.New(fs), cache.NewObjectLRUDefault()) + expected := plumbing.NewHash("f3dfe29d268303fc6e1bbce268605fc99573406e") + obj, err := o.EncodedObject(plumbing.AnyObject, expected) + c.Assert(err, IsNil) + c.Assert(obj.Hash(), Equals, expected) + + prefix, _ := hex.DecodeString("f3dfe2") + hashes, err := o.HashesWithPrefix(prefix) + c.Assert(err, IsNil) + c.Assert(hashes, HasLen, 1) + c.Assert(hashes[0].String(), Equals, "f3dfe29d268303fc6e1bbce268605fc99573406e") +} + func BenchmarkPackfileIter(b *testing.B) { defer fixtures.Clean() diff --git a/storage/memory/storage.go b/storage/memory/storage.go index fdf8fcf..a8e5669 100644 --- a/storage/memory/storage.go +++ b/storage/memory/storage.go @@ -195,10 +195,10 @@ func (o *ObjectStorage) DeleteOldObjectPackAndIndex(plumbing.Hash, time.Time) er var errNotSupported = fmt.Errorf("Not supported") -func (s *ObjectStorage) LooseObjectTime(hash plumbing.Hash) (time.Time, error) { +func (o *ObjectStorage) LooseObjectTime(hash plumbing.Hash) (time.Time, error) { return time.Time{}, errNotSupported } -func (s *ObjectStorage) DeleteLooseObject(plumbing.Hash) error { +func (o *ObjectStorage) DeleteLooseObject(plumbing.Hash) error { return errNotSupported } diff --git a/storage/transactional/config_test.go b/storage/transactional/config_test.go index ec7ae89..1f3a572 100644 --- a/storage/transactional/config_test.go +++ b/storage/transactional/config_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&ConfigSuite{}) diff --git a/storage/transactional/index_test.go b/storage/transactional/index_test.go index 88fa1f5..0028c0e 100644 --- a/storage/transactional/index_test.go +++ b/storage/transactional/index_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing/format/index" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&IndexSuite{}) diff --git a/storage/transactional/object_test.go b/storage/transactional/object_test.go index e634409..df277c4 100644 --- a/storage/transactional/object_test.go +++ b/storage/transactional/object_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&ObjectSuite{}) diff --git a/storage/transactional/reference.go b/storage/transactional/reference.go index c3a727c..3b009e2 100644 --- a/storage/transactional/reference.go +++ b/storage/transactional/reference.go @@ -27,7 +27,7 @@ func NewReferenceStorage(base, temporal storer.ReferenceStorer) *ReferenceStorag ReferenceStorer: base, temporal: temporal, - deleted: make(map[plumbing.ReferenceName]struct{}, 0), + deleted: make(map[plumbing.ReferenceName]struct{}), } } diff --git a/storage/transactional/reference_test.go b/storage/transactional/reference_test.go index a6bd1ce..05a4fcf 100644 --- a/storage/transactional/reference_test.go +++ b/storage/transactional/reference_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&ReferenceSuite{}) diff --git a/storage/transactional/shallow_test.go b/storage/transactional/shallow_test.go index 1209fe6..15d423c 100644 --- a/storage/transactional/shallow_test.go +++ b/storage/transactional/shallow_test.go @@ -1,9 +1,10 @@ package transactional import ( - . "gopkg.in/check.v1" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" + + . "gopkg.in/check.v1" ) var _ = Suite(&ShallowSuite{}) |