code.gitea.io/gitea@v1.21.7/services/repository/files/tree.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package files
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net/url"
    10  
    11  	"code.gitea.io/gitea/models"
    12  	repo_model "code.gitea.io/gitea/models/repo"
    13  	"code.gitea.io/gitea/modules/git"
    14  	"code.gitea.io/gitea/modules/setting"
    15  	api "code.gitea.io/gitea/modules/structs"
    16  )
    17  
    18  // GetTreeBySHA get the GitTreeResponse of a repository using a sha hash.
    19  func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string, page, perPage int, recursive bool) (*api.GitTreeResponse, error) {
    20  	gitTree, err := gitRepo.GetTree(sha)
    21  	if err != nil || gitTree == nil {
    22  		return nil, models.ErrSHANotFound{
    23  			SHA: sha,
    24  		}
    25  	}
    26  	tree := new(api.GitTreeResponse)
    27  	tree.SHA = gitTree.ResolvedID.String()
    28  	tree.URL = repo.APIURL() + "/git/trees/" + url.PathEscape(tree.SHA)
    29  	var entries git.Entries
    30  	if recursive {
    31  		entries, err = gitTree.ListEntriesRecursiveWithSize()
    32  	} else {
    33  		entries, err = gitTree.ListEntries()
    34  	}
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	apiURL := repo.APIURL()
    39  	apiURLLen := len(apiURL)
    40  
    41  	// 51 is len(sha1) + len("/git/blobs/"). 40 + 11.
    42  	blobURL := make([]byte, apiURLLen+51)
    43  	copy(blobURL, apiURL)
    44  	copy(blobURL[apiURLLen:], "/git/blobs/")
    45  
    46  	// 51 is len(sha1) + len("/git/trees/"). 40 + 11.
    47  	treeURL := make([]byte, apiURLLen+51)
    48  	copy(treeURL, apiURL)
    49  	copy(treeURL[apiURLLen:], "/git/trees/")
    50  
    51  	// 40 is the size of the sha1 hash in hexadecimal format.
    52  	copyPos := len(treeURL) - git.SHAFullLength
    53  
    54  	if perPage <= 0 || perPage > setting.API.DefaultGitTreesPerPage {
    55  		perPage = setting.API.DefaultGitTreesPerPage
    56  	}
    57  	if page <= 0 {
    58  		page = 1
    59  	}
    60  	tree.Page = page
    61  	tree.TotalCount = len(entries)
    62  	rangeStart := perPage * (page - 1)
    63  	if rangeStart >= len(entries) {
    64  		return tree, nil
    65  	}
    66  	var rangeEnd int
    67  	if len(entries) > perPage {
    68  		tree.Truncated = true
    69  	}
    70  	if rangeStart+perPage < len(entries) {
    71  		rangeEnd = rangeStart + perPage
    72  	} else {
    73  		rangeEnd = len(entries)
    74  	}
    75  	tree.Entries = make([]api.GitEntry, rangeEnd-rangeStart)
    76  	for e := rangeStart; e < rangeEnd; e++ {
    77  		i := e - rangeStart
    78  
    79  		tree.Entries[i].Path = entries[e].Name()
    80  		tree.Entries[i].Mode = fmt.Sprintf("%06o", entries[e].Mode())
    81  		tree.Entries[i].Type = entries[e].Type()
    82  		tree.Entries[i].Size = entries[e].Size()
    83  		tree.Entries[i].SHA = entries[e].ID.String()
    84  
    85  		if entries[e].IsDir() {
    86  			copy(treeURL[copyPos:], entries[e].ID.String())
    87  			tree.Entries[i].URL = string(treeURL)
    88  		} else if entries[e].IsSubModule() {
    89  			// In Github Rest API Version=2022-11-28, if a tree entry is a submodule,
    90  			// its url will be returned as an empty string.
    91  			// So the URL will be set to "" here.
    92  			tree.Entries[i].URL = ""
    93  		} else {
    94  			copy(blobURL[copyPos:], entries[e].ID.String())
    95  			tree.Entries[i].URL = string(blobURL)
    96  		}
    97  	}
    98  	return tree, nil
    99  }