Faster git.GetDivergingCommits (#24482)

Using `git rev-list --left-right` is almost 2x faster than calling `git
rev-list` twice.

Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
oliverpool 2023-05-04 07:08:41 +02:00 committed by GitHub
parent 377a0a20f0
commit 75ea0d5dba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 23 deletions

View file

@ -244,35 +244,28 @@ type DivergeObject struct {
Behind int Behind int
} }
func checkDivergence(ctx context.Context, repoPath, baseBranch, targetBranch string) (int, error) { // GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
branches := fmt.Sprintf("%s..%s", baseBranch, targetBranch) func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) {
cmd := NewCommand(ctx, "rev-list", "--count").AddDynamicArguments(branches) cmd := NewCommand(ctx, "rev-list", "--count", "--left-right").
AddDynamicArguments(baseBranch + "..." + targetBranch)
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
if err != nil { if err != nil {
return -1, err return do, err
} }
outInteger, errInteger := strconv.Atoi(strings.Trim(stdout, "\n")) left, right, found := strings.Cut(strings.Trim(stdout, "\n"), "\t")
if errInteger != nil { if !found {
return -1, errInteger return do, fmt.Errorf("git rev-list output is missing a tab: %q", stdout)
}
return outInteger, nil
} }
// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch do.Behind, err = strconv.Atoi(left)
func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (DivergeObject, error) { if err != nil {
// $(git rev-list --count master..feature) commits ahead of master return do, err
ahead, errorAhead := checkDivergence(ctx, repoPath, baseBranch, targetBranch)
if errorAhead != nil {
return DivergeObject{}, errorAhead
} }
do.Ahead, err = strconv.Atoi(right)
// $(git rev-list --count feature..master) commits behind master if err != nil {
behind, errorBehind := checkDivergence(ctx, repoPath, targetBranch, baseBranch) return do, err
if errorBehind != nil {
return DivergeObject{}, errorBehind
} }
return do, nil
return DivergeObject{ahead, behind}, nil
} }
// CreateBundle create bundle content to the target path // CreateBundle create bundle content to the target path

View file

@ -4,6 +4,7 @@
package git package git
import ( import (
"context"
"path/filepath" "path/filepath"
"testing" "testing"
@ -29,3 +30,27 @@ func TestRepoIsEmpty(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, isEmpty) assert.True(t, isEmpty)
} }
func TestRepoGetDivergingCommits(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
do, err := GetDivergingCommits(context.Background(), bareRepo1Path, "master", "branch2")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 1,
Behind: 5,
}, do)
do, err = GetDivergingCommits(context.Background(), bareRepo1Path, "master", "master")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 0,
Behind: 0,
}, do)
do, err = GetDivergingCommits(context.Background(), bareRepo1Path, "master", "test")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 0,
Behind: 2,
}, do)
}