diff options
Diffstat (limited to 'plumbing')
-rw-r--r-- | plumbing/cache/queue.go | 46 | ||||
-rw-r--r-- | plumbing/format/index/index.go | 32 | ||||
-rw-r--r-- | plumbing/format/index/index_test.go | 37 | ||||
-rw-r--r-- | plumbing/format/index/match.go | 186 | ||||
-rw-r--r-- | plumbing/storer/object.go | 2 | ||||
-rw-r--r-- | plumbing/transport/server/server.go | 11 | ||||
-rw-r--r-- | plumbing/transport/ssh/auth_method.go | 3 | ||||
-rw-r--r-- | plumbing/transport/ssh/auth_method_test.go | 6 | ||||
-rw-r--r-- | plumbing/transport/test/receive_pack.go | 22 |
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) |