@@ -88,8 +88,25 @@ func (err ErrRepoFileDoesNotExist) Unwrap() error {
8888 return util .ErrNotExist
8989}
9090
91+ type LazyReader interface {
92+ io.Closer
93+ OpenLazyReader () error
94+ }
95+
9196// ChangeRepoFiles adds, updates or removes multiple files in the given repository
92- func ChangeRepoFiles (ctx context.Context , repo * repo_model.Repository , doer * user_model.User , opts * ChangeRepoFilesOptions ) (* structs.FilesResponse , error ) {
97+ func ChangeRepoFiles (ctx context.Context , repo * repo_model.Repository , doer * user_model.User , opts * ChangeRepoFilesOptions ) (_ * structs.FilesResponse , errRet error ) {
98+ var addedLfsPointers []lfs.Pointer
99+ defer func () {
100+ if errRet != nil {
101+ for _ , lfsPointer := range addedLfsPointers {
102+ _ , err := git_model .RemoveLFSMetaObjectByOid (ctx , repo .ID , lfsPointer .Oid )
103+ if err != nil {
104+ log .Error ("ChangeRepoFiles: RemoveLFSMetaObjectByOid failed: %v" , err )
105+ }
106+ }
107+ }
108+ }()
109+
93110 err := repo .MustNotBeArchived ()
94111 if err != nil {
95112 return nil , err
@@ -241,10 +258,14 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
241258 lfsContentStore := lfs .NewContentStore ()
242259 for _ , file := range opts .Files {
243260 switch file .Operation {
244- case "create" , "update" , "rename" :
245- if err = CreateUpdateRenameFile (ctx , t , file , lfsContentStore , repo .ID , hasOldBranch ); err != nil {
261+ case "create" , "update" , "rename" , "upload" :
262+ addedLfsPointer , err := modifyFile (ctx , t , file , lfsContentStore , repo .ID )
263+ if err != nil {
246264 return nil , err
247265 }
266+ if addedLfsPointer != nil {
267+ addedLfsPointers = append (addedLfsPointers , * addedLfsPointer )
268+ }
248269 case "delete" :
249270 if err = t .RemoveFilesFromIndex (ctx , file .TreePath ); err != nil {
250271 return nil , err
@@ -366,6 +387,7 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string {
366387
367388// handles the check for various issues for ChangeRepoFiles
368389func handleCheckErrors (file * ChangeRepoFile , commit * git.Commit , opts * ChangeRepoFilesOptions ) error {
390+ // create: old must not exist; update: old must exist; upload: old existence doesn't matter
369391 if file .Operation == "update" || file .Operation == "delete" || file .Operation == "rename" {
370392 fromEntry , err := commit .GetTreeEntryByPath (file .Options .fromTreePath )
371393 if err != nil {
@@ -403,7 +425,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
403425 file .Options .executable = fromEntry .IsExecutable ()
404426 }
405427
406- if file .Operation == "create" || file .Operation == "update" || file .Operation == "rename" {
428+ if file .Operation == "create" || file .Operation == "update" || file .Operation == "upload" || file . Operation == " rename" {
407429 // For operation's target path, we need to make sure no parts of the path are existing files or links
408430 // except for the last item in the path (which is the file name).
409431 // And that shouldn't exist IF it is a new file OR is being moved to a new path.
@@ -454,18 +476,23 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
454476 return nil
455477}
456478
457- func CreateUpdateRenameFile (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile , contentStore * lfs.ContentStore , repoID int64 , hasOldBranch bool ) error {
479+ func modifyFile (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile , contentStore * lfs.ContentStore , repoID int64 ) (addedLfsPointer * lfs.Pointer , _ error ) {
480+ if rd , ok := file .ContentReader .(LazyReader ); ok {
481+ if err := rd .OpenLazyReader (); err != nil {
482+ return nil , fmt .Errorf ("OpenLazyReader: %w" , err )
483+ }
484+ defer rd .Close ()
485+ }
486+
458487 // Get the two paths (might be the same if not moving) from the index if they exist
459488 filesInIndex , err := t .LsFiles (ctx , file .TreePath , file .FromTreePath )
460489 if err != nil {
461- return fmt .Errorf ("UpdateRepoFile : %w" , err )
490+ return nil , fmt .Errorf ("LsFiles : %w" , err )
462491 }
463492 // If is a new file (not updating) then the given path shouldn't exist
464493 if file .Operation == "create" {
465494 if slices .Contains (filesInIndex , file .TreePath ) {
466- return ErrRepoFileAlreadyExists {
467- Path : file .TreePath ,
468- }
495+ return nil , ErrRepoFileAlreadyExists {Path : file .TreePath }
469496 }
470497 }
471498
@@ -474,53 +501,54 @@ func CreateUpdateRenameFile(ctx context.Context, t *TemporaryUploadRepository, f
474501 for _ , indexFile := range filesInIndex {
475502 if indexFile == file .Options .fromTreePath {
476503 if err = t .RemoveFilesFromIndex (ctx , file .FromTreePath ); err != nil {
477- return err
504+ return nil , err
478505 }
479506 }
480507 }
481508 }
482509
483510 var writeObjectRet * writeRepoObjectRet
484511 switch file .Operation {
485- case "create" , "update" :
486- writeObjectRet , err = writeRepoObjectForCreateOrUpdate (ctx , t , file )
512+ case "create" , "update" , "upload" :
513+ writeObjectRet , err = writeRepoObjectForModify (ctx , t , file )
487514 case "rename" :
488515 writeObjectRet , err = writeRepoObjectForRename (ctx , t , file )
489516 default :
490- return util .NewInvalidArgumentErrorf ("unknown file modification operation: '%s'" , file .Operation )
517+ return nil , util .NewInvalidArgumentErrorf ("unknown file modification operation: '%s'" , file .Operation )
491518 }
492519 if err != nil {
493- return err
520+ return nil , err
494521 }
495522
496523 // Add the object to the index, the "file.Options.executable" is set in handleCheckErrors by the caller (legacy hacky approach)
497524 if err = t .AddObjectToIndex (ctx , util .Iif (file .Options .executable , "100755" , "100644" ), writeObjectRet .ObjectHash , file .Options .treePath ); err != nil {
498- return err
525+ return nil , err
499526 }
500527
501528 if writeObjectRet .LfsContent == nil {
502- return nil // No LFS pointer, so nothing to do
529+ return nil , nil // No LFS pointer, so nothing to do
503530 }
504531 defer writeObjectRet .LfsContent .Close ()
505532
506533 // Now we must store the content into an LFS object
507534 lfsMetaObject , err := git_model .NewLFSMetaObject (ctx , repoID , writeObjectRet .LfsPointer )
508535 if err != nil {
509- return err
510- }
511- if exist , err := contentStore .Exists (lfsMetaObject .Pointer ); err != nil {
512- return err
513- } else if exist {
514- return nil
536+ return nil , err
515537 }
516-
517- err = contentStore .Put (lfsMetaObject .Pointer , writeObjectRet .LfsContent )
538+ exist , err := contentStore .Exists (lfsMetaObject .Pointer )
518539 if err != nil {
519- if _ , errRemove := git_model .RemoveLFSMetaObjectByOid (ctx , repoID , lfsMetaObject .Oid ); errRemove != nil {
520- return fmt .Errorf ("unable to remove failed inserted LFS object %s: %v (Prev Error: %w)" , lfsMetaObject .Oid , errRemove , err )
540+ return nil , err
541+ }
542+ if ! exist {
543+ err = contentStore .Put (lfsMetaObject .Pointer , writeObjectRet .LfsContent )
544+ if err != nil {
545+ if _ , errRemove := git_model .RemoveLFSMetaObjectByOid (ctx , repoID , lfsMetaObject .Oid ); errRemove != nil {
546+ return nil , fmt .Errorf ("unable to remove failed inserted LFS object %s: %v (Prev Error: %w)" , lfsMetaObject .Oid , errRemove , err )
547+ }
548+ return nil , err
521549 }
522550 }
523- return err
551+ return & lfsMetaObject . Pointer , nil
524552}
525553
526554func checkIsLfsFileInGitAttributes (ctx context.Context , t * TemporaryUploadRepository , paths []string ) (ret []bool , err error ) {
@@ -544,8 +572,8 @@ type writeRepoObjectRet struct {
544572 LfsPointer lfs.Pointer
545573}
546574
547- // writeRepoObjectForCreateOrUpdate hashes the git object for create or update operations
548- func writeRepoObjectForCreateOrUpdate (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile ) (ret * writeRepoObjectRet , err error ) {
575+ // writeRepoObjectForModify hashes the git object for create or update operations
576+ func writeRepoObjectForModify (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile ) (ret * writeRepoObjectRet , err error ) {
549577 ret = & writeRepoObjectRet {}
550578 treeObjectContentReader := file .ContentReader
551579 if setting .LFS .StartServer {
@@ -574,7 +602,7 @@ func writeRepoObjectForCreateOrUpdate(ctx context.Context, t *TemporaryUploadRep
574602 return ret , nil
575603}
576604
577- // writeRepoObjectForRename the same as writeRepoObjectForCreateOrUpdate buf for "rename"
605+ // writeRepoObjectForRename the same as writeRepoObjectForModify buf for "rename"
578606func writeRepoObjectForRename (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile ) (ret * writeRepoObjectRet , err error ) {
579607 lastCommitID , err := t .GetLastCommit (ctx )
580608 if err != nil {
0 commit comments