Files
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the
Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in
NOTICE, alongside LICENSE. The header lint accepts any copyright line above the
SPDX line, so it no longer demands an upstream one.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 17:50:35 -07:00

136 lines
5.5 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"context"
"fmt"
"path"
"strconv"
"strings"
git_model "github.com/hanzoai/git/models/git"
repo_model "github.com/hanzoai/git/models/repo"
user_model "github.com/hanzoai/git/models/user"
"github.com/hanzoai/git/modules/git"
"github.com/hanzoai/git/modules/gitrepo"
"github.com/hanzoai/git/modules/log"
"github.com/hanzoai/git/modules/markup"
repo_module "github.com/hanzoai/git/modules/repository"
"github.com/hanzoai/git/modules/setting"
"github.com/hanzoai/git/modules/util"
context_service "github.com/hanzoai/git/services/context"
)
// getUniquePatchBranchName Gets a unique branch name for a new patch branch
// It will be in the form of <username>-patch-<num> where <num> 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(ctx context.Context, 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(ctx, gitRepo, treePath); err != nil {
return f(path.Dir(treePath), commit) // failed to get the tree, going up a dir
} else if entries, err := tree.ListEntries(ctx, gitRepo); 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(ctx)
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 -<num> 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),
})
}