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.

151 lines
3.0 KiB

  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. var escapeChar = []byte("\\")
  26. // UnescapeChars reverses escaped characters.
  27. func UnescapeChars(in []byte) []byte {
  28. if bytes.Index(in, escapeChar) == -1 {
  29. return in
  30. }
  31. endIdx := len(in) - 1
  32. isEscape := false
  33. out := make([]byte, 0, endIdx+1)
  34. for i := range in {
  35. if in[i] == '\\' && !isEscape {
  36. isEscape = true
  37. continue
  38. }
  39. isEscape = false
  40. out = append(out, in[i])
  41. }
  42. return out
  43. }
  44. // parseTreeData parses tree information from the (uncompressed) raw
  45. // data from the tree object.
  46. func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
  47. entries := make([]*TreeEntry, 0, 10)
  48. l := len(data)
  49. pos := 0
  50. for pos < l {
  51. entry := new(TreeEntry)
  52. entry.ptree = tree
  53. step := 6
  54. switch string(data[pos : pos+step]) {
  55. case "100644":
  56. entry.mode = ENTRY_MODE_BLOB
  57. entry.Type = OBJECT_BLOB
  58. case "100755":
  59. entry.mode = ENTRY_MODE_EXEC
  60. entry.Type = OBJECT_BLOB
  61. case "120000":
  62. entry.mode = ENTRY_MODE_SYMLINK
  63. entry.Type = OBJECT_BLOB
  64. case "160000":
  65. entry.mode = ENTRY_MODE_COMMIT
  66. entry.Type = OBJECT_COMMIT
  67. step = 8
  68. case "040000":
  69. entry.mode = ENTRY_MODE_TREE
  70. entry.Type = OBJECT_TREE
  71. default:
  72. return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+step]))
  73. }
  74. pos += step + 6 // Skip string type of entry type.
  75. step = 40
  76. id, err := NewIDFromString(string(data[pos : pos+step]))
  77. if err != nil {
  78. return nil, err
  79. }
  80. entry.ID = id
  81. pos += step + 1 // Skip half of sha1.
  82. step = bytes.IndexByte(data[pos:], '\n')
  83. // In case entry name is surrounded by double quotes(it happens only in git-shell).
  84. if data[pos] == '"' {
  85. entry.name = string(UnescapeChars(data[pos+1 : pos+step-1]))
  86. } else {
  87. entry.name = string(data[pos : pos+step])
  88. }
  89. pos += step + 1
  90. entries = append(entries, entry)
  91. }
  92. return entries, nil
  93. }
  94. func (t *Tree) SubTree(rpath string) (*Tree, error) {
  95. if len(rpath) == 0 {
  96. return t, nil
  97. }
  98. paths := strings.Split(rpath, "/")
  99. var (
  100. err error
  101. g = t
  102. p = t
  103. te *TreeEntry
  104. )
  105. for _, name := range paths {
  106. te, err = p.GetTreeEntryByPath(name)
  107. if err != nil {
  108. return nil, err
  109. }
  110. g, err = t.repo.getTree(te.ID)
  111. if err != nil {
  112. return nil, err
  113. }
  114. g.ptree = p
  115. p = g
  116. }
  117. return g, nil
  118. }
  119. // ListEntries returns all entries of current tree.
  120. func (t *Tree) ListEntries() (Entries, error) {
  121. if t.entriesParsed {
  122. return t.entries, nil
  123. }
  124. t.entriesParsed = true
  125. stdout, err := NewCommand("ls-tree", t.ID.String()).RunInDirBytes(t.repo.Path)
  126. if err != nil {
  127. return nil, err
  128. }
  129. t.entries, err = parseTreeData(t, stdout)
  130. return t.entries, err
  131. }