Skip to content

Commit 121c0bb

Browse files
committed
fix
1 parent 439ebe7 commit 121c0bb

File tree

2 files changed

+126
-118
lines changed

2 files changed

+126
-118
lines changed

modules/optional/option.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ package optional
55

66
import "strconv"
77

8+
// Option is a generic type that can hold a value of type T or be empty (None).
9+
//
10+
// It must use the slice type to work with "chi" form values binding:
11+
// * non-existing value are represented as an empty slice (None)
12+
// * existing value is represented as a slice with one element (Some)
13+
// * multiple values are represented as a slice with multiple elements (Some), the Value is the first element (not well-defined in this case)
814
type Option[T any] []T
915

1016
func None[T any]() Option[T] {

services/repository/files/update.go

Lines changed: 120 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string {
372372

373373
// handles the check for various issues for ChangeRepoFiles
374374
func 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

Comments
 (0)