| package object |
| |
| import ( |
| "io" |
| |
| "gopkg.in/src-d/go-git.v4/plumbing" |
| "gopkg.in/src-d/go-git.v4/plumbing/filemode" |
| "gopkg.in/src-d/go-git.v4/utils/merkletrie/noder" |
| ) |
| |
| // A treenoder is a helper type that wraps git trees into merkletrie |
| // noders. |
| // |
| // As a merkletrie noder doesn't understand the concept of modes (e.g. |
| // file permissions), the treenoder includes the mode of the git tree in |
| // the hash, so changes in the modes will be detected as modifications |
| // to the file contents by the merkletrie difftree algorithm. This is |
| // consistent with how the "git diff-tree" command works. |
| type treeNoder struct { |
| parent *Tree // the root node is its own parent |
| name string // empty string for the root node |
| mode filemode.FileMode |
| hash plumbing.Hash |
| children []noder.Noder // memoized |
| } |
| |
| // NewTreeRootNode returns the root node of a Tree |
| func NewTreeRootNode(t *Tree) noder.Noder { |
| if t == nil { |
| return &treeNoder{} |
| } |
| |
| return &treeNoder{ |
| parent: t, |
| name: "", |
| mode: filemode.Dir, |
| hash: t.Hash, |
| } |
| } |
| |
| func (t *treeNoder) isRoot() bool { |
| return t.name == "" |
| } |
| |
| func (t *treeNoder) String() string { |
| return "treeNoder <" + t.name + ">" |
| } |
| |
| func (t *treeNoder) Hash() []byte { |
| if t.mode == filemode.Deprecated { |
| return append(t.hash[:], filemode.Regular.Bytes()...) |
| } |
| return append(t.hash[:], t.mode.Bytes()...) |
| } |
| |
| func (t *treeNoder) Name() string { |
| return t.name |
| } |
| |
| func (t *treeNoder) IsDir() bool { |
| return t.mode == filemode.Dir |
| } |
| |
| // Children will return the children of a treenoder as treenoders, |
| // building them from the children of the wrapped git tree. |
| func (t *treeNoder) Children() ([]noder.Noder, error) { |
| if t.mode != filemode.Dir { |
| return noder.NoChildren, nil |
| } |
| |
| // children are memoized for efficiency |
| if t.children != nil { |
| return t.children, nil |
| } |
| |
| // the parent of the returned children will be ourself as a tree if |
| // we are a not the root treenoder. The root is special as it |
| // is is own parent. |
| parent := t.parent |
| if !t.isRoot() { |
| var err error |
| if parent, err = t.parent.Tree(t.name); err != nil { |
| return nil, err |
| } |
| } |
| |
| return transformChildren(parent) |
| } |
| |
| // Returns the children of a tree as treenoders. |
| // Efficiency is key here. |
| func transformChildren(t *Tree) ([]noder.Noder, error) { |
| var err error |
| var e TreeEntry |
| |
| // there will be more tree entries than children in the tree, |
| // due to submodules and empty directories, but I think it is still |
| // worth it to pre-allocate the whole array now, even if sometimes |
| // is bigger than needed. |
| ret := make([]noder.Noder, 0, len(t.Entries)) |
| |
| walker := NewTreeWalker(t, false, nil) // don't recurse |
| // don't defer walker.Close() for efficiency reasons. |
| for { |
| _, e, err = walker.Next() |
| if err == io.EOF { |
| break |
| } |
| if err != nil { |
| walker.Close() |
| return nil, err |
| } |
| |
| ret = append(ret, &treeNoder{ |
| parent: t, |
| name: e.Name, |
| mode: e.Mode, |
| hash: e.Hash, |
| }) |
| } |
| walker.Close() |
| |
| return ret, nil |
| } |
| |
| // len(t.tree.Entries) != the number of elements walked by treewalker |
| // for some reason because of empty directories, submodules, etc, so we |
| // have to walk here. |
| func (t *treeNoder) NumChildren() (int, error) { |
| children, err := t.Children() |
| if err != nil { |
| return 0, err |
| } |
| |
| return len(children), nil |
| } |