diff --git a/models/repo/repo.go b/models/repo/repo.go
index 819356dfad..605a9e0f3f 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 3228ae11a4..bf560cf695 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 18808cac24..920c317f79 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/modules/markup/markdown/markdown_math_test.go b/modules/markup/markdown/markdown_math_test.go
index a75f18d36a..9e368cb689 100644
--- a/modules/markup/markdown/markdown_math_test.go
+++ b/modules/markup/markdown/markdown_math_test.go
@@ -30,6 +30,10 @@ func TestMathRender(t *testing.T) {
"$ a $",
`
a
` + nl,
},
+ {
+ "$a$$b$",
+ `ab
` + nl,
+ },
{
"$a$ $b$",
`a b
` + nl,
@@ -59,7 +63,7 @@ func TestMathRender(t *testing.T) {
`a$b $a a$b b$
` + nl,
},
{
- "a$x$",
+ "a$x$", // Pattern: "word$other$" The real world example is: "Price is between US$1 and US$2.", so don't parse this.
`a$x$
` + nl,
},
{
@@ -70,6 +74,10 @@ func TestMathRender(t *testing.T) {
"$a$ ($b$) [$c$] {$d$}",
`a (b) [$c$] {$d$}
` + nl,
},
+ {
+ "[$a$](link)",
+ `a
` + nl,
+ },
{
"$$a$$",
`a
` + nl,
diff --git a/modules/markup/markdown/math/inline_parser.go b/modules/markup/markdown/math/inline_parser.go
index a711d1e1cd..564861df90 100644
--- a/modules/markup/markdown/math/inline_parser.go
+++ b/modules/markup/markdown/math/inline_parser.go
@@ -54,6 +54,10 @@ func isAlphanumeric(b byte) bool {
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}
+func isInMarkdownLinkText(block text.Reader, lineAfter []byte) bool {
+ return block.PrecendingCharacter() == '[' && bytes.HasPrefix(lineAfter, []byte("]("))
+}
+
// Parse parses the current line and returns a result of parsing.
func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
line, _ := block.PeekLine()
@@ -115,7 +119,9 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
}
// check valid ending character
isValidEndingChar := isPunctuation(succeedingCharacter) || isParenthesesClose(succeedingCharacter) ||
- succeedingCharacter == ' ' || succeedingCharacter == '\n' || succeedingCharacter == 0
+ succeedingCharacter == ' ' || succeedingCharacter == '\n' || succeedingCharacter == 0 ||
+ succeedingCharacter == '$' ||
+ isInMarkdownLinkText(block, line[i+len(stopMark):])
if checkSurrounding && !isValidEndingChar {
break
}
diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go
index b6f9f07f98..57dc23b17f 100644
--- a/modules/setting/config_provider.go
+++ b/modules/setting/config_provider.go
@@ -337,14 +337,14 @@ func LogStartupProblem(skip int, level log.Level, format string, args ...any) {
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
if rootCfg.Section(oldSection).HasKey(oldKey) {
- LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
+ LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` present, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
}
}
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
if rootCfg.Section(oldSection).HasKey(oldKey) {
- LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
+ LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` present but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
}
}
diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go
index f21f568231..2b0ba9072d 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 f910f0bd40..07bcb474f0 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 edad756b6b..35124c5c3e 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 ea15e90e5c..8f4adb2ad2 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 a558231df1..c7a19062d2 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/context/csrf.go b/services/context/csrf.go
index f190465bdb..aa99f34b03 100644
--- a/services/context/csrf.go
+++ b/services/context/csrf.go
@@ -118,7 +118,7 @@ func (c *csrfProtector) PrepareForSessionUser(ctx *Context) {
if uidChanged {
_ = ctx.Session.Set(c.opt.oldSessionKey, c.id)
} else if cookieToken != "" {
- // If cookie token presents, re-use existing unexpired token, else generate a new one.
+ // If cookie token present, re-use existing unexpired token, else generate a new one.
if issueTime, ok := ParseCsrfToken(cookieToken); ok {
dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time.
if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval {
diff --git a/services/doctor/misc.go b/services/doctor/misc.go
index ce7eea1dcc..445ff61ffb 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
}
diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go
index b61345e830..bae189ba87 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 04f48f0565..ecc0b2c7ce 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 0a2fd30620..8c43fe4b3f 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 731f23855d..b7f4afdebc 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 caf15265a0..3ec31dac22 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 8d6f11372c..692b801303 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 acac6fd9ad..8f515326ad 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 acc5ce56cf..93fbcb51f7 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 98307a447a..af477fc7f1 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 6a57a9a63e..5f74817ef3 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,
diff --git a/web_src/js/features/comp/ComboMarkdownEditor.ts b/web_src/js/features/comp/ComboMarkdownEditor.ts
index 9ceb087005..86b1a037a0 100644
--- a/web_src/js/features/comp/ComboMarkdownEditor.ts
+++ b/web_src/js/features/comp/ComboMarkdownEditor.ts
@@ -17,7 +17,7 @@ import {POST} from '../../modules/fetch.ts';
import {
EventEditorContentChanged,
initTextareaMarkdown,
- textareaInsertText,
+ replaceTextareaSelection,
triggerEditorContentChanged,
} from './EditorMarkdown.ts';
import {DropzoneCustomEventReloadFiles, initDropzone} from '../dropzone.ts';
@@ -273,7 +273,7 @@ export class ComboMarkdownEditor {
let cols = parseInt(addTablePanel.querySelector('[name=cols]')!.value);
rows = Math.max(1, Math.min(100, rows));
cols = Math.max(1, Math.min(100, cols));
- textareaInsertText(this.textarea, `\n${this.generateMarkdownTable(rows, cols)}\n\n`);
+ replaceTextareaSelection(this.textarea, `\n${this.generateMarkdownTable(rows, cols)}\n\n`);
addTablePanelTippy.hide();
});
}
diff --git a/web_src/js/features/comp/EditorMarkdown.ts b/web_src/js/features/comp/EditorMarkdown.ts
index 2240e2f41b..da7bbcfef7 100644
--- a/web_src/js/features/comp/EditorMarkdown.ts
+++ b/web_src/js/features/comp/EditorMarkdown.ts
@@ -4,14 +4,23 @@ export function triggerEditorContentChanged(target: HTMLElement) {
target.dispatchEvent(new CustomEvent(EventEditorContentChanged, {bubbles: true}));
}
-export function textareaInsertText(textarea: HTMLTextAreaElement, value: string) {
- const startPos = textarea.selectionStart;
- const endPos = textarea.selectionEnd;
- textarea.value = textarea.value.substring(0, startPos) + value + textarea.value.substring(endPos);
- textarea.selectionStart = startPos;
- textarea.selectionEnd = startPos + value.length;
+/** replace selected text or insert text by creating a new edit history entry,
+ * e.g. CTRL-Z works after this */
+export function replaceTextareaSelection(textarea: HTMLTextAreaElement, text: string) {
+ const before = textarea.value.slice(0, textarea.selectionStart);
+ const after = textarea.value.slice(textarea.selectionEnd);
+
textarea.focus();
- triggerEditorContentChanged(textarea);
+ let success = false;
+ try {
+ success = document.execCommand('insertText', false, text); // eslint-disable-line @typescript-eslint/no-deprecated
+ } catch {}
+
+ // fall back to regular replacement
+ if (!success) {
+ textarea.value = `${before}${text}${after}`;
+ triggerEditorContentChanged(textarea);
+ }
}
type TextareaValueSelection = {
@@ -176,7 +185,7 @@ export function markdownHandleIndention(tvs: TextareaValueSelection): MarkdownHa
return {handled: true, valueSelection: {value: linesBuf.lines.join('\n'), selStart: newPos, selEnd: newPos}};
}
-function handleNewline(textarea: HTMLTextAreaElement, e: Event) {
+function handleNewline(textarea: HTMLTextAreaElement, e: KeyboardEvent) {
const ret = markdownHandleIndention({value: textarea.value, selStart: textarea.selectionStart, selEnd: textarea.selectionEnd});
if (!ret.handled || !ret.valueSelection) return; // FIXME: the "handled" seems redundant, only valueSelection is enough (null for unhandled)
e.preventDefault();
@@ -185,6 +194,28 @@ function handleNewline(textarea: HTMLTextAreaElement, e: Event) {
triggerEditorContentChanged(textarea);
}
+// Keys that act as dead keys will not work because the spec dictates that such keys are
+// emitted as `Dead` in e.key instead of the actual key.
+const pairs = new Map([
+ ["'", "'"],
+ ['"', '"'],
+ ['`', '`'],
+ ['(', ')'],
+ ['[', ']'],
+ ['{', '}'],
+ ['<', '>'],
+]);
+
+function handlePairCharacter(textarea: HTMLTextAreaElement, e: KeyboardEvent): void {
+ const selStart = textarea.selectionStart;
+ const selEnd = textarea.selectionEnd;
+ if (selEnd === selStart) return; // do not process when no selection
+ e.preventDefault();
+ const inner = textarea.value.substring(selStart, selEnd);
+ replaceTextareaSelection(textarea, `${e.key}${inner}${pairs.get(e.key)}`);
+ textarea.setSelectionRange(selStart + 1, selEnd + 1);
+}
+
function isTextExpanderShown(textarea: HTMLElement): boolean {
return Boolean(textarea.closest('text-expander')?.querySelector('.suggestions'));
}
@@ -198,6 +229,8 @@ export function initTextareaMarkdown(textarea: HTMLTextAreaElement) {
} else if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey) {
// use Enter to insert a new line with the same indention and prefix
handleNewline(textarea, e);
+ } else if (pairs.has(e.key)) {
+ handlePairCharacter(textarea, e);
}
});
}
diff --git a/web_src/js/features/comp/EditorUpload.ts b/web_src/js/features/comp/EditorUpload.ts
index 92593e7092..6aff4242ba 100644
--- a/web_src/js/features/comp/EditorUpload.ts
+++ b/web_src/js/features/comp/EditorUpload.ts
@@ -1,5 +1,5 @@
import {imageInfo} from '../../utils/image.ts';
-import {textareaInsertText, triggerEditorContentChanged} from './EditorMarkdown.ts';
+import {replaceTextareaSelection, triggerEditorContentChanged} from './EditorMarkdown.ts';
import {
DropzoneCustomEventRemovedFile,
DropzoneCustomEventUploadDone,
@@ -43,7 +43,7 @@ class TextareaEditor {
}
insertPlaceholder(value: string) {
- textareaInsertText(this.editor, value);
+ replaceTextareaSelection(this.editor, value);
}
replacePlaceholder(oldVal: string, newVal: string) {