// Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package repo import ( "context" "fmt" "path" "strconv" "strings" git_model "gitea.dev/models/git" repo_model "gitea.dev/models/repo" user_model "gitea.dev/models/user" "gitea.dev/modules/git" "gitea.dev/modules/gitrepo" "gitea.dev/modules/log" "gitea.dev/modules/markup" repo_module "gitea.dev/modules/repository" "gitea.dev/modules/setting" "gitea.dev/modules/util" context_service "gitea.dev/services/context" ) // getUniquePatchBranchName Gets a unique branch name for a new patch branch // It will be in the form of -patch- where is the first branch of this format // that doesn't already exist. If we exceed 1000 tries or an error is thrown, we just return "" so the user has to // type in the branch name themselves (will be an empty field) func getUniquePatchBranchName(ctx context.Context, prefixName string, repo *repo_model.Repository) string { prefix := prefixName + "-patch-" for i := 1; i <= 1000; i++ { branchName := fmt.Sprintf("%s%d", prefix, i) if exist, err := git_model.IsBranchExist(ctx, repo.ID, branchName); err != nil { log.Error("getUniquePatchBranchName: %v", err) return "" } else if !exist { return branchName } } return "" } // getClosestParentWithFiles Recursively gets the closest path of parent in a tree that has files when a file in a tree is // deleted. It returns "" for the tree root if no parents other than the root have files. func getClosestParentWithFiles(gitRepo *git.Repository, branchName, originTreePath string) string { var f func(treePath string, commit *git.Commit) string f = func(treePath string, commit *git.Commit) string { if treePath == "" || treePath == "." { return "" } // see if the tree has entries if tree, err := commit.SubTree(treePath); err != nil { return f(path.Dir(treePath), commit) // failed to get the tree, going up a dir } else if entries, err := tree.ListEntries(); err != nil || len(entries) == 0 { return f(path.Dir(treePath), commit) // no files in this dir, going up a dir } return treePath } commit, err := gitRepo.GetBranchCommit(branchName) // must get the commit again to get the latest change if err != nil { log.Error("GetBranchCommit: %v", err) return "" } return f(originTreePath, commit) } // CodeEditorConfig is also used by frontend, defined in "codeeditor" module type CodeEditorConfig struct { Filename string `json:"filename"` // the base name, not full path Autofocus bool `json:"autofocus"` PreviewableExtensions []string `json:"previewableExtensions,omitempty"` LineWrapExtensions []string `json:"lineWrapExtensions,omitempty"` LineWrap bool `json:"lineWrap"` Previewable bool `json:"previewable,omitempty"` // the following can be read from .editorconfig if exists, or use default value IndentStyle string `json:"indentStyle"` // in most cases, keep it empty by default, detected by the source code IndentSize int `json:"indentSize"` TabWidth int `json:"tabWidth"` TrimTrailingWhitespace *bool `json:"trimTrailingWhitespace,omitempty"` } func getCodeEditorConfigByEditorconfig(ctx *context_service.Context, treePath string) CodeEditorConfig { ret := CodeEditorConfig{Filename: path.Base(treePath)} ret.PreviewableExtensions = markup.PreviewableExtensions() ret.LineWrapExtensions = setting.Repository.Editor.LineWrapExtensions ret.LineWrap = util.SliceContainsString(ret.LineWrapExtensions, path.Ext(treePath), true) ret.Previewable = util.SliceContainsString(ret.PreviewableExtensions, path.Ext(treePath), true) ec, _, err := ctx.Repo.GetEditorconfig() if err == nil { def, err := ec.GetDefinitionForFilename(treePath) if err == nil { ret.IndentStyle = util.IfZero(def.IndentStyle, ret.IndentStyle) ret.IndentSize, _ = strconv.Atoi(def.IndentSize) ret.TabWidth = def.TabWidth ret.TrimTrailingWhitespace = def.TrimTrailingWhitespace } } return ret } // getParentTreeFields returns list of parent tree names and corresponding tree paths based on given treePath. // eg: []{"a", "b", "c"}, []{"a", "a/b", "a/b/c"} // or: []{""}, []{""} for the root treePath func getParentTreeFields(treePath string) (treeNames, treePaths []string) { treeNames = strings.Split(treePath, "/") treePaths = make([]string, len(treeNames)) for i := range treeNames { treePaths[i] = strings.Join(treeNames[:i+1], "/") } return treeNames, treePaths } // getUniqueRepositoryName Gets a unique repository name for a user // It will append a - postfix if the name is already taken func getUniqueRepositoryName(ctx context.Context, ownerID int64, name string) string { uniqueName := name for i := 1; i < 1000; i++ { _, err := repo_model.GetRepositoryByName(ctx, ownerID, uniqueName) if err != nil || repo_model.IsErrRepoNotExist(err) { return uniqueName } uniqueName = fmt.Sprintf("%s-%d", name, i) i++ } return "" } func editorPushBranchToForkedRepository(ctx context.Context, doer *user_model.User, baseRepo *repo_model.Repository, baseBranchName string, targetRepo *repo_model.Repository, targetBranchName string) error { return gitrepo.Push(ctx, baseRepo, targetRepo, git.PushOptions{ Branch: baseBranchName + ":" + targetBranchName, Env: repo_module.PushingEnvironment(doer, targetRepo), }) }