From 0ec78a8da42578665282fbff787e60a7410ef292 Mon Sep 17 00:00:00 2001 From: Andrew Thornton <art27@cantab.net> Date: Wed, 1 Dec 2021 20:10:43 +0000 Subject: [PATCH 1/8] Improve checkBranchName The current implementation of checkBranchName is highly inefficient involving opening the repository, the listing all of the branch names checking them individually before then using using opened repo to get the tags. This PR avoids this by simply walking the references from show-ref instead of opening the repository (in the nogogit case). Signed-off-by: Andrew Thornton <art27@cantab.net> --- modules/git/repo_branch_gogit.go | 24 ++++++++++++++ modules/git/repo_branch_nogogit.go | 50 +++++++++++++++++++++--------- services/repository/branch.go | 46 +++++++++++---------------- 3 files changed, 78 insertions(+), 42 deletions(-) diff --git a/modules/git/repo_branch_gogit.go b/modules/git/repo_branch_gogit.go index 6bf14b3999865..4b7d8421bb125 100644 --- a/modules/git/repo_branch_gogit.go +++ b/modules/git/repo_branch_gogit.go @@ -9,6 +9,7 @@ package git import ( + "context" "strings" "github.com/go-git/go-git/v5/plumbing" @@ -79,3 +80,26 @@ func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) { return branchNames, count, nil } + +// WalkReferences walks all the references from the repository +func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) error) (int, error) { + repo, err := OpenRepositoryCtx(ctx, repoPath) + if err != nil { + return 0, err + } + defer repo.Close() + + i := 0 + iter, err := repo.gogitRepo.References() + if err != nil { + return i, err + } + defer iter.Close() + + err = iter.ForEach(func(ref *plumbing.Reference) error { + err := walkfn(string(ref.Name())) + i++ + return err + }) + return i, err +} diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index 1928c7515bcab..1584a7b49d88f 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -67,8 +67,26 @@ func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) { return callShowRef(repo.Ctx, repo.Path, BranchPrefix, "--heads", skip, limit) } +// WalkReferences walks all the references from the repository +func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) error) (int, error) { + return walkShowRef(ctx, repoPath, "", 0, 0, walkfn) +} + // callShowRef return refs, if limit = 0 it will not limit func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) { + countAll, err = walkShowRef(ctx, repoPath, arg, limit, skip, func(branchName string) error { + branchName = strings.TrimPrefix(branchName, prefix) + if len(branchName) > 0 { + branchName = branchName[:len(branchName)-1] + } + branchNames = append(branchNames, branchName) + + return nil + }) + return +} + +func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(string) error) (countAll int, err error) { stdoutReader, stdoutWriter := io.Pipe() defer func() { _ = stdoutReader.Close() @@ -77,7 +95,11 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit go func() { stderrBuilder := &strings.Builder{} - err := NewCommandContext(ctx, "show-ref", arg).RunInDirPipeline(repoPath, stdoutWriter, stderrBuilder) + args := []string{"show-ref"} + if arg != "" { + args = append(args, arg) + } + err := NewCommandContext(ctx, args...).RunInDirPipeline(repoPath, stdoutWriter, stderrBuilder) if err != nil { if stderrBuilder.Len() == 0 { _ = stdoutWriter.Close() @@ -94,10 +116,10 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit for i < skip { _, isPrefix, err := bufReader.ReadLine() if err == io.EOF { - return branchNames, i, nil + return i, nil } if err != nil { - return nil, 0, err + return 0, err } if !isPrefix { i++ @@ -112,39 +134,39 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit _, err = bufReader.ReadSlice(' ') } if err == io.EOF { - return branchNames, i, nil + return i, nil } if err != nil { - return nil, 0, err + 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 branchNames, i, nil + return i, nil } if err != nil { - return nil, i, err + return i, err } - branchName = strings.TrimPrefix(branchName, prefix) - if len(branchName) > 0 { - branchName = branchName[:len(branchName)-1] + + err = walkfn(branchName) + if err != nil { + return i, err } - branchNames = append(branchNames, branchName) i++ } // count all refs for limit != 0 { _, isPrefix, err := bufReader.ReadLine() if err == io.EOF { - return branchNames, i, nil + return i, nil } if err != nil { - return nil, 0, err + return 0, err } if !isPrefix { i++ } } - return branchNames, i, nil + return i, nil } diff --git a/services/repository/branch.go b/services/repository/branch.go index f33bac762181c..394a87ed43862 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -5,8 +5,10 @@ package repository import ( + "context" "errors" "fmt" + "strings" "code.gitea.io/gitea/models" user_model "code.gitea.io/gitea/models/user" @@ -20,7 +22,7 @@ import ( // CreateNewBranch creates a new repository branch func CreateNewBranch(doer *user_model.User, repo *models.Repository, oldBranchName, branchName string) (err error) { // Check if branch name can be used - if err := checkBranchName(repo, branchName); err != nil { + if err := checkBranchName(git.DefaultContext, repo, branchName); err != nil { return err } @@ -65,44 +67,32 @@ func GetBranches(repo *models.Repository, skip, limit int) ([]*git.Branch, int, } // checkBranchName validates branch name with existing repository branches -func checkBranchName(repo *models.Repository, name string) error { - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return err - } - defer gitRepo.Close() - - branches, _, err := GetBranches(repo, 0, 0) - if err != nil { - return err - } - - for _, branch := range branches { - if branch.Name == name { +func checkBranchName(ctx context.Context, repo *models.Repository, name string) error { + _, err := git.WalkReferences(ctx, repo.RepoPath(), func(refName string) error { + switch { + case refName == git.BranchPrefix+name: return models.ErrBranchAlreadyExists{ - BranchName: branch.Name, + BranchName: name, } - } else if (len(branch.Name) < len(name) && branch.Name+"/" == name[0:len(branch.Name)+1]) || - (len(branch.Name) > len(name) && name+"/" == branch.Name[0:len(name)+1]) { + case strings.HasPrefix(refName, git.BranchPrefix+name+"/"): return models.ErrBranchNameConflict{ - BranchName: branch.Name, + BranchName: name, + } + case refName == git.TagPrefix+name: + return models.ErrTagAlreadyExists{ + TagName: name, } } - } - - if _, err := gitRepo.GetTag(name); err == nil { - return models.ErrTagAlreadyExists{ - TagName: name, - } - } + return nil + }) - return nil + return err } // CreateNewBranchFromCommit creates a new repository branch func CreateNewBranchFromCommit(doer *user_model.User, repo *models.Repository, commit, branchName string) (err error) { // Check if branch name can be used - if err := checkBranchName(repo, branchName); err != nil { + if err := checkBranchName(git.DefaultContext, repo, branchName); err != nil { return err } From 54d1a86a12c73a3b6e5e57265437db41b81fd4cf Mon Sep 17 00:00:00 2001 From: Andrew Thornton <art27@cantab.net> Date: Fri, 3 Dec 2021 17:54:03 +0000 Subject: [PATCH 2/8] oops Signed-off-by: Andrew Thornton <art27@cantab.net> --- modules/git/repo_branch_nogogit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index 1584a7b49d88f..73d687fa711c6 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -74,7 +74,7 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) er // callShowRef return refs, if limit = 0 it will not limit func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) { - countAll, err = walkShowRef(ctx, repoPath, arg, limit, skip, func(branchName string) error { + countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(branchName string) error { branchName = strings.TrimPrefix(branchName, prefix) if len(branchName) > 0 { branchName = branchName[:len(branchName)-1] From 9d2b525b8c9f5ec0aa394cb18c5726dc4c63dc4a Mon Sep 17 00:00:00 2001 From: Andrew Thornton <art27@cantab.net> Date: Fri, 3 Dec 2021 23:17:41 +0000 Subject: [PATCH 3/8] fix tests Signed-off-by: Andrew Thornton <art27@cantab.net> --- services/repository/branch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/repository/branch.go b/services/repository/branch.go index 394a87ed43862..b934799dc3c6d 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -70,7 +70,7 @@ func GetBranches(repo *models.Repository, skip, limit int) ([]*git.Branch, int, func checkBranchName(ctx context.Context, repo *models.Repository, name string) error { _, err := git.WalkReferences(ctx, repo.RepoPath(), func(refName string) error { switch { - case refName == git.BranchPrefix+name: + case refName == name || refName == git.BranchPrefix+name: return models.ErrBranchAlreadyExists{ BranchName: name, } From b15af7f6872a7c93d3f2c488baf9e26f134a8542 Mon Sep 17 00:00:00 2001 From: Andrew Thornton <art27@cantab.net> Date: Sat, 4 Dec 2021 00:18:01 +0000 Subject: [PATCH 4/8] fix tests again Signed-off-by: Andrew Thornton <art27@cantab.net> --- modules/git/repo_branch_nogogit.go | 3 +++ services/repository/branch.go | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index 73d687fa711c6..294e2622ec0d5 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -149,6 +149,9 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal return i, err } + if len(branchName) > 0 { + branchName = branchName[:len(branchName)-1] + } err = walkfn(branchName) if err != nil { return i, err diff --git a/services/repository/branch.go b/services/repository/branch.go index b934799dc3c6d..08310134bdf6f 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -69,14 +69,21 @@ func GetBranches(repo *models.Repository, skip, limit int) ([]*git.Branch, int, // checkBranchName validates branch name with existing repository branches func checkBranchName(ctx context.Context, repo *models.Repository, name string) error { _, err := git.WalkReferences(ctx, repo.RepoPath(), func(refName string) error { + branchRefName := strings.TrimPrefix(refName, git.BranchPrefix) switch { - case refName == name || refName == git.BranchPrefix+name: + case branchRefName == name: return models.ErrBranchAlreadyExists{ BranchName: name, } - case strings.HasPrefix(refName, git.BranchPrefix+name+"/"): + // If branchRefName like a/b but we want to create a branch named a then we have a conflict + case strings.HasPrefix(branchRefName, name+"/"): return models.ErrBranchNameConflict{ - BranchName: name, + BranchName: branchRefName, + } + // Conversely if branchRefName like a but we want to create a branch named a/b then we also have a conflict + case strings.HasPrefix(name, branchRefName+"/"): + return models.ErrBranchNameConflict{ + BranchName: branchRefName, } case refName == git.TagPrefix+name: return models.ErrTagAlreadyExists{ From 131285cd25c2f35002ee3f701ea287de4c902f6c Mon Sep 17 00:00:00 2001 From: Andrew Thornton <art27@cantab.net> Date: Fri, 3 Dec 2021 23:49:40 +0000 Subject: [PATCH 5/8] Use the common ctx.Repo.Gitrepo instead of reopening it for loadBranches --- modules/context/repo.go | 4 ++-- modules/git/repo_branch.go | 9 +++++++-- modules/git/repo_branch_nogogit.go | 4 ++-- modules/git/repo_branch_test.go | 8 ++++---- routers/web/repo/branch.go | 4 ++-- routers/web/repo/compare.go | 4 ++-- routers/web/repo/issue.go | 2 +- services/repository/adopt.go | 2 +- 8 files changed, 21 insertions(+), 16 deletions(-) diff --git a/modules/context/repo.go b/modules/context/repo.go index 159fd07d9d18b..b2844c04c4c15 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -584,7 +584,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { } ctx.Data["Tags"] = tags - brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0) + brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0) if err != nil { ctx.ServerError("GetBranches", err) return @@ -810,7 +810,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context if len(ctx.Params("*")) == 0 { refName = ctx.Repo.Repository.DefaultBranch if !ctx.Repo.GitRepo.IsBranchExist(refName) { - brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0) + brs, _, err := ctx.Repo.GitRepo.GetBranchNames(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 98b1bc8ae7c78..c9cbd062c9586 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -95,7 +95,12 @@ func GetBranchesByPath(path string, skip, limit int) ([]*Branch, int, error) { } defer gitRepo.Close() - brs, countAll, err := gitRepo.GetBranches(skip, limit) + return gitRepo.GetBranches(skip, limit) +} + +// GetBranches returns a slice of *git.Branch +func (gitRepo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) { + brs, countAll, err := gitRepo.GetBranchNames(skip, limit) if err != nil { return nil, 0, err } @@ -103,7 +108,7 @@ func GetBranchesByPath(path string, skip, limit int) ([]*Branch, int, error) { branches := make([]*Branch, len(brs)) for i := range brs { branches[i] = &Branch{ - Path: path, + Path: gitRepo.Path, Name: brs[i], gitRepo: gitRepo, } diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index 294e2622ec0d5..23c4a07f0a14b 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -61,9 +61,9 @@ func (repo *Repository) IsBranchExist(name string) bool { return repo.IsReferenceExist(BranchPrefix + name) } -// GetBranches returns branches from the repository, skipping skip initial branches and +// GetBranchNames 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) { +func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { return callShowRef(repo.Ctx, repo.Path, BranchPrefix, "--heads", skip, limit) } diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go index 05d5237e6a65b..ac5f5deea9bea 100644 --- a/modules/git/repo_branch_test.go +++ b/modules/git/repo_branch_test.go @@ -17,21 +17,21 @@ func TestRepository_GetBranches(t *testing.T) { assert.NoError(t, err) defer bareRepo1.Close() - branches, countAll, err := bareRepo1.GetBranches(0, 2) + branches, countAll, err := bareRepo1.GetBranchNames(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) + branches, countAll, err = bareRepo1.GetBranchNames(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) + branches, countAll, err = bareRepo1.GetBranchNames(5, 1) assert.NoError(t, err) assert.Len(t, branches, 0) @@ -48,7 +48,7 @@ func BenchmarkRepository_GetBranches(b *testing.B) { defer bareRepo1.Close() for i := 0; i < b.N; i++ { - _, _, err := bareRepo1.GetBranches(0, 0) + _, _, err := bareRepo1.GetBranchNames(0, 0) if err != nil { b.Fatal(err) } diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index 05b45eba4b204..9c2518059699d 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -165,14 +165,14 @@ func redirect(ctx *context.Context) { // loadBranches loads branches from the repository limited by page & pageSize. // NOTE: May write to context on error. func loadBranches(ctx *context.Context, skip, limit int) ([]*Branch, int) { - defaultBranch, err := repo_service.GetBranch(ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch) + defaultBranch, err := ctx.Repo.GitRepo.GetBranch(ctx.Repo.Repository.DefaultBranch) if err != nil { log.Error("loadBranches: get default branch: %v", err) ctx.ServerError("GetDefaultBranch", err) return nil, 0 } - rawBranches, totalNumOfBranches, err := repo_service.GetBranches(ctx.Repo.Repository, skip, limit) + rawBranches, totalNumOfBranches, err := ctx.Repo.GitRepo.GetBranches(skip, limit) if err != nil { log.Error("GetBranches: %v", err) ctx.ServerError("GetBranches", err) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 865a734caf2fb..842c0b06debaa 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -654,7 +654,7 @@ func getBranchesAndTagsForRepo(repo *models.Repository) (branches, tags []string } defer gitRepo.Close() - branches, _, err = gitRepo.GetBranches(0, 0) + branches, _, err = gitRepo.GetBranchNames(0, 0) if err != nil { return nil, nil, err } @@ -705,7 +705,7 @@ func CompareDiff(ctx *context.Context) { return } - headBranches, _, err := ci.HeadGitRepo.GetBranches(0, 0) + headBranches, _, err := ci.HeadGitRepo.GetBranchNames(0, 0) if err != nil { ctx.ServerError("GetBranches", err) return diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index f0857b18c0eb6..398aa26cc49dc 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -690,7 +690,7 @@ func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository, isPull boo return nil } - brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0) + brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0) if err != nil { ctx.ServerError("GetBranches", err) return nil diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 3f4045a778310..5503155ab0384 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -142,7 +142,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r repo.DefaultBranch = strings.TrimPrefix(repo.DefaultBranch, git.BranchPrefix) } - branches, _, _ := gitRepo.GetBranches(0, 0) + branches, _, _ := gitRepo.GetBranchNames(0, 0) found := false hasDefault := false hasMaster := false From 1c1312dca1e2858d765baf73dbd5f473c579fcf3 Mon Sep 17 00:00:00 2001 From: Andrew Thornton <art27@cantab.net> Date: Sat, 4 Dec 2021 00:24:55 +0000 Subject: [PATCH 6/8] another fixup Signed-off-by: Andrew Thornton <art27@cantab.net> --- modules/git/repo_branch_nogogit.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index 23c4a07f0a14b..55952acda4a3d 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -76,9 +76,6 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) er func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) { countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(branchName string) error { branchName = strings.TrimPrefix(branchName, prefix) - if len(branchName) > 0 { - branchName = branchName[:len(branchName)-1] - } branchNames = append(branchNames, branchName) return nil From 7b459b7cc1493adec221752e7d63960032f4b270 Mon Sep 17 00:00:00 2001 From: Andrew Thornton <art27@cantab.net> Date: Sat, 4 Dec 2021 00:41:24 +0000 Subject: [PATCH 7/8] placate lint Signed-off-by: Andrew Thornton <art27@cantab.net> --- modules/git/repo_branch.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index c9cbd062c9586..01933d7ade6a0 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -99,8 +99,8 @@ func GetBranchesByPath(path string, skip, limit int) ([]*Branch, int, error) { } // GetBranches returns a slice of *git.Branch -func (gitRepo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) { - brs, countAll, err := gitRepo.GetBranchNames(skip, limit) +func (repo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) { + brs, countAll, err := repo.GetBranchNames(skip, limit) if err != nil { return nil, 0, err } @@ -108,9 +108,9 @@ func (gitRepo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) branches := make([]*Branch, len(brs)) for i := range brs { branches[i] = &Branch{ - Path: gitRepo.Path, + Path: repo.Path, Name: brs[i], - gitRepo: gitRepo, + gitRepo: repo, } } From 4ed5af22e8041bf60ed756f9d4e216809655147a Mon Sep 17 00:00:00 2001 From: Andrew Thornton <art27@cantab.net> Date: Sat, 4 Dec 2021 00:45:38 +0000 Subject: [PATCH 8/8] placate lint2 Signed-off-by: Andrew Thornton <art27@cantab.net> --- modules/git/repo_branch_gogit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/git/repo_branch_gogit.go b/modules/git/repo_branch_gogit.go index 4b7d8421bb125..d159aafd6f3bb 100644 --- a/modules/git/repo_branch_gogit.go +++ b/modules/git/repo_branch_gogit.go @@ -53,7 +53,7 @@ func (repo *Repository) IsBranchExist(name string) bool { // 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) { +func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { var branchNames []string branches, err := repo.gogitRepo.Branches()