diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2021-11-24 15:56:24 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-24 15:56:24 +0800 |
commit | c97d66d23cf14e88514cccb88c393d21db51ab7a (patch) | |
tree | 625857bea77befb61847c01bf17aac1177255787 /modules | |
parent | 754fdd8f9c2b1e4e78d507fd414968334cf586fd (diff) | |
download | gitea-c97d66d23cf14e88514cccb88c393d21db51ab7a.tar.gz gitea-c97d66d23cf14e88514cccb88c393d21db51ab7a.zip |
Move repofiles from modules/repofiles to services/repository/files (#17774)
* Move repofiles from modules to services
* rename services/repository/repofiles -> services/repository/files
* Fix test
Co-authored-by: 6543 <6543@obermui.de>
Diffstat (limited to 'modules')
-rw-r--r-- | modules/convert/pull.go | 10 | ||||
-rw-r--r-- | modules/repofiles/blob.go | 41 | ||||
-rw-r--r-- | modules/repofiles/blob_test.go | 40 | ||||
-rw-r--r-- | modules/repofiles/commit.go | 19 | ||||
-rw-r--r-- | modules/repofiles/commit_status.go | 41 | ||||
-rw-r--r-- | modules/repofiles/content.go | 217 | ||||
-rw-r--r-- | modules/repofiles/content_test.go | 220 | ||||
-rw-r--r-- | modules/repofiles/delete.go | 197 | ||||
-rw-r--r-- | modules/repofiles/diff.go | 42 | ||||
-rw-r--r-- | modules/repofiles/diff_test.go | 172 | ||||
-rw-r--r-- | modules/repofiles/file.go | 125 | ||||
-rw-r--r-- | modules/repofiles/file_test.go | 104 | ||||
-rw-r--r-- | modules/repofiles/repofiles.go | 23 | ||||
-rw-r--r-- | modules/repofiles/repofiles_test.go | 27 | ||||
-rw-r--r-- | modules/repofiles/temp_repo.go | 345 | ||||
-rw-r--r-- | modules/repofiles/tree.go | 98 | ||||
-rw-r--r-- | modules/repofiles/tree_test.go | 53 | ||||
-rw-r--r-- | modules/repofiles/update.go | 479 | ||||
-rw-r--r-- | modules/repofiles/upload.go | 207 | ||||
-rw-r--r-- | modules/repofiles/verification.go | 33 | ||||
-rw-r--r-- | modules/repository/branch.go | 26 | ||||
-rw-r--r-- | modules/repository/check.go | 205 |
22 files changed, 8 insertions, 2716 deletions
diff --git a/modules/convert/pull.go b/modules/convert/pull.go index ab17c13421..0f2b5af1b4 100644 --- a/modules/convert/pull.go +++ b/modules/convert/pull.go @@ -10,7 +10,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" - repo_module "code.gitea.io/gitea/modules/repository" api "code.gitea.io/gitea/modules/structs" ) @@ -83,7 +82,14 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *models.User) *api.PullReques }, } - baseBranch, err = repo_module.GetBranch(pr.BaseRepo, pr.BaseBranch) + gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + if err != nil { + log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err) + return nil + } + defer gitRepo.Close() + + baseBranch, err = gitRepo.GetBranch(pr.BaseBranch) if err != nil && !git.IsErrBranchNotExist(err) { log.Error("GetBranch[%s]: %v", pr.BaseBranch, err) return nil diff --git a/modules/repofiles/blob.go b/modules/repofiles/blob.go deleted file mode 100644 index 02bc1ebcab..0000000000 --- a/modules/repofiles/blob.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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 ( - "net/url" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" -) - -// GetBlobBySHA get the GitBlobResponse of a repository using a sha hash. -func GetBlobBySHA(repo *models.Repository, sha string) (*api.GitBlobResponse, error) { - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() - gitBlob, err := gitRepo.GetBlob(sha) - if err != nil { - return nil, err - } - content := "" - if gitBlob.Size() <= setting.API.DefaultMaxBlobSize { - content, err = gitBlob.GetBlobContentBase64() - if err != nil { - return nil, err - } - } - return &api.GitBlobResponse{ - SHA: gitBlob.ID.String(), - URL: repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()), - Size: gitBlob.Size(), - Encoding: "base64", - Content: content, - }, nil -} diff --git a/modules/repofiles/blob_test.go b/modules/repofiles/blob_test.go deleted file mode 100644 index 8950c349e6..0000000000 --- a/modules/repofiles/blob_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// 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 ( - "testing" - - "code.gitea.io/gitea/models/unittest" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/test" - - "github.com/stretchr/testify/assert" -) - -func TestGetBlobBySHA(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" - ctx.SetParams(":id", "1") - ctx.SetParams(":sha", sha) - - gbr, err := GetBlobBySHA(ctx.Repo.Repository, ctx.Params(":sha")) - expectedGBR := &api.GitBlobResponse{ - Content: "dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK", - Encoding: "base64", - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/65f1bf27bc3bf70f64657658635e66094edbcb4d", - SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", - Size: 180, - } - assert.NoError(t, err) - assert.Equal(t, expectedGBR, gbr) -} diff --git a/modules/repofiles/commit.go b/modules/repofiles/commit.go deleted file mode 100644 index 371e6cf3ab..0000000000 --- a/modules/repofiles/commit.go +++ /dev/null @@ -1,19 +0,0 @@ -// 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 ( - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" -) - -// CountDivergingCommits determines how many commits a branch is ahead or behind the repository's base branch -func CountDivergingCommits(repo *models.Repository, branch string) (*git.DivergeObject, error) { - divergence, err := git.GetDivergingCommits(repo.RepoPath(), repo.DefaultBranch, branch) - if err != nil { - return nil, err - } - return &divergence, nil -} diff --git a/modules/repofiles/commit_status.go b/modules/repofiles/commit_status.go deleted file mode 100644 index 3d93c58d85..0000000000 --- a/modules/repofiles/commit_status.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" -) - -// CreateCommitStatus creates a new CommitStatus given a bunch of parameters -// NOTE: All text-values will be trimmed from whitespaces. -// Requires: Repo, Creator, SHA -func CreateCommitStatus(repo *models.Repository, creator *models.User, sha string, status *models.CommitStatus) error { - repoPath := repo.RepoPath() - - // confirm that commit is exist - gitRepo, err := git.OpenRepository(repoPath) - if err != nil { - return fmt.Errorf("OpenRepository[%s]: %v", repoPath, err) - } - if _, err := gitRepo.GetCommit(sha); err != nil { - gitRepo.Close() - return fmt.Errorf("GetCommit[%s]: %v", sha, err) - } - gitRepo.Close() - - if err := models.NewCommitStatus(models.NewCommitStatusOptions{ - Repo: repo, - Creator: creator, - SHA: sha, - CommitStatus: status, - }); err != nil { - return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %v", repo.ID, creator.ID, sha, err) - } - - return nil -} diff --git a/modules/repofiles/content.go b/modules/repofiles/content.go deleted file mode 100644 index 838bfabdc6..0000000000 --- a/modules/repofiles/content.go +++ /dev/null @@ -1,217 +0,0 @@ -// 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" - "net/url" - "path" - "strings" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - api "code.gitea.io/gitea/modules/structs" -) - -// ContentType repo content type -type ContentType string - -// The string representations of different content types -const ( - // ContentTypeRegular regular content type (file) - ContentTypeRegular ContentType = "file" - // ContentTypeDir dir content type (dir) - ContentTypeDir ContentType = "dir" - // ContentLink link content type (symlink) - ContentTypeLink ContentType = "symlink" - // ContentTag submodule content type (submodule) - ContentTypeSubmodule ContentType = "submodule" -) - -// String gets the string of ContentType -func (ct *ContentType) String() string { - return string(*ct) -} - -// GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree -// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag -func GetContentsOrList(repo *models.Repository, treePath, ref string) (interface{}, error) { - if repo.IsEmpty { - return make([]interface{}, 0), nil - } - if ref == "" { - ref = repo.DefaultBranch - } - origRef := ref - - // Check that the path given in opts.treePath is valid (not a git path) - cleanTreePath := CleanUploadFileName(treePath) - if cleanTreePath == "" && treePath != "" { - return nil, models.ErrFilenameInvalid{ - Path: treePath, - } - } - treePath = cleanTreePath - - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() - - // Get the commit object for the ref - commit, err := gitRepo.GetCommit(ref) - if err != nil { - return nil, err - } - - entry, err := commit.GetTreeEntryByPath(treePath) - if err != nil { - return nil, err - } - - if entry.Type() != "tree" { - return GetContents(repo, treePath, origRef, false) - } - - // We are in a directory, so we return a list of FileContentResponse objects - var fileList []*api.ContentsResponse - - gitTree, err := commit.SubTree(treePath) - if err != nil { - return nil, err - } - entries, err := gitTree.ListEntries() - if err != nil { - return nil, err - } - for _, e := range entries { - subTreePath := path.Join(treePath, e.Name()) - fileContentResponse, err := GetContents(repo, subTreePath, origRef, true) - if err != nil { - return nil, err - } - fileList = append(fileList, fileContentResponse) - } - return fileList, nil -} - -// GetContents gets the meta data on a file's contents. Ref can be a branch, commit or tag -func GetContents(repo *models.Repository, treePath, ref string, forList bool) (*api.ContentsResponse, error) { - if ref == "" { - ref = repo.DefaultBranch - } - origRef := ref - - // Check that the path given in opts.treePath is valid (not a git path) - cleanTreePath := CleanUploadFileName(treePath) - if cleanTreePath == "" && treePath != "" { - return nil, models.ErrFilenameInvalid{ - Path: treePath, - } - } - treePath = cleanTreePath - - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() - - // Get the commit object for the ref - commit, err := gitRepo.GetCommit(ref) - if err != nil { - return nil, err - } - commitID := commit.ID.String() - if len(ref) >= 4 && strings.HasPrefix(commitID, ref) { - ref = commit.ID.String() - } - - entry, err := commit.GetTreeEntryByPath(treePath) - if err != nil { - return nil, err - } - - refType := gitRepo.GetRefType(ref) - if refType == "invalid" { - return nil, fmt.Errorf("no commit found for the ref [ref: %s]", ref) - } - - selfURL, err := url.Parse(fmt.Sprintf("%s/contents/%s?ref=%s", repo.APIURL(), treePath, origRef)) - if err != nil { - return nil, err - } - selfURLString := selfURL.String() - - // All content types have these fields in populated - contentsResponse := &api.ContentsResponse{ - Name: entry.Name(), - Path: treePath, - SHA: entry.ID.String(), - Size: entry.Size(), - URL: &selfURLString, - Links: &api.FileLinksResponse{ - Self: &selfURLString, - }, - } - - // Now populate the rest of the ContentsResponse based on entry type - if entry.IsRegular() || entry.IsExecutable() { - contentsResponse.Type = string(ContentTypeRegular) - if blobResponse, err := GetBlobBySHA(repo, entry.ID.String()); err != nil { - return nil, err - } else if !forList { - // We don't show the content if we are getting a list of FileContentResponses - contentsResponse.Encoding = &blobResponse.Encoding - contentsResponse.Content = &blobResponse.Content - } - } else if entry.IsDir() { - contentsResponse.Type = string(ContentTypeDir) - } else if entry.IsLink() { - contentsResponse.Type = string(ContentTypeLink) - // The target of a symlink file is the content of the file - targetFromContent, err := entry.Blob().GetBlobContent() - if err != nil { - return nil, err - } - contentsResponse.Target = &targetFromContent - } else if entry.IsSubModule() { - contentsResponse.Type = string(ContentTypeSubmodule) - submodule, err := commit.GetSubModule(treePath) - if err != nil { - return nil, err - } - contentsResponse.SubmoduleGitURL = &submodule.URL - } - // Handle links - if entry.IsRegular() || entry.IsLink() { - downloadURL, err := url.Parse(fmt.Sprintf("%s/raw/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath)) - if err != nil { - return nil, err - } - downloadURLString := downloadURL.String() - contentsResponse.DownloadURL = &downloadURLString - } - if !entry.IsSubModule() { - htmlURL, err := url.Parse(fmt.Sprintf("%s/src/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath)) - if err != nil { - return nil, err - } - htmlURLString := htmlURL.String() - contentsResponse.HTMLURL = &htmlURLString - contentsResponse.Links.HTMLURL = &htmlURLString - - gitURL, err := url.Parse(fmt.Sprintf("%s/git/blobs/%s", repo.APIURL(), entry.ID.String())) - if err != nil { - return nil, err - } - gitURLString := gitURL.String() - contentsResponse.GitURL = &gitURLString - contentsResponse.Links.GitURL = &gitURLString - } - - return contentsResponse, nil -} diff --git a/modules/repofiles/content_test.go b/modules/repofiles/content_test.go deleted file mode 100644 index f6a953fab9..0000000000 --- a/modules/repofiles/content_test.go +++ /dev/null @@ -1,220 +0,0 @@ -// 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 ( - "path/filepath" - "testing" - - "code.gitea.io/gitea/models/unittest" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/test" - - "github.com/stretchr/testify/assert" -) - -func TestMain(m *testing.M) { - unittest.MainTest(m, filepath.Join("..", "..")) -} - -func getExpectedReadmeContentsResponse() *api.ContentsResponse { - treePath := "README.md" - sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" - encoding := "base64" - content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x" - selfURL := "https://try.gitea.io/api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master" - htmlURL := "https://try.gitea.io/user2/repo1/src/branch/master/" + treePath - gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha - downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath - return &api.ContentsResponse{ - Name: treePath, - Path: treePath, - SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", - Type: "file", - Size: 30, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, - Links: &api.FileLinksResponse{ - Self: &selfURL, - GitURL: &gitURL, - HTMLURL: &htmlURL, - }, - } -} - -func TestGetContents(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - treePath := "README.md" - ref := ctx.Repo.Repository.DefaultBranch - - expectedContentsResponse := getExpectedReadmeContentsResponse() - - t.Run("Get README.md contents with GetContents()", func(t *testing.T) { - fileContentResponse, err := GetContents(ctx.Repo.Repository, treePath, ref, false) - assert.EqualValues(t, expectedContentsResponse, fileContentResponse) - assert.NoError(t, err) - }) - - t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContents()", func(t *testing.T) { - fileContentResponse, err := GetContents(ctx.Repo.Repository, treePath, "", false) - assert.EqualValues(t, expectedContentsResponse, fileContentResponse) - assert.NoError(t, err) - }) -} - -func TestGetContentsOrListForDir(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - treePath := "" // root dir - ref := ctx.Repo.Repository.DefaultBranch - - readmeContentsResponse := getExpectedReadmeContentsResponse() - // because will be in a list, doesn't have encoding and content - readmeContentsResponse.Encoding = nil - readmeContentsResponse.Content = nil - - expectedContentsListResponse := []*api.ContentsResponse{ - readmeContentsResponse, - } - - t.Run("Get root dir contents with GetContentsOrList()", func(t *testing.T) { - fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, ref) - assert.EqualValues(t, expectedContentsListResponse, fileContentResponse) - assert.NoError(t, err) - }) - - t.Run("Get root dir contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList()", func(t *testing.T) { - fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, "") - assert.EqualValues(t, expectedContentsListResponse, fileContentResponse) - assert.NoError(t, err) - }) -} - -func TestGetContentsOrListForFile(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - treePath := "README.md" - ref := ctx.Repo.Repository.DefaultBranch - - expectedContentsResponse := getExpectedReadmeContentsResponse() - - t.Run("Get README.md contents with GetContentsOrList()", func(t *testing.T) { - fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, ref) - assert.EqualValues(t, expectedContentsResponse, fileContentResponse) - assert.NoError(t, err) - }) - - t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList()", func(t *testing.T) { - fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, "") - assert.EqualValues(t, expectedContentsResponse, fileContentResponse) - assert.NoError(t, err) - }) -} - -func TestGetContentsErrors(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - repo := ctx.Repo.Repository - treePath := "README.md" - ref := repo.DefaultBranch - - t.Run("bad treePath", func(t *testing.T) { - badTreePath := "bad/tree.md" - fileContentResponse, err := GetContents(repo, badTreePath, ref, false) - assert.Error(t, err) - assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]") - assert.Nil(t, fileContentResponse) - }) - - t.Run("bad ref", func(t *testing.T) { - badRef := "bad_ref" - fileContentResponse, err := GetContents(repo, treePath, badRef, false) - assert.Error(t, err) - assert.EqualError(t, err, "object does not exist [id: "+badRef+", rel_path: ]") - assert.Nil(t, fileContentResponse) - }) -} - -func TestGetContentsOrListErrors(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - repo := ctx.Repo.Repository - treePath := "README.md" - ref := repo.DefaultBranch - - t.Run("bad treePath", func(t *testing.T) { - badTreePath := "bad/tree.md" - fileContentResponse, err := GetContentsOrList(repo, badTreePath, ref) - assert.Error(t, err) - assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]") - assert.Nil(t, fileContentResponse) - }) - - t.Run("bad ref", func(t *testing.T) { - badRef := "bad_ref" - fileContentResponse, err := GetContentsOrList(repo, treePath, badRef) - assert.Error(t, err) - assert.EqualError(t, err, "object does not exist [id: "+badRef+", rel_path: ]") - assert.Nil(t, fileContentResponse) - }) -} - -func TestGetContentsOrListOfEmptyRepos(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo15") - ctx.SetParams(":id", "15") - test.LoadRepo(t, ctx, 15) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - repo := ctx.Repo.Repository - - t.Run("empty repo", func(t *testing.T) { - contents, err := GetContentsOrList(repo, "", "") - assert.NoError(t, err) - assert.Empty(t, contents) - }) -} diff --git a/modules/repofiles/delete.go b/modules/repofiles/delete.go deleted file mode 100644 index 5ae418b7f6..0000000000 --- a/modules/repofiles/delete.go +++ /dev/null @@ -1,197 +0,0 @@ -// 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 - Signoff bool -} - -// 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 if err := VerifyBranchProtection(repo, doer, opts.OldBranch, opts.TreePath); err != nil { - return nil, err - } - - // 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.Signoff, opts.Dates.Author, opts.Dates.Committer) - } else { - commitHash, err = t.CommitTree(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 { - 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 -} diff --git a/modules/repofiles/diff.go b/modules/repofiles/diff.go deleted file mode 100644 index c98bbc7684..0000000000 --- a/modules/repofiles/diff.go +++ /dev/null @@ -1,42 +0,0 @@ -// 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 ( - "strings" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/services/gitdiff" -) - -// GetDiffPreview produces and returns diff result of a file which is not yet committed. -func GetDiffPreview(repo *models.Repository, branch, treePath, content string) (*gitdiff.Diff, error) { - if branch == "" { - branch = repo.DefaultBranch - } - t, err := NewTemporaryUploadRepository(repo) - if err != nil { - return nil, err - } - defer t.Close() - if err := t.Clone(branch); err != nil { - return nil, err - } - if err := t.SetDefaultIndex(); err != nil { - return nil, err - } - - // Add the object to the database - objectHash, err := t.HashObject(strings.NewReader(content)) - if err != nil { - return nil, err - } - - // Add the object to the index - if err := t.AddObjectToIndex("100644", objectHash, treePath); err != nil { - return nil, err - } - return t.DiffIndex() -} diff --git a/modules/repofiles/diff_test.go b/modules/repofiles/diff_test.go deleted file mode 100644 index 4bd1ef6f4d..0000000000 --- a/modules/repofiles/diff_test.go +++ /dev/null @@ -1,172 +0,0 @@ -// 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 ( - "testing" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/services/gitdiff" - - "github.com/stretchr/testify/assert" -) - -func TestGetDiffPreview(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - branch := ctx.Repo.Repository.DefaultBranch - treePath := "README.md" - content := "# repo1\n\nDescription for repo1\nthis is a new line" - - expectedDiff := &gitdiff.Diff{ - TotalAddition: 2, - TotalDeletion: 1, - Files: []*gitdiff.DiffFile{ - { - Name: "README.md", - OldName: "README.md", - Index: 1, - Addition: 2, - Deletion: 1, - Type: 2, - IsCreated: false, - IsDeleted: false, - IsBin: false, - IsLFSFile: false, - IsRenamed: false, - IsSubmodule: false, - Sections: []*gitdiff.DiffSection{ - { - FileName: "README.md", - Name: "", - Lines: []*gitdiff.DiffLine{ - { - LeftIdx: 0, - RightIdx: 0, - Type: 4, - Content: "@@ -1,3 +1,4 @@", - Comments: nil, - SectionInfo: &gitdiff.DiffLineSectionInfo{ - Path: "README.md", - LastLeftIdx: 0, - LastRightIdx: 0, - LeftIdx: 1, - RightIdx: 1, - LeftHunkSize: 3, - RightHunkSize: 4, - }, - }, - { - LeftIdx: 1, - RightIdx: 1, - Type: 1, - Content: " # repo1", - Comments: nil, - }, - { - LeftIdx: 2, - RightIdx: 2, - Type: 1, - Content: " ", - Comments: nil, - }, - { - LeftIdx: 3, - RightIdx: 0, - Match: 4, - Type: 3, - Content: "-Description for repo1", - Comments: nil, - }, - { - LeftIdx: 0, - RightIdx: 3, - Match: 3, - Type: 2, - Content: "+Description for repo1", - Comments: nil, - }, - { - LeftIdx: 0, - RightIdx: 4, - Match: -1, - Type: 2, - Content: "+this is a new line", - Comments: nil, - }, - }, - }, - }, - IsIncomplete: false, - }, - }, - IsIncomplete: false, - } - expectedDiff.NumFiles = len(expectedDiff.Files) - - t.Run("with given branch", func(t *testing.T) { - diff, err := GetDiffPreview(ctx.Repo.Repository, branch, treePath, content) - assert.NoError(t, err) - expectedBs, err := json.Marshal(expectedDiff) - assert.NoError(t, err) - bs, err := json.Marshal(diff) - assert.NoError(t, err) - assert.EqualValues(t, expectedBs, bs) - }) - - t.Run("empty branch, same results", func(t *testing.T) { - diff, err := GetDiffPreview(ctx.Repo.Repository, "", treePath, content) - assert.NoError(t, err) - expectedBs, err := json.Marshal(expectedDiff) - assert.NoError(t, err) - bs, err := json.Marshal(diff) - assert.NoError(t, err) - assert.EqualValues(t, expectedBs, bs) - }) -} - -func TestGetDiffPreviewErrors(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - branch := ctx.Repo.Repository.DefaultBranch - treePath := "README.md" - content := "# repo1\n\nDescription for repo1\nthis is a new line" - - t.Run("empty repo", func(t *testing.T) { - diff, err := GetDiffPreview(&models.Repository{}, branch, treePath, content) - assert.Nil(t, diff) - assert.EqualError(t, err, "repository does not exist [id: 0, uid: 0, owner_name: , name: ]") - }) - - t.Run("bad branch", func(t *testing.T) { - badBranch := "bad_branch" - diff, err := GetDiffPreview(ctx.Repo.Repository, badBranch, treePath, content) - assert.Nil(t, diff) - assert.EqualError(t, err, "branch does not exist [name: "+badBranch+"]") - }) - - t.Run("empty treePath", func(t *testing.T) { - diff, err := GetDiffPreview(ctx.Repo.Repository, branch, "", content) - assert.Nil(t, diff) - assert.EqualError(t, err, "path is invalid [path: ]") - }) -} diff --git a/modules/repofiles/file.go b/modules/repofiles/file.go deleted file mode 100644 index 4030924017..0000000000 --- a/modules/repofiles/file.go +++ /dev/null @@ -1,125 +0,0 @@ -// 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" - "net/url" - "strings" - "time" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - api "code.gitea.io/gitea/modules/structs" -) - -// GetFileResponseFromCommit Constructs a FileResponse from a Commit object -func GetFileResponseFromCommit(repo *models.Repository, commit *git.Commit, branch, treeName string) (*api.FileResponse, error) { - fileContents, _ := GetContents(repo, treeName, branch, false) // ok if fails, then will be nil - fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil - verification := GetPayloadCommitVerification(commit) - fileResponse := &api.FileResponse{ - Content: fileContents, - Commit: fileCommitResponse, - Verification: verification, - } - return fileResponse, nil -} - -// GetFileCommitResponse Constructs a FileCommitResponse from a Commit object -func GetFileCommitResponse(repo *models.Repository, commit *git.Commit) (*api.FileCommitResponse, error) { - if repo == nil { - return nil, fmt.Errorf("repo cannot be nil") - } - if commit == nil { - return nil, fmt.Errorf("commit cannot be nil") - } - commitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String())) - commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + url.PathEscape(commit.Tree.ID.String())) - parents := make([]*api.CommitMeta, commit.ParentCount()) - for i := 0; i <= commit.ParentCount(); i++ { - if parent, err := commit.Parent(i); err == nil && parent != nil { - parentCommitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(parent.ID.String())) - parents[i] = &api.CommitMeta{ - SHA: parent.ID.String(), - URL: parentCommitURL.String(), - } - } - } - commitHTMLURL, _ := url.Parse(repo.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String())) - fileCommit := &api.FileCommitResponse{ - CommitMeta: api.CommitMeta{ - SHA: commit.ID.String(), - URL: commitURL.String(), - }, - HTMLURL: commitHTMLURL.String(), - Author: &api.CommitUser{ - Identity: api.Identity{ - Name: commit.Author.Name, - Email: commit.Author.Email, - }, - Date: commit.Author.When.UTC().Format(time.RFC3339), - }, - Committer: &api.CommitUser{ - Identity: api.Identity{ - Name: commit.Committer.Name, - Email: commit.Committer.Email, - }, - Date: commit.Committer.When.UTC().Format(time.RFC3339), - }, - Message: commit.Message(), - Tree: &api.CommitMeta{ - URL: commitTreeURL.String(), - SHA: commit.Tree.ID.String(), - }, - Parents: parents, - } - return fileCommit, nil -} - -// GetAuthorAndCommitterUsers Gets the author and committer user objects from the IdentityOptions -func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models.User) (authorUser, committerUser *models.User) { - // Committer and author are optional. If they are not the doer (not same email address) - // then we use bogus User objects for them to store their FullName and Email. - // If only one of the two are provided, we set both of them to it. - // If neither are provided, both are the doer. - if committer != nil && committer.Email != "" { - if doer != nil && strings.EqualFold(doer.Email, committer.Email) { - committerUser = doer // the committer is the doer, so will use their user object - if committer.Name != "" { - committerUser.FullName = committer.Name - } - } else { - committerUser = &models.User{ - FullName: committer.Name, - Email: committer.Email, - } - } - } - if author != nil && author.Email != "" { - if doer != nil && strings.EqualFold(doer.Email, author.Email) { - authorUser = doer // the author is the doer, so will use their user object - if authorUser.Name != "" { - authorUser.FullName = author.Name - } - } else { - authorUser = &models.User{ - FullName: author.Name, - Email: author.Email, - } - } - } - if authorUser == nil { - if committerUser != nil { - authorUser = committerUser // No valid author was given so use the committer - } else if doer != nil { - authorUser = doer // No valid author was given and no valid committer so use the doer - } - } - if committerUser == nil { - committerUser = authorUser // No valid committer so use the author as the committer (was set to a valid user above) - } - return authorUser, committerUser -} diff --git a/modules/repofiles/file_test.go b/modules/repofiles/file_test.go deleted file mode 100644 index 54205a89c7..0000000000 --- a/modules/repofiles/file_test.go +++ /dev/null @@ -1,104 +0,0 @@ -// 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 ( - "testing" - - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/test" - - "github.com/stretchr/testify/assert" -) - -func getExpectedFileResponse() *api.FileResponse { - treePath := "README.md" - sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" - encoding := "base64" - content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x" - selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master" - htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath - gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha - downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath - return &api.FileResponse{ - Content: &api.ContentsResponse{ - Name: treePath, - Path: treePath, - SHA: sha, - Type: "file", - Size: 30, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, - Links: &api.FileLinksResponse{ - Self: &selfURL, - GitURL: &gitURL, - HTMLURL: &htmlURL, - }, - }, - Commit: &api.FileCommitResponse{ - CommitMeta: api.CommitMeta{ - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", - SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", - }, - HTMLURL: "https://try.gitea.io/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d", - Author: &api.CommitUser{ - Identity: api.Identity{ - Name: "user1", - Email: "address1@example.com", - }, - Date: "2017-03-19T20:47:59Z", - }, - Committer: &api.CommitUser{ - Identity: api.Identity{ - Name: "Ethan Koenig", - Email: "ethantkoenig@gmail.com", - }, - Date: "2017-03-19T20:47:59Z", - }, - Parents: []*api.CommitMeta{}, - Message: "Initial commit\n", - Tree: &api.CommitMeta{ - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6", - SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6", - }, - }, - Verification: &api.PayloadCommitVerification{ - Verified: false, - Reason: "gpg.error.not_signed_commit", - Signature: "", - Payload: "", - }, - } -} - -func TestGetFileResponseFromCommit(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - repo := ctx.Repo.Repository - branch := repo.DefaultBranch - treePath := "README.md" - gitRepo, _ := git.OpenRepository(repo.RepoPath()) - defer gitRepo.Close() - commit, _ := gitRepo.GetBranchCommit(branch) - expectedFileResponse := getExpectedFileResponse() - - fileResponse, err := GetFileResponseFromCommit(repo, commit, branch, treePath) - assert.NoError(t, err) - assert.EqualValues(t, expectedFileResponse, fileResponse) -} diff --git a/modules/repofiles/repofiles.go b/modules/repofiles/repofiles.go deleted file mode 100644 index 1fc900490c..0000000000 --- a/modules/repofiles/repofiles.go +++ /dev/null @@ -1,23 +0,0 @@ -// 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 - -package repofiles - -import ( - "path" - "strings" -) - -// CleanUploadFileName Trims a filename and returns empty string if it is a .git directory -func CleanUploadFileName(name string) string { - // Rebase the filename - name = strings.Trim(path.Clean("/"+name), " /") - // Git disallows any filenames to have a .git directory in them. - for _, part := range strings.Split(name, "/") { - if strings.ToLower(part) == ".git" { - return "" - } - } - return name -} diff --git a/modules/repofiles/repofiles_test.go b/modules/repofiles/repofiles_test.go deleted file mode 100644 index 1686378a4a..0000000000 --- a/modules/repofiles/repofiles_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCleanUploadFileName(t *testing.T) { - t.Run("Clean regular file", func(t *testing.T) { - name := "this/is/test" - cleanName := CleanUploadFileName(name) - expectedCleanName := name - assert.EqualValues(t, expectedCleanName, cleanName) - }) - - t.Run("Clean a .git path", func(t *testing.T) { - name := "this/is/test/.git" - cleanName := CleanUploadFileName(name) - expectedCleanName := "" - assert.EqualValues(t, expectedCleanName, cleanName) - }) -} diff --git a/modules/repofiles/temp_repo.go b/modules/repofiles/temp_repo.go deleted file mode 100644 index 700ce92ebf..0000000000 --- a/modules/repofiles/temp_repo.go +++ /dev/null @@ -1,345 +0,0 @@ -// 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 ( - "bytes" - "context" - "fmt" - "io" - "os" - "regexp" - "strings" - "time" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/services/gitdiff" -) - -// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone -type TemporaryUploadRepository struct { - repo *models.Repository - gitRepo *git.Repository - basePath string -} - -// NewTemporaryUploadRepository creates a new temporary upload repository -func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepository, error) { - basePath, err := models.CreateTemporaryPath("upload") - if err != nil { - return nil, err - } - t := &TemporaryUploadRepository{repo: repo, basePath: basePath} - return t, nil -} - -// Close the repository cleaning up all files -func (t *TemporaryUploadRepository) Close() { - defer t.gitRepo.Close() - if err := models.RemoveTemporaryPath(t.basePath); err != nil { - log.Error("Failed to remove temporary path %s: %v", t.basePath, err) - } -} - -// Clone the base repository to our path and set branch as the HEAD -func (t *TemporaryUploadRepository) Clone(branch string) error { - if _, err := git.NewCommand("clone", "-s", "--bare", "-b", branch, t.repo.RepoPath(), t.basePath).Run(); err != nil { - stderr := err.Error() - if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched { - return git.ErrBranchNotExist{ - Name: branch, - } - } else if matched, _ := regexp.MatchString(".* repository .* does not exist.*", stderr); matched { - return models.ErrRepoNotExist{ - ID: t.repo.ID, - UID: t.repo.OwnerID, - OwnerName: t.repo.OwnerName, - Name: t.repo.Name, - } - } else { - return fmt.Errorf("Clone: %v %s", err, stderr) - } - } - gitRepo, err := git.OpenRepository(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("read-tree", "HEAD").RunInDir(t.basePath); err != nil { - return fmt.Errorf("SetDefaultIndex: %v", err) - } - return nil -} - -// LsFiles checks if the given filename arguments are in the index -func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, error) { - stdOut := new(bytes.Buffer) - stdErr := new(bytes.Buffer) - - cmdArgs := []string{"ls-files", "-z", "--"} - for _, arg := range filenames { - if arg != "" { - cmdArgs = append(cmdArgs, arg) - } - } - - if err := git.NewCommand(cmdArgs...).RunInDirPipeline(t.basePath, stdOut, stdErr); err != nil { - log.Error("Unable to run git ls-files for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) - err = fmt.Errorf("Unable to run git ls-files for temporary repo of: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) - return nil, err - } - - filelist := make([]string, len(filenames)) - for _, line := range bytes.Split(stdOut.Bytes(), []byte{'\000'}) { - filelist = append(filelist, string(line)) - } - - return filelist, nil -} - -// RemoveFilesFromIndex removes the given files from the index -func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) error { - stdOut := new(bytes.Buffer) - stdErr := new(bytes.Buffer) - stdIn := new(bytes.Buffer) - for _, file := range filenames { - if file != "" { - stdIn.WriteString("0 0000000000000000000000000000000000000000\t") - stdIn.WriteString(file) - stdIn.WriteByte('\000') - } - } - - if err := git.NewCommand("update-index", "--remove", "-z", "--index-info").RunInDirFullPipeline(t.basePath, stdOut, stdErr, stdIn); err != nil { - log.Error("Unable to update-index for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) - return fmt.Errorf("Unable to update-index for temporary repo: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) - } - return nil -} - -// HashObject writes the provided content to the object db and returns its hash -func (t *TemporaryUploadRepository) HashObject(content io.Reader) (string, error) { - stdOut := new(bytes.Buffer) - stdErr := new(bytes.Buffer) - - if err := git.NewCommand("hash-object", "-w", "--stdin").RunInDirFullPipeline(t.basePath, stdOut, stdErr, content); err != nil { - log.Error("Unable to hash-object to temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) - return "", fmt.Errorf("Unable to hash-object to temporary repo: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) - } - - return strings.TrimSpace(stdOut.String()), nil -} - -// AddObjectToIndex adds the provided object hash to the index with the provided mode and path -func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPath string) error { - if _, err := git.NewCommand("update-index", "--add", "--replace", "--cacheinfo", mode, objectHash, objectPath).RunInDir(t.basePath); err != nil { - stderr := err.Error() - if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched { - return models.ErrFilePathInvalid{ - Message: objectPath, - Path: objectPath, - } - } - log.Error("Unable to add object to index: %s %s %s in temporary repo %s(%s) Error: %v", mode, objectHash, objectPath, t.repo.FullName(), t.basePath, err) - return fmt.Errorf("Unable to add object to index at %s in temporary repo %s Error: %v", objectPath, t.repo.FullName(), err) - } - return nil -} - -// WriteTree writes the current index as a tree to the object db and returns its hash -func (t *TemporaryUploadRepository) WriteTree() (string, error) { - stdout, err := git.NewCommand("write-tree").RunInDir(t.basePath) - if err != nil { - log.Error("Unable to write tree in temporary repo: %s(%s): Error: %v", t.repo.FullName(), t.basePath, err) - return "", fmt.Errorf("Unable to write-tree in temporary repo for: %s Error: %v", t.repo.FullName(), err) - } - return strings.TrimSpace(stdout), nil -} - -// GetLastCommit gets the last commit ID SHA of the repo -func (t *TemporaryUploadRepository) GetLastCommit() (string, error) { - return t.GetLastCommitByRef("HEAD") -} - -// GetLastCommitByRef gets the last commit ID SHA of the repo by ref -func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, error) { - if ref == "" { - ref = "HEAD" - } - stdout, err := git.NewCommand("rev-parse", ref).RunInDir(t.basePath) - if err != nil { - log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err) - return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %v", ref, t.repo.FullName(), err) - } - return strings.TrimSpace(stdout), nil -} - -// CommitTree creates a commit from a given tree for the user with provided message -func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, treeHash string, message string, signoff bool) (string, error) { - return t.CommitTreeWithDate(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 *models.User, treeHash string, message string, signoff bool, authorDate, committerDate time.Time) (string, error) { - authorSig := author.NewGitSig() - committerSig := committer.NewGitSig() - - err := git.LoadGitVersion() - if err != nil { - return "", fmt.Errorf("Unable to get git version: %v", err) - } - - // Because this may call hooks we should pass in the environment - env := append(os.Environ(), - "GIT_AUTHOR_NAME="+authorSig.Name, - "GIT_AUTHOR_EMAIL="+authorSig.Email, - "GIT_AUTHOR_DATE="+authorDate.Format(time.RFC3339), - "GIT_COMMITTER_DATE="+committerDate.Format(time.RFC3339), - ) - - messageBytes := new(bytes.Buffer) - _, _ = messageBytes.WriteString(message) - _, _ = messageBytes.WriteString("\n") - - args := []string{"commit-tree", treeHash, "-p", "HEAD"} - - // Determine if we should sign - if git.CheckGitVersionAtLeast("1.7.9") == nil { - sign, keyID, signer, _ := t.repo.SignCRUDAction(author, t.basePath, "HEAD") - if sign { - args = append(args, "-S"+keyID) - if t.repo.GetTrustModel() == models.CommitterTrustModel || t.repo.GetTrustModel() == models.CollaboratorCommitterTrustModel { - if committerSig.Name != authorSig.Name || committerSig.Email != authorSig.Email { - // Add trailers - _, _ = messageBytes.WriteString("\n") - _, _ = messageBytes.WriteString("Co-authored-by: ") - _, _ = messageBytes.WriteString(committerSig.String()) - _, _ = messageBytes.WriteString("\n") - _, _ = messageBytes.WriteString("Co-committed-by: ") - _, _ = messageBytes.WriteString(committerSig.String()) - _, _ = messageBytes.WriteString("\n") - } - committerSig = signer - } - } else if git.CheckGitVersionAtLeast("2.0.0") == nil { - args = append(args, "--no-gpg-sign") - } - } - - if signoff { - // Signed-off-by - _, _ = messageBytes.WriteString("\n") - _, _ = messageBytes.WriteString("Signed-off-by: ") - _, _ = messageBytes.WriteString(committerSig.String()) - } - - env = append(env, - "GIT_COMMITTER_NAME="+committerSig.Name, - "GIT_COMMITTER_EMAIL="+committerSig.Email, - ) - - stdout := new(bytes.Buffer) - stderr := new(bytes.Buffer) - if err := git.NewCommand(args...).RunInDirTimeoutEnvFullPipeline(env, -1, t.basePath, stdout, stderr, messageBytes); err != nil { - log.Error("Unable to commit-tree in temporary repo: %s (%s) Error: %v\nStdout: %s\nStderr: %s", - t.repo.FullName(), t.basePath, err, stdout, stderr) - return "", fmt.Errorf("Unable to commit-tree in temporary repo: %s Error: %v\nStdout: %s\nStderr: %s", - t.repo.FullName(), err, stdout, stderr) - } - return strings.TrimSpace(stdout.String()), nil -} - -// Push the provided commitHash to the repository branch by the provided user -func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error { - // Because calls hooks we need to pass in the environment - env := models.PushingEnvironment(doer, t.repo) - if err := git.Push(t.basePath, git.PushOptions{ - Remote: t.repo.RepoPath(), - Branch: strings.TrimSpace(commitHash) + ":refs/heads/" + strings.TrimSpace(branch), - Env: env, - }); err != nil { - if git.IsErrPushOutOfDate(err) { - return err - } else if git.IsErrPushRejected(err) { - rejectErr := err.(*git.ErrPushRejected) - log.Info("Unable to push back to repo from temporary repo due to rejection: %s (%s)\nStdout: %s\nStderr: %s\nError: %v", - t.repo.FullName(), t.basePath, rejectErr.StdOut, rejectErr.StdErr, rejectErr.Err) - return err - } - log.Error("Unable to push back to repo from temporary repo: %s (%s)\nError: %v", - t.repo.FullName(), t.basePath, err) - return fmt.Errorf("Unable to push back to repo from temporary repo: %s (%s) Error: %v", - t.repo.FullName(), t.basePath, err) - } - return nil -} - -// DiffIndex returns a Diff of the current index to the head -func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { - stdoutReader, stdoutWriter, err := os.Pipe() - if err != nil { - log.Error("Unable to open stdout pipe: %v", err) - return nil, fmt.Errorf("Unable to open stdout pipe: %v", err) - } - defer func() { - _ = stdoutReader.Close() - _ = stdoutWriter.Close() - }() - stderr := new(bytes.Buffer) - var diff *gitdiff.Diff - var finalErr error - - if err := git.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD"). - RunInDirTimeoutEnvFullPipelineFunc(nil, 30*time.Second, t.basePath, stdoutWriter, stderr, nil, func(ctx context.Context, cancel context.CancelFunc) error { - _ = stdoutWriter.Close() - diff, finalErr = gitdiff.ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "") - if finalErr != nil { - log.Error("ParsePatch: %v", finalErr) - cancel() - } - _ = stdoutReader.Close() - return finalErr - }); err != nil { - if finalErr != nil { - log.Error("Unable to ParsePatch in temporary repo %s (%s). Error: %v", t.repo.FullName(), t.basePath, finalErr) - return nil, finalErr - } - log.Error("Unable to run diff-index pipeline in temporary repo %s (%s). Error: %v\nStderr: %s", - t.repo.FullName(), t.basePath, err, stderr) - return nil, fmt.Errorf("Unable to run diff-index pipeline in temporary repo %s. Error: %v\nStderr: %s", - t.repo.FullName(), err, stderr) - } - - diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(t.basePath, "--cached", "HEAD") - if err != nil { - return nil, err - } - - return diff, nil -} - -// GetBranchCommit Gets the commit object of the given branch -func (t *TemporaryUploadRepository) GetBranchCommit(branch string) (*git.Commit, error) { - if t.gitRepo == nil { - return nil, fmt.Errorf("repository has not been cloned") - } - return t.gitRepo.GetBranchCommit(branch) -} - -// GetCommit Gets the commit object of the given commit ID -func (t *TemporaryUploadRepository) GetCommit(commitID string) (*git.Commit, error) { - if t.gitRepo == nil { - return nil, fmt.Errorf("repository has not been cloned") - } - return t.gitRepo.GetCommit(commitID) -} diff --git a/modules/repofiles/tree.go b/modules/repofiles/tree.go deleted file mode 100644 index 81579dccc5..0000000000 --- a/modules/repofiles/tree.go +++ /dev/null @@ -1,98 +0,0 @@ -// 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" - "net/url" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" -) - -// GetTreeBySHA get the GitTreeResponse of a repository using a sha hash. -func GetTreeBySHA(repo *models.Repository, sha string, page, perPage int, recursive bool) (*api.GitTreeResponse, error) { - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() - gitTree, err := gitRepo.GetTree(sha) - if err != nil || gitTree == nil { - return nil, models.ErrSHANotFound{ - SHA: sha, - } - } - tree := new(api.GitTreeResponse) - tree.SHA = gitTree.ResolvedID.String() - tree.URL = repo.APIURL() + "/git/trees/" + url.PathEscape(tree.SHA) - var entries git.Entries - if recursive { - entries, err = gitTree.ListEntriesRecursive() - } else { - entries, err = gitTree.ListEntries() - } - if err != nil { - return nil, err - } - apiURL := repo.APIURL() - apiURLLen := len(apiURL) - - // 51 is len(sha1) + len("/git/blobs/"). 40 + 11. - blobURL := make([]byte, apiURLLen+51) - copy(blobURL, apiURL) - copy(blobURL[apiURLLen:], "/git/blobs/") - - // 51 is len(sha1) + len("/git/trees/"). 40 + 11. - treeURL := make([]byte, apiURLLen+51) - copy(treeURL, apiURL) - copy(treeURL[apiURLLen:], "/git/trees/") - - // 40 is the size of the sha1 hash in hexadecimal format. - copyPos := len(treeURL) - 40 - - if perPage <= 0 || perPage > setting.API.DefaultGitTreesPerPage { - perPage = setting.API.DefaultGitTreesPerPage - } - if page <= 0 { - page = 1 - } - tree.Page = page - tree.TotalCount = len(entries) - rangeStart := perPage * (page - 1) - if rangeStart >= len(entries) { - return tree, nil - } - var rangeEnd int - if len(entries) > perPage { - tree.Truncated = true - } - if rangeStart+perPage < len(entries) { - rangeEnd = rangeStart + perPage - } else { - rangeEnd = len(entries) - } - tree.Entries = make([]api.GitEntry, rangeEnd-rangeStart) - for e := rangeStart; e < rangeEnd; e++ { - i := e - rangeStart - - tree.Entries[i].Path = entries[e].Name() - tree.Entries[i].Mode = fmt.Sprintf("%06o", entries[e].Mode()) - tree.Entries[i].Type = entries[e].Type() - tree.Entries[i].Size = entries[e].Size() - tree.Entries[i].SHA = entries[e].ID.String() - - if entries[e].IsDir() { - copy(treeURL[copyPos:], entries[e].ID.String()) - tree.Entries[i].URL = string(treeURL) - } else { - copy(blobURL[copyPos:], entries[e].ID.String()) - tree.Entries[i].URL = string(blobURL) - } - } - return tree, nil -} diff --git a/modules/repofiles/tree_test.go b/modules/repofiles/tree_test.go deleted file mode 100644 index 6917b8915b..0000000000 --- a/modules/repofiles/tree_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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 ( - "testing" - - "code.gitea.io/gitea/models/unittest" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/test" - - "github.com/stretchr/testify/assert" -) - -func TestGetTreeBySHA(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - sha := ctx.Repo.Repository.DefaultBranch - page := 1 - perPage := 10 - ctx.SetParams(":id", "1") - ctx.SetParams(":sha", sha) - - tree, err := GetTreeBySHA(ctx.Repo.Repository, ctx.Params(":sha"), page, perPage, true) - assert.NoError(t, err) - expectedTree := &api.GitTreeResponse{ - SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/65f1bf27bc3bf70f64657658635e66094edbcb4d", - Entries: []api.GitEntry{ - { - Path: "README.md", - Mode: "100644", - Type: "blob", - Size: 30, - SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/4b4851ad51df6a7d9f25c979345979eaeb5b349f", - }, - }, - Truncated: false, - Page: 1, - TotalCount: 1, - } - - assert.EqualValues(t, expectedTree, tree) -} diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go deleted file mode 100644 index d25accff18..0000000000 --- a/modules/repofiles/update.go +++ /dev/null @@ -1,479 +0,0 @@ -// 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 ( - "bytes" - "fmt" - "path" - "strings" - "time" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/lfs" - "code.gitea.io/gitea/modules/log" - repo_module "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" - - stdcharset "golang.org/x/net/html/charset" - "golang.org/x/text/transform" -) - -// IdentityOptions for a person's identity like an author or committer -type IdentityOptions struct { - Name string - Email string -} - -// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE -type CommitDateOptions struct { - Author time.Time - Committer time.Time -} - -// UpdateRepoFileOptions holds the repository file update options -type UpdateRepoFileOptions struct { - LastCommitID string - OldBranch string - NewBranch string - TreePath string - FromTreePath string - Message string - Content string - SHA string - IsNewFile bool - Author *IdentityOptions - Committer *IdentityOptions - Dates *CommitDateOptions - Signoff bool -} - -func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string, bool) { - reader, err := entry.Blob().DataAsync() - if err != nil { - // return default - return "UTF-8", false - } - defer reader.Close() - buf := make([]byte, 1024) - n, err := util.ReadAtMost(reader, buf) - if err != nil { - // return default - return "UTF-8", false - } - buf = buf[:n] - - if setting.LFS.StartServer { - pointer, _ := lfs.ReadPointerFromBuffer(buf) - if pointer.IsValid() { - meta, err := repo.GetLFSMetaObjectByOid(pointer.Oid) - if err != nil && err != models.ErrLFSObjectNotExist { - // return default - return "UTF-8", false - } - if meta != nil { - dataRc, err := lfs.ReadMetaObject(pointer) - if err != nil { - // return default - return "UTF-8", false - } - defer dataRc.Close() - buf = make([]byte, 1024) - n, err = util.ReadAtMost(dataRc, buf) - if err != nil { - // return default - return "UTF-8", false - } - buf = buf[:n] - } - } - } - - encoding, err := charset.DetectEncoding(buf) - if err != nil { - // just default to utf-8 and no bom - return "UTF-8", false - } - if encoding == "UTF-8" { - return encoding, bytes.Equal(buf[0:3], charset.UTF8BOM) - } - charsetEncoding, _ := stdcharset.Lookup(encoding) - if charsetEncoding == nil { - return "UTF-8", false - } - - result, n, err := transform.String(charsetEncoding.NewDecoder(), string(buf)) - if err != nil { - // return default - return "UTF-8", false - } - - if n > 2 { - return encoding, bytes.Equal([]byte(result)[0:3], charset.UTF8BOM) - } - - return encoding, false -} - -// CreateOrUpdateRepoFile adds or updates a file in the given repository -func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepoFileOptions) (*structs.FileResponse, error) { - // If no branch name is set, assume 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 { - existingBranch, err := repo_module.GetBranch(repo, opts.NewBranch) - if existingBranch != nil { - return nil, models.ErrBranchAlreadyExists{ - BranchName: opts.NewBranch, - } - } - if err != nil && !git.IsErrBranchNotExist(err) { - return nil, err - } - } else if err := VerifyBranchProtection(repo, doer, opts.OldBranch, opts.TreePath); err != nil { - return nil, err - } - - // If FromTreePath is not set, set it to the opts.TreePath - if opts.TreePath != "" && opts.FromTreePath == "" { - opts.FromTreePath = opts.TreePath - } - - // Check that the path given in opts.treePath is valid (not a git path) - treePath := CleanUploadFileName(opts.TreePath) - if treePath == "" { - return nil, models.ErrFilenameInvalid{ - Path: opts.TreePath, - } - } - // If there is a fromTreePath (we are copying it), also clean it up - fromTreePath := CleanUploadFileName(opts.FromTreePath) - if fromTreePath == "" && opts.FromTreePath != "" { - return nil, models.ErrFilenameInvalid{ - Path: opts.FromTreePath, - } - } - - message := strings.TrimSpace(opts.Message) - - author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer) - - t, err := NewTemporaryUploadRepository(repo) - if err != nil { - log.Error("%v", 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() - - } - - encoding := "UTF-8" - bom := false - executable := false - - 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 - } - } 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() - } - - // 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 - } - 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 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 - filesInIndex, err := t.LsFiles(opts.TreePath, opts.FromTreePath) - if err != nil { - return nil, fmt.Errorf("UpdateRepoFile: %v", err) - } - // If is a new file (not updating) then the given path shouldn't exist - if opts.IsNewFile { - for _, file := range filesInIndex { - if file == opts.TreePath { - return nil, models.ErrRepoFileAlreadyExists{ - Path: opts.TreePath, - } - } - } - } - - // Remove the old path from the tree - if fromTreePath != treePath && len(filesInIndex) > 0 { - for _, file := range filesInIndex { - if file == fromTreePath { - if err := t.RemoveFilesFromIndex(opts.FromTreePath); err != nil { - return nil, err - } - } - } - } - - content := opts.Content - if bom { - content = string(charset.UTF8BOM) + content - } - if encoding != "UTF-8" { - charsetEncoding, _ := stdcharset.Lookup(encoding) - if charsetEncoding != nil { - result, _, err := transform.String(charsetEncoding.NewEncoder(), content) - if err != nil { - // Look if we can't encode back in to the original we should just stick with utf-8 - log.Error("Error re-encoding %s (%s) as %s - will stay as UTF-8: %v", opts.TreePath, opts.FromTreePath, encoding, err) - result = content - } - content = result - } else { - log.Error("Unknown encoding: %s", encoding) - } - } - // Reset the opts.Content to our adjusted content to ensure that LFS gets the correct content - opts.Content = content - var lfsMetaObject *models.LFSMetaObject - - if setting.LFS.StartServer { - // Check there is no way this can return multiple infos - filename2attribute2info, err := t.gitRepo.CheckAttribute(git.CheckAttributeOpts{ - Attributes: []string{"filter"}, - Filenames: []string{treePath}, - }) - if err != nil { - return nil, err - } - - if filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" { - // OK so we are supposed to LFS this data! - pointer, err := lfs.GeneratePointer(strings.NewReader(opts.Content)) - if err != nil { - return nil, err - } - lfsMetaObject = &models.LFSMetaObject{Pointer: pointer, RepositoryID: repo.ID} - content = pointer.StringContent() - } - } - // Add the object to the database - objectHash, err := t.HashObject(strings.NewReader(content)) - if err != nil { - return nil, err - } - - // Add the object to the index - if executable { - if err := t.AddObjectToIndex("100755", objectHash, treePath); err != nil { - return nil, err - } - } else { - if err := t.AddObjectToIndex("100644", objectHash, 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.Signoff, opts.Dates.Author, opts.Dates.Committer) - } else { - commitHash, err = t.CommitTree(author, committer, treeHash, message, opts.Signoff) - } - if err != nil { - return nil, err - } - - if lfsMetaObject != nil { - // We have an LFS object - create it - lfsMetaObject, err = models.NewLFSMetaObject(lfsMetaObject) - if err != nil { - return nil, err - } - contentStore := lfs.NewContentStore() - exist, err := contentStore.Exists(lfsMetaObject.Pointer) - if err != nil { - return nil, err - } - if !exist { - if err := contentStore.Put(lfsMetaObject.Pointer, strings.NewReader(opts.Content)); err != nil { - if _, err2 := repo.RemoveLFSMetaObjectByOid(lfsMetaObject.Oid); err2 != nil { - return nil, fmt.Errorf("Error whilst removing failed inserted LFS object %s: %v (Prev Error: %v)", lfsMetaObject.Oid, err2, err) - } - 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 - } - - file, err := GetFileResponseFromCommit(repo, commit, opts.NewBranch, treePath) - if err != nil { - return nil, err - } - return file, nil -} - -// VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch -func VerifyBranchProtection(repo *models.Repository, doer *models.User, branchName string, treePath string) error { - protectedBranch, err := repo.GetBranchProtection(branchName) - if err != nil { - return err - } - if protectedBranch != nil { - isUnprotectedFile := false - glob := protectedBranch.GetUnprotectedFilePatterns() - if len(glob) != 0 { - isUnprotectedFile = protectedBranch.IsUnprotectedFile(glob, treePath) - } - if !protectedBranch.CanUserPush(doer.ID) && !isUnprotectedFile { - return models.ErrUserCannotCommit{ - UserName: doer.LowerName, - } - } - if protectedBranch.RequireSignedCommits { - _, _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), branchName) - if err != nil { - if !models.IsErrWontSign(err) { - return err - } - return models.ErrUserCannotCommit{ - UserName: doer.LowerName, - } - } - } - patterns := protectedBranch.GetProtectedFilePatterns() - for _, pat := range patterns { - if pat.Match(strings.ToLower(treePath)) { - return models.ErrFilePathProtected{ - Path: treePath, - } - } - } - } - return nil -} diff --git a/modules/repofiles/upload.go b/modules/repofiles/upload.go deleted file mode 100644 index e97f55a656..0000000000 --- a/modules/repofiles/upload.go +++ /dev/null @@ -1,207 +0,0 @@ -// 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" - "os" - "path" - "strings" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/lfs" - "code.gitea.io/gitea/modules/setting" -) - -// UploadRepoFileOptions contains the uploaded repository file options -type UploadRepoFileOptions struct { - LastCommitID string - OldBranch string - NewBranch string - TreePath string - Message string - Files []string // In UUID format. - Signoff bool -} - -type uploadInfo struct { - upload *models.Upload - lfsMetaObject *models.LFSMetaObject -} - -func cleanUpAfterFailure(infos *[]uploadInfo, t *TemporaryUploadRepository, original error) error { - for _, info := range *infos { - if info.lfsMetaObject == nil { - continue - } - if !info.lfsMetaObject.Existing { - if _, err := t.repo.RemoveLFSMetaObjectByOid(info.lfsMetaObject.Oid); err != nil { - original = fmt.Errorf("%v, %v", original, err) - } - } - } - return original -} - -// UploadRepoFiles uploads files to the given repository -func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions) error { - if len(opts.Files) == 0 { - return nil - } - - uploads, err := models.GetUploadsByUUIDs(opts.Files) - if err != nil { - return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err) - } - - names := make([]string, len(uploads)) - infos := make([]uploadInfo, len(uploads)) - for i, upload := range uploads { - // Check file is not lfs locked, will return nil if lock setting not enabled - filepath := path.Join(opts.TreePath, upload.Name) - lfsLock, err := repo.GetTreePathLock(filepath) - if err != nil { - return err - } - if lfsLock != nil && lfsLock.OwnerID != doer.ID { - return models.ErrLFSFileLocked{RepoID: repo.ID, Path: filepath, UserName: lfsLock.Owner.Name} - } - - names[i] = upload.Name - infos[i] = uploadInfo{upload: upload} - } - - t, err := NewTemporaryUploadRepository(repo) - if err != nil { - return err - } - defer t.Close() - if err := t.Clone(opts.OldBranch); err != nil { - return err - } - if err := t.SetDefaultIndex(); err != nil { - return err - } - - var filename2attribute2info map[string]map[string]string - if setting.LFS.StartServer { - filename2attribute2info, err = t.gitRepo.CheckAttribute(git.CheckAttributeOpts{ - Attributes: []string{"filter"}, - Filenames: names, - }) - if err != nil { - return err - } - } - - // Copy uploaded files into repository. - for i := range infos { - if err := copyUploadedLFSFileIntoRepository(&infos[i], filename2attribute2info, t, opts.TreePath); err != nil { - return err - } - } - - // Now write the tree - treeHash, err := t.WriteTree() - if err != nil { - return err - } - - // make author and committer the doer - author := doer - committer := doer - - // Now commit the tree - commitHash, err := t.CommitTree(author, committer, treeHash, opts.Message, opts.Signoff) - if err != nil { - return err - } - - // Now deal with LFS objects - for i := range infos { - if infos[i].lfsMetaObject == nil { - continue - } - infos[i].lfsMetaObject, err = models.NewLFSMetaObject(infos[i].lfsMetaObject) - if err != nil { - // OK Now we need to cleanup - return cleanUpAfterFailure(&infos, t, err) - } - // Don't move the files yet - we need to ensure that - // everything can be inserted first - } - - // OK now we can insert the data into the store - there's no way to clean up the store - // once it's in there, it's in there. - contentStore := lfs.NewContentStore() - for _, info := range infos { - if err := uploadToLFSContentStore(info, contentStore); err != nil { - return cleanUpAfterFailure(&infos, t, err) - } - } - - // Then push this tree to NewBranch - if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { - return err - } - - return models.DeleteUploads(uploads...) -} - -func copyUploadedLFSFileIntoRepository(info *uploadInfo, filename2attribute2info map[string]map[string]string, t *TemporaryUploadRepository, treePath string) error { - file, err := os.Open(info.upload.LocalPath()) - if err != nil { - return err - } - defer file.Close() - - var objectHash string - if setting.LFS.StartServer && filename2attribute2info[info.upload.Name] != nil && filename2attribute2info[info.upload.Name]["filter"] == "lfs" { - // Handle LFS - // FIXME: Inefficient! this should probably happen in models.Upload - pointer, err := lfs.GeneratePointer(file) - if err != nil { - return err - } - - info.lfsMetaObject = &models.LFSMetaObject{Pointer: pointer, RepositoryID: t.repo.ID} - - if objectHash, err = t.HashObject(strings.NewReader(pointer.StringContent())); err != nil { - return err - } - } else if objectHash, err = t.HashObject(file); err != nil { - return err - } - - // Add the object to the index - return t.AddObjectToIndex("100644", objectHash, path.Join(treePath, info.upload.Name)) -} - -func uploadToLFSContentStore(info uploadInfo, contentStore *lfs.ContentStore) error { - if info.lfsMetaObject == nil { - return nil - } - exist, err := contentStore.Exists(info.lfsMetaObject.Pointer) - if err != nil { - return err - } - if !exist { - file, err := os.Open(info.upload.LocalPath()) - if err != nil { - return err - } - - defer file.Close() - // FIXME: Put regenerates the hash and copies the file over. - // I guess this strictly ensures the soundness of the store but this is inefficient. - if err := contentStore.Put(info.lfsMetaObject.Pointer, file); err != nil { - // OK Now we need to cleanup - // Can't clean up the store, once uploaded there they're there. - return err - } - } - return nil -} diff --git a/modules/repofiles/verification.go b/modules/repofiles/verification.go deleted file mode 100644 index 3889b7993c..0000000000 --- a/modules/repofiles/verification.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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 ( - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/structs" -) - -// GetPayloadCommitVerification returns the verification information of a commit -func GetPayloadCommitVerification(commit *git.Commit) *structs.PayloadCommitVerification { - verification := &structs.PayloadCommitVerification{} - commitVerification := models.ParseCommitWithSignature(commit) - if commit.Signature != nil { - verification.Signature = commit.Signature.Signature - verification.Payload = commit.Signature.Payload - } - if commitVerification.SigningUser != nil { - verification.Signer = &structs.PayloadUser{ - Name: commitVerification.SigningUser.Name, - Email: commitVerification.SigningUser.Email, - } - } - verification.Verified = commitVerification.Verified - verification.Reason = commitVerification.Reason - if verification.Reason == "" && !verification.Verified { - verification.Reason = "gpg.error.not_signed_commit" - } - return verification -} diff --git a/modules/repository/branch.go b/modules/repository/branch.go deleted file mode 100644 index dcd82554d5..0000000000 --- a/modules/repository/branch.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 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 repository - -import ( - "fmt" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" -) - -// GetBranch returns a branch by its name -func GetBranch(repo *models.Repository, branch string) (*git.Branch, error) { - if len(branch) == 0 { - return nil, fmt.Errorf("GetBranch: empty string for branch") - } - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() - - return gitRepo.GetBranch(branch) -} diff --git a/modules/repository/check.go b/modules/repository/check.go deleted file mode 100644 index 36bd4e2e0d..0000000000 --- a/modules/repository/check.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2020 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 repository - -import ( - "context" - "fmt" - "strings" - "time" - - "code.gitea.io/gitea/models" - admin_model "code.gitea.io/gitea/models/admin" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" - - "xorm.io/builder" -) - -// GitFsck calls 'git fsck' to check repository health. -func GitFsck(ctx context.Context, timeout time.Duration, args []string) error { - log.Trace("Doing: GitFsck") - - if err := db.Iterate( - db.DefaultContext, - new(models.Repository), - builder.Expr("id>0 AND is_fsck_enabled=?", true), - func(idx int, bean interface{}) error { - repo := bean.(*models.Repository) - select { - case <-ctx.Done(): - return db.ErrCancelledf("before fsck of %s", repo.FullName()) - default: - } - log.Trace("Running health check on repository %v", repo) - repoPath := repo.RepoPath() - if err := git.Fsck(ctx, repoPath, timeout, args...); err != nil { - log.Warn("Failed to health check repository (%v): %v", repo, err) - if err = admin_model.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - } - return nil - }, - ); err != nil { - log.Trace("Error: GitFsck: %v", err) - return err - } - - log.Trace("Finished: GitFsck") - return nil -} - -// GitGcRepos calls 'git gc' to remove unnecessary files and optimize the local repository -func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) error { - log.Trace("Doing: GitGcRepos") - args = append([]string{"gc"}, args...) - - if err := db.Iterate( - db.DefaultContext, - new(models.Repository), - builder.Gt{"id": 0}, - func(idx int, bean interface{}) error { - repo := bean.(*models.Repository) - select { - case <-ctx.Done(): - return db.ErrCancelledf("before GC of %s", repo.FullName()) - default: - } - log.Trace("Running git gc on %v", repo) - command := git.NewCommandContext(ctx, args...). - SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName())) - var stdout string - var err error - if timeout > 0 { - var stdoutBytes []byte - stdoutBytes, err = command.RunInDirTimeout( - timeout, - repo.RepoPath()) - stdout = string(stdoutBytes) - } else { - stdout, err = command.RunInDir(repo.RepoPath()) - } - - if err != nil { - log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err) - desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err) - if err = admin_model.CreateRepositoryNotice(desc); err != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %v", repo.FullName(), err) - } - - // Now update the size of the repository - if err := repo.UpdateSize(db.DefaultContext); err != nil { - log.Error("Updating size as part of garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err) - desc := fmt.Sprintf("Updating size as part of garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err) - if err = admin_model.CreateRepositoryNotice(desc); err != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - return fmt.Errorf("Updating size as part of garbage collection failed in repo: %s: Error: %v", repo.FullName(), err) - } - - return nil - }, - ); err != nil { - return err - } - - log.Trace("Finished: GitGcRepos") - return nil -} - -func gatherMissingRepoRecords(ctx context.Context) ([]*models.Repository, error) { - repos := make([]*models.Repository, 0, 10) - if err := db.Iterate( - db.DefaultContext, - new(models.Repository), - builder.Gt{"id": 0}, - func(idx int, bean interface{}) error { - repo := bean.(*models.Repository) - select { - case <-ctx.Done(): - return db.ErrCancelledf("during gathering missing repo records before checking %s", repo.FullName()) - default: - } - isDir, err := util.IsDir(repo.RepoPath()) - if err != nil { - return fmt.Errorf("Unable to check dir for %s. %w", repo.FullName(), err) - } - if !isDir { - repos = append(repos, repo) - } - return nil - }, - ); err != nil { - if strings.HasPrefix(err.Error(), "Aborted gathering missing repo") { - return nil, err - } - if err2 := admin_model.CreateRepositoryNotice("gatherMissingRepoRecords: %v", err); err2 != nil { - log.Error("CreateRepositoryNotice: %v", err2) - } - return nil, err - } - return repos, nil -} - -// DeleteMissingRepositories deletes all repository records that lost Git files. -func DeleteMissingRepositories(ctx context.Context, doer *models.User) error { - repos, err := gatherMissingRepoRecords(ctx) - if err != nil { - return err - } - - if len(repos) == 0 { - return nil - } - - for _, repo := range repos { - select { - case <-ctx.Done(): - return db.ErrCancelledf("during DeleteMissingRepositories before %s", repo.FullName()) - default: - } - log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID) - if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil { - log.Error("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err) - if err2 := admin_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - } - } - return nil -} - -// ReinitMissingRepositories reinitializes all repository records that lost Git files. -func ReinitMissingRepositories(ctx context.Context) error { - repos, err := gatherMissingRepoRecords(ctx) - if err != nil { - return err - } - - if len(repos) == 0 { - return nil - } - - for _, repo := range repos { - select { - case <-ctx.Done(): - return db.ErrCancelledf("during ReinitMissingRepositories before %s", repo.FullName()) - default: - } - log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID) - if err := git.InitRepository(repo.RepoPath(), true); err != nil { - log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err) - if err2 := admin_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil { - log.Error("CreateRepositoryNotice: %v", err2) - } - } - } - return nil -} |