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"
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
"code.gitea.io/gitea/modules/gitrepo"
2019-04-17 19:06:35 +03:00
"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
2024-02-19 17:50:03 +03:00
ContentReader io . ReadSeeker
2023-07-18 21:14:47 +03:00
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
}
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
gitRepo , closer , err := gitrepo . RepositoryFromContextOrOpen ( ctx , repo )
2022-01-20 02:26:57 +03:00
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 {
2024-04-07 14:17:06 +03:00
log . Error ( "NewTemporaryUploadRepository failed: %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
2024-01-16 18:06:51 +03:00
if err := t . Clone ( opts . OldBranch , true ) ; 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 {
2024-04-29 11:47:56 +03:00
if err := handleCheckErrors ( file , commit , opts ) ; 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
2024-04-29 11:47:56 +03:00
func handleCheckErrors ( file * ChangeRepoFile , commit * git . Commit , opts * ChangeRepoFilesOptions ) error {
2023-05-29 12:41:35 +03:00
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 {
2024-02-19 17:50:03 +03:00
_ , err := file . ContentReader . Seek ( 0 , io . SeekStart )
if err != nil {
return err
}
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
}