aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/object
diff options
context:
space:
mode:
authorSantiago M. Mola <santi@mola.io>2016-12-14 23:12:44 +0100
committerMáximo Cuadros <mcuadros@gmail.com>2016-12-14 23:12:44 +0100
commit0af572dd21c0aa79d13745b633ee24ba6c4d6cf1 (patch)
tree49e81e74e82d84fd88b2fc1e4b0dc7c7bfe9c40f /plumbing/object
parentdf0f38af83f972f026d7e14150f3d37b95f13484 (diff)
downloadgo-git-0af572dd21c0aa79d13745b633ee24ba6c4d6cf1.tar.gz
move plumbing from top level package to plumbing (#183)
* plumbing: rename Object -> EncodedObject. * plumbing/storer: rename ObjectStorer -> EncodedObjectStorer. * move difftree to plumbing/difftree. * move diff -> utils/diff * make Object/Tag/Blob/Tree/Commit/File depend on storer. * Object and its implementations now depend only on storer.EncodedObjectStorer, not git.Repository. * Tests are decoupled accordingly. * move Object/Commit/File/Tag/Tree to plumbing/object. * move Object/Commit/File/Tag/Tree to plumbing/object. * move checkClose to utils/ioutil. * move RevListObjects to plumbing/revlist.Objects. * move DiffTree to plumbing/difftree package. * rename files with plural nouns to singular * plumbing/object: add GetBlob/GetCommit/GetTag/GetTree.
Diffstat (limited to 'plumbing/object')
-rw-r--r--plumbing/object/blob.go135
-rw-r--r--plumbing/object/blob_test.go96
-rw-r--r--plumbing/object/commit.go293
-rw-r--r--plumbing/object/commit_test.go169
-rw-r--r--plumbing/object/commit_walker.go67
-rw-r--r--plumbing/object/commit_walker_test.go34
-rw-r--r--plumbing/object/file.go116
-rw-r--r--plumbing/object/file_test.go249
-rw-r--r--plumbing/object/object.go217
-rw-r--r--plumbing/object/object_test.go199
-rw-r--r--plumbing/object/tag.go268
-rw-r--r--plumbing/object/tag_test.go169
-rw-r--r--plumbing/object/tree.go449
-rw-r--r--plumbing/object/tree_test.go1425
14 files changed, 3886 insertions, 0 deletions
diff --git a/plumbing/object/blob.go b/plumbing/object/blob.go
new file mode 100644
index 0000000..b0cac41
--- /dev/null
+++ b/plumbing/object/blob.go
@@ -0,0 +1,135 @@
+package object
+
+import (
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
+)
+
+// Blob is used to store file data - it is generally a file.
+type Blob struct {
+ Hash plumbing.Hash
+ Size int64
+
+ obj plumbing.EncodedObject
+}
+
+// GetBlob gets a blob from an object storer and decodes it.
+func GetBlob(s storer.EncodedObjectStorer, h plumbing.Hash) (*Blob, error) {
+ o, err := s.EncodedObject(plumbing.BlobObject, h)
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeBlob(o)
+}
+
+func DecodeBlob(o plumbing.EncodedObject) (*Blob, error) {
+ b := &Blob{}
+ if err := b.Decode(o); err != nil {
+ return nil, err
+ }
+
+ return b, nil
+}
+
+// ID returns the object ID of the blob. The returned value will always match
+// the current value of Blob.Hash.
+//
+// ID is present to fulfill the Object interface.
+func (b *Blob) ID() plumbing.Hash {
+ return b.Hash
+}
+
+// Type returns the type of object. It always returns plumbing.BlobObject.
+//
+// Type is present to fulfill the Object interface.
+func (b *Blob) Type() plumbing.ObjectType {
+ return plumbing.BlobObject
+}
+
+// Decode transforms a plumbing.EncodedObject into a Blob struct.
+func (b *Blob) Decode(o plumbing.EncodedObject) error {
+ if o.Type() != plumbing.BlobObject {
+ return ErrUnsupportedObject
+ }
+
+ b.Hash = o.Hash()
+ b.Size = o.Size()
+ b.obj = o
+
+ return nil
+}
+
+// Encode transforms a Blob into a plumbing.EncodedObject.
+func (b *Blob) Encode(o plumbing.EncodedObject) error {
+ w, err := o.Writer()
+ if err != nil {
+ return err
+ }
+ defer ioutil.CheckClose(w, &err)
+ r, err := b.Reader()
+ if err != nil {
+ return err
+ }
+ defer ioutil.CheckClose(r, &err)
+ _, err = io.Copy(w, r)
+ o.SetType(plumbing.BlobObject)
+ return err
+}
+
+// Reader returns a reader allow the access to the content of the blob
+func (b *Blob) Reader() (io.ReadCloser, error) {
+ return b.obj.Reader()
+}
+
+// BlobIter provides an iterator for a set of blobs.
+type BlobIter struct {
+ storer.EncodedObjectIter
+ s storer.EncodedObjectStorer
+}
+
+// NewBlobIter returns a CommitIter for the given repository and underlying
+// object iterator.
+//
+// The returned BlobIter will automatically skip over non-blob objects.
+func NewBlobIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *BlobIter {
+ return &BlobIter{iter, s}
+}
+
+// Next moves the iterator to the next blob and returns a pointer to it. If it
+// has reached the end of the set it will return io.EOF.
+func (iter *BlobIter) Next() (*Blob, error) {
+ for {
+ obj, err := iter.EncodedObjectIter.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ if obj.Type() != plumbing.BlobObject {
+ continue
+ }
+
+ return DecodeBlob(obj)
+ }
+}
+
+// ForEach call the cb function for each blob contained on this iter until
+// an error happens or the end of the iter is reached. If ErrStop is sent
+// the iteration is stop but no error is returned. The iterator is closed.
+func (iter *BlobIter) ForEach(cb func(*Blob) error) error {
+ return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error {
+ if obj.Type() != plumbing.BlobObject {
+ return nil
+ }
+
+ b, err := DecodeBlob(obj)
+ if err != nil {
+ return err
+ }
+
+ return cb(b)
+ })
+}
diff --git a/plumbing/object/blob_test.go b/plumbing/object/blob_test.go
new file mode 100644
index 0000000..5ed9de0
--- /dev/null
+++ b/plumbing/object/blob_test.go
@@ -0,0 +1,96 @@
+package object
+
+import (
+ "io"
+ "io/ioutil"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+
+ . "gopkg.in/check.v1"
+)
+
+type BlobsSuite struct {
+ BaseObjectsSuite
+}
+
+var _ = Suite(&BlobsSuite{})
+
+func (s *BlobsSuite) TestBlobHash(c *C) {
+ o := &plumbing.MemoryObject{}
+ o.SetType(plumbing.BlobObject)
+ o.SetSize(3)
+
+ writer, err := o.Writer()
+ c.Assert(err, IsNil)
+ defer func() { c.Assert(writer.Close(), IsNil) }()
+
+ writer.Write([]byte{'F', 'O', 'O'})
+
+ blob := &Blob{}
+ c.Assert(blob.Decode(o), IsNil)
+
+ c.Assert(blob.Size, Equals, int64(3))
+ c.Assert(blob.Hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5")
+
+ reader, err := blob.Reader()
+ c.Assert(err, IsNil)
+ defer func() { c.Assert(reader.Close(), IsNil) }()
+
+ data, err := ioutil.ReadAll(reader)
+ c.Assert(err, IsNil)
+ c.Assert(string(data), Equals, "FOO")
+}
+
+func (s *BlobsSuite) TestBlobDecodeEncodeIdempotent(c *C) {
+ var objects []*plumbing.MemoryObject
+ for _, str := range []string{"foo", "foo\n"} {
+ obj := &plumbing.MemoryObject{}
+ obj.Write([]byte(str))
+ obj.SetType(plumbing.BlobObject)
+ obj.Hash()
+ objects = append(objects, obj)
+ }
+ for _, object := range objects {
+ blob := &Blob{}
+ err := blob.Decode(object)
+ c.Assert(err, IsNil)
+ newObject := &plumbing.MemoryObject{}
+ err = blob.Encode(newObject)
+ c.Assert(err, IsNil)
+ newObject.Hash() // Ensure Hash is pre-computed before deep comparison
+ c.Assert(newObject, DeepEquals, object)
+ }
+}
+
+func (s *BlobsSuite) TestBlobIter(c *C) {
+ encIter, err := s.Storer.IterEncodedObjects(plumbing.BlobObject)
+ c.Assert(err, IsNil)
+ iter := NewBlobIter(s.Storer, encIter)
+
+ blobs := []*Blob{}
+ iter.ForEach(func(b *Blob) error {
+ blobs = append(blobs, b)
+ return nil
+ })
+
+ c.Assert(len(blobs) > 0, Equals, true)
+ iter.Close()
+
+ encIter, err = s.Storer.IterEncodedObjects(plumbing.BlobObject)
+ c.Assert(err, IsNil)
+ iter = NewBlobIter(s.Storer, encIter)
+
+ i := 0
+ for {
+ b, err := iter.Next()
+ if err == io.EOF {
+ break
+ }
+
+ c.Assert(err, IsNil)
+ c.Assert(b, DeepEquals, blobs[i])
+ i += 1
+ }
+
+ iter.Close()
+}
diff --git a/plumbing/object/commit.go b/plumbing/object/commit.go
new file mode 100644
index 0000000..f0ce6e8
--- /dev/null
+++ b/plumbing/object/commit.go
@@ -0,0 +1,293 @@
+package object
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "sort"
+ "strings"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
+)
+
+// Hash hash of an object
+type Hash plumbing.Hash
+
+// Commit points to a single tree, marking it as what the project looked like
+// at a certain point in time. It contains meta-information about that point
+// in time, such as a timestamp, the author of the changes since the last
+// commit, a pointer to the previous commit(s), etc.
+// http://schacon.github.io/gitbook/1_the_git_object_model.html
+type Commit struct {
+ Hash plumbing.Hash
+ Author Signature
+ Committer Signature
+ Message string
+
+ tree plumbing.Hash
+ parents []plumbing.Hash
+ s storer.EncodedObjectStorer
+}
+
+// GetCommit gets a commit from an object storer and decodes it.
+func GetCommit(s storer.EncodedObjectStorer, h plumbing.Hash) (*Commit, error) {
+ o, err := s.EncodedObject(plumbing.CommitObject, h)
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeCommit(s, o)
+}
+
+// DecodeCommit decodes an encoded object into a *Commit and associates it to
+// the given object storer.
+func DecodeCommit(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (*Commit, error) {
+ c := &Commit{s: s}
+ if err := c.Decode(o); err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
+
+// Tree returns the Tree from the commit
+func (c *Commit) Tree() (*Tree, error) {
+ return GetTree(c.s, c.tree)
+}
+
+// Parents return a CommitIter to the parent Commits
+func (c *Commit) Parents() *CommitIter {
+ return NewCommitIter(c.s,
+ storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, c.parents),
+ )
+}
+
+// NumParents returns the number of parents in a commit.
+func (c *Commit) NumParents() int {
+ return len(c.parents)
+}
+
+// File returns the file with the specified "path" in the commit and a
+// nil error if the file exists. If the file does not exist, it returns
+// a nil file and the ErrFileNotFound error.
+func (c *Commit) File(path string) (*File, error) {
+ tree, err := c.Tree()
+ if err != nil {
+ return nil, err
+ }
+
+ return tree.File(path)
+}
+
+// Files returns a FileIter allowing to iterate over the Tree
+func (c *Commit) Files() (*FileIter, error) {
+ tree, err := c.Tree()
+ if err != nil {
+ return nil, err
+ }
+
+ return tree.Files(), nil
+}
+
+// ID returns the object ID of the commit. The returned value will always match
+// the current value of Commit.Hash.
+//
+// ID is present to fulfill the Object interface.
+func (c *Commit) ID() plumbing.Hash {
+ return c.Hash
+}
+
+// Type returns the type of object. It always returns plumbing.CommitObject.
+//
+// Type is present to fulfill the Object interface.
+func (c *Commit) Type() plumbing.ObjectType {
+ return plumbing.CommitObject
+}
+
+// Decode transforms a plumbing.EncodedObject into a Commit struct.
+func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
+ if o.Type() != plumbing.CommitObject {
+ return ErrUnsupportedObject
+ }
+
+ c.Hash = o.Hash()
+
+ reader, err := o.Reader()
+ if err != nil {
+ return err
+ }
+ defer ioutil.CheckClose(reader, &err)
+
+ r := bufio.NewReader(reader)
+
+ var message bool
+ for {
+ line, err := r.ReadSlice('\n')
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ if !message {
+ line = bytes.TrimSpace(line)
+ if len(line) == 0 {
+ message = true
+ continue
+ }
+
+ split := bytes.SplitN(line, []byte{' '}, 2)
+ switch string(split[0]) {
+ case "tree":
+ c.tree = plumbing.NewHash(string(split[1]))
+ case "parent":
+ c.parents = append(c.parents, plumbing.NewHash(string(split[1])))
+ case "author":
+ c.Author.Decode(split[1])
+ case "committer":
+ c.Committer.Decode(split[1])
+ }
+ } else {
+ c.Message += string(line)
+ }
+
+ if err == io.EOF {
+ return nil
+ }
+ }
+}
+
+// History return a slice with the previous commits in the history of this commit
+func (c *Commit) History() ([]*Commit, error) {
+ var commits []*Commit
+ err := WalkCommitHistory(c, func(commit *Commit) error {
+ commits = append(commits, commit)
+ return nil
+ })
+
+ ReverseSortCommits(commits)
+ return commits, err
+}
+
+// Encode transforms a Commit into a plumbing.EncodedObject.
+func (b *Commit) Encode(o plumbing.EncodedObject) error {
+ o.SetType(plumbing.CommitObject)
+ w, err := o.Writer()
+ if err != nil {
+ return err
+ }
+ defer ioutil.CheckClose(w, &err)
+ if _, err = fmt.Fprintf(w, "tree %s\n", b.tree.String()); err != nil {
+ return err
+ }
+ for _, parent := range b.parents {
+ if _, err = fmt.Fprintf(w, "parent %s\n", parent.String()); err != nil {
+ return err
+ }
+ }
+ if _, err = fmt.Fprint(w, "author "); err != nil {
+ return err
+ }
+ if err = b.Author.Encode(w); err != nil {
+ return err
+ }
+ if _, err = fmt.Fprint(w, "\ncommitter "); err != nil {
+ return err
+ }
+ if err = b.Committer.Encode(w); err != nil {
+ return err
+ }
+ if _, err = fmt.Fprintf(w, "\n\n%s", b.Message); err != nil {
+ return err
+ }
+ return err
+}
+
+func (c *Commit) String() string {
+ return fmt.Sprintf(
+ "%s %s\nAuthor: %s\nDate: %s\n\n%s\n",
+ plumbing.CommitObject, c.Hash, c.Author.String(),
+ c.Author.When.Format(DateFormat), indent(c.Message),
+ )
+}
+
+func indent(t string) string {
+ var output []string
+ for _, line := range strings.Split(t, "\n") {
+ if len(line) != 0 {
+ line = " " + line
+ }
+
+ output = append(output, line)
+ }
+
+ return strings.Join(output, "\n")
+}
+
+// CommitIter provides an iterator for a set of commits.
+type CommitIter struct {
+ storer.EncodedObjectIter
+ s storer.EncodedObjectStorer
+}
+
+// NewCommitIter returns a CommitIter for the given object storer and underlying
+// object iterator.
+//
+// The returned CommitIter will automatically skip over non-commit objects.
+func NewCommitIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *CommitIter {
+ return &CommitIter{iter, s}
+}
+
+// Next moves the iterator to the next commit and returns a pointer to it. If it
+// has reached the end of the set it will return io.EOF.
+func (iter *CommitIter) Next() (*Commit, error) {
+ obj, err := iter.EncodedObjectIter.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeCommit(iter.s, obj)
+}
+
+// ForEach call the cb function for each commit contained on this iter until
+// an error appends or the end of the iter is reached. If ErrStop is sent
+// the iteration is stop but no error is returned. The iterator is closed.
+func (iter *CommitIter) ForEach(cb func(*Commit) error) error {
+ return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error {
+ c, err := DecodeCommit(iter.s, obj)
+ if err != nil {
+ return err
+ }
+
+ return cb(c)
+ })
+}
+
+type commitSorterer struct {
+ l []*Commit
+}
+
+func (s commitSorterer) Len() int {
+ return len(s.l)
+}
+
+func (s commitSorterer) Less(i, j int) bool {
+ return s.l[i].Committer.When.Before(s.l[j].Committer.When)
+}
+
+func (s commitSorterer) Swap(i, j int) {
+ s.l[i], s.l[j] = s.l[j], s.l[i]
+}
+
+// SortCommits sort a commit list by commit date, from older to newer.
+func SortCommits(l []*Commit) {
+ s := &commitSorterer{l}
+ sort.Sort(s)
+}
+
+// ReverseSortCommits sort a commit list by commit date, from newer to older.
+func ReverseSortCommits(l []*Commit) {
+ s := &commitSorterer{l}
+ sort.Sort(sort.Reverse(s))
+}
diff --git a/plumbing/object/commit_test.go b/plumbing/object/commit_test.go
new file mode 100644
index 0000000..abf92dd
--- /dev/null
+++ b/plumbing/object/commit_test.go
@@ -0,0 +1,169 @@
+package object
+
+import (
+ "io"
+ "time"
+
+ "gopkg.in/src-d/go-git.v4/fixtures"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+
+ . "gopkg.in/check.v1"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
+)
+
+type SuiteCommit struct {
+ BaseObjectsSuite
+ Commit *Commit
+}
+
+var _ = Suite(&SuiteCommit{})
+
+func (s *SuiteCommit) SetUpSuite(c *C) {
+ s.BaseObjectsSuite.SetUpSuite(c)
+
+ hash := plumbing.NewHash("1669dce138d9b841a518c64b10914d88f5e488ea")
+
+ s.Commit = s.commit(c, hash)
+}
+
+func (s *SuiteCommit) TestDecodeNonCommit(c *C) {
+ hash := plumbing.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492")
+ blob, err := s.Storer.EncodedObject(plumbing.AnyObject, hash)
+ c.Assert(err, IsNil)
+
+ commit := &Commit{}
+ err = commit.Decode(blob)
+ c.Assert(err, Equals, ErrUnsupportedObject)
+}
+
+func (s *SuiteCommit) TestType(c *C) {
+ c.Assert(s.Commit.Type(), Equals, plumbing.CommitObject)
+}
+
+func (s *SuiteCommit) TestTree(c *C) {
+ tree, err := s.Commit.Tree()
+ c.Assert(err, IsNil)
+ c.Assert(tree.ID().String(), Equals, "eba74343e2f15d62adedfd8c883ee0262b5c8021")
+}
+
+func (s *SuiteCommit) TestParents(c *C) {
+ expected := []string{
+ "35e85108805c84807bc66a02d91535e1e24b38b9",
+ "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69",
+ }
+
+ var output []string
+ i := s.Commit.Parents()
+ err := i.ForEach(func(commit *Commit) error {
+ output = append(output, commit.ID().String())
+ return nil
+ })
+
+ c.Assert(err, IsNil)
+ c.Assert(output, DeepEquals, expected)
+}
+
+func (s *SuiteCommit) TestCommitEncodeDecodeIdempotent(c *C) {
+ ts, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05-07:00")
+ c.Assert(err, IsNil)
+ commits := []*Commit{
+ {
+ Author: Signature{Name: "Foo", Email: "foo@example.local", When: ts},
+ Committer: Signature{Name: "Bar", Email: "bar@example.local", When: ts},
+ Message: "Message\n\nFoo\nBar\nWith trailing blank lines\n\n",
+ tree: plumbing.NewHash("f000000000000000000000000000000000000001"),
+ parents: []plumbing.Hash{plumbing.NewHash("f000000000000000000000000000000000000002")},
+ },
+ {
+ Author: Signature{Name: "Foo", Email: "foo@example.local", When: ts},
+ Committer: Signature{Name: "Bar", Email: "bar@example.local", When: ts},
+ Message: "Message\n\nFoo\nBar\nWith no trailing blank lines",
+ tree: plumbing.NewHash("0000000000000000000000000000000000000003"),
+ parents: []plumbing.Hash{
+ plumbing.NewHash("f000000000000000000000000000000000000004"),
+ plumbing.NewHash("f000000000000000000000000000000000000005"),
+ plumbing.NewHash("f000000000000000000000000000000000000006"),
+ plumbing.NewHash("f000000000000000000000000000000000000007"),
+ },
+ },
+ }
+ for _, commit := range commits {
+ obj := &plumbing.MemoryObject{}
+ err = commit.Encode(obj)
+ c.Assert(err, IsNil)
+ newCommit := &Commit{}
+ err = newCommit.Decode(obj)
+ c.Assert(err, IsNil)
+ commit.Hash = obj.Hash()
+ c.Assert(newCommit, DeepEquals, commit)
+ }
+}
+
+func (s *SuiteCommit) TestFile(c *C) {
+ file, err := s.Commit.File("CHANGELOG")
+ c.Assert(err, IsNil)
+ c.Assert(file.Name, Equals, "CHANGELOG")
+}
+
+func (s *SuiteCommit) TestNumParents(c *C) {
+ c.Assert(s.Commit.NumParents(), Equals, 2)
+}
+
+func (s *SuiteCommit) TestHistory(c *C) {
+ commits, err := s.Commit.History()
+ c.Assert(err, IsNil)
+ c.Assert(commits, HasLen, 5)
+ c.Assert(commits[0].Hash.String(), Equals, s.Commit.Hash.String())
+ c.Assert(commits[len(commits)-1].Hash.String(), Equals, "b029517f6300c2da0f4b651b8642506cd6aaf45d")
+}
+
+func (s *SuiteCommit) TestString(c *C) {
+ c.Assert(s.Commit.String(), Equals, ""+
+ "commit 1669dce138d9b841a518c64b10914d88f5e488ea\n"+
+ "Author: Máximo Cuadros Ortiz <mcuadros@gmail.com>\n"+
+ "Date: Tue Mar 31 13:48:14 2015 +0200\n"+
+ "\n"+
+ " Merge branch 'master' of github.com:tyba/git-fixture\n"+
+ "\n",
+ )
+}
+
+func (s *SuiteCommit) TestStringMultiLine(c *C) {
+ hash := plumbing.NewHash("e7d896db87294e33ca3202e536d4d9bb16023db3")
+
+ f := fixtures.ByURL("https://github.com/src-d/go-git.git").One()
+ sto, err := filesystem.NewStorage(f.DotGit())
+
+ o, err := sto.EncodedObject(plumbing.CommitObject, hash)
+ c.Assert(err, IsNil)
+ commit, err := DecodeCommit(sto, o)
+ c.Assert(err, IsNil)
+
+ c.Assert(commit.String(), Equals, ""+
+ "commit e7d896db87294e33ca3202e536d4d9bb16023db3\n"+
+ "Author: Alberto Cortés <alberto@sourced.tech>\n"+
+ "Date: Wed Jan 27 11:13:49 2016 +0100\n"+
+ "\n"+
+ " fix zlib invalid header error\n"+
+ "\n"+
+ " The return value of reads to the packfile were being ignored, so zlib\n"+
+ " was getting invalid data on it read buffers.\n"+
+ "\n",
+ )
+}
+
+func (s *SuiteCommit) TestCommitIterNext(c *C) {
+ i := s.Commit.Parents()
+
+ commit, err := i.Next()
+ c.Assert(err, IsNil)
+ c.Assert(commit.ID().String(), Equals, "35e85108805c84807bc66a02d91535e1e24b38b9")
+
+ commit, err = i.Next()
+ c.Assert(err, IsNil)
+ c.Assert(commit.ID().String(), Equals, "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69")
+
+ commit, err = i.Next()
+ c.Assert(err, Equals, io.EOF)
+ c.Assert(commit, IsNil)
+}
diff --git a/plumbing/object/commit_walker.go b/plumbing/object/commit_walker.go
new file mode 100644
index 0000000..dcce6b9
--- /dev/null
+++ b/plumbing/object/commit_walker.go
@@ -0,0 +1,67 @@
+package object
+
+import (
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+)
+
+type commitWalker struct {
+ seen map[plumbing.Hash]bool
+ stack []*CommitIter
+ start *Commit
+ cb func(*Commit) error
+}
+
+// WalkCommitHistory walks the commit history
+func WalkCommitHistory(c *Commit, cb func(*Commit) error) error {
+ w := &commitWalker{
+ seen: make(map[plumbing.Hash]bool),
+ stack: make([]*CommitIter, 0),
+ start: c,
+ cb: cb,
+ }
+
+ return w.walk()
+}
+
+func (w *commitWalker) walk() error {
+ var commit *Commit
+
+ if w.start != nil {
+ commit = w.start
+ w.start = nil
+ } else {
+ current := len(w.stack) - 1
+ if current < 0 {
+ return nil
+ }
+
+ var err error
+ commit, err = w.stack[current].Next()
+ if err == io.EOF {
+ w.stack = w.stack[:current]
+ return w.walk()
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ // check and update seen
+ if w.seen[commit.Hash] {
+ return w.walk()
+ }
+
+ w.seen[commit.Hash] = true
+ if commit.NumParents() > 0 {
+ w.stack = append(w.stack, commit.Parents())
+ }
+
+ if err := w.cb(commit); err != nil {
+ return err
+ }
+
+ return w.walk()
+}
diff --git a/plumbing/object/commit_walker_test.go b/plumbing/object/commit_walker_test.go
new file mode 100644
index 0000000..67d6695
--- /dev/null
+++ b/plumbing/object/commit_walker_test.go
@@ -0,0 +1,34 @@
+package object
+
+import . "gopkg.in/check.v1"
+
+type CommitWalkerSuite struct {
+ BaseObjectsSuite
+}
+
+var _ = Suite(&CommitWalkerSuite{})
+
+func (s *CommitWalkerSuite) TestWalkerNext(c *C) {
+ commit := s.commit(c, s.Fixture.Head)
+
+ var commits []*Commit
+
+ WalkCommitHistory(commit, func(c *Commit) error {
+ commits = append(commits, c)
+ return nil
+ })
+
+ SortCommits(commits)
+ c.Assert(commits, HasLen, 8)
+
+ expected := []string{
+ "b029517f6300c2da0f4b651b8642506cd6aaf45d", "b8e471f58bcbca63b07bda20e428190409c2db47",
+ "35e85108805c84807bc66a02d91535e1e24b38b9", "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69",
+ "1669dce138d9b841a518c64b10914d88f5e488ea", "af2d6a6954d532f8ffb47615169c8fdf9d383a1a",
+ "918c48b83bd081e863dbe1b80f8998f058cd8294", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
+ }
+
+ for i, commit := range commits {
+ c.Assert(commit.Hash.String(), Equals, expected[i])
+ }
+}
diff --git a/plumbing/object/file.go b/plumbing/object/file.go
new file mode 100644
index 0000000..4846f98
--- /dev/null
+++ b/plumbing/object/file.go
@@ -0,0 +1,116 @@
+package object
+
+import (
+ "bytes"
+ "io"
+ "os"
+ "strings"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
+)
+
+// File represents git file objects.
+type File struct {
+ Name string
+ Mode os.FileMode
+ Blob
+}
+
+// NewFile returns a File based on the given blob object
+func NewFile(name string, m os.FileMode, b *Blob) *File {
+ return &File{Name: name, Mode: m, Blob: *b}
+}
+
+// Contents returns the contents of a file as a string.
+func (f *File) Contents() (content string, err error) {
+ reader, err := f.Reader()
+ if err != nil {
+ return "", err
+ }
+ defer ioutil.CheckClose(reader, &err)
+
+ buf := new(bytes.Buffer)
+ if _, err := buf.ReadFrom(reader); err != nil {
+ return "", err
+ }
+
+ return buf.String(), nil
+}
+
+// Lines returns a slice of lines from the contents of a file, stripping
+// all end of line characters. If the last line is empty (does not end
+// in an end of line), it is also stripped.
+func (f *File) Lines() ([]string, error) {
+ content, err := f.Contents()
+ if err != nil {
+ return nil, err
+ }
+
+ splits := strings.Split(content, "\n")
+ // remove the last line if it is empty
+ if splits[len(splits)-1] == "" {
+ return splits[:len(splits)-1], nil
+ }
+
+ return splits, nil
+}
+
+type FileIter struct {
+ s storer.EncodedObjectStorer
+ w TreeWalker
+}
+
+func NewFileIter(s storer.EncodedObjectStorer, t *Tree) *FileIter {
+ return &FileIter{s: s, w: *NewTreeWalker(t, true)}
+}
+
+func (iter *FileIter) Next() (*File, error) {
+ for {
+ name, entry, err := iter.w.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ if entry.Mode.IsDir() {
+ continue
+ }
+
+ blob, err := GetBlob(iter.s, entry.Hash)
+ if err != nil {
+ return nil, err
+ }
+
+ return NewFile(name, entry.Mode, blob), nil
+ }
+}
+
+// ForEach call the cb function for each file contained on this iter until
+// an error happends or the end of the iter is reached. If plumbing.ErrStop is sent
+// the iteration is stop but no error is returned. The iterator is closed.
+func (iter *FileIter) ForEach(cb func(*File) error) error {
+ defer iter.Close()
+
+ for {
+ f, err := iter.Next()
+ if err != nil {
+ if err == io.EOF {
+ return nil
+ }
+
+ return err
+ }
+
+ if err := cb(f); err != nil {
+ if err == storer.ErrStop {
+ return nil
+ }
+
+ return err
+ }
+ }
+}
+
+func (iter *FileIter) Close() {
+ iter.w.Close()
+}
diff --git a/plumbing/object/file_test.go b/plumbing/object/file_test.go
new file mode 100644
index 0000000..f734455
--- /dev/null
+++ b/plumbing/object/file_test.go
@@ -0,0 +1,249 @@
+package object
+
+import (
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/fixtures"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
+
+ . "gopkg.in/check.v1"
+)
+
+type FileSuite struct {
+ BaseObjectsSuite
+}
+
+var _ = Suite(&FileSuite{})
+
+type fileIterExpectedEntry struct {
+ Name string
+ Hash string
+}
+
+var fileIterTests = []struct {
+ repo string // the repo name as in localRepos
+ commit string // the commit to search for the file
+ files []fileIterExpectedEntry
+}{
+ {"https://github.com/git-fixtures/basic.git", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", []fileIterExpectedEntry{
+ {".gitignore", "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88"},
+ {"CHANGELOG", "d3ff53e0564a9f87d8e84b6e28e5060e517008aa"},
+ {"LICENSE", "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f"},
+ {"binary.jpg", "d5c0f4ab811897cadf03aec358ae60d21f91c50d"},
+ {"go/example.go", "880cd14280f4b9b6ed3986d6671f907d7cc2a198"},
+ {"json/long.json", "49c6bb89b17060d7b4deacb7b338fcc6ea2352a9"},
+ {"json/short.json", "c8f1d8c61f9da76f4cb49fd86322b6e685dba956"},
+ {"php/crappy.php", "9a48f23120e880dfbe41f7c9b7b708e9ee62a492"},
+ {"vendor/foo.go", "9dea2395f5403188298c1dabe8bdafe562c491e3"},
+ }},
+}
+
+func (s *FileSuite) TestIter(c *C) {
+ for i, t := range fileIterTests {
+ f := fixtures.ByURL(t.repo).One()
+ sto, err := filesystem.NewStorage(f.DotGit())
+ c.Assert(err, IsNil)
+
+ h := plumbing.NewHash(t.commit)
+ commit, err := GetCommit(sto, h)
+ c.Assert(err, IsNil, Commentf("subtest %d: %v (%s)", i, err, t.commit))
+
+ tree, err := commit.Tree()
+ c.Assert(err, IsNil)
+
+ iter := NewFileIter(sto, tree)
+ for k := 0; k < len(t.files); k++ {
+ exp := t.files[k]
+ file, err := iter.Next()
+ c.Assert(err, IsNil, Commentf("subtest %d, iter %d, err=%v", i, k, err))
+ c.Assert(file.Mode.String(), Equals, "-rw-r--r--")
+ c.Assert(file.Hash.IsZero(), Equals, false)
+ c.Assert(file.Hash, Equals, file.ID())
+ c.Assert(file.Name, Equals, exp.Name, Commentf("subtest %d, iter %d, name=%s, expected=%s", i, k, file.Name, exp.Hash))
+ c.Assert(file.Hash.String(), Equals, exp.Hash, Commentf("subtest %d, iter %d, hash=%v, expected=%s", i, k, file.Hash.String(), exp.Hash))
+ }
+ _, err = iter.Next()
+ c.Assert(err, Equals, io.EOF)
+ }
+}
+
+var contentsTests = []struct {
+ repo string // the repo name as in localRepos
+ commit string // the commit to search for the file
+ path string // the path of the file to find
+ contents string // expected contents of the file
+}{
+ {
+ "https://github.com/git-fixtures/basic.git",
+ "b029517f6300c2da0f4b651b8642506cd6aaf45d",
+ ".gitignore",
+ `*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+`,
+ },
+ {
+ "https://github.com/git-fixtures/basic.git",
+ "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
+ "CHANGELOG",
+ `Initial changelog
+`,
+ },
+}
+
+func (s *FileSuite) TestContents(c *C) {
+ for i, t := range contentsTests {
+ f := fixtures.ByURL(t.repo).One()
+ sto, err := filesystem.NewStorage(f.DotGit())
+ c.Assert(err, IsNil)
+
+ h := plumbing.NewHash(t.commit)
+ commit, err := GetCommit(sto, h)
+ c.Assert(err, IsNil, Commentf("subtest %d: %v (%s)", i, err, t.commit))
+
+ file, err := commit.File(t.path)
+ c.Assert(err, IsNil)
+ content, err := file.Contents()
+ c.Assert(err, IsNil)
+ c.Assert(content, Equals, t.contents, Commentf(
+ "subtest %d: commit=%s, path=%s", i, t.commit, t.path))
+ }
+}
+
+var linesTests = []struct {
+ repo string // the repo name as in localRepos
+ commit string // the commit to search for the file
+ path string // the path of the file to find
+ lines []string // expected lines in the file
+}{
+ {
+ "https://github.com/git-fixtures/basic.git",
+ "b029517f6300c2da0f4b651b8642506cd6aaf45d",
+ ".gitignore",
+ []string{
+ "*.class",
+ "",
+ "# Mobile Tools for Java (J2ME)",
+ ".mtj.tmp/",
+ "",
+ "# Package Files #",
+ "*.jar",
+ "*.war",
+ "*.ear",
+ "",
+ "# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml",
+ "hs_err_pid*",
+ },
+ },
+ {
+ "https://github.com/git-fixtures/basic.git",
+ "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
+ "CHANGELOG",
+ []string{
+ "Initial changelog",
+ },
+ },
+}
+
+func (s *FileSuite) TestLines(c *C) {
+ for i, t := range linesTests {
+ f := fixtures.ByURL(t.repo).One()
+ sto, err := filesystem.NewStorage(f.DotGit())
+ c.Assert(err, IsNil)
+
+ h := plumbing.NewHash(t.commit)
+ commit, err := GetCommit(sto, h)
+ c.Assert(err, IsNil, Commentf("subtest %d: %v (%s)", i, err, t.commit))
+
+ file, err := commit.File(t.path)
+ c.Assert(err, IsNil)
+ lines, err := file.Lines()
+ c.Assert(err, IsNil)
+ c.Assert(lines, DeepEquals, t.lines, Commentf(
+ "subtest %d: commit=%s, path=%s", i, t.commit, t.path))
+ }
+}
+
+var ignoreEmptyDirEntriesTests = []struct {
+ repo string // the repo name as in localRepos
+ commit string // the commit to search for the file
+}{
+ {
+ "https://github.com/cpcs499/Final_Pres_P.git",
+ "70bade703ce556c2c7391a8065c45c943e8b6bc3",
+ // the Final dir in this commit is empty
+ },
+}
+
+// It is difficult to assert that we are ignoring an (empty) dir as even
+// if we don't, no files will be found in it.
+//
+// At least this test has a high chance of panicking if
+// we don't ignore empty dirs.
+func (s *FileSuite) TestIgnoreEmptyDirEntries(c *C) {
+ for i, t := range ignoreEmptyDirEntriesTests {
+ f := fixtures.ByURL(t.repo).One()
+ sto, err := filesystem.NewStorage(f.DotGit())
+ c.Assert(err, IsNil)
+
+ h := plumbing.NewHash(t.commit)
+ commit, err := GetCommit(sto, h)
+ c.Assert(err, IsNil, Commentf("subtest %d: %v (%s)", i, err, t.commit))
+
+ tree, err := commit.Tree()
+ c.Assert(err, IsNil)
+
+ iter := tree.Files()
+ defer iter.Close()
+ for file, err := iter.Next(); err == nil; file, err = iter.Next() {
+ _, _ = file.Contents()
+ // this would probably panic if we are not ignoring empty dirs
+ }
+ }
+}
+
+func (s *FileSuite) TestFileIter(c *C) {
+ hash := plumbing.NewHash("1669dce138d9b841a518c64b10914d88f5e488ea")
+ commit, err := GetCommit(s.Storer, hash)
+ c.Assert(err, IsNil)
+
+ tree, err := commit.Tree()
+ c.Assert(err, IsNil)
+
+ expected := []string{
+ ".gitignore",
+ "CHANGELOG",
+ "LICENSE",
+ "binary.jpg",
+ }
+
+ var count int
+ i := tree.Files()
+ i.ForEach(func(f *File) error {
+ c.Assert(f.Name, Equals, expected[count])
+ count++
+ return nil
+ })
+
+ c.Assert(count, Equals, 4)
+
+ count = 0
+ i = tree.Files()
+ i.ForEach(func(f *File) error {
+ count++
+ return storer.ErrStop
+ })
+
+ c.Assert(count, Equals, 1)
+}
diff --git a/plumbing/object/object.go b/plumbing/object/object.go
new file mode 100644
index 0000000..8bdbb2a
--- /dev/null
+++ b/plumbing/object/object.go
@@ -0,0 +1,217 @@
+package object
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "time"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+)
+
+// ErrUnsupportedObject trigger when a non-supported object is being decoded.
+var ErrUnsupportedObject = errors.New("unsupported object type")
+
+// Object is a generic representation of any git object. It is implemented by
+// Commit, Tree, Blob and Tag, and includes the functions that are common to
+// them.
+//
+// Object is returned when an object could of any type. It is frequently used
+// with a type cast to acquire the specific type of object:
+//
+// func process(obj Object) {
+// switch o := obj.(type) {
+// case *Commit:
+// // o is a Commit
+// case *Tree:
+// // o is a Tree
+// case *Blob:
+// // o is a Blob
+// case *Tag:
+// // o is a Tag
+// }
+// }
+//
+// This interface is intentionally different from plumbing.EncodedObject, which is a lower
+// level interface used by storage implementations to read and write objects.
+type Object interface {
+ ID() plumbing.Hash
+ Type() plumbing.ObjectType
+ Decode(plumbing.EncodedObject) error
+ Encode(plumbing.EncodedObject) error
+}
+
+// GetObject gets an object from an object storer and decodes it.
+func GetObject(s storer.EncodedObjectStorer, h plumbing.Hash) (Object, error) {
+ o, err := s.EncodedObject(plumbing.AnyObject, h)
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeObject(s, o)
+}
+
+// DecodeObject decodes an encoded object into an Object and associates it to
+// the given object storer.
+func DecodeObject(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (Object, error) {
+ switch o.Type() {
+ case plumbing.CommitObject:
+ return DecodeCommit(s, o)
+ case plumbing.TreeObject:
+ return DecodeTree(s, o)
+ case plumbing.BlobObject:
+ return DecodeBlob(o)
+ case plumbing.TagObject:
+ return DecodeTag(s, o)
+ default:
+ return nil, plumbing.ErrInvalidType
+ }
+}
+
+// DateFormat is the format being use in the orignal git implementation
+const DateFormat = "Mon Jan 02 15:04:05 2006 -0700"
+
+// Signature represents an action signed by a person
+type Signature struct {
+ Name string
+ Email string
+ When time.Time
+}
+
+// Decode decodes a byte slice into a signature
+func (s *Signature) Decode(b []byte) {
+ open := bytes.IndexByte(b, '<')
+ close := bytes.IndexByte(b, '>')
+ if open == -1 || close == -1 {
+ return
+ }
+
+ s.Name = string(bytes.Trim(b[:open], " "))
+ s.Email = string(b[open+1 : close])
+
+ hasTime := close+2 < len(b)
+ if hasTime {
+ s.decodeTimeAndTimeZone(b[close+2:])
+ }
+}
+
+// Encode encodes a Signature into a writer.
+func (s *Signature) Encode(w io.Writer) error {
+ if _, err := fmt.Fprintf(w, "%s <%s> ", s.Name, s.Email); err != nil {
+ return err
+ }
+ if err := s.encodeTimeAndTimeZone(w); err != nil {
+ return err
+ }
+ return nil
+}
+
+var timeZoneLength = 5
+
+func (s *Signature) decodeTimeAndTimeZone(b []byte) {
+ space := bytes.IndexByte(b, ' ')
+ if space == -1 {
+ space = len(b)
+ }
+
+ ts, err := strconv.ParseInt(string(b[:space]), 10, 64)
+ if err != nil {
+ return
+ }
+
+ s.When = time.Unix(ts, 0).In(time.UTC)
+ var tzStart = space + 1
+ if tzStart >= len(b) || tzStart+timeZoneLength > len(b) {
+ return
+ }
+
+ tl, err := time.Parse("-0700", string(b[tzStart:tzStart+timeZoneLength]))
+ if err != nil {
+ return
+ }
+
+ s.When = s.When.In(tl.Location())
+}
+
+func (s *Signature) encodeTimeAndTimeZone(w io.Writer) error {
+ _, err := fmt.Fprintf(w, "%d %s", s.When.Unix(), s.When.Format("-0700"))
+ return err
+}
+
+func (s *Signature) String() string {
+ return fmt.Sprintf("%s <%s>", s.Name, s.Email)
+}
+
+// ObjectIter provides an iterator for a set of objects.
+type ObjectIter struct {
+ storer.EncodedObjectIter
+ s storer.EncodedObjectStorer
+}
+
+// NewObjectIter returns a ObjectIter for the given repository and underlying
+// object iterator.
+func NewObjectIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *ObjectIter {
+ return &ObjectIter{iter, s}
+}
+
+// Next moves the iterator to the next object and returns a pointer to it. If it
+// has reached the end of the set it will return io.EOF.
+func (iter *ObjectIter) Next() (Object, error) {
+ for {
+ obj, err := iter.EncodedObjectIter.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ o, err := iter.toObject(obj)
+ if err == plumbing.ErrInvalidType {
+ continue
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ return o, nil
+ }
+}
+
+// ForEach call the cb function for each object contained on this iter until
+// an error happens or the end of the iter is reached. If ErrStop is sent
+// the iteration is stop but no error is returned. The iterator is closed.
+func (iter *ObjectIter) ForEach(cb func(Object) error) error {
+ return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error {
+ o, err := iter.toObject(obj)
+ if err == plumbing.ErrInvalidType {
+ return nil
+ }
+
+ if err != nil {
+ return err
+ }
+
+ return cb(o)
+ })
+}
+
+func (iter *ObjectIter) toObject(obj plumbing.EncodedObject) (Object, error) {
+ switch obj.Type() {
+ case plumbing.BlobObject:
+ blob := &Blob{}
+ return blob, blob.Decode(obj)
+ case plumbing.TreeObject:
+ tree := &Tree{s: iter.s}
+ return tree, tree.Decode(obj)
+ case plumbing.CommitObject:
+ commit := &Commit{}
+ return commit, commit.Decode(obj)
+ case plumbing.TagObject:
+ tag := &Tag{}
+ return tag, tag.Decode(obj)
+ default:
+ return nil, plumbing.ErrInvalidType
+ }
+}
diff --git a/plumbing/object/object_test.go b/plumbing/object/object_test.go
new file mode 100644
index 0000000..04f2b73
--- /dev/null
+++ b/plumbing/object/object_test.go
@@ -0,0 +1,199 @@
+package object
+
+import (
+ "io"
+ "io/ioutil"
+ "testing"
+ "time"
+
+ "gopkg.in/src-d/go-git.v4/fixtures"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
+
+ . "gopkg.in/check.v1"
+)
+
+func Test(t *testing.T) { TestingT(t) }
+
+type BaseObjectsSuite struct {
+ fixtures.Suite
+ Storer storer.EncodedObjectStorer
+ Fixture *fixtures.Fixture
+}
+
+func (s *BaseObjectsSuite) SetUpSuite(c *C) {
+ s.Suite.SetUpSuite(c)
+ s.Fixture = fixtures.Basic().One()
+ storer, err := filesystem.NewStorage(s.Fixture.DotGit())
+ c.Assert(err, IsNil)
+ s.Storer = storer
+}
+
+func (s *BaseObjectsSuite) tag(c *C, h plumbing.Hash) *Tag {
+ t, err := GetTag(s.Storer, h)
+ c.Assert(err, IsNil)
+ return t
+}
+
+func (s *BaseObjectsSuite) tree(c *C, h plumbing.Hash) *Tree {
+ t, err := GetTree(s.Storer, h)
+ c.Assert(err, IsNil)
+ return t
+}
+
+func (s *BaseObjectsSuite) commit(c *C, h plumbing.Hash) *Commit {
+ commit, err := GetCommit(s.Storer, h)
+ c.Assert(err, IsNil)
+ return commit
+}
+
+type ObjectsSuite struct {
+ BaseObjectsSuite
+}
+
+var _ = Suite(&ObjectsSuite{})
+
+func (s *ObjectsSuite) TestNewCommit(c *C) {
+ hash := plumbing.NewHash("a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69")
+ commit := s.commit(c, hash)
+
+ c.Assert(commit.Hash, Equals, commit.ID())
+ c.Assert(commit.Hash.String(), Equals, "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69")
+
+ tree, err := commit.Tree()
+ c.Assert(err, IsNil)
+ c.Assert(tree.Hash.String(), Equals, "c2d30fa8ef288618f65f6eed6e168e0d514886f4")
+
+ parents := commit.Parents()
+ parentCommit, err := parents.Next()
+ c.Assert(err, IsNil)
+ c.Assert(parentCommit.Hash.String(), Equals, "b029517f6300c2da0f4b651b8642506cd6aaf45d")
+
+ parentCommit, err = parents.Next()
+ c.Assert(err, IsNil)
+ c.Assert(parentCommit.Hash.String(), Equals, "b8e471f58bcbca63b07bda20e428190409c2db47")
+
+ c.Assert(commit.Author.Email, Equals, "mcuadros@gmail.com")
+ c.Assert(commit.Author.Name, Equals, "Máximo Cuadros")
+ c.Assert(commit.Author.When.Format(time.RFC3339), Equals, "2015-03-31T13:47:14+02:00")
+ c.Assert(commit.Committer.Email, Equals, "mcuadros@gmail.com")
+ c.Assert(commit.Message, Equals, "Merge pull request #1 from dripolles/feature\n\nCreating changelog")
+}
+
+func (s *ObjectsSuite) TestParseTree(c *C) {
+ hash := plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c")
+ tree, err := GetTree(s.Storer, hash)
+ c.Assert(err, IsNil)
+
+ c.Assert(tree.Entries, HasLen, 8)
+
+ tree.buildMap()
+ c.Assert(tree.m, HasLen, 8)
+ c.Assert(tree.m[".gitignore"].Name, Equals, ".gitignore")
+ c.Assert(tree.m[".gitignore"].Mode.String(), Equals, "-rw-r--r--")
+ c.Assert(tree.m[".gitignore"].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
+
+ count := 0
+ iter := tree.Files()
+ defer iter.Close()
+ for f, err := iter.Next(); err == nil; f, err = iter.Next() {
+ count++
+ if f.Name == "go/example.go" {
+ reader, err := f.Reader()
+ c.Assert(err, IsNil)
+ defer func() { c.Assert(reader.Close(), IsNil) }()
+ content, _ := ioutil.ReadAll(reader)
+ c.Assert(content, HasLen, 2780)
+ }
+ }
+
+ c.Assert(count, Equals, 9)
+}
+
+func (s *ObjectsSuite) TestParseSignature(c *C) {
+ cases := map[string]Signature{
+ `Foo Bar <foo@bar.com> 1257894000 +0100`: {
+ Name: "Foo Bar",
+ Email: "foo@bar.com",
+ When: MustParseTime("2009-11-11 00:00:00 +0100"),
+ },
+ `Foo Bar <foo@bar.com> 1257894000 -0700`: {
+ Name: "Foo Bar",
+ Email: "foo@bar.com",
+ When: MustParseTime("2009-11-10 16:00:00 -0700"),
+ },
+ `Foo Bar <> 1257894000 +0100`: {
+ Name: "Foo Bar",
+ Email: "",
+ When: MustParseTime("2009-11-11 00:00:00 +0100"),
+ },
+ ` <> 1257894000`: {
+ Name: "",
+ Email: "",
+ When: MustParseTime("2009-11-10 23:00:00 +0000"),
+ },
+ `Foo Bar <foo@bar.com>`: {
+ Name: "Foo Bar",
+ Email: "foo@bar.com",
+ When: time.Time{},
+ },
+ ``: {
+ Name: "",
+ Email: "",
+ When: time.Time{},
+ },
+ `<`: {
+ Name: "",
+ Email: "",
+ When: time.Time{},
+ },
+ }
+
+ for raw, exp := range cases {
+ got := &Signature{}
+ got.Decode([]byte(raw))
+
+ c.Assert(got.Name, Equals, exp.Name)
+ c.Assert(got.Email, Equals, exp.Email)
+ c.Assert(got.When.Format(time.RFC3339), Equals, exp.When.Format(time.RFC3339))
+ }
+}
+
+func (s *ObjectsSuite) TestObjectIter(c *C) {
+ encIter, err := s.Storer.IterEncodedObjects(plumbing.AnyObject)
+ c.Assert(err, IsNil)
+ iter := NewObjectIter(s.Storer, encIter)
+
+ objects := []Object{}
+ iter.ForEach(func(o Object) error {
+ objects = append(objects, o)
+ return nil
+ })
+
+ c.Assert(len(objects) > 0, Equals, true)
+ iter.Close()
+
+ encIter, err = s.Storer.IterEncodedObjects(plumbing.AnyObject)
+ c.Assert(err, IsNil)
+ iter = NewObjectIter(s.Storer, encIter)
+
+ i := 0
+ for {
+ o, err := iter.Next()
+ if err == io.EOF {
+ break
+ }
+
+ c.Assert(err, IsNil)
+ c.Assert(o, DeepEquals, objects[i])
+ i += 1
+ }
+
+ iter.Close()
+}
+
+func MustParseTime(value string) time.Time {
+ t, _ := time.Parse("2006-01-02 15:04:05 -0700", value)
+ return t
+}
diff --git a/plumbing/object/tag.go b/plumbing/object/tag.go
new file mode 100644
index 0000000..5ca363d
--- /dev/null
+++ b/plumbing/object/tag.go
@@ -0,0 +1,268 @@
+package object
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ stdioutil "io/ioutil"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
+)
+
+// Tag represents an annotated tag object. It points to a single git object of
+// any type, but tags typically are applied to commit or blob objects. It
+// provides a reference that associates the target with a tag name. It also
+// contains meta-information about the tag, including the tagger, tag date and
+// message.
+//
+// https://git-scm.com/book/en/v2/Git-Internals-Git-References#Tags
+type Tag struct {
+ Hash plumbing.Hash
+ Name string
+ Tagger Signature
+ Message string
+ TargetType plumbing.ObjectType
+ Target plumbing.Hash
+
+ s storer.EncodedObjectStorer
+}
+
+// GetTag gets a tag from an object storer and decodes it.
+func GetTag(s storer.EncodedObjectStorer, h plumbing.Hash) (*Tag, error) {
+ o, err := s.EncodedObject(plumbing.TagObject, h)
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeTag(s, o)
+}
+
+// DecodeTag decodes an encoded object into a *Commit and associates it to the
+// given object storer.
+func DecodeTag(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (*Tag, error) {
+ t := &Tag{s: s}
+ if err := t.Decode(o); err != nil {
+ return nil, err
+ }
+
+ return t, nil
+}
+
+// ID returns the object ID of the tag, not the object that the tag references.
+// The returned value will always match the current value of Tag.Hash.
+//
+// ID is present to fulfill the Object interface.
+func (t *Tag) ID() plumbing.Hash {
+ return t.Hash
+}
+
+// Type returns the type of object. It always returns plumbing.TagObject.
+//
+// Type is present to fulfill the Object interface.
+func (t *Tag) Type() plumbing.ObjectType {
+ return plumbing.TagObject
+}
+
+// Decode transforms a plumbing.EncodedObject into a Tag struct.
+func (t *Tag) Decode(o plumbing.EncodedObject) (err error) {
+ if o.Type() != plumbing.TagObject {
+ return ErrUnsupportedObject
+ }
+
+ t.Hash = o.Hash()
+
+ reader, err := o.Reader()
+ if err != nil {
+ return err
+ }
+ defer ioutil.CheckClose(reader, &err)
+
+ r := bufio.NewReader(reader)
+ for {
+ line, err := r.ReadSlice('\n')
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ line = bytes.TrimSpace(line)
+ if len(line) == 0 {
+ break // Start of message
+ }
+
+ split := bytes.SplitN(line, []byte{' '}, 2)
+ switch string(split[0]) {
+ case "object":
+ t.Target = plumbing.NewHash(string(split[1]))
+ case "type":
+ t.TargetType, err = plumbing.ParseObjectType(string(split[1]))
+ if err != nil {
+ return err
+ }
+ case "tag":
+ t.Name = string(split[1])
+ case "tagger":
+ t.Tagger.Decode(split[1])
+ }
+
+ if err == io.EOF {
+ return nil
+ }
+ }
+
+ data, err := stdioutil.ReadAll(r)
+ if err != nil {
+ return err
+ }
+ t.Message = string(data)
+
+ return nil
+}
+
+// Encode transforms a Tag into a plumbing.EncodedObject.
+func (t *Tag) Encode(o plumbing.EncodedObject) error {
+ o.SetType(plumbing.TagObject)
+ w, err := o.Writer()
+ if err != nil {
+ return err
+ }
+ defer ioutil.CheckClose(w, &err)
+
+ if _, err = fmt.Fprintf(w,
+ "object %s\ntype %s\ntag %s\ntagger ",
+ t.Target.String(), t.TargetType.Bytes(), t.Name); err != nil {
+ return err
+ }
+
+ if err = t.Tagger.Encode(w); err != nil {
+ return err
+ }
+
+ if _, err = fmt.Fprint(w, "\n\n"); err != nil {
+ return err
+ }
+
+ if _, err = fmt.Fprint(w, t.Message); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// Commit returns the commit pointed to by the tag. If the tag points to a
+// different type of object ErrUnsupportedObject will be returned.
+func (t *Tag) Commit() (*Commit, error) {
+ if t.TargetType != plumbing.CommitObject {
+ return nil, ErrUnsupportedObject
+ }
+
+ o, err := t.s.EncodedObject(plumbing.CommitObject, t.Target)
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeCommit(t.s, o)
+}
+
+// Tree returns the tree pointed to by the tag. If the tag points to a commit
+// object the tree of that commit will be returned. If the tag does not point
+// to a commit or tree object ErrUnsupportedObject will be returned.
+func (t *Tag) Tree() (*Tree, error) {
+ switch t.TargetType {
+ case plumbing.CommitObject:
+ c, err := t.Commit()
+ if err != nil {
+ return nil, err
+ }
+
+ return c.Tree()
+ case plumbing.TreeObject:
+ return GetTree(t.s, t.Target)
+ default:
+ return nil, ErrUnsupportedObject
+ }
+}
+
+// Blob returns the blob pointed to by the tag. If the tag points to a
+// different type of object ErrUnsupportedObject will be returned.
+func (t *Tag) Blob() (*Blob, error) {
+ if t.TargetType != plumbing.BlobObject {
+ return nil, ErrUnsupportedObject
+ }
+
+ return GetBlob(t.s, t.Target)
+}
+
+// Object returns the object pointed to by the tag.
+func (t *Tag) Object() (Object, error) {
+ o, err := t.s.EncodedObject(t.TargetType, t.Target)
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeObject(t.s, o)
+}
+
+// String returns the meta information contained in the tag as a formatted
+// string.
+func (t *Tag) String() string {
+ obj, _ := t.Object()
+
+ return fmt.Sprintf(
+ "%s %s\nTagger: %s\nDate: %s\n\n%s\n%s",
+ plumbing.TagObject, t.Name, t.Tagger.String(), t.Tagger.When.Format(DateFormat),
+ t.Message, objectAsString(obj),
+ )
+}
+
+// TagIter provides an iterator for a set of tags.
+type TagIter struct {
+ storer.EncodedObjectIter
+ s storer.EncodedObjectStorer
+}
+
+// NewTagIter returns a TagIter for the given object storer and underlying
+// object iterator.
+//
+// The returned TagIter will automatically skip over non-tag objects.
+func NewTagIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *TagIter {
+ return &TagIter{iter, s}
+}
+
+// Next moves the iterator to the next tag and returns a pointer to it. If it
+// has reached the end of the set it will return io.EOF.
+func (iter *TagIter) Next() (*Tag, error) {
+ obj, err := iter.EncodedObjectIter.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeTag(iter.s, obj)
+}
+
+// ForEach call the cb function for each tag contained on this iter until
+// an error happends or the end of the iter is reached. If ErrStop is sent
+// the iteration is stop but no error is returned. The iterator is closed.
+func (iter *TagIter) ForEach(cb func(*Tag) error) error {
+ return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error {
+ t, err := DecodeTag(iter.s, obj)
+ if err != nil {
+ return err
+ }
+
+ return cb(t)
+ })
+}
+
+func objectAsString(obj Object) string {
+ switch o := obj.(type) {
+ case *Commit:
+ return o.String()
+ case *Tag:
+ return o.String()
+ }
+
+ return ""
+}
diff --git a/plumbing/object/tag_test.go b/plumbing/object/tag_test.go
new file mode 100644
index 0000000..2721442
--- /dev/null
+++ b/plumbing/object/tag_test.go
@@ -0,0 +1,169 @@
+package object
+
+import (
+ "fmt"
+ "time"
+
+ "gopkg.in/src-d/go-git.v4/fixtures"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
+
+ . "gopkg.in/check.v1"
+)
+
+type TagSuite struct {
+ BaseObjectsSuite
+}
+
+var _ = Suite(&TagSuite{})
+
+func (s *TagSuite) SetUpSuite(c *C) {
+ s.BaseObjectsSuite.SetUpSuite(c)
+ storer, err := filesystem.NewStorage(fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit())
+ c.Assert(err, IsNil)
+ s.Storer = storer
+}
+
+func (s *TagSuite) TestName(c *C) {
+ tag := s.tag(c, plumbing.NewHash("b742a2a9fa0afcfa9a6fad080980fbc26b007c69"))
+ c.Assert(tag.Name, Equals, "annotated-tag")
+}
+
+func (s *TagSuite) TestTagger(c *C) {
+ tag := s.tag(c, plumbing.NewHash("b742a2a9fa0afcfa9a6fad080980fbc26b007c69"))
+ c.Assert(tag.Tagger.String(), Equals, "Máximo Cuadros <mcuadros@gmail.com>")
+}
+
+func (s *TagSuite) TestAnnotated(c *C) {
+ tag := s.tag(c, plumbing.NewHash("b742a2a9fa0afcfa9a6fad080980fbc26b007c69"))
+ c.Assert(tag.Message, Equals, "example annotated tag\n")
+
+ commit, err := tag.Commit()
+ c.Assert(err, IsNil)
+ c.Assert(commit.Type(), Equals, plumbing.CommitObject)
+ c.Assert(commit.ID().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f")
+}
+
+func (s *TagSuite) TestCommit(c *C) {
+ tag := s.tag(c, plumbing.NewHash("ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"))
+ c.Assert(tag.Message, Equals, "a tagged commit\n")
+
+ commit, err := tag.Commit()
+ c.Assert(err, IsNil)
+ c.Assert(commit.Type(), Equals, plumbing.CommitObject)
+ c.Assert(commit.ID().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f")
+}
+
+func (s *TagSuite) TestBlob(c *C) {
+ tag := s.tag(c, plumbing.NewHash("fe6cb94756faa81e5ed9240f9191b833db5f40ae"))
+ c.Assert(tag.Message, Equals, "a tagged blob\n")
+
+ blob, err := tag.Blob()
+ c.Assert(err, IsNil)
+ c.Assert(blob.Type(), Equals, plumbing.BlobObject)
+ c.Assert(blob.ID().String(), Equals, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")
+}
+
+func (s *TagSuite) TestTree(c *C) {
+ tag := s.tag(c, plumbing.NewHash("152175bf7e5580299fa1f0ba41ef6474cc043b70"))
+ c.Assert(tag.Message, Equals, "a tagged tree\n")
+
+ tree, err := tag.Tree()
+ c.Assert(err, IsNil)
+ c.Assert(tree.Type(), Equals, plumbing.TreeObject)
+ c.Assert(tree.ID().String(), Equals, "70846e9a10ef7b41064b40f07713d5b8b9a8fc73")
+}
+
+func (s *TagSuite) TestTreeFromCommit(c *C) {
+ tag := s.tag(c, plumbing.NewHash("ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"))
+ c.Assert(tag.Message, Equals, "a tagged commit\n")
+
+ tree, err := tag.Tree()
+ c.Assert(err, IsNil)
+ c.Assert(tree.Type(), Equals, plumbing.TreeObject)
+ c.Assert(tree.ID().String(), Equals, "70846e9a10ef7b41064b40f07713d5b8b9a8fc73")
+}
+
+func (s *TagSuite) TestObject(c *C) {
+ tag := s.tag(c, plumbing.NewHash("ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"))
+
+ obj, err := tag.Object()
+ c.Assert(err, IsNil)
+ c.Assert(obj.Type(), Equals, plumbing.CommitObject)
+ c.Assert(obj.ID().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f")
+}
+
+func (s *TagSuite) TestTagItter(c *C) {
+ iter, err := s.Storer.IterEncodedObjects(plumbing.TagObject)
+ c.Assert(err, IsNil)
+
+ var count int
+ i := NewTagIter(s.Storer, iter)
+ err = i.ForEach(func(t *Tag) error {
+ count++
+ return nil
+ })
+
+ c.Assert(err, IsNil)
+ c.Assert(count, Equals, 4)
+}
+
+func (s *TagSuite) TestTagIterError(c *C) {
+ iter, err := s.Storer.IterEncodedObjects(plumbing.TagObject)
+ c.Assert(err, IsNil)
+
+ i := NewTagIter(s.Storer, iter)
+ err = i.ForEach(func(t *Tag) error {
+ return fmt.Errorf("a random error")
+ })
+
+ c.Assert(err, NotNil)
+}
+
+func (s *TagSuite) TestTagEncodeDecodeIdempotent(c *C) {
+ ts, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05-07:00")
+ c.Assert(err, IsNil)
+ tags := []*Tag{
+ {
+ Name: "foo",
+ Tagger: Signature{Name: "Foo", Email: "foo@example.local", When: ts},
+ Message: "Message\n\nFoo\nBar\nBaz\n\n",
+ TargetType: plumbing.BlobObject,
+ Target: plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
+ },
+ {
+ Name: "foo",
+ Tagger: Signature{Name: "Foo", Email: "foo@example.local", When: ts},
+ TargetType: plumbing.BlobObject,
+ Target: plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
+ },
+ }
+ for _, tag := range tags {
+ obj := &plumbing.MemoryObject{}
+ err = tag.Encode(obj)
+ c.Assert(err, IsNil)
+ newTag := &Tag{}
+ err = newTag.Decode(obj)
+ c.Assert(err, IsNil)
+ tag.Hash = obj.Hash()
+ c.Assert(newTag, DeepEquals, tag)
+ }
+}
+
+func (s *TagSuite) TestString(c *C) {
+ tag := s.tag(c, plumbing.NewHash("b742a2a9fa0afcfa9a6fad080980fbc26b007c69"))
+ c.Assert(tag.String(), Equals, ""+
+ "tag annotated-tag\n"+
+ "Tagger: Máximo Cuadros <mcuadros@gmail.com>\n"+
+ "Date: Wed Sep 21 21:13:35 2016 +0200\n"+
+ "\n"+
+ "example annotated tag\n"+
+ "\n"+
+ "commit f7b877701fbf855b44c0a9e86f3fdce2c298b07f\n"+
+ "Author: Máximo Cuadros <mcuadros@gmail.com>\n"+
+ "Date: Wed Sep 21 21:10:52 2016 +0200\n"+
+ "\n"+
+ " initial\n"+
+ "\n",
+ )
+}
diff --git a/plumbing/object/tree.go b/plumbing/object/tree.go
new file mode 100644
index 0000000..7a8c1a3
--- /dev/null
+++ b/plumbing/object/tree.go
@@ -0,0 +1,449 @@
+package object
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "strconv"
+ "strings"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
+)
+
+const (
+ maxTreeDepth = 1024
+ startingStackSize = 8
+ submoduleMode = 0160000
+ directoryMode = 0040000
+)
+
+// New errors defined by this package.
+var (
+ ErrMaxTreeDepth = errors.New("maximum tree depth exceeded")
+ ErrFileNotFound = errors.New("file not found")
+)
+
+// Tree is basically like a directory - it references a bunch of other trees
+// and/or blobs (i.e. files and sub-directories)
+type Tree struct {
+ Entries []TreeEntry
+ Hash plumbing.Hash
+
+ s storer.EncodedObjectStorer
+ m map[string]*TreeEntry
+}
+
+// GetTree gets a tree from an object storer and decodes it.
+func GetTree(s storer.EncodedObjectStorer, h plumbing.Hash) (*Tree, error) {
+ o, err := s.EncodedObject(plumbing.TreeObject, h)
+ if err != nil {
+ return nil, err
+ }
+
+ return DecodeTree(s, o)
+}
+
+// DecodeTree decodes an encoded object into a *Tree and associates it to the
+// given object storer.
+func DecodeTree(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (*Tree, error) {
+ t := &Tree{s: s}
+ if err := t.Decode(o); err != nil {
+ return nil, err
+ }
+
+ return t, nil
+}
+
+// TreeEntry represents a file
+type TreeEntry struct {
+ Name string
+ Mode os.FileMode
+ Hash plumbing.Hash
+}
+
+// File returns the hash of the file identified by the `path` argument.
+// The path is interpreted as relative to the tree receiver.
+func (t *Tree) File(path string) (*File, error) {
+ e, err := t.findEntry(path)
+ if err != nil {
+ return nil, ErrFileNotFound
+ }
+
+ blob, err := GetBlob(t.s, e.Hash)
+ if err != nil {
+ return nil, err
+ }
+
+ return NewFile(path, e.Mode, blob), nil
+}
+
+// TreeEntryFile returns the *File for a given *TreeEntry.
+func (t *Tree) TreeEntryFile(e *TreeEntry) (*File, error) {
+ blob, err := GetBlob(t.s, e.Hash)
+ if err != nil {
+ return nil, err
+ }
+
+ return NewFile(e.Name, e.Mode, blob), nil
+}
+
+func (t *Tree) findEntry(path string) (*TreeEntry, error) {
+ pathParts := strings.Split(path, "/")
+
+ var tree *Tree
+ var err error
+ for tree = t; len(pathParts) > 1; pathParts = pathParts[1:] {
+ if tree, err = tree.dir(pathParts[0]); err != nil {
+ return nil, err
+ }
+ }
+
+ return tree.entry(pathParts[0])
+}
+
+var errDirNotFound = errors.New("directory not found")
+
+func (t *Tree) dir(baseName string) (*Tree, error) {
+ entry, err := t.entry(baseName)
+ if err != nil {
+ return nil, errDirNotFound
+ }
+
+ obj, err := t.s.EncodedObject(plumbing.TreeObject, entry.Hash)
+ if err != nil {
+ return nil, err
+ }
+
+ tree := &Tree{s: t.s}
+ tree.Decode(obj)
+
+ return tree, nil
+}
+
+var errEntryNotFound = errors.New("entry not found")
+
+func (t *Tree) entry(baseName string) (*TreeEntry, error) {
+ if t.m == nil {
+ t.buildMap()
+ }
+ entry, ok := t.m[baseName]
+ if !ok {
+ return nil, errEntryNotFound
+ }
+
+ return entry, nil
+}
+
+// Files returns a FileIter allowing to iterate over the Tree
+func (t *Tree) Files() *FileIter {
+ return NewFileIter(t.s, t)
+}
+
+// ID returns the object ID of the tree. The returned value will always match
+// the current value of Tree.Hash.
+//
+// ID is present to fulfill the Object interface.
+func (t *Tree) ID() plumbing.Hash {
+ return t.Hash
+}
+
+// Type returns the type of object. It always returns plumbing.TreeObject.
+func (t *Tree) Type() plumbing.ObjectType {
+ return plumbing.TreeObject
+}
+
+// Decode transform an plumbing.EncodedObject into a Tree struct
+func (t *Tree) Decode(o plumbing.EncodedObject) (err error) {
+ if o.Type() != plumbing.TreeObject {
+ return ErrUnsupportedObject
+ }
+
+ t.Hash = o.Hash()
+ if o.Size() == 0 {
+ return nil
+ }
+
+ t.Entries = nil
+ t.m = nil
+
+ reader, err := o.Reader()
+ if err != nil {
+ return err
+ }
+ defer ioutil.CheckClose(reader, &err)
+
+ r := bufio.NewReader(reader)
+ for {
+ mode, err := r.ReadString(' ')
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+
+ return err
+ }
+
+ fm, err := t.decodeFileMode(mode[:len(mode)-1])
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ name, err := r.ReadString(0)
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ var hash plumbing.Hash
+ if _, err = io.ReadFull(r, hash[:]); err != nil {
+ return err
+ }
+
+ baseName := name[:len(name)-1]
+ t.Entries = append(t.Entries, TreeEntry{
+ Hash: hash,
+ Mode: fm,
+ Name: baseName,
+ })
+ }
+
+ return nil
+}
+
+func (t *Tree) decodeFileMode(mode string) (os.FileMode, error) {
+ fm, err := strconv.ParseInt(mode, 8, 32)
+ if err != nil && err != io.EOF {
+ return 0, err
+ }
+
+ m := os.FileMode(fm)
+ switch fm {
+ case 0040000: //tree
+ m = m | os.ModeDir
+ case 0120000: //symlink
+ m = m | os.ModeSymlink
+ }
+
+ return m, nil
+}
+
+// Encode transforms a Tree into a plumbing.EncodedObject.
+func (t *Tree) Encode(o plumbing.EncodedObject) error {
+ o.SetType(plumbing.TreeObject)
+ w, err := o.Writer()
+ if err != nil {
+ return err
+ }
+
+ var size int
+ defer ioutil.CheckClose(w, &err)
+ for _, entry := range t.Entries {
+ n, err := fmt.Fprintf(w, "%o %s", entry.Mode, entry.Name)
+ if err != nil {
+ return err
+ }
+
+ size += n
+ n, err = w.Write([]byte{0x00})
+ if err != nil {
+ return err
+ }
+
+ size += n
+ n, err = w.Write([]byte(entry.Hash[:]))
+ if err != nil {
+ return err
+ }
+ size += n
+ }
+
+ o.SetSize(int64(size))
+ return err
+}
+
+func (t *Tree) buildMap() {
+ t.m = make(map[string]*TreeEntry)
+ for i := 0; i < len(t.Entries); i++ {
+ t.m[t.Entries[i].Name] = &t.Entries[i]
+ }
+}
+
+// treeEntryIter facilitates iterating through the TreeEntry objects in a Tree.
+type treeEntryIter struct {
+ t *Tree
+ pos int
+}
+
+func (iter *treeEntryIter) Next() (TreeEntry, error) {
+ if iter.pos >= len(iter.t.Entries) {
+ return TreeEntry{}, io.EOF
+ }
+ iter.pos++
+ return iter.t.Entries[iter.pos-1], nil
+}
+
+// TreeWalker provides a means of walking through all of the entries in a Tree.
+type TreeWalker struct {
+ stack []treeEntryIter
+ base string
+ recursive bool
+
+ s storer.EncodedObjectStorer
+ t *Tree
+}
+
+// NewTreeWalker returns a new TreeWalker for the given tree.
+//
+// It is the caller's responsibility to call Close() when finished with the
+// tree walker.
+func NewTreeWalker(t *Tree, recursive bool) *TreeWalker {
+ stack := make([]treeEntryIter, 0, startingStackSize)
+ stack = append(stack, treeEntryIter{t, 0})
+
+ return &TreeWalker{
+ stack: stack,
+ recursive: recursive,
+
+ s: t.s,
+ t: t,
+ }
+}
+
+// Next returns the next object from the tree. Objects are returned in order
+// and subtrees are included. After the last object has been returned further
+// calls to Next() will return io.EOF.
+//
+// In the current implementation any objects which cannot be found in the
+// underlying repository will be skipped automatically. It is possible that this
+// may change in future versions.
+func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) {
+ var obj Object
+ for {
+ current := len(w.stack) - 1
+ if current < 0 {
+ // Nothing left on the stack so we're finished
+ err = io.EOF
+ return
+ }
+
+ if current > maxTreeDepth {
+ // We're probably following bad data or some self-referencing tree
+ err = ErrMaxTreeDepth
+ return
+ }
+
+ entry, err = w.stack[current].Next()
+ if err == io.EOF {
+ // Finished with the current tree, move back up to the parent
+ w.stack = w.stack[:current]
+ w.base, _ = path.Split(w.base)
+ w.base = path.Clean(w.base) // Remove trailing slash
+ continue
+ }
+
+ if err != nil {
+ return
+ }
+
+ if entry.Mode == submoduleMode {
+ err = nil
+ continue
+ }
+
+ if entry.Mode.IsDir() {
+ obj, err = GetTree(w.s, entry.Hash)
+ }
+
+ name = path.Join(w.base, entry.Name)
+
+ if err != nil {
+ err = io.EOF
+ return
+ }
+
+ break
+ }
+
+ if !w.recursive {
+ return
+ }
+
+ if t, ok := obj.(*Tree); ok {
+ w.stack = append(w.stack, treeEntryIter{t, 0})
+ w.base = path.Join(w.base, entry.Name)
+ }
+
+ return
+}
+
+// Tree returns the tree that the tree walker most recently operated on.
+func (w *TreeWalker) Tree() *Tree {
+ current := len(w.stack) - 1
+ if w.stack[current].pos == 0 {
+ current--
+ }
+
+ if current < 0 {
+ return nil
+ }
+
+ return w.stack[current].t
+}
+
+// Close releases any resources used by the TreeWalker.
+func (w *TreeWalker) Close() {
+ w.stack = nil
+}
+
+// TreeIter provides an iterator for a set of trees.
+type TreeIter struct {
+ storer.EncodedObjectIter
+ s storer.EncodedObjectStorer
+}
+
+// NewTreeIter returns a TreeIter for the given repository and underlying
+// object iterator.
+//
+// The returned TreeIter will automatically skip over non-tree objects.
+func NewTreeIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *TreeIter {
+ return &TreeIter{iter, s}
+}
+
+// Next moves the iterator to the next tree and returns a pointer to it. If it
+// has reached the end of the set it will return io.EOF.
+func (iter *TreeIter) Next() (*Tree, error) {
+ for {
+ obj, err := iter.EncodedObjectIter.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ if obj.Type() != plumbing.TreeObject {
+ continue
+ }
+
+ return DecodeTree(iter.s, obj)
+ }
+}
+
+// ForEach call the cb function for each tree contained on this iter until
+// an error happens or the end of the iter is reached. If ErrStop is sent
+// the iteration is stop but no error is returned. The iterator is closed.
+func (iter *TreeIter) ForEach(cb func(*Tree) error) error {
+ return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error {
+ if obj.Type() != plumbing.TreeObject {
+ return nil
+ }
+
+ t, err := DecodeTree(iter.s, obj)
+ if err != nil {
+ return err
+ }
+
+ return cb(t)
+ })
+}
diff --git a/plumbing/object/tree_test.go b/plumbing/object/tree_test.go
new file mode 100644
index 0000000..00601c1
--- /dev/null
+++ b/plumbing/object/tree_test.go
@@ -0,0 +1,1425 @@
+package object
+
+import (
+ "io"
+ "os"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+
+ . "gopkg.in/check.v1"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+)
+
+type TreeSuite struct {
+ BaseObjectsSuite
+ Tree *Tree
+}
+
+var _ = Suite(&TreeSuite{})
+
+func (s *TreeSuite) SetUpSuite(c *C) {
+ s.BaseObjectsSuite.SetUpSuite(c)
+ hash := plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c")
+
+ s.Tree = s.tree(c, hash)
+}
+
+func (s *TreeSuite) TestDecode(c *C) {
+ c.Assert(s.Tree.Entries, HasLen, 8)
+ c.Assert(s.Tree.Entries[0].Name, Equals, ".gitignore")
+ c.Assert(s.Tree.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
+ c.Assert(s.Tree.Entries[0].Mode.String(), Equals, "-rw-r--r--")
+ c.Assert(s.Tree.Entries[4].Name, Equals, "go")
+ c.Assert(s.Tree.Entries[4].Hash.String(), Equals, "a39771a7651f97faf5c72e08224d857fc35133db")
+ c.Assert(s.Tree.Entries[4].Mode.String(), Equals, "d---------")
+}
+
+func (s *TreeSuite) TestDecodeNonTree(c *C) {
+ hash := plumbing.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492")
+ blob, err := s.Storer.EncodedObject(plumbing.BlobObject, hash)
+ c.Assert(err, IsNil)
+
+ tree := &Tree{}
+ err = tree.Decode(blob)
+ c.Assert(err, Equals, ErrUnsupportedObject)
+}
+
+func (s *TreeSuite) TestType(c *C) {
+ c.Assert(s.Tree.Type(), Equals, plumbing.TreeObject)
+}
+
+func (s *TreeSuite) TestFile(c *C) {
+ f, err := s.Tree.File("LICENSE")
+ c.Assert(err, IsNil)
+ c.Assert(f.Name, Equals, "LICENSE")
+}
+
+func (s *TreeSuite) TestFileNotFound(c *C) {
+ f, err := s.Tree.File("not-found")
+ c.Assert(f, IsNil)
+ c.Assert(err, Equals, ErrFileNotFound)
+}
+
+func (s *TreeSuite) TestFiles(c *C) {
+ var count int
+ err := s.Tree.Files().ForEach(func(f *File) error {
+ count++
+ return nil
+ })
+
+ c.Assert(err, IsNil)
+ c.Assert(count, Equals, 9)
+}
+
+// This plumbing.EncodedObject implementation has a reader that only returns 6
+// bytes at a time, this should simulate the conditions when a read
+// returns less bytes than asked, for example when reading a hash which
+// is bigger than 6 bytes.
+type SortReadObject struct {
+ t plumbing.ObjectType
+ h plumbing.Hash
+ cont []byte
+ sz int64
+}
+
+func (o *SortReadObject) Hash() plumbing.Hash { return o.h }
+func (o *SortReadObject) Type() plumbing.ObjectType { return o.t }
+func (o *SortReadObject) SetType(t plumbing.ObjectType) { o.t = t }
+func (o *SortReadObject) Size() int64 { return o.sz }
+func (o *SortReadObject) SetSize(s int64) { o.sz = s }
+func (o *SortReadObject) Content() []byte { return o.cont }
+func (o *SortReadObject) Reader() (io.ReadCloser, error) {
+ return &SortReadCloser{pos: 0, data: o.cont}, nil
+}
+func (o *SortReadObject) Writer() (io.WriteCloser, error) { return o, nil }
+func (o *SortReadObject) Write(p []byte) (n int, err error) { return len(p), nil }
+func (o *SortReadObject) Close() error { return nil }
+
+// a ReadCloser that only returns 6 bytes at a time, to simulate incomplete reads.
+type SortReadCloser struct {
+ pos int
+ data []byte
+}
+
+func (o *SortReadCloser) Close() error { return nil }
+func (o *SortReadCloser) Read(p []byte) (int, error) {
+ if o.pos == len(o.data) {
+ return 0, io.EOF
+ }
+
+ sz := len(p)
+ remaining := len(o.data) - o.pos
+ if sz > 6 { // don't read more than 6 bytes at a time
+ sz = 6
+ }
+ if sz > remaining {
+ sz = remaining
+ }
+
+ src := o.data[o.pos : o.pos+sz]
+ nw := copy(p, src)
+ o.pos += nw
+
+ return nw, nil
+}
+
+func (s *TreeSuite) TestTreeDecodeEncodeIdempotent(c *C) {
+ trees := []*Tree{
+ {
+ Entries: []TreeEntry{
+ {"foo", os.FileMode(0), plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d")},
+ {"bar", os.FileMode(0), plumbing.NewHash("c029517f6300c2da0f4b651b8642506cd6aaf45d")},
+ {"baz", os.FileMode(0), plumbing.NewHash("d029517f6300c2da0f4b651b8642506cd6aaf45d")},
+ },
+ },
+ }
+ for _, tree := range trees {
+ obj := &plumbing.MemoryObject{}
+ err := tree.Encode(obj)
+ c.Assert(err, IsNil)
+ newTree := &Tree{}
+ err = newTree.Decode(obj)
+ c.Assert(err, IsNil)
+ tree.Hash = obj.Hash()
+ c.Assert(newTree, DeepEquals, tree)
+ }
+}
+
+func (s *TreeSuite) TestTreeIter(c *C) {
+ encIter, err := s.Storer.IterEncodedObjects(plumbing.TreeObject)
+ c.Assert(err, IsNil)
+ iter := NewTreeIter(s.Storer, encIter)
+
+ trees := []*Tree{}
+ iter.ForEach(func(t *Tree) error {
+ t.s = nil
+ trees = append(trees, t)
+ return nil
+ })
+
+ c.Assert(len(trees) > 0, Equals, true)
+ iter.Close()
+
+ encIter, err = s.Storer.IterEncodedObjects(plumbing.TreeObject)
+ c.Assert(err, IsNil)
+ iter = NewTreeIter(s.Storer, encIter)
+
+ i := 0
+ for {
+ t, err := iter.Next()
+ if err == io.EOF {
+ break
+ }
+
+ t.s = nil
+ c.Assert(err, IsNil)
+ c.Assert(t, DeepEquals, trees[i])
+ i += 1
+ }
+
+ iter.Close()
+}
+
+func (s *TreeSuite) TestTreeWalkerNext(c *C) {
+ commit, err := GetCommit(s.Storer, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ c.Assert(err, IsNil)
+ tree, err := commit.Tree()
+ c.Assert(err, IsNil)
+
+ walker := NewTreeWalker(tree, true)
+ for _, e := range treeWalkerExpects {
+ name, entry, err := walker.Next()
+ if err == io.EOF {
+ break
+ }
+
+ c.Assert(err, IsNil)
+ c.Assert(name, Equals, e.Path)
+ c.Assert(entry.Name, Equals, e.Name)
+ c.Assert(entry.Mode.String(), Equals, e.Mode)
+ c.Assert(entry.Hash.String(), Equals, e.Hash)
+
+ c.Assert(walker.Tree().ID().String(), Equals, e.Tree)
+ }
+}
+
+func (s *TreeSuite) TestTreeWalkerNextNonRecursive(c *C) {
+ commit := s.commit(c, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ tree, err := commit.Tree()
+ c.Assert(err, IsNil)
+
+ var count int
+ walker := NewTreeWalker(tree, false)
+ for {
+ name, entry, err := walker.Next()
+ if err == io.EOF {
+ break
+ }
+
+ c.Assert(err, IsNil)
+ c.Assert(name, Not(Equals), "")
+ c.Assert(entry, NotNil)
+
+ c.Assert(walker.Tree().ID().String(), Equals, "a8d315b2b1c615d43042c3a62402b8a54288cf5c")
+
+ count++
+ }
+
+ c.Assert(count, Equals, 8)
+}
+
+var treeWalkerExpects = []struct {
+ Path, Mode, Name, Hash, Tree string
+}{{
+ Path: ".gitignore", Mode: "-rw-r--r--", Name: ".gitignore",
+ Hash: "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "CHANGELOG", Mode: "-rw-r--r--", Name: "CHANGELOG",
+ Hash: "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "LICENSE", Mode: "-rw-r--r--", Name: "LICENSE",
+ Hash: "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "binary.jpg", Mode: "-rw-r--r--", Name: "binary.jpg",
+ Hash: "d5c0f4ab811897cadf03aec358ae60d21f91c50d", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "go", Mode: "d---------", Name: "go",
+ Hash: "a39771a7651f97faf5c72e08224d857fc35133db", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "go/example.go", Mode: "-rw-r--r--", Name: "example.go",
+ Hash: "880cd14280f4b9b6ed3986d6671f907d7cc2a198", Tree: "a39771a7651f97faf5c72e08224d857fc35133db",
+}, {
+ Path: "json", Mode: "d---------", Name: "json",
+ Hash: "5a877e6a906a2743ad6e45d99c1793642aaf8eda", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "json/long.json", Mode: "-rw-r--r--", Name: "long.json",
+ Hash: "49c6bb89b17060d7b4deacb7b338fcc6ea2352a9", Tree: "5a877e6a906a2743ad6e45d99c1793642aaf8eda",
+}, {
+ Path: "json/short.json", Mode: "-rw-r--r--", Name: "short.json",
+ Hash: "c8f1d8c61f9da76f4cb49fd86322b6e685dba956", Tree: "5a877e6a906a2743ad6e45d99c1793642aaf8eda",
+}, {
+ Path: "php", Mode: "d---------", Name: "php",
+ Hash: "586af567d0bb5e771e49bdd9434f5e0fb76d25fa", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "php/crappy.php", Mode: "-rw-r--r--", Name: "crappy.php",
+ Hash: "9a48f23120e880dfbe41f7c9b7b708e9ee62a492", Tree: "586af567d0bb5e771e49bdd9434f5e0fb76d25fa",
+}, {
+ Path: "vendor", Mode: "d---------", Name: "vendor",
+ Hash: "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "vendor/foo.go", Mode: "-rw-r--r--", Name: "foo.go",
+ Hash: "9dea2395f5403188298c1dabe8bdafe562c491e3", Tree: "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b",
+}}
+
+func entriesEquals(a, b []TreeEntry) bool {
+ if a == nil && b == nil {
+ return true
+ }
+
+ if a == nil || b == nil {
+ return false
+ }
+
+ if len(a) != len(b) {
+ return false
+ }
+
+ for i, v := range a {
+ if v != b[i] {
+ return false
+ }
+ }
+
+ return true
+}
+
+// When decoding a tree we were not checking the return value of read
+// when reading hashes. As a hash is quite small, it worked well nearly
+// all the time.
+//
+// I have found some examples of repos where the read is incomplete and
+// the tree decode fails, for example
+// http://github.com/sqlcipher/sqlcipher.git, object
+// 0ba19d22411289293ab5c012891529967d7c933e.
+//
+// This tests is performed with that object but using a SortReadObject to
+// simulate incomplete reads on all platforms and operating systems.
+func (s *TreeSuite) TestTreeDecodeReadBug(c *C) {
+ cont := []byte{
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x61, 0x6c, 0x74,
+ 0x65, 0x72, 0x2e, 0x63, 0x0, 0xa4, 0x9d, 0x33, 0x49, 0xd7,
+ 0xe2, 0x3f, 0xb5, 0x81, 0x19, 0x4f, 0x4c, 0xb5, 0x9a, 0xc0,
+ 0xd5, 0x1b, 0x2, 0x1f, 0x78, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x2e,
+ 0x63, 0x0, 0x9a, 0x3e, 0x95, 0x97, 0xdb, 0xb, 0x3, 0x20,
+ 0x77, 0xc9, 0x1d, 0x96, 0x9d, 0x22, 0xc6, 0x27, 0x3f, 0x70,
+ 0x2a, 0xc, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x61,
+ 0x74, 0x74, 0x61, 0x63, 0x68, 0x2e, 0x63, 0x0, 0xb8, 0xe1,
+ 0x21, 0x99, 0xb5, 0x7d, 0xe8, 0x11, 0xea, 0xe0, 0xd0, 0x61,
+ 0x42, 0xd5, 0xac, 0x4f, 0xd4, 0x30, 0xb1, 0xd8, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x61, 0x75, 0x74, 0x68, 0x2e,
+ 0x63, 0x0, 0xd3, 0x8b, 0xb8, 0x36, 0xa7, 0x84, 0xfb, 0xfa,
+ 0xb6, 0xab, 0x7b, 0x3, 0xd4, 0xe6, 0xdd, 0x43, 0xed, 0xc4,
+ 0x1f, 0xa7, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x62,
+ 0x61, 0x63, 0x6b, 0x75, 0x70, 0x2e, 0x63, 0x0, 0x25, 0x2f,
+ 0x61, 0xcf, 0xca, 0xa8, 0xfc, 0xf3, 0x13, 0x7e, 0x8, 0xed,
+ 0x68, 0x47, 0xdc, 0xfe, 0x1d, 0xc1, 0xde, 0x54, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x62, 0x69, 0x74, 0x76, 0x65,
+ 0x63, 0x2e, 0x63, 0x0, 0x52, 0x18, 0x4a, 0xa9, 0x64, 0xce,
+ 0x18, 0x98, 0xf3, 0x5d, 0x1b, 0x3d, 0x87, 0x87, 0x1c, 0x2d,
+ 0xe, 0xf4, 0xc5, 0x3d, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x62, 0x74, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x2e, 0x63,
+ 0x0, 0xd8, 0x7d, 0x4d, 0x5f, 0xee, 0xb6, 0x30, 0x7a, 0xec,
+ 0xdc, 0x9a, 0x83, 0x11, 0x14, 0x89, 0xab, 0x30, 0xc6, 0x78,
+ 0xc3, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x62, 0x74,
+ 0x72, 0x65, 0x65, 0x2e, 0x63, 0x0, 0x3c, 0xa6, 0x5, 0x83,
+ 0xe3, 0xc8, 0xe3, 0x12, 0x0, 0xf9, 0x73, 0xe0, 0xe9, 0xc4,
+ 0x53, 0x62, 0x58, 0xb2, 0x64, 0x39, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x62, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x68,
+ 0x0, 0xac, 0xe0, 0xf8, 0xcd, 0x21, 0x77, 0x70, 0xa2, 0xf6,
+ 0x6b, 0x2e, 0xb8, 0x71, 0xbb, 0xc5, 0xfd, 0xc6, 0xfc, 0x2b,
+ 0x68, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x62, 0x74,
+ 0x72, 0x65, 0x65, 0x49, 0x6e, 0x74, 0x2e, 0x68, 0x0, 0xce,
+ 0x3c, 0x54, 0x93, 0xf8, 0xca, 0xd0, 0xbc, 0x54, 0x8a, 0xe8,
+ 0xe4, 0x4e, 0x51, 0x28, 0x31, 0xd8, 0xfa, 0xc4, 0x31, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x62, 0x75, 0x69, 0x6c,
+ 0x64, 0x2e, 0x63, 0x0, 0x3c, 0x91, 0xcd, 0xcf, 0xdb, 0x7b,
+ 0x1, 0x7c, 0xbc, 0x2d, 0x5c, 0x29, 0x57, 0x1a, 0x98, 0x27,
+ 0xd, 0xe0, 0x71, 0xe6, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e,
+ 0x63, 0x0, 0xd4, 0xc, 0x65, 0xcb, 0x92, 0x45, 0x80, 0x29,
+ 0x6a, 0xd0, 0x69, 0xa0, 0x4b, 0xf9, 0xc9, 0xe9, 0x53, 0x4e,
+ 0xca, 0xa7, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63,
+ 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2e, 0x63, 0x0,
+ 0x9e, 0x91, 0x40, 0x8, 0x5c, 0x0, 0x46, 0xed, 0x3b, 0xf6,
+ 0xf4, 0x48, 0x52, 0x20, 0x69, 0x2d, 0xca, 0x17, 0x43, 0xc5,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63, 0x72, 0x79,
+ 0x70, 0x74, 0x6f, 0x2e, 0x63, 0x0, 0x25, 0x51, 0xe6, 0xba,
+ 0x2, 0x39, 0xf8, 0x5a, 0x35, 0x77, 0x96, 0xa8, 0xdd, 0xa8,
+ 0xca, 0x3e, 0x29, 0x70, 0x93, 0xf8, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e,
+ 0x68, 0x0, 0xf7, 0x1f, 0x53, 0x2c, 0xdc, 0x44, 0x8f, 0xa,
+ 0x1d, 0xd5, 0xc6, 0xef, 0xf5, 0xfb, 0xd3, 0x3a, 0x91, 0x55,
+ 0xaa, 0x97, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63,
+ 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x63, 0x63, 0x2e, 0x63,
+ 0x0, 0x53, 0x7d, 0xf7, 0xe3, 0xb3, 0x6a, 0xb5, 0xcf, 0xdd,
+ 0x6f, 0xca, 0x40, 0x28, 0xeb, 0xca, 0xe1, 0x86, 0x87, 0xd6,
+ 0x4d, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63, 0x72,
+ 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x2e,
+ 0x63, 0x0, 0xa5, 0x89, 0x27, 0xc7, 0x6e, 0xf6, 0x20, 0x56,
+ 0x77, 0xbe, 0x5c, 0x1a, 0x8e, 0x80, 0xc9, 0x83, 0x56, 0xb3,
+ 0xa9, 0xd3, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63,
+ 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x62, 0x74,
+ 0x6f, 0x6d, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2e, 0x63, 0x0,
+ 0x1a, 0x33, 0x83, 0xe0, 0x1, 0xa7, 0x21, 0x11, 0xc3, 0xf6,
+ 0x61, 0x92, 0x22, 0xb0, 0x65, 0xf4, 0xbd, 0x1, 0xb, 0xe1,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63, 0x72, 0x79,
+ 0x70, 0x74, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
+ 0x6c, 0x2e, 0x63, 0x0, 0xd0, 0x19, 0x81, 0x3b, 0x47, 0x6c,
+ 0x52, 0xd0, 0x20, 0xe2, 0xc0, 0xac, 0xd5, 0x24, 0xe9, 0xea,
+ 0x3d, 0xf, 0xb9, 0xfe, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x63, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x63, 0x0, 0x60,
+ 0x59, 0x5f, 0xf8, 0x8d, 0x92, 0xf7, 0x8, 0x26, 0x4, 0xfb,
+ 0xd9, 0xdf, 0x9a, 0xfe, 0xa1, 0x6a, 0xe8, 0x6f, 0xf, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x64, 0x61, 0x74, 0x65,
+ 0x2e, 0x63, 0x0, 0x75, 0x8d, 0xd7, 0xc8, 0x9b, 0xca, 0x39,
+ 0x37, 0xa9, 0xd, 0x70, 0x6e, 0xa9, 0x82, 0xce, 0x3a, 0xcf,
+ 0x11, 0xd1, 0x83, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x2e, 0x63, 0x0, 0x63,
+ 0x4e, 0x11, 0x55, 0x63, 0xae, 0x12, 0xba, 0x65, 0x58, 0xcc,
+ 0xc5, 0x12, 0xae, 0xd6, 0x31, 0xc0, 0x66, 0xba, 0xd8, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x65, 0x78, 0x70, 0x72,
+ 0x2e, 0x63, 0x0, 0x66, 0x3, 0x97, 0xe0, 0x78, 0xae, 0x48,
+ 0xb2, 0xe7, 0x17, 0x5e, 0x33, 0x85, 0x67, 0x78, 0x19, 0x72,
+ 0x2d, 0xdd, 0x6c, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x0, 0xc3, 0x2,
+ 0x8c, 0x4f, 0x93, 0x6e, 0xdf, 0x96, 0x71, 0x2d, 0xbe, 0x73,
+ 0xa0, 0x76, 0x62, 0xf0, 0xa2, 0x6b, 0x1d, 0xa, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x66, 0x6b, 0x65, 0x79, 0x2e,
+ 0x63, 0x0, 0xac, 0x35, 0xbc, 0x19, 0x4c, 0xde, 0xb1, 0x27,
+ 0x98, 0x9b, 0x9, 0x40, 0x35, 0xce, 0xe0, 0x6f, 0x57, 0x37,
+ 0x6f, 0x5e, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x66,
+ 0x75, 0x6e, 0x63, 0x2e, 0x63, 0x0, 0xc0, 0x2f, 0x9, 0x6a,
+ 0xda, 0xd5, 0xbc, 0xe9, 0xac, 0x83, 0xd3, 0x5f, 0xf, 0x46,
+ 0x9, 0xd6, 0xf6, 0xd4, 0x3b, 0xe5, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e,
+ 0x63, 0x0, 0x7b, 0x2, 0xcf, 0x21, 0x30, 0xe0, 0xd1, 0xa7,
+ 0xb8, 0x89, 0xd8, 0x44, 0xc, 0xcc, 0x82, 0x8, 0xf7, 0xb6,
+ 0x7b, 0xf9, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x68,
+ 0x61, 0x73, 0x68, 0x2e, 0x63, 0x0, 0xe8, 0x1d, 0xcf, 0x95,
+ 0xe4, 0x38, 0x48, 0xfa, 0x70, 0x86, 0xb7, 0xf7, 0x81, 0xc0,
+ 0x90, 0xad, 0xc7, 0xe6, 0xca, 0x8e, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x68, 0x61, 0x73, 0x68, 0x2e, 0x68, 0x0,
+ 0x82, 0xb7, 0xc5, 0x8c, 0x71, 0x9, 0xb, 0x54, 0x7e, 0x10,
+ 0x17, 0x42, 0xaa, 0x9, 0x51, 0x73, 0x9f, 0xf2, 0xee, 0xe7,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x68, 0x77, 0x74,
+ 0x69, 0x6d, 0x65, 0x2e, 0x68, 0x0, 0xb8, 0xbc, 0x5a, 0x29,
+ 0x5b, 0xe3, 0xfa, 0xc8, 0x35, 0x1f, 0xa9, 0xf0, 0x8a, 0x77,
+ 0x57, 0x9d, 0x59, 0xc9, 0xa8, 0xe4, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x0, 0x9a, 0x56, 0x61, 0xf5, 0x9a, 0x72, 0x95, 0x2b,
+ 0xe6, 0xc1, 0x67, 0xa0, 0xc2, 0xdb, 0x15, 0x9b, 0x91, 0xb7,
+ 0x1f, 0xae, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6a,
+ 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x0, 0xfe,
+ 0xd2, 0x7b, 0xe3, 0xe3, 0x80, 0x55, 0xd2, 0x20, 0x43, 0x95,
+ 0xcd, 0xe6, 0xff, 0xc9, 0x45, 0x89, 0xfb, 0xf5, 0xe8, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6c, 0x65, 0x67, 0x61,
+ 0x63, 0x79, 0x2e, 0x63, 0x0, 0x94, 0x64, 0x9a, 0xe7, 0x5,
+ 0xab, 0x93, 0x85, 0x10, 0x8d, 0xd, 0x88, 0x7a, 0xf0, 0x75,
+ 0x92, 0x89, 0xfb, 0x23, 0xcb, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x6c, 0x65, 0x6d, 0x70, 0x61, 0x72, 0x2e, 0x63,
+ 0x0, 0x2a, 0xfa, 0xa6, 0xce, 0xa6, 0xd8, 0x29, 0x60, 0x2c,
+ 0x27, 0x86, 0xc1, 0xf8, 0xa3, 0x7f, 0x56, 0x7c, 0xf6, 0xfd,
+ 0x53, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6c, 0x6f,
+ 0x61, 0x64, 0x65, 0x78, 0x74, 0x2e, 0x63, 0x0, 0xcd, 0xcf,
+ 0x6a, 0x93, 0xb8, 0xc4, 0xf, 0x91, 0x4b, 0x94, 0x24, 0xe,
+ 0xf1, 0x4c, 0xb4, 0xa3, 0xa, 0x37, 0xec, 0xa1, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x2e,
+ 0x63, 0x0, 0x39, 0xf6, 0x4, 0x21, 0xe6, 0x81, 0x27, 0x7c,
+ 0xc3, 0xdb, 0xa0, 0x9a, 0xbe, 0x7c, 0xf7, 0x90, 0xd5, 0x28,
+ 0xf5, 0xc3, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d,
+ 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x2e, 0x63, 0x0, 0x35, 0xa4,
+ 0x4e, 0x5f, 0x61, 0xc2, 0xe4, 0x4c, 0x48, 0x1c, 0x62, 0x51,
+ 0xbd, 0xa, 0xae, 0x7a, 0xcd, 0xa4, 0xde, 0xb, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x65, 0x6d, 0x30, 0x2e,
+ 0x63, 0x0, 0xd, 0xb, 0x66, 0x67, 0xd6, 0xa, 0x95, 0x5a,
+ 0x6, 0x96, 0xdf, 0x62, 0x89, 0xb4, 0x91, 0x78, 0x96, 0x93,
+ 0x43, 0xaa, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d,
+ 0x65, 0x6d, 0x31, 0x2e, 0x63, 0x0, 0x35, 0x78, 0x49, 0x6f,
+ 0x33, 0x3, 0x7, 0xb2, 0x31, 0xdf, 0xb5, 0x3c, 0xc, 0x2e,
+ 0x1c, 0x6b, 0x32, 0x3d, 0x79, 0x1e, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x6d, 0x65, 0x6d, 0x32, 0x2e, 0x63, 0x0,
+ 0x26, 0x44, 0x8e, 0xa8, 0xaa, 0xe0, 0x36, 0x6a, 0xf0, 0x54,
+ 0x1a, 0xfe, 0xa4, 0x79, 0xb, 0x42, 0xf4, 0xa6, 0x9b, 0x5a,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x65, 0x6d,
+ 0x33, 0x2e, 0x63, 0x0, 0x1a, 0x1b, 0x79, 0x1f, 0x28, 0xf8,
+ 0xcf, 0x3c, 0xe4, 0xf9, 0xa3, 0x5c, 0xda, 0xd7, 0xb7, 0x10,
+ 0x75, 0x68, 0xc7, 0x15, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x6d, 0x65, 0x6d, 0x35, 0x2e, 0x63, 0x0, 0x78, 0x3c,
+ 0xef, 0x61, 0x76, 0xc5, 0x9c, 0xbf, 0x30, 0x91, 0x46, 0x31,
+ 0x9, 0x5a, 0x1a, 0x54, 0xf4, 0xe4, 0x2e, 0x8, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x65, 0x6d, 0x6a, 0x6f,
+ 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x0, 0x5, 0x72,
+ 0x59, 0x48, 0xf6, 0x5d, 0x42, 0x7b, 0x7, 0xf7, 0xf9, 0x29,
+ 0xac, 0xa3, 0xff, 0x22, 0x4b, 0x17, 0x53, 0xdf, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78,
+ 0x2e, 0x63, 0x0, 0xb5, 0x67, 0xe7, 0xc2, 0x7e, 0xf2, 0x4,
+ 0x10, 0x86, 0xaf, 0xe0, 0xf6, 0x96, 0x66, 0xe2, 0x7b, 0xf5,
+ 0x9, 0x8a, 0x59, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x6d, 0x75, 0x74, 0x65, 0x78, 0x2e, 0x68, 0x0, 0x9, 0x78,
+ 0x81, 0x22, 0x52, 0x77, 0x89, 0xa, 0x9c, 0x36, 0xc2, 0x4d,
+ 0x41, 0xf6, 0x11, 0x4d, 0x64, 0xc0, 0x6d, 0xb3, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78,
+ 0x5f, 0x6e, 0x6f, 0x6f, 0x70, 0x2e, 0x63, 0x0, 0x45, 0x6e,
+ 0x82, 0xa2, 0x5e, 0x27, 0x1b, 0x6, 0x14, 0xe7, 0xf4, 0xf8,
+ 0x3c, 0x22, 0x85, 0x53, 0xb7, 0xfa, 0x1, 0x58, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78,
+ 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x2e, 0x63, 0x0, 0xec, 0xa7,
+ 0x29, 0x58, 0x31, 0xc2, 0xf0, 0xee, 0x48, 0xba, 0x54, 0xd0,
+ 0x62, 0x91, 0x4d, 0x6, 0xa1, 0xdd, 0x8e, 0xbe, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78,
+ 0x5f, 0x77, 0x33, 0x32, 0x2e, 0x63, 0x0, 0x27, 0xd1, 0xa,
+ 0xf5, 0xbd, 0x33, 0x1b, 0xdb, 0x97, 0x3f, 0x61, 0x45, 0xb7,
+ 0x4f, 0x72, 0xb6, 0x7, 0xcf, 0xc4, 0x6e, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79,
+ 0x2e, 0x63, 0x0, 0xfc, 0xab, 0x5b, 0xfa, 0xf0, 0x19, 0x8,
+ 0xd3, 0xde, 0x93, 0xfa, 0x88, 0xb5, 0xea, 0xe9, 0xe9, 0x6c,
+ 0xa3, 0xc8, 0xe8, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x6f, 0x73, 0x2e, 0x63, 0x0, 0xbe, 0x2e, 0xa4, 0xcf, 0xc0,
+ 0x19, 0x59, 0x93, 0xa3, 0x40, 0xc9, 0x2, 0xae, 0xdd, 0xf1,
+ 0xbe, 0x4b, 0x8e, 0xd7, 0x3a, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x6f, 0x73, 0x2e, 0x68, 0x0, 0x7, 0xa, 0x2d,
+ 0xdd, 0x17, 0xf7, 0x71, 0xf9, 0x8f, 0xf8, 0xcc, 0xd6, 0xf0,
+ 0x33, 0xbd, 0xac, 0xc5, 0xe9, 0xf6, 0xc, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x6f, 0x73, 0x5f, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x0, 0xf6, 0xc3, 0xe7, 0xff,
+ 0x89, 0x46, 0x30, 0x86, 0x40, 0x18, 0x22, 0xf4, 0x81, 0xe7,
+ 0xe3, 0xb8, 0x7b, 0x2c, 0x78, 0xc7, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x6f, 0x73, 0x5f, 0x75, 0x6e, 0x69, 0x78,
+ 0x2e, 0x63, 0x0, 0xab, 0xc2, 0x3a, 0x45, 0x2e, 0x72, 0xf7,
+ 0x1c, 0x76, 0xaf, 0xa9, 0x98, 0x3c, 0x3a, 0xd9, 0xd4, 0x25,
+ 0x61, 0x6c, 0x6d, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x6f, 0x73, 0x5f, 0x77, 0x69, 0x6e, 0x2e, 0x63, 0x0, 0xae,
+ 0xb0, 0x88, 0x14, 0xb3, 0xda, 0xbe, 0x81, 0xb8, 0x4c, 0xda,
+ 0x91, 0x85, 0x82, 0xb0, 0xf, 0xfd, 0x86, 0xe4, 0x87, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x70, 0x61, 0x67, 0x65,
+ 0x72, 0x2e, 0x63, 0x0, 0x61, 0x72, 0x7f, 0xaa, 0x9c, 0xf,
+ 0x3d, 0x56, 0x62, 0x65, 0xbe, 0x7e, 0xec, 0x5b, 0x2a, 0x35,
+ 0xf6, 0xa4, 0xbc, 0x9f, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x70, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x68, 0x0, 0x6f,
+ 0x65, 0x91, 0x36, 0xe2, 0x76, 0x7, 0x9d, 0xa4, 0x3a, 0x2e,
+ 0x39, 0xe1, 0xb6, 0x86, 0x37, 0xec, 0xad, 0xcf, 0x68, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x70, 0x61, 0x72, 0x73,
+ 0x65, 0x2e, 0x79, 0x0, 0x83, 0x10, 0xb2, 0x69, 0x89, 0xb0,
+ 0x5b, 0xed, 0x1e, 0x1b, 0x3, 0xda, 0x80, 0xf5, 0xc0, 0xa5,
+ 0x2e, 0x9a, 0xd1, 0xd2, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x70, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x0,
+ 0x48, 0x2a, 0x18, 0x8b, 0xee, 0x19, 0x91, 0xbc, 0x8a, 0xda,
+ 0xc9, 0x6a, 0x19, 0x3a, 0x53, 0xe5, 0x46, 0x2a, 0x8c, 0x10,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x70, 0x63, 0x61,
+ 0x63, 0x68, 0x65, 0x2e, 0x68, 0x0, 0xf4, 0xd4, 0xad, 0x71,
+ 0xc1, 0xd, 0x78, 0xc6, 0xda, 0xbd, 0xe2, 0x52, 0x15, 0xcd,
+ 0x41, 0x5a, 0x76, 0x1, 0x48, 0xca, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x70, 0x63, 0x61, 0x63, 0x68, 0x65, 0x31,
+ 0x2e, 0x63, 0x0, 0x41, 0x47, 0xd2, 0xef, 0xf5, 0x5b, 0xdd,
+ 0x9f, 0xf7, 0xc6, 0x86, 0xc, 0x60, 0x18, 0x10, 0x20, 0x16,
+ 0x6c, 0x5f, 0x50, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x63, 0x0, 0x22,
+ 0x97, 0x71, 0x69, 0x61, 0x7d, 0x49, 0x22, 0xb3, 0x99, 0x3f,
+ 0x76, 0x9d, 0x90, 0xfa, 0x7b, 0xc4, 0x41, 0xea, 0x50, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x70, 0x72, 0x65, 0x70,
+ 0x61, 0x72, 0x65, 0x2e, 0x63, 0x0, 0xd7, 0x8d, 0x83, 0xcb,
+ 0xd8, 0x78, 0x97, 0xf5, 0x73, 0x30, 0x3f, 0x9f, 0x57, 0xab,
+ 0x8d, 0xe0, 0x24, 0xa6, 0xe3, 0xf8, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x2e,
+ 0x63, 0x0, 0x9f, 0x68, 0xd2, 0x4, 0xff, 0xdc, 0x9f, 0x3d,
+ 0x42, 0x7f, 0x80, 0xa8, 0x23, 0x9a, 0x7f, 0xa3, 0xa9, 0x8a,
+ 0xec, 0xbd, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x72,
+ 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2e, 0x63, 0x0, 0x23, 0x4e,
+ 0xbd, 0xf6, 0x58, 0xf4, 0x36, 0xcc, 0x7c, 0x68, 0xf0, 0x27,
+ 0xc4, 0x8b, 0xe, 0x1b, 0x9b, 0xa3, 0x4e, 0x98, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x6c,
+ 0x76, 0x65, 0x2e, 0x63, 0x0, 0x91, 0xef, 0xca, 0xa1, 0xa1,
+ 0x6b, 0xfc, 0x98, 0xfb, 0x35, 0xd8, 0x5c, 0xad, 0x15, 0x6b,
+ 0x93, 0x53, 0x3e, 0x4e, 0x6, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x74, 0x2e, 0x63,
+ 0x0, 0x57, 0x61, 0xf9, 0x85, 0x50, 0xb1, 0x76, 0xcc, 0xe1,
+ 0x1d, 0xcb, 0xce, 0xc9, 0x38, 0x99, 0xa0, 0x75, 0xbb, 0x64,
+ 0xfd, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x65,
+ 0x6c, 0x65, 0x63, 0x74, 0x2e, 0x63, 0x0, 0xf3, 0xf1, 0x49,
+ 0x9, 0x63, 0x95, 0x5b, 0x8e, 0xd0, 0xc9, 0xfe, 0x6e, 0x1e,
+ 0xec, 0x83, 0x6c, 0x1a, 0x52, 0x94, 0xb4, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x2e,
+ 0x63, 0x0, 0x1b, 0xe2, 0x87, 0x1f, 0xed, 0x9a, 0x1f, 0xdf,
+ 0x1d, 0xf7, 0x19, 0x8e, 0x11, 0x25, 0x36, 0x0, 0xec, 0xba,
+ 0x76, 0xcc, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73,
+ 0x71, 0x6c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x2e, 0x68,
+ 0x0, 0x82, 0x75, 0x30, 0x95, 0xcd, 0x17, 0x23, 0xc5, 0xff,
+ 0x4f, 0x11, 0x15, 0xe4, 0x97, 0x55, 0x91, 0xee, 0x34, 0xf5,
+ 0xce, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x71,
+ 0x6c, 0x69, 0x74, 0x65, 0x2e, 0x68, 0x2e, 0x69, 0x6e, 0x0,
+ 0x66, 0x8, 0x82, 0x31, 0x75, 0xde, 0x5b, 0x6a, 0xd, 0x37,
+ 0x8f, 0xdb, 0xc, 0x38, 0x18, 0xb6, 0xab, 0x4f, 0xbf, 0x8e,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x71, 0x6c,
+ 0x69, 0x74, 0x65, 0x33, 0x2e, 0x72, 0x63, 0x0, 0x96, 0x98,
+ 0x76, 0xda, 0x1e, 0x57, 0x14, 0x3d, 0xe0, 0xb4, 0xd1, 0xc7,
+ 0x62, 0x9f, 0xd3, 0x35, 0x6f, 0x2e, 0x1c, 0x96, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x71, 0x6c, 0x69, 0x74,
+ 0x65, 0x33, 0x65, 0x78, 0x74, 0x2e, 0x68, 0x0, 0x92, 0x8b,
+ 0xb3, 0xba, 0xd9, 0xdd, 0x64, 0x3c, 0x30, 0x1d, 0xd2, 0xb0,
+ 0xac, 0x22, 0x28, 0x7a, 0x81, 0x28, 0x48, 0x84, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x71, 0x6c, 0x69, 0x74,
+ 0x65, 0x49, 0x6e, 0x74, 0x2e, 0x68, 0x0, 0x59, 0x50, 0xf2,
+ 0x37, 0xd9, 0xf9, 0xf2, 0xd3, 0xef, 0x6b, 0xd8, 0xbe, 0x34,
+ 0x2d, 0xcf, 0x64, 0x89, 0x22, 0x51, 0x42, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x73, 0x71, 0x6c, 0x69, 0x74, 0x65,
+ 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x2e, 0x68, 0x0, 0xc7, 0xae,
+ 0xe5, 0x3c, 0xeb, 0xca, 0x94, 0xda, 0x51, 0xe7, 0x1a, 0x82,
+ 0x2e, 0xa5, 0xa6, 0xde, 0xb9, 0x3, 0x85, 0xdf, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x2e, 0x63, 0x0, 0x28, 0x34, 0x9e, 0x6d, 0x3d, 0x20,
+ 0x88, 0xe0, 0x0, 0x3b, 0x76, 0xf8, 0xa, 0x89, 0x54, 0xfa,
+ 0xec, 0x59, 0x30, 0xba, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x63, 0x0, 0x26,
+ 0xbb, 0xfb, 0x4f, 0x45, 0x6c, 0x42, 0x98, 0x25, 0x29, 0xea,
+ 0x1a, 0x63, 0xa0, 0x17, 0x51, 0xdd, 0x3e, 0xe9, 0x5a, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x63, 0x6c, 0x73,
+ 0x71, 0x6c, 0x69, 0x74, 0x65, 0x2e, 0x63, 0x0, 0xf1, 0xbb,
+ 0x29, 0x21, 0xda, 0xc, 0x68, 0xa4, 0xf1, 0xc8, 0xe1, 0x5c,
+ 0xf5, 0x66, 0xb2, 0x33, 0xe9, 0x2a, 0x51, 0x9f, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x31,
+ 0x2e, 0x63, 0x0, 0xa6, 0x38, 0xe4, 0x80, 0xad, 0xdf, 0x14,
+ 0x43, 0x9c, 0xdf, 0xa4, 0xee, 0x16, 0x4d, 0xc3, 0x1b, 0x79,
+ 0xf8, 0xbc, 0xac, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x32, 0x2e, 0x63, 0x0, 0xd1, 0x30,
+ 0xe9, 0xd0, 0x1b, 0x70, 0x24, 0xa5, 0xec, 0x6d, 0x73, 0x5,
+ 0x92, 0xee, 0x4d, 0x1f, 0xb0, 0x2c, 0xfd, 0xb4, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x33,
+ 0x2e, 0x63, 0x0, 0xe3, 0xed, 0x31, 0xc, 0x81, 0x4, 0xfe,
+ 0x36, 0x21, 0xce, 0xbb, 0xf, 0x51, 0xd1, 0x1, 0x45, 0x1,
+ 0x8d, 0x4f, 0xac, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x34, 0x2e, 0x63, 0x0, 0xa6, 0x37,
+ 0x5c, 0x7c, 0xc4, 0x3, 0xf6, 0xc, 0xaa, 0xb7, 0xe9, 0x59,
+ 0x53, 0x3e, 0x3d, 0xb1, 0xff, 0x75, 0xa, 0xe4, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x35,
+ 0x2e, 0x63, 0x0, 0x30, 0x3d, 0x12, 0x5, 0xb2, 0x26, 0x28,
+ 0x42, 0x3d, 0x98, 0x6f, 0x71, 0xe2, 0x7c, 0x7c, 0xf7, 0x14,
+ 0xa7, 0x45, 0xa6, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x36, 0x2e, 0x63, 0x0, 0xc1, 0x51,
+ 0xea, 0x42, 0x98, 0x9b, 0xb, 0xe2, 0x4e, 0xe4, 0xb9, 0xa4,
+ 0xbe, 0x37, 0x8b, 0x4f, 0x63, 0x6d, 0xb6, 0x41, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x37,
+ 0x2e, 0x63, 0x0, 0x3c, 0xd4, 0xa2, 0x24, 0xd7, 0xe8, 0xe1,
+ 0x6b, 0xd7, 0xcb, 0xe4, 0x9e, 0x2d, 0x3e, 0x94, 0xce, 0x9b,
+ 0x17, 0xbd, 0x76, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x38, 0x2e, 0x63, 0x0, 0xc5, 0x73,
+ 0x93, 0x32, 0xd4, 0x6e, 0x57, 0x12, 0x1d, 0xa2, 0x7c, 0x3e,
+ 0x88, 0xfd, 0xe7, 0x5a, 0xeb, 0x87, 0x10, 0xf7, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x39,
+ 0x2e, 0x63, 0x0, 0xe5, 0x99, 0x3e, 0x8f, 0xf7, 0x8f, 0x61,
+ 0xc2, 0x43, 0x5b, 0x6f, 0x97, 0xa3, 0xb4, 0x63, 0xe2, 0x27,
+ 0xc7, 0x67, 0xac, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x79, 0x6e, 0x63,
+ 0x2e, 0x63, 0x0, 0xb0, 0xb9, 0x43, 0x18, 0x5b, 0xfc, 0x23,
+ 0xc1, 0x7f, 0xd0, 0x8f, 0x55, 0x76, 0x8c, 0xac, 0x12, 0xa9,
+ 0xf5, 0x69, 0x51, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x65,
+ 0x78, 0x74, 0x2e, 0x63, 0x0, 0xb5, 0x1, 0x3f, 0x31, 0x73,
+ 0xa2, 0x17, 0x6e, 0x2d, 0x9f, 0xc, 0xaa, 0x99, 0x19, 0x30,
+ 0x36, 0xbf, 0xc3, 0x7e, 0x91, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x61, 0x63,
+ 0x6b, 0x75, 0x70, 0x2e, 0x63, 0x0, 0xe9, 0x67, 0x42, 0x4a,
+ 0x29, 0xf, 0x73, 0x8a, 0xec, 0xfd, 0xac, 0x57, 0x8e, 0x9b,
+ 0x87, 0xa4, 0xc4, 0xae, 0x8d, 0x7f, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x74,
+ 0x72, 0x65, 0x65, 0x2e, 0x63, 0x0, 0xdb, 0x72, 0x88, 0x9b,
+ 0x2a, 0xfb, 0x62, 0x72, 0x82, 0x8d, 0xda, 0x86, 0x6d, 0xcc,
+ 0xf1, 0x22, 0xa4, 0x9a, 0x72, 0x99, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x63, 0x0, 0x53, 0x47, 0x27,
+ 0xa0, 0x80, 0x42, 0xb6, 0xca, 0xd6, 0x7e, 0x26, 0x7e, 0x87,
+ 0xb4, 0x3, 0xa4, 0x1a, 0x73, 0xb2, 0x99, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x64,
+ 0x65, 0x6d, 0x6f, 0x76, 0x66, 0x73, 0x2e, 0x63, 0x0, 0x63,
+ 0x76, 0x27, 0x7, 0x1d, 0x9e, 0x28, 0xf4, 0xb3, 0x45, 0x1b,
+ 0xbb, 0xdd, 0xf8, 0x8, 0xd1, 0xa9, 0x12, 0x0, 0xf8, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74,
+ 0x5f, 0x64, 0x65, 0x76, 0x73, 0x79, 0x6d, 0x2e, 0x63, 0x0,
+ 0x21, 0xf0, 0xf6, 0x84, 0xd8, 0x61, 0x11, 0x67, 0x70, 0xde,
+ 0xfc, 0xde, 0xcd, 0x53, 0x2b, 0xa3, 0xee, 0xab, 0xa9, 0x75,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73,
+ 0x74, 0x5f, 0x66, 0x73, 0x2e, 0x63, 0x0, 0x47, 0x8c, 0xad,
+ 0x80, 0xb1, 0x6a, 0x90, 0x9b, 0x23, 0xbd, 0x3, 0xc2, 0xda,
+ 0xd8, 0xb4, 0x49, 0xa7, 0x45, 0x87, 0xa1, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x66,
+ 0x75, 0x6e, 0x63, 0x2e, 0x63, 0x0, 0x6f, 0x9b, 0xb0, 0x3d,
+ 0xc8, 0x8a, 0x21, 0xd6, 0x58, 0xbf, 0x99, 0x99, 0xba, 0xf6,
+ 0x6d, 0xc1, 0xd5, 0x2e, 0xbc, 0x54, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x68, 0x65,
+ 0x78, 0x69, 0x6f, 0x2e, 0x63, 0x0, 0xb2, 0xb, 0x5c, 0xe7,
+ 0x30, 0xab, 0x7f, 0xa8, 0x0, 0xd2, 0xd0, 0xcc, 0x38, 0xc7,
+ 0x72, 0x75, 0x59, 0x3e, 0xbd, 0xbb, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e,
+ 0x69, 0x74, 0x2e, 0x63, 0x0, 0xe3, 0x72, 0x4d, 0x8b, 0xe3,
+ 0x14, 0xdb, 0x9, 0xee, 0xa8, 0x4, 0xb, 0x9d, 0xdf, 0xc8,
+ 0xa8, 0xbe, 0xee, 0x22, 0x91, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x74,
+ 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x0, 0xf5, 0xc3,
+ 0xd9, 0xe4, 0x5, 0x9a, 0x16, 0x56, 0x7, 0x34, 0x7, 0xe4,
+ 0x3a, 0x92, 0x11, 0x79, 0x99, 0x69, 0x7b, 0x93, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
+ 0x69, 0x6e, 0x74, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x68,
+ 0x0, 0x69, 0x13, 0x37, 0xd1, 0xae, 0xd6, 0x37, 0x15, 0xd6,
+ 0x2e, 0x76, 0x26, 0x6f, 0xf, 0x3b, 0x50, 0x8b, 0x1, 0xa,
+ 0x34, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65,
+ 0x73, 0x74, 0x5f, 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c,
+ 0x2e, 0x63, 0x0, 0xe8, 0x70, 0x1a, 0x4e, 0xea, 0xdb, 0x8e,
+ 0xad, 0x16, 0x9d, 0x60, 0x6, 0x40, 0x7d, 0x54, 0xa8, 0x98,
+ 0x59, 0x2d, 0x70, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x65,
+ 0x78, 0x74, 0x2e, 0x63, 0x0, 0x11, 0x37, 0xe3, 0xa9, 0xaa,
+ 0xe9, 0x29, 0x6, 0xb8, 0x28, 0x9f, 0x6c, 0x3d, 0xaa, 0x61,
+ 0xf0, 0xd0, 0x70, 0xf5, 0x5a, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x6c,
+ 0x6c, 0x6f, 0x63, 0x2e, 0x63, 0x0, 0xcf, 0x98, 0xa8, 0xfb,
+ 0x21, 0x82, 0xc0, 0xba, 0xf5, 0xa, 0xd5, 0x79, 0x79, 0xb6,
+ 0x75, 0xbb, 0x70, 0x7a, 0x93, 0xb0, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x75,
+ 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x2e, 0x63, 0x0,
+ 0x62, 0x45, 0x41, 0xb3, 0x2a, 0x10, 0xd2, 0x1a, 0x2f, 0xd1,
+ 0xa, 0x35, 0xee, 0x66, 0x32, 0xbd, 0xac, 0x55, 0x2d, 0x41,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73,
+ 0x74, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65,
+ 0x78, 0x2e, 0x68, 0x0, 0xb7, 0xe1, 0xaf, 0xea, 0x5f, 0xd7,
+ 0x8b, 0x87, 0x58, 0x2, 0x65, 0xf8, 0x4c, 0x81, 0x61, 0x2c,
+ 0xbd, 0x2, 0x5b, 0xaf, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x75, 0x74, 0x65,
+ 0x78, 0x2e, 0x63, 0x0, 0xc9, 0xb4, 0xa2, 0x9a, 0xb7, 0x5c,
+ 0x77, 0xea, 0x5f, 0x36, 0xb5, 0x19, 0x32, 0x56, 0xd7, 0xf,
+ 0xe6, 0x58, 0xe, 0x95, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x65, 0x66,
+ 0x69, 0x6c, 0x65, 0x2e, 0x63, 0x0, 0x69, 0x86, 0x74, 0x41,
+ 0xb8, 0xcc, 0x9a, 0x62, 0x1a, 0xf3, 0x24, 0x13, 0xfc, 0x63,
+ 0xda, 0x80, 0x99, 0x37, 0x64, 0xf4, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x73,
+ 0x69, 0x6e, 0x73, 0x74, 0x2e, 0x63, 0x0, 0x53, 0x14, 0x33,
+ 0x31, 0x3e, 0xe3, 0x6c, 0x7, 0xeb, 0x21, 0xc0, 0x2f, 0x31,
+ 0x15, 0xcb, 0x7a, 0x37, 0x48, 0x6c, 0x79, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70,
+ 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x0, 0x8f, 0xcf,
+ 0xe7, 0xe2, 0x6e, 0x3f, 0xf1, 0x74, 0x96, 0xb8, 0x40, 0xf5,
+ 0xd6, 0x3c, 0x75, 0x78, 0x3a, 0xff, 0x81, 0x62, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
+ 0x71, 0x75, 0x6f, 0x74, 0x61, 0x2e, 0x63, 0x0, 0xe5, 0x90,
+ 0x99, 0x6c, 0xa4, 0xb8, 0x57, 0x4a, 0xb1, 0xe4, 0x18, 0x5d,
+ 0x57, 0x77, 0x56, 0x66, 0x4a, 0xd2, 0x49, 0x5f, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
+ 0x71, 0x75, 0x6f, 0x74, 0x61, 0x2e, 0x68, 0x0, 0x2d, 0x7,
+ 0x67, 0xa1, 0x9a, 0xb7, 0xc3, 0xa4, 0x21, 0xcd, 0xba, 0x6a,
+ 0x3, 0x49, 0x20, 0x43, 0x67, 0xc2, 0x2c, 0x81, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
+ 0x72, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x63, 0x0, 0xf5, 0x4a,
+ 0xe9, 0xb0, 0x63, 0xbb, 0x73, 0x71, 0x2f, 0xcf, 0xc1, 0xc6,
+ 0x83, 0x2e, 0x2a, 0x50, 0xf6, 0x2a, 0x97, 0xe7, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
+ 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x63, 0x0, 0x12,
+ 0x64, 0x44, 0x67, 0x64, 0x7d, 0x51, 0x39, 0x4a, 0x1, 0xf9,
+ 0xfa, 0x60, 0x37, 0x62, 0x98, 0x18, 0x54, 0x66, 0xfd, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74,
+ 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x0,
+ 0xed, 0x8, 0x18, 0xe6, 0xf6, 0x5f, 0x27, 0x28, 0x2d, 0xc7,
+ 0xb1, 0xc1, 0x90, 0xec, 0x18, 0x8c, 0x89, 0x33, 0x0, 0x2b,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73,
+ 0x74, 0x5f, 0x73, 0x71, 0x6c, 0x6c, 0x6f, 0x67, 0x2e, 0x63,
+ 0x0, 0x4a, 0xa6, 0x8b, 0x7c, 0x42, 0x93, 0x23, 0xb8, 0xee,
+ 0xbe, 0x6c, 0x9c, 0x2d, 0x7, 0xfc, 0x66, 0xd, 0x8d, 0x47,
+ 0xc9, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65,
+ 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x63, 0x0,
+ 0xd4, 0xc9, 0x2, 0xb5, 0xea, 0x11, 0x1a, 0xd5, 0x8a, 0x73,
+ 0x71, 0x12, 0xc2, 0x8f, 0x0, 0x38, 0x43, 0x4c, 0x85, 0xc0,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73,
+ 0x74, 0x5f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6c, 0x6f, 0x63,
+ 0x6b, 0x2e, 0x63, 0x0, 0x93, 0x6f, 0xca, 0xd0, 0xc5, 0x6f,
+ 0x6b, 0xc8, 0x58, 0x9, 0x74, 0x2f, 0x6a, 0xe1, 0xc1, 0xee,
+ 0xb8, 0xb7, 0xd2, 0xf1, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
+ 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x79, 0x73, 0x63,
+ 0x61, 0x6c, 0x6c, 0x2e, 0x63, 0x0, 0x7c, 0x8, 0x73, 0xc1,
+ 0x6d, 0x84, 0x32, 0x2, 0xf3, 0xe, 0x2d, 0xb9, 0x45, 0x9f,
+ 0xa2, 0x99, 0x75, 0xea, 0x5e, 0x68, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x63,
+ 0x6c, 0x76, 0x61, 0x72, 0x2e, 0x63, 0x0, 0x12, 0x19, 0x19,
+ 0xc, 0x3, 0x0, 0xfd, 0x5e, 0xc7, 0xa3, 0xc5, 0x84, 0x8,
+ 0xf3, 0x38, 0x43, 0xd2, 0xe, 0xee, 0x15, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74,
+ 0x68, 0x72, 0x65, 0x61, 0x64, 0x2e, 0x63, 0x0, 0x2f, 0x93,
+ 0x63, 0xb7, 0x50, 0x1e, 0x51, 0x19, 0x81, 0xfe, 0x32, 0x83,
+ 0x1f, 0xf2, 0xe8, 0xfd, 0x2f, 0x30, 0xc4, 0x93, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
+ 0x76, 0x66, 0x73, 0x2e, 0x63, 0x0, 0xfc, 0xd5, 0x77, 0x43,
+ 0x9c, 0xfd, 0x6c, 0x72, 0xdd, 0xe4, 0x83, 0x58, 0x92, 0x14,
+ 0x20, 0xcf, 0x6e, 0xf1, 0xf8, 0x6d, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x66,
+ 0x73, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x63, 0x0, 0xa,
+ 0xac, 0xc0, 0x1f, 0xe4, 0x2e, 0x77, 0xfe, 0xb8, 0x58, 0xe4,
+ 0xbe, 0xd0, 0xcb, 0x7e, 0x4, 0xa4, 0x35, 0xb2, 0x10, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74,
+ 0x5f, 0x77, 0x73, 0x64, 0x2e, 0x63, 0x0, 0x99, 0xe4, 0xa0,
+ 0x56, 0x58, 0x1f, 0x58, 0xf4, 0x53, 0x6f, 0xdb, 0x5a, 0x5d,
+ 0xf7, 0x5c, 0x74, 0x69, 0x8a, 0x81, 0x62, 0x31, 0x30, 0x30,
+ 0x36, 0x34, 0x34, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x69,
+ 0x7a, 0x65, 0x2e, 0x63, 0x0, 0xfa, 0xea, 0x5f, 0x26, 0xc7,
+ 0x9c, 0x5e, 0x18, 0x8f, 0xa8, 0x7f, 0x2f, 0xdf, 0x6f, 0xf7,
+ 0x6a, 0x7a, 0x60, 0x6, 0xc5, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x2e,
+ 0x63, 0x0, 0xf1, 0xff, 0x76, 0x6e, 0x20, 0x2a, 0x45, 0x18,
+ 0xec, 0x10, 0xe5, 0x27, 0x12, 0xc, 0xd3, 0xe, 0x83, 0xfb,
+ 0xd0, 0x34, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x75,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x0, 0x3a, 0xb1,
+ 0xab, 0x2a, 0x4b, 0x65, 0xda, 0x3f, 0x19, 0x8c, 0x15, 0x84,
+ 0xd5, 0x4d, 0x36, 0xf1, 0x8c, 0xa1, 0x21, 0x4a, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x75, 0x74, 0x66, 0x2e, 0x63,
+ 0x0, 0x6d, 0x5b, 0x1b, 0xfe, 0x40, 0xc, 0x37, 0x48, 0xaa,
+ 0x70, 0xa3, 0xb2, 0xfd, 0x5e, 0xe, 0xac, 0x5f, 0xc0, 0x4d,
+ 0xe2, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x75, 0x74,
+ 0x69, 0x6c, 0x2e, 0x63, 0x0, 0xd8, 0x3a, 0x63, 0x1, 0x5f,
+ 0xd8, 0x7d, 0xcc, 0x4f, 0xb4, 0x41, 0x66, 0xfa, 0xbf, 0x2e,
+ 0x9b, 0xc9, 0x67, 0x1e, 0xb8, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x76, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x2e, 0x63,
+ 0x0, 0x4a, 0xfb, 0x2c, 0xca, 0x64, 0xdd, 0x60, 0x76, 0x11,
+ 0x22, 0x2c, 0x7, 0x93, 0x2d, 0x12, 0xea, 0xcf, 0xa, 0x2c,
+ 0x22, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x76, 0x64,
+ 0x62, 0x65, 0x2e, 0x63, 0x0, 0xf3, 0x43, 0xe1, 0x3d, 0x4e,
+ 0x91, 0x78, 0x4b, 0x15, 0x88, 0x10, 0xc5, 0xb7, 0xd4, 0x46,
+ 0x84, 0xdf, 0xbf, 0xa2, 0xa5, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x76, 0x64, 0x62, 0x65, 0x2e, 0x68, 0x0, 0xfa,
+ 0x7b, 0x31, 0xb7, 0x27, 0xa, 0x90, 0xd4, 0xf6, 0x37, 0x36,
+ 0x5a, 0xfc, 0xc9, 0xbd, 0xa1, 0xd1, 0xb1, 0xe1, 0xd6, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x76, 0x64, 0x62, 0x65,
+ 0x49, 0x6e, 0x74, 0x2e, 0x68, 0x0, 0x3a, 0x5b, 0x40, 0x28,
+ 0xbb, 0xd6, 0xc9, 0x56, 0x10, 0xd7, 0xc, 0xce, 0x3, 0x69,
+ 0xdf, 0xcd, 0x60, 0x7a, 0xa9, 0x0, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x76, 0x64, 0x62, 0x65, 0x61, 0x70, 0x69,
+ 0x2e, 0x63, 0x0, 0x7c, 0x86, 0x1e, 0x2d, 0x47, 0x21, 0x8c,
+ 0x91, 0x63, 0x31, 0x77, 0x77, 0xc3, 0x7, 0x21, 0x99, 0xe9,
+ 0xb4, 0x2, 0x80, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x76, 0x64, 0x62, 0x65, 0x61, 0x75, 0x78, 0x2e, 0x63, 0x0,
+ 0x2c, 0x42, 0x69, 0xa5, 0x9e, 0x6d, 0xbc, 0xe8, 0x67, 0x1c,
+ 0x47, 0x4f, 0x34, 0x61, 0x90, 0xbe, 0x2a, 0xe, 0x18, 0x51,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x76, 0x64, 0x62,
+ 0x65, 0x62, 0x6c, 0x6f, 0x62, 0x2e, 0x63, 0x0, 0x2e, 0x8f,
+ 0xd8, 0xee, 0x74, 0x47, 0xe6, 0x46, 0x46, 0xe3, 0x49, 0x4b,
+ 0x4c, 0x4, 0x1d, 0x3a, 0x4a, 0xbb, 0x8, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x76, 0x64, 0x62, 0x65, 0x6d,
+ 0x65, 0x6d, 0x2e, 0x63, 0x0, 0x8f, 0xc2, 0x22, 0xe2, 0xde,
+ 0x20, 0x50, 0x14, 0x50, 0xec, 0xea, 0x9d, 0x4e, 0xbf, 0xaa,
+ 0xc9, 0x81, 0x4a, 0xae, 0x59, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x76, 0x64, 0x62, 0x65, 0x73, 0x6f, 0x72, 0x74,
+ 0x2e, 0x63, 0x0, 0xfd, 0xfc, 0x4a, 0x79, 0xdd, 0xc9, 0x6e,
+ 0x59, 0x9b, 0x1b, 0xe, 0xeb, 0xac, 0xbd, 0xb8, 0x45, 0xc6,
+ 0x38, 0x13, 0xb2, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x76, 0x64, 0x62, 0x65, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e,
+ 0x63, 0x0, 0x35, 0x62, 0x77, 0xe8, 0xd2, 0x3b, 0xca, 0xdb,
+ 0x67, 0x6b, 0x59, 0xd1, 0xa4, 0xdc, 0xf8, 0x42, 0xfd, 0xc4,
+ 0xc9, 0x72, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x76,
+ 0x74, 0x61, 0x62, 0x2e, 0x63, 0x0, 0x95, 0x82, 0x2, 0xc3,
+ 0x1e, 0x24, 0x15, 0xb, 0x60, 0xf1, 0xa, 0x8a, 0xf, 0x74,
+ 0x41, 0xaf, 0xac, 0x3f, 0xbb, 0x1c, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x77, 0x61, 0x6c, 0x2e, 0x63, 0x0, 0xe6,
+ 0x42, 0xea, 0x21, 0x5, 0xb5, 0xc5, 0x4a, 0xf3, 0x5, 0x88,
+ 0x9, 0x62, 0x69, 0xab, 0x75, 0xcb, 0xef, 0x8f, 0xf2, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x77, 0x61, 0x6c, 0x2e,
+ 0x68, 0x0, 0x9, 0x25, 0x46, 0x35, 0x4b, 0x34, 0xc0, 0xab,
+ 0x3d, 0x20, 0x5, 0x6a, 0x7f, 0x8a, 0x8a, 0x52, 0xe4, 0xd0,
+ 0xb5, 0xf5, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x77,
+ 0x61, 0x6c, 0x6b, 0x65, 0x72, 0x2e, 0x63, 0x0, 0xe7, 0x1e,
+ 0xd2, 0xac, 0x48, 0x4c, 0x91, 0x6c, 0x1c, 0xc1, 0x0, 0x7e,
+ 0x5e, 0x5, 0xda, 0x47, 0x1c, 0xb4, 0x95, 0x99, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65,
+ 0x2e, 0x63, 0x0, 0xe6, 0x14, 0xf4, 0xa6, 0xd8, 0x64, 0xe7,
+ 0xe, 0xc4, 0x32, 0x8d, 0xb, 0xdb, 0x25, 0x4e, 0x3a, 0xc9,
+ 0xf0, 0xd2, 0x87,
+ }
+ obj := &SortReadObject{
+ t: plumbing.TreeObject,
+ h: plumbing.ZeroHash,
+ cont: cont,
+ sz: 5313,
+ }
+
+ expected := &Tree{
+ Entries: []TreeEntry{
+ {
+ Name: "alter.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xa4, 0x9d, 0x33, 0x49, 0xd7, 0xe2, 0x3f, 0xb5, 0x81, 0x19, 0x4f, 0x4c, 0xb5, 0x9a, 0xc0, 0xd5, 0x1b, 0x2, 0x1f, 0x78},
+ },
+ {
+ Name: "analyze.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x9a, 0x3e, 0x95, 0x97, 0xdb, 0xb, 0x3, 0x20, 0x77, 0xc9, 0x1d, 0x96, 0x9d, 0x22, 0xc6, 0x27, 0x3f, 0x70, 0x2a, 0xc},
+ },
+ {
+ Name: "attach.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xb8, 0xe1, 0x21, 0x99, 0xb5, 0x7d, 0xe8, 0x11, 0xea, 0xe0, 0xd0, 0x61, 0x42, 0xd5, 0xac, 0x4f, 0xd4, 0x30, 0xb1, 0xd8},
+ },
+ {
+ Name: "auth.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd3, 0x8b, 0xb8, 0x36, 0xa7, 0x84, 0xfb, 0xfa, 0xb6, 0xab, 0x7b, 0x3, 0xd4, 0xe6, 0xdd, 0x43, 0xed, 0xc4, 0x1f, 0xa7},
+ },
+ {
+ Name: "backup.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x25, 0x2f, 0x61, 0xcf, 0xca, 0xa8, 0xfc, 0xf3, 0x13, 0x7e, 0x8, 0xed, 0x68, 0x47, 0xdc, 0xfe, 0x1d, 0xc1, 0xde, 0x54},
+ },
+ {
+ Name: "bitvec.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x52, 0x18, 0x4a, 0xa9, 0x64, 0xce, 0x18, 0x98, 0xf3, 0x5d, 0x1b, 0x3d, 0x87, 0x87, 0x1c, 0x2d, 0xe, 0xf4, 0xc5, 0x3d},
+ },
+ {
+ Name: "btmutex.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd8, 0x7d, 0x4d, 0x5f, 0xee, 0xb6, 0x30, 0x7a, 0xec, 0xdc, 0x9a, 0x83, 0x11, 0x14, 0x89, 0xab, 0x30, 0xc6, 0x78, 0xc3},
+ },
+ {
+ Name: "btree.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x3c, 0xa6, 0x5, 0x83, 0xe3, 0xc8, 0xe3, 0x12, 0x0, 0xf9, 0x73, 0xe0, 0xe9, 0xc4, 0x53, 0x62, 0x58, 0xb2, 0x64, 0x39},
+ },
+ {
+ Name: "btree.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xac, 0xe0, 0xf8, 0xcd, 0x21, 0x77, 0x70, 0xa2, 0xf6, 0x6b, 0x2e, 0xb8, 0x71, 0xbb, 0xc5, 0xfd, 0xc6, 0xfc, 0x2b, 0x68},
+ },
+ {
+ Name: "btreeInt.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xce, 0x3c, 0x54, 0x93, 0xf8, 0xca, 0xd0, 0xbc, 0x54, 0x8a, 0xe8, 0xe4, 0x4e, 0x51, 0x28, 0x31, 0xd8, 0xfa, 0xc4, 0x31},
+ },
+ {
+ Name: "build.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x3c, 0x91, 0xcd, 0xcf, 0xdb, 0x7b, 0x1, 0x7c, 0xbc, 0x2d, 0x5c, 0x29, 0x57, 0x1a, 0x98, 0x27, 0xd, 0xe0, 0x71, 0xe6},
+ },
+ {
+ Name: "callback.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd4, 0xc, 0x65, 0xcb, 0x92, 0x45, 0x80, 0x29, 0x6a, 0xd0, 0x69, 0xa0, 0x4b, 0xf9, 0xc9, 0xe9, 0x53, 0x4e, 0xca, 0xa7},
+ },
+ {
+ Name: "complete.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x9e, 0x91, 0x40, 0x8, 0x5c, 0x0, 0x46, 0xed, 0x3b, 0xf6, 0xf4, 0x48, 0x52, 0x20, 0x69, 0x2d, 0xca, 0x17, 0x43, 0xc5},
+ },
+ {
+ Name: "crypto.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x25, 0x51, 0xe6, 0xba, 0x2, 0x39, 0xf8, 0x5a, 0x35, 0x77, 0x96, 0xa8, 0xdd, 0xa8, 0xca, 0x3e, 0x29, 0x70, 0x93, 0xf8},
+ },
+ {
+ Name: "crypto.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf7, 0x1f, 0x53, 0x2c, 0xdc, 0x44, 0x8f, 0xa, 0x1d, 0xd5, 0xc6, 0xef, 0xf5, 0xfb, 0xd3, 0x3a, 0x91, 0x55, 0xaa, 0x97},
+ },
+ {
+ Name: "crypto_cc.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x53, 0x7d, 0xf7, 0xe3, 0xb3, 0x6a, 0xb5, 0xcf, 0xdd, 0x6f, 0xca, 0x40, 0x28, 0xeb, 0xca, 0xe1, 0x86, 0x87, 0xd6, 0x4d},
+ },
+ {
+ Name: "crypto_impl.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xa5, 0x89, 0x27, 0xc7, 0x6e, 0xf6, 0x20, 0x56, 0x77, 0xbe, 0x5c, 0x1a, 0x8e, 0x80, 0xc9, 0x83, 0x56, 0xb3, 0xa9, 0xd3},
+ },
+ {
+ Name: "crypto_libtomcrypt.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x1a, 0x33, 0x83, 0xe0, 0x1, 0xa7, 0x21, 0x11, 0xc3, 0xf6, 0x61, 0x92, 0x22, 0xb0, 0x65, 0xf4, 0xbd, 0x1, 0xb, 0xe1},
+ },
+ {
+ Name: "crypto_openssl.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd0, 0x19, 0x81, 0x3b, 0x47, 0x6c, 0x52, 0xd0, 0x20, 0xe2, 0xc0, 0xac, 0xd5, 0x24, 0xe9, 0xea, 0x3d, 0xf, 0xb9, 0xfe},
+ },
+ {
+ Name: "ctime.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x60, 0x59, 0x5f, 0xf8, 0x8d, 0x92, 0xf7, 0x8, 0x26, 0x4, 0xfb, 0xd9, 0xdf, 0x9a, 0xfe, 0xa1, 0x6a, 0xe8, 0x6f, 0xf},
+ },
+ {
+ Name: "date.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x75, 0x8d, 0xd7, 0xc8, 0x9b, 0xca, 0x39, 0x37, 0xa9, 0xd, 0x70, 0x6e, 0xa9, 0x82, 0xce, 0x3a, 0xcf, 0x11, 0xd1, 0x83},
+ },
+ {
+ Name: "delete.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x63, 0x4e, 0x11, 0x55, 0x63, 0xae, 0x12, 0xba, 0x65, 0x58, 0xcc, 0xc5, 0x12, 0xae, 0xd6, 0x31, 0xc0, 0x66, 0xba, 0xd8},
+ },
+ {
+ Name: "expr.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x66, 0x3, 0x97, 0xe0, 0x78, 0xae, 0x48, 0xb2, 0xe7, 0x17, 0x5e, 0x33, 0x85, 0x67, 0x78, 0x19, 0x72, 0x2d, 0xdd, 0x6c},
+ },
+ {
+ Name: "fault.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xc3, 0x2, 0x8c, 0x4f, 0x93, 0x6e, 0xdf, 0x96, 0x71, 0x2d, 0xbe, 0x73, 0xa0, 0x76, 0x62, 0xf0, 0xa2, 0x6b, 0x1d, 0xa},
+ },
+ {
+ Name: "fkey.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xac, 0x35, 0xbc, 0x19, 0x4c, 0xde, 0xb1, 0x27, 0x98, 0x9b, 0x9, 0x40, 0x35, 0xce, 0xe0, 0x6f, 0x57, 0x37, 0x6f, 0x5e},
+ },
+ {
+ Name: "func.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xc0, 0x2f, 0x9, 0x6a, 0xda, 0xd5, 0xbc, 0xe9, 0xac, 0x83, 0xd3, 0x5f, 0xf, 0x46, 0x9, 0xd6, 0xf6, 0xd4, 0x3b, 0xe5},
+ },
+ {
+ Name: "global.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x7b, 0x2, 0xcf, 0x21, 0x30, 0xe0, 0xd1, 0xa7, 0xb8, 0x89, 0xd8, 0x44, 0xc, 0xcc, 0x82, 0x8, 0xf7, 0xb6, 0x7b, 0xf9},
+ },
+ {
+ Name: "hash.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe8, 0x1d, 0xcf, 0x95, 0xe4, 0x38, 0x48, 0xfa, 0x70, 0x86, 0xb7, 0xf7, 0x81, 0xc0, 0x90, 0xad, 0xc7, 0xe6, 0xca, 0x8e},
+ },
+ {
+ Name: "hash.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x82, 0xb7, 0xc5, 0x8c, 0x71, 0x9, 0xb, 0x54, 0x7e, 0x10, 0x17, 0x42, 0xaa, 0x9, 0x51, 0x73, 0x9f, 0xf2, 0xee, 0xe7},
+ },
+ {
+ Name: "hwtime.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xb8, 0xbc, 0x5a, 0x29, 0x5b, 0xe3, 0xfa, 0xc8, 0x35, 0x1f, 0xa9, 0xf0, 0x8a, 0x77, 0x57, 0x9d, 0x59, 0xc9, 0xa8, 0xe4},
+ },
+ {
+ Name: "insert.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x9a, 0x56, 0x61, 0xf5, 0x9a, 0x72, 0x95, 0x2b, 0xe6, 0xc1, 0x67, 0xa0, 0xc2, 0xdb, 0x15, 0x9b, 0x91, 0xb7, 0x1f, 0xae},
+ },
+ {
+ Name: "journal.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xfe, 0xd2, 0x7b, 0xe3, 0xe3, 0x80, 0x55, 0xd2, 0x20, 0x43, 0x95, 0xcd, 0xe6, 0xff, 0xc9, 0x45, 0x89, 0xfb, 0xf5, 0xe8},
+ },
+ {
+ Name: "legacy.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x94, 0x64, 0x9a, 0xe7, 0x5, 0xab, 0x93, 0x85, 0x10, 0x8d, 0xd, 0x88, 0x7a, 0xf0, 0x75, 0x92, 0x89, 0xfb, 0x23, 0xcb},
+ },
+ {
+ Name: "lempar.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x2a, 0xfa, 0xa6, 0xce, 0xa6, 0xd8, 0x29, 0x60, 0x2c, 0x27, 0x86, 0xc1, 0xf8, 0xa3, 0x7f, 0x56, 0x7c, 0xf6, 0xfd, 0x53},
+ },
+ {
+ Name: "loadext.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xcd, 0xcf, 0x6a, 0x93, 0xb8, 0xc4, 0xf, 0x91, 0x4b, 0x94, 0x24, 0xe, 0xf1, 0x4c, 0xb4, 0xa3, 0xa, 0x37, 0xec, 0xa1},
+ },
+ {
+ Name: "main.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x39, 0xf6, 0x4, 0x21, 0xe6, 0x81, 0x27, 0x7c, 0xc3, 0xdb, 0xa0, 0x9a, 0xbe, 0x7c, 0xf7, 0x90, 0xd5, 0x28, 0xf5, 0xc3},
+ },
+ {
+ Name: "malloc.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x35, 0xa4, 0x4e, 0x5f, 0x61, 0xc2, 0xe4, 0x4c, 0x48, 0x1c, 0x62, 0x51, 0xbd, 0xa, 0xae, 0x7a, 0xcd, 0xa4, 0xde, 0xb},
+ },
+ {
+ Name: "mem0.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd, 0xb, 0x66, 0x67, 0xd6, 0xa, 0x95, 0x5a, 0x6, 0x96, 0xdf, 0x62, 0x89, 0xb4, 0x91, 0x78, 0x96, 0x93, 0x43, 0xaa},
+ },
+ {
+ Name: "mem1.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x35, 0x78, 0x49, 0x6f, 0x33, 0x3, 0x7, 0xb2, 0x31, 0xdf, 0xb5, 0x3c, 0xc, 0x2e, 0x1c, 0x6b, 0x32, 0x3d, 0x79, 0x1e},
+ },
+ {
+ Name: "mem2.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x26, 0x44, 0x8e, 0xa8, 0xaa, 0xe0, 0x36, 0x6a, 0xf0, 0x54, 0x1a, 0xfe, 0xa4, 0x79, 0xb, 0x42, 0xf4, 0xa6, 0x9b, 0x5a},
+ },
+ {
+ Name: "mem3.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x1a, 0x1b, 0x79, 0x1f, 0x28, 0xf8, 0xcf, 0x3c, 0xe4, 0xf9, 0xa3, 0x5c, 0xda, 0xd7, 0xb7, 0x10, 0x75, 0x68, 0xc7, 0x15},
+ },
+ {
+ Name: "mem5.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x78, 0x3c, 0xef, 0x61, 0x76, 0xc5, 0x9c, 0xbf, 0x30, 0x91, 0x46, 0x31, 0x9, 0x5a, 0x1a, 0x54, 0xf4, 0xe4, 0x2e, 0x8},
+ },
+ {
+ Name: "memjournal.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x5, 0x72, 0x59, 0x48, 0xf6, 0x5d, 0x42, 0x7b, 0x7, 0xf7, 0xf9, 0x29, 0xac, 0xa3, 0xff, 0x22, 0x4b, 0x17, 0x53, 0xdf},
+ },
+ {
+ Name: "mutex.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xb5, 0x67, 0xe7, 0xc2, 0x7e, 0xf2, 0x4, 0x10, 0x86, 0xaf, 0xe0, 0xf6, 0x96, 0x66, 0xe2, 0x7b, 0xf5, 0x9, 0x8a, 0x59},
+ },
+ {
+ Name: "mutex.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x9, 0x78, 0x81, 0x22, 0x52, 0x77, 0x89, 0xa, 0x9c, 0x36, 0xc2, 0x4d, 0x41, 0xf6, 0x11, 0x4d, 0x64, 0xc0, 0x6d, 0xb3},
+ },
+ {
+ Name: "mutex_noop.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x45, 0x6e, 0x82, 0xa2, 0x5e, 0x27, 0x1b, 0x6, 0x14, 0xe7, 0xf4, 0xf8, 0x3c, 0x22, 0x85, 0x53, 0xb7, 0xfa, 0x1, 0x58},
+ },
+ {
+ Name: "mutex_unix.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xec, 0xa7, 0x29, 0x58, 0x31, 0xc2, 0xf0, 0xee, 0x48, 0xba, 0x54, 0xd0, 0x62, 0x91, 0x4d, 0x6, 0xa1, 0xdd, 0x8e, 0xbe},
+ },
+ {
+ Name: "mutex_w32.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x27, 0xd1, 0xa, 0xf5, 0xbd, 0x33, 0x1b, 0xdb, 0x97, 0x3f, 0x61, 0x45, 0xb7, 0x4f, 0x72, 0xb6, 0x7, 0xcf, 0xc4, 0x6e},
+ },
+ {
+ Name: "notify.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xfc, 0xab, 0x5b, 0xfa, 0xf0, 0x19, 0x8, 0xd3, 0xde, 0x93, 0xfa, 0x88, 0xb5, 0xea, 0xe9, 0xe9, 0x6c, 0xa3, 0xc8, 0xe8},
+ },
+ {
+ Name: "os.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xbe, 0x2e, 0xa4, 0xcf, 0xc0, 0x19, 0x59, 0x93, 0xa3, 0x40, 0xc9, 0x2, 0xae, 0xdd, 0xf1, 0xbe, 0x4b, 0x8e, 0xd7, 0x3a},
+ },
+ {
+ Name: "os.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x7, 0xa, 0x2d, 0xdd, 0x17, 0xf7, 0x71, 0xf9, 0x8f, 0xf8, 0xcc, 0xd6, 0xf0, 0x33, 0xbd, 0xac, 0xc5, 0xe9, 0xf6, 0xc},
+ },
+ {
+ Name: "os_common.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf6, 0xc3, 0xe7, 0xff, 0x89, 0x46, 0x30, 0x86, 0x40, 0x18, 0x22, 0xf4, 0x81, 0xe7, 0xe3, 0xb8, 0x7b, 0x2c, 0x78, 0xc7},
+ },
+ {
+ Name: "os_unix.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xab, 0xc2, 0x3a, 0x45, 0x2e, 0x72, 0xf7, 0x1c, 0x76, 0xaf, 0xa9, 0x98, 0x3c, 0x3a, 0xd9, 0xd4, 0x25, 0x61, 0x6c, 0x6d},
+ },
+ {
+ Name: "os_win.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xae, 0xb0, 0x88, 0x14, 0xb3, 0xda, 0xbe, 0x81, 0xb8, 0x4c, 0xda, 0x91, 0x85, 0x82, 0xb0, 0xf, 0xfd, 0x86, 0xe4, 0x87},
+ },
+ {
+ Name: "pager.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x61, 0x72, 0x7f, 0xaa, 0x9c, 0xf, 0x3d, 0x56, 0x62, 0x65, 0xbe, 0x7e, 0xec, 0x5b, 0x2a, 0x35, 0xf6, 0xa4, 0xbc, 0x9f},
+ },
+ {
+ Name: "pager.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x6f, 0x65, 0x91, 0x36, 0xe2, 0x76, 0x7, 0x9d, 0xa4, 0x3a, 0x2e, 0x39, 0xe1, 0xb6, 0x86, 0x37, 0xec, 0xad, 0xcf, 0x68},
+ },
+ {
+ Name: "parse.y", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x83, 0x10, 0xb2, 0x69, 0x89, 0xb0, 0x5b, 0xed, 0x1e, 0x1b, 0x3, 0xda, 0x80, 0xf5, 0xc0, 0xa5, 0x2e, 0x9a, 0xd1, 0xd2},
+ },
+ {
+ Name: "pcache.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x48, 0x2a, 0x18, 0x8b, 0xee, 0x19, 0x91, 0xbc, 0x8a, 0xda, 0xc9, 0x6a, 0x19, 0x3a, 0x53, 0xe5, 0x46, 0x2a, 0x8c, 0x10},
+ },
+ {
+ Name: "pcache.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf4, 0xd4, 0xad, 0x71, 0xc1, 0xd, 0x78, 0xc6, 0xda, 0xbd, 0xe2, 0x52, 0x15, 0xcd, 0x41, 0x5a, 0x76, 0x1, 0x48, 0xca},
+ },
+ {
+ Name: "pcache1.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x41, 0x47, 0xd2, 0xef, 0xf5, 0x5b, 0xdd, 0x9f, 0xf7, 0xc6, 0x86, 0xc, 0x60, 0x18, 0x10, 0x20, 0x16, 0x6c, 0x5f, 0x50},
+ },
+ {
+ Name: "pragma.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x22, 0x97, 0x71, 0x69, 0x61, 0x7d, 0x49, 0x22, 0xb3, 0x99, 0x3f, 0x76, 0x9d, 0x90, 0xfa, 0x7b, 0xc4, 0x41, 0xea, 0x50},
+ },
+ {
+ Name: "prepare.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd7, 0x8d, 0x83, 0xcb, 0xd8, 0x78, 0x97, 0xf5, 0x73, 0x30, 0x3f, 0x9f, 0x57, 0xab, 0x8d, 0xe0, 0x24, 0xa6, 0xe3, 0xf8},
+ },
+ {
+ Name: "printf.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x9f, 0x68, 0xd2, 0x4, 0xff, 0xdc, 0x9f, 0x3d, 0x42, 0x7f, 0x80, 0xa8, 0x23, 0x9a, 0x7f, 0xa3, 0xa9, 0x8a, 0xec, 0xbd},
+ },
+ {
+ Name: "random.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x23, 0x4e, 0xbd, 0xf6, 0x58, 0xf4, 0x36, 0xcc, 0x7c, 0x68, 0xf0, 0x27, 0xc4, 0x8b, 0xe, 0x1b, 0x9b, 0xa3, 0x4e, 0x98},
+ },
+ {
+ Name: "resolve.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x91, 0xef, 0xca, 0xa1, 0xa1, 0x6b, 0xfc, 0x98, 0xfb, 0x35, 0xd8, 0x5c, 0xad, 0x15, 0x6b, 0x93, 0x53, 0x3e, 0x4e, 0x6},
+ },
+ {
+ Name: "rowset.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x57, 0x61, 0xf9, 0x85, 0x50, 0xb1, 0x76, 0xcc, 0xe1, 0x1d, 0xcb, 0xce, 0xc9, 0x38, 0x99, 0xa0, 0x75, 0xbb, 0x64, 0xfd},
+ },
+ {
+ Name: "select.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf3, 0xf1, 0x49, 0x9, 0x63, 0x95, 0x5b, 0x8e, 0xd0, 0xc9, 0xfe, 0x6e, 0x1e, 0xec, 0x83, 0x6c, 0x1a, 0x52, 0x94, 0xb4},
+ },
+ {
+ Name: "shell.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x1b, 0xe2, 0x87, 0x1f, 0xed, 0x9a, 0x1f, 0xdf, 0x1d, 0xf7, 0x19, 0x8e, 0x11, 0x25, 0x36, 0x0, 0xec, 0xba, 0x76, 0xcc},
+ },
+ {
+ Name: "sqlcipher.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x82, 0x75, 0x30, 0x95, 0xcd, 0x17, 0x23, 0xc5, 0xff, 0x4f, 0x11, 0x15, 0xe4, 0x97, 0x55, 0x91, 0xee, 0x34, 0xf5, 0xce},
+ },
+ {
+ Name: "sqlite.h.in", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x66, 0x8, 0x82, 0x31, 0x75, 0xde, 0x5b, 0x6a, 0xd, 0x37, 0x8f, 0xdb, 0xc, 0x38, 0x18, 0xb6, 0xab, 0x4f, 0xbf, 0x8e},
+ },
+ {
+ Name: "sqlite3.rc", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x96, 0x98, 0x76, 0xda, 0x1e, 0x57, 0x14, 0x3d, 0xe0, 0xb4, 0xd1, 0xc7, 0x62, 0x9f, 0xd3, 0x35, 0x6f, 0x2e, 0x1c, 0x96},
+ },
+ {
+ Name: "sqlite3ext.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x92, 0x8b, 0xb3, 0xba, 0xd9, 0xdd, 0x64, 0x3c, 0x30, 0x1d, 0xd2, 0xb0, 0xac, 0x22, 0x28, 0x7a, 0x81, 0x28, 0x48, 0x84},
+ },
+ {
+ Name: "sqliteInt.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x59, 0x50, 0xf2, 0x37, 0xd9, 0xf9, 0xf2, 0xd3, 0xef, 0x6b, 0xd8, 0xbe, 0x34, 0x2d, 0xcf, 0x64, 0x89, 0x22, 0x51, 0x42},
+ },
+ {
+ Name: "sqliteLimit.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xc7, 0xae, 0xe5, 0x3c, 0xeb, 0xca, 0x94, 0xda, 0x51, 0xe7, 0x1a, 0x82, 0x2e, 0xa5, 0xa6, 0xde, 0xb9, 0x3, 0x85, 0xdf},
+ },
+ {
+ Name: "status.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x28, 0x34, 0x9e, 0x6d, 0x3d, 0x20, 0x88, 0xe0, 0x0, 0x3b, 0x76, 0xf8, 0xa, 0x89, 0x54, 0xfa, 0xec, 0x59, 0x30, 0xba},
+ },
+ {
+ Name: "table.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x26, 0xbb, 0xfb, 0x4f, 0x45, 0x6c, 0x42, 0x98, 0x25, 0x29, 0xea, 0x1a, 0x63, 0xa0, 0x17, 0x51, 0xdd, 0x3e, 0xe9, 0x5a},
+ },
+ {
+ Name: "tclsqlite.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf1, 0xbb, 0x29, 0x21, 0xda, 0xc, 0x68, 0xa4, 0xf1, 0xc8, 0xe1, 0x5c, 0xf5, 0x66, 0xb2, 0x33, 0xe9, 0x2a, 0x51, 0x9f},
+ },
+ {
+ Name: "test1.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xa6, 0x38, 0xe4, 0x80, 0xad, 0xdf, 0x14, 0x43, 0x9c, 0xdf, 0xa4, 0xee, 0x16, 0x4d, 0xc3, 0x1b, 0x79, 0xf8, 0xbc, 0xac},
+ },
+ {
+ Name: "test2.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd1, 0x30, 0xe9, 0xd0, 0x1b, 0x70, 0x24, 0xa5, 0xec, 0x6d, 0x73, 0x5, 0x92, 0xee, 0x4d, 0x1f, 0xb0, 0x2c, 0xfd, 0xb4},
+ },
+ {
+ Name: "test3.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe3, 0xed, 0x31, 0xc, 0x81, 0x4, 0xfe, 0x36, 0x21, 0xce, 0xbb, 0xf, 0x51, 0xd1, 0x1, 0x45, 0x1, 0x8d, 0x4f, 0xac},
+ },
+ {
+ Name: "test4.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xa6, 0x37, 0x5c, 0x7c, 0xc4, 0x3, 0xf6, 0xc, 0xaa, 0xb7, 0xe9, 0x59, 0x53, 0x3e, 0x3d, 0xb1, 0xff, 0x75, 0xa, 0xe4},
+ },
+ {
+ Name: "test5.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x30, 0x3d, 0x12, 0x5, 0xb2, 0x26, 0x28, 0x42, 0x3d, 0x98, 0x6f, 0x71, 0xe2, 0x7c, 0x7c, 0xf7, 0x14, 0xa7, 0x45, 0xa6},
+ },
+ {
+ Name: "test6.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xc1, 0x51, 0xea, 0x42, 0x98, 0x9b, 0xb, 0xe2, 0x4e, 0xe4, 0xb9, 0xa4, 0xbe, 0x37, 0x8b, 0x4f, 0x63, 0x6d, 0xb6, 0x41},
+ },
+ {
+ Name: "test7.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x3c, 0xd4, 0xa2, 0x24, 0xd7, 0xe8, 0xe1, 0x6b, 0xd7, 0xcb, 0xe4, 0x9e, 0x2d, 0x3e, 0x94, 0xce, 0x9b, 0x17, 0xbd, 0x76},
+ },
+ {
+ Name: "test8.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xc5, 0x73, 0x93, 0x32, 0xd4, 0x6e, 0x57, 0x12, 0x1d, 0xa2, 0x7c, 0x3e, 0x88, 0xfd, 0xe7, 0x5a, 0xeb, 0x87, 0x10, 0xf7},
+ },
+ {
+ Name: "test9.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe5, 0x99, 0x3e, 0x8f, 0xf7, 0x8f, 0x61, 0xc2, 0x43, 0x5b, 0x6f, 0x97, 0xa3, 0xb4, 0x63, 0xe2, 0x27, 0xc7, 0x67, 0xac},
+ },
+ {
+ Name: "test_async.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xb0, 0xb9, 0x43, 0x18, 0x5b, 0xfc, 0x23, 0xc1, 0x7f, 0xd0, 0x8f, 0x55, 0x76, 0x8c, 0xac, 0x12, 0xa9, 0xf5, 0x69, 0x51},
+ },
+ {
+ Name: "test_autoext.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xb5, 0x1, 0x3f, 0x31, 0x73, 0xa2, 0x17, 0x6e, 0x2d, 0x9f, 0xc, 0xaa, 0x99, 0x19, 0x30, 0x36, 0xbf, 0xc3, 0x7e, 0x91},
+ },
+ {
+ Name: "test_backup.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe9, 0x67, 0x42, 0x4a, 0x29, 0xf, 0x73, 0x8a, 0xec, 0xfd, 0xac, 0x57, 0x8e, 0x9b, 0x87, 0xa4, 0xc4, 0xae, 0x8d, 0x7f},
+ },
+ {
+ Name: "test_btree.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xdb, 0x72, 0x88, 0x9b, 0x2a, 0xfb, 0x62, 0x72, 0x82, 0x8d, 0xda, 0x86, 0x6d, 0xcc, 0xf1, 0x22, 0xa4, 0x9a, 0x72, 0x99},
+ },
+ {
+ Name: "test_config.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x53, 0x47, 0x27, 0xa0, 0x80, 0x42, 0xb6, 0xca, 0xd6, 0x7e, 0x26, 0x7e, 0x87, 0xb4, 0x3, 0xa4, 0x1a, 0x73, 0xb2, 0x99},
+ },
+ {
+ Name: "test_demovfs.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x63, 0x76, 0x27, 0x7, 0x1d, 0x9e, 0x28, 0xf4, 0xb3, 0x45, 0x1b, 0xbb, 0xdd, 0xf8, 0x8, 0xd1, 0xa9, 0x12, 0x0, 0xf8},
+ },
+ {
+ Name: "test_devsym.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x21, 0xf0, 0xf6, 0x84, 0xd8, 0x61, 0x11, 0x67, 0x70, 0xde, 0xfc, 0xde, 0xcd, 0x53, 0x2b, 0xa3, 0xee, 0xab, 0xa9, 0x75},
+ },
+ {
+ Name: "test_fs.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x47, 0x8c, 0xad, 0x80, 0xb1, 0x6a, 0x90, 0x9b, 0x23, 0xbd, 0x3, 0xc2, 0xda, 0xd8, 0xb4, 0x49, 0xa7, 0x45, 0x87, 0xa1},
+ },
+ {
+ Name: "test_func.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x6f, 0x9b, 0xb0, 0x3d, 0xc8, 0x8a, 0x21, 0xd6, 0x58, 0xbf, 0x99, 0x99, 0xba, 0xf6, 0x6d, 0xc1, 0xd5, 0x2e, 0xbc, 0x54},
+ },
+ {
+ Name: "test_hexio.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xb2, 0xb, 0x5c, 0xe7, 0x30, 0xab, 0x7f, 0xa8, 0x0, 0xd2, 0xd0, 0xcc, 0x38, 0xc7, 0x72, 0x75, 0x59, 0x3e, 0xbd, 0xbb},
+ },
+ {
+ Name: "test_init.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe3, 0x72, 0x4d, 0x8b, 0xe3, 0x14, 0xdb, 0x9, 0xee, 0xa8, 0x4, 0xb, 0x9d, 0xdf, 0xc8, 0xa8, 0xbe, 0xee, 0x22, 0x91},
+ },
+ {
+ Name: "test_intarray.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf5, 0xc3, 0xd9, 0xe4, 0x5, 0x9a, 0x16, 0x56, 0x7, 0x34, 0x7, 0xe4, 0x3a, 0x92, 0x11, 0x79, 0x99, 0x69, 0x7b, 0x93},
+ },
+ {
+ Name: "test_intarray.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x69, 0x13, 0x37, 0xd1, 0xae, 0xd6, 0x37, 0x15, 0xd6, 0x2e, 0x76, 0x26, 0x6f, 0xf, 0x3b, 0x50, 0x8b, 0x1, 0xa, 0x34},
+ },
+ {
+ Name: "test_journal.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe8, 0x70, 0x1a, 0x4e, 0xea, 0xdb, 0x8e, 0xad, 0x16, 0x9d, 0x60, 0x6, 0x40, 0x7d, 0x54, 0xa8, 0x98, 0x59, 0x2d, 0x70},
+ },
+ {
+ Name: "test_loadext.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x11, 0x37, 0xe3, 0xa9, 0xaa, 0xe9, 0x29, 0x6, 0xb8, 0x28, 0x9f, 0x6c, 0x3d, 0xaa, 0x61, 0xf0, 0xd0, 0x70, 0xf5, 0x5a},
+ },
+ {
+ Name: "test_malloc.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xcf, 0x98, 0xa8, 0xfb, 0x21, 0x82, 0xc0, 0xba, 0xf5, 0xa, 0xd5, 0x79, 0x79, 0xb6, 0x75, 0xbb, 0x70, 0x7a, 0x93, 0xb0},
+ },
+ {
+ Name: "test_multiplex.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x62, 0x45, 0x41, 0xb3, 0x2a, 0x10, 0xd2, 0x1a, 0x2f, 0xd1, 0xa, 0x35, 0xee, 0x66, 0x32, 0xbd, 0xac, 0x55, 0x2d, 0x41},
+ },
+ {
+ Name: "test_multiplex.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xb7, 0xe1, 0xaf, 0xea, 0x5f, 0xd7, 0x8b, 0x87, 0x58, 0x2, 0x65, 0xf8, 0x4c, 0x81, 0x61, 0x2c, 0xbd, 0x2, 0x5b, 0xaf},
+ },
+ {
+ Name: "test_mutex.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xc9, 0xb4, 0xa2, 0x9a, 0xb7, 0x5c, 0x77, 0xea, 0x5f, 0x36, 0xb5, 0x19, 0x32, 0x56, 0xd7, 0xf, 0xe6, 0x58, 0xe, 0x95},
+ },
+ {
+ Name: "test_onefile.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x69, 0x86, 0x74, 0x41, 0xb8, 0xcc, 0x9a, 0x62, 0x1a, 0xf3, 0x24, 0x13, 0xfc, 0x63, 0xda, 0x80, 0x99, 0x37, 0x64, 0xf4},
+ },
+ {
+ Name: "test_osinst.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x53, 0x14, 0x33, 0x31, 0x3e, 0xe3, 0x6c, 0x7, 0xeb, 0x21, 0xc0, 0x2f, 0x31, 0x15, 0xcb, 0x7a, 0x37, 0x48, 0x6c, 0x79},
+ },
+ {
+ Name: "test_pcache.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x8f, 0xcf, 0xe7, 0xe2, 0x6e, 0x3f, 0xf1, 0x74, 0x96, 0xb8, 0x40, 0xf5, 0xd6, 0x3c, 0x75, 0x78, 0x3a, 0xff, 0x81, 0x62},
+ },
+ {Name: "test_quota.c", Mode: 0x81a4, Hash: plumbing.Hash{
+ 0xe5, 0x90, 0x99, 0x6c, 0xa4, 0xb8, 0x57, 0x4a, 0xb1, 0xe4, 0x18, 0x5d, 0x57, 0x77, 0x56, 0x66, 0x4a, 0xd2, 0x49, 0x5f}}, {Name: "test_quota.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x2d, 0x7, 0x67, 0xa1, 0x9a, 0xb7, 0xc3, 0xa4, 0x21, 0xcd, 0xba, 0x6a, 0x3, 0x49, 0x20, 0x43, 0x67, 0xc2, 0x2c, 0x81},
+ },
+ {
+ Name: "test_rtree.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf5, 0x4a, 0xe9, 0xb0, 0x63, 0xbb, 0x73, 0x71, 0x2f, 0xcf, 0xc1, 0xc6, 0x83, 0x2e, 0x2a, 0x50, 0xf6, 0x2a, 0x97, 0xe7},
+ },
+ {
+ Name: "test_schema.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x12, 0x64, 0x44, 0x67, 0x64, 0x7d, 0x51, 0x39, 0x4a, 0x1, 0xf9, 0xfa, 0x60, 0x37, 0x62, 0x98, 0x18, 0x54, 0x66, 0xfd},
+ },
+ {
+ Name: "test_server.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xed, 0x8, 0x18, 0xe6, 0xf6, 0x5f, 0x27, 0x28, 0x2d, 0xc7, 0xb1, 0xc1, 0x90, 0xec, 0x18, 0x8c, 0x89, 0x33, 0x0, 0x2b},
+ },
+ {
+ Name: "test_sqllog.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x4a, 0xa6, 0x8b, 0x7c, 0x42, 0x93, 0x23, 0xb8, 0xee, 0xbe, 0x6c, 0x9c, 0x2d, 0x7, 0xfc, 0x66, 0xd, 0x8d, 0x47, 0xc9},
+ },
+ {
+ Name: "test_stat.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd4, 0xc9, 0x2, 0xb5, 0xea, 0x11, 0x1a, 0xd5, 0x8a, 0x73, 0x71, 0x12, 0xc2, 0x8f, 0x0, 0x38, 0x43, 0x4c, 0x85, 0xc0},
+ },
+ {
+ Name: "test_superlock.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x93, 0x6f, 0xca, 0xd0, 0xc5, 0x6f, 0x6b, 0xc8, 0x58, 0x9, 0x74, 0x2f, 0x6a, 0xe1, 0xc1, 0xee, 0xb8, 0xb7, 0xd2, 0xf1},
+ },
+ {
+ Name: "test_syscall.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x7c, 0x8, 0x73, 0xc1, 0x6d, 0x84, 0x32, 0x2, 0xf3, 0xe, 0x2d, 0xb9, 0x45, 0x9f, 0xa2, 0x99, 0x75, 0xea, 0x5e, 0x68},
+ },
+ {
+ Name: "test_tclvar.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x12, 0x19, 0x19, 0xc, 0x3, 0x0, 0xfd, 0x5e, 0xc7, 0xa3, 0xc5, 0x84, 0x8, 0xf3, 0x38, 0x43, 0xd2, 0xe, 0xee, 0x15},
+ },
+ {
+ Name: "test_thread.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x2f, 0x93, 0x63, 0xb7, 0x50, 0x1e, 0x51, 0x19, 0x81, 0xfe, 0x32, 0x83, 0x1f, 0xf2, 0xe8, 0xfd, 0x2f, 0x30, 0xc4, 0x93},
+ },
+ {
+ Name: "test_vfs.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xfc, 0xd5, 0x77, 0x43, 0x9c, 0xfd, 0x6c, 0x72, 0xdd, 0xe4, 0x83, 0x58, 0x92, 0x14, 0x20, 0xcf, 0x6e, 0xf1, 0xf8, 0x6d},
+ },
+ {
+ Name: "test_vfstrace.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xa, 0xac, 0xc0, 0x1f, 0xe4, 0x2e, 0x77, 0xfe, 0xb8, 0x58, 0xe4, 0xbe, 0xd0, 0xcb, 0x7e, 0x4, 0xa4, 0x35, 0xb2, 0x10},
+ },
+ {
+ Name: "test_wsd.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x99, 0xe4, 0xa0, 0x56, 0x58, 0x1f, 0x58, 0xf4, 0x53, 0x6f, 0xdb, 0x5a, 0x5d, 0xf7, 0x5c, 0x74, 0x69, 0x8a, 0x81, 0x62},
+ },
+ {
+ Name: "tokenize.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xfa, 0xea, 0x5f, 0x26, 0xc7, 0x9c, 0x5e, 0x18, 0x8f, 0xa8, 0x7f, 0x2f, 0xdf, 0x6f, 0xf7, 0x6a, 0x7a, 0x60, 0x6, 0xc5},
+ },
+ {
+ Name: "trigger.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf1, 0xff, 0x76, 0x6e, 0x20, 0x2a, 0x45, 0x18, 0xec, 0x10, 0xe5, 0x27, 0x12, 0xc, 0xd3, 0xe, 0x83, 0xfb, 0xd0, 0x34},
+ },
+ {
+ Name: "update.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x3a, 0xb1, 0xab, 0x2a, 0x4b, 0x65, 0xda, 0x3f, 0x19, 0x8c, 0x15, 0x84, 0xd5, 0x4d, 0x36, 0xf1, 0x8c, 0xa1, 0x21, 0x4a},
+ },
+ {
+ Name: "utf.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x6d, 0x5b, 0x1b, 0xfe, 0x40, 0xc, 0x37, 0x48, 0xaa, 0x70, 0xa3, 0xb2, 0xfd, 0x5e, 0xe, 0xac, 0x5f, 0xc0, 0x4d, 0xe2},
+ },
+ {
+ Name: "util.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xd8, 0x3a, 0x63, 0x1, 0x5f, 0xd8, 0x7d, 0xcc, 0x4f, 0xb4, 0x41, 0x66, 0xfa, 0xbf, 0x2e, 0x9b, 0xc9, 0x67, 0x1e, 0xb8},
+ },
+ {
+ Name: "vacuum.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x4a, 0xfb, 0x2c, 0xca, 0x64, 0xdd, 0x60, 0x76, 0x11, 0x22, 0x2c, 0x7, 0x93, 0x2d, 0x12, 0xea, 0xcf, 0xa, 0x2c, 0x22},
+ },
+ {
+ Name: "vdbe.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xf3, 0x43, 0xe1, 0x3d, 0x4e, 0x91, 0x78, 0x4b, 0x15, 0x88, 0x10, 0xc5, 0xb7, 0xd4, 0x46, 0x84, 0xdf, 0xbf, 0xa2, 0xa5},
+ },
+ {
+ Name: "vdbe.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xfa, 0x7b, 0x31, 0xb7, 0x27, 0xa, 0x90, 0xd4, 0xf6, 0x37, 0x36, 0x5a, 0xfc, 0xc9, 0xbd, 0xa1, 0xd1, 0xb1, 0xe1, 0xd6},
+ },
+ {
+ Name: "vdbeInt.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x3a, 0x5b, 0x40, 0x28, 0xbb, 0xd6, 0xc9, 0x56, 0x10, 0xd7, 0xc, 0xce, 0x3, 0x69, 0xdf, 0xcd, 0x60, 0x7a, 0xa9, 0x0},
+ },
+ {
+ Name: "vdbeapi.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x7c, 0x86, 0x1e, 0x2d, 0x47, 0x21, 0x8c, 0x91, 0x63, 0x31, 0x77, 0x77, 0xc3, 0x7, 0x21, 0x99, 0xe9, 0xb4, 0x2, 0x80},
+ },
+ {
+ Name: "vdbeaux.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x2c, 0x42, 0x69, 0xa5, 0x9e, 0x6d, 0xbc, 0xe8, 0x67, 0x1c, 0x47, 0x4f, 0x34, 0x61, 0x90, 0xbe, 0x2a, 0xe, 0x18, 0x51},
+ },
+ {
+ Name: "vdbeblob.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x2e, 0x8f, 0xd8, 0xee, 0x74, 0x47, 0xe6, 0x46, 0x46, 0xe3, 0x49, 0x4b, 0x4c, 0x4, 0x1d, 0x3a, 0x4a, 0xbb, 0x8, 0x85},
+ },
+ {
+ Name: "vdbemem.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x8f, 0xc2, 0x22, 0xe2, 0xde, 0x20, 0x50, 0x14, 0x50, 0xec, 0xea, 0x9d, 0x4e, 0xbf, 0xaa, 0xc9, 0x81, 0x4a, 0xae, 0x59},
+ },
+ {
+ Name: "vdbesort.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xfd, 0xfc, 0x4a, 0x79, 0xdd, 0xc9, 0x6e, 0x59, 0x9b, 0x1b, 0xe, 0xeb, 0xac, 0xbd, 0xb8, 0x45, 0xc6, 0x38, 0x13, 0xb2},
+ },
+ {
+ Name: "vdbetrace.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x35, 0x62, 0x77, 0xe8, 0xd2, 0x3b, 0xca, 0xdb, 0x67, 0x6b, 0x59, 0xd1, 0xa4, 0xdc, 0xf8, 0x42, 0xfd, 0xc4, 0xc9, 0x72},
+ },
+ {
+ Name: "vtab.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x95, 0x82, 0x2, 0xc3, 0x1e, 0x24, 0x15, 0xb, 0x60, 0xf1, 0xa, 0x8a, 0xf, 0x74, 0x41, 0xaf, 0xac, 0x3f, 0xbb, 0x1c},
+ },
+ {
+ Name: "wal.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe6, 0x42, 0xea, 0x21, 0x5, 0xb5, 0xc5, 0x4a, 0xf3, 0x5, 0x88, 0x9, 0x62, 0x69, 0xab, 0x75, 0xcb, 0xef, 0x8f, 0xf2},
+ },
+ {
+ Name: "wal.h", Mode: 0x81a4,
+ Hash: plumbing.Hash{0x9, 0x25, 0x46, 0x35, 0x4b, 0x34, 0xc0, 0xab, 0x3d, 0x20, 0x5, 0x6a, 0x7f, 0x8a, 0x8a, 0x52, 0xe4, 0xd0, 0xb5, 0xf5},
+ },
+ {
+ Name: "walker.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe7, 0x1e, 0xd2, 0xac, 0x48, 0x4c, 0x91, 0x6c, 0x1c, 0xc1, 0x0, 0x7e, 0x5e, 0x5, 0xda, 0x47, 0x1c, 0xb4, 0x95, 0x99},
+ },
+ {
+ Name: "where.c", Mode: 0x81a4,
+ Hash: plumbing.Hash{0xe6, 0x14, 0xf4, 0xa6, 0xd8, 0x64, 0xe7, 0xe, 0xc4, 0x32, 0x8d, 0xb, 0xdb, 0x25, 0x4e, 0x3a, 0xc9, 0xf0, 0xd2, 0x87},
+ },
+ },
+ Hash: plumbing.Hash{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ s: (storer.EncodedObjectStorer)(nil),
+ m: map[string]*TreeEntry(nil),
+ }
+
+ var obtained Tree
+ err := obtained.Decode(obj)
+ c.Assert(err, IsNil)
+ c.Assert(entriesEquals(obtained.Entries, expected.Entries), Equals, true)
+}