@@ -5,13 +5,11 @@ package files
55
66import (
77 "context"
8- "fmt"
98 "net/url"
109 "path"
1110
1211 repo_model "code.gitea.io/gitea/models/repo"
1312 "code.gitea.io/gitea/modules/git"
14- "code.gitea.io/gitea/modules/gitrepo"
1513 "code.gitea.io/gitea/modules/setting"
1614 api "code.gitea.io/gitea/modules/structs"
1715 "code.gitea.io/gitea/modules/util"
@@ -34,54 +32,47 @@ func (ct *ContentType) String() string {
3432 return string (* ct )
3533}
3634
35+ type GetContentsOrListOptions struct {
36+ IncludeSingleFileContent bool // include the file's content when the tree path is a file
37+ }
38+
3739// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
3840// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
39- func GetContentsOrList (ctx context.Context , repo * repo_model.Repository , refCommit * utils.RefCommit , treePath string ) (any , error ) {
40- if repo .IsEmpty {
41- return make ([]any , 0 ), nil
41+ func GetContentsOrList (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , treePath string , opts GetContentsOrListOptions ) (ret api.ContentsExtResponse , _ error ) {
42+ entry , err := prepareGetContentsEntry (refCommit , & treePath )
43+ if repo .IsEmpty && treePath == "" {
44+ return api.ContentsExtResponse {DirContents : make ([]* api.ContentsResponse , 0 )}, nil
4245 }
43-
44- // Check that the path given in opts.treePath is valid (not a git path)
45- cleanTreePath := CleanGitTreePath (treePath )
46- if cleanTreePath == "" && treePath != "" {
47- return nil , ErrFilenameInvalid {
48- Path : treePath ,
49- }
50- }
51- treePath = cleanTreePath
52-
53- // Get the commit object for the ref
54- commit := refCommit .Commit
55-
56- entry , err := commit .GetTreeEntryByPath (treePath )
5746 if err != nil {
58- return nil , err
47+ return ret , err
5948 }
6049
50+ // get file contents
6151 if entry .Type () != "tree" {
62- return GetContents (ctx , repo , refCommit , treePath , false )
52+ ret .FileContents , err = getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
53+ return ret , err
6354 }
6455
65- // We are in a directory, so we return a list of FileContentResponse objects
66- var fileList []* api.ContentsResponse
67-
68- gitTree , err := commit .SubTree (treePath )
56+ // list directory contents
57+ gitTree , err := refCommit .Commit .SubTree (treePath )
6958 if err != nil {
70- return nil , err
59+ return ret , err
7160 }
7261 entries , err := gitTree .ListEntries ()
7362 if err != nil {
74- return nil , err
63+ return ret , err
7564 }
65+ opts = GetContentsOrListOptions {IncludeSingleFileContent : false } // never include file content when listing a directory
66+ ret .DirContents = make ([]* api.ContentsResponse , 0 , len (entries ))
7667 for _ , e := range entries {
7768 subTreePath := path .Join (treePath , e .Name ())
78- fileContentResponse , err := GetContents (ctx , repo , refCommit , subTreePath , true )
69+ fileContentResponse , err := GetFileContents (ctx , repo , gitRepo , refCommit , subTreePath , opts )
7970 if err != nil {
80- return nil , err
71+ return ret , err
8172 }
82- fileList = append (fileList , fileContentResponse )
73+ ret . DirContents = append (ret . DirContents , fileContentResponse )
8374 }
84- return fileList , nil
75+ return ret , nil
8576}
8677
8778// GetObjectTypeFromTreeEntry check what content is behind it
@@ -100,34 +91,36 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType {
10091 }
10192}
10293
103- // GetContents gets the metadata on a file's contents. Ref can be a branch, commit or tag
104- func GetContents (ctx context.Context , repo * repo_model.Repository , refCommit * utils.RefCommit , treePath string , forList bool ) (* api.ContentsResponse , error ) {
94+ func prepareGetContentsEntry (refCommit * utils.RefCommit , treePath * string ) (* git.TreeEntry , error ) {
10595 // Check that the path given in opts.treePath is valid (not a git path)
106- cleanTreePath := CleanGitTreePath (treePath )
107- if cleanTreePath == "" && treePath != "" {
108- return nil , ErrFilenameInvalid {
109- Path : treePath ,
110- }
96+ cleanTreePath := CleanGitTreePath (* treePath )
97+ if cleanTreePath == "" && * treePath != "" {
98+ return nil , ErrFilenameInvalid {Path : * treePath }
11199 }
112- treePath = cleanTreePath
100+ * treePath = cleanTreePath
113101
114- gitRepo , closer , err := gitrepo .RepositoryFromContextOrOpen (ctx , repo )
115- if err != nil {
116- return nil , err
102+ // Only allow safe ref types
103+ refType := refCommit .RefName .RefType ()
104+ if refType != git .RefTypeBranch && refType != git .RefTypeTag && refType != git .RefTypeCommit {
105+ return nil , util .NewNotExistErrorf ("no commit found for the ref [ref: %s]" , refCommit .RefName )
117106 }
118- defer closer .Close ()
119107
120- commit := refCommit .Commit
121- entry , err := commit .GetTreeEntryByPath (treePath )
108+ return refCommit .Commit .GetTreeEntryByPath (* treePath )
109+ }
110+
111+ // GetFileContents gets the metadata on a file's contents. Ref can be a branch, commit or tag
112+ func GetFileContents (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , treePath string , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
113+ entry , err := prepareGetContentsEntry (refCommit , & treePath )
122114 if err != nil {
123115 return nil , err
124116 }
117+ return getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
118+ }
125119
120+ func getFileContentsByEntryInternal (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , entry * git.TreeEntry , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
126121 refType := refCommit .RefName .RefType ()
127- if refType != git .RefTypeBranch && refType != git .RefTypeTag && refType != git .RefTypeCommit {
128- return nil , fmt .Errorf ("no commit found for the ref [ref: %s]" , refCommit .RefName )
129- }
130-
122+ commit := refCommit .Commit
123+ treePath := entry .Name ()
131124 selfURL , err := url .Parse (repo .APIURL () + "/contents/" + util .PathEscapeSegments (treePath ) + "?ref=" + url .QueryEscape (refCommit .InputRef ))
132125 if err != nil {
133126 return nil , err
@@ -139,7 +132,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
139132 return nil , err
140133 }
141134
142- lastCommit , err := commit .GetCommitByPath (treePath )
135+ lastCommit , err := refCommit . Commit .GetCommitByPath (treePath )
143136 if err != nil {
144137 return nil , err
145138 }
@@ -170,7 +163,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
170163 if entry .IsRegular () || entry .IsExecutable () {
171164 contentsResponse .Type = string (ContentTypeRegular )
172165 // if it is listing the repo root dir, don't waste system resources on reading content
173- if ! forList {
166+ if opts . IncludeSingleFileContent {
174167 blobResponse , err := GetBlobBySHA (ctx , repo , gitRepo , entry .ID .String ())
175168 if err != nil {
176169 return nil , err
0 commit comments