2019-04-17 19:06:35 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2019-04-17 19:06:35 +03:00
2021-11-24 10:56:24 +03:00
package files
2019-04-17 19:06:35 +03:00
import (
2022-01-20 02:26:57 +03:00
"context"
2019-04-17 19:06:35 +03:00
"fmt"
2023-07-18 21:14:47 +03:00
"io"
2019-04-17 19:06:35 +03:00
"path"
"strings"
2019-12-24 05:33:52 +03:00
"time"
2019-04-17 19:06:35 +03:00
"code.gitea.io/gitea/models"
2022-06-12 18:51:54 +03:00
git_model "code.gitea.io/gitea/models/git"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2019-04-17 19:06:35 +03:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/lfs"
2019-04-26 15:00:30 +03:00
"code.gitea.io/gitea/modules/log"
2019-04-17 19:06:35 +03:00
"code.gitea.io/gitea/modules/setting"
2019-05-11 13:21:34 +03:00
"code.gitea.io/gitea/modules/structs"
2021-12-10 11:14:24 +03:00
asymkey_service "code.gitea.io/gitea/services/asymkey"
2019-04-17 19:06:35 +03:00
)
// IdentityOptions for a person's identity like an author or committer
type IdentityOptions struct {
Name string
Email string
}
2019-12-24 05:33:52 +03:00
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
type CommitDateOptions struct {
Author time . Time
Committer time . Time
}
2023-05-29 12:41:35 +03:00
type ChangeRepoFile struct {
2023-07-18 21:14:47 +03:00
Operation string
TreePath string
FromTreePath string
ContentReader io . Reader
SHA string
Options * RepoFileOptions
2023-05-29 12:41:35 +03:00
}
2023-05-31 12:07:51 +03:00
// ChangeRepoFilesOptions holds the repository files update options
2023-05-29 12:41:35 +03:00
type ChangeRepoFilesOptions struct {
LastCommitID string
OldBranch string
NewBranch string
Message string
Files [ ] * ChangeRepoFile
2019-04-17 19:06:35 +03:00
Author * IdentityOptions
Committer * IdentityOptions
2019-12-24 05:33:52 +03:00
Dates * CommitDateOptions
2021-01-29 11:57:45 +03:00
Signoff bool
2019-04-17 19:06:35 +03:00
}
2023-05-29 12:41:35 +03:00
type RepoFileOptions struct {
treePath string
fromTreePath string
executable bool
}
// ChangeRepoFiles adds, updates or removes multiple files in the given repository
func ChangeRepoFiles ( ctx context . Context , repo * repo_model . Repository , doer * user_model . User , opts * ChangeRepoFilesOptions ) ( * structs . FilesResponse , error ) {
2023-09-22 02:43:29 +03:00
err := repo . MustNotBeArchived ( )
if err != nil {
return nil , err
}
2020-10-13 21:50:57 +03:00
// If no branch name is set, assume default branch
2019-04-17 19:06:35 +03:00
if opts . OldBranch == "" {
opts . OldBranch = repo . DefaultBranch
}
if opts . NewBranch == "" {
opts . NewBranch = opts . OldBranch
}
2022-01-20 02:26:57 +03:00
gitRepo , closer , err := git . RepositoryFromContextOrOpen ( ctx , repo . RepoPath ( ) )
if err != nil {
return nil , err
}
defer closer . Close ( )
2019-04-17 19:06:35 +03:00
// oldBranch must exist for this operation
2022-03-28 22:48:41 +03:00
if _ , err := gitRepo . GetBranch ( opts . OldBranch ) ; err != nil && ! repo . IsEmpty {
2019-04-17 19:06:35 +03:00
return nil , err
}
2023-05-31 12:07:51 +03:00
var treePaths [ ] string
2023-05-29 12:41:35 +03:00
for _ , file := range opts . Files {
// If FromTreePath is not set, set it to the opts.TreePath
if file . TreePath != "" && file . FromTreePath == "" {
file . FromTreePath = file . TreePath
}
// Check that the path given in opts.treePath is valid (not a git path)
treePath := CleanUploadFileName ( file . TreePath )
if treePath == "" {
return nil , models . ErrFilenameInvalid {
Path : file . TreePath ,
}
}
// If there is a fromTreePath (we are copying it), also clean it up
fromTreePath := CleanUploadFileName ( file . FromTreePath )
if fromTreePath == "" && file . FromTreePath != "" {
return nil , models . ErrFilenameInvalid {
Path : file . FromTreePath ,
}
}
file . Options = & RepoFileOptions {
treePath : treePath ,
fromTreePath : fromTreePath ,
executable : false ,
}
treePaths = append ( treePaths , treePath )
}
2019-04-17 19:06:35 +03:00
// 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 {
2022-01-20 02:26:57 +03:00
existingBranch , err := gitRepo . GetBranch ( opts . NewBranch )
2019-04-17 19:06:35 +03:00
if existingBranch != nil {
2023-06-29 13:03:20 +03:00
return nil , git_model . ErrBranchAlreadyExists {
2019-04-17 19:06:35 +03:00
BranchName : opts . NewBranch ,
}
}
2019-04-19 15:17:27 +03:00
if err != nil && ! git . IsErrBranchNotExist ( err ) {
2019-04-17 19:06:35 +03:00
return nil , err
}
2023-05-29 12:41:35 +03:00
} else if err := VerifyBranchProtection ( ctx , repo , doer , opts . OldBranch , treePaths ) ; err != nil {
2021-09-11 17:21:17 +03:00
return nil , err
2019-04-17 19:06:35 +03:00
}
message := strings . TrimSpace ( opts . Message )
2019-12-09 16:11:24 +03:00
author , committer := GetAuthorAndCommitterUsers ( opts . Author , opts . Committer , doer )
2019-04-17 19:06:35 +03:00
2022-01-20 02:26:57 +03:00
t , err := NewTemporaryUploadRepository ( ctx , repo )
2019-04-17 19:06:35 +03:00
if err != nil {
2019-06-12 22:41:28 +03:00
log . Error ( "%v" , err )
2019-04-17 19:06:35 +03:00
}
2019-06-12 22:41:28 +03:00
defer t . Close ( )
2022-03-28 22:48:41 +03:00
hasOldBranch := true
2019-04-17 19:06:35 +03:00
if err := t . Clone ( opts . OldBranch ) ; err != nil {
2023-05-29 12:41:35 +03:00
for _ , file := range opts . Files {
if file . Operation == "delete" {
return nil , err
}
}
2022-03-28 22:48:41 +03:00
if ! git . IsErrBranchNotExist ( err ) || ! repo . IsEmpty {
return nil , err
}
2023-12-17 14:56:08 +03:00
if err := t . Init ( repo . ObjectFormatName ) ; err != nil {
2022-03-28 22:48:41 +03:00
return nil , err
}
hasOldBranch = false
opts . LastCommitID = ""
2019-04-17 19:06:35 +03:00
}
2022-03-28 22:48:41 +03:00
if hasOldBranch {
if err := t . SetDefaultIndex ( ) ; err != nil {
return nil , err
2019-08-05 23:39:39 +03:00
}
2019-04-17 19:06:35 +03:00
}
2023-05-29 12:41:35 +03:00
for _ , file := range opts . Files {
if file . Operation == "delete" {
// Get the files in the index
filesInIndex , err := t . LsFiles ( file . TreePath )
if err != nil {
return nil , fmt . Errorf ( "DeleteRepoFile: %w" , err )
}
// Find the file we want to delete in the index
inFilelist := false
for _ , indexFile := range filesInIndex {
if indexFile == file . TreePath {
inFilelist = true
break
}
}
if ! inFilelist {
return nil , models . ErrRepoFileDoesNotExist {
Path : file . TreePath ,
}
}
}
}
2019-04-26 15:00:30 +03:00
2022-03-28 22:48:41 +03:00
if hasOldBranch {
// Get the commit of the original branch
commit , err := t . GetBranchCommit ( opts . OldBranch )
2019-04-17 19:06:35 +03:00
if err != nil {
2022-03-28 22:48:41 +03:00
return nil , err // Couldn't get a commit for the branch
2019-04-17 19:06:35 +03:00
}
2022-03-28 22:48:41 +03:00
// Assigned LastCommitID in opts if it hasn't been set
if opts . LastCommitID == "" {
opts . LastCommitID = commit . ID . String ( )
} else {
2023-12-14 00:02:00 +03:00
lastCommitID , err := t . gitRepo . ConvertToGitID ( opts . LastCommitID )
2022-03-28 22:48:41 +03:00
if err != nil {
2022-10-24 22:29:17 +03:00
return nil , fmt . Errorf ( "ConvertToSHA1: Invalid last commit ID: %w" , err )
2019-04-17 19:06:35 +03:00
}
2022-03-28 22:48:41 +03:00
opts . LastCommitID = lastCommitID . String ( )
}
2023-05-29 12:41:35 +03:00
for _ , file := range opts . Files {
if err := handleCheckErrors ( file , commit , opts , repo ) ; err != nil {
2022-03-28 22:48:41 +03:00
return nil , err
}
2023-05-29 12:41:35 +03:00
}
}
contentStore := lfs . NewContentStore ( )
for _ , file := range opts . Files {
switch file . Operation {
case "create" , "update" :
if err := CreateOrUpdateFile ( ctx , t , file , contentStore , repo . ID , hasOldBranch ) ; err != nil {
return nil , err
}
case "delete" :
// Remove the file from the index
if err := t . RemoveFilesFromIndex ( file . TreePath ) ; err != nil {
return nil , err
}
default :
2023-05-31 12:07:51 +03:00
return nil , fmt . Errorf ( "invalid file operation: %s %s, supported operations are create, update, delete" , file . Operation , file . Options . treePath )
2023-05-29 12:41:35 +03:00
}
}
// 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 ( opts . LastCommitID , author , committer , treeHash , message , opts . Signoff , opts . Dates . Author , opts . Dates . Committer )
} else {
commitHash , err = t . CommitTree ( opts . LastCommitID , author , committer , treeHash , message , opts . Signoff )
}
if err != nil {
return nil , err
}
// Then push this tree to NewBranch
if err := t . Push ( doer , commitHash , opts . NewBranch ) ; err != nil {
log . Error ( "%T %v" , err , err )
return nil , err
}
commit , err := t . GetCommit ( commitHash )
if err != nil {
return nil , err
}
2023-05-31 12:07:51 +03:00
filesResponse , err := GetFilesResponseFromCommit ( ctx , repo , commit , opts . NewBranch , treePaths )
2023-05-29 12:41:35 +03:00
if err != nil {
return nil , err
}
if repo . IsEmpty {
2023-08-17 07:43:39 +03:00
if isEmpty , err := gitRepo . IsEmpty ( ) ; err == nil && ! isEmpty {
_ = repo_model . UpdateRepositoryCols ( ctx , & repo_model . Repository { ID : repo . ID , IsEmpty : false , DefaultBranch : opts . NewBranch } , "is_empty" , "default_branch" )
}
2023-05-29 12:41:35 +03:00
}
2023-05-31 12:07:51 +03:00
return filesResponse , nil
2023-05-29 12:41:35 +03:00
}
// handles the check for various issues for ChangeRepoFiles
func handleCheckErrors ( file * ChangeRepoFile , commit * git . Commit , opts * ChangeRepoFilesOptions , repo * repo_model . Repository ) error {
if file . Operation == "update" || file . Operation == "delete" {
fromEntry , err := commit . GetTreeEntryByPath ( file . Options . fromTreePath )
if err != nil {
return err
}
if file . SHA != "" {
// If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error
if file . SHA != fromEntry . ID . String ( ) {
return models . ErrSHADoesNotMatch {
Path : file . Options . treePath ,
GivenSHA : file . SHA ,
CurrentSHA : fromEntry . ID . String ( ) ,
2022-03-28 22:48:41 +03:00
}
2023-05-29 12:41:35 +03:00
}
} 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 ( file . Options . treePath , opts . LastCommitID ) ; err != nil {
return err
} else if changed {
return models . ErrCommitIDDoesNotMatch {
GivenCommitID : opts . LastCommitID ,
CurrentCommitID : opts . LastCommitID ,
2019-04-17 19:06:35 +03:00
}
}
2023-05-29 12:41:35 +03:00
// The file wasn't modified, so we are good to delete it
2019-04-17 19:06:35 +03:00
}
2023-05-29 12:41:35 +03:00
} 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 models . ErrSHAOrCommitIDNotProvided { }
2019-04-17 19:06:35 +03:00
}
2023-05-29 12:41:35 +03:00
file . Options . executable = fromEntry . IsExecutable ( )
}
if file . Operation == "create" || file . Operation == "update" {
2022-03-28 22:48:41 +03:00
// 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.
2023-05-29 12:41:35 +03:00
treePathParts := strings . Split ( file . Options . treePath , "/" )
2022-03-28 22:48:41 +03:00
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
}
2023-05-29 12:41:35 +03:00
return err
2019-04-17 19:06:35 +03:00
}
2022-03-28 22:48:41 +03:00
if index < len ( treePathParts ) - 1 {
if ! entry . IsDir ( ) {
2023-05-29 12:41:35 +03:00
return models . ErrFilePathInvalid {
2022-03-28 22:48:41 +03:00
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 ( ) {
2023-05-29 12:41:35 +03:00
return models . ErrFilePathInvalid {
2022-03-28 22:48:41 +03:00
Message : fmt . Sprintf ( "a symbolic link exists where you’ re trying to create a subdirectory [path: %s]" , subTreePath ) ,
2019-04-17 19:06:35 +03:00
Path : subTreePath ,
Name : part ,
2022-03-28 22:48:41 +03:00
Type : git . EntryModeSymlink ,
}
} else if entry . IsDir ( ) {
2023-05-29 12:41:35 +03:00
return models . ErrFilePathInvalid {
2022-03-28 22:48:41 +03:00
Message : fmt . Sprintf ( "a directory exists where you’ re trying to create a file [path: %s]" , subTreePath ) ,
Path : subTreePath ,
Name : part ,
Type : git . EntryModeTree ,
}
2023-05-29 12:41:35 +03:00
} else if file . Options . fromTreePath != file . Options . treePath || file . Operation == "create" {
2022-03-28 22:48:41 +03:00
// The entry shouldn't exist if we are creating new file or moving to a new path
2023-05-29 12:41:35 +03:00
return models . ErrRepoFileAlreadyExists {
Path : file . Options . treePath ,
2019-04-17 19:06:35 +03:00
}
}
2022-03-28 22:48:41 +03:00
}
2019-04-17 19:06:35 +03:00
}
2023-05-29 12:41:35 +03:00
return nil
}
2023-05-31 12:07:51 +03:00
// CreateOrUpdateFile handles creating or updating a file for ChangeRepoFiles
2023-05-29 12:41:35 +03:00
func CreateOrUpdateFile ( ctx context . Context , t * TemporaryUploadRepository , file * ChangeRepoFile , contentStore * lfs . ContentStore , repoID int64 , hasOldBranch bool ) error {
2019-04-17 19:06:35 +03:00
// Get the two paths (might be the same if not moving) from the index if they exist
2023-05-29 12:41:35 +03:00
filesInIndex , err := t . LsFiles ( file . TreePath , file . FromTreePath )
2019-04-17 19:06:35 +03:00
if err != nil {
2023-05-29 12:41:35 +03:00
return fmt . Errorf ( "UpdateRepoFile: %w" , err )
2019-04-17 19:06:35 +03:00
}
// If is a new file (not updating) then the given path shouldn't exist
2023-05-29 12:41:35 +03:00
if file . Operation == "create" {
for _ , indexFile := range filesInIndex {
if indexFile == file . TreePath {
return models . ErrRepoFileAlreadyExists {
Path : file . TreePath ,
2019-04-17 19:06:35 +03:00
}
}
}
}
// Remove the old path from the tree
2023-05-29 12:41:35 +03:00
if file . Options . fromTreePath != file . Options . treePath && len ( filesInIndex ) > 0 {
for _ , indexFile := range filesInIndex {
if indexFile == file . Options . fromTreePath {
if err := t . RemoveFilesFromIndex ( file . FromTreePath ) ; err != nil {
return err
2019-04-17 19:06:35 +03:00
}
}
}
}
2023-07-18 21:14:47 +03:00
treeObjectContentReader := file . ContentReader
2022-06-12 18:51:54 +03:00
var lfsMetaObject * git_model . LFSMetaObject
2022-03-28 22:48:41 +03:00
if setting . LFS . StartServer && hasOldBranch {
2019-10-12 03:13:27 +03:00
// Check there is no way this can return multiple infos
2021-03-01 15:14:17 +03:00
filename2attribute2info , err := t . gitRepo . CheckAttribute ( git . CheckAttributeOpts {
Refactor git command package to improve security and maintainability (#22678)
This PR follows #21535 (and replace #22592)
## Review without space diff
https://github.com/go-gitea/gitea/pull/22678/files?diff=split&w=1
## Purpose of this PR
1. Make git module command completely safe (risky user inputs won't be
passed as argument option anymore)
2. Avoid low-level mistakes like
https://github.com/go-gitea/gitea/pull/22098#discussion_r1045234918
3. Remove deprecated and dirty `CmdArgCheck` function, hide the `CmdArg`
type
4. Simplify code when using git command
## The main idea of this PR
* Move the `git.CmdArg` to the `internal` package, then no other package
except `git` could use it. Then developers could never do
`AddArguments(git.CmdArg(userInput))` any more.
* Introduce `git.ToTrustedCmdArgs`, it's for user-provided and already
trusted arguments. It's only used in a few cases, for example: use git
arguments from config file, help unit test with some arguments.
* Introduce `AddOptionValues` and `AddOptionFormat`, they make code more
clear and simple:
* Before: `AddArguments("-m").AddDynamicArguments(message)`
* After: `AddOptionValues("-m", message)`
* -
* Before: `AddArguments(git.CmdArg(fmt.Sprintf("--author='%s <%s>'",
sig.Name, sig.Email)))`
* After: `AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)`
## FAQ
### Why these changes were not done in #21535 ?
#21535 is mainly a search&replace, it did its best to not change too
much logic.
Making the framework better needs a lot of changes, so this separate PR
is needed as the second step.
### The naming of `AddOptionXxx`
According to git's manual, the `--xxx` part is called `option`.
### How can it guarantee that `internal.CmdArg` won't be not misused?
Go's specification guarantees that. Trying to access other package's
internal package causes compilation error.
And, `golangci-lint` also denies the git/internal package. Only the
`git/command.go` can use it carefully.
### There is still a `ToTrustedCmdArgs`, will it still allow developers
to make mistakes and pass untrusted arguments?
Generally speaking, no. Because when using `ToTrustedCmdArgs`, the code
will be very complex (see the changes for examples). Then developers and
reviewers can know that something might be unreasonable.
### Why there was a `CmdArgCheck` and why it's removed?
At the moment of #21535, to reduce unnecessary changes, `CmdArgCheck`
was introduced as a hacky patch. Now, almost all code could be written
as `cmd := NewCommand(); cmd.AddXxx(...)`, then there is no need for
`CmdArgCheck` anymore.
### Why many codes for `signArg == ""` is deleted?
Because in the old code, `signArg` could never be empty string, it's
either `-S[key-id]` or `--no-gpg-sign`. So the `signArg == ""` is just
dead code.
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-04 05:30:43 +03:00
Attributes : [ ] string { "filter" } ,
2023-05-29 12:41:35 +03:00
Filenames : [ ] string { file . Options . treePath } ,
2022-01-18 10:44:30 +03:00
CachedOnly : true ,
2021-03-01 15:14:17 +03:00
} )
2019-04-17 19:06:35 +03:00
if err != nil {
2023-05-29 12:41:35 +03:00
return err
2019-04-17 19:06:35 +03:00
}
2023-05-29 12:41:35 +03:00
if filename2attribute2info [ file . Options . treePath ] != nil && filename2attribute2info [ file . Options . treePath ] [ "filter" ] == "lfs" {
2019-10-12 03:13:27 +03:00
// OK so we are supposed to LFS this data!
2023-07-18 21:14:47 +03:00
pointer , err := lfs . GeneratePointer ( treeObjectContentReader )
2019-10-12 03:13:27 +03:00
if err != nil {
2023-05-29 12:41:35 +03:00
return err
2019-10-12 03:13:27 +03:00
}
2023-05-29 12:41:35 +03:00
lfsMetaObject = & git_model . LFSMetaObject { Pointer : pointer , RepositoryID : repoID }
2023-07-18 21:14:47 +03:00
treeObjectContentReader = strings . NewReader ( pointer . StringContent ( ) )
2019-10-12 03:13:27 +03:00
}
}
2023-05-29 12:41:35 +03:00
2019-04-17 19:06:35 +03:00
// Add the object to the database
2023-07-18 21:14:47 +03:00
objectHash , err := t . HashObject ( treeObjectContentReader )
2019-04-17 19:06:35 +03:00
if err != nil {
2023-05-29 12:41:35 +03:00
return err
2019-04-17 19:06:35 +03:00
}
// Add the object to the index
2023-05-29 12:41:35 +03:00
if file . Options . executable {
if err := t . AddObjectToIndex ( "100755" , objectHash , file . Options . treePath ) ; err != nil {
return err
2020-03-05 02:46:12 +03:00
}
} else {
2023-05-29 12:41:35 +03:00
if err := t . AddObjectToIndex ( "100644" , objectHash , file . Options . treePath ) ; err != nil {
return err
2020-03-05 02:46:12 +03:00
}
2019-04-17 19:06:35 +03:00
}
if lfsMetaObject != nil {
// We have an LFS object - create it
2023-12-07 10:27:36 +03:00
lfsMetaObject , err = git_model . NewLFSMetaObject ( ctx , lfsMetaObject . RepositoryID , lfsMetaObject . Pointer )
2019-04-17 19:06:35 +03:00
if err != nil {
2023-05-29 12:41:35 +03:00
return err
2019-04-17 19:06:35 +03:00
}
2021-04-09 01:25:57 +03:00
exist , err := contentStore . Exists ( lfsMetaObject . Pointer )
2020-09-08 18:45:10 +03:00
if err != nil {
2023-05-29 12:41:35 +03:00
return err
2020-09-08 18:45:10 +03:00
}
if ! exist {
2023-07-18 21:14:47 +03:00
if err := contentStore . Put ( lfsMetaObject . Pointer , file . ContentReader ) ; err != nil {
2023-05-29 12:41:35 +03:00
if _ , err2 := git_model . RemoveLFSMetaObjectByOid ( ctx , repoID , lfsMetaObject . Oid ) ; err2 != nil {
2023-05-31 12:07:51 +03:00
return fmt . Errorf ( "unable to remove failed inserted LFS object %s: %v (Prev Error: %w)" , lfsMetaObject . Oid , err2 , err )
2019-04-17 19:06:35 +03:00
}
2023-05-29 12:41:35 +03:00
return err
2019-04-17 19:06:35 +03:00
}
}
}
2023-05-29 12:41:35 +03:00
return nil
2019-04-17 19:06:35 +03:00
}
2021-09-11 17:21:17 +03:00
// VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch
2023-05-29 12:41:35 +03:00
func VerifyBranchProtection ( ctx context . Context , repo * repo_model . Repository , doer * user_model . User , branchName string , treePaths [ ] string ) error {
2023-01-16 11:00:22 +03:00
protectedBranch , err := git_model . GetFirstMatchProtectedBranchRule ( ctx , repo . ID , branchName )
2021-09-11 17:21:17 +03:00
if err != nil {
return err
}
if protectedBranch != nil {
2023-01-16 11:00:22 +03:00
protectedBranch . Repo = repo
2023-05-29 12:41:35 +03:00
globUnprotected := protectedBranch . GetUnprotectedFilePatterns ( )
globProtected := protectedBranch . GetProtectedFilePatterns ( )
canUserPush := protectedBranch . CanUserPush ( ctx , doer )
for _ , treePath := range treePaths {
isUnprotectedFile := false
if len ( globUnprotected ) != 0 {
isUnprotectedFile = protectedBranch . IsUnprotectedFile ( globUnprotected , treePath )
}
if ! canUserPush && ! isUnprotectedFile {
return models . ErrUserCannotCommit {
UserName : doer . LowerName ,
}
}
if protectedBranch . IsProtectedFile ( globProtected , treePath ) {
return models . ErrFilePathProtected {
Path : treePath ,
}
2021-09-11 17:21:17 +03:00
}
}
if protectedBranch . RequireSignedCommits {
2022-01-20 02:26:57 +03:00
_ , _ , _ , err := asymkey_service . SignCRUDAction ( ctx , repo . RepoPath ( ) , doer , repo . RepoPath ( ) , branchName )
2021-09-11 17:21:17 +03:00
if err != nil {
2021-12-10 11:14:24 +03:00
if ! asymkey_service . IsErrWontSign ( err ) {
2021-09-11 17:21:17 +03:00
return err
}
return models . ErrUserCannotCommit {
UserName : doer . LowerName ,
}
}
}
}
return nil
}