gitea源码

tree_entry.go 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. "path"
  7. "sort"
  8. "strings"
  9. "code.gitea.io/gitea/modules/util"
  10. )
  11. // Type returns the type of the entry (commit, tree, blob)
  12. func (te *TreeEntry) Type() string {
  13. switch te.Mode() {
  14. case EntryModeCommit:
  15. return "commit"
  16. case EntryModeTree:
  17. return "tree"
  18. default:
  19. return "blob"
  20. }
  21. }
  22. type EntryFollowResult struct {
  23. SymlinkContent string
  24. TargetFullPath string
  25. TargetEntry *TreeEntry
  26. }
  27. func EntryFollowLink(commit *Commit, fullPath string, te *TreeEntry) (*EntryFollowResult, error) {
  28. if !te.IsLink() {
  29. return nil, util.ErrorWrap(util.ErrUnprocessableContent, "%q is not a symlink", fullPath)
  30. }
  31. // git's filename max length is 4096, hopefully a link won't be longer than multiple of that
  32. const maxSymlinkSize = 20 * 4096
  33. if te.Blob().Size() > maxSymlinkSize {
  34. return nil, util.ErrorWrap(util.ErrUnprocessableContent, "%q content exceeds symlink limit", fullPath)
  35. }
  36. link, err := te.Blob().GetBlobContent(maxSymlinkSize)
  37. if err != nil {
  38. return nil, err
  39. }
  40. if strings.HasPrefix(link, "/") {
  41. // It's said that absolute path will be stored as is in Git
  42. return &EntryFollowResult{SymlinkContent: link}, util.ErrorWrap(util.ErrUnprocessableContent, "%q is an absolute symlink", fullPath)
  43. }
  44. targetFullPath := path.Join(path.Dir(fullPath), link)
  45. targetEntry, err := commit.GetTreeEntryByPath(targetFullPath)
  46. if err != nil {
  47. return &EntryFollowResult{SymlinkContent: link}, err
  48. }
  49. return &EntryFollowResult{SymlinkContent: link, TargetFullPath: targetFullPath, TargetEntry: targetEntry}, nil
  50. }
  51. func EntryFollowLinks(commit *Commit, firstFullPath string, firstTreeEntry *TreeEntry, optLimit ...int) (res *EntryFollowResult, err error) {
  52. limit := util.OptionalArg(optLimit, 10)
  53. treeEntry, fullPath := firstTreeEntry, firstFullPath
  54. for range limit {
  55. res, err = EntryFollowLink(commit, fullPath, treeEntry)
  56. if err != nil {
  57. return res, err
  58. }
  59. treeEntry, fullPath = res.TargetEntry, res.TargetFullPath
  60. if !treeEntry.IsLink() {
  61. break
  62. }
  63. }
  64. if treeEntry.IsLink() {
  65. return res, util.ErrorWrap(util.ErrUnprocessableContent, "%q has too many links", firstFullPath)
  66. }
  67. return res, nil
  68. }
  69. // returns the Tree pointed to by this TreeEntry, or nil if this is not a tree
  70. func (te *TreeEntry) Tree() *Tree {
  71. t, err := te.ptree.repo.getTree(te.ID)
  72. if err != nil {
  73. return nil
  74. }
  75. t.ptree = te.ptree
  76. return t
  77. }
  78. // GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory )
  79. func (te *TreeEntry) GetSubJumpablePathName() string {
  80. if te.IsSubModule() || !te.IsDir() {
  81. return ""
  82. }
  83. tree, err := te.ptree.SubTree(te.Name())
  84. if err != nil {
  85. return te.Name()
  86. }
  87. entries, _ := tree.ListEntries()
  88. if len(entries) == 1 && entries[0].IsDir() {
  89. name := entries[0].GetSubJumpablePathName()
  90. if name != "" {
  91. return te.Name() + "/" + name
  92. }
  93. }
  94. return te.Name()
  95. }
  96. // Entries a list of entry
  97. type Entries []*TreeEntry
  98. type customSortableEntries struct {
  99. Comparer func(s1, s2 string) bool
  100. Entries
  101. }
  102. var sorter = []func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool{
  103. func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool {
  104. return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
  105. },
  106. func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool {
  107. return cmp(t1.Name(), t2.Name())
  108. },
  109. }
  110. func (ctes customSortableEntries) Len() int { return len(ctes.Entries) }
  111. func (ctes customSortableEntries) Swap(i, j int) {
  112. ctes.Entries[i], ctes.Entries[j] = ctes.Entries[j], ctes.Entries[i]
  113. }
  114. func (ctes customSortableEntries) Less(i, j int) bool {
  115. t1, t2 := ctes.Entries[i], ctes.Entries[j]
  116. var k int
  117. for k = 0; k < len(sorter)-1; k++ {
  118. s := sorter[k]
  119. switch {
  120. case s(t1, t2, ctes.Comparer):
  121. return true
  122. case s(t2, t1, ctes.Comparer):
  123. return false
  124. }
  125. }
  126. return sorter[k](t1, t2, ctes.Comparer)
  127. }
  128. // Sort sort the list of entry
  129. func (tes Entries) Sort() {
  130. sort.Sort(customSortableEntries{func(s1, s2 string) bool {
  131. return s1 < s2
  132. }, tes})
  133. }
  134. // CustomSort customizable string comparing sort entry list
  135. func (tes Entries) CustomSort(cmp func(s1, s2 string) bool) {
  136. sort.Sort(customSortableEntries{cmp, tes})
  137. }