diff options
author | zeripath <art27@cantab.net> | 2022-03-28 20:48:41 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-28 21:48:41 +0200 |
commit | e69b7a92ed91a4b078d216581a24497a72363494 (patch) | |
tree | a46682f38082fb024e0053aaec5b0838ff64aeb8 /services | |
parent | 54961f3ac97709238710b2207e8c42f0a9897d80 (diff) | |
download | gitea-e69b7a92ed91a4b078d216581a24497a72363494.tar.gz gitea-e69b7a92ed91a4b078d216581a24497a72363494.zip |
Allow API to create file on empty repo (#19224)
This PR adds the necessary work to make it possible to create files on empty
repos using the API.
Fix #10993
Signed-off-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'services')
-rw-r--r-- | services/repository/files/cherry_pick.go | 4 | ||||
-rw-r--r-- | services/repository/files/delete.go | 4 | ||||
-rw-r--r-- | services/repository/files/patch.go | 4 | ||||
-rw-r--r-- | services/repository/files/temp_repo.go | 35 | ||||
-rw-r--r-- | services/repository/files/update.go | 200 | ||||
-rw-r--r-- | services/repository/files/upload.go | 2 |
6 files changed, 143 insertions, 106 deletions
diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go index dc932b39c2..0107d99e66 100644 --- a/services/repository/files/cherry_pick.go +++ b/services/repository/files/cherry_pick.go @@ -97,9 +97,9 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod // Now commit the tree var commitHash string if opts.Dates != nil { - commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) + commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) } else { - commitHash, err = t.CommitTree(author, committer, treeHash, message, opts.Signoff) + commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff) } if err != nil { return nil, err diff --git a/services/repository/files/delete.go b/services/repository/files/delete.go index 95d05b5202..781a762d0f 100644 --- a/services/repository/files/delete.go +++ b/services/repository/files/delete.go @@ -179,9 +179,9 @@ func DeleteRepoFile(ctx context.Context, repo *repo_model.Repository, doer *user // Now commit the tree var commitHash string if opts.Dates != nil { - commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) + commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) } else { - commitHash, err = t.CommitTree(author, committer, treeHash, message, opts.Signoff) + commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff) } if err != nil { return nil, err diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index 09a8b3ea0c..904512f3f3 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -164,9 +164,9 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user // Now commit the tree var commitHash string if opts.Dates != nil { - commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) + commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) } else { - commitHash, err = t.CommitTree(author, committer, treeHash, message, opts.Signoff) + commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff) } if err != nil { return nil, err diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 2223e1c8fd..c789ad4445 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -77,6 +77,19 @@ func (t *TemporaryUploadRepository) Clone(branch string) error { return nil } +// Init the repository +func (t *TemporaryUploadRepository) Init() error { + if err := git.InitRepository(t.ctx, t.basePath, false); err != nil { + return err + } + gitRepo, err := git.OpenRepositoryCtx(t.ctx, t.basePath) + if err != nil { + return err + } + t.gitRepo = gitRepo + return nil +} + // SetDefaultIndex sets the git index to our HEAD func (t *TemporaryUploadRepository) SetDefaultIndex() error { if _, err := git.NewCommand(t.ctx, "read-tree", "HEAD").RunInDir(t.basePath); err != nil { @@ -209,12 +222,12 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, erro } // CommitTree creates a commit from a given tree for the user with provided message -func (t *TemporaryUploadRepository) CommitTree(author, committer *user_model.User, treeHash, message string, signoff bool) (string, error) { - return t.CommitTreeWithDate(author, committer, treeHash, message, signoff, time.Now(), time.Now()) +func (t *TemporaryUploadRepository) CommitTree(parent string, author, committer *user_model.User, treeHash, message string, signoff bool) (string, error) { + return t.CommitTreeWithDate(parent, author, committer, treeHash, message, signoff, time.Now(), time.Now()) } // CommitTreeWithDate creates a commit from a given tree for the user with provided message -func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *user_model.User, treeHash, message string, signoff bool, authorDate, committerDate time.Time) (string, error) { +func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, committer *user_model.User, treeHash, message string, signoff bool, authorDate, committerDate time.Time) (string, error) { authorSig := author.NewGitSig() committerSig := committer.NewGitSig() @@ -235,11 +248,23 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *user_m _, _ = messageBytes.WriteString(message) _, _ = messageBytes.WriteString("\n") - args := []string{"commit-tree", treeHash, "-p", "HEAD"} + var args []string + if parent != "" { + args = []string{"commit-tree", treeHash, "-p", parent} + } else { + args = []string{"commit-tree", treeHash} + } // Determine if we should sign if git.CheckGitVersionAtLeast("1.7.9") == nil { - sign, keyID, signer, _ := asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, "HEAD") + var sign bool + var keyID string + var signer *git.Signature + if parent != "" { + sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, parent) + } else { + sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author) + } if sign { args = append(args, "-S"+keyID) if t.repo.GetTrustModel() == repo_model.CommitterTrustModel || t.repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { diff --git a/services/repository/files/update.go b/services/repository/files/update.go index 4b8653a7f7..2cb40aac47 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -141,7 +141,7 @@ func CreateOrUpdateRepoFile(ctx context.Context, repo *repo_model.Repository, do defer closer.Close() // oldBranch must exist for this operation - if _, err := gitRepo.GetBranch(opts.OldBranch); err != nil { + if _, err := gitRepo.GetBranch(opts.OldBranch); err != nil && !repo.IsEmpty { return nil, err } @@ -191,118 +191,130 @@ func CreateOrUpdateRepoFile(ctx context.Context, repo *repo_model.Repository, do log.Error("%v", err) } defer t.Close() + hasOldBranch := true if err := t.Clone(opts.OldBranch); err != nil { - return nil, err - } - if err := t.SetDefaultIndex(); err != nil { - return nil, err - } - - // Get the commit of the original branch - commit, err := t.GetBranchCommit(opts.OldBranch) - if err != nil { - return nil, err // Couldn't get a commit for the branch + if !git.IsErrBranchNotExist(err) || !repo.IsEmpty { + return nil, err + } + if err := t.Init(); err != nil { + return nil, err + } + hasOldBranch = false + opts.LastCommitID = "" } - - // Assigned LastCommitID in opts if it hasn't been set - if opts.LastCommitID == "" { - opts.LastCommitID = commit.ID.String() - } else { - lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID) - if err != nil { - return nil, fmt.Errorf("DeleteRepoFile: Invalid last commit ID: %v", err) + if hasOldBranch { + if err := t.SetDefaultIndex(); err != nil { + return nil, err } - opts.LastCommitID = lastCommitID.String() - } encoding := "UTF-8" bom := false executable := false - if !opts.IsNewFile { - fromEntry, err := commit.GetTreeEntryByPath(fromTreePath) + if hasOldBranch { + // Get the commit of the original branch + commit, err := t.GetBranchCommit(opts.OldBranch) if err != nil { - return nil, err + return nil, err // Couldn't get a commit for the branch } - if opts.SHA != "" { - // If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error - if opts.SHA != fromEntry.ID.String() { - return nil, models.ErrSHADoesNotMatch{ - Path: treePath, - GivenSHA: opts.SHA, - CurrentSHA: fromEntry.ID.String(), - } + + // Assigned LastCommitID in opts if it hasn't been set + if opts.LastCommitID == "" { + opts.LastCommitID = commit.ID.String() + } else { + lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID) + if err != nil { + return nil, fmt.Errorf("ConvertToSHA1: Invalid last commit ID: %v", err) } - } else if opts.LastCommitID != "" { - // If a lastCommitID was given and it doesn't match the commitID of the head of the branch throw - // an error, but only if we aren't creating a new branch. - if commit.ID.String() != opts.LastCommitID && opts.OldBranch == opts.NewBranch { - if changed, err := commit.FileChangedSinceCommit(treePath, opts.LastCommitID); err != nil { - return nil, err - } else if changed { - return nil, models.ErrCommitIDDoesNotMatch{ - GivenCommitID: opts.LastCommitID, - CurrentCommitID: opts.LastCommitID, + opts.LastCommitID = lastCommitID.String() + + } + + if !opts.IsNewFile { + fromEntry, err := commit.GetTreeEntryByPath(fromTreePath) + if err != nil { + return nil, err + } + if opts.SHA != "" { + // If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error + if opts.SHA != fromEntry.ID.String() { + return nil, models.ErrSHADoesNotMatch{ + Path: treePath, + GivenSHA: opts.SHA, + CurrentSHA: fromEntry.ID.String(), + } + } + } else if opts.LastCommitID != "" { + // If a lastCommitID was given and it doesn't match the commitID of the head of the branch throw + // an error, but only if we aren't creating a new branch. + if commit.ID.String() != opts.LastCommitID && opts.OldBranch == opts.NewBranch { + if changed, err := commit.FileChangedSinceCommit(treePath, opts.LastCommitID); err != nil { + return nil, err + } else if changed { + return nil, models.ErrCommitIDDoesNotMatch{ + GivenCommitID: opts.LastCommitID, + CurrentCommitID: opts.LastCommitID, + } } + // The file wasn't modified, so we are good to delete it } - // The file wasn't modified, so we are good to delete it + } else { + // When updating a file, a lastCommitID or SHA needs to be given to make sure other commits + // haven't been made. We throw an error if one wasn't provided. + return nil, models.ErrSHAOrCommitIDNotProvided{} } - } else { - // When updating a file, a lastCommitID or SHA needs to be given to make sure other commits - // haven't been made. We throw an error if one wasn't provided. - return nil, models.ErrSHAOrCommitIDNotProvided{} + encoding, bom = detectEncodingAndBOM(fromEntry, repo) + executable = fromEntry.IsExecutable() } - encoding, bom = detectEncodingAndBOM(fromEntry, repo) - executable = fromEntry.IsExecutable() - } - - // For the path where this file will be created/updated, we need to make - // sure no parts of the path are existing files or links except for the last - // item in the path which is the file name, and that shouldn't exist IF it is - // a new file OR is being moved to a new path. - treePathParts := strings.Split(treePath, "/") - subTreePath := "" - for index, part := range treePathParts { - subTreePath = path.Join(subTreePath, part) - entry, err := commit.GetTreeEntryByPath(subTreePath) - if err != nil { - if git.IsErrNotExist(err) { - // Means there is no item with that name, so we're good - break + + // For the path where this file will be created/updated, we need to make + // sure no parts of the path are existing files or links except for the last + // item in the path which is the file name, and that shouldn't exist IF it is + // a new file OR is being moved to a new path. + treePathParts := strings.Split(treePath, "/") + subTreePath := "" + for index, part := range treePathParts { + subTreePath = path.Join(subTreePath, part) + entry, err := commit.GetTreeEntryByPath(subTreePath) + if err != nil { + if git.IsErrNotExist(err) { + // Means there is no item with that name, so we're good + break + } + return nil, err } - return nil, err - } - if index < len(treePathParts)-1 { - if !entry.IsDir() { + if index < len(treePathParts)-1 { + if !entry.IsDir() { + return nil, models.ErrFilePathInvalid{ + Message: fmt.Sprintf("a file exists where you’re trying to create a subdirectory [path: %s]", subTreePath), + Path: subTreePath, + Name: part, + Type: git.EntryModeBlob, + } + } + } else if entry.IsLink() { return nil, models.ErrFilePathInvalid{ - Message: fmt.Sprintf("a file exists where you’re trying to create a subdirectory [path: %s]", subTreePath), + Message: fmt.Sprintf("a symbolic link exists where you’re trying to create a subdirectory [path: %s]", subTreePath), Path: subTreePath, Name: part, - Type: git.EntryModeBlob, + Type: git.EntryModeSymlink, + } + } else if entry.IsDir() { + return nil, models.ErrFilePathInvalid{ + Message: fmt.Sprintf("a directory exists where you’re trying to create a file [path: %s]", subTreePath), + Path: subTreePath, + Name: part, + Type: git.EntryModeTree, + } + } else if fromTreePath != treePath || opts.IsNewFile { + // The entry shouldn't exist if we are creating new file or moving to a new path + return nil, models.ErrRepoFileAlreadyExists{ + Path: treePath, } } - } else if entry.IsLink() { - return nil, models.ErrFilePathInvalid{ - Message: fmt.Sprintf("a symbolic link exists where you’re trying to create a subdirectory [path: %s]", subTreePath), - Path: subTreePath, - Name: part, - Type: git.EntryModeSymlink, - } - } else if entry.IsDir() { - return nil, models.ErrFilePathInvalid{ - Message: fmt.Sprintf("a directory exists where you’re trying to create a file [path: %s]", subTreePath), - Path: subTreePath, - Name: part, - Type: git.EntryModeTree, - } - } else if fromTreePath != treePath || opts.IsNewFile { - // The entry shouldn't exist if we are creating new file or moving to a new path - return nil, models.ErrRepoFileAlreadyExists{ - Path: treePath, - } - } + } } // Get the two paths (might be the same if not moving) from the index if they exist @@ -354,7 +366,7 @@ func CreateOrUpdateRepoFile(ctx context.Context, repo *repo_model.Repository, do opts.Content = content var lfsMetaObject *models.LFSMetaObject - if setting.LFS.StartServer { + if setting.LFS.StartServer && hasOldBranch { // Check there is no way this can return multiple infos filename2attribute2info, err := t.gitRepo.CheckAttribute(git.CheckAttributeOpts{ Attributes: []string{"filter"}, @@ -401,9 +413,9 @@ func CreateOrUpdateRepoFile(ctx context.Context, repo *repo_model.Repository, do // Now commit the tree var commitHash string if opts.Dates != nil { - commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) + commitHash, err = t.CommitTreeWithDate(opts.LastCommitID, author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) } else { - commitHash, err = t.CommitTree(author, committer, treeHash, message, opts.Signoff) + commitHash, err = t.CommitTree(opts.LastCommitID, author, committer, treeHash, message, opts.Signoff) } if err != nil { return nil, err @@ -436,7 +448,7 @@ func CreateOrUpdateRepoFile(ctx context.Context, repo *repo_model.Repository, do return nil, err } - commit, err = t.GetCommit(commitHash) + commit, err := t.GetCommit(commitHash) if err != nil { return nil, err } diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go index 79fca3ead7..ff0448fc87 100644 --- a/services/repository/files/upload.go +++ b/services/repository/files/upload.go @@ -123,7 +123,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use committer := doer // Now commit the tree - commitHash, err := t.CommitTree(author, committer, treeHash, opts.Message, opts.Signoff) + commitHash, err := t.CommitTree(opts.LastCommitID, author, committer, treeHash, opts.Message, opts.Signoff) if err != nil { return err } |