From 0056fdb94201f54fcbb51d741a68b04bf41213fc Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sat, 15 Mar 2025 19:48:59 -0700
Subject: [PATCH] Move git references checking to gitrepo packages to reduce
 expose of repository path (#33891)

---
 modules/git/repo_tag.go                    |  6 ------
 modules/gitrepo/branch.go                  | 18 ++++++++++++++++++
 modules/gitrepo/tag.go                     | 15 +++++++++++++++
 routers/api/v1/repo/branch.go              |  4 ++--
 routers/api/v1/repo/pull.go                |  2 +-
 routers/api/v1/repo/repo.go                |  2 +-
 routers/private/hook_pre_receive.go        |  5 +++--
 routers/web/repo/compare.go                |  8 ++++----
 routers/web/repo/issue_comment.go          |  3 ++-
 routers/web/repo/issue_view.go             |  3 ++-
 routers/web/repo/pull.go                   |  6 +++---
 routers/web/repo/release.go                |  3 ++-
 routers/web/repo/setting/default_branch.go |  2 +-
 services/agit/agit.go                      |  5 +++--
 services/automerge/automerge.go            |  6 +++---
 services/context/api.go                    |  4 ++--
 services/context/repo.go                   |  6 +++---
 services/pull/commit_status.go             |  5 ++---
 services/pull/protected_branch.go          |  5 +++--
 services/pull/pull.go                      |  2 +-
 services/pull/temp_repo.go                 |  3 ++-
 services/release/release.go                |  2 +-
 services/repository/branch.go              |  8 ++++----
 services/wiki/wiki.go                      |  2 +-
 24 files changed, 79 insertions(+), 46 deletions(-)
 create mode 100644 modules/gitrepo/tag.go

diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go
index d653b0e2e6..c74618471a 100644
--- a/modules/git/repo_tag.go
+++ b/modules/git/repo_tag.go
@@ -5,7 +5,6 @@
 package git
 
 import (
-	"context"
 	"fmt"
 	"io"
 	"strings"
@@ -17,11 +16,6 @@ import (
 // TagPrefix tags prefix path on the repository
 const TagPrefix = "refs/tags/"
 
-// IsTagExist returns true if given tag exists in the repository.
-func IsTagExist(ctx context.Context, repoPath, name string) bool {
-	return IsReferenceExist(ctx, repoPath, TagPrefix+name)
-}
-
 // CreateTag create one tag in the repository
 func (repo *Repository) CreateTag(name, revision string) error {
 	_, _, err := NewCommand("tag").AddDashesAndList(name, revision).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
diff --git a/modules/gitrepo/branch.go b/modules/gitrepo/branch.go
index 3336036248..9c4bdc5bdf 100644
--- a/modules/gitrepo/branch.go
+++ b/modules/gitrepo/branch.go
@@ -47,3 +47,21 @@ func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) {
 func GetWikiDefaultBranch(ctx context.Context, repo Repository) (string, error) {
 	return git.GetDefaultBranch(ctx, wikiPath(repo))
 }
+
+// IsReferenceExist returns true if given reference exists in the repository.
+func IsReferenceExist(ctx context.Context, repo Repository, name string) bool {
+	return git.IsReferenceExist(ctx, repoPath(repo), name)
+}
+
+func IsWikiReferenceExist(ctx context.Context, repo Repository, name string) bool {
+	return git.IsReferenceExist(ctx, wikiPath(repo), name)
+}
+
+// IsBranchExist returns true if given branch exists in the repository.
+func IsBranchExist(ctx context.Context, repo Repository, name string) bool {
+	return IsReferenceExist(ctx, repo, git.BranchPrefix+name)
+}
+
+func IsWikiBranchExist(ctx context.Context, repo Repository, name string) bool {
+	return IsWikiReferenceExist(ctx, repo, git.BranchPrefix+name)
+}
diff --git a/modules/gitrepo/tag.go b/modules/gitrepo/tag.go
new file mode 100644
index 0000000000..58ed204a99
--- /dev/null
+++ b/modules/gitrepo/tag.go
@@ -0,0 +1,15 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gitrepo
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/modules/git"
+)
+
+// IsTagExist returns true if given tag exists in the repository.
+func IsTagExist(ctx context.Context, repo Repository, name string) bool {
+	return IsReferenceExist(ctx, repo, git.TagPrefix+name)
+}
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index f866f2048e..9c6e572fb4 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -227,7 +227,7 @@ func CreateBranch(ctx *context.APIContext) {
 			return
 		}
 	} else if len(opt.OldBranchName) > 0 { //nolint
-		if ctx.Repo.GitRepo.IsBranchExist(opt.OldBranchName) { //nolint
+		if gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, opt.OldBranchName) { //nolint
 			oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint
 			if err != nil {
 				ctx.APIErrorInternal(err)
@@ -1019,7 +1019,7 @@ func EditBranchProtection(ctx *context.APIContext) {
 	isPlainRule := !git_model.IsRuleNameSpecial(bpName)
 	var isBranchExist bool
 	if isPlainRule {
-		isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), bpName)
+		isBranchExist = gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, bpName)
 	}
 
 	if isBranchExist {
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 2f32f01c4f..f5d0e37c65 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -742,7 +742,7 @@ func EditPullRequest(ctx *context.APIContext) {
 
 	// change pull target branch
 	if !pr.HasMerged && len(form.Base) != 0 && form.Base != pr.BaseBranch {
-		if !ctx.Repo.GitRepo.IsBranchExist(form.Base) {
+		if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, form.Base) {
 			ctx.APIError(http.StatusNotFound, fmt.Errorf("new base '%s' not exist", form.Base))
 			return
 		}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 5ef510fd84..3d638cb05e 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -734,7 +734,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
 
 	// Default branch only updated if changed and exist or the repository is empty
 	updateRepoLicense := false
-	if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch)) {
+	if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, *opts.DefaultBranch)) {
 		if !repo.IsEmpty {
 			if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil {
 				ctx.APIErrorInternal(err)
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index c64033d9e2..ae23abc542 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/models/unit"
 	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/log"
 	"code.gitea.io/gitea/modules/private"
 	"code.gitea.io/gitea/modules/web"
@@ -447,13 +448,13 @@ func preReceiveFor(ctx *preReceiveContext, refFullName git.RefName) {
 	baseBranchName := refFullName.ForBranchName()
 
 	baseBranchExist := false
-	if ctx.Repo.GitRepo.IsBranchExist(baseBranchName) {
+	if gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, baseBranchName) {
 		baseBranchExist = true
 	}
 
 	if !baseBranchExist {
 		for p, v := range baseBranchName {
-			if v == '/' && ctx.Repo.GitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 {
+			if v == '/' && gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, baseBranchName[:p]) && p != len(baseBranchName)-1 {
 				baseBranchExist = true
 				break
 			}
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index a01cd753bf..6cea95e387 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -301,8 +301,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
 
 	// Check if base branch is valid.
 	baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch)
-	baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch)
-	baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch)
+	baseIsBranch := gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, ci.BaseBranch)
+	baseIsTag := gitrepo.IsTagExist(ctx, ctx.Repo.Repository, ci.BaseBranch)
 
 	if !baseIsCommit && !baseIsBranch && !baseIsTag {
 		// Check if baseBranch is short sha commit hash
@@ -504,8 +504,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
 
 	// Check if head branch is valid.
 	headIsCommit := ci.HeadGitRepo.IsCommitExist(ci.HeadBranch)
-	headIsBranch := ci.HeadGitRepo.IsBranchExist(ci.HeadBranch)
-	headIsTag := ci.HeadGitRepo.IsTagExist(ci.HeadBranch)
+	headIsBranch := gitrepo.IsBranchExist(ctx, ci.HeadRepo, ci.HeadBranch)
+	headIsTag := gitrepo.IsTagExist(ctx, ci.HeadRepo, ci.HeadBranch)
 	if !headIsCommit && !headIsBranch && !headIsTag {
 		// Check if headBranch is short sha commit hash
 		if headCommit, _ := ci.HeadGitRepo.GetCommit(ci.HeadBranch); headCommit != nil {
diff --git a/routers/web/repo/issue_comment.go b/routers/web/repo/issue_comment.go
index bc84950701..45463200f6 100644
--- a/routers/web/repo/issue_comment.go
+++ b/routers/web/repo/issue_comment.go
@@ -13,6 +13,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/log"
 	"code.gitea.io/gitea/modules/markup/markdown"
 	repo_module "code.gitea.io/gitea/modules/repository"
@@ -117,7 +118,7 @@ func NewComment(ctx *context.Context) {
 						ctx.ServerError("Unable to load head repo", err)
 						return
 					}
-					if ok := git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.BaseBranch); !ok {
+					if ok := gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.BaseBranch); !ok {
 						// todo localize
 						ctx.JSONError("The origin branch is delete, cannot reopen.")
 						return
diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go
index 37e1b27931..b312f1260a 100644
--- a/routers/web/repo/issue_view.go
+++ b/routers/web/repo/issue_view.go
@@ -24,6 +24,7 @@ import (
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/emoji"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markup"
 	"code.gitea.io/gitea/modules/markup/markdown"
@@ -526,7 +527,7 @@ func preparePullViewDeleteBranch(ctx *context.Context, issue *issues_model.Issue
 	pull := issue.PullRequest
 	isPullBranchDeletable := canDelete &&
 		pull.HeadRepo != nil &&
-		git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.HeadBranch) &&
+		gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.HeadBranch) &&
 		(!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"])
 
 	if isPullBranchDeletable && pull.HasMerged {
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 9eb6917617..e12798f93d 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -347,7 +347,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
 		defer baseGitRepo.Close()
 	}
 
-	if !baseGitRepo.IsBranchExist(pull.BaseBranch) {
+	if !gitrepo.IsBranchExist(ctx, pull.BaseRepo, pull.BaseBranch) {
 		ctx.Data["BaseBranchNotExist"] = true
 		ctx.Data["IsPullRequestBroken"] = true
 		ctx.Data["BaseTarget"] = pull.BaseBranch
@@ -404,9 +404,9 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
 		defer closer.Close()
 
 		if pull.Flow == issues_model.PullRequestFlowGithub {
-			headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
+			headBranchExist = gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.HeadBranch)
 		} else {
-			headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName())
+			headBranchExist = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitRefName())
 		}
 
 		if headBranchExist {
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 2ad0bf7e3a..553bdbf6e5 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models/unit"
 	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/markup/markdown"
 	"code.gitea.io/gitea/modules/optional"
 	"code.gitea.io/gitea/modules/setting"
@@ -420,7 +421,7 @@ func NewReleasePost(ctx *context.Context) {
 		return
 	}
 
-	if !ctx.Repo.GitRepo.IsBranchExist(form.Target) {
+	if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, form.Target) {
 		ctx.RenderWithErr(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form)
 		return
 	}
diff --git a/routers/web/repo/setting/default_branch.go b/routers/web/repo/setting/default_branch.go
index e0822ba540..044c9e556a 100644
--- a/routers/web/repo/setting/default_branch.go
+++ b/routers/web/repo/setting/default_branch.go
@@ -34,7 +34,7 @@ func SetDefaultBranchPost(ctx *context.Context) {
 		}
 
 		branch := ctx.FormString("branch")
-		if err := repo_service.SetRepoDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, branch); err != nil {
+		if err := repo_service.SetRepoDefaultBranch(ctx, ctx.Repo.Repository, branch); err != nil {
 			switch {
 			case git_model.IsErrBranchNotExist(err):
 				ctx.Status(http.StatusNotFound)
diff --git a/services/agit/agit.go b/services/agit/agit.go
index 7d2ccbd0c2..1e6ce93312 100644
--- a/services/agit/agit.go
+++ b/services/agit/agit.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/log"
 	"code.gitea.io/gitea/modules/private"
 	"code.gitea.io/gitea/modules/setting"
@@ -56,10 +57,10 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
 
 		baseBranchName := opts.RefFullNames[i].ForBranchName()
 		currentTopicBranch := ""
-		if !gitRepo.IsBranchExist(baseBranchName) {
+		if !gitrepo.IsBranchExist(ctx, repo, baseBranchName) {
 			// try match refs/for/<target-branch>/<topic-branch>
 			for p, v := range baseBranchName {
-				if v == '/' && gitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 {
+				if v == '/' && gitrepo.IsBranchExist(ctx, repo, baseBranchName[:p]) && p != len(baseBranchName)-1 {
 					currentTopicBranch = baseBranchName[p+1:]
 					baseBranchName = baseBranchName[:p]
 					break
diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go
index bdb0493ae8..62d560ff94 100644
--- a/services/automerge/automerge.go
+++ b/services/automerge/automerge.go
@@ -248,13 +248,13 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
 
 	switch pr.Flow {
 	case issues_model.PullRequestFlowGithub:
-		headBranchExist := headGitRepo.IsBranchExist(pr.HeadBranch)
-		if pr.HeadRepo == nil || !headBranchExist {
+		headBranchExist := pr.HeadRepo != nil && gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch)
+		if !headBranchExist {
 			log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch: %s]", pr, pr.HeadRepoID, pr.HeadBranch)
 			return
 		}
 	case issues_model.PullRequestFlowAGit:
-		headBranchExist := git.IsReferenceExist(ctx, baseGitRepo.Path, pr.GetGitRefName())
+		headBranchExist := gitrepo.IsReferenceExist(ctx, pr.BaseRepo, pr.GetGitRefName())
 		if !headBranchExist {
 			log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch(Agit): %s]", pr, pr.HeadRepoID, pr.HeadBranch)
 			return
diff --git a/services/context/api.go b/services/context/api.go
index 89280cac80..10fad419ba 100644
--- a/services/context/api.go
+++ b/services/context/api.go
@@ -304,14 +304,14 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 		refName, _, _ := getRefNameLegacy(ctx.Base, ctx.Repo, ctx.PathParam("*"), ctx.FormTrim("ref"))
 		var err error
 
-		if ctx.Repo.GitRepo.IsBranchExist(refName) {
+		if gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, refName) {
 			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
 			if err != nil {
 				ctx.APIErrorInternal(err)
 				return
 			}
 			ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
-		} else if ctx.Repo.GitRepo.IsTagExist(refName) {
+		} else if gitrepo.IsTagExist(ctx, ctx.Repo.Repository, refName) {
 			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
 			if err != nil {
 				ctx.APIErrorInternal(err)
diff --git a/services/context/repo.go b/services/context/repo.go
index e63e3e5317..6eccd1312a 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -814,7 +814,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
 		reqPath := ctx.PathParam("*")
 		if reqPath == "" {
 			refShortName = ctx.Repo.Repository.DefaultBranch
-			if !ctx.Repo.GitRepo.IsBranchExist(refShortName) {
+			if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, refShortName) {
 				brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
 				if err == nil && len(brs) != 0 {
 					refShortName = brs[0].Name
@@ -854,7 +854,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
 				return
 			}
 
-			if refType == git.RefTypeBranch && ctx.Repo.GitRepo.IsBranchExist(refShortName) {
+			if refType == git.RefTypeBranch && gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, refShortName) {
 				ctx.Repo.BranchName = refShortName
 				ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName)
 
@@ -864,7 +864,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
 					return
 				}
 				ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
-			} else if refType == git.RefTypeTag && ctx.Repo.GitRepo.IsTagExist(refShortName) {
+			} else if refType == git.RefTypeTag && gitrepo.IsTagExist(ctx, ctx.Repo.Repository, refShortName) {
 				ctx.Repo.RefFullName = git.RefNameFromTag(refShortName)
 
 				ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refShortName)
diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go
index aa1ad7cd66..0bfff21746 100644
--- a/services/pull/commit_status.go
+++ b/services/pull/commit_status.go
@@ -10,7 +10,6 @@ import (
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
-	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/structs"
@@ -131,10 +130,10 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
 	}
 	defer closer.Close()
 
-	if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) {
+	if pr.Flow == issues_model.PullRequestFlowGithub && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
 		return "", errors.New("Head branch does not exist, can not merge")
 	}
-	if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, pr.GetGitRefName()) {
+	if pr.Flow == issues_model.PullRequestFlowAGit && !gitrepo.IsReferenceExist(ctx, pr.HeadRepo, pr.GetGitRefName()) {
 		return "", errors.New("Head branch does not exist, can not merge")
 	}
 
diff --git a/services/pull/protected_branch.go b/services/pull/protected_branch.go
index 5f42629ddc..181bd32f44 100644
--- a/services/pull/protected_branch.go
+++ b/services/pull/protected_branch.go
@@ -8,7 +8,7 @@ import (
 
 	git_model "code.gitea.io/gitea/models/git"
 	repo_model "code.gitea.io/gitea/models/repo"
-	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/gitrepo"
 )
 
 func CreateOrUpdateProtectedBranch(ctx context.Context, repo *repo_model.Repository,
@@ -22,7 +22,8 @@ func CreateOrUpdateProtectedBranch(ctx context.Context, repo *repo_model.Reposit
 	isPlainRule := !git_model.IsRuleNameSpecial(protectBranch.RuleName)
 	var isBranchExist bool
 	if isPlainRule {
-		isBranchExist = git.IsBranchExist(ctx, repo.RepoPath(), protectBranch.RuleName)
+		// TODO: read the database directly to check if the branch exists
+		isBranchExist = gitrepo.IsBranchExist(ctx, repo, protectBranch.RuleName)
 	}
 
 	if isBranchExist {
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 21f31e16cf..4641d4ac40 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -491,7 +491,7 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
 		for _, pr := range prs {
 			divergence, err := GetDiverging(ctx, pr)
 			if err != nil {
-				if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
+				if git_model.IsErrBranchNotExist(err) && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
 					log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch)
 				} else {
 					log.Error("GetDiverging: %v", err)
diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go
index 911db85585..3f33370798 100644
--- a/services/pull/temp_repo.go
+++ b/services/pull/temp_repo.go
@@ -15,6 +15,7 @@ import (
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/log"
 	repo_module "code.gitea.io/gitea/modules/repository"
 )
@@ -181,7 +182,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
 	if err := git.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
 		Run(ctx, prCtx.RunOpts()); err != nil {
 		cancel()
-		if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
+		if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
 			return nil, nil, git_model.ErrBranchNotExist{
 				BranchName: pr.HeadBranch,
 			}
diff --git a/services/release/release.go b/services/release/release.go
index d1dbb11ea1..0b8a74252a 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -77,7 +77,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
 	var created bool
 	// Only actual create when publish.
 	if !rel.IsDraft {
-		if !gitRepo.IsTagExist(rel.TagName) {
+		if !gitrepo.IsTagExist(ctx, rel.Repo, rel.TagName) {
 			if err := rel.LoadAttributes(ctx); err != nil {
 				log.Error("LoadAttributes: %v", err)
 				return false, err
diff --git a/services/repository/branch.go b/services/repository/branch.go
index d0d8851423..8804778bd5 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -410,11 +410,11 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
 		return "target_exist", nil
 	}
 
-	if gitRepo.IsBranchExist(to) {
+	if gitrepo.IsBranchExist(ctx, repo, to) {
 		return "target_exist", nil
 	}
 
-	if !gitRepo.IsBranchExist(from) {
+	if !gitrepo.IsBranchExist(ctx, repo, from) {
 		return "from_not_exist", nil
 	}
 
@@ -618,12 +618,12 @@ func AddAllRepoBranchesToSyncQueue(ctx context.Context) error {
 	return nil
 }
 
-func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, newBranchName string) error {
+func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, newBranchName string) error {
 	if repo.DefaultBranch == newBranchName {
 		return nil
 	}
 
-	if !gitRepo.IsBranchExist(newBranchName) {
+	if !gitrepo.IsBranchExist(ctx, repo, newBranchName) {
 		return git_model.ErrBranchNotExist{
 			BranchName: newBranchName,
 		}
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 66f4456185..413d416b8d 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -100,7 +100,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
 		return fmt.Errorf("InitWiki: %w", err)
 	}
 
-	hasDefaultBranch := git.IsBranchExist(ctx, repo.WikiPath(), repo.DefaultWikiBranch)
+	hasDefaultBranch := gitrepo.IsWikiBranchExist(ctx, repo, repo.DefaultWikiBranch)
 
 	basePath, err := repo_module.CreateTemporaryPath("update-wiki")
 	if err != nil {