aboutsummaryrefslogtreecommitdiffstats
path: root/tree.go
diff options
context:
space:
mode:
Diffstat (limited to 'tree.go')
-rw-r--r--tree.go421
1 files changed, 0 insertions, 421 deletions
diff --git a/tree.go b/tree.go
deleted file mode 100644
index 753ed0c..0000000
--- a/tree.go
+++ /dev/null
@@ -1,421 +0,0 @@
-package git
-
-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"
-)
-
-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
-
- r *Repository
- m map[string]*TreeEntry
-}
-
-// 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
- }
-
- obj, err := t.r.s.Object(plumbing.BlobObject, e.Hash)
- if err != nil {
- return nil, err
- }
-
- blob := &Blob{}
- blob.Decode(obj)
-
- return NewFile(path, 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.r.s.Object(plumbing.TreeObject, entry.Hash)
- if err != nil {
- return nil, err
- }
-
- tree := &Tree{r: t.r}
- 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.r, 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.Object into a Tree struct
-func (t *Tree) Decode(o plumbing.Object) (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 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.Object.
-func (t *Tree) Encode(o plumbing.Object) error {
- o.SetType(plumbing.TreeObject)
- w, err := o.Writer()
- if err != nil {
- return err
- }
-
- var size int
- defer 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
-
- r *Repository
- t *Tree
-}
-
-// NewTreeWalker returns a new TreeWalker for the given repository and tree.
-//
-// It is the caller's responsibility to call Close() when finished with the
-// tree walker.
-func NewTreeWalker(r *Repository, t *Tree, recursive bool) *TreeWalker {
- stack := make([]treeEntryIter, 0, startingStackSize)
- stack = append(stack, treeEntryIter{t, 0})
-
- return &TreeWalker{
- stack: stack,
- recursive: recursive,
-
- r: r,
- 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 = w.r.Tree(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.ObjectIter
- r *Repository
-}
-
-// NewTreeIter returns a TreeIter for the given repository and underlying
-// object iterator.
-//
-// The returned TreeIter will automatically skip over non-tree objects.
-func NewTreeIter(r *Repository, iter storer.ObjectIter) *TreeIter {
- return &TreeIter{iter, r}
-}
-
-// 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.ObjectIter.Next()
- if err != nil {
- return nil, err
- }
-
- if obj.Type() != plumbing.TreeObject {
- continue
- }
-
- tree := &Tree{r: iter.r}
- return tree, tree.Decode(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.ObjectIter.ForEach(func(obj plumbing.Object) error {
- if obj.Type() != plumbing.TreeObject {
- return nil
- }
-
- tree := &Tree{r: iter.r}
- if err := tree.Decode(obj); err != nil {
- return err
- }
-
- return cb(tree)
- })
-}