@@ -5,11 +5,14 @@ package files
5
5
6
6
import (
7
7
"context"
8
+ "io"
8
9
"net/url"
9
10
"path"
11
+ "strings"
10
12
11
13
repo_model "code.gitea.io/gitea/models/repo"
12
14
"code.gitea.io/gitea/modules/git"
15
+ "code.gitea.io/gitea/modules/lfs"
13
16
"code.gitea.io/gitea/modules/setting"
14
17
api "code.gitea.io/gitea/modules/structs"
15
18
"code.gitea.io/gitea/modules/util"
@@ -35,6 +38,7 @@ func (ct *ContentType) String() string {
35
38
type GetContentsOrListOptions struct {
36
39
TreePath string
37
40
IncludeSingleFileContent bool // include the file's content when the tree path is a file
41
+ IncludeLfsMetadata bool
38
42
}
39
43
40
44
// 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
65
69
}
66
70
ret .DirContents = make ([]* api.ContentsResponse , 0 , len (entries ))
67
71
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 )
71
76
if err != nil {
72
77
return ret , err
73
78
}
@@ -118,7 +123,7 @@ func GetFileContents(ctx context.Context, repo *repo_model.Repository, gitRepo *
118
123
return getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
119
124
}
120
125
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 ) {
122
127
refType := refCommit .RefName .RefType ()
123
128
commit := refCommit .Commit
124
129
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
164
169
contentsResponse .Type = string (ContentTypeRegular )
165
170
// if it is listing the repo root dir, don't waste system resources on reading content
166
171
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 ())
168
180
if err != nil {
169
181
return nil , err
170
182
}
171
- contentsResponse .Encoding = blobResponse .Encoding
172
- contentsResponse .Content = blobResponse .Content
173
183
}
174
184
} else if entry .IsDir () {
175
185
contentsResponse .Type = string (ContentTypeDir )
@@ -221,8 +231,7 @@ func getFileContentsByEntryInternal(ctx context.Context, repo *repo_model.Reposi
221
231
return contentsResponse , nil
222
232
}
223
233
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 ) {
226
235
gitBlob , err := gitRepo .GetBlob (sha )
227
236
if err != nil {
228
237
return nil , err
@@ -232,12 +241,49 @@ func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
232
241
URL : repo .APIURL () + "/git/blobs/" + url .PathEscape (gitBlob .ID .String ()),
233
242
Size : gitBlob .Size (),
234
243
}
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 ()))
241
263
}
242
264
return ret , nil
243
265
}
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