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.

tree_entry.go 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package git
  5. import (
  6. "io"
  7. "sort"
  8. "strings"
  9. )
  10. // Type returns the type of the entry (commit, tree, blob)
  11. func (te *TreeEntry) Type() string {
  12. switch te.Mode() {
  13. case EntryModeCommit:
  14. return "commit"
  15. case EntryModeTree:
  16. return "tree"
  17. default:
  18. return "blob"
  19. }
  20. }
  21. // FollowLink returns the entry pointed to by a symlink
  22. func (te *TreeEntry) FollowLink() (*TreeEntry, error) {
  23. if !te.IsLink() {
  24. return nil, ErrBadLink{te.Name(), "not a symlink"}
  25. }
  26. // read the link
  27. r, err := te.Blob().DataAsync()
  28. if err != nil {
  29. return nil, err
  30. }
  31. closed := false
  32. defer func() {
  33. if !closed {
  34. _ = r.Close()
  35. }
  36. }()
  37. buf := make([]byte, te.Size())
  38. _, err = io.ReadFull(r, buf)
  39. if err != nil {
  40. return nil, err
  41. }
  42. _ = r.Close()
  43. closed = true
  44. lnk := string(buf)
  45. t := te.ptree
  46. // traverse up directories
  47. for ; t != nil && strings.HasPrefix(lnk, "../"); lnk = lnk[3:] {
  48. t = t.ptree
  49. }
  50. if t == nil {
  51. return nil, ErrBadLink{te.Name(), "points outside of repo"}
  52. }
  53. target, err := t.GetTreeEntryByPath(lnk)
  54. if err != nil {
  55. if IsErrNotExist(err) {
  56. return nil, ErrBadLink{te.Name(), "broken link"}
  57. }
  58. return nil, err
  59. }
  60. return target, nil
  61. }
  62. // FollowLinks returns the entry ultimately pointed to by a symlink
  63. func (te *TreeEntry) FollowLinks() (*TreeEntry, error) {
  64. if !te.IsLink() {
  65. return nil, ErrBadLink{te.Name(), "not a symlink"}
  66. }
  67. entry := te
  68. for i := 0; i < 999; i++ {
  69. if entry.IsLink() {
  70. next, err := entry.FollowLink()
  71. if err != nil {
  72. return nil, err
  73. }
  74. if next.ID == entry.ID {
  75. return nil, ErrBadLink{
  76. entry.Name(),
  77. "recursive link",
  78. }
  79. }
  80. entry = next
  81. } else {
  82. break
  83. }
  84. }
  85. if entry.IsLink() {
  86. return nil, ErrBadLink{
  87. te.Name(),
  88. "too many levels of symbolic links",
  89. }
  90. }
  91. return entry, nil
  92. }
  93. // returns the Tree pointed to by this TreeEntry, or nil if this is not a tree
  94. func (te *TreeEntry) Tree() *Tree {
  95. t, err := te.ptree.repo.getTree(te.ID)
  96. if err != nil {
  97. return nil
  98. }
  99. t.ptree = te.ptree
  100. return t
  101. }
  102. // GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory )
  103. func (te *TreeEntry) GetSubJumpablePathName() string {
  104. if te.IsSubModule() || !te.IsDir() {
  105. return ""
  106. }
  107. tree, err := te.ptree.SubTree(te.Name())
  108. if err != nil {
  109. return te.Name()
  110. }
  111. entries, _ := tree.ListEntries()
  112. if len(entries) == 1 && entries[0].IsDir() {
  113. name := entries[0].GetSubJumpablePathName()
  114. if name != "" {
  115. return te.Name() + "/" + name
  116. }
  117. }
  118. return te.Name()
  119. }
  120. // Entries a list of entry
  121. type Entries []*TreeEntry
  122. type customSortableEntries struct {
  123. Comparer func(s1, s2 string) bool
  124. Entries
  125. }
  126. var sorter = []func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool{
  127. func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool {
  128. return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
  129. },
  130. func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool {
  131. return cmp(t1.Name(), t2.Name())
  132. },
  133. }
  134. func (ctes customSortableEntries) Len() int { return len(ctes.Entries) }
  135. func (ctes customSortableEntries) Swap(i, j int) {
  136. ctes.Entries[i], ctes.Entries[j] = ctes.Entries[j], ctes.Entries[i]
  137. }
  138. func (ctes customSortableEntries) Less(i, j int) bool {
  139. t1, t2 := ctes.Entries[i], ctes.Entries[j]
  140. var k int
  141. for k = 0; k < len(sorter)-1; k++ {
  142. s := sorter[k]
  143. switch {
  144. case s(t1, t2, ctes.Comparer):
  145. return true
  146. case s(t2, t1, ctes.Comparer):
  147. return false
  148. }
  149. }
  150. return sorter[k](t1, t2, ctes.Comparer)
  151. }
  152. // Sort sort the list of entry
  153. func (tes Entries) Sort() {
  154. sort.Sort(customSortableEntries{func(s1, s2 string) bool {
  155. return s1 < s2
  156. }, tes})
  157. }
  158. // CustomSort customizable string comparing sort entry list
  159. func (tes Entries) CustomSort(cmp func(s1, s2 string) bool) {
  160. sort.Sort(customSortableEntries{cmp, tes})
  161. }