diff --git a/cmd/serv.go b/cmd/serv.go index 4110fda0d50b4..43e90ff141486 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -21,6 +21,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/gitcmd" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/lfstransfer" "code.gitea.io/gitea/modules/log" @@ -60,8 +61,9 @@ func setup(ctx context.Context, debug bool) { setupConsoleLogger(log.FATAL, false, os.Stderr) } setting.MustInstalled() - if _, err := os.Stat(setting.RepoRootPath); err != nil { - _ = fail(ctx, "Unable to access repository path", "Unable to access repository path %q, err: %v", setting.RepoRootPath, err) + + if err := gitrepo.RepoStoreStat(); err != nil { + _ = fail(ctx, "Unable to access repository path", "Check repository store failed: %v", err) return } if err := git.InitSimple(); err != nil { diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go index 479a46379c2eb..454c0b9b208ef 100644 --- a/models/migrations/base/db.go +++ b/models/migrations/base/db.go @@ -13,7 +13,6 @@ import ( "reflect" "regexp" "strings" - "time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" @@ -516,18 +515,6 @@ func ModifyColumn(x *xorm.Engine, tableName string, col *schemas.Column) error { return nil } -func removeAllWithRetry(dir string) error { - var err error - for range 20 { - err = os.RemoveAll(dir) - if err == nil { - break - } - time.Sleep(100 * time.Millisecond) - } - return err -} - func newXORMEngine() (*xorm.Engine, error) { if err := db.InitEngine(context.Background()); err != nil { return nil, err diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index 83beca8fb9df7..e9f7803e15be4 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/tempdir" "code.gitea.io/gitea/modules/test" @@ -33,7 +34,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu ourSkip := 2 ourSkip += skip deferFn := testlogger.PrintCurrentTest(t, ourSkip) - require.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) + require.NoError(t, gitrepo.SyncLocalToRepoStore(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"))) if err := deleteDB(); err != nil { t.Fatalf("unable to reset database: %v", err) @@ -142,8 +143,8 @@ func MainTest(m *testing.M) { exitStatus := m.Run() - if err := removeAllWithRetry(setting.RepoRootPath); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err) + if err := gitrepo.RemoveRepoStore(); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "gitrepo.RemoveRepoStore: %v\n", err) } os.Exit(exitStatus) } diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go index ff5b12af18f1c..17311c2e97304 100644 --- a/models/migrations/v1_12/v128.go +++ b/models/migrations/v1_12/v128.go @@ -13,21 +13,26 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/gitcmd" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "xorm.io/xorm" ) -func FixMergeBase(ctx context.Context, x *xorm.Engine) error { - type Repository struct { - ID int64 `xorm:"pk autoincr"` - OwnerID int64 `xorm:"UNIQUE(s) index"` - OwnerName string - LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` - Name string `xorm:"INDEX NOT NULL"` - } +type Repository struct { + ID int64 `xorm:"pk autoincr"` + OwnerID int64 `xorm:"UNIQUE(s) index"` + OwnerName string + LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` + Name string `xorm:"INDEX NOT NULL"` +} +func (r *Repository) RelativePath() string { + return filepath.Join(strings.ToLower(r.OwnerName), strings.ToLower(r.Name)+".git") +} + +func FixMergeBase(ctx context.Context, x *xorm.Engine) error { type PullRequest struct { ID int64 `xorm:"pk autoincr"` Index int64 @@ -77,24 +82,22 @@ func FixMergeBase(ctx context.Context, x *xorm.Engine) error { log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID) continue } - userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName)) - repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git") gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index) if !pr.HasMerged { var err error - pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).WithDir(repoPath).RunStdString(ctx) + pr.MergeBase, err = gitrepo.RunCmdString(ctx, baseRepo, gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName)) if err != nil { var err2 error - pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).WithDir(repoPath).RunStdString(ctx) + pr.MergeBase, err2 = gitrepo.RunCmdString(ctx, baseRepo, gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch)) if err2 != nil { log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2) continue } } } else { - parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx) + parentsString, err := gitrepo.RunCmdString(ctx, baseRepo, gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID)) if err != nil { log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue @@ -107,8 +110,7 @@ func FixMergeBase(ctx context.Context, x *xorm.Engine) error { refs := append([]string{}, parents[1:]...) refs = append(refs, gitRefName) cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...) - - pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx) + pr.MergeBase, err = gitrepo.RunCmdString(ctx, baseRepo, cmd) if err != nil { log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go index 98bb8dbda72fe..4c703418f46cc 100644 --- a/models/migrations/v1_12/v134.go +++ b/models/migrations/v1_12/v134.go @@ -12,30 +12,38 @@ import ( "time" "code.gitea.io/gitea/modules/git/gitcmd" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "xorm.io/xorm" ) -func RefixMergeBase(ctx context.Context, x *xorm.Engine) error { - type Repository struct { - ID int64 `xorm:"pk autoincr"` - OwnerID int64 `xorm:"UNIQUE(s) index"` - OwnerName string - LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` - Name string `xorm:"INDEX NOT NULL"` - } +type Repository2 struct { + ID int64 `xorm:"pk autoincr"` + OwnerID int64 `xorm:"UNIQUE(s) index"` + OwnerName string + LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` + Name string `xorm:"INDEX NOT NULL"` +} - type PullRequest struct { - ID int64 `xorm:"pk autoincr"` - Index int64 - HeadRepoID int64 `xorm:"INDEX"` - BaseRepoID int64 `xorm:"INDEX"` - HeadBranch string - BaseBranch string - MergeBase string `xorm:"VARCHAR(40)"` +func (r *Repository2) TableName() string { + return "repository" +} +func (r *Repository2) RelativePath() string { + return filepath.Join(strings.ToLower(r.OwnerName), strings.ToLower(r.Name)+".git") +} + +func RefixMergeBase(ctx context.Context, x *xorm.Engine) error { + type PullRequest struct { + ID int64 `xorm:"pk autoincr"` + Index int64 + HeadRepoID int64 `xorm:"INDEX"` + BaseRepoID int64 `xorm:"INDEX"` + HeadBranch string + BaseBranch string + MergeBase string `xorm:"VARCHAR(40)"` HasMerged bool `xorm:"INDEX"` MergedCommitID string `xorm:"VARCHAR(40)"` } @@ -66,7 +74,7 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error { start += 50 for _, pr := range prs { - baseRepo := &Repository{ID: pr.BaseRepoID} + baseRepo := &Repository2{ID: pr.BaseRepoID} has, err := x.Table("repository").Get(baseRepo) if err != nil { return fmt.Errorf("Unable to get base repo %d %w", pr.BaseRepoID, err) @@ -75,12 +83,10 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error { log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID) continue } - userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName)) - repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git") gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index) - parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx) + parentsString, err := gitrepo.RunCmdString(ctx, baseRepo, gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID)) if err != nil { log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue @@ -94,8 +100,7 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error { refs := append([]string{}, parents[1:]...) refs = append(refs, gitRefName) cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...) - - pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx) + pr.MergeBase, err = gitrepo.RunCmdString(ctx, baseRepo, cmd) if err != nil { log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue diff --git a/models/migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go index 593d3f9c70ca3..b560ad5d05e68 100644 --- a/models/migrations/v1_14/v156.go +++ b/models/migrations/v1_14/v156.go @@ -6,23 +6,24 @@ package v1_14 import ( "context" "fmt" - "path/filepath" "strings" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" "xorm.io/xorm" ) -// Copy paste from models/repo.go because we cannot import models package -func repoPath(userName, repoName string) string { - return filepath.Join(userPath(userName), strings.ToLower(repoName)+".git") +type Repository struct { + ID int64 + OwnerID int64 + OwnerName string + Name string } -func userPath(userName string) string { - return filepath.Join(setting.RepoRootPath, strings.ToLower(userName)) +func (r *Repository) RelativePath() string { + return fmt.Sprintf("%s/%s.git", strings.ToLower(r.OwnerName), strings.ToLower(r.Name)) } func FixPublisherIDforTagReleases(ctx context.Context, x *xorm.Engine) error { @@ -34,13 +35,6 @@ func FixPublisherIDforTagReleases(ctx context.Context, x *xorm.Engine) error { PublisherID int64 } - type Repository struct { - ID int64 - OwnerID int64 - OwnerName string - Name string - } - type User struct { ID int64 Name string @@ -109,7 +103,7 @@ func FixPublisherIDforTagReleases(ctx context.Context, x *xorm.Engine) error { return err } } - gitRepo, err = git.OpenRepository(ctx, repoPath(repo.OwnerName, repo.Name)) + gitRepo, err = gitrepo.OpenRepository(ctx, repo) if err != nil { log.Error("Error whilst opening git repo for [%d]%s/%s. Error: %v", repo.ID, repo.OwnerName, repo.Name, err) return err diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go index be24b31902ee4..7699011b6e966 100644 --- a/models/migrations/v1_21/v276.go +++ b/models/migrations/v1_21/v276.go @@ -161,7 +161,7 @@ func migratePushMirrors(x *xorm.Engine) error { func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) { ctx := context.Background() relativePath := repo_model.RelativePath(ownerName, repoName) - if exist, _ := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(relativePath)); !exist { + if exist, _ := gitrepo.IsRepositoryExist(repo_model.StorageRepo(relativePath)); !exist { return "", nil } diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 4611a079ec32c..e5222e30d5ba5 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting/config" @@ -137,8 +138,8 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) { if err = storage.Init(); err != nil { fatalTestError("storage.Init: %v\n", err) } - if err = SyncDirs(filepath.Join(giteaRoot, "tests", "gitea-repositories-meta"), setting.RepoRootPath); err != nil { - fatalTestError("util.SyncDirs: %v\n", err) + if err = gitrepo.SyncLocalToRepoStore(filepath.Join(giteaRoot, "tests", "gitea-repositories-meta")); err != nil { + fatalTestError("gitrepo.SyncLocalToRepoStore: %v\n", err) } if err = git.InitFull(); err != nil { @@ -200,6 +201,6 @@ func PrepareTestDatabase() error { func PrepareTestEnv(t testing.TB) { assert.NoError(t, PrepareTestDatabase()) metaPath := filepath.Join(giteaRoot, "tests", "gitea-repositories-meta") - assert.NoError(t, SyncDirs(metaPath, setting.RepoRootPath)) + assert.NoError(t, gitrepo.SyncLocalToRepoStore(metaPath)) test.SetupGiteaRoot() // Makes sure GITEA_ROOT is set } diff --git a/models/user/user.go b/models/user/user.go index 1797d3eefc2b5..ffa362f3a8f0f 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -11,7 +11,6 @@ import ( "mime" "net/mail" "net/url" - "path/filepath" "regexp" "strings" "sync" @@ -983,11 +982,6 @@ func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, er Find(&users) } -// UserPath returns the path absolute path of user repositories. -func UserPath(userName string) string { //revive:disable-line:exported - return filepath.Join(setting.RepoRootPath, filepath.Clean(strings.ToLower(userName))) -} - // GetUserByID returns the user object by given ID if exists. func GetUserByID(ctx context.Context, id int64) (*User, error) { u := new(User) diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go index 986264fd9360a..b3b164fd4549f 100644 --- a/modules/git/repo_base_gogit.go +++ b/modules/git/repo_base_gogit.go @@ -64,7 +64,7 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) { // so use "/" for AlternatesFS, I guess it is the same behavior as current nogogit (no limitation or check for the "objects/info/alternates" paths), trust the "clone" command executed by the server. var altFs billy.Filesystem if setting.IsWindows { - altFs = osfs.New(filepath.VolumeName(setting.RepoRootPath) + "\\") // TODO: does it really work for Windows? Need some time to check. + altFs = osfs.New(filepath.VolumeName(repoPath) + "\\") // TODO: does it really work for Windows? Need some time to check. } else { altFs = osfs.New("/") } diff --git a/modules/gitrepo/gitrepo.go b/modules/gitrepo/gitrepo.go index 535d72ed98afd..600c34fd006e4 100644 --- a/modules/gitrepo/gitrepo.go +++ b/modules/gitrepo/gitrepo.go @@ -68,18 +68,18 @@ func RepositoryFromRequestContextOrOpen(ctx reqctx.RequestContext, repo Reposito } // IsRepositoryExist returns true if the repository directory exists in the disk -func IsRepositoryExist(ctx context.Context, repo Repository) (bool, error) { +func IsRepositoryExist(repo Repository) (bool, error) { return util.IsExist(repoPath(repo)) } // DeleteRepository deletes the repository directory from the disk, it will return // nil if the repository does not exist. -func DeleteRepository(ctx context.Context, repo Repository) error { +func DeleteRepository(repo Repository) error { return util.RemoveAll(repoPath(repo)) } // RenameRepository renames a repository's name on disk -func RenameRepository(ctx context.Context, repo, newRepo Repository) error { +func RenameRepository(repo, newRepo Repository) error { dstDir := repoPath(newRepo) if err := os.MkdirAll(filepath.Dir(dstDir), os.ModePerm); err != nil { return fmt.Errorf("Failed to create dir %s: %w", filepath.Dir(dstDir), err) @@ -104,25 +104,35 @@ func GetRepoFS(repo Repository) fs.FS { return os.DirFS(repoPath(repo)) } -func IsRepoFileExist(ctx context.Context, repo Repository, relativeFilePath string) (bool, error) { +func IsRepoFileExist(repo Repository, relativeFilePath string) (bool, error) { absoluteFilePath := filepath.Join(repoPath(repo), relativeFilePath) return util.IsExist(absoluteFilePath) } -func IsRepoDirExist(ctx context.Context, repo Repository, relativeDirPath string) (bool, error) { +func IsRepoDirExist(repo Repository, relativeDirPath string) (bool, error) { absoluteDirPath := filepath.Join(repoPath(repo), relativeDirPath) return util.IsDir(absoluteDirPath) } -func RemoveRepoFileOrDir(ctx context.Context, repo Repository, relativeFileOrDirPath string) error { +func RemoveRepoFileOrDir(repo Repository, relativeFileOrDirPath string) error { absoluteFilePath := filepath.Join(repoPath(repo), relativeFileOrDirPath) - return util.Remove(absoluteFilePath) + return util.RemoveAll(absoluteFilePath) } -func CreateRepoFile(ctx context.Context, repo Repository, relativeFilePath string) (io.WriteCloser, error) { +func CreateRepoFile(repo Repository, relativeFilePath string) (io.WriteCloser, error) { absoluteFilePath := filepath.Join(repoPath(repo), relativeFilePath) if err := os.MkdirAll(filepath.Dir(absoluteFilePath), os.ModePerm); err != nil { return nil, err } return os.Create(absoluteFilePath) } + +func CreateRepositoryDir(repo Repository) error { + return os.MkdirAll(repoPath(repo), os.ModePerm) +} + +func CopyRepository(srcRepo, dstRepo Repository) error { + srcPath := repoPath(srcRepo) + dstPath := repoPath(dstRepo) + return util.SyncDirs(srcPath, dstPath) +} diff --git a/modules/gitrepo/store.go b/modules/gitrepo/store.go new file mode 100644 index 0000000000000..ab6c284709e3b --- /dev/null +++ b/modules/gitrepo/store.go @@ -0,0 +1,55 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "io/fs" + "os" + "path/filepath" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" +) + +var ErrRepoStoreConfig = util.NewInvalidArgumentErrorf("invalid repository store configuration") + +func IsRepoStoreConfigured() bool { + return setting.RepoRootPath != "" +} + +func RepoStoreStat() error { + if setting.RepoRootPath == "" { + return ErrRepoStoreConfig + } + _, err := os.Stat(setting.RepoRootPath) + return err +} + +func SyncLocalToRepoStore(localDir string) error { + return util.SyncDirs(localDir, setting.RepoRootPath) +} + +func RemoveRepoStore() error { + return util.RemoveAll(setting.RepoRootPath) +} + +func RemoveRepoStoreDir(dirName string) error { + return util.RemoveAll(filepath.Join(setting.RepoRootPath, dirName)) +} + +func RenameRepoStoreDir(oldDirName, newDirName string) error { + oldPath := filepath.Join(setting.RepoRootPath, oldDirName) + newPath := filepath.Join(setting.RepoRootPath, newDirName) + return util.Rename(oldPath, newPath) +} + +func WalkRepoStoreDirs(relativeDir string, fn fs.WalkDirFunc) error { + return filepath.WalkDir(filepath.Join(setting.RepoRootPath, relativeDir), func(path string, d os.DirEntry, err error) error { + p, err1 := filepath.Rel(setting.RepoRootPath, path) + if err1 != nil { + return err1 + } + return fn(p, d, err) + }) +} diff --git a/models/unittest/fscopy.go b/modules/util/fscopy.go similarity index 83% rename from models/unittest/fscopy.go rename to modules/util/fscopy.go index 98b01815bd751..ebadf39e3ef8e 100644 --- a/models/unittest/fscopy.go +++ b/modules/util/fscopy.go @@ -1,14 +1,12 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package unittest +package util import ( "os" "path/filepath" "strings" - - "code.gitea.io/gitea/modules/util" ) // SyncFile synchronizes the two files. This is skipped if both files @@ -17,7 +15,7 @@ func SyncFile(srcPath, destPath string) error { dest, err := os.Stat(destPath) if err != nil { if os.IsNotExist(err) { - return util.CopyFile(srcPath, destPath) + return CopyFile(srcPath, destPath) } return err } @@ -33,7 +31,7 @@ func SyncFile(srcPath, destPath string) error { return nil } - return util.CopyFile(srcPath, destPath) + return CopyFile(srcPath, destPath) } // SyncDirs synchronizes files recursively from source to target directory. @@ -49,7 +47,7 @@ func SyncDirs(srcPath, destPath string) error { const keepFile = ".keep" // find and delete all untracked files - destFiles, err := util.ListDirRecursively(destPath, &util.ListDirOptions{IncludeDir: true}) + destFiles, err := ListDirRecursively(destPath, &ListDirOptions{IncludeDir: true}) if err != nil { return err } @@ -72,13 +70,13 @@ func SyncDirs(srcPath, destPath string) error { } // sync src files to dest - srcFiles, err := util.ListDirRecursively(srcPath, &util.ListDirOptions{IncludeDir: true}) + srcFiles, err := ListDirRecursively(srcPath, &ListDirOptions{IncludeDir: true}) if err != nil { return err } for _, srcFile := range srcFiles { destFilePath := filepath.Join(destPath, srcFile) - // util.ListDirRecursively appends a slash to the directory name + // ListDirRecursively appends a slash to the directory name if strings.HasSuffix(srcFile, "/") { err = os.MkdirAll(destFilePath, os.ModePerm) } else if filepath.Base(destFilePath) != keepFile { diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go index 92711409f00fb..c733e835b492f 100644 --- a/routers/api/v1/admin/adopt.go +++ b/routers/api/v1/admin/adopt.go @@ -99,7 +99,7 @@ func AdoptRepository(ctx *context.APIContext) { ctx.APIErrorInternal(err) return } - exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) if err != nil { ctx.APIErrorInternal(err) return @@ -161,7 +161,7 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) { ctx.APIErrorInternal(err) return } - exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go index 424219815c632..33a6eb512ad57 100644 --- a/routers/web/admin/repos.go +++ b/routers/web/admin/repos.go @@ -134,7 +134,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) { ctx.ServerError("IsRepositoryExist", err) return } - exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) if err != nil { ctx.ServerError("IsDir", err) return diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go index abf9d8c6db90b..d94f6e006bdff 100644 --- a/routers/web/user/setting/adopt.go +++ b/routers/web/user/setting/adopt.go @@ -32,7 +32,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) { return } - exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, dir))) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, dir))) if err != nil { ctx.ServerError("IsDir", err) return diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 35303221fe463..fa07c6aa3aab2 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" @@ -256,15 +257,15 @@ func Repos(ctx *context.Context) { repoNames := make([]string, 0, setting.UI.Admin.UserPagingNum) repos := map[string]*repo_model.Repository{} // We're going to iterate by pagesize. - root := user_model.UserPath(ctxUser.Name) - if err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + dir := strings.ToLower(ctxUser.Name) + if err := gitrepo.WalkRepoStoreDirs(dir, func(relativePath string, d os.DirEntry, err error) error { if err != nil { if os.IsNotExist(err) { return nil } return err } - if !d.IsDir() || path == root { + if !d.IsDir() || relativePath == dir { return nil } name := d.Name() diff --git a/services/doctor/checkOldArchives.go b/services/doctor/checkOldArchives.go index fa1a6ccb1d1ab..11197535f49f9 100644 --- a/services/doctor/checkOldArchives.go +++ b/services/doctor/checkOldArchives.go @@ -19,14 +19,14 @@ func checkOldArchives(ctx context.Context, logger log.Logger, autofix bool) erro return nil } - isDir, err := gitrepo.IsRepoDirExist(ctx, repo, "archives") + isDir, err := gitrepo.IsRepoDirExist(repo, "archives") if err != nil { log.Warn("check if %s is directory failed: %v", repo.FullName(), err) } if isDir { numRepos++ if autofix { - err := gitrepo.RemoveRepoFileOrDir(ctx, repo, "archives") + err := gitrepo.RemoveRepoFileOrDir(repo, "archives") if err == nil { numReposUpdated++ } else { diff --git a/services/doctor/misc.go b/services/doctor/misc.go index 8765cfa0250f4..d3e6dc671022c 100644 --- a/services/doctor/misc.go +++ b/services/doctor/misc.go @@ -140,7 +140,7 @@ func checkDaemonExport(ctx context.Context, logger log.Logger, autofix bool) err // Create/Remove git-daemon-export-ok for git-daemon... daemonExportFile := `git-daemon-export-ok` - isExist, err := gitrepo.IsRepoFileExist(ctx, repo, daemonExportFile) + isExist, err := gitrepo.IsRepoFileExist(repo, daemonExportFile) if err != nil { log.Error("Unable to check if %s:%s exists. Error: %v", repo.FullName(), daemonExportFile, err) return err @@ -151,11 +151,11 @@ func checkDaemonExport(ctx context.Context, logger log.Logger, autofix bool) err numNeedUpdate++ if autofix { if !isPublic && isExist { - if err = gitrepo.RemoveRepoFileOrDir(ctx, repo, daemonExportFile); err != nil { + if err = gitrepo.RemoveRepoFileOrDir(repo, daemonExportFile); err != nil { log.Error("Failed to remove %s:%s: %v", repo.FullName(), daemonExportFile, err) } } else if isPublic && !isExist { - if f, err := gitrepo.CreateRepoFile(ctx, repo, daemonExportFile); err != nil { + if f, err := gitrepo.CreateRepoFile(repo, daemonExportFile); err != nil { log.Error("Failed to create %s:%s: %v", repo.FullName(), daemonExportFile, err) } else { f.Close() @@ -188,7 +188,7 @@ func checkCommitGraph(ctx context.Context, logger log.Logger, autofix bool) erro commitGraphExists := func() (bool, error) { // Check commit-graph exists commitGraphFile := `objects/info/commit-graph` - isExist, err := gitrepo.IsRepoFileExist(ctx, repo, commitGraphFile) + isExist, err := gitrepo.IsRepoFileExist(repo, commitGraphFile) if err != nil { logger.Error("Unable to check if %s exists. Error: %v", commitGraphFile, err) return false, err @@ -196,7 +196,7 @@ func checkCommitGraph(ctx context.Context, logger log.Logger, autofix bool) erro if !isExist { commitGraphsDir := `objects/info/commit-graphs` - isExist, err = gitrepo.IsRepoDirExist(ctx, repo, commitGraphsDir) + isExist, err = gitrepo.IsRepoDirExist(repo, commitGraphsDir) if err != nil { logger.Error("Unable to check if %s exists. Error: %v", commitGraphsDir, err) return false, err diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index f32c095d76a9f..fe13d8450c9a4 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -587,7 +587,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *ba } defer ret.Close() - f, err := gitrepo.CreateRepoFile(ctx, g.repo, fmt.Sprintf("pulls/%d.patch", pr.Number)) + f, err := gitrepo.CreateRepoFile(g.repo, fmt.Sprintf("pulls/%d.patch", pr.Number)) if err != nil { return err } diff --git a/services/org/org.go b/services/org/org.go index 8da77c691c169..5ea261d7416cc 100644 --- a/services/org/org.go +++ b/services/org/org.go @@ -6,6 +6,7 @@ package org import ( "context" "fmt" + "strings" actions_model "code.gitea.io/gitea/models/actions" activities_model "code.gitea.io/gitea/models/activities" @@ -16,10 +17,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" secret_model "code.gitea.io/gitea/models/secret" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" repo_service "code.gitea.io/gitea/services/repository" ) @@ -86,9 +87,8 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge // FIXME: system notice // Note: There are something just cannot be roll back, // so just keep error logs of those operations. - path := user_model.UserPath(org.Name) - - if err := util.RemoveAll(path); err != nil { + path := strings.ToLower(org.Name) + if err := gitrepo.RemoveRepoStoreDir(path); err != nil { return fmt.Errorf("failed to RemoveAll %s: %w", path, err) } diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 18d70d1bee360..8adda59a643cb 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -109,7 +109,7 @@ func AdoptRepository(ctx context.Context, doer, owner *user_model.User, opts Cre } func adoptRepository(ctx context.Context, repo *repo_model.Repository, defaultBranch string) (err error) { - isExist, err := gitrepo.IsRepositoryExist(ctx, repo) + isExist, err := gitrepo.IsRepositoryExist(repo) if err != nil { log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err) return err @@ -214,7 +214,7 @@ func DeleteUnadoptedRepository(ctx context.Context, doer, u *user_model.User, re } relativePath := repo_model.RelativePath(u.Name, repoName) - exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(relativePath)) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(relativePath)) if err != nil { log.Error("Unable to check if %s exists. Error: %v", relativePath, err) return err @@ -235,7 +235,7 @@ func DeleteUnadoptedRepository(ctx context.Context, doer, u *user_model.User, re } } - return gitrepo.DeleteRepository(ctx, repo_model.StorageRepo(relativePath)) + return gitrepo.DeleteRepository(repo_model.StorageRepo(relativePath)) } type unadoptedRepositories struct { @@ -322,18 +322,17 @@ func ListUnadoptedRepositories(ctx context.Context, query string, opts *db.ListO var userName string // We're going to iterate by pagesize. - root := filepath.Clean(setting.RepoRootPath) - if err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + if err := gitrepo.WalkRepoStoreDirs("", func(path string, d os.DirEntry, err error) error { if err != nil { return err } - if !d.IsDir() || path == root { + if !d.IsDir() || path == "" { return nil } name := d.Name() - if !strings.ContainsRune(path[len(root)+1:], filepath.Separator) { + if !strings.ContainsRune(path, filepath.Separator) { // Got a new user if err = checkUnadoptedRepositories(ctx, userName, repoNamesToCheck, unadopted); err != nil { return err diff --git a/services/repository/adopt_test.go b/services/repository/adopt_test.go index 46f2f484175bf..fdc86fc7d7203 100644 --- a/services/repository/adopt_test.go +++ b/services/repository/adopt_test.go @@ -4,17 +4,14 @@ package repository import ( - "os" "path" - "path/filepath" "testing" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/gitrepo" "github.com/stretchr/testify/assert" ) @@ -70,22 +67,25 @@ func TestCheckUnadoptedRepositories(t *testing.T) { func TestListUnadoptedRepositories_ListOptions(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) username := "user2" - unadoptedList := []string{path.Join(username, "unadopted1"), path.Join(username, "unadopted2")} + unadoptedList := []gitrepo.Repository{ + repo_model.StorageRepo(repo_model.RelativePath(username, "unadopted1")), + repo_model.StorageRepo(repo_model.RelativePath(username, "unadopted2")), + } for _, unadopted := range unadoptedList { - _ = os.Mkdir(filepath.Join(setting.RepoRootPath, unadopted+".git"), 0o755) + _ = gitrepo.CreateRepositoryDir(unadopted) } opts := db.ListOptions{Page: 1, PageSize: 1} repoNames, count, err := ListUnadoptedRepositories(t.Context(), "", &opts) assert.NoError(t, err) assert.Equal(t, 2, count) - assert.Equal(t, unadoptedList[0], repoNames[0]) + assert.Equal(t, unadoptedList[0].RelativePath(), repoNames[0]+".git") opts = db.ListOptions{Page: 2, PageSize: 1} repoNames, count, err = ListUnadoptedRepositories(t.Context(), "", &opts) assert.NoError(t, err) assert.Equal(t, 2, count) - assert.Equal(t, unadoptedList[1], repoNames[0]) + assert.Equal(t, unadoptedList[1].RelativePath(), repoNames[0]+".git") } func TestAdoptRepository(t *testing.T) { @@ -94,8 +94,8 @@ func TestAdoptRepository(t *testing.T) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // a successful adopt - destDir := filepath.Join(setting.RepoRootPath, user2.Name, "test-adopt.git") - assert.NoError(t, unittest.SyncDirs(filepath.Join(setting.RepoRootPath, user2.Name, "repo1.git"), destDir)) + destRepo := repo_model.StorageRepo(repo_model.RelativePath(user2.Name, "test-adopt")) + assert.NoError(t, gitrepo.CopyRepository(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, "repo1")), destRepo)) adoptedRepo, err := AdoptRepository(t.Context(), user2, user2, CreateRepoOptions{Name: "test-adopt"}) assert.NoError(t, err) @@ -110,8 +110,12 @@ func TestAdoptRepository(t *testing.T) { // a failed adopt because some mock data // remove the hooks directory and create a file so that we cannot create the hooks successfully - _ = os.RemoveAll(filepath.Join(destDir, "hooks", "update.d")) - assert.NoError(t, os.WriteFile(filepath.Join(destDir, "hooks", "update.d"), []byte("tests"), os.ModePerm)) + _ = gitrepo.RemoveRepoFileOrDir(destRepo, "hooks/update.d") + f, err := gitrepo.CreateRepoFile(destRepo, "hooks/update.d") + assert.NoError(t, err) + _, err = f.Write([]byte("tests")) + assert.NoError(t, err) + assert.NoError(t, f.Close()) adoptedRepo, err = AdoptRepository(t.Context(), user2, user2, CreateRepoOptions{Name: "test-adopt"}) assert.Error(t, err) @@ -119,7 +123,7 @@ func TestAdoptRepository(t *testing.T) { unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: "test-adopt"}) - exist, err := util.IsExist(repo_model.RepoPath(user2.Name, "test-adopt")) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, "test-adopt"))) assert.NoError(t, err) assert.True(t, exist) // the repository should be still in the disk } diff --git a/services/repository/check.go b/services/repository/check.go index 57d627c63d2ea..2ac0ad64afda4 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -122,7 +122,7 @@ func gatherMissingRepoRecords(ctx context.Context) (repo_model.RepositoryList, e return db.ErrCancelledf("during gathering missing repo records before checking %s", repo.FullName()) default: } - exist, err := gitrepo.IsRepositoryExist(ctx, repo) + exist, err := gitrepo.IsRepositoryExist(repo) if err != nil { return fmt.Errorf("Unable to check dir for %s. %w", repo.FullName(), err) } diff --git a/services/repository/create.go b/services/repository/create.go index 7439fc8f088b8..b618ec21c933f 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -277,7 +277,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User, // 2 - check whether the repository with the same storage exists var isExist bool - isExist, err = gitrepo.IsRepositoryExist(ctx, repo) + isExist, err = gitrepo.IsRepositoryExist(repo) if err != nil { log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err) return nil, err diff --git a/services/repository/create_test.go b/services/repository/create_test.go index b8c2ea59ddb92..e5ff03069dc83 100644 --- a/services/repository/create_test.go +++ b/services/repository/create_test.go @@ -4,13 +4,12 @@ package repository import ( - "os" "testing" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/gitrepo" "github.com/stretchr/testify/assert" ) @@ -27,7 +26,7 @@ func TestCreateRepositoryDirectly(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, createdRepo) - exist, err := util.IsExist(repo_model.RepoPath(user2.Name, createdRepo.Name)) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, createdRepo.Name))) assert.NoError(t, err) assert.True(t, exist) @@ -38,7 +37,7 @@ func TestCreateRepositoryDirectly(t *testing.T) { // a failed creating because some mock data // create the repository directory so that the creation will fail after database record created. - assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, createdRepo.Name), os.ModePerm)) + assert.NoError(t, gitrepo.CreateRepositoryDir(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, createdRepo.Name)))) createdRepo2, err := CreateRepositoryDirectly(t.Context(), user2, user2, CreateRepoOptions{ Name: "created-repo", @@ -49,7 +48,7 @@ func TestCreateRepositoryDirectly(t *testing.T) { // assert the cleanup is successful unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: createdRepo.Name}) - exist, err = util.IsExist(repo_model.RepoPath(user2.Name, createdRepo.Name)) + exist, err = gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, createdRepo.Name))) assert.NoError(t, err) assert.False(t, exist) } diff --git a/services/repository/delete.go b/services/repository/delete.go index 040280c8a8d0e..bfee809c3e581 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -308,7 +308,7 @@ func DeleteRepositoryDirectly(ctx context.Context, repoID int64, ignoreOrgTeams // we delete the file but the database rollback, the repository will be broken. // Remove repository files. - if err := gitrepo.DeleteRepository(ctx, repo); err != nil { + if err := gitrepo.DeleteRepository(repo); err != nil { desc := fmt.Sprintf("Delete repository files [%s]: %v", repo.FullName(), err) if err = system_model.CreateNotice(graceful.GetManager().ShutdownContext(), system_model.NoticeRepository, desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) @@ -316,7 +316,7 @@ func DeleteRepositoryDirectly(ctx context.Context, repoID int64, ignoreOrgTeams } // Remove wiki files if it exists. - if err := gitrepo.DeleteRepository(ctx, repo.WikiStorageRepo()); err != nil { + if err := gitrepo.DeleteRepository(repo.WikiStorageRepo()); err != nil { desc := fmt.Sprintf("Delete wiki repository files [%s]: %v", repo.FullName(), err) // Note we use the db.DefaultContext here rather than passing in a context as the context may be cancelled if err = system_model.CreateNotice(graceful.GetManager().ShutdownContext(), system_model.NoticeRepository, desc); err != nil { diff --git a/services/repository/fork.go b/services/repository/fork.go index f92af656059da..f07f863a13493 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -130,7 +130,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork // 2 - check whether the repository with the same storage exists var isExist bool - isExist, err = gitrepo.IsRepositoryExist(ctx, repo) + isExist, err = gitrepo.IsRepositoryExist(repo) if err != nil { log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err) return nil, err diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go index 680a7f3207e5c..1a0efcd239ce6 100644 --- a/services/repository/fork_test.go +++ b/services/repository/fork_test.go @@ -4,15 +4,14 @@ package repository import ( - "os" "testing" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) @@ -63,7 +62,7 @@ func TestForkRepositoryCleanup(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, fork) - exist, err := util.IsExist(repo_model.RepoPath(user2.Name, "test")) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, "test"))) assert.NoError(t, err) assert.True(t, exist) @@ -72,8 +71,7 @@ func TestForkRepositoryCleanup(t *testing.T) { // a failed creating because some mock data // create the repository directory so that the creation will fail after database record created. - assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, "test"), os.ModePerm)) - + assert.NoError(t, gitrepo.CreateRepositoryDir(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, "test")))) fork2, err := ForkRepository(t.Context(), user2, user2, ForkRepoOptions{ BaseRepo: repo10, Name: "test", @@ -84,7 +82,7 @@ func TestForkRepositoryCleanup(t *testing.T) { // assert the cleanup is successful unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: "test"}) - exist, err = util.IsExist(repo_model.RepoPath(user2.Name, "test")) + exist, err = gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, "test"))) assert.NoError(t, err) assert.False(t, exist) } diff --git a/services/repository/migrate.go b/services/repository/migrate.go index 8f515326ad60d..19ddd0de1e651 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -35,12 +35,12 @@ func cloneWiki(ctx context.Context, repo *repo_model.Repository, opts migration. storageRepo := repo.WikiStorageRepo() - if err := gitrepo.DeleteRepository(ctx, storageRepo); err != nil { + if err := gitrepo.DeleteRepository(storageRepo); err != nil { return "", fmt.Errorf("failed to remove existing wiki dir %q, err: %w", storageRepo.RelativePath(), err) } cleanIncompleteWikiPath := func() { - if err := gitrepo.DeleteRepository(ctx, storageRepo); err != nil { + if err := gitrepo.DeleteRepository(storageRepo); err != nil { log.Error("Failed to remove incomplete wiki dir %q, err: %v", storageRepo.RelativePath(), err) } } @@ -86,7 +86,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second - if err := gitrepo.DeleteRepository(ctx, repo); err != nil { + if err := gitrepo.DeleteRepository(repo); err != nil { return repo, fmt.Errorf("failed to remove existing repo dir %q, err: %w", repo.FullName(), err) } diff --git a/services/repository/repository.go b/services/repository/repository.go index 318ab423e5e04..c707fa966b2fe 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -250,7 +250,7 @@ func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error // Create/Remove git-daemon-export-ok for git-daemon... daemonExportFile := `git-daemon-export-ok` - isExist, err := gitrepo.IsRepoFileExist(ctx, repo, daemonExportFile) + isExist, err := gitrepo.IsRepoFileExist(repo, daemonExportFile) if err != nil { log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err) return err @@ -258,11 +258,11 @@ func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error isPublic := !repo.IsPrivate && repo.Owner.Visibility == structs.VisibleTypePublic if !isPublic && isExist { - if err = gitrepo.RemoveRepoFileOrDir(ctx, repo, daemonExportFile); err != nil { + if err = gitrepo.RemoveRepoFileOrDir(repo, daemonExportFile); err != nil { log.Error("Failed to remove %s: %v", daemonExportFile, err) } } else if isPublic && !isExist { - if f, err := gitrepo.CreateRepoFile(ctx, repo, daemonExportFile); err != nil { + if f, err := gitrepo.CreateRepoFile(repo, daemonExportFile); err != nil { log.Error("Failed to create %s: %v", daemonExportFile, err) } else { f.Close() @@ -336,7 +336,7 @@ func updateRepository(ctx context.Context, repo *repo_model.Repository, visibili } func HasWiki(ctx context.Context, repo *repo_model.Repository) bool { - hasWiki, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo()) + hasWiki, err := gitrepo.IsRepositoryExist(repo.WikiStorageRepo()) if err != nil { log.Error("gitrepo.IsRepositoryExist: %v", err) } @@ -360,7 +360,7 @@ func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, na return repo_model.ErrRepoAlreadyExist{Uname: owner.Name, Name: name} } repo := repo_model.StorageRepo(repo_model.RelativePath(owner.Name, name)) - isExist, err := gitrepo.IsRepositoryExist(ctx, repo) + isExist, err := gitrepo.IsRepositoryExist(repo) if err != nil { log.Error("Unable to check if %s exists. Error: %v", repo.RelativePath(), err) return err diff --git a/services/repository/template.go b/services/repository/template.go index 7444bf7b60637..7041604b9e156 100644 --- a/services/repository/template.go +++ b/services/repository/template.go @@ -106,7 +106,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ }() // 2 - check whether the repository with the same storage exists - isExist, err := gitrepo.IsRepositoryExist(ctx, generateRepo) + isExist, err := gitrepo.IsRepositoryExist(generateRepo) if err != nil { log.Error("Unable to check if %s exists. Error: %v", generateRepo.FullName(), err) return nil, err diff --git a/services/repository/transfer.go b/services/repository/transfer.go index a601ee6f1687b..a426cc8d8551f 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -96,7 +96,7 @@ func isRepositoryModelOrDirExist(ctx context.Context, u *user_model.User, repoNa return false, err } repo := repo_model.StorageRepo(repo_model.RelativePath(u.Name, repoName)) - isExist, err := gitrepo.IsRepositoryExist(ctx, repo) + isExist, err := gitrepo.IsRepositoryExist(repo) return has || isExist, err } @@ -118,7 +118,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName if repoRenamed { oldRelativePath, newRelativePath := repo_model.RelativePath(newOwnerName, repo.Name), repo_model.RelativePath(oldOwnerName, repo.Name) - if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { + if err := gitrepo.RenameRepository(repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { log.Critical("Unable to move repository %s/%s directory from %s back to correct place %s: %v", oldOwnerName, repo.Name, oldRelativePath, newRelativePath, err) } @@ -126,7 +126,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName if wikiRenamed { oldRelativePath, newRelativePath := repo_model.RelativeWikiPath(newOwnerName, repo.Name), repo_model.RelativeWikiPath(oldOwnerName, repo.Name) - if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { + if err := gitrepo.RenameRepository(repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { log.Critical("Unable to move wiki for repository %s/%s directory from %s back to correct place %s: %v", oldOwnerName, repo.Name, oldRelativePath, newRelativePath, err) } @@ -291,18 +291,18 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName // Rename remote repository to new path and delete local copy. oldRelativePath, newRelativePath := repo_model.RelativePath(oldOwner.Name, repo.Name), repo_model.RelativePath(newOwner.Name, repo.Name) - if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { + if err := gitrepo.RenameRepository(repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { return fmt.Errorf("rename repository directory: %w", err) } repoRenamed = true // Rename remote wiki repository to new path and delete local copy. wikiStorageRepo := repo_model.StorageRepo(repo_model.RelativeWikiPath(oldOwner.Name, repo.Name)) - if isExist, err := gitrepo.IsRepositoryExist(ctx, wikiStorageRepo); err != nil { + if isExist, err := gitrepo.IsRepositoryExist(wikiStorageRepo); err != nil { log.Error("Unable to check if %s exists. Error: %v", wikiStorageRepo.RelativePath(), err) return err } else if isExist { - if err := gitrepo.RenameRepository(ctx, wikiStorageRepo, repo_model.StorageRepo(repo_model.RelativeWikiPath(newOwner.Name, repo.Name))); err != nil { + if err := gitrepo.RenameRepository(wikiStorageRepo, repo_model.StorageRepo(repo_model.RelativeWikiPath(newOwner.Name, repo.Name))); err != nil { return fmt.Errorf("rename repository wiki: %w", err) } wikiRenamed = true @@ -361,13 +361,13 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR } } - if err = gitrepo.RenameRepository(ctx, repo, + if err = gitrepo.RenameRepository(repo, repo_model.StorageRepo(repo_model.RelativePath(repo.OwnerName, newRepoName))); err != nil { return fmt.Errorf("rename repository directory: %w", err) } if HasWiki(ctx, repo) { - if err = gitrepo.RenameRepository(ctx, repo.WikiStorageRepo(), repo_model.StorageRepo( + if err = gitrepo.RenameRepository(repo.WikiStorageRepo(), repo_model.StorageRepo( repo_model.RelativeWikiPath(repo.OwnerName, newRepoName))); err != nil { return fmt.Errorf("rename repository wiki: %w", err) } diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go index 8d73fef7f4bf1..87e282f8dae1f 100644 --- a/services/repository/transfer_test.go +++ b/services/repository/transfer_test.go @@ -13,9 +13,9 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/feed" notify_service "code.gitea.io/gitea/services/notify" @@ -47,10 +47,10 @@ func TestTransferOwnership(t *testing.T) { assert.EqualValues(t, 1, transferredRepo.OwnerID) // repo_transfer.yml id=1 unittest.AssertNotExistsBean(t, &repo_model.RepoTransfer{ID: 1}) - exist, err := util.IsExist(repo_model.RepoPath("org3", "repo3")) + exist, err := gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath("org3", "repo3"))) assert.NoError(t, err) assert.False(t, exist) - exist, err = util.IsExist(repo_model.RepoPath("user1", "repo3")) + exist, err = gitrepo.IsRepositoryExist(repo_model.StorageRepo(repo_model.RelativePath("user1", "repo3"))) assert.NoError(t, err) assert.True(t, exist) unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ diff --git a/services/user/user.go b/services/user/user.go index 9b8bcf83c0bc7..002df9bb747ed 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -17,11 +17,11 @@ import ( system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/eventsource" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/agit" asymkey_service "code.gitea.io/gitea/services/asymkey" org_service "code.gitea.io/gitea/services/org" @@ -98,7 +98,7 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string, doe } // Do not fail if directory does not exist - if err = util.Rename(user_model.UserPath(oldUserName), user_model.UserPath(newUserName)); err != nil && !os.IsNotExist(err) { + if err = gitrepo.RenameRepoStoreDir(strings.ToLower(oldUserName), strings.ToLower(newUserName)); err != nil && !os.IsNotExist(err) { u.Name = oldUserName u.LowerName = strings.ToLower(oldUserName) return fmt.Errorf("rename user directory: %w", err) @@ -107,7 +107,7 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string, doe if err = committer.Commit(); err != nil { u.Name = oldUserName u.LowerName = strings.ToLower(oldUserName) - if err2 := util.Rename(user_model.UserPath(newUserName), user_model.UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) { + if err2 := gitrepo.RenameRepoStoreDir(strings.ToLower(newUserName), strings.ToLower(oldUserName)); err2 != nil && !os.IsNotExist(err2) { log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2) return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2) } @@ -257,8 +257,8 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { } // Note: There are something just cannot be roll back, so just keep error logs of those operations. - path := user_model.UserPath(u.Name) - if err := util.RemoveAll(path); err != nil { + path := strings.ToLower(u.Name) + if err := gitrepo.RemoveRepoStoreDir(path); err != nil { err = fmt.Errorf("failed to RemoveAll %s: %w", path, err) _ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err)) } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index f4115038cbd2c..dd494ab0c717a 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -33,7 +33,7 @@ func getWikiWorkingLockKey(repoID int64) string { // it does nothing when repository already has wiki. func InitWiki(ctx context.Context, repo *repo_model.Repository) error { // don't use HasWiki because the error should not be ignored. - if exist, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo()); err != nil { + if exist, err := gitrepo.IsRepositoryExist(repo.WikiStorageRepo()); err != nil { return err } else if exist { return nil @@ -368,7 +368,7 @@ func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error { return err } - if err := gitrepo.DeleteRepository(ctx, repo.WikiStorageRepo()); err != nil { + if err := gitrepo.DeleteRepository(repo.WikiStorageRepo()); err != nil { desc := fmt.Sprintf("Delete wiki repository files [%s]: %v", repo.FullName(), err) // Note we use the db.DefaultContext here rather than passing in a context as the context may be cancelled if err = system_model.CreateNotice(graceful.GetManager().ShutdownContext(), system_model.NoticeRepository, desc); err != nil { diff --git a/tests/integration/migration-test/migration_test.go b/tests/integration/migration-test/migration_test.go index 2659c5c53dbad..07159ce12658d 100644 --- a/tests/integration/migration-test/migration_test.go +++ b/tests/integration/migration-test/migration_test.go @@ -23,6 +23,7 @@ import ( migrate_base "code.gitea.io/gitea/models/migrations/base" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" @@ -54,8 +55,8 @@ func initMigrationTest(t *testing.T) func() { unittest.InitSettingsForTesting() - assert.NotEmpty(t, setting.RepoRootPath) - assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) + assert.NotEqual(t, gitrepo.ErrRepoStoreConfig, gitrepo.RepoStoreStat()) + assert.NoError(t, gitrepo.SyncLocalToRepoStore(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"))) assert.NoError(t, git.InitFull()) setting.LoadDBSetting() setting.InitLoggersForTest() diff --git a/tests/integration/mirror_pull_test.go b/tests/integration/mirror_pull_test.go index 7902dc10cbffe..f67572e278c51 100644 --- a/tests/integration/mirror_pull_test.go +++ b/tests/integration/mirror_pull_test.go @@ -29,14 +29,13 @@ func TestMirrorPull(t *testing.T) { ctx := t.Context() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - repoPath := repo_model.RepoPath(user.Name, repo.Name) opts := migration.MigrateOptions{ RepoName: "test_mirror", Description: "Test mirror", Private: false, Mirror: true, - CloneAddr: repoPath, + CloneAddr: repo.RepoPath(), Wiki: true, Releases: true, } diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 53e68432ccde6..400a8e6fb8275 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -400,29 +400,24 @@ func TestCantMergeUnrelated(t *testing.T) { OwnerID: user1.ID, Name: "repo1", }) - path := repo_model.RepoPath(user1.Name, repo1.Name) - err := gitcmd.NewCommand("read-tree", "--empty").WithDir(path).Run(t.Context()) + err := gitrepo.RunCmd(t.Context(), repo1, gitcmd.NewCommand("read-tree", "--empty")) assert.NoError(t, err) stdin := strings.NewReader("Unrelated File") var stdout strings.Builder - err = gitcmd.NewCommand("hash-object", "-w", "--stdin"). - WithDir(path). + err = gitrepo.RunCmd(t.Context(), repo1, gitcmd.NewCommand("hash-object", "-w", "--stdin"). WithStdin(stdin). - WithStdout(&stdout). - Run(t.Context()) + WithStdout(&stdout)) assert.NoError(t, err) sha := strings.TrimSpace(stdout.String()) - _, _, err = gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo"). - AddDynamicArguments("100644", sha, "somewher-over-the-rainbow"). - WithDir(path). - RunStdString(t.Context()) + _, err = gitrepo.RunCmdString(t.Context(), repo1, gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo"). + AddDynamicArguments("100644", sha, "somewher-over-the-rainbow")) assert.NoError(t, err) - treeSha, _, err := gitcmd.NewCommand("write-tree").WithDir(path).RunStdString(t.Context()) + treeSha, err := gitrepo.RunCmdString(t.Context(), repo1, gitcmd.NewCommand("write-tree")) assert.NoError(t, err) treeSha = strings.TrimSpace(treeSha) @@ -442,19 +437,15 @@ func TestCantMergeUnrelated(t *testing.T) { _, _ = messageBytes.WriteString("\n") stdout.Reset() - err = gitcmd.NewCommand("commit-tree").AddDynamicArguments(treeSha). + err = gitrepo.RunCmd(t.Context(), repo1, gitcmd.NewCommand("commit-tree").AddDynamicArguments(treeSha). WithEnv(env). - WithDir(path). WithStdin(messageBytes). - WithStdout(&stdout). - Run(t.Context()) + WithStdout(&stdout)) assert.NoError(t, err) commitSha := strings.TrimSpace(stdout.String()) - _, _, err = gitcmd.NewCommand("branch", "unrelated"). - AddDynamicArguments(commitSha). - WithDir(path). - RunStdString(t.Context()) + _, err = gitrepo.RunCmdString(t.Context(), repo1, gitcmd.NewCommand("branch", "unrelated"). + AddDynamicArguments(commitSha)) assert.NoError(t, err) testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index e88b6b62249da..0ce216de994a4 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -6,7 +6,6 @@ package integration import ( "fmt" "net/http" - "os" "path" "strconv" "strings" @@ -17,9 +16,9 @@ import ( "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" @@ -531,7 +530,7 @@ func TestGenerateRepository(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, generatedRepo) - exist, err := util.IsExist(repo_model.RepoPath(user2.Name, generatedRepo.Name)) + exist, err := gitrepo.IsRepositoryExist(generatedRepo) assert.NoError(t, err) assert.True(t, exist) @@ -542,7 +541,7 @@ func TestGenerateRepository(t *testing.T) { // a failed creating because some mock data // create the repository directory so that the creation will fail after database record created. - assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, "generated-from-template-44"), os.ModePerm)) + assert.NoError(t, gitrepo.CreateRepositoryDir(repo_model.StorageRepo(repo_model.RelativePath(user2.Name, "generated-from-template-44")))) generatedRepo2, err := repo_service.GenerateRepository(t.Context(), user2, user2, repo44, repo_service.GenerateRepoOptions{ Name: "generated-from-template-44", @@ -554,7 +553,7 @@ func TestGenerateRepository(t *testing.T) { // assert the cleanup is successful unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: generatedRepo.Name}) - exist, err = util.IsExist(repo_model.RepoPath(user2.Name, generatedRepo.Name)) + exist, err = gitrepo.IsRepositoryExist(generatedRepo) assert.NoError(t, err) assert.False(t, exist) } diff --git a/tests/test_utils.go b/tests/test_utils.go index e63eb7584695d..5c42cc835cb57 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -14,6 +14,7 @@ import ( packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -180,10 +181,10 @@ func PrepareAttachmentsStorage(t testing.TB) { } func PrepareGitRepoDirectory(t testing.TB) { - if !assert.NotEmpty(t, setting.RepoRootPath) { + if !gitrepo.IsRepoStoreConfigured() { return } - assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) + assert.NoError(t, gitrepo.SyncLocalToRepoStore(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"))) } func PrepareArtifactsStorage(t testing.TB) {