From 1d187c8e182b316bc0906626c010f2516e54eaff Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 4 Dec 2025 21:13:37 -0800 Subject: [PATCH 1/2] Use gitrepo's clone and push when possible --- models/repo/repo.go | 10 --------- models/repo/update.go | 31 -------------------------- modules/gitrepo/push.go | 15 ++++++++++++- routers/web/repo/branch.go | 4 ++-- routers/web/repo/editor_util.go | 4 ++-- routers/web/repo/issue_comment.go | 4 ++-- routers/web/repo/migrate.go | 3 ++- routers/web/repo/setting/lfs.go | 3 ++- services/mirror/mirror_push.go | 2 +- services/pull/pull.go | 5 +---- services/repository/branch.go | 3 +-- services/repository/files/temp_repo.go | 4 ++-- services/repository/generate.go | 3 +-- services/repository/merge_upstream.go | 4 ++-- services/repository/migrate.go | 6 ++--- services/repository/repository.go | 28 +++++++++++++++++++++++ services/repository/transfer.go | 15 +++++++++++-- services/wiki/wiki.go | 8 ++----- 18 files changed, 77 insertions(+), 75 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index 819356dfad342..605a9e0f3fa34 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -869,16 +869,6 @@ func GetRepositoriesMapByIDs(ctx context.Context, ids []int64) (map[int64]*Repos return repos, db.GetEngine(ctx).In("id", ids).Find(&repos) } -// IsRepositoryModelOrDirExist returns true if the repository with given name under user has already existed. -func IsRepositoryModelOrDirExist(ctx context.Context, u *user_model.User, repoName string) (bool, error) { - has, err := IsRepositoryModelExist(ctx, u, repoName) - if err != nil { - return false, err - } - isDir, err := util.IsDir(RepoPath(u.Name, repoName)) - return has || isDir, err -} - func IsRepositoryModelExist(ctx context.Context, u *user_model.User, repoName string) (bool, error) { return db.GetEngine(ctx).Get(&Repository{ OwnerID: u.ID, diff --git a/models/repo/update.go b/models/repo/update.go index 3228ae11a4eb3..bf560cf69504e 100644 --- a/models/repo/update.go +++ b/models/repo/update.go @@ -9,8 +9,6 @@ import ( "time" "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" ) @@ -106,35 +104,6 @@ func (err ErrRepoFilesAlreadyExist) Unwrap() error { return util.ErrAlreadyExist } -// CheckCreateRepository check if doer could create a repository in new owner -func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, name string, overwriteOrAdopt bool) error { - if !doer.CanCreateRepoIn(owner) { - return ErrReachLimitOfRepo{owner.MaxRepoCreation} - } - - if err := IsUsableRepoName(name); err != nil { - return err - } - - has, err := IsRepositoryModelOrDirExist(ctx, owner, name) - if err != nil { - return fmt.Errorf("IsRepositoryExist: %w", err) - } else if has { - return ErrRepoAlreadyExist{owner.Name, name} - } - - repoPath := RepoPath(owner.Name, name) - isExist, err := util.IsExist(repoPath) - if err != nil { - log.Error("Unable to check if %s exists. Error: %v", repoPath, err) - return err - } - if !overwriteOrAdopt && isExist { - return ErrRepoFilesAlreadyExist{owner.Name, name} - } - return nil -} - // UpdateRepoSize updates the repository size, calculating it using getDirectorySize func UpdateRepoSize(ctx context.Context, repoID, gitSize, lfsSize int64) error { _, err := db.GetEngine(ctx).ID(repoID).Cols("size", "git_size", "lfs_size").NoAutoTime().Update(&Repository{ diff --git a/modules/gitrepo/push.go b/modules/gitrepo/push.go index 18808cac24548..920c317f7966d 100644 --- a/modules/gitrepo/push.go +++ b/modules/gitrepo/push.go @@ -9,6 +9,19 @@ import ( "code.gitea.io/gitea/modules/git" ) -func Push(ctx context.Context, repo Repository, opts git.PushOptions) error { +// PushToExternal pushes a managed repository to an external remote. +func PushToExternal(ctx context.Context, repo Repository, opts git.PushOptions) error { return git.Push(ctx, repoPath(repo), opts) } + +// Push pushes from one managed repository to another managed repository. +func Push(ctx context.Context, fromRepo, toRepo Repository, opts git.PushOptions) error { + opts.Remote = repoPath(toRepo) + return git.Push(ctx, repoPath(fromRepo), opts) +} + +// PushFromLocal pushes from a local path to a managed repository. +func PushFromLocal(ctx context.Context, fromLocalPath string, toRepo Repository, opts git.PushOptions) error { + opts.Remote = repoPath(toRepo) + return git.Push(ctx, fromLocalPath, opts) +} diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index f21f5682318a6..2b0ba9072d83c 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -15,6 +15,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" @@ -133,8 +134,7 @@ func RestoreBranchPost(ctx *context.Context) { return } - if err := git.Push(ctx, ctx.Repo.Repository.RepoPath(), git.PushOptions{ - Remote: ctx.Repo.Repository.RepoPath(), + if err := gitrepo.Push(ctx, ctx.Repo.Repository, ctx.Repo.Repository, git.PushOptions{ Branch: fmt.Sprintf("%s:%s%s", deletedBranch.CommitID, git.BranchPrefix, deletedBranch.Name), Env: repo_module.PushingEnvironment(ctx.Doer, ctx.Repo.Repository), }); err != nil { diff --git a/routers/web/repo/editor_util.go b/routers/web/repo/editor_util.go index f910f0bd40729..07bcb474f0a98 100644 --- a/routers/web/repo/editor_util.go +++ b/routers/web/repo/editor_util.go @@ -13,6 +13,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" @@ -102,8 +103,7 @@ func getUniqueRepositoryName(ctx context.Context, ownerID int64, name string) st } func editorPushBranchToForkedRepository(ctx context.Context, doer *user_model.User, baseRepo *repo_model.Repository, baseBranchName string, targetRepo *repo_model.Repository, targetBranchName string) error { - return git.Push(ctx, baseRepo.RepoPath(), git.PushOptions{ - Remote: targetRepo.RepoPath(), + return gitrepo.Push(ctx, baseRepo, targetRepo, git.PushOptions{ Branch: baseBranchName + ":" + targetBranchName, Env: repo_module.PushingEnvironment(doer, targetRepo), }) diff --git a/routers/web/repo/issue_comment.go b/routers/web/repo/issue_comment.go index edad756b6bd5c..35124c5c3ef8c 100644 --- a/routers/web/repo/issue_comment.go +++ b/routers/web/repo/issue_comment.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/models/renderhelper" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/htmlutil" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup/markdown" @@ -141,8 +142,7 @@ func NewComment(ctx *context.Context) { if prHeadCommitID != headBranchCommitID { // force push to base repo - err := git.Push(ctx, pull.HeadRepo.RepoPath(), git.PushOptions{ - Remote: pull.BaseRepo.RepoPath(), + err := gitrepo.Push(ctx, pull.HeadRepo, pull.BaseRepo, git.PushOptions{ Branch: pull.HeadBranch + ":" + prHeadRef, Force: true, Env: repo_module.InternalPushingEnvironment(pull.Issue.Poster, pull.BaseRepo), diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go index ea15e90e5c297..8f4adb2ad220f 100644 --- a/routers/web/repo/migrate.go +++ b/routers/web/repo/migrate.go @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/migrations" + repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/services/task" ) @@ -237,7 +238,7 @@ func MigratePost(ctx *context.Context) { opts.AWSSecretAccessKey = form.AWSSecretAccessKey } - err = repo_model.CheckCreateRepository(ctx, ctx.Doer, ctxUser, opts.RepoName, false) + err = repo_service.CheckCreateRepository(ctx, ctx.Doer, ctxUser, opts.RepoName, false) if err != nil { handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, form) return diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index a558231df1224..c7a19062d267d 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/attribute" "code.gitea.io/gitea/modules/git/pipeline" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" @@ -112,7 +113,7 @@ func LFSLocks(ctx *context.Context) { } defer cleanup() - if err := git.Clone(ctx, ctx.Repo.Repository.RepoPath(), tmpBasePath, git.CloneRepoOptions{ + if err := gitrepo.CloneRepoToLocal(ctx, ctx.Repo.Repository, tmpBasePath, git.CloneRepoOptions{ Bare: true, Shared: true, }); err != nil { diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index b61345e830d96..bae189ba879cf 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -153,7 +153,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { log.Trace("Pushing %s mirror[%d] remote %s", storageRepo.RelativePath(), m.ID, m.RemoteName) envs := proxy.EnvWithProxy(remoteURL.URL) - if err := gitrepo.Push(ctx, storageRepo, git.PushOptions{ + if err := gitrepo.PushToExternal(ctx, storageRepo, git.PushOptions{ Remote: m.RemoteName, Force: true, Mirror: true, diff --git a/services/pull/pull.go b/services/pull/pull.go index 04f48f0565565..ecc0b2c7cebce 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -570,13 +570,11 @@ func pushToBaseRepoHelper(ctx context.Context, pr *issues_model.PullRequest, pre log.Error("Unable to load head repository for PR[%d] Error: %v", pr.ID, err) return err } - headRepoPath := pr.HeadRepo.RepoPath() if err := pr.LoadBaseRepo(ctx); err != nil { log.Error("Unable to load base repository for PR[%d] Error: %v", pr.ID, err) return err } - baseRepoPath := pr.BaseRepo.RepoPath() if err = pr.LoadIssue(ctx); err != nil { return fmt.Errorf("unable to load issue %d for pr %d: %w", pr.IssueID, pr.ID, err) @@ -587,8 +585,7 @@ func pushToBaseRepoHelper(ctx context.Context, pr *issues_model.PullRequest, pre gitRefName := pr.GetGitHeadRefName() - if err := git.Push(ctx, headRepoPath, git.PushOptions{ - Remote: baseRepoPath, + if err := gitrepo.Push(ctx, pr.HeadRepo, pr.BaseRepo, git.PushOptions{ Branch: prefixHeadBranch + pr.HeadBranch + ":" + gitRefName, Force: true, // Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/... diff --git a/services/repository/branch.go b/services/repository/branch.go index 0a2fd30620d22..8c43fe4b3fbc5 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -385,8 +385,7 @@ func CreateNewBranchFromCommit(ctx context.Context, doer *user_model.User, repo return err } - if err := git.Push(ctx, repo.RepoPath(), git.PushOptions{ - Remote: repo.RepoPath(), + if err := gitrepo.Push(ctx, repo, repo, git.PushOptions{ Branch: fmt.Sprintf("%s:%s%s", commitID, git.BranchPrefix, branchName), Env: repo_module.PushingEnvironment(doer, repo), }); err != nil { diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 731f23855d28a..b7f4afdebc25c 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -18,6 +18,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "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" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -362,8 +363,7 @@ func (t *TemporaryUploadRepository) CommitTree(ctx context.Context, opts *Commit func (t *TemporaryUploadRepository) Push(ctx context.Context, doer *user_model.User, commitHash, branch string, force bool) error { // Because calls hooks we need to pass in the environment env := repo_module.PushingEnvironment(doer, t.repo) - if err := git.Push(ctx, t.basePath, git.PushOptions{ - Remote: t.repo.RepoPath(), + if err := gitrepo.PushFromLocal(ctx, t.basePath, t.repo, git.PushOptions{ Branch: strings.TrimSpace(commitHash) + ":" + git.BranchPrefix + strings.TrimSpace(branch), Env: env, Force: force, diff --git a/services/repository/generate.go b/services/repository/generate.go index caf15265a00a0..3ec31dac22509 100644 --- a/services/repository/generate.go +++ b/services/repository/generate.go @@ -230,8 +230,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r ) // Clone to temporary path and do the init commit. - templateRepoPath := templateRepo.RepoPath() - if err := git.Clone(ctx, templateRepoPath, tmpDir, git.CloneRepoOptions{ + if err := gitrepo.CloneRepoToLocal(ctx, templateRepo, tmpDir, git.CloneRepoOptions{ Depth: 1, Branch: templateRepo.DefaultBranch, }); err != nil { diff --git a/services/repository/merge_upstream.go b/services/repository/merge_upstream.go index 8d6f11372c41e..692b80130355a 100644 --- a/services/repository/merge_upstream.go +++ b/services/repository/merge_upstream.go @@ -11,6 +11,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/reqctx" "code.gitea.io/gitea/modules/util" @@ -33,8 +34,7 @@ func MergeUpstream(ctx reqctx.RequestContext, doer *user_model.User, repo *repo_ return "up-to-date", nil } - err = git.Push(ctx, repo.BaseRepo.RepoPath(), git.PushOptions{ - Remote: repo.RepoPath(), + err = gitrepo.Push(ctx, repo.BaseRepo, repo, git.PushOptions{ Branch: fmt.Sprintf("%s:%s", divergingInfo.BaseBranchName, branch), Env: repo_module.PushingEnvironment(doer, repo), }) diff --git a/services/repository/migrate.go b/services/repository/migrate.go index acac6fd9ad688..8f515326ad60d 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -74,8 +74,6 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, repo *repo_model.Repository, opts migration.MigrateOptions, httpTransport *http.Transport, ) (*repo_model.Repository, error) { - repoPath := repo.RepoPath() - if u.IsOrganization() { t, err := organization.OrgFromUser(u).GetOwnerTeam(ctx) if err != nil { @@ -92,7 +90,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, return repo, fmt.Errorf("failed to remove existing repo dir %q, err: %w", repo.FullName(), err) } - if err := git.Clone(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{ + if err := gitrepo.CloneExternalRepo(ctx, opts.CloneAddr, repo, git.CloneRepoOptions{ Mirror: true, Quiet: true, Timeout: migrateTimeout, @@ -104,7 +102,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, return repo, fmt.Errorf("clone error: %w", err) } - if err := git.WriteCommitGraph(ctx, repoPath); err != nil { + if err := gitrepo.WriteCommitGraph(ctx, repo); err != nil { return repo, err } diff --git a/services/repository/repository.go b/services/repository/repository.go index acc5ce56cf585..93fbcb51f759a 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -345,3 +345,31 @@ func HasWiki(ctx context.Context, repo *repo_model.Repository) bool { } return hasWiki && err == nil } + +// CheckCreateRepository check if doer could create a repository in new owner +func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, name string, overwriteOrAdopt bool) error { + if !doer.CanCreateRepoIn(owner) { + return repo_model.ErrReachLimitOfRepo{Limit: owner.MaxRepoCreation} + } + + if err := repo_model.IsUsableRepoName(name); err != nil { + return err + } + + has, err := repo_model.IsRepositoryModelExist(ctx, owner, name) + if err != nil { + return err + } else if has { + 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) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", repo.RelativePath(), err) + return err + } + if !overwriteOrAdopt && isExist { + return repo_model.ErrRepoFilesAlreadyExist{Uname: owner.Name, Name: name} + } + return nil +} diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 98307a447a1c0..af477fc7f13dd 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -90,6 +90,17 @@ func AcceptTransferOwnership(ctx context.Context, repo *repo_model.Repository, d return nil } +// isRepositoryModelOrDirExist returns true if the repository with given name under user has already existed. +func isRepositoryModelOrDirExist(ctx context.Context, u *user_model.User, repoName string) (bool, error) { + has, err := repo_model.IsRepositoryModelExist(ctx, u, repoName) + if err != nil { + return false, err + } + repo := repo_model.StorageRepo(repo_model.RelativePath(u.Name, repoName)) + isExist, err := gitrepo.IsRepositoryExist(ctx, repo) + return has || isExist, err +} + // transferOwnership transfers all corresponding repository items from old user to new one. func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName string, repo *repo_model.Repository, teams []*organization.Team) (err error) { repoRenamed := false @@ -143,7 +154,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName newOwnerName = newOwner.Name // ensure capitalisation matches // Check if new owner has repository with same name. - if has, err := repo_model.IsRepositoryModelOrDirExist(ctx, newOwner, repo.Name); err != nil { + if has, err := isRepositoryModelOrDirExist(ctx, newOwner, repo.Name); err != nil { return fmt.Errorf("IsRepositoryExist: %w", err) } else if has { return repo_model.ErrRepoAlreadyExist{ @@ -345,7 +356,7 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR return err } - has, err := repo_model.IsRepositoryModelOrDirExist(ctx, repo.Owner, newRepoName) + has, err := isRepositoryModelOrDirExist(ctx, repo.Owner, newRepoName) if err != nil { return fmt.Errorf("IsRepositoryExist: %w", err) } else if has { diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 6a57a9a63e7d8..5f74817ef3f4f 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -25,8 +25,6 @@ import ( repo_service "code.gitea.io/gitea/services/repository" ) -const DefaultRemote = "origin" - func getWikiWorkingLockKey(repoID int64) string { return fmt.Sprintf("wiki_working_%d", repoID) } @@ -214,8 +212,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model return err } - if err := git.Push(gitRepo.Ctx, basePath, git.PushOptions{ - Remote: DefaultRemote, + if err := gitrepo.PushFromLocal(gitRepo.Ctx, basePath, repo.WikiStorageRepo(), git.PushOptions{ Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, repo.DefaultWikiBranch), Env: repo_module.FullPushingEnvironment( doer, @@ -333,8 +330,7 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model return err } - if err := git.Push(gitRepo.Ctx, basePath, git.PushOptions{ - Remote: DefaultRemote, + if err := gitrepo.PushFromLocal(gitRepo.Ctx, basePath, repo.WikiStorageRepo(), git.PushOptions{ Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, repo.DefaultWikiBranch), Env: repo_module.FullPushingEnvironment( doer, From fd68183f8f792fb793d16ee73f01c42baaa5552e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 4 Dec 2025 21:16:51 -0800 Subject: [PATCH 2/2] writecommitgraph --- services/doctor/misc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/doctor/misc.go b/services/doctor/misc.go index ce7eea1dcc808..445ff61ffb1e7 100644 --- a/services/doctor/misc.go +++ b/services/doctor/misc.go @@ -215,7 +215,7 @@ func checkCommitGraph(ctx context.Context, logger log.Logger, autofix bool) erro if !isExist { numNeedUpdate++ if autofix { - if err := git.WriteCommitGraph(ctx, repo.RepoPath()); err != nil { + if err := gitrepo.WriteCommitGraph(ctx, repo); err != nil { logger.Error("Unable to write commit-graph in %s. Error: %v", repo.FullName(), err) return err }