aboutsummaryrefslogtreecommitdiffstats
path: root/tree_walker.go
diff options
context:
space:
mode:
Diffstat (limited to 'tree_walker.go')
-rw-r--r--tree_walker.go110
1 files changed, 110 insertions, 0 deletions
diff --git a/tree_walker.go b/tree_walker.go
new file mode 100644
index 0000000..3272718
--- /dev/null
+++ b/tree_walker.go
@@ -0,0 +1,110 @@
+package git
+
+import (
+ "io"
+ "path"
+
+ "gopkg.in/src-d/go-git.v3/core"
+)
+
+const (
+ startingStackSize = 8
+)
+
+// TreeWalker provides a means of walking through all of the entries in a Tree.
+type TreeWalker struct {
+ stack []TreeEntryIter
+ base string
+
+ r *Repository
+}
+
+// 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) *TreeWalker {
+ w := TreeWalker{
+ stack: make([]TreeEntryIter, 0, startingStackSize),
+ base: "",
+ r: r,
+ }
+ w.stack = append(w.stack, *NewTreeEntryIter(t))
+ return &w
+}
+
+// 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, obj core.Object, err error) {
+ 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
+ }
+
+ obj, err = w.r.Storage.Get(entry.Hash)
+ if err == core.ObjectNotFoundErr {
+ // FIXME: Avoid doing this here in case the caller actually cares about
+ // missing objects.
+ continue // ignore entries without hash (= submodule dirs)
+ }
+
+ name = path.Join(w.base, entry.Name)
+
+ if err != nil {
+ return
+ }
+
+ break
+ }
+
+ if obj.Type() == core.TreeObject {
+ tree := &Tree{r: w.r}
+ err = tree.Decode(obj)
+ if err != nil {
+ return
+ }
+ w.stack = append(w.stack, *NewTreeEntryIter(tree))
+ 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 current < 0 {
+ return nil
+ }
+ return w.stack[current].t
+}
+
+// Close releases any resources used by the TreeWalker.
+func (w *TreeWalker) Close() {
+ w.stack = nil
+}