gitserver: grpc: create changed files RPC implementation for gRPC server (#62262)

Part of https://github.com/sourcegraph/sourcegraph/issues/60654


This PR implements a new gitserver gRPC method, ChangedFiles that lists the files that changed between two commits alonside their added/modified/deleted status. This PR builds on the functionality exposed in https://github.com/sourcegraph/sourcegraph/pull/62252. 

## Test plan

Unit tests
This commit is contained in:
Geoffrey Gilmore 2024-05-07 10:43:40 -07:00 committed by GitHub
parent b4c835ba12
commit 5f818566d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 3216 additions and 461 deletions

View File

@ -87,13 +87,18 @@ func newGitDiffIterator(rc io.ReadCloser) git.ChangedFilesIterator {
scanner := bufio.NewScanner(rc)
scanner.Split(byteutils.ScanNullLines)
closeChan := make(chan struct{})
closer := sync.OnceValue(func() error {
return rc.Close()
err := rc.Close()
close(closeChan)
return err
})
return &gitDiffIterator{
rc: rc,
scanner: scanner,
closeChan: closeChan,
onceFuncCloser: closer,
}
}
@ -102,11 +107,24 @@ type gitDiffIterator struct {
rc io.ReadCloser
scanner *bufio.Scanner
closeChan chan struct{}
onceFuncCloser func() error
}
func (i *gitDiffIterator) Next() (gitdomain.PathStatus, error) {
select {
case <-i.closeChan:
return gitdomain.PathStatus{}, io.EOF
default:
}
for i.scanner.Scan() {
select {
case <-i.closeChan:
return gitdomain.PathStatus{}, io.EOF
default:
}
status := i.scanner.Text()
if len(status) == 0 {
continue

View File

@ -660,6 +660,32 @@ func TestGitDiffIterator(t *testing.T) {
err := iter.Close()
require.NoError(t, err)
// Verify that Next returns io.EOF after the iterator is closed
_, err = iter.Next()
require.Equal(t, io.EOF, err)
})
t.Run("close iterator during iteration", func(t *testing.T) {
input := combineBytes(
[]byte("A"), []byte{NUL}, []byte("file1.txt"), []byte{NUL},
[]byte("M"), []byte{NUL}, []byte("file2.txt"), []byte{NUL},
[]byte("D"), []byte{NUL}, []byte("file3.txt"), []byte{NUL},
)
rc := io.NopCloser(bytes.NewReader(input))
iter := newGitDiffIterator(rc)
// Iterate over the first change
_, err := iter.Next()
require.NoError(t, err)
// Close the iterator during iteration
err = iter.Close()
require.NoError(t, err)
// Verify that Next returns io.EOF after the iterator is closed
_, err = iter.Next()
require.Equal(t, io.EOF, err)
})
}

View File

@ -217,6 +217,8 @@ type ChangedFilesIterator interface {
// If there are no more files, io.EOF is returned.
Next() (gitdomain.PathStatus, error)
// Close releases resources associated with the iterator.
//
// After Close() is called, Next() will always return io.EOF.
Close() error
}

View File

@ -1439,6 +1439,87 @@ func (gs *grpcServer) BehindAhead(ctx context.Context, req *proto.BehindAheadReq
return behindAhead.ToProto(), nil
}
func (gs *grpcServer) ChangedFiles(req *proto.ChangedFilesRequest, ss proto.GitserverService_ChangedFilesServer) error {
ctx := ss.Context()
accesslog.Record(
ctx,
req.GetRepoName(),
log.String("base", string(req.GetBase())),
log.String("head", string(req.GetHead())),
)
if req.GetRepoName() == "" {
return status.New(codes.InvalidArgument, "repo must be specified").Err()
}
if len(req.GetHead()) == 0 {
return status.New(codes.InvalidArgument, "head (<tree-ish>) must be specified").Err()
}
repoName := api.RepoName(req.GetRepoName())
repoDir := gs.fs.RepoDir(repoName)
if err := gs.checkRepoExists(ctx, repoName); err != nil {
return err
}
backend := gs.getBackendFunc(repoDir, repoName)
iterator, err := backend.ChangedFiles(ctx, string(req.GetBase()), string(req.GetHead()))
if err != nil {
if gitdomain.IsRevisionNotFoundError(err) {
s, err := status.New(codes.NotFound, "revision not found").WithDetails(&proto.RevisionNotFoundPayload{
Repo: req.GetRepoName(),
Spec: err.Error(),
})
if err != nil {
return err
}
return s.Err()
}
gs.svc.LogIfCorrupt(ctx, repoName, err)
return err
}
defer iterator.Close()
chunker := chunk.New(func(paths []*proto.ChangedFile) error {
out := &proto.ChangedFilesResponse{
Files: paths,
}
return ss.Send(out)
})
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
file, err := iterator.Next()
if err == io.EOF {
break
}
if err != nil {
return errors.Wrap(err, "failed to get changed file")
}
if err := chunker.Send(file.ToProto()); err != nil {
return errors.Wrapf(err, "failed to send changed file %s", file)
}
}
if err := chunker.Flush(); err != nil {
return errors.Wrap(err, "failed to flush file chunks")
}
return nil
}
// checkRepoExists checks if a given repository is cloned on disk, and returns an
// error otherwise.
// On Sourcegraph.com, not all repos are managed by the scheduler. We thus

View File

@ -6,15 +6,17 @@ 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"
"google.golang.org/grpc/codes"
"github.com/sourcegraph/log"
"google.golang.org/grpc/status"
proto "github.com/sourcegraph/sourcegraph/internal/gitserver/v1"
"github.com/sourcegraph/sourcegraph/internal/trace"
"google.golang.org/grpc/status"
)
// loggingGRPCServer is a wrapper around the provided GitserverServiceServer
@ -1004,6 +1006,35 @@ func BehindAheadRequestToLogFields(req *proto.BehindAheadRequest) []log.Field {
}
}
func (l *loggingGRPCServer) ChangedFiles(req *proto.ChangedFilesRequest, ss proto.GitserverService_ChangedFilesServer) (err error) {
start := time.Now()
defer func() {
elapsed := time.Since(start)
doLog(
l.logger,
proto.GitserverService_ChangedFiles_FullMethodName,
status.Code(err),
trace.Context(ss.Context()).TraceID,
elapsed,
changedFilesRequestToLogFields(req)...,
)
}()
return l.base.ChangedFiles(req, ss)
}
func changedFilesRequestToLogFields(req *proto.ChangedFilesRequest) []log.Field {
return []log.Field{
log.String("repoName", req.GetRepoName()),
log.String("base", string(req.GetBase())),
log.String("head", string(req.GetHead())),
}
}
type loggingRepositoryServiceServer struct {
base proto.GitserverRepositoryServiceServer
logger log.Logger

View File

@ -906,6 +906,105 @@ func TestGRPCServer_ContributorCounts(t *testing.T) {
})
}
func TestGRPCServer_ChangedFiles(t *testing.T) {
mockSS := gitserver.NewMockGitserverService_ChangedFilesServer()
mockSS.ContextFunc.SetDefaultReturn(context.Background())
t.Run("argument validation", func(t *testing.T) {
t.Run("repo must be specified", func(t *testing.T) {
gs := &grpcServer{}
err := gs.ChangedFiles(&v1.ChangedFilesRequest{RepoName: "", Head: []byte("HEAD")}, mockSS)
require.ErrorContains(t, err, "repo must be specified")
assertGRPCStatusCode(t, err, codes.InvalidArgument)
})
t.Run("head (<tree-ish>) must be specified", func(t *testing.T) {
gs := &grpcServer{}
err := gs.ChangedFiles(&v1.ChangedFilesRequest{RepoName: "therepo"}, mockSS)
require.ErrorContains(t, err, "head (<tree-ish>) must be specified")
assertGRPCStatusCode(t, err, codes.InvalidArgument)
})
})
t.Run("checks for uncloned repo", func(t *testing.T) {
fs := gitserverfs.NewMockFS()
fs.RepoClonedFunc.SetDefaultReturn(false, nil)
locker := NewMockRepositoryLocker()
locker.StatusFunc.SetDefaultReturn("cloning", true)
gs := &grpcServer{svc: NewMockService(), fs: fs, locker: locker}
err := gs.ChangedFiles(&v1.ChangedFilesRequest{RepoName: "therepo", Base: []byte("base"), Head: []byte("head")}, mockSS)
require.Error(t, err)
assertGRPCStatusCode(t, err, codes.NotFound)
assertHasGRPCErrorDetailOfType(t, err, &proto.RepoNotFoundPayload{})
require.Contains(t, err.Error(), "repo not found")
mockassert.Called(t, fs.RepoClonedFunc)
mockassert.Called(t, locker.StatusFunc)
})
t.Run("revision not found", func(t *testing.T) {
fs := gitserverfs.NewMockFS()
// Repo is cloned, proceed!
fs.RepoClonedFunc.SetDefaultReturn(true, nil)
gs := &grpcServer{
svc: NewMockService(),
fs: fs,
getBackendFunc: func(common.GitDir, api.RepoName) git.GitBackend {
b := git.NewMockGitBackend()
b.ChangedFilesFunc.SetDefaultReturn(nil, &gitdomain.RevisionNotFoundError{Repo: "therepo", Spec: "base...head"})
return b
},
}
err := gs.ChangedFiles(&v1.ChangedFilesRequest{RepoName: "therepo", Base: []byte("base"), Head: []byte("head")}, mockSS)
require.Error(t, err)
assertGRPCStatusCode(t, err, codes.NotFound)
assertHasGRPCErrorDetailOfType(t, err, &proto.RevisionNotFoundPayload{})
require.Contains(t, err.Error(), "revision not found")
})
t.Run("e2e", func(t *testing.T) {
fs := gitserverfs.NewMockFS()
// Repo is cloned, proceed!
fs.RepoClonedFunc.SetDefaultReturn(true, nil)
b := git.NewMockGitBackend()
b.ChangedFilesFunc.SetDefaultReturn(&testChangedFilesIterator{
paths: []gitdomain.PathStatus{
{Path: "file1.txt", Status: gitdomain.AddedAMD},
{Path: "file2.txt", Status: gitdomain.ModifiedAMD},
{Path: "file3.txt", Status: gitdomain.DeletedAMD},
},
}, nil)
gs := &grpcServer{
svc: NewMockService(),
fs: fs,
getBackendFunc: func(common.GitDir, api.RepoName) git.GitBackend {
return b
},
}
cli := spawnServer(t, gs)
r, err := cli.ChangedFiles(context.Background(), &v1.ChangedFilesRequest{
RepoName: "therepo",
Base: []byte("base"),
Head: []byte("head"),
})
require.NoError(t, err)
var paths []*proto.ChangedFile
for {
msg, err := r.Recv()
if err != nil {
if err == io.EOF {
break
}
require.NoError(t, err)
}
paths = append(paths, msg.GetFiles()...)
}
if diff := cmp.Diff([]*proto.ChangedFile{
{Path: []byte("file1.txt"), Status: proto.ChangedFile_STATUS_ADDED},
{Path: []byte("file2.txt"), Status: proto.ChangedFile_STATUS_MODIFIED},
{Path: []byte("file3.txt"), Status: proto.ChangedFile_STATUS_DELETED},
}, paths, protocmp.Transform()); diff != "" {
t.Fatalf("unexpected response (-want +got):\n%s", diff)
}
})
}
func TestGRPCServer_FirstCommitEver(t *testing.T) {
ctx := context.Background()
@ -1106,3 +1205,20 @@ func spawnServer(t *testing.T, server *grpcServer) proto.GitserverServiceClient
return proto.NewGitserverServiceClient(cc)
}
type testChangedFilesIterator struct {
paths []gitdomain.PathStatus
}
func (t *testChangedFilesIterator) Next() (gitdomain.PathStatus, error) {
if len(t.paths) == 0 {
return gitdomain.PathStatus{}, io.EOF
}
path := t.paths[0]
t.paths = t.paths[1:]
return path, nil
}
func (t *testChangedFilesIterator) Close() error {
return nil
}

View File

@ -329,4 +329,22 @@ func (r *errorTranslatingClient) BehindAhead(ctx context.Context, in *proto.Behi
return res, convertGRPCErrorToGitDomainError(err)
}
type errorTranslatingChangedFilesClient struct {
proto.GitserverService_ChangedFilesClient
}
func (r *errorTranslatingChangedFilesClient) Recv() (*proto.ChangedFilesResponse, error) {
res, err := r.GitserverService_ChangedFilesClient.Recv()
return res, convertGRPCErrorToGitDomainError(err)
}
// ChangedFiles implements v1.GitserverServiceClient.
func (r *errorTranslatingClient) ChangedFiles(ctx context.Context, in *proto.ChangedFilesRequest, opts ...grpc.CallOption) (proto.GitserverService_ChangedFilesClient, error) {
cc, err := r.base.ChangedFiles(ctx, in, opts...)
if err != nil {
return nil, convertGRPCErrorToGitDomainError(err)
}
return &errorTranslatingChangedFilesClient{cc}, nil
}
var _ proto.GitserverServiceClient = &errorTranslatingClient{}

View File

@ -23,7 +23,10 @@ go_library(
go_test(
name = "gitdomain_test",
timeout = "short",
srcs = ["common_test.go"],
srcs = [
"common_test.go",
"log_test.go",
],
embed = [":gitdomain"],
deps = [
"//internal/api",

View File

@ -5,6 +5,7 @@ import (
"fmt"
"io"
proto "github.com/sourcegraph/sourcegraph/internal/gitserver/v1"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
@ -18,14 +19,72 @@ type PathStatus struct {
Status StatusAMD
}
func (s *PathStatus) String() string {
return fmt.Sprintf("%s %q", s.Status, s.Path)
}
func PathStatusFromProto(p *proto.ChangedFile) PathStatus {
return PathStatus{
Path: string(p.Path),
Status: StatusAMDFromProto(p.Status),
}
}
func (p *PathStatus) ToProto() *proto.ChangedFile {
return &proto.ChangedFile{
Path: []byte(p.Path),
Status: p.Status.ToProto(),
}
}
type StatusAMD int
const (
AddedAMD StatusAMD = 0
ModifiedAMD StatusAMD = 1
DeletedAMD StatusAMD = 2
StatusUnspecifiedAMD StatusAMD = 0
AddedAMD StatusAMD = 1
ModifiedAMD StatusAMD = 2
DeletedAMD StatusAMD = 3
)
func (s StatusAMD) String() string {
switch s {
case AddedAMD:
return "Added"
case ModifiedAMD:
return "Modified"
case DeletedAMD:
return "Deleted"
default:
return "Unspecified"
}
}
func StatusAMDFromProto(s proto.ChangedFile_Status) StatusAMD {
switch s {
case proto.ChangedFile_STATUS_ADDED:
return AddedAMD
case proto.ChangedFile_STATUS_MODIFIED:
return ModifiedAMD
case proto.ChangedFile_STATUS_DELETED:
return DeletedAMD
default:
return StatusUnspecifiedAMD
}
}
func (s StatusAMD) ToProto() proto.ChangedFile_Status {
switch s {
case AddedAMD:
return proto.ChangedFile_STATUS_ADDED
case ModifiedAMD:
return proto.ChangedFile_STATUS_MODIFIED
case DeletedAMD:
return proto.ChangedFile_STATUS_DELETED
default:
return proto.ChangedFile_STATUS_UNSPECIFIED
}
}
func LogReverseArgs(n int, givenCommit string) []string {
return []string{
"log",

View File

@ -0,0 +1,76 @@
package gitdomain
import (
"math/rand"
"reflect"
"testing"
"testing/quick"
"github.com/google/go-cmp/cmp"
)
func TestPathStatus_RoundTrip(t *testing.T) {
var diff string
if err := quick.Check(func(path string, status fuzzStatus) bool {
original := &PathStatus{
Path: path,
Status: StatusAMD(status),
}
converted := PathStatusFromProto(original.ToProto())
if diff = cmp.Diff(original, &converted); diff != "" {
return false
}
return true
}, nil); err != nil {
t.Errorf("PathStatus roundtrip mismatch (-want +got):\n%s", diff)
}
}
func TestStatusAMD_RoundTrip(t *testing.T) {
// Can't use testing/quick here because the enum has only 4 values. The underlying type of the enum is int,
// so we can't fuzz since it would use the full range of int.
tests := []struct {
name string
amd StatusAMD
}{
{
name: "AddedAMD",
amd: AddedAMD,
},
{
name: "ModifiedAMD",
amd: ModifiedAMD,
},
{
name: "DeletedAMD",
amd: DeletedAMD,
},
{
name: "StatusUnspecifiedAMD",
amd: StatusUnspecifiedAMD,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
converted := StatusAMDFromProto(test.amd.ToProto())
if diff := cmp.Diff(test.amd, converted); diff != "" {
t.Errorf("StatusAMD roundtrip mismatch (-want +got):\n%s", diff)
}
})
}
}
type fuzzStatus StatusAMD
func (fuzzStatus) Generate(rand *rand.Rand, _ int) reflect.Value {
validValues := []StatusAMD{AddedAMD, ModifiedAMD, DeletedAMD, StatusUnspecifiedAMD}
return reflect.ValueOf(fuzzStatus(validValues[rand.Intn(len(validValues))]))
}
var _ quick.Generator = fuzzStatus(AddedAMD)

File diff suppressed because it is too large Load Diff

View File

@ -175,4 +175,9 @@ func (r *automaticRetryClient) BehindAhead(ctx context.Context, in *proto.Behind
return r.base.BehindAhead(ctx, in, opts...)
}
func (r *automaticRetryClient) ChangedFiles(ctx context.Context, in *proto.ChangedFilesRequest, opts ...grpc.CallOption) (proto.GitserverService_ChangedFilesClient, error) {
opts = append(defaults.RetryPolicy, opts...)
return r.base.ChangedFiles(ctx, in, opts...)
}
var _ proto.GitserverServiceClient = &automaticRetryClient{}

File diff suppressed because it is too large Load Diff

View File

@ -248,6 +248,20 @@ service GitserverService {
rpc BehindAhead(BehindAheadRequest) returns (BehindAheadResponse) {
option idempotency_level = NO_SIDE_EFFECTS;
}
// 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).
//
// - Renamed files are represented as a deletion of the old path and an addition of the new path.
// - No copy detection is performed.
// - The only file status codes returned are 'A' (added), 'M' (modified), or 'D' (deleted).
//
// 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, an error with a `RevisionNotFoundPayload` is returned.
rpc ChangedFiles(ChangedFilesRequest) returns (stream ChangedFilesResponse) {
option idempotency_level = NO_SIDE_EFFECTS;
}
}
message ContributorCountsRequest {
@ -1084,3 +1098,32 @@ message BehindAheadResponse {
// ahead is the number of commits that are solely reachable in "right" but not "left".
uint32 ahead = 2;
}
message ChangedFilesRequest {
// repo_name is the name of the repo to get the changed files for.
// Note: We use field ID 2 here to reserve 1 for a future repo int32 field.
string repo_name = 2;
// base is a <tree-ish> id, for now, we allow non-utf8 revspecs.
//
// If base is empty, the parent of the head is used.
optional bytes base = 3;
// head is a <tree-ish> id, for now, we allow non-utf8 revspecs.
bytes head = 4;
}
message ChangedFilesResponse {
repeated ChangedFile files = 1;
}
message ChangedFile {
// path is the file path of the file that the status is for.
bytes path = 1;
// status is the status of the path.
enum Status {
STATUS_UNSPECIFIED = 0;
STATUS_ADDED = 1;
STATUS_MODIFIED = 2;
STATUS_DELETED = 3;
}
Status status = 2;
}

View File

@ -184,6 +184,7 @@ const (
GitserverService_ContributorCounts_FullMethodName = "/gitserver.v1.GitserverService/ContributorCounts"
GitserverService_FirstEverCommit_FullMethodName = "/gitserver.v1.GitserverService/FirstEverCommit"
GitserverService_BehindAhead_FullMethodName = "/gitserver.v1.GitserverService/BehindAhead"
GitserverService_ChangedFiles_FullMethodName = "/gitserver.v1.GitserverService/ChangedFiles"
)
// GitserverServiceClient is the client API for GitserverService service.
@ -340,6 +341,17 @@ type GitserverServiceClient interface {
// If the given repo is not cloned, it will be enqueued for cloning and a
// NotFound error will be returned, with a RepoNotFoundPayload in the details.
BehindAhead(ctx context.Context, in *BehindAheadRequest, opts ...grpc.CallOption) (*BehindAheadResponse, 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).
//
// - Renamed files are represented as a deletion of the old path and an addition of the new path.
// - No copy detection is performed.
// - The only file status codes returned are 'A' (added), 'M' (modified), or 'D' (deleted).
//
// 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, an error with a `RevisionNotFoundPayload` is returned.
ChangedFiles(ctx context.Context, in *ChangedFilesRequest, opts ...grpc.CallOption) (GitserverService_ChangedFilesClient, error)
}
type gitserverServiceClient struct {
@ -797,6 +809,38 @@ func (c *gitserverServiceClient) BehindAhead(ctx context.Context, in *BehindAhea
return out, nil
}
func (c *gitserverServiceClient) ChangedFiles(ctx context.Context, in *ChangedFilesRequest, opts ...grpc.CallOption) (GitserverService_ChangedFilesClient, error) {
stream, err := c.cc.NewStream(ctx, &GitserverService_ServiceDesc.Streams[8], GitserverService_ChangedFiles_FullMethodName, opts...)
if err != nil {
return nil, err
}
x := &gitserverServiceChangedFilesClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type GitserverService_ChangedFilesClient interface {
Recv() (*ChangedFilesResponse, error)
grpc.ClientStream
}
type gitserverServiceChangedFilesClient struct {
grpc.ClientStream
}
func (x *gitserverServiceChangedFilesClient) Recv() (*ChangedFilesResponse, error) {
m := new(ChangedFilesResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// GitserverServiceServer is the server API for GitserverService service.
// All implementations must embed UnimplementedGitserverServiceServer
// for forward compatibility
@ -951,6 +995,17 @@ type GitserverServiceServer interface {
// If the given repo is not cloned, it will be enqueued for cloning and a
// NotFound error will be returned, with a RepoNotFoundPayload in the details.
BehindAhead(context.Context, *BehindAheadRequest) (*BehindAheadResponse, 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).
//
// - Renamed files are represented as a deletion of the old path and an addition of the new path.
// - No copy detection is performed.
// - The only file status codes returned are 'A' (added), 'M' (modified), or 'D' (deleted).
//
// 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, an error with a `RevisionNotFoundPayload` is returned.
ChangedFiles(*ChangedFilesRequest, GitserverService_ChangedFilesServer) error
mustEmbedUnimplementedGitserverServiceServer()
}
@ -1045,6 +1100,9 @@ func (UnimplementedGitserverServiceServer) FirstEverCommit(context.Context, *Fir
func (UnimplementedGitserverServiceServer) BehindAhead(context.Context, *BehindAheadRequest) (*BehindAheadResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method BehindAhead not implemented")
}
func (UnimplementedGitserverServiceServer) ChangedFiles(*ChangedFilesRequest, GitserverService_ChangedFilesServer) error {
return status.Errorf(codes.Unimplemented, "method ChangedFiles not implemented")
}
func (UnimplementedGitserverServiceServer) mustEmbedUnimplementedGitserverServiceServer() {}
// UnsafeGitserverServiceServer may be embedded to opt out of forward compatibility for this service.
@ -1609,6 +1667,27 @@ func _GitserverService_BehindAhead_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler)
}
func _GitserverService_ChangedFiles_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ChangedFilesRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(GitserverServiceServer).ChangedFiles(m, &gitserverServiceChangedFilesServer{stream})
}
type GitserverService_ChangedFilesServer interface {
Send(*ChangedFilesResponse) error
grpc.ServerStream
}
type gitserverServiceChangedFilesServer struct {
grpc.ServerStream
}
func (x *gitserverServiceChangedFilesServer) Send(m *ChangedFilesResponse) error {
return x.ServerStream.SendMsg(m)
}
// GitserverService_ServiceDesc is the grpc.ServiceDesc for GitserverService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -1742,6 +1821,11 @@ var GitserverService_ServiceDesc = grpc.ServiceDesc{
Handler: _GitserverService_RawDiff_Handler,
ServerStreams: true,
},
{
StreamName: "ChangedFiles",
Handler: _GitserverService_ChangedFiles_Handler,
ServerStreams: true,
},
},
Metadata: "gitserver.proto",
}

View File

@ -146,6 +146,8 @@
- GitserverService_ListRefsClient
- GitserverService_RawDiffServer
- GitserverService_RawDiffClient
- GitserverService_ChangedFilesServer
- GitserverService_ChangedFilesClient
- filename: cmd/gitserver/internal/git/mock.go
path: github.com/sourcegraph/sourcegraph/cmd/gitserver/internal/git
interfaces: