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