aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing
diff options
context:
space:
mode:
Diffstat (limited to 'plumbing')
-rw-r--r--plumbing/cache/queue.go46
-rw-r--r--plumbing/format/index/index.go32
-rw-r--r--plumbing/format/index/index_test.go37
-rw-r--r--plumbing/format/index/match.go186
-rw-r--r--plumbing/storer/object.go2
-rw-r--r--plumbing/transport/server/server.go11
-rw-r--r--plumbing/transport/ssh/auth_method.go3
-rw-r--r--plumbing/transport/ssh/auth_method_test.go6
-rw-r--r--plumbing/transport/test/receive_pack.go22
9 files changed, 275 insertions, 70 deletions
diff --git a/plumbing/cache/queue.go b/plumbing/cache/queue.go
deleted file mode 100644
index 85413e0..0000000
--- a/plumbing/cache/queue.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package cache
-
-import "gopkg.in/src-d/go-git.v4/plumbing"
-
-// queue is a basic FIFO queue based on a circular list that resize as needed.
-type queue struct {
- elements []plumbing.Hash
- size int
- head int
- tail int
- count int
-}
-
-// newQueue returns a queue with the specified initial size
-func newQueue(size int) *queue {
- return &queue{
- elements: make([]plumbing.Hash, size),
- size: size,
- }
-}
-
-// Push adds a node to the queue.
-func (q *queue) Push(h plumbing.Hash) {
- if q.head == q.tail && q.count > 0 {
- elements := make([]plumbing.Hash, len(q.elements)+q.size)
- copy(elements, q.elements[q.head:])
- copy(elements[len(q.elements)-q.head:], q.elements[:q.head])
- q.head = 0
- q.tail = len(q.elements)
- q.elements = elements
- }
- q.elements[q.tail] = h
- q.tail = (q.tail + 1) % len(q.elements)
- q.count++
-}
-
-// Pop removes and returns a Hash from the queue in first to last order.
-func (q *queue) Pop() plumbing.Hash {
- if q.count == 0 {
- return plumbing.ZeroHash
- }
- node := q.elements[q.head]
- q.head = (q.head + 1) % len(q.elements)
- q.count--
- return node
-}
diff --git a/plumbing/format/index/index.go b/plumbing/format/index/index.go
index 9de4230..fc7b8cd 100644
--- a/plumbing/format/index/index.go
+++ b/plumbing/format/index/index.go
@@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
+ "path/filepath"
"time"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -51,8 +52,20 @@ type Index struct {
ResolveUndo *ResolveUndo
}
+// Add creates a new Entry and returns it. The caller should first check that
+// another entry with the same path does not exist.
+func (i *Index) Add(path string) *Entry {
+ e := &Entry{
+ Name: filepath.ToSlash(path),
+ }
+
+ i.Entries = append(i.Entries, e)
+ return e
+}
+
// Entry returns the entry that match the given path, if any.
func (i *Index) Entry(path string) (*Entry, error) {
+ path = filepath.ToSlash(path)
for _, e := range i.Entries {
if e.Name == path {
return e, nil
@@ -64,6 +77,7 @@ func (i *Index) Entry(path string) (*Entry, error) {
// Remove remove the entry that match the give path and returns deleted entry.
func (i *Index) Remove(path string) (*Entry, error) {
+ path = filepath.ToSlash(path)
for index, e := range i.Entries {
if e.Name == path {
i.Entries = append(i.Entries[:index], i.Entries[index+1:]...)
@@ -74,6 +88,24 @@ func (i *Index) Remove(path string) (*Entry, error) {
return nil, ErrEntryNotFound
}
+// Glob returns the all entries matching pattern or nil if there is no matching
+// entry. The syntax of patterns is the same as in filepath.Glob.
+func (i *Index) Glob(pattern string) (matches []*Entry, err error) {
+ pattern = filepath.ToSlash(pattern)
+ for _, e := range i.Entries {
+ m, err := match(pattern, e.Name)
+ if err != nil {
+ return nil, err
+ }
+
+ if m {
+ matches = append(matches, e)
+ }
+ }
+
+ return
+}
+
// String is equivalent to `git ls-files --stage --debug`
func (i *Index) String() string {
buf := bytes.NewBuffer(nil)
diff --git a/plumbing/format/index/index_test.go b/plumbing/format/index/index_test.go
index cad5f9c..ecf3c0d 100644
--- a/plumbing/format/index/index_test.go
+++ b/plumbing/format/index/index_test.go
@@ -1,9 +1,22 @@
package index
import (
+ "path/filepath"
+
. "gopkg.in/check.v1"
)
+func (s *IndexSuite) TestIndexAdd(c *C) {
+ idx := &Index{}
+ e := idx.Add("foo")
+ e.Size = 42
+
+ e, err := idx.Entry("foo")
+ c.Assert(err, IsNil)
+ c.Assert(e.Name, Equals, "foo")
+ c.Assert(e.Size, Equals, uint32(42))
+}
+
func (s *IndexSuite) TestIndexEntry(c *C) {
idx := &Index{
Entries: []*Entry{
@@ -37,3 +50,27 @@ func (s *IndexSuite) TestIndexRemove(c *C) {
c.Assert(e, IsNil)
c.Assert(err, Equals, ErrEntryNotFound)
}
+
+func (s *IndexSuite) TestIndexGlob(c *C) {
+ idx := &Index{
+ Entries: []*Entry{
+ {Name: "foo/bar/bar", Size: 42},
+ {Name: "foo/baz/qux", Size: 42},
+ {Name: "fux", Size: 82},
+ },
+ }
+
+ m, err := idx.Glob(filepath.Join("foo", "b*"))
+ c.Assert(err, IsNil)
+ c.Assert(m, HasLen, 2)
+ c.Assert(m[0].Name, Equals, "foo/bar/bar")
+ c.Assert(m[1].Name, Equals, "foo/baz/qux")
+
+ m, err = idx.Glob("f*")
+ c.Assert(err, IsNil)
+ c.Assert(m, HasLen, 3)
+
+ m, err = idx.Glob("f*/baz/q*")
+ c.Assert(err, IsNil)
+ c.Assert(m, HasLen, 1)
+}
diff --git a/plumbing/format/index/match.go b/plumbing/format/index/match.go
new file mode 100644
index 0000000..2891d7d
--- /dev/null
+++ b/plumbing/format/index/match.go
@@ -0,0 +1,186 @@
+package index
+
+import (
+ "path/filepath"
+ "runtime"
+ "unicode/utf8"
+)
+
+// match is filepath.Match with support to match fullpath and not only filenames
+// code from:
+// https://github.com/golang/go/blob/39852bf4cce6927e01d0136c7843f65a801738cb/src/path/filepath/match.go#L44-L224
+func match(pattern, name string) (matched bool, err error) {
+Pattern:
+ for len(pattern) > 0 {
+ var star bool
+ var chunk string
+ star, chunk, pattern = scanChunk(pattern)
+
+ // Look for match at current position.
+ t, ok, err := matchChunk(chunk, name)
+ // if we're the last chunk, make sure we've exhausted the name
+ // otherwise we'll give a false result even if we could still match
+ // using the star
+ if ok && (len(t) == 0 || len(pattern) > 0) {
+ name = t
+ continue
+ }
+ if err != nil {
+ return false, err
+ }
+ if star {
+ // Look for match skipping i+1 bytes.
+ // Cannot skip /.
+ for i := 0; i < len(name); i++ {
+ t, ok, err := matchChunk(chunk, name[i+1:])
+ if ok {
+ // if we're the last chunk, make sure we exhausted the name
+ if len(pattern) == 0 && len(t) > 0 {
+ continue
+ }
+ name = t
+ continue Pattern
+ }
+ if err != nil {
+ return false, err
+ }
+ }
+ }
+ return false, nil
+ }
+ return len(name) == 0, nil
+}
+
+// scanChunk gets the next segment of pattern, which is a non-star string
+// possibly preceded by a star.
+func scanChunk(pattern string) (star bool, chunk, rest string) {
+ for len(pattern) > 0 && pattern[0] == '*' {
+ pattern = pattern[1:]
+ star = true
+ }
+ inrange := false
+ var i int
+Scan:
+ for i = 0; i < len(pattern); i++ {
+ switch pattern[i] {
+ case '\\':
+ if runtime.GOOS != "windows" {
+ // error check handled in matchChunk: bad pattern.
+ if i+1 < len(pattern) {
+ i++
+ }
+ }
+ case '[':
+ inrange = true
+ case ']':
+ inrange = false
+ case '*':
+ if !inrange {
+ break Scan
+ }
+ }
+ }
+ return star, pattern[0:i], pattern[i:]
+}
+
+// matchChunk checks whether chunk matches the beginning of s.
+// If so, it returns the remainder of s (after the match).
+// Chunk is all single-character operators: literals, char classes, and ?.
+func matchChunk(chunk, s string) (rest string, ok bool, err error) {
+ for len(chunk) > 0 {
+ if len(s) == 0 {
+ return
+ }
+ switch chunk[0] {
+ case '[':
+ // character class
+ r, n := utf8.DecodeRuneInString(s)
+ s = s[n:]
+ chunk = chunk[1:]
+ // We can't end right after '[', we're expecting at least
+ // a closing bracket and possibly a caret.
+ if len(chunk) == 0 {
+ err = filepath.ErrBadPattern
+ return
+ }
+ // possibly negated
+ negated := chunk[0] == '^'
+ if negated {
+ chunk = chunk[1:]
+ }
+ // parse all ranges
+ match := false
+ nrange := 0
+ for {
+ if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 {
+ chunk = chunk[1:]
+ break
+ }
+ var lo, hi rune
+ if lo, chunk, err = getEsc(chunk); err != nil {
+ return
+ }
+ hi = lo
+ if chunk[0] == '-' {
+ if hi, chunk, err = getEsc(chunk[1:]); err != nil {
+ return
+ }
+ }
+ if lo <= r && r <= hi {
+ match = true
+ }
+ nrange++
+ }
+ if match == negated {
+ return
+ }
+
+ case '?':
+ _, n := utf8.DecodeRuneInString(s)
+ s = s[n:]
+ chunk = chunk[1:]
+
+ case '\\':
+ if runtime.GOOS != "windows" {
+ chunk = chunk[1:]
+ if len(chunk) == 0 {
+ err = filepath.ErrBadPattern
+ return
+ }
+ }
+ fallthrough
+
+ default:
+ if chunk[0] != s[0] {
+ return
+ }
+ s = s[1:]
+ chunk = chunk[1:]
+ }
+ }
+ return s, true, nil
+}
+
+// getEsc gets a possibly-escaped character from chunk, for a character class.
+func getEsc(chunk string) (r rune, nchunk string, err error) {
+ if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' {
+ err = filepath.ErrBadPattern
+ return
+ }
+ if chunk[0] == '\\' && runtime.GOOS != "windows" {
+ chunk = chunk[1:]
+ if len(chunk) == 0 {
+ err = filepath.ErrBadPattern
+ return
+ }
+ }
+ r, n := utf8.DecodeRuneInString(chunk)
+ if r == utf8.RuneError && n == 1 {
+ err = filepath.ErrBadPattern
+ }
+ nchunk = chunk[n:]
+ if len(nchunk) == 0 {
+ err = filepath.ErrBadPattern
+ }
+ return
+}
diff --git a/plumbing/storer/object.go b/plumbing/storer/object.go
index f1d19ef..92aa629 100644
--- a/plumbing/storer/object.go
+++ b/plumbing/storer/object.go
@@ -174,7 +174,6 @@ func (iter *EncodedObjectLookupIter) Close() {
// no longer needed.
type EncodedObjectSliceIter struct {
series []plumbing.EncodedObject
- pos int
}
// NewEncodedObjectSliceIter returns an object iterator for the given slice of
@@ -218,7 +217,6 @@ func (iter *EncodedObjectSliceIter) Close() {
// longer needed.
type MultiEncodedObjectIter struct {
iters []EncodedObjectIter
- pos int
}
// NewMultiEncodedObjectIter returns an object iterator for the given slice of
diff --git a/plumbing/transport/server/server.go b/plumbing/transport/server/server.go
index 2357bd6..20bd12e 100644
--- a/plumbing/transport/server/server.go
+++ b/plumbing/transport/server/server.go
@@ -298,17 +298,6 @@ func (s *rpSession) updateReferences(req *packp.ReferenceUpdateRequest) {
}
}
-func (s *rpSession) failAtomicUpdate() (*packp.ReportStatus, error) {
- rs := s.reportStatus()
- for _, cs := range rs.CommandStatuses {
- if cs.Error() == nil {
- cs.Status = "atomic updated"
- }
- }
-
- return rs, s.firstErr
-}
-
func (s *rpSession) writePackfile(r io.ReadCloser) error {
if r == nil {
return nil
diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go
index 0cdf2b7..84cfab2 100644
--- a/plumbing/transport/ssh/auth_method.go
+++ b/plumbing/transport/ssh/auth_method.go
@@ -124,6 +124,9 @@ type PublicKeys struct {
// (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
func NewPublicKeys(user string, pemBytes []byte, password string) (*PublicKeys, error) {
block, _ := pem.Decode(pemBytes)
+ if block == nil {
+ return nil, errors.New("invalid PEM data")
+ }
if x509.IsEncryptedPEMBlock(block) {
key, err := x509.DecryptPEMBlock(block, []byte(password))
if err != nil {
diff --git a/plumbing/transport/ssh/auth_method_test.go b/plumbing/transport/ssh/auth_method_test.go
index 1e77ca0..0025669 100644
--- a/plumbing/transport/ssh/auth_method_test.go
+++ b/plumbing/transport/ssh/auth_method_test.go
@@ -143,3 +143,9 @@ func (*SuiteCommon) TestNewPublicKeysFromFile(c *C) {
c.Assert(err, IsNil)
c.Assert(auth, NotNil)
}
+
+func (*SuiteCommon) TestNewPublicKeysWithInvalidPEM(c *C) {
+ auth, err := NewPublicKeys("foo", []byte("bar"), "")
+ c.Assert(err, NotNil)
+ c.Assert(auth, IsNil)
+}
diff --git a/plumbing/transport/test/receive_pack.go b/plumbing/transport/test/receive_pack.go
index 0f3352c..a68329e 100644
--- a/plumbing/transport/test/receive_pack.go
+++ b/plumbing/transport/test/receive_pack.go
@@ -50,7 +50,7 @@ func (s *ReceivePackSuite) TestAdvertisedReferencesNotExists(c *C) {
c.Assert(err, IsNil)
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"master", plumbing.ZeroHash, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")},
+ {Name: "master", Old: plumbing.ZeroHash, New: plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")},
}
writer, err := r.ReceivePack(context.Background(), req)
@@ -99,7 +99,7 @@ func (s *ReceivePackSuite) TestFullSendPackOnEmpty(c *C) {
fixture := fixtures.Basic().ByTag("packfile").One()
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/master", plumbing.ZeroHash, fixture.Head},
+ {Name: "refs/heads/master", Old: plumbing.ZeroHash, New: fixture.Head},
}
s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
@@ -110,7 +110,7 @@ func (s *ReceivePackSuite) TestSendPackWithContext(c *C) {
req := packp.NewReferenceUpdateRequest()
req.Packfile = fixture.Packfile()
req.Commands = []*packp.Command{
- {"refs/heads/master", plumbing.ZeroHash, fixture.Head},
+ {Name: "refs/heads/master", Old: plumbing.ZeroHash, New: fixture.Head},
}
r, err := s.Client.NewReceivePackSession(s.EmptyEndpoint, s.EmptyAuth)
@@ -135,7 +135,7 @@ func (s *ReceivePackSuite) TestSendPackOnEmpty(c *C) {
fixture := fixtures.Basic().ByTag("packfile").One()
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/master", plumbing.ZeroHash, fixture.Head},
+ {Name: "refs/heads/master", Old: plumbing.ZeroHash, New: fixture.Head},
}
s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
@@ -147,7 +147,7 @@ func (s *ReceivePackSuite) TestSendPackOnEmptyWithReportStatus(c *C) {
fixture := fixtures.Basic().ByTag("packfile").One()
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/master", plumbing.ZeroHash, fixture.Head},
+ {Name: "refs/heads/master", Old: plumbing.ZeroHash, New: fixture.Head},
}
req.Capabilities.Set(capability.ReportStatus)
s.receivePack(c, endpoint, req, fixture, full)
@@ -160,7 +160,7 @@ func (s *ReceivePackSuite) TestFullSendPackOnNonEmpty(c *C) {
fixture := fixtures.Basic().ByTag("packfile").One()
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/master", fixture.Head, fixture.Head},
+ {Name: "refs/heads/master", Old: fixture.Head, New: fixture.Head},
}
s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
@@ -172,7 +172,7 @@ func (s *ReceivePackSuite) TestSendPackOnNonEmpty(c *C) {
fixture := fixtures.Basic().ByTag("packfile").One()
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/master", fixture.Head, fixture.Head},
+ {Name: "refs/heads/master", Old: fixture.Head, New: fixture.Head},
}
s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
@@ -184,7 +184,7 @@ func (s *ReceivePackSuite) TestSendPackOnNonEmptyWithReportStatus(c *C) {
fixture := fixtures.Basic().ByTag("packfile").One()
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/master", fixture.Head, fixture.Head},
+ {Name: "refs/heads/master", Old: fixture.Head, New: fixture.Head},
}
req.Capabilities.Set(capability.ReportStatus)
@@ -198,7 +198,7 @@ func (s *ReceivePackSuite) TestSendPackOnNonEmptyWithReportStatusWithError(c *C)
fixture := fixtures.Basic().ByTag("packfile").One()
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/master", plumbing.ZeroHash, fixture.Head},
+ {Name: "refs/heads/master", Old: plumbing.ZeroHash, New: fixture.Head},
}
req.Capabilities.Set(capability.ReportStatus)
@@ -306,7 +306,7 @@ func (s *ReceivePackSuite) testSendPackAddReference(c *C) {
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/newbranch", plumbing.ZeroHash, fixture.Head},
+ {Name: "refs/heads/newbranch", Old: plumbing.ZeroHash, New: fixture.Head},
}
if ar.Capabilities.Supports(capability.ReportStatus) {
req.Capabilities.Set(capability.ReportStatus)
@@ -329,7 +329,7 @@ func (s *ReceivePackSuite) testSendPackDeleteReference(c *C) {
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
- {"refs/heads/newbranch", fixture.Head, plumbing.ZeroHash},
+ {Name: "refs/heads/newbranch", Old: fixture.Head, New: plumbing.ZeroHash},
}
if ar.Capabilities.Supports(capability.ReportStatus) {
req.Capabilities.Set(capability.ReportStatus)