@@ -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