本站源代码
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.

197 lines
4.0KB

  1. package filesystem
  2. import (
  3. "io"
  4. "os"
  5. "path"
  6. "gopkg.in/src-d/go-git.v4/plumbing"
  7. "gopkg.in/src-d/go-git.v4/plumbing/filemode"
  8. "gopkg.in/src-d/go-git.v4/utils/merkletrie/noder"
  9. "gopkg.in/src-d/go-billy.v4"
  10. )
  11. var ignore = map[string]bool{
  12. ".git": true,
  13. }
  14. // The node represents a file or a directory in a billy.Filesystem. It
  15. // implements the interface noder.Noder of merkletrie package.
  16. //
  17. // This implementation implements a "standard" hash method being able to be
  18. // compared with any other noder.Noder implementation inside of go-git.
  19. type node struct {
  20. fs billy.Filesystem
  21. submodules map[string]plumbing.Hash
  22. path string
  23. hash []byte
  24. children []noder.Noder
  25. isDir bool
  26. }
  27. // NewRootNode returns the root node based on a given billy.Filesystem.
  28. //
  29. // In order to provide the submodule hash status, a map[string]plumbing.Hash
  30. // should be provided where the key is the path of the submodule and the commit
  31. // of the submodule HEAD
  32. func NewRootNode(
  33. fs billy.Filesystem,
  34. submodules map[string]plumbing.Hash,
  35. ) noder.Noder {
  36. return &node{fs: fs, submodules: submodules, isDir: true}
  37. }
  38. // Hash the hash of a filesystem is the result of concatenating the computed
  39. // plumbing.Hash of the file as a Blob and its plumbing.FileMode; that way the
  40. // difftree algorithm will detect changes in the contents of files and also in
  41. // their mode.
  42. //
  43. // The hash of a directory is always a 24-bytes slice of zero values
  44. func (n *node) Hash() []byte {
  45. return n.hash
  46. }
  47. func (n *node) Name() string {
  48. return path.Base(n.path)
  49. }
  50. func (n *node) IsDir() bool {
  51. return n.isDir
  52. }
  53. func (n *node) Children() ([]noder.Noder, error) {
  54. if err := n.calculateChildren(); err != nil {
  55. return nil, err
  56. }
  57. return n.children, nil
  58. }
  59. func (n *node) NumChildren() (int, error) {
  60. if err := n.calculateChildren(); err != nil {
  61. return -1, err
  62. }
  63. return len(n.children), nil
  64. }
  65. func (n *node) calculateChildren() error {
  66. if !n.IsDir() {
  67. return nil
  68. }
  69. if len(n.children) != 0 {
  70. return nil
  71. }
  72. files, err := n.fs.ReadDir(n.path)
  73. if err != nil {
  74. if os.IsNotExist(err) {
  75. return nil
  76. }
  77. return nil
  78. }
  79. for _, file := range files {
  80. if _, ok := ignore[file.Name()]; ok {
  81. continue
  82. }
  83. c, err := n.newChildNode(file)
  84. if err != nil {
  85. return err
  86. }
  87. n.children = append(n.children, c)
  88. }
  89. return nil
  90. }
  91. func (n *node) newChildNode(file os.FileInfo) (*node, error) {
  92. path := path.Join(n.path, file.Name())
  93. hash, err := n.calculateHash(path, file)
  94. if err != nil {
  95. return nil, err
  96. }
  97. node := &node{
  98. fs: n.fs,
  99. submodules: n.submodules,
  100. path: path,
  101. hash: hash,
  102. isDir: file.IsDir(),
  103. }
  104. if hash, isSubmodule := n.submodules[path]; isSubmodule {
  105. node.hash = append(hash[:], filemode.Submodule.Bytes()...)
  106. node.isDir = false
  107. }
  108. return node, nil
  109. }
  110. func (n *node) calculateHash(path string, file os.FileInfo) ([]byte, error) {
  111. if file.IsDir() {
  112. return make([]byte, 24), nil
  113. }
  114. var hash plumbing.Hash
  115. var err error
  116. if file.Mode()&os.ModeSymlink != 0 {
  117. hash, err = n.doCalculateHashForSymlink(path, file)
  118. } else {
  119. hash, err = n.doCalculateHashForRegular(path, file)
  120. }
  121. if err != nil {
  122. return nil, err
  123. }
  124. mode, err := filemode.NewFromOSFileMode(file.Mode())
  125. if err != nil {
  126. return nil, err
  127. }
  128. return append(hash[:], mode.Bytes()...), nil
  129. }
  130. func (n *node) doCalculateHashForRegular(path string, file os.FileInfo) (plumbing.Hash, error) {
  131. f, err := n.fs.Open(path)
  132. if err != nil {
  133. return plumbing.ZeroHash, err
  134. }
  135. defer f.Close()
  136. h := plumbing.NewHasher(plumbing.BlobObject, file.Size())
  137. if _, err := io.Copy(h, f); err != nil {
  138. return plumbing.ZeroHash, err
  139. }
  140. return h.Sum(), nil
  141. }
  142. func (n *node) doCalculateHashForSymlink(path string, file os.FileInfo) (plumbing.Hash, error) {
  143. target, err := n.fs.Readlink(path)
  144. if err != nil {
  145. return plumbing.ZeroHash, err
  146. }
  147. h := plumbing.NewHasher(plumbing.BlobObject, file.Size())
  148. if _, err := h.Write([]byte(target)); err != nil {
  149. return plumbing.ZeroHash, err
  150. }
  151. return h.Sum(), nil
  152. }
  153. func (n *node) String() string {
  154. return n.path
  155. }
上海开阖软件有限公司 沪ICP备12045867号-1