@@ -372,6 +372,7 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string {
372372
373373// handles the check for various issues for ChangeRepoFiles
374374func handleCheckErrors (file * ChangeRepoFile , commit * git.Commit , opts * ChangeRepoFilesOptions ) error {
375+ // FIXME: should it also handle "rename" errors?
375376 if file .Operation == "update" || file .Operation == "delete" {
376377 fromEntry , err := commit .GetTreeEntryByPath (file .Options .fromTreePath )
377378 if err != nil {
@@ -407,6 +408,8 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
407408 }
408409 file .Options .executable = fromEntry .IsExecutable ()
409410 }
411+
412+ // FIXME: should it also handle "rename" errors?
410413 if file .Operation == "create" || file .Operation == "update" {
411414 // For the path where this file will be created/updated, we need to make
412415 // sure no parts of the path are existing files or links except for the last
@@ -481,181 +484,180 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
481484 if file .Options .fromTreePath != file .Options .treePath && len (filesInIndex ) > 0 {
482485 for _ , indexFile := range filesInIndex {
483486 if indexFile == file .Options .fromTreePath {
484- if err : = t .RemoveFilesFromIndex (ctx , file .FromTreePath ); err != nil {
487+ if err = t .RemoveFilesFromIndex (ctx , file .FromTreePath ); err != nil {
485488 return err
486489 }
487490 }
488491 }
489492 }
490493
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 )
500- if err != nil {
501- return err
502- }
503-
504- if oldEntry , err = commit .GetTreeEntryByPath (file .Options .fromTreePath ); err != nil {
505- return err
506- }
507- }
508-
509494 var objectHash string
510495 var lfsPointer * lfs.Pointer
496+ var oldEntry * git.TreeEntry
511497 switch file .Operation {
512498 case "create" , "update" :
513- objectHash , lfsPointer , err = createOrUpdateFileHash (ctx , t , file , hasOldBranch )
499+ objectHash , lfsPointer , err = hashRepoObjectForCreateOrUpdate (ctx , t , file , hasOldBranch )
514500 case "rename" :
515- objectHash , lfsPointer , err = renameFileHash (ctx , t , oldEntry , file )
501+ objectHash , lfsPointer , oldEntry , err = hashRepoObjectForRename (ctx , t , file )
502+ default :
503+ return util .NewInvalidArgumentErrorf ("unknown operation: %s" , file .Operation )
516504 }
517505 if err != nil {
518506 return err
519507 }
520508
521509 // Add the object to the index
522- if file .Options .executable {
523- if err := t .AddObjectToIndex (ctx , "100755" , objectHash , file .Options .treePath ); err != nil {
524- return err
525- }
526- } else {
527- if err := t .AddObjectToIndex (ctx , "100644" , objectHash , file .Options .treePath ); err != nil {
528- return err
529- }
510+ if err = t .AddObjectToIndex (ctx , util .Iif (file .Options .executable , "100755" , "100644" ), objectHash , file .Options .treePath ); err != nil {
511+ return err
530512 }
531513
532- if lfsPointer != nil {
533- // We have an LFS object - create it
534- lfsMetaObject , err := git_model .NewLFSMetaObject (ctx , repoID , * lfsPointer )
535- if err != nil {
514+ if lfsPointer == nil {
515+ return nil // No LFS pointer, so nothing to do
516+ }
517+
518+ // Now we must store the content into an LFS object
519+ lfsMetaObject , err := git_model .NewLFSMetaObject (ctx , repoID , * lfsPointer )
520+ if err != nil {
521+ return err
522+ }
523+ if exist , err := contentStore .Exists (lfsMetaObject .Pointer ); err != nil {
524+ return err
525+ } else if exist {
526+ return nil
527+ }
528+
529+ var lfsContentReader io.ReadCloser
530+ if file .Operation == "create" || file .Operation == "update" {
531+ if _ , err = file .ContentReader .Seek (0 , io .SeekStart ); err != nil {
536532 return err
537533 }
538- exist , err := contentStore .Exists (lfsMetaObject .Pointer )
539- if err != nil {
534+ lfsContentReader = io .NopCloser (file .ContentReader )
535+ } else if file .Operation == "rename" {
536+ if lfsContentReader , err = oldEntry .Blob ().DataAsync (); err != nil {
540537 return err
541538 }
542- if ! exist {
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 ()
554- }
539+ defer lfsContentReader .Close ()
540+ }
555541
556- if err := contentStore .Put (lfsMetaObject .Pointer , lfsContentReader ); err != nil {
557- if _ , err2 := git_model .RemoveLFSMetaObjectByOid (ctx , repoID , lfsMetaObject .Oid ); err2 != nil {
558- return fmt .Errorf ("unable to remove failed inserted LFS object %s: %v (Prev Error: %w)" , lfsMetaObject .Oid , err2 , err )
559- }
560- return err
561- }
542+ err = contentStore .Put (lfsMetaObject .Pointer , lfsContentReader )
543+ if err != nil {
544+ if _ , errRemove := git_model .RemoveLFSMetaObjectByOid (ctx , repoID , lfsMetaObject .Oid ); errRemove != nil {
545+ return fmt .Errorf ("unable to remove failed inserted LFS object %s: %v (Prev Error: %w)" , lfsMetaObject .Oid , errRemove , err )
562546 }
563547 }
548+ return err
549+ }
564550
565- return nil
551+ func checkIsLfsFileInGitAttributes (ctx context.Context , t * TemporaryUploadRepository , paths []string ) (ret []bool , err error ) {
552+ attributesMap , err := attribute .CheckAttributes (ctx , t .gitRepo , "" /* use temp repo's working dir */ , attribute.CheckAttributeOpts {
553+ Attributes : []string {attribute .Filter },
554+ Filenames : paths ,
555+ })
556+ if err != nil {
557+ return nil , err
558+ }
559+ for _ , p := range paths {
560+ isLFSFile := attributesMap [p ] != nil && attributesMap [p ].Get (attribute .Filter ).ToString ().Value () == "lfs"
561+ ret = append (ret , isLFSFile )
562+ }
563+ return ret , nil
566564}
567565
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- })
566+ // hashRepoObjectForCreateOrUpdate hashes the git object for create or update operations
567+ // If returned lfsPointer is nil, then the content will be stored as a normal git object.
568+ // Otherwise (lfsPointer is not nil), the content will be stored in LFS as lfsPointer.
569+ func hashRepoObjectForCreateOrUpdate (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile , hasOldBranch bool ) (objectHash string , lfsPointer * lfs.Pointer , err error ) {
570+ var treeObjectContentReader io.Reader
571+ if setting .LFS .StartServer && hasOldBranch /* non-empty */ {
572+ checkIsLfsFiles , err := checkIsLfsFileInGitAttributes (ctx , t , []string {file .Options .treePath })
577573 if err != nil {
578574 return "" , nil , err
579575 }
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 )
576+ if checkIsLfsFiles [0 ] {
577+ // OK, so we are supposed to LFS this data!
578+ pointer , err := lfs .GeneratePointer (file .ContentReader )
584579 if err != nil {
585580 return "" , nil , err
586581 }
587582 lfsPointer = & pointer
588583 treeObjectContentReader = strings .NewReader (pointer .StringContent ())
589584 }
585+ } else {
586+ treeObjectContentReader = file .ContentReader
590587 }
591588
592- // Add the object to the database
593- objectHash , err := t .HashObject (ctx , treeObjectContentReader )
589+ objectHash , err = t .HashObject (ctx , treeObjectContentReader )
594590 if err != nil {
595591 return "" , nil , err
596592 }
597593
598594 return objectHash , lfsPointer , nil
599595}
600596
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- }
597+ // hashRepoObjectForRename the same as hashRepoObjectForCreateOrUpdate buf for "rename"
598+ func hashRepoObjectForRename (ctx context.Context , t * TemporaryUploadRepository , file * ChangeRepoFile ) (objectHash string , lfsPointer * lfs.Pointer , oldEntry * git.TreeEntry , err error ) {
599+ lastCommitID , err := t .GetLastCommit (ctx )
600+ if err != nil {
601+ return "" , nil , nil , err
602+ }
603+ commit , err := t .GetCommit (lastCommitID )
604+ if err != nil {
605+ return "" , nil , nil , err
606+ }
607+ oldEntry , err = commit .GetTreeEntryByPath (file .Options .fromTreePath )
608+ if err != nil {
609+ return "" , nil , nil , err
610+ }
610611
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"
612+ if ! setting .LFS .StartServer {
613+ return oldEntry .ID .String (), nil , nil , nil
614+ }
613615
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- }
616+ checkIsLfsFiles , err := checkIsLfsFileInGitAttributes (ctx , t , []string {file .Options .treePath , file .Options .fromTreePath })
617+ if err != nil {
618+ return "" , nil , nil , err
619+ }
620+ oldIsLfs , newIsLfs := checkIsLfsFiles [0 ], checkIsLfsFiles [1 ]
621+
622+ // 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
623+ // as the object doesn't change
624+ if oldIsLfs == newIsLfs {
625+ return oldEntry .ID .String (), nil , nil , nil
626+ }
627+
628+ oldEntryReader , err := oldEntry .Blob ().DataAsync ()
629+ if err != nil {
630+ return "" , nil , nil , err
631+ }
632+ defer oldEntryReader .Close ()
619633
620- oldEntryReader , err := oldEntry .Blob ().DataAsync ()
634+ var treeObjectContentReader io.ReadCloser
635+ if oldIsLfs {
636+ // If the old is in lfs but the new isn't, read the content from lfs and add it as a normal git object
637+ pointer , err := lfs .ReadPointer (oldEntryReader )
621638 if err != nil {
622- return "" , nil , err
639+ return "" , nil , nil , err
623640 }
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
641+ treeObjectContentReader , err = lfs .ReadMetaObject (pointer )
642+ if err != nil {
643+ return "" , nil , nil , err
648644 }
649-
650- // Add the object to the database
651- objectID , err := t .HashObject (ctx , treeObjectContentReader )
645+ defer treeObjectContentReader .Close ()
646+ } else {
647+ // If the new is in lfs but the old isn't, read the content from the git object and generate a lfs pointer of it
648+ pointer , err := lfs .GeneratePointer (oldEntryReader )
652649 if err != nil {
653- return "" , nil , err
650+ return "" , nil , nil , err
654651 }
655- return objectID , lfsPointer , nil
652+ treeObjectContentReader = io .NopCloser (strings .NewReader (pointer .StringContent ()))
653+ lfsPointer = & pointer
656654 }
657655
658- return oldEntry .ID .String (), nil , nil
656+ objectHash , err = t .HashObject (ctx , treeObjectContentReader )
657+ if err != nil {
658+ return "" , nil , nil , err
659+ }
660+ return objectHash , lfsPointer , oldEntry , nil
659661}
660662
661663// VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch
0 commit comments