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"
2024-08-12 18:16:55 +03:00
"code.gitea.io/gitea/modules/log"
2016-11-04 01:16:01 +03:00
)
2024-08-12 18:16:55 +03:00
// TreeEntry the leaf in the git tree
type TreeEntry struct {
ID ObjectID
ptree * Tree
entryMode EntryMode
name string
size int64
sized bool
fullName string
}
// Name returns the name of the entry
func ( te * TreeEntry ) Name ( ) string {
if te . fullName != "" {
return te . fullName
}
return te . name
}
// Mode returns the mode of the entry
func ( te * TreeEntry ) Mode ( ) EntryMode {
return te . entryMode
}
// Size returns the size of the entry
func ( te * TreeEntry ) Size ( ) int64 {
if te . IsDir ( ) {
return 0
} else if te . sized {
return te . size
}
2024-08-20 20:04:57 +03:00
wr , rd , cancel , err := te . ptree . repo . CatFileBatchCheck ( te . ptree . repo . Ctx )
if err != nil {
log . Debug ( "error whilst reading size for %s in %s. Error: %v" , te . ID . String ( ) , te . ptree . repo . Path , err )
return 0
}
2024-08-12 18:16:55 +03:00
defer cancel ( )
2024-08-20 20:04:57 +03:00
_ , err = wr . Write ( [ ] byte ( te . ID . String ( ) + "\n" ) )
2024-08-12 18:16:55 +03:00
if err != nil {
log . Debug ( "error whilst reading size for %s in %s. Error: %v" , te . ID . String ( ) , te . ptree . repo . Path , err )
return 0
}
_ , _ , te . size , err = ReadBatchLine ( rd )
if err != nil {
log . Debug ( "error whilst reading size for %s in %s. Error: %v" , te . ID . String ( ) , te . ptree . repo . Path , err )
return 0
}
te . sized = true
return te . size
}
// IsSubModule if the entry is a sub module
func ( te * TreeEntry ) IsSubModule ( ) bool {
return te . entryMode == EntryModeCommit
}
// IsDir if the entry is a sub dir
func ( te * TreeEntry ) IsDir ( ) bool {
return te . entryMode == EntryModeTree
}
// IsLink if the entry is a symlink
func ( te * TreeEntry ) IsLink ( ) bool {
return te . entryMode == EntryModeSymlink
}
// IsRegular if the entry is a regular file
func ( te * TreeEntry ) IsRegular ( ) bool {
return te . entryMode == EntryModeBlob
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func ( te * TreeEntry ) IsExecutable ( ) bool {
return te . entryMode == EntryModeExec
}
// Blob returns the blob object the entry
func ( te * TreeEntry ) Blob ( ) * Blob {
return & Blob {
ID : te . ID ,
name : te . Name ( ) ,
size : te . size ,
gotSize : te . sized ,
repo : te . ptree . repo ,
}
}
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
}