本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.2KB

  1. package object
  2. import (
  3. "io"
  4. "gopkg.in/src-d/go-git.v4/plumbing"
  5. "gopkg.in/src-d/go-git.v4/plumbing/filemode"
  6. "gopkg.in/src-d/go-git.v4/utils/merkletrie/noder"
  7. )
  8. // A treenoder is a helper type that wraps git trees into merkletrie
  9. // noders.
  10. //
  11. // As a merkletrie noder doesn't understand the concept of modes (e.g.
  12. // file permissions), the treenoder includes the mode of the git tree in
  13. // the hash, so changes in the modes will be detected as modifications
  14. // to the file contents by the merkletrie difftree algorithm. This is
  15. // consistent with how the "git diff-tree" command works.
  16. type treeNoder struct {
  17. parent *Tree // the root node is its own parent
  18. name string // empty string for the root node
  19. mode filemode.FileMode
  20. hash plumbing.Hash
  21. children []noder.Noder // memoized
  22. }
  23. // NewTreeRootNode returns the root node of a Tree
  24. func NewTreeRootNode(t *Tree) noder.Noder {
  25. if t == nil {
  26. return &treeNoder{}
  27. }
  28. return &treeNoder{
  29. parent: t,
  30. name: "",
  31. mode: filemode.Dir,
  32. hash: t.Hash,
  33. }
  34. }
  35. func (t *treeNoder) isRoot() bool {
  36. return t.name == ""
  37. }
  38. func (t *treeNoder) String() string {
  39. return "treeNoder <" + t.name + ">"
  40. }
  41. func (t *treeNoder) Hash() []byte {
  42. if t.mode == filemode.Deprecated {
  43. return append(t.hash[:], filemode.Regular.Bytes()...)
  44. }
  45. return append(t.hash[:], t.mode.Bytes()...)
  46. }
  47. func (t *treeNoder) Name() string {
  48. return t.name
  49. }
  50. func (t *treeNoder) IsDir() bool {
  51. return t.mode == filemode.Dir
  52. }
  53. // Children will return the children of a treenoder as treenoders,
  54. // building them from the children of the wrapped git tree.
  55. func (t *treeNoder) Children() ([]noder.Noder, error) {
  56. if t.mode != filemode.Dir {
  57. return noder.NoChildren, nil
  58. }
  59. // children are memoized for efficiency
  60. if t.children != nil {
  61. return t.children, nil
  62. }
  63. // the parent of the returned children will be ourself as a tree if
  64. // we are a not the root treenoder. The root is special as it
  65. // is is own parent.
  66. parent := t.parent
  67. if !t.isRoot() {
  68. var err error
  69. if parent, err = t.parent.Tree(t.name); err != nil {
  70. return nil, err
  71. }
  72. }
  73. return transformChildren(parent)
  74. }
  75. // Returns the children of a tree as treenoders.
  76. // Efficiency is key here.
  77. func transformChildren(t *Tree) ([]noder.Noder, error) {
  78. var err error
  79. var e TreeEntry
  80. // there will be more tree entries than children in the tree,
  81. // due to submodules and empty directories, but I think it is still
  82. // worth it to pre-allocate the whole array now, even if sometimes
  83. // is bigger than needed.
  84. ret := make([]noder.Noder, 0, len(t.Entries))
  85. walker := NewTreeWalker(t, false, nil) // don't recurse
  86. // don't defer walker.Close() for efficiency reasons.
  87. for {
  88. _, e, err = walker.Next()
  89. if err == io.EOF {
  90. break
  91. }
  92. if err != nil {
  93. walker.Close()
  94. return nil, err
  95. }
  96. ret = append(ret, &treeNoder{
  97. parent: t,
  98. name: e.Name,
  99. mode: e.Mode,
  100. hash: e.Hash,
  101. })
  102. }
  103. walker.Close()
  104. return ret, nil
  105. }
  106. // len(t.tree.Entries) != the number of elements walked by treewalker
  107. // for some reason because of empty directories, submodules, etc, so we
  108. // have to walk here.
  109. func (t *treeNoder) NumChildren() (int, error) {
  110. children, err := t.Children()
  111. if err != nil {
  112. return 0, err
  113. }
  114. return len(children), nil
  115. }
上海开阖软件有限公司 沪ICP备12045867号-1