mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 17:31:43 +00:00
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:
parent
b4c835ba12
commit
5f818566d3
@ -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
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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{}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
76
internal/gitserver/gitdomain/log_test.go
Normal file
76
internal/gitserver/gitdomain/log_test.go
Normal 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
@ -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{}
|
||||
|
||||
1207
internal/gitserver/v1/gitserver.pb.go
generated
1207
internal/gitserver/v1/gitserver.pb.go
generated
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
||||
84
internal/gitserver/v1/gitserver_grpc.pb.go
generated
84
internal/gitserver/v1/gitserver_grpc.pb.go
generated
@ -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",
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user