diff options
author | 6543 <6543@obermui.de> | 2021-02-03 20:06:13 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-03 20:06:13 +0100 |
commit | 0d1444751f755c624ffb4c56cb0020ce7a083c77 (patch) | |
tree | c00ab7d4a1b120f4fb4a70db134e5d712bef91ed /modules | |
parent | c295a27d4a8ef9a3a75280306e1ed94daa3b001d (diff) | |
download | gitea-0d1444751f755c624ffb4c56cb0020ce7a083c77.tar.gz gitea-0d1444751f755c624ffb4c56cb0020ce7a083c77.zip |
[API] Add pagination to ListBranches (#14524)
* make PaginateUserSlice generic -> PaginateSlice
* Add pagination to ListBranches
* add skip, limit to Repository.GetBranches()
* Move routers/api/v1/utils/utils PaginateSlice -> modules/util/paginate.go
* repo_module.GetBranches paginate
* fix & rename & more logging
* better description
Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: a1012112796 <1012112796@qq.com>
Diffstat (limited to 'modules')
-rw-r--r-- | modules/context/repo.go | 4 | ||||
-rw-r--r-- | modules/git/repo_branch.go | 11 | ||||
-rw-r--r-- | modules/git/repo_branch_gogit.go | 19 | ||||
-rw-r--r-- | modules/git/repo_branch_nogogit.go | 50 | ||||
-rw-r--r-- | modules/git/repo_branch_test.go | 19 | ||||
-rw-r--r-- | modules/git/repo_tag_nogogit.go | 5 | ||||
-rw-r--r-- | modules/repository/branch.go | 12 | ||||
-rw-r--r-- | modules/repository/init.go | 2 | ||||
-rw-r--r-- | modules/util/paginate.go | 34 | ||||
-rw-r--r-- | modules/util/paginate_test.go | 47 |
10 files changed, 172 insertions, 31 deletions
diff --git a/modules/context/repo.go b/modules/context/repo.go index 13037f4625..bf149b8158 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -554,7 +554,7 @@ func RepoAssignment() func(http.Handler) http.Handler { } ctx.Data["Tags"] = tags - brs, err := ctx.Repo.GitRepo.GetBranches() + brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0) if err != nil { ctx.ServerError("GetBranches", err) return @@ -747,7 +747,7 @@ func RepoRefByType(refType RepoRefType) func(http.Handler) http.Handler { refName = ctx.Repo.Repository.DefaultBranch ctx.Repo.BranchName = refName if !ctx.Repo.GitRepo.IsBranchExist(refName) { - brs, err := ctx.Repo.GitRepo.GetBranches() + brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0) if err != nil { ctx.ServerError("GetBranches", err) return diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 25438530f5..58781eb1c7 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -78,16 +78,17 @@ func (repo *Repository) GetBranch(branch string) (*Branch, error) { } // GetBranchesByPath returns a branch by it's path -func GetBranchesByPath(path string) ([]*Branch, error) { +// if limit = 0 it will not limit +func GetBranchesByPath(path string, skip, limit int) ([]*Branch, int, error) { gitRepo, err := OpenRepository(path) if err != nil { - return nil, err + return nil, 0, err } defer gitRepo.Close() - brs, err := gitRepo.GetBranches() + brs, countAll, err := gitRepo.GetBranches(skip, limit) if err != nil { - return nil, err + return nil, 0, err } branches := make([]*Branch, len(brs)) @@ -99,7 +100,7 @@ func GetBranchesByPath(path string) ([]*Branch, error) { } } - return branches, nil + return branches, countAll, nil } // DeleteBranchOptions Option(s) for delete branch diff --git a/modules/git/repo_branch_gogit.go b/modules/git/repo_branch_gogit.go index 65cb77a8b5..b00253f6ff 100644 --- a/modules/git/repo_branch_gogit.go +++ b/modules/git/repo_branch_gogit.go @@ -25,21 +25,32 @@ func (repo *Repository) IsBranchExist(name string) bool { return reference.Type() != plumbing.InvalidReference } -// GetBranches returns all branches of the repository. -func (repo *Repository) GetBranches() ([]string, error) { +// GetBranches returns branches from the repository, skipping skip initial branches and +// returning at most limit branches, or all branches if limit is 0. +func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) { var branchNames []string branches, err := repo.gogitRepo.Branches() if err != nil { - return nil, err + return nil, 0, err } + i := 0 + count := 0 _ = branches.ForEach(func(branch *plumbing.Reference) error { + count++ + if i < skip { + i++ + return nil + } else if limit != 0 && count > skip+limit { + return nil + } + branchNames = append(branchNames, strings.TrimPrefix(branch.Name().String(), BranchPrefix)) return nil }) // TODO: Sort? - return branchNames, nil + return branchNames, count, nil } diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index 5ec46d725e..0628a57285 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -21,14 +21,14 @@ func (repo *Repository) IsBranchExist(name string) bool { return IsReferenceExist(repo.Path, BranchPrefix+name) } -// GetBranches returns all branches of the repository. -func (repo *Repository) GetBranches() ([]string, error) { - return callShowRef(repo.Path, BranchPrefix, "--heads") +// GetBranches returns branches from the repository, skipping skip initial branches and +// returning at most limit branches, or all branches if limit is 0. +func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) { + return callShowRef(repo.Path, BranchPrefix, "--heads", skip, limit) } -func callShowRef(repoPath, prefix, arg string) ([]string, error) { - var branchNames []string - +// 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() defer func() { _ = stdoutReader.Close() @@ -49,8 +49,21 @@ func callShowRef(repoPath, prefix, arg string) ([]string, error) { } }() + i := 0 bufReader := bufio.NewReader(stdoutReader) - for { + for i < skip { + _, isPrefix, err := bufReader.ReadLine() + if err == io.EOF { + return branchNames, i, nil + } + if err != nil { + return nil, 0, err + } + if !isPrefix { + i++ + } + } + for limit == 0 || i < skip+limit { // The output of show-ref is simply a list: // <sha> SP <ref> LF _, err := bufReader.ReadSlice(' ') @@ -59,24 +72,39 @@ func callShowRef(repoPath, prefix, arg string) ([]string, error) { _, err = bufReader.ReadSlice(' ') } if err == io.EOF { - return branchNames, nil + return branchNames, i, nil } if err != nil { - return nil, err + return nil, 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 branchNames, nil + return branchNames, i, nil } if err != nil { - return nil, err + return nil, i, err } branchName = strings.TrimPrefix(branchName, prefix) if len(branchName) > 0 { branchName = branchName[:len(branchName)-1] } branchNames = append(branchNames, branchName) + i++ + } + // count all refs + for limit != 0 { + _, isPrefix, err := bufReader.ReadLine() + if err == io.EOF { + return branchNames, i, nil + } + if err != nil { + return nil, 0, err + } + if !isPrefix { + i++ + } } + return branchNames, i, nil } diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go index 33d31aef68..05d5237e6a 100644 --- a/modules/git/repo_branch_test.go +++ b/modules/git/repo_branch_test.go @@ -17,11 +17,26 @@ func TestRepository_GetBranches(t *testing.T) { assert.NoError(t, err) defer bareRepo1.Close() - branches, err := bareRepo1.GetBranches() + branches, countAll, err := bareRepo1.GetBranches(0, 2) + + assert.NoError(t, err) + assert.Len(t, branches, 2) + assert.EqualValues(t, 3, countAll) + assert.ElementsMatch(t, []string{"branch1", "branch2"}, branches) + + branches, countAll, err = bareRepo1.GetBranches(0, 0) assert.NoError(t, err) assert.Len(t, branches, 3) + assert.EqualValues(t, 3, countAll) assert.ElementsMatch(t, []string{"branch1", "branch2", "master"}, branches) + + branches, countAll, err = bareRepo1.GetBranches(5, 1) + + assert.NoError(t, err) + assert.Len(t, branches, 0) + assert.EqualValues(t, 3, countAll) + assert.ElementsMatch(t, []string{}, branches) } func BenchmarkRepository_GetBranches(b *testing.B) { @@ -33,7 +48,7 @@ func BenchmarkRepository_GetBranches(b *testing.B) { defer bareRepo1.Close() for i := 0; i < b.N; i++ { - _, err := bareRepo1.GetBranches() + _, _, err := bareRepo1.GetBranches(0, 0) if err != nil { b.Fatal(err) } diff --git a/modules/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go index 83cbc58e34..b3fa5d6dc4 100644 --- a/modules/git/repo_tag_nogogit.go +++ b/modules/git/repo_tag_nogogit.go @@ -13,6 +13,7 @@ func (repo *Repository) IsTagExist(name string) bool { } // GetTags returns all tags of the repository. -func (repo *Repository) GetTags() ([]string, error) { - return callShowRef(repo.Path, TagPrefix, "--tags") +func (repo *Repository) GetTags() (tags []string, err error) { + tags, _, err = callShowRef(repo.Path, TagPrefix, "--tags", 0, 0) + return } diff --git a/modules/repository/branch.go b/modules/repository/branch.go index d369a200b0..275bae91e3 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -13,6 +13,9 @@ import ( // GetBranch returns a branch by its name func GetBranch(repo *models.Repository, branch string) (*git.Branch, error) { + if len(branch) == 0 { + return nil, fmt.Errorf("GetBranch: empty string for branch") + } gitRepo, err := git.OpenRepository(repo.RepoPath()) if err != nil { return nil, err @@ -22,9 +25,10 @@ func GetBranch(repo *models.Repository, branch string) (*git.Branch, error) { return gitRepo.GetBranch(branch) } -// GetBranches returns all the branches of a repository -func GetBranches(repo *models.Repository) ([]*git.Branch, error) { - return git.GetBranchesByPath(repo.RepoPath()) +// GetBranches returns branches from the repository, skipping skip initial branches and +// returning at most limit branches, or all branches if limit is 0. +func GetBranches(repo *models.Repository, skip, limit int) ([]*git.Branch, int, error) { + return git.GetBranchesByPath(repo.RepoPath(), skip, limit) } // checkBranchName validates branch name with existing repository branches @@ -35,7 +39,7 @@ func checkBranchName(repo *models.Repository, name string) error { } defer gitRepo.Close() - branches, err := GetBranches(repo) + branches, _, err := GetBranches(repo, 0, 0) if err != nil { return err } diff --git a/modules/repository/init.go b/modules/repository/init.go index a100456e77..50cde4c0b9 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -239,7 +239,7 @@ func adoptRepository(ctx models.DBContext, repoPath string, u *models.User, repo repo.DefaultBranch = strings.TrimPrefix(repo.DefaultBranch, git.BranchPrefix) } - branches, _ := gitRepo.GetBranches() + branches, _, _ := gitRepo.GetBranches(0, 0) found := false hasDefault := false hasMaster := false diff --git a/modules/util/paginate.go b/modules/util/paginate.go new file mode 100644 index 0000000000..2baa71664e --- /dev/null +++ b/modules/util/paginate.go @@ -0,0 +1,34 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package util + +import "reflect" + +// PaginateSlice cut a slice as per pagination options +// if page = 0 it do not paginate +func PaginateSlice(list interface{}, page, pageSize int) interface{} { + if page <= 0 || pageSize <= 0 { + return list + } + if reflect.TypeOf(list).Kind() != reflect.Slice { + return list + } + + listValue := reflect.ValueOf(list) + + page-- + + if page*pageSize >= listValue.Len() { + return listValue.Slice(listValue.Len(), listValue.Len()).Interface() + } + + listValue = listValue.Slice(page*pageSize, listValue.Len()) + + if listValue.Len() > pageSize { + return listValue.Slice(0, pageSize).Interface() + } + + return listValue.Interface() +} diff --git a/modules/util/paginate_test.go b/modules/util/paginate_test.go new file mode 100644 index 0000000000..d962e04c16 --- /dev/null +++ b/modules/util/paginate_test.go @@ -0,0 +1,47 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPaginateSlice(t *testing.T) { + stringSlice := []string{"a", "b", "c", "d", "e"} + result, ok := PaginateSlice(stringSlice, 1, 2).([]string) + assert.True(t, ok) + assert.EqualValues(t, []string{"a", "b"}, result) + + result, ok = PaginateSlice(stringSlice, 100, 2).([]string) + assert.True(t, ok) + assert.EqualValues(t, []string{}, result) + + result, ok = PaginateSlice(stringSlice, 3, 2).([]string) + assert.True(t, ok) + assert.EqualValues(t, []string{"e"}, result) + + result, ok = PaginateSlice(stringSlice, 1, 0).([]string) + assert.True(t, ok) + assert.EqualValues(t, []string{"a", "b", "c", "d", "e"}, result) + + result, ok = PaginateSlice(stringSlice, 1, -1).([]string) + assert.True(t, ok) + assert.EqualValues(t, []string{"a", "b", "c", "d", "e"}, result) + + type Test struct { + Val int + } + + var testVar = []*Test{{Val: 2}, {Val: 3}, {Val: 4}} + testVar, ok = PaginateSlice(testVar, 1, 50).([]*Test) + assert.True(t, ok) + assert.EqualValues(t, []*Test{{Val: 2}, {Val: 3}, {Val: 4}}, testVar) + + testVar, ok = PaginateSlice(testVar, 2, 2).([]*Test) + assert.True(t, ok) + assert.EqualValues(t, []*Test{{Val: 4}}, testVar) +} |