@@ -88,8 +88,25 @@ func (err ErrRepoFileDoesNotExist) Unwrap() error {
88
88
return util .ErrNotExist
89
89
}
90
90
91
+ type LazyReader interface {
92
+ io.Closer
93
+ OpenLazyReader () error
94
+ }
95
+
91
96
// 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
+
93
110
err := repo .MustNotBeArchived ()
94
111
if err != nil {
95
112
return nil , err
@@ -241,10 +258,14 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
241
258
lfsContentStore := lfs .NewContentStore ()
242
259
for _ , file := range opts .Files {
243
260
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 {
246
264
return nil , err
247
265
}
266
+ if addedLfsPointer != nil {
267
+ addedLfsPointers = append (addedLfsPointers , * addedLfsPointer )
268
+ }
248
269
case "delete" :
249
270
if err = t .RemoveFilesFromIndex (ctx , file .TreePath ); err != nil {
250
271
return nil , err
@@ -366,6 +387,7 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string {
366
387
367
388
// handles the check for various issues for ChangeRepoFiles
368
389
func 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
369
391
if file .Operation == "update" || file .Operation == "delete" || file .Operation == "rename" {
370
392
fromEntry , err := commit .GetTreeEntryByPath (file .Options .fromTreePath )
371
393
if err != nil {
@@ -403,7 +425,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
403
425
file .Options .executable = fromEntry .IsExecutable ()
404
426
}
405
427
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" {
407
429
// For operation's target path, we need to make sure no parts of the path are existing files or links
408
430
// except for the last item in the path (which is the file name).
409
431
// 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
454
476
return nil
455
477
}
456
478
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
+
458
487
// Get the two paths (might be the same if not moving) from the index if they exist
459
488
filesInIndex , err := t .LsFiles (ctx , file .TreePath , file .FromTreePath )
460
489
if err != nil {
461
- return fmt .Errorf ("UpdateRepoFile : %w" , err )
490
+ return nil , fmt .Errorf ("LsFiles : %w" , err )
462
491
}
463
492
// If is a new file (not updating) then the given path shouldn't exist
464
493
if file .Operation == "create" {
465
494
if slices .Contains (filesInIndex , file .TreePath ) {
466
- return ErrRepoFileAlreadyExists {
467
- Path : file .TreePath ,
468
- }
495
+ return nil , ErrRepoFileAlreadyExists {Path : file .TreePath }
469
496
}
470
497
}
471
498
@@ -474,53 +501,54 @@ func CreateUpdateRenameFile(ctx context.Context, t *TemporaryUploadRepository, f
474
501
for _ , indexFile := range filesInIndex {
475
502
if indexFile == file .Options .fromTreePath {
476
503
if err = t .RemoveFilesFromIndex (ctx , file .FromTreePath ); err != nil {
477
- return err
504
+ return nil , err
478
505
}
479
506
}
480
507
}
481
508
}
482
509
483
510
var writeObjectRet * writeRepoObjectRet
484
511
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 )
487
514
case "rename" :
488
515
writeObjectRet , err = writeRepoObjectForRename (ctx , t , file )
489
516
default :
490
- return util .NewInvalidArgumentErrorf ("unknown file modification operation: '%s'" , file .Operation )
517
+ return nil , util .NewInvalidArgumentErrorf ("unknown file modification operation: '%s'" , file .Operation )
491
518
}
492
519
if err != nil {
493
- return err
520
+ return nil , err
494
521
}
495
522
496
523
// Add the object to the index, the "file.Options.executable" is set in handleCheckErrors by the caller (legacy hacky approach)
497
524
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
499
526
}
500
527
501
528
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
503
530
}
504
531
defer writeObjectRet .LfsContent .Close ()
505
532
506
533
// Now we must store the content into an LFS object
507
534
lfsMetaObject , err := git_model .NewLFSMetaObject (ctx , repoID , writeObjectRet .LfsPointer )
508
535
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
515
537
}
516
-
517
- err = contentStore .Put (lfsMetaObject .Pointer , writeObjectRet .LfsContent )
538
+ exist , err := contentStore .Exists (lfsMetaObject .Pointer )
518
539
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
521
549
}
522
550
}
523
- return err
551
+ return & lfsMetaObject . Pointer , nil
524
552
}
525
553
526
554
func checkIsLfsFileInGitAttributes (ctx context.Context , t * TemporaryUploadRepository , paths []string ) (ret []bool , err error ) {
@@ -544,8 +572,8 @@ type writeRepoObjectRet struct {
544
572
LfsPointer lfs.Pointer
545
573
}
546
574
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 ) {
549
577
ret = & writeRepoObjectRet {}
550
578
treeObjectContentReader := file .ContentReader
551
579
if setting .LFS .StartServer {
@@ -574,7 +602,7 @@ func writeRepoObjectForCreateOrUpdate(ctx context.Context, t *TemporaryUploadRep
574
602
return ret , nil
575
603
}
576
604
577
- // writeRepoObjectForRename the same as writeRepoObjectForCreateOrUpdate buf for "rename"
605
+ // writeRepoObjectForRename the same as writeRepoObjectForModify buf for "rename"
578
606
func writeRepoObjectForRename (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile ) (ret * writeRepoObjectRet , err error ) {
579
607
lastCommitID , err := t .GetLastCommit (ctx )
580
608
if err != nil {
0 commit comments