@@ -246,7 +246,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
246
246
contentStore := lfs .NewContentStore ()
247
247
for _ , file := range opts .Files {
248
248
switch file .Operation {
249
- case "create" , "update" :
249
+ case "create" , "update" , "rename" :
250
250
if err := CreateOrUpdateFile (ctx , t , file , contentStore , repo .ID , hasOldBranch ); err != nil {
251
251
return nil , err
252
252
}
@@ -488,31 +488,32 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
488
488
}
489
489
}
490
490
491
- treeObjectContentReader := file .ContentReader
492
- var lfsMetaObject * git_model.LFSMetaObject
493
- if setting .LFS .StartServer && hasOldBranch {
494
- // Check there is no way this can return multiple infos
495
- attributesMap , err := attribute .CheckAttributes (ctx , t .gitRepo , "" /* use temp repo's working dir */ , attribute.CheckAttributeOpts {
496
- Attributes : []string {attribute .Filter },
497
- Filenames : []string {file .Options .treePath },
498
- })
491
+ var oldEntry * git.TreeEntry
492
+ // Assume that the file.ContentReader of a pure rename operation is invalid. Use the file content how it's present in
493
+ // git instead
494
+ if file .Operation == "rename" {
495
+ lastCommitID , err := t .GetLastCommit (ctx )
496
+ if err != nil {
497
+ return err
498
+ }
499
+ commit , err := t .GetCommit (lastCommitID )
499
500
if err != nil {
500
501
return err
501
502
}
502
503
503
- if attributesMap [file .Options .treePath ] != nil && attributesMap [file .Options .treePath ].Get (attribute .Filter ).ToString ().Value () == "lfs" {
504
- // OK so we are supposed to LFS this data!
505
- pointer , err := lfs .GeneratePointer (treeObjectContentReader )
506
- if err != nil {
507
- return err
508
- }
509
- lfsMetaObject = & git_model.LFSMetaObject {Pointer : pointer , RepositoryID : repoID }
510
- treeObjectContentReader = strings .NewReader (pointer .StringContent ())
504
+ if oldEntry , err = commit .GetTreeEntryByPath (file .Options .fromTreePath ); err != nil {
505
+ return err
511
506
}
512
507
}
513
508
514
- // Add the object to the database
515
- objectHash , err := t .HashObject (ctx , treeObjectContentReader )
509
+ var objectHash string
510
+ var lfsPointer * lfs.Pointer
511
+ switch file .Operation {
512
+ case "create" , "update" :
513
+ objectHash , lfsPointer , err = createOrUpdateFileHash (ctx , t , file , hasOldBranch )
514
+ case "rename" :
515
+ objectHash , lfsPointer , err = renameFileHash (ctx , t , oldEntry , file )
516
+ }
516
517
if err != nil {
517
518
return err
518
519
}
@@ -528,9 +529,9 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
528
529
}
529
530
}
530
531
531
- if lfsMetaObject != nil {
532
+ if lfsPointer != nil {
532
533
// We have an LFS object - create it
533
- lfsMetaObject , err = git_model .NewLFSMetaObject (ctx , lfsMetaObject . RepositoryID , lfsMetaObject . Pointer )
534
+ lfsMetaObject , err : = git_model .NewLFSMetaObject (ctx , repoID , * lfsPointer )
534
535
if err != nil {
535
536
return err
536
537
}
@@ -539,11 +540,20 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
539
540
return err
540
541
}
541
542
if ! exist {
542
- _ , err := file .ContentReader .Seek (0 , io .SeekStart )
543
- if err != nil {
544
- return err
543
+ var lfsContentReader io.Reader
544
+ if file .Operation != "rename" {
545
+ if _ , err := file .ContentReader .Seek (0 , io .SeekStart ); err != nil {
546
+ return err
547
+ }
548
+ lfsContentReader = file .ContentReader
549
+ } else {
550
+ if lfsContentReader , err = oldEntry .Blob ().DataAsync (); err != nil {
551
+ return err
552
+ }
553
+ defer lfsContentReader .(io.ReadCloser ).Close ()
545
554
}
546
- if err := contentStore .Put (lfsMetaObject .Pointer , file .ContentReader ); err != nil {
555
+
556
+ if err := contentStore .Put (lfsMetaObject .Pointer , lfsContentReader ); err != nil {
547
557
if _ , err2 := git_model .RemoveLFSMetaObjectByOid (ctx , repoID , lfsMetaObject .Oid ); err2 != nil {
548
558
return fmt .Errorf ("unable to remove failed inserted LFS object %s: %v (Prev Error: %w)" , lfsMetaObject .Oid , err2 , err )
549
559
}
@@ -555,6 +565,99 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
555
565
return nil
556
566
}
557
567
568
+ func createOrUpdateFileHash (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile , hasOldBranch bool ) (string , * lfs.Pointer , error ) {
569
+ treeObjectContentReader := file .ContentReader
570
+ var lfsPointer * lfs.Pointer
571
+ if setting .LFS .StartServer && hasOldBranch {
572
+ // Check there is no way this can return multiple infos
573
+ attributesMap , err := attribute .CheckAttributes (ctx , t .gitRepo , "" /* use temp repo's working dir */ , attribute.CheckAttributeOpts {
574
+ Attributes : []string {attribute .Filter },
575
+ Filenames : []string {file .Options .treePath },
576
+ })
577
+ if err != nil {
578
+ return "" , nil , err
579
+ }
580
+
581
+ if attributesMap [file .Options .treePath ] != nil && attributesMap [file .Options .treePath ].Get (attribute .Filter ).ToString ().Value () == "lfs" {
582
+ // OK so we are supposed to LFS this data!
583
+ pointer , err := lfs .GeneratePointer (treeObjectContentReader )
584
+ if err != nil {
585
+ return "" , nil , err
586
+ }
587
+ lfsPointer = & pointer
588
+ treeObjectContentReader = strings .NewReader (pointer .StringContent ())
589
+ }
590
+ }
591
+
592
+ // Add the object to the database
593
+ objectHash , err := t .HashObject (ctx , treeObjectContentReader )
594
+ if err != nil {
595
+ return "" , nil , err
596
+ }
597
+
598
+ return objectHash , lfsPointer , nil
599
+ }
600
+
601
+ func renameFileHash (ctx context.Context , t * TemporaryUploadRepository , oldEntry * git.TreeEntry , file * ChangeRepoFile ) (string , * lfs.Pointer , error ) {
602
+ if setting .LFS .StartServer {
603
+ attributesMap , err := attribute .CheckAttributes (ctx , t .gitRepo , "" /* use temp repo's working dir */ , attribute.CheckAttributeOpts {
604
+ Attributes : []string {attribute .Filter },
605
+ Filenames : []string {file .Options .treePath , file .Options .fromTreePath },
606
+ })
607
+ if err != nil {
608
+ return "" , nil , err
609
+ }
610
+
611
+ oldIsLfs := attributesMap [file .Options .fromTreePath ] != nil && attributesMap [file .Options .fromTreePath ].Get (attribute .Filter ).ToString ().Value () == "lfs"
612
+ newIsLfs := attributesMap [file .Options .treePath ] != nil && attributesMap [file .Options .treePath ].Get (attribute .Filter ).ToString ().Value () == "lfs"
613
+
614
+ // If the old and new paths are both in lfs or both not in lfs, the object hash of the old file can be used directly
615
+ // as the object doesn't change
616
+ if oldIsLfs == newIsLfs {
617
+ return oldEntry .ID .String (), nil , nil
618
+ }
619
+
620
+ oldEntryReader , err := oldEntry .Blob ().DataAsync ()
621
+ if err != nil {
622
+ return "" , nil , err
623
+ }
624
+ defer oldEntryReader .Close ()
625
+
626
+ var treeObjectContentReader io.Reader
627
+ var lfsPointer * lfs.Pointer
628
+ // If the old path is in lfs but the new isn't, read the content from lfs and add it as normal git object
629
+ // If the new path is in lfs but the old isn't, read the content from the git object and generate a lfs
630
+ // pointer of it
631
+ if oldIsLfs {
632
+ pointer , err := lfs .ReadPointer (oldEntryReader )
633
+ if err != nil {
634
+ return "" , nil , err
635
+ }
636
+ treeObjectContentReader , err = lfs .ReadMetaObject (pointer )
637
+ if err != nil {
638
+ return "" , nil , err
639
+ }
640
+ defer treeObjectContentReader .(io.ReadCloser ).Close ()
641
+ } else {
642
+ pointer , err := lfs .GeneratePointer (oldEntryReader )
643
+ if err != nil {
644
+ return "" , nil , err
645
+ }
646
+ treeObjectContentReader = strings .NewReader (pointer .StringContent ())
647
+ lfsPointer = & pointer
648
+ }
649
+
650
+ // Add the object to the database
651
+ objectID , err := t .HashObject (ctx , treeObjectContentReader )
652
+ if err != nil {
653
+ return "" , nil , err
654
+ }
655
+ return objectID , lfsPointer , nil
656
+ }
657
+
658
+ return oldEntry .ID .String (), nil , nil
659
+ }
660
+
558
661
// VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch
559
662
func VerifyBranchProtection (ctx context.Context , repo * repo_model.Repository , doer * user_model.User , branchName string , treePaths []string ) error {
560
663
protectedBranch , err := git_model .GetFirstMatchProtectedBranchRule (ctx , repo .ID , branchName )
0 commit comments