diff --git a/modules/context/repo.go b/modules/context/repo.go index 3bdc34f0d..b8b4c961b 100755 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -532,7 +532,7 @@ func RepoAssignment() macaron.Handler { duration := time.Since(startTime) log.Info("GetTags cost: %v seconds", duration.Seconds()) - brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0) + brs, total, err := ctx.Repo.GitRepo.GetBranchNames(0, 15) if err != nil { ctx.ServerError("GetBranches", err) return @@ -542,7 +542,7 @@ func RepoAssignment() macaron.Handler { log.Info("GetBranches cost: %v seconds", duration.Seconds()) ctx.Data["Branches"] = brs - ctx.Data["BranchesCount"] = len(brs) + ctx.Data["BranchesCount"] = total ctx.Data["TagName"] = ctx.Repo.TagName diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index ad5acfdbf..bb48223c5 100755 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -173,6 +173,106 @@ func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) { return callShowRef(repo.Path, BranchPrefix, "--heads", skip, limit) } +func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { + return callShowRefNew(repo.Path, BranchPrefix, []string{BranchPrefix, "--sort=-committerdate"}, skip, limit) +} + +func callShowRefNew(repoPath, trimPrefix string, extraArgs []string, skip, limit int) (branchNames []string, countAll int, err error) { + countAll, err = walkShowRef(repoPath, extraArgs, skip, limit, func(_, branchName string) error { + branchName = strings.TrimPrefix(branchName, trimPrefix) + branchNames = append(branchNames, branchName) + return nil + }) + return branchNames, countAll, err +} + +func walkShowRef(repoPath string, extraArgs []string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) { + stdoutReader, stdoutWriter := io.Pipe() + defer func() { + _ = stdoutReader.Close() + _ = stdoutWriter.Close() + }() + + go func() { + stderrBuilder := &strings.Builder{} + args := []string{"for-each-ref", "--format=%(objectname) %(refname)"} + args = append(args, extraArgs...) + err := NewCommand(args...).RunInDirPipeline(repoPath, stdoutWriter, 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 { + _, isPrefix, err := bufReader.ReadLine() + if err == io.EOF { + return i, nil + } + if err != nil { + return 0, err + } + if !isPrefix { + i++ + } + } + for limit == 0 || i < skip+limit { + // The output of show-ref is simply a list: + // SP LF + sha, err := bufReader.ReadString(' ') + if err == io.EOF { + return i, nil + } + if err != nil { + return 0, err + } + + branchName, err := bufReader.ReadString('\n') + if err == io.EOF { + // This shouldn't happen... but we'll tolerate it for the sake of peace + return i, nil + } + if err != nil { + return i, err + } + + if len(branchName) > 0 { + branchName = branchName[:len(branchName)-1] + } + + if len(sha) > 0 { + sha = sha[:len(sha)-1] + } + + err = walkfn(sha, branchName) + if err != nil { + return i, err + } + i++ + } + // count all refs + for limit != 0 { + _, isPrefix, err := bufReader.ReadLine() + if err == io.EOF { + return i, nil + } + if err != nil { + return 0, err + } + if !isPrefix { + i++ + } + } + return i, nil +} + // callShowRef return refs, if limit = 0 it will not limit func callShowRef(repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) { stdoutReader, stdoutWriter := io.Pipe() diff --git a/routers/repo/branch.go b/routers/repo/branch.go index c8e492373..ed6f061d3 100755 --- a/routers/repo/branch.go +++ b/routers/repo/branch.go @@ -6,6 +6,7 @@ package repo import ( + "net/http" "strings" "code.gitea.io/gitea/models" @@ -54,6 +55,26 @@ func Branches(ctx *context.Context) { ctx.HTML(200, tplBranch) } +func BranchNames(ctx *context.Context) { + + page := ctx.QueryInt("page") + pageSize := ctx.QueryInt("pageSize") + if page <= 0 { + page = 1 + } + maxPagingNum := 50 + defaultPagingNum := 15 + if pageSize == 0 || pageSize > maxPagingNum { + pageSize = defaultPagingNum + } + skip := (page - 1) * pageSize + branchNames, total, _ := ctx.Repo.GitRepo.GetBranchNames(skip, pageSize) + ctx.JSON(http.StatusOK, map[string]interface{}{ + "branches": branchNames, + "total": total, + }) +} + // DeleteBranchPost responses for delete merged branch func DeleteBranchPost(ctx *context.Context) { defer redirect(ctx) diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 31bba2aeb..5f2a5582e 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -1402,6 +1402,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/branches", func() { m.Get("", repo.Branches) + m.Get("/names", repo.BranchNames) }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) m.Group("/blob_excerpt", func() {