Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gitserver: grpc: add new ChangedFiles method to gitserver client #62354

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 4 additions & 6 deletions cmd/gitserver/internal/server_grpc_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ import (
"sync/atomic"
"time"

"google.golang.org/grpc/codes"

"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/urlredactor"
"github.com/sourcegraph/sourcegraph/internal/grpc/grpcutil"
"github.com/sourcegraph/sourcegraph/internal/vcs"

"github.com/sourcegraph/log"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/urlredactor"
proto "github.com/sourcegraph/sourcegraph/internal/gitserver/v1"
"github.com/sourcegraph/sourcegraph/internal/grpc/grpcutil"
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/internal/vcs"
)

// loggingGRPCServer is a wrapper around the provided GitserverServiceServer
Expand Down
131 changes: 131 additions & 0 deletions internal/batches/sources/mocks_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions internal/gitserver/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"io/fs"
"strings"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -439,6 +440,14 @@ type Client interface {
// If the specified commit does not exist, a RevisionNotFoundError is returned.
NewFileReader(ctx context.Context, repo api.RepoName, commit api.CommitID, name string) (io.ReadCloser, error)

// ChangedFiles returns the list of files that have been added, modified, or
// deleted in the entire repository between the two given <tree-ish> identifiers (e.g., commit, branch, tag).
//
// If base is omitted, the parent of head is used as the base.
//
// If either the base or head <tree-ish> id does not exist, a gitdomain.RevisionNotFoundError is returned.
ChangedFiles(ctx context.Context, repo api.RepoName, base, head string) (ChangedFilesIterator, error)

// DiffSymbols performs a diff command which is expected to be parsed by our symbols package
DiffSymbols(ctx context.Context, repo api.RepoName, commitA, commitB api.CommitID) ([]byte, error)

Expand Down Expand Up @@ -1190,3 +1199,60 @@ func (c *clientImplementor) ListGitoliteRepos(ctx context.Context, gitoliteHost

return list, nil
}

// ChangedFilesIterator is an iterator for retrieving the status of changed files in a Git repository.
// It allows iterating over the changed files and retrieving their paths and statuses.
//
// The caller must ensure that they call Close() when the iterator is no longer needed to release any associated resources.
type ChangedFilesIterator interface {
// Next returns the next changed file's path and status.
//
// If there are no more changed files, Next returns an io.EOF error.
// If an error occurs during iteration, Next returns the error that occurred.
Next() (gitdomain.PathStatus, error)

// Close closes the iterator and releases any associated resources.
//
// It is important to call Close when the iterator is no longer needed to avoid resource leaks.
// After calling Close, any subsequent calls to Next will return an io.EOF error.
Close()
}

// NewChangedFilesIteratorFromSlice returns a new ChangedFilesIterator that iterates over the given slice of changed files (in order),
// which is useful for testing.
func NewChangedFilesIteratorFromSlice(files []gitdomain.PathStatus) ChangedFilesIterator {
return &changedFilesSliceIterator{files: files}
}

type changedFilesSliceIterator struct {
mu sync.Mutex
files []gitdomain.PathStatus
closed bool
}

func (c *changedFilesSliceIterator) Next() (gitdomain.PathStatus, error) {
c.mu.Lock()
defer c.mu.Unlock()

if c.closed {
return gitdomain.PathStatus{}, io.EOF
}

if len(c.files) == 0 {
return gitdomain.PathStatus{}, io.EOF
}

file := c.files[0]
c.files = c.files[1:]

return file, nil
}

func (c *changedFilesSliceIterator) Close() {
c.mu.Lock()
defer c.mu.Unlock()

c.closed = true
}

var _ ChangedFilesIterator = &changedFilesSliceIterator{}
47 changes: 47 additions & 0 deletions internal/gitserver/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gitserver_test

import (
"context"
"io"
"math/rand"
"os/exec"
"path/filepath"
Expand All @@ -20,6 +21,7 @@ import (
"github.com/sourcegraph/sourcegraph/internal/api"
"github.com/sourcegraph/sourcegraph/internal/extsvc/gitolite"
"github.com/sourcegraph/sourcegraph/internal/gitserver"
"github.com/sourcegraph/sourcegraph/internal/gitserver/gitdomain"
"github.com/sourcegraph/sourcegraph/internal/gitserver/protocol"
proto "github.com/sourcegraph/sourcegraph/internal/gitserver/v1"
)
Expand Down Expand Up @@ -397,3 +399,48 @@ func (fuzzTime) Generate(rand *rand.Rand, _ int) reflect.Value {
}

var _ quick.Generator = fuzzTime{}

func TestNewChangedFilesIteratorFromSlice(t *testing.T) {
t.Run("IterateThroughFiles", func(t *testing.T) {
files := []gitdomain.PathStatus{
{Path: "file1.txt", Status: gitdomain.AddedAMD},
{Path: "file2.txt", Status: gitdomain.ModifiedAMD},
{Path: "file3.txt", Status: gitdomain.DeletedAMD},
}

iter := gitserver.NewChangedFilesIteratorFromSlice(files)
defer iter.Close()

for i := 0; i < len(files); i++ {
file, err := iter.Next()
require.NoError(t, err)
require.Equal(t, files[i], file)
}

_, err := iter.Next()
require.Equal(t, io.EOF, err)
})

t.Run("EmptySlice", func(t *testing.T) {
iter := gitserver.NewChangedFilesIteratorFromSlice(nil)
defer iter.Close()

_, err := iter.Next()
require.Equal(t, io.EOF, err)
})

t.Run("Close", func(t *testing.T) {
files := []gitdomain.PathStatus{
{Path: "file1.txt", Status: gitdomain.AddedAMD},
}

iter := gitserver.NewChangedFilesIteratorFromSlice(files)

// Close should be idempotent
iter.Close()
iter.Close()

_, err := iter.Next()
require.Equal(t, io.EOF, err)
})
}