tree.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package git
  5. import (
  6. "bytes"
  7. "fmt"
  8. "strings"
  9. )
  10. // Tree represents a flat directory listing.
  11. type Tree struct {
  12. ID sha1
  13. repo *Repository
  14. // parent tree
  15. ptree *Tree
  16. entries Entries
  17. entriesParsed bool
  18. }
  19. func NewTree(repo *Repository, id sha1) *Tree {
  20. return &Tree{
  21. ID: id,
  22. repo: repo,
  23. }
  24. }
  25. // Predefine []byte variables to avoid runtime allocations.
  26. var (
  27. escapedSlash = []byte(`\\`)
  28. regularSlash = []byte(`\`)
  29. escapedTab = []byte(`\t`)
  30. regularTab = []byte("\t")
  31. )
  32. // UnescapeChars reverses escaped characters.
  33. func UnescapeChars(in []byte) []byte {
  34. // LEGACY [Go 1.7]: use more expressive bytes.ContainsAny
  35. if bytes.IndexAny(in, "\\\t") == -1 {
  36. return in
  37. }
  38. out := bytes.Replace(in, escapedSlash, regularSlash, -1)
  39. out = bytes.Replace(out, escapedTab, regularTab, -1)
  40. return out
  41. }
  42. // parseTreeData parses tree information from the (uncompressed) raw
  43. // data from the tree object.
  44. func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
  45. entries := make([]*TreeEntry, 0, 10)
  46. l := len(data)
  47. pos := 0
  48. for pos < l {
  49. entry := new(TreeEntry)
  50. entry.ptree = tree
  51. step := 6
  52. switch string(data[pos : pos+step]) {
  53. case "100644", "100664":
  54. entry.mode = ENTRY_MODE_BLOB
  55. entry.Type = OBJECT_BLOB
  56. case "100755":
  57. entry.mode = ENTRY_MODE_EXEC
  58. entry.Type = OBJECT_BLOB
  59. case "120000":
  60. entry.mode = ENTRY_MODE_SYMLINK
  61. entry.Type = OBJECT_BLOB
  62. case "160000":
  63. entry.mode = ENTRY_MODE_COMMIT
  64. entry.Type = OBJECT_COMMIT
  65. step = 8
  66. case "040000":
  67. entry.mode = ENTRY_MODE_TREE
  68. entry.Type = OBJECT_TREE
  69. default:
  70. return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+step]))
  71. }
  72. pos += step + 6 // Skip string type of entry type.
  73. step = 40
  74. id, err := NewIDFromString(string(data[pos : pos+step]))
  75. if err != nil {
  76. return nil, err
  77. }
  78. entry.ID = id
  79. pos += step + 1 // Skip half of sha1.
  80. step = bytes.IndexByte(data[pos:], '\n')
  81. // In case entry name is surrounded by double quotes(it happens only in git-shell).
  82. if data[pos] == '"' {
  83. entry.name = string(UnescapeChars(data[pos+1 : pos+step-1]))
  84. } else {
  85. entry.name = string(data[pos : pos+step])
  86. }
  87. pos += step + 1
  88. entries = append(entries, entry)
  89. }
  90. return entries, nil
  91. }
  92. func (t *Tree) SubTree(rpath string) (*Tree, error) {
  93. if len(rpath) == 0 {
  94. return t, nil
  95. }
  96. paths := strings.Split(rpath, "/")
  97. var (
  98. err error
  99. g = t
  100. p = t
  101. te *TreeEntry
  102. )
  103. for _, name := range paths {
  104. te, err = p.GetTreeEntryByPath(name)
  105. if err != nil {
  106. return nil, err
  107. }
  108. g, err = t.repo.getTree(te.ID)
  109. if err != nil {
  110. return nil, err
  111. }
  112. g.ptree = p
  113. p = g
  114. }
  115. return g, nil
  116. }
  117. // ListEntries returns all entries of current tree.
  118. func (t *Tree) ListEntries() (Entries, error) {
  119. if t.entriesParsed {
  120. return t.entries, nil
  121. }
  122. t.entriesParsed = true
  123. stdout, err := NewCommand("ls-tree", t.ID.String()).RunInDirBytes(t.repo.Path)
  124. if err != nil {
  125. return nil, err
  126. }
  127. t.entries, err = parseTreeData(t, stdout)
  128. return t.entries, err
  129. }