新增:分支列表接口支持搜索参数

This commit is contained in:
yystopf 2023-02-23 09:47:54 +08:00
parent b83c3eabc9
commit a1099096cb
4 changed files with 168 additions and 5 deletions

10
go.mod
View File

@ -8,9 +8,14 @@ require (
github.com/caddyserver/certmagic v0.17.2
github.com/felixge/fgprof v0.9.3
github.com/go-chi/cors v1.2.1
github.com/gobwas/glob v0.2.3
github.com/klauspost/cpuid/v2 v2.2.2
github.com/russross/blackfriday/v2 v2.1.0
github.com/urfave/cli v1.22.10
golang.org/x/net v0.4.0
golang.org/x/text v0.5.0
gopkg.in/ini.v1 v1.67.0
xorm.io/builder v0.3.12
xorm.io/xorm v1.3.2
)
@ -93,7 +98,6 @@ require (
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 // indirect
@ -172,7 +176,6 @@ require (
github.com/quasoft/websspi v1.1.2 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/rs/xid v1.4.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
@ -201,10 +204,8 @@ require (
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/oauth2 v0.3.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.4.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
@ -216,5 +217,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
mvdan.cc/xurls/v2 v2.4.0 // indirect
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 // indirect
xorm.io/builder v0.3.12 // indirect
)

View File

@ -0,0 +1,124 @@
package git
import (
"bufio"
"context"
"fmt"
"io"
"strings"
gitea_git "code.gitea.io/gitea/modules/git"
)
func GetSearchBranches(ctx context.Context, repo *gitea_git.Repository, search string, skip, limit int) ([]*gitea_git.Branch, int, error) {
brs, countAll, err := callShowSearchRef(ctx, repo.Path, gitea_git.BranchPrefix, "--heads", search, skip, limit)
if err != nil {
return nil, 0, err
}
branches := make([]*gitea_git.Branch, len(brs))
for i := range brs {
branches[i] = &gitea_git.Branch{
Name: brs[i],
Path: repo.Path,
}
}
return branches, countAll, nil
}
func callShowSearchRef(ctx context.Context, repoPath, prefix, arg, search string, skip, limit int) (branchNames []string, countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe()
defer func() {
_ = stdoutReader.Close()
_ = stdoutWriter.Close()
}()
go func() {
stderrBuilder := &strings.Builder{}
err := gitea_git.NewCommand(ctx, "show-ref", gitea_git.CmdArg(arg)).
Run(&gitea_git.RunOpts{
Dir: repoPath,
Stdout: stdoutWriter,
Stderr: stderrBuilder,
})
if err != nil {
if stderrBuilder.Len() == 0 {
_ = stdoutWriter.Close()
return
}
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String()))
} else {
_ = stdoutWriter.Close()
}
}()
i := 0
bufReader := bufio.NewReader(stdoutReader)
for i < skip {
line, isPrefix, err := bufReader.ReadLine()
if err == io.EOF {
return branchNames, i, nil
}
if err != nil {
return nil, 0, err
}
branchName := strings.TrimPrefix(strings.Split(string(line), " ")[1], prefix)
if len(branchName) > 0 {
branchName = branchName[:len(branchName)-1]
}
isSeached := strings.Contains(branchName, search)
if !isPrefix && isSeached {
i++
}
}
for limit == 0 || i < skip+limit {
branchName, err := bufReader.ReadString('\n')
if err == io.EOF {
// This shouldn't happen... but we'll tolerate it for the sake of peace
return branchNames, i, nil
}
if err != nil {
return nil, i, err
}
branchName = strings.TrimPrefix(strings.Split(string(branchName), " ")[1], prefix)
if len(branchName) > 0 {
branchName = branchName[:len(branchName)-1]
}
isSeached := strings.Contains(branchName, search)
if isSeached {
i++
branchNames = append(branchNames, branchName)
}
}
// count all refs
for limit != 0 {
line, isPrefix, err := bufReader.ReadLine()
if err == io.EOF {
return branchNames, i, nil
}
if err != nil {
return nil, 0, err
}
branchName := strings.TrimPrefix(strings.Split(string(line), " ")[1], prefix)
if len(branchName) > 0 {
branchName = branchName[:len(branchName)-1]
}
isSeached := strings.Contains(branchName, search)
if !isPrefix && isSeached {
i++
}
}
return branchNames, i, nil
}
func ConcatenateError(err error, stderr string) error {
if len(stderr) == 0 {
return err
}
return fmt.Errorf("%w - %s", err, stderr)
}

View File

@ -71,6 +71,7 @@ func Routers(ctx gocontext.Context) *web.Route {
m.Get("/tag_name_set", context.ReferencesGitRepo(), repo.TagNameSet)
m.Get("/branch_tag_count", context.ReferencesGitRepo(), repo.BranchTagCount)
m.Group("/branches", func() {
m.Get("", repo.ListBranches)
m.Get("/branches_slice", context.ReferencesGitRepo(), repo.ListBranchesSlice)
}, reqRepoReader(unit_model.TypeCode))
m.Group("/commits", func() {

View File

@ -9,12 +9,50 @@ import (
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
gitea_api "code.gitea.io/gitea/modules/structs"
hat_convert "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/convert"
hat_git "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/git"
"code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/git"
"code.gitea.io/gitea/routers/api/v1/utils"
)
func ListBranches(ctx *context.APIContext) {
searchName := ctx.Params("name")
listOptions := utils.GetListOptions(ctx)
skip, _ := listOptions.GetStartEnd()
branches, totalNumOfBranches, err := hat_git.GetSearchBranches(ctx, ctx.Repo.GitRepo, searchName, skip, listOptions.PageSize)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
return
}
apiBranches := make([]*gitea_api.Branch, len(branches))
for i := range branches {
c, err := branches[i].GetCommit()
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
return
}
branchProtection, err := git_model.GetProtectedBranchBy(ctx, ctx.Repo.Repository.ID, branches[i].Name)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
return
}
apiBranches[i], err = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
if err != nil {
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
return
}
}
ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize)
ctx.RespHeader().Set("X-Total-Count", fmt.Sprintf("%d", totalNumOfBranches))
ctx.RespHeader().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &apiBranches)
}
func ListBranchesSlice(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)