diff --git a/models/repo/repo.go b/models/repo/repo.go
index a8732f60bfff8..972828aa4cf7a 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -219,6 +219,10 @@ func RelativePath(ownerName, repoName string) string {
 	return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
 }
 
+func RelativeWikiPath(ownerName, repoName string) string {
+	return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git"
+}
+
 // RelativePath should be an unix style path like username/reponame.git
 func (repo *Repository) RelativePath() string {
 	return RelativePath(repo.OwnerName, repo.Name)
@@ -232,7 +236,7 @@ func (sr StorageRepo) RelativePath() string {
 }
 
 func (repo *Repository) WikiStorageRepo() StorageRepo {
-	return StorageRepo(strings.ToLower(repo.OwnerName) + "/" + strings.ToLower(repo.Name) + ".wiki.git")
+	return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name))
 }
 
 // SanitizedOriginalURL returns a sanitized OriginalURL
diff --git a/models/repo/wiki.go b/models/repo/wiki.go
index 4239a815b28ca..0485dcc4ed386 100644
--- a/models/repo/wiki.go
+++ b/models/repo/wiki.go
@@ -11,7 +11,6 @@ import (
 	"strings"
 
 	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/util"
 )
 
@@ -86,12 +85,3 @@ func WikiPath(userName, repoName string) string {
 func (repo *Repository) WikiPath() string {
 	return WikiPath(repo.OwnerName, repo.Name)
 }
-
-// HasWiki returns true if repository has wiki.
-func (repo *Repository) HasWiki() bool {
-	isDir, err := util.IsDir(repo.WikiPath())
-	if err != nil {
-		log.Error("Unable to check if %s is a directory: %v", repo.WikiPath(), err)
-	}
-	return isDir
-}
diff --git a/models/repo/wiki_test.go b/models/repo/wiki_test.go
index 103420a3925df..cfb398ab59ff8 100644
--- a/models/repo/wiki_test.go
+++ b/models/repo/wiki_test.go
@@ -9,6 +9,7 @@ import (
 
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
+	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/setting"
 
 	"github.com/stretchr/testify/assert"
@@ -39,7 +40,12 @@ func TestRepository_WikiPath(t *testing.T) {
 func TestRepository_HasWiki(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
-	assert.True(t, repo1.HasWiki())
+	exist, err := gitrepo.IsRepositoryExist(t.Context(), repo1.WikiStorageRepo())
+	assert.NoError(t, err)
+	assert.True(t, exist)
+
 	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
-	assert.False(t, repo2.HasWiki())
+	exist, err = gitrepo.IsRepositoryExist(t.Context(), repo2.WikiStorageRepo())
+	assert.NoError(t, err)
+	assert.False(t, exist)
 }
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index 20c8c2b406527..da84df2279f6d 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -557,7 +557,12 @@ func Wiki(ctx *context.Context) {
 		return
 	}
 
-	if !ctx.Repo.Repository.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, ctx.Repo.Repository.WikiStorageRepo())
+	if err != nil {
+		ctx.ServerError("IsWikiRepositoryExist", err)
+		return
+	}
+	if !hasWiki {
 		ctx.Data["Title"] = ctx.Tr("repo.wiki")
 		ctx.HTML(http.StatusOK, tplWikiStart)
 		return
@@ -598,7 +603,12 @@ func Wiki(ctx *context.Context) {
 func WikiRevision(ctx *context.Context) {
 	ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
 
-	if !ctx.Repo.Repository.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, ctx.Repo.Repository.WikiStorageRepo())
+	if err != nil {
+		ctx.ServerError("IsWikiRepositoryExist", err)
+		return
+	}
+	if !hasWiki {
 		ctx.Data["Title"] = ctx.Tr("repo.wiki")
 		ctx.HTML(http.StatusOK, tplWikiStart)
 		return
@@ -634,7 +644,12 @@ func WikiRevision(ctx *context.Context) {
 
 // WikiPages render wiki pages list page
 func WikiPages(ctx *context.Context) {
-	if !ctx.Repo.Repository.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, ctx.Repo.Repository.WikiStorageRepo())
+	if err != nil {
+		ctx.ServerError("IsWikiRepositoryExist", err)
+		return
+	}
+	if !hasWiki {
 		ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
 		return
 	}
@@ -753,7 +768,12 @@ func WikiRaw(ctx *context.Context) {
 func NewWiki(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
 
-	if !ctx.Repo.Repository.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, ctx.Repo.Repository.WikiStorageRepo())
+	if err != nil {
+		ctx.ServerError("IsWikiRepositoryExist", err)
+		return
+	}
+	if !hasWiki {
 		ctx.Data["title"] = "Home"
 	}
 	if ctx.FormString("title") != "" {
@@ -806,7 +826,12 @@ func NewWikiPost(ctx *context.Context) {
 func EditWiki(ctx *context.Context) {
 	ctx.Data["PageIsWikiEdit"] = true
 
-	if !ctx.Repo.Repository.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, ctx.Repo.Repository.WikiStorageRepo())
+	if err != nil {
+		ctx.ServerError("IsWikiRepositoryExist", err)
+		return
+	}
+	if !hasWiki {
 		ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
 		return
 	}
diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index e44cf46ba8398..0f4d394eff5e7 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -241,7 +241,9 @@ func TestDefaultWikiBranch(t *testing.T) {
 
 	// repo with no wiki
 	repoWithNoWiki := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
-	assert.False(t, repoWithNoWiki.HasWiki())
+	exist, err := gitrepo.IsRepositoryExist(db.DefaultContext, repoWithNoWiki.WikiStorageRepo())
+	assert.NoError(t, err)
+	assert.False(t, exist)
 	assert.NoError(t, wiki_service.ChangeDefaultWikiBranch(db.DefaultContext, repoWithNoWiki, "main"))
 
 	// repo with wiki
diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go
index f52d4157c8136..e7897ab6ff0f4 100644
--- a/services/migrations/gitea_uploader_test.go
+++ b/services/migrations/gitea_uploader_test.go
@@ -63,7 +63,9 @@ func TestGiteaUploadRepo(t *testing.T) {
 	assert.NoError(t, err)
 
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName})
-	assert.True(t, repo.HasWiki())
+	exist, err := gitrepo.IsRepositoryExist(ctx, repo)
+	assert.NoError(t, err)
+	assert.True(t, exist)
 	assert.EqualValues(t, repo_model.RepositoryReady, repo.Status)
 
 	milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index 658747e7c83f1..92c9e5332b2c9 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -52,7 +52,11 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
 		return err
 	}
 
-	if m.Repo.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, m.Repo.WikiStorageRepo())
+	if err != nil {
+		return err
+	}
+	if hasWiki {
 		wikiPath := m.Repo.WikiPath()
 		wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr)
 		// Remove old remote of wiki
@@ -339,17 +343,21 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
 		endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
 		lfsClient := lfs.NewClient(endpoint, nil)
 		if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
-			log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err)
+			log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo.FullName(), err)
 		}
 	}
 	gitRepo.Close()
 
 	log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo)
 	if err := repo_module.UpdateRepoSize(ctx, m.Repo); err != nil {
-		log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo, err)
+		log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo.FullName(), err)
 	}
 
-	if m.Repo.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, m.Repo.WikiStorageRepo())
+	if err != nil {
+		log.Error("SyncMirrors [repo: %-v]: failed to check if wiki repository exists: %v", m.Repo.FullName(), err)
+	}
+	if hasWiki {
 		log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo)
 		stderrBuilder.Reset()
 		stdoutBuilder.Reset()
diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go
index 9b57427d98041..7e0300fbb6874 100644
--- a/services/mirror/mirror_push.go
+++ b/services/mirror/mirror_push.go
@@ -47,7 +47,11 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str
 		return err
 	}
 
-	if m.Repo.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, m.Repo.WikiStorageRepo())
+	if err != nil {
+		return err
+	}
+	if hasWiki {
 		wikiRemoteURL := repository.WikiRemoteURL(ctx, addr)
 		if len(wikiRemoteURL) > 0 {
 			if err := addRemoteAndConfig(wikiRemoteURL, m.Repo.WikiPath()); err != nil {
@@ -68,7 +72,11 @@ func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error
 		return err
 	}
 
-	if m.Repo.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, m.Repo.WikiStorageRepo())
+	if err != nil {
+		return err
+	}
+	if hasWiki {
 		if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: m.Repo.WikiPath()}); err != nil {
 			// The wiki remote may not exist
 			log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err)
@@ -183,7 +191,11 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
 		return err
 	}
 
-	if m.Repo.HasWiki() {
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, m.Repo.WikiStorageRepo())
+	if err != nil {
+		return err
+	}
+	if hasWiki {
 		_, err := git.GetRemoteAddress(ctx, m.Repo.WikiPath(), m.RemoteName)
 		if err == nil {
 			err := performPush(m.Repo, true)
diff --git a/services/repository/delete.go b/services/repository/delete.go
index ff74a20817a7c..160ea4513b382 100644
--- a/services/repository/delete.go
+++ b/services/repository/delete.go
@@ -298,9 +298,13 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
 		}
 	}
 
-	// Remove wiki files
-	if repo.HasWiki() {
-		system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath())
+	// Remove wiki files if it exists.
+	if err := gitrepo.DeleteRepository(ctx, 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(db.DefaultContext, system_model.NoticeRepository, desc); err != nil {
+			log.Error("CreateRepositoryNotice: %v", err)
+		}
 	}
 
 	// Remove archives
diff --git a/services/repository/hooks.go b/services/repository/hooks.go
index c13b272550a13..a6b6102877d2e 100644
--- a/services/repository/hooks.go
+++ b/services/repository/hooks.go
@@ -32,11 +32,15 @@ func SyncRepositoryHooks(ctx context.Context) error {
 			}
 
 			if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
-				return fmt.Errorf("SyncRepositoryHook: %w", err)
+				return fmt.Errorf("CreateDelegateHooks: %w", err)
 			}
-			if repo.HasWiki() {
+			exist, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo())
+			if err != nil {
+				return fmt.Errorf("IsRepositoryExist: %w", err)
+			}
+			if exist {
 				if err := gitrepo.CreateDelegateHooks(ctx, repo.WikiStorageRepo()); err != nil {
-					return fmt.Errorf("SyncRepositoryHook: %w", err)
+					return fmt.Errorf("CreateDelegateHooks: %w", err)
 				}
 			}
 			return nil
diff --git a/services/repository/migrate.go b/services/repository/migrate.go
index 5b6feccb8d879..5d911b52d8614 100644
--- a/services/repository/migrate.go
+++ b/services/repository/migrate.go
@@ -268,18 +268,23 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo
 	if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
 		return repo, fmt.Errorf("createDelegateHooks: %w", err)
 	}
-	if repo.HasWiki() {
+
+	hasWiki, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo())
+	if err != nil {
+		return repo, fmt.Errorf("IsWikiRepositoryExist: %w", err)
+	}
+	if hasWiki {
 		if err := gitrepo.CreateDelegateHooks(ctx, repo.WikiStorageRepo()); err != nil {
 			return repo, fmt.Errorf("createDelegateHooks.(wiki): %w", err)
 		}
 	}
 
-	_, _, err := git.NewCommand("remote", "rm", "origin").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
+	_, _, err = git.NewCommand("remote", "rm", "origin").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
 	if err != nil && !git.IsRemoteNotExistError(err) {
 		return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err)
 	}
 
-	if repo.HasWiki() {
+	if hasWiki {
 		if err := cleanUpMigrateGitConfig(ctx, repo.WikiPath()); err != nil {
 			return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %w", err)
 		}
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index a589bc469d87f..3814136735c69 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -341,14 +341,14 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
 		return fmt.Errorf("rename repository directory: %w", err)
 	}
 
-	wikiPath := repo.WikiPath()
-	isExist, err := util.IsExist(wikiPath)
+	isExist, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo())
 	if err != nil {
-		log.Error("Unable to check if %s exists. Error: %v", wikiPath, err)
+		log.Error("Unable to check if the wiki of %s exists. Error: %v", repo.FullName(), err)
 		return err
 	}
 	if isExist {
-		if err = util.Rename(wikiPath, repo_model.WikiPath(repo.Owner.Name, newRepoName)); err != nil {
+		if err = gitrepo.RenameRepository(ctx, 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/wiki/wiki.go b/services/wiki/wiki.go
index b21f46639dc99..ed467b02e744f 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -35,7 +35,9 @@ func getWikiWorkingLockKey(repoID int64) string {
 // InitWiki initializes a wiki for repository,
 // it does nothing when repository already has wiki.
 func InitWiki(ctx context.Context, repo *repo_model.Repository) error {
-	if repo.HasWiki() {
+	if exist, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo()); err != nil {
+		return err
+	} else if exist {
 		return nil
 	}
 
@@ -363,7 +365,14 @@ func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error {
 		return err
 	}
 
-	system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath())
+	if err := gitrepo.DeleteRepository(ctx, 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(db.DefaultContext, system_model.NoticeRepository, desc); err != nil {
+			log.Error("CreateRepositoryNotice: %v", err)
+		}
+	}
+
 	return nil
 }
 
@@ -377,7 +386,9 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n
 			return fmt.Errorf("unable to update database: %w", err)
 		}
 
-		if !repo.HasWiki() {
+		if exist, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo()); err != nil {
+			return err
+		} else if !exist {
 			return nil
 		}
 
diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go
index 288d2582793d0..b7e55b31d5548 100644
--- a/services/wiki/wiki_test.go
+++ b/services/wiki/wiki_test.go
@@ -149,7 +149,9 @@ func TestRepository_InitWiki(t *testing.T) {
 	// repo2 does not already have a wiki
 	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
 	assert.NoError(t, InitWiki(git.DefaultContext, repo2))
-	assert.True(t, repo2.HasWiki())
+	exist, err := gitrepo.IsRepositoryExist(git.DefaultContext, repo2.WikiStorageRepo())
+	assert.NoError(t, err)
+	assert.True(t, exist)
 }
 
 func TestRepository_AddWikiPage(t *testing.T) {