mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-29 11:20:42 +00:00
4979f15c3f
* Add configurable Trust Models Gitea's default signature verification model differs from GitHub. GitHub uses signatures to verify that the committer is who they say they are - meaning that when GitHub makes a signed commit it must be the committer. The GitHub model prevents re-publishing of commits after revocation of a key and prevents re-signing of other people's commits to create a completely trusted repository signed by one key or a set of trusted keys. The default behaviour of Gitea in contrast is to always display the avatar and information related to a signature. This allows signatures to be decoupled from the committer. That being said, allowing arbitary users to present other peoples commits as theirs is not necessarily desired therefore we have a trust model whereby signatures from collaborators are marked trusted, signatures matching the commit line are marked untrusted and signatures that match a user in the db but not the committer line are marked unmatched. The problem with this model is that this conflicts with Github therefore we need to provide an option to allow users to choose the Github model should they wish to. Signed-off-by: Andrew Thornton <art27@cantab.net> * Adjust locale strings Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @6543 Co-authored-by: 6543 <6543@obermui.de> * Update models/gpg_key.go * Add migration for repository Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
225 lines
6.3 KiB
Go
225 lines
6.3 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package repofiles
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/models"
|
|
"code.gitea.io/gitea/modules/git"
|
|
repo_module "code.gitea.io/gitea/modules/repository"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
)
|
|
|
|
// DeleteRepoFileOptions holds the repository delete file options
|
|
type DeleteRepoFileOptions struct {
|
|
LastCommitID string
|
|
OldBranch string
|
|
NewBranch string
|
|
TreePath string
|
|
Message string
|
|
SHA string
|
|
Author *IdentityOptions
|
|
Committer *IdentityOptions
|
|
Dates *CommitDateOptions
|
|
}
|
|
|
|
// DeleteRepoFile deletes a file in the given repository
|
|
func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepoFileOptions) (*api.FileResponse, error) {
|
|
// If no branch name is set, assume the repo's default branch
|
|
if opts.OldBranch == "" {
|
|
opts.OldBranch = repo.DefaultBranch
|
|
}
|
|
if opts.NewBranch == "" {
|
|
opts.NewBranch = opts.OldBranch
|
|
}
|
|
|
|
// oldBranch must exist for this operation
|
|
if _, err := repo_module.GetBranch(repo, opts.OldBranch); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// A NewBranch can be specified for the file to be created/updated in a new branch.
|
|
// Check to make sure the branch does not already exist, otherwise we can't proceed.
|
|
// If we aren't branching to a new branch, make sure user can commit to the given branch
|
|
if opts.NewBranch != opts.OldBranch {
|
|
newBranch, err := repo_module.GetBranch(repo, opts.NewBranch)
|
|
if err != nil && !git.IsErrBranchNotExist(err) {
|
|
return nil, err
|
|
}
|
|
if newBranch != nil {
|
|
return nil, models.ErrBranchAlreadyExists{
|
|
BranchName: opts.NewBranch,
|
|
}
|
|
}
|
|
} else {
|
|
protectedBranch, err := repo.GetBranchProtection(opts.OldBranch)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if protectedBranch != nil {
|
|
if !protectedBranch.CanUserPush(doer.ID) {
|
|
return nil, models.ErrUserCannotCommit{
|
|
UserName: doer.LowerName,
|
|
}
|
|
}
|
|
if protectedBranch.RequireSignedCommits {
|
|
_, _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), opts.OldBranch)
|
|
if err != nil {
|
|
if !models.IsErrWontSign(err) {
|
|
return nil, err
|
|
}
|
|
return nil, models.ErrUserCannotCommit{
|
|
UserName: doer.LowerName,
|
|
}
|
|
}
|
|
}
|
|
patterns := protectedBranch.GetProtectedFilePatterns()
|
|
for _, pat := range patterns {
|
|
if pat.Match(strings.ToLower(opts.TreePath)) {
|
|
return nil, models.ErrFilePathProtected{
|
|
Path: opts.TreePath,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check that the path given in opts.treeName is valid (not a git path)
|
|
treePath := CleanUploadFileName(opts.TreePath)
|
|
if treePath == "" {
|
|
return nil, models.ErrFilenameInvalid{
|
|
Path: opts.TreePath,
|
|
}
|
|
}
|
|
|
|
message := strings.TrimSpace(opts.Message)
|
|
|
|
author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
|
|
|
|
t, err := NewTemporaryUploadRepository(repo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer t.Close()
|
|
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
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
opts.LastCommitID = lastCommitID.String()
|
|
}
|
|
|
|
// Get the files in the index
|
|
filesInIndex, err := t.LsFiles(opts.TreePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DeleteRepoFile: %v", err)
|
|
}
|
|
|
|
// Find the file we want to delete in the index
|
|
inFilelist := false
|
|
for _, file := range filesInIndex {
|
|
if file == opts.TreePath {
|
|
inFilelist = true
|
|
break
|
|
}
|
|
}
|
|
if !inFilelist {
|
|
return nil, models.ErrRepoFileDoesNotExist{
|
|
Path: opts.TreePath,
|
|
}
|
|
}
|
|
|
|
// Get the entry of treePath and check if the SHA given is the same as the file
|
|
entry, err := commit.GetTreeEntryByPath(treePath)
|
|
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 != entry.ID.String() {
|
|
return nil, models.ErrSHADoesNotMatch{
|
|
Path: treePath,
|
|
GivenSHA: opts.SHA,
|
|
CurrentSHA: entry.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 {
|
|
// CommitIDs don't match, but we don't want to throw a ErrCommitIDDoesNotMatch unless
|
|
// this specific file has been edited since opts.LastCommitID
|
|
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
|
|
}
|
|
} else {
|
|
// When deleting 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{}
|
|
}
|
|
|
|
// Remove the file from the index
|
|
if err := t.RemoveFilesFromIndex(opts.TreePath); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Now write the tree
|
|
treeHash, err := t.WriteTree()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Now commit the tree
|
|
var commitHash string
|
|
if opts.Dates != nil {
|
|
commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Dates.Author, opts.Dates.Committer)
|
|
} else {
|
|
commitHash, err = t.CommitTree(author, committer, treeHash, message)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Then push this tree to NewBranch
|
|
if err := t.Push(doer, commitHash, opts.NewBranch); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
commit, err = t.GetCommit(commitHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
file, err := GetFileResponseFromCommit(repo, commit, opts.NewBranch, treePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return file, nil
|
|
}
|