@@ -5,11 +5,14 @@ package files
55
66import (
77 "context"
8+ "io"
89 "net/url"
910 "path"
11+ "strings"
1012
1113 repo_model "code.gitea.io/gitea/models/repo"
1214 "code.gitea.io/gitea/modules/git"
15+ "code.gitea.io/gitea/modules/lfs"
1316 "code.gitea.io/gitea/modules/setting"
1417 api "code.gitea.io/gitea/modules/structs"
1518 "code.gitea.io/gitea/modules/util"
@@ -35,6 +38,7 @@ func (ct *ContentType) String() string {
3538type GetContentsOrListOptions struct {
3639 TreePath string
3740 IncludeSingleFileContent bool // include the file's content when the tree path is a file
41+ IncludeLfsMetadata bool
3842}
3943
4044// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
@@ -65,9 +69,10 @@ func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, gitRepo
6569 }
6670 ret .DirContents = make ([]* api.ContentsResponse , 0 , len (entries ))
6771 for _ , e := range entries {
68- // never include file content when listing a directory
69- subTreePath := path .Join (opts .TreePath , e .Name ())
70- fileContentResponse , err := GetFileContents (ctx , repo , gitRepo , refCommit , GetContentsOrListOptions {TreePath : subTreePath , IncludeSingleFileContent : false })
72+ subOpts := opts
73+ subOpts .TreePath = path .Join (opts .TreePath , e .Name ())
74+ subOpts .IncludeSingleFileContent = false // never include file content when listing a directory
75+ fileContentResponse , err := GetFileContents (ctx , repo , gitRepo , refCommit , subOpts )
7176 if err != nil {
7277 return ret , err
7378 }
@@ -118,7 +123,7 @@ func GetFileContents(ctx context.Context, repo *repo_model.Repository, gitRepo *
118123 return getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
119124}
120125
121- func getFileContentsByEntryInternal (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , entry * git.TreeEntry , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
126+ func getFileContentsByEntryInternal (_ context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , entry * git.TreeEntry , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
122127 refType := refCommit .RefName .RefType ()
123128 commit := refCommit .Commit
124129 selfURL , err := url .Parse (repo .APIURL () + "/contents/" + util .PathEscapeSegments (opts .TreePath ) + "?ref=" + url .QueryEscape (refCommit .InputRef ))
@@ -164,12 +169,17 @@ func getFileContentsByEntryInternal(ctx context.Context, repo *repo_model.Reposi
164169 contentsResponse .Type = string (ContentTypeRegular )
165170 // if it is listing the repo root dir, don't waste system resources on reading content
166171 if opts .IncludeSingleFileContent {
167- blobResponse , err := GetBlobBySHA (ctx , repo , gitRepo , entry .ID .String ())
172+ blobResponse , err := GetBlobBySHA (repo , gitRepo , entry .ID .String ())
173+ if err != nil {
174+ return nil , err
175+ }
176+ contentsResponse .Encoding , contentsResponse .Content = blobResponse .Encoding , blobResponse .Content
177+ contentsResponse .LfsOid , contentsResponse .LfsSize = blobResponse .LfsOid , blobResponse .LfsSize
178+ } else if opts .IncludeLfsMetadata {
179+ contentsResponse .LfsOid , contentsResponse .LfsSize , err = parsePossibleLfsPointerBlob (gitRepo , entry .ID .String ())
168180 if err != nil {
169181 return nil , err
170182 }
171- contentsResponse .Encoding = blobResponse .Encoding
172- contentsResponse .Content = blobResponse .Content
173183 }
174184 } else if entry .IsDir () {
175185 contentsResponse .Type = string (ContentTypeDir )
@@ -221,8 +231,7 @@ func getFileContentsByEntryInternal(ctx context.Context, repo *repo_model.Reposi
221231 return contentsResponse , nil
222232}
223233
224- // GetBlobBySHA get the GitBlobResponse of a repository using a sha hash.
225- func GetBlobBySHA (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , sha string ) (* api.GitBlobResponse , error ) {
234+ func GetBlobBySHA (repo * repo_model.Repository , gitRepo * git.Repository , sha string ) (* api.GitBlobResponse , error ) {
226235 gitBlob , err := gitRepo .GetBlob (sha )
227236 if err != nil {
228237 return nil , err
@@ -232,12 +241,49 @@ func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
232241 URL : repo .APIURL () + "/git/blobs/" + url .PathEscape (gitBlob .ID .String ()),
233242 Size : gitBlob .Size (),
234243 }
235- if gitBlob .Size () <= setting .API .DefaultMaxBlobSize {
236- content , err := gitBlob .GetBlobContentBase64 ()
237- if err != nil {
238- return nil , err
239- }
240- ret .Encoding , ret .Content = util .ToPointer ("base64" ), & content
244+
245+ blobSize := gitBlob .Size ()
246+ if blobSize > setting .API .DefaultMaxBlobSize {
247+ return ret , nil
248+ }
249+
250+ var originContent * strings.Builder
251+ if 0 < blobSize && blobSize < lfs .MetaFileMaxSize {
252+ originContent = & strings.Builder {}
253+ }
254+
255+ content , err := gitBlob .GetBlobContentBase64 (originContent )
256+ if err != nil {
257+ return nil , err
258+ }
259+
260+ ret .Encoding , ret .Content = util .ToPointer ("base64" ), & content
261+ if originContent != nil {
262+ ret .LfsOid , ret .LfsSize = parsePossibleLfsPointerBuffer (strings .NewReader (originContent .String ()))
241263 }
242264 return ret , nil
243265}
266+
267+ func parsePossibleLfsPointerBuffer (r io.Reader ) (* string , * int64 ) {
268+ p , _ := lfs .ReadPointer (r )
269+ if p .IsValid () {
270+ return & p .Oid , & p .Size
271+ }
272+ return nil , nil
273+ }
274+
275+ func parsePossibleLfsPointerBlob (gitRepo * git.Repository , sha string ) (* string , * int64 , error ) {
276+ gitBlob , err := gitRepo .GetBlob (sha )
277+ if err != nil {
278+ return nil , nil , err
279+ }
280+ if gitBlob .Size () > lfs .MetaFileMaxSize {
281+ return nil , nil , nil // not a LFS pointer
282+ }
283+ buf , err := gitBlob .GetBlobContent (lfs .MetaFileMaxSize )
284+ if err != nil {
285+ return nil , nil , err
286+ }
287+ oid , size := parsePossibleLfsPointerBuffer (strings .NewReader (buf ))
288+ return oid , size , nil
289+ }
0 commit comments