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