2016-11-04 01:16:01 +03:00
// Copyright 2015 The Gogs Authors. All rights reserved.
2019-04-19 15:17:27 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2016-11-04 01:16:01 +03:00
package git
import (
2018-05-01 10:04:36 +03:00
"io"
2016-11-04 01:16:01 +03:00
"sort"
"strings"
)
2019-04-19 15:17:27 +03:00
// Type returns the type of the entry (commit, tree, blob)
func ( te * TreeEntry ) Type ( ) string {
switch te . Mode ( ) {
case EntryModeCommit :
return "commit"
case EntryModeTree :
return "tree"
default :
return "blob"
}
2018-11-28 10:00:25 +03:00
}
2018-05-01 10:04:36 +03:00
// FollowLink returns the entry pointed to by a symlink
2024-02-29 22:51:02 +03:00
func ( te * TreeEntry ) FollowLink ( ) ( * TreeEntry , string , error ) {
2018-05-01 10:04:36 +03:00
if ! te . IsLink ( ) {
2024-02-29 22:51:02 +03:00
return nil , "" , ErrBadLink { te . Name ( ) , "not a symlink" }
2018-05-01 10:04:36 +03:00
}
// read the link
2019-04-19 15:17:27 +03:00
r , err := te . Blob ( ) . DataAsync ( )
2018-05-01 10:04:36 +03:00
if err != nil {
2024-02-29 22:51:02 +03:00
return nil , "" , err
2018-05-01 10:04:36 +03:00
}
2021-05-10 04:27:03 +03:00
closed := false
defer func ( ) {
if ! closed {
_ = r . Close ( )
}
} ( )
2018-05-01 10:04:36 +03:00
buf := make ( [ ] byte , te . Size ( ) )
_ , err = io . ReadFull ( r , buf )
if err != nil {
2024-02-29 22:51:02 +03:00
return nil , "" , err
2018-05-01 10:04:36 +03:00
}
2021-05-10 04:27:03 +03:00
_ = r . Close ( )
closed = true
2018-05-01 10:04:36 +03:00
lnk := string ( buf )
t := te . ptree
// traverse up directories
for ; t != nil && strings . HasPrefix ( lnk , "../" ) ; lnk = lnk [ 3 : ] {
t = t . ptree
}
if t == nil {
2024-02-29 22:51:02 +03:00
return nil , "" , ErrBadLink { te . Name ( ) , "points outside of repo" }
2018-05-01 10:04:36 +03:00
}
target , err := t . GetTreeEntryByPath ( lnk )
if err != nil {
if IsErrNotExist ( err ) {
2024-02-29 22:51:02 +03:00
return nil , "" , ErrBadLink { te . Name ( ) , "broken link" }
2018-05-01 10:04:36 +03:00
}
2024-02-29 22:51:02 +03:00
return nil , "" , err
2018-05-01 10:04:36 +03:00
}
2024-02-29 22:51:02 +03:00
return target , lnk , nil
2018-05-01 10:04:36 +03:00
}
2020-02-22 02:04:20 +03:00
// FollowLinks returns the entry ultimately pointed to by a symlink
2024-02-29 22:51:02 +03:00
func ( te * TreeEntry ) FollowLinks ( ) ( * TreeEntry , string , error ) {
2020-02-22 02:04:20 +03:00
if ! te . IsLink ( ) {
2024-02-29 22:51:02 +03:00
return nil , "" , ErrBadLink { te . Name ( ) , "not a symlink" }
2020-02-22 02:04:20 +03:00
}
entry := te
2024-02-29 22:51:02 +03:00
entryLink := ""
2020-02-22 02:04:20 +03:00
for i := 0 ; i < 999 ; i ++ {
if entry . IsLink ( ) {
2024-02-29 22:51:02 +03:00
next , link , err := entry . FollowLink ( )
entryLink = link
2020-02-22 02:04:20 +03:00
if err != nil {
2024-02-29 22:51:02 +03:00
return nil , "" , err
2020-02-22 02:04:20 +03:00
}
if next . ID == entry . ID {
2024-02-29 22:51:02 +03:00
return nil , "" , ErrBadLink {
2020-02-22 02:04:20 +03:00
entry . Name ( ) ,
"recursive link" ,
}
}
entry = next
} else {
break
}
}
if entry . IsLink ( ) {
2024-02-29 22:51:02 +03:00
return nil , "" , ErrBadLink {
2020-02-22 02:04:20 +03:00
te . Name ( ) ,
"too many levels of symbolic links" ,
}
}
2024-02-29 22:51:02 +03:00
return entry , entryLink , nil
2020-02-22 02:04:20 +03:00
}
2023-02-15 00:23:04 +03:00
// returns the Tree pointed to by this TreeEntry, or nil if this is not a tree
2023-02-12 10:08:10 +03:00
func ( te * TreeEntry ) Tree ( ) * Tree {
t , err := te . ptree . repo . getTree ( te . ID )
if err != nil {
return nil
}
2023-02-15 00:23:04 +03:00
t . ptree = te . ptree
2023-02-12 10:08:10 +03:00
return t
}
2016-12-28 19:35:52 +03:00
// GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory )
func ( te * TreeEntry ) GetSubJumpablePathName ( ) string {
if te . IsSubModule ( ) || ! te . IsDir ( ) {
return ""
}
2019-04-19 15:17:27 +03:00
tree , err := te . ptree . SubTree ( te . Name ( ) )
2016-12-28 19:35:52 +03:00
if err != nil {
2019-04-19 15:17:27 +03:00
return te . Name ( )
2016-12-28 19:35:52 +03:00
}
entries , _ := tree . ListEntries ( )
if len ( entries ) == 1 && entries [ 0 ] . IsDir ( ) {
name := entries [ 0 ] . GetSubJumpablePathName ( )
if name != "" {
2019-04-19 15:17:27 +03:00
return te . Name ( ) + "/" + name
2016-12-28 19:35:52 +03:00
}
}
2019-04-19 15:17:27 +03:00
return te . Name ( )
2016-12-28 19:35:52 +03:00
}
2016-12-22 12:30:52 +03:00
// Entries a list of entry
2016-11-04 01:16:01 +03:00
type Entries [ ] * TreeEntry
2017-09-19 11:37:03 +03:00
type customSortableEntries struct {
Comparer func ( s1 , s2 string ) bool
Entries
}
var sorter = [ ] func ( t1 , t2 * TreeEntry , cmp func ( s1 , s2 string ) bool ) bool {
func ( t1 , t2 * TreeEntry , cmp func ( s1 , s2 string ) bool ) bool {
2016-11-04 01:16:01 +03:00
return ( t1 . IsDir ( ) || t1 . IsSubModule ( ) ) && ! t2 . IsDir ( ) && ! t2 . IsSubModule ( )
} ,
2017-09-19 11:37:03 +03:00
func ( t1 , t2 * TreeEntry , cmp func ( s1 , s2 string ) bool ) bool {
2019-04-19 15:17:27 +03:00
return cmp ( t1 . Name ( ) , t2 . Name ( ) )
2016-11-04 01:16:01 +03:00
} ,
}
2017-09-19 11:37:03 +03:00
func ( ctes customSortableEntries ) Len ( ) int { return len ( ctes . Entries ) }
func ( ctes customSortableEntries ) Swap ( i , j int ) {
ctes . Entries [ i ] , ctes . Entries [ j ] = ctes . Entries [ j ] , ctes . Entries [ i ]
}
func ( ctes customSortableEntries ) Less ( i , j int ) bool {
t1 , t2 := ctes . Entries [ i ] , ctes . Entries [ j ]
2016-11-04 01:16:01 +03:00
var k int
for k = 0 ; k < len ( sorter ) - 1 ; k ++ {
2016-11-12 14:09:25 +03:00
s := sorter [ k ]
2016-11-04 01:16:01 +03:00
switch {
2017-09-19 11:37:03 +03:00
case s ( t1 , t2 , ctes . Comparer ) :
2016-11-04 01:16:01 +03:00
return true
2017-09-19 11:37:03 +03:00
case s ( t2 , t1 , ctes . Comparer ) :
2016-11-04 01:16:01 +03:00
return false
}
}
2017-09-19 11:37:03 +03:00
return sorter [ k ] ( t1 , t2 , ctes . Comparer )
2016-11-04 01:16:01 +03:00
}
2016-12-22 12:30:52 +03:00
// Sort sort the list of entry
2016-11-04 01:16:01 +03:00
func ( tes Entries ) Sort ( ) {
2017-09-19 11:37:03 +03:00
sort . Sort ( customSortableEntries { func ( s1 , s2 string ) bool {
return s1 < s2
} , tes } )
}
// CustomSort customizable string comparing sort entry list
func ( tes Entries ) CustomSort ( cmp func ( s1 , s2 string ) bool ) {
sort . Sort ( customSortableEntries { cmp , tes } )
2016-11-04 01:16:01 +03:00
}