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  }