diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2020-10-15 15:16:34 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2020-10-15 15:16:34 +0300 |
commit | 67bb4e402505ce3fdf151db638a992545a858d82 (patch) | |
tree | 5c1e95e9f3f0ccb117dec6e63f0dd579fece5687 | |
parent | edc556107506a698e6bf95110d458e9eb07d311f (diff) | |
parent | 2f2e703cf4a535ed8b007b3d6123751a9a2baf78 (diff) |
Merge branch 'pks-go-list-conflict-files' into 'master'
conflicts: Port ListConflictFiles to Go
Closes #3060
See merge request gitlab-org/gitaly!2598
-rw-r--r-- | changelogs/unreleased/pks-go-list-conflict-files.yml | 5 | ||||
-rw-r--r-- | cmd/gitaly-git2go/conflicts.go | 66 | ||||
-rw-r--r-- | cmd/gitaly-git2go/conflicts_test.go | 63 | ||||
-rw-r--r-- | cmd/gitaly-git2go/util.go | 6 | ||||
-rw-r--r-- | internal/git2go/conflicts.go | 37 | ||||
-rw-r--r-- | internal/git2go/conflicts_test.go | 16 | ||||
-rw-r--r-- | internal/gitaly/service/conflicts/list_conflict_files.go | 123 | ||||
-rw-r--r-- | internal/gitaly/service/conflicts/list_conflict_files_test.go | 120 | ||||
-rw-r--r-- | internal/gitaly/service/conflicts/server.go | 14 | ||||
-rw-r--r-- | internal/gitaly/service/conflicts/testhelper_test.go | 5 | ||||
-rw-r--r-- | internal/gitaly/service/register.go | 2 | ||||
-rw-r--r-- | internal/metadata/featureflag/feature_flags.go | 3 |
12 files changed, 380 insertions, 80 deletions
diff --git a/changelogs/unreleased/pks-go-list-conflict-files.yml b/changelogs/unreleased/pks-go-list-conflict-files.yml new file mode 100644 index 000000000..bbbb37487 --- /dev/null +++ b/changelogs/unreleased/pks-go-list-conflict-files.yml @@ -0,0 +1,5 @@ +--- +title: 'conflicts: Port ListConflictFiles to Go' +merge_request: 2598 +author: +type: performance diff --git a/cmd/gitaly-git2go/conflicts.go b/cmd/gitaly-git2go/conflicts.go index 2a96c34a7..f2ba28abd 100644 --- a/cmd/gitaly-git2go/conflicts.go +++ b/cmd/gitaly-git2go/conflicts.go @@ -10,6 +10,9 @@ import ( git "github.com/libgit2/git2go/v30" "gitlab.com/gitlab-org/gitaly/internal/git2go" + "gitlab.com/gitlab-org/gitaly/internal/helper" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type conflictsSubcommand struct { @@ -22,14 +25,17 @@ func (cmd *conflictsSubcommand) Flags() *flag.FlagSet { return flags } -func indexEntryPath(entry *git.IndexEntry) string { +func conflictEntryFromIndex(entry *git.IndexEntry) git2go.ConflictEntry { if entry == nil { - return "" + return git2go.ConflictEntry{} + } + return git2go.ConflictEntry{ + Path: entry.Path, + Mode: int32(entry.Mode), } - return entry.Path } -func conflictContent(repo *git.Repository, conflict git.IndexConflict) (string, error) { +func conflictContent(repo *git.Repository, conflict git.IndexConflict) ([]byte, error) { var ancestor, our, their git.MergeFileInput for entry, input := range map[*git.IndexEntry]*git.MergeFileInput{ @@ -43,7 +49,7 @@ func conflictContent(repo *git.Repository, conflict git.IndexConflict) (string, blob, err := repo.LookupBlob(entry.Id) if err != nil { - return "", fmt.Errorf("could not get conflicting blob: %w", err) + return nil, helper.ErrPreconditionFailedf("could not get conflicting blob: %w", err) } input.Path = entry.Path @@ -53,10 +59,25 @@ func conflictContent(repo *git.Repository, conflict git.IndexConflict) (string, merge, err := git.MergeFile(ancestor, our, their, nil) if err != nil { - return "", fmt.Errorf("could not compute conflicts: %w", err) + return nil, fmt.Errorf("could not compute conflicts: %w", err) } - return string(merge.Contents), nil + return merge.Contents, nil +} + +func conflictError(code codes.Code, message string) error { + result := git2go.ConflictsResult{ + Error: git2go.ConflictError{ + Code: code, + Message: message, + }, + } + + if err := result.SerializeTo(os.Stdout); err != nil { + return err + } + + return nil } // Run performs a merge and prints resulting conflicts to stdout. @@ -71,19 +92,29 @@ func (cmd *conflictsSubcommand) Run() error { return fmt.Errorf("could not open repository: %w", err) } - ours, err := lookupCommit(repo, request.Ours) + oursOid, err := git.NewOid(request.Ours) if err != nil { - return fmt.Errorf("could not lookup commit %q: %w", request.Ours, err) + return err } - theirs, err := lookupCommit(repo, request.Theirs) + ours, err := repo.LookupCommit(oursOid) if err != nil { - return fmt.Errorf("could not lookup commit %q: %w", request.Theirs, err) + return err + } + + theirsOid, err := git.NewOid(request.Theirs) + if err != nil { + return err + } + + theirs, err := repo.LookupCommit(theirsOid) + if err != nil { + return err } index, err := repo.MergeCommits(ours, theirs, nil) if err != nil { - return fmt.Errorf("could not merge commits: %w", err) + return conflictError(codes.FailedPrecondition, fmt.Sprintf("could not merge commits: %v", err)) } conflicts, err := index.ConflictIterator() @@ -104,14 +135,17 @@ func (cmd *conflictsSubcommand) Run() error { content, err := conflictContent(repo, conflict) if err != nil { + if status, ok := status.FromError(err); ok { + return conflictError(status.Code(), status.Message()) + } return err } result.Conflicts = append(result.Conflicts, git2go.Conflict{ - AncestorPath: indexEntryPath(conflict.Ancestor), - OurPath: indexEntryPath(conflict.Our), - TheirPath: indexEntryPath(conflict.Their), - Content: content, + Ancestor: conflictEntryFromIndex(conflict.Ancestor), + Our: conflictEntryFromIndex(conflict.Our), + Their: conflictEntryFromIndex(conflict.Their), + Content: content, }) } diff --git a/cmd/gitaly-git2go/conflicts_test.go b/cmd/gitaly-git2go/conflicts_test.go index e1310bc1e..4cef16c12 100644 --- a/cmd/gitaly-git2go/conflicts_test.go +++ b/cmd/gitaly-git2go/conflicts_test.go @@ -45,10 +45,10 @@ func TestConflicts(t *testing.T) { }, conflicts: []git2go.Conflict{ { - AncestorPath: "file", - OurPath: "file", - TheirPath: "file", - Content: "<<<<<<< file\nb\n=======\nc\n>>>>>>> file\n", + Ancestor: git2go.ConflictEntry{Path: "file", Mode: 0100644}, + Our: git2go.ConflictEntry{Path: "file", Mode: 0100644}, + Their: git2go.ConflictEntry{Path: "file", Mode: 0100644}, + Content: []byte("<<<<<<< file\nb\n=======\nc\n>>>>>>> file\n"), }, }, }, @@ -68,10 +68,10 @@ func TestConflicts(t *testing.T) { }, conflicts: []git2go.Conflict{ { - AncestorPath: "file-2", - OurPath: "file-2", - TheirPath: "file-2", - Content: "<<<<<<< file-2\nb\n=======\nc\n>>>>>>> file-2\n", + Ancestor: git2go.ConflictEntry{Path: "file-2", Mode: 0100644}, + Our: git2go.ConflictEntry{Path: "file-2", Mode: 0100644}, + Their: git2go.ConflictEntry{Path: "file-2", Mode: 0100644}, + Content: []byte("<<<<<<< file-2\nb\n=======\nc\n>>>>>>> file-2\n"), }, }, }, @@ -91,16 +91,16 @@ func TestConflicts(t *testing.T) { }, conflicts: []git2go.Conflict{ { - AncestorPath: "file-1", - OurPath: "file-1", - TheirPath: "file-1", - Content: "<<<<<<< file-1\nb\n=======\nc\n>>>>>>> file-1\n", + Ancestor: git2go.ConflictEntry{Path: "file-1", Mode: 0100644}, + Our: git2go.ConflictEntry{Path: "file-1", Mode: 0100644}, + Their: git2go.ConflictEntry{Path: "file-1", Mode: 0100644}, + Content: []byte("<<<<<<< file-1\nb\n=======\nc\n>>>>>>> file-1\n"), }, { - AncestorPath: "file-2", - OurPath: "file-2", - TheirPath: "file-2", - Content: "<<<<<<< file-2\nb\n=======\nc\n>>>>>>> file-2\n", + Ancestor: git2go.ConflictEntry{Path: "file-2", Mode: 0100644}, + Our: git2go.ConflictEntry{Path: "file-2", Mode: 0100644}, + Their: git2go.ConflictEntry{Path: "file-2", Mode: 0100644}, + Content: []byte("<<<<<<< file-2\nb\n=======\nc\n>>>>>>> file-2\n"), }, }, }, @@ -117,10 +117,10 @@ func TestConflicts(t *testing.T) { }, conflicts: []git2go.Conflict{ { - AncestorPath: "file", - OurPath: "file", - TheirPath: "", - Content: "<<<<<<< file\nchanged\n=======\n>>>>>>> \n", + Ancestor: git2go.ConflictEntry{Path: "file", Mode: 0100644}, + Our: git2go.ConflictEntry{Path: "file", Mode: 0100644}, + Their: git2go.ConflictEntry{}, + Content: []byte("<<<<<<< file\nchanged\n=======\n>>>>>>> \n"), }, }, }, @@ -140,21 +140,22 @@ func TestConflicts(t *testing.T) { }, conflicts: []git2go.Conflict{ { - AncestorPath: "file", - OurPath: "", - TheirPath: "", + Ancestor: git2go.ConflictEntry{Path: "file", Mode: 0100644}, + Our: git2go.ConflictEntry{}, + Their: git2go.ConflictEntry{}, + Content: []byte{}, }, { - AncestorPath: "", - OurPath: "renamed-1", - TheirPath: "", - Content: "a\nb\nc\nd\ne\nf\ng\n", + Ancestor: git2go.ConflictEntry{}, + Our: git2go.ConflictEntry{Path: "renamed-1", Mode: 0100644}, + Their: git2go.ConflictEntry{}, + Content: []byte("a\nb\nc\nd\ne\nf\ng\n"), }, { - AncestorPath: "", - OurPath: "", - TheirPath: "renamed-2", - Content: "a\nb\nc\nd\ne\nf\ng\n", + Ancestor: git2go.ConflictEntry{}, + Our: git2go.ConflictEntry{}, + Their: git2go.ConflictEntry{Path: "renamed-2", Mode: 0100644}, + Content: []byte("a\nb\nc\nd\ne\nf\ng\n"), }, }, }, diff --git a/cmd/gitaly-git2go/util.go b/cmd/gitaly-git2go/util.go index d580582cd..a315c5f3a 100644 --- a/cmd/gitaly-git2go/util.go +++ b/cmd/gitaly-git2go/util.go @@ -11,17 +11,17 @@ import ( func lookupCommit(repo *git.Repository, ref string) (*git.Commit, error) { object, err := repo.RevparseSingle(ref) if err != nil { - return nil, fmt.Errorf("could not lookup reference: %w", err) + return nil, fmt.Errorf("could not lookup reference %q: %w", ref, err) } peeled, err := object.Peel(git.ObjectCommit) if err != nil { - return nil, fmt.Errorf("could not peel reference: %w", err) + return nil, fmt.Errorf("could not peel reference %q: %w", ref, err) } commit, err := peeled.AsCommit() if err != nil { - return nil, fmt.Errorf("could not cast to commit: %w", err) + return nil, fmt.Errorf("reference %q is not a commit: %w", ref, err) } return commit, nil diff --git a/internal/git2go/conflicts.go b/internal/git2go/conflicts.go index 101c260ae..c5848c618 100644 --- a/internal/git2go/conflicts.go +++ b/internal/git2go/conflicts.go @@ -7,6 +7,8 @@ import ( "io" "gitlab.com/gitlab-org/gitaly/internal/gitaly/config" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // ConflictsCommand contains parameters to perform a merge and return its conflicts. @@ -19,22 +21,39 @@ type ConflictsCommand struct { Theirs string `json:"theirs"` } +// ConflictEntry represents a conflict entry which is one of the sides of a conflict. +type ConflictEntry struct { + // Path is the path of the conflicting file. + Path string `json:"path"` + // Mode is the mode of the conflicting file. + Mode int32 `json:"mode"` +} + // Conflict represents a merge conflict for a single file. type Conflict struct { - // AncestorPath is the path of the ancestor. - AncestorPath string `json:"ancestor_path"` - // OurPath is the path of ours. - OurPath string `json:"our_path"` - // TheirPath is the path of theirs. - TheirPath string `json:"their_path"` + // Ancestor is the conflict entry of the merge-base. + Ancestor ConflictEntry `json:"ancestor"` + // Our is the conflict entry of ours. + Our ConflictEntry `json:"our"` + // Their is the conflict entry of theirs. + Their ConflictEntry `json:"their"` // Content contains the conflicting merge results. - Content string `json:"content"` + Content []byte `json:"content"` +} + +type ConflictError struct { + // Code is the GRPC error code + Code codes.Code + // Message is the error message + Message string } // ConflictsResult contains all conflicts resulting from a merge. type ConflictsResult struct { // Conflicts Conflicts []Conflict `json:"conflicts"` + // Error is an optional conflict error + Error ConflictError `json:"error"` } // ConflictsCommandFromSerialized constructs a ConflictsCommand from its serialized representation. @@ -77,6 +96,10 @@ func (c ConflictsCommand) Run(ctx context.Context, cfg config.Cfg) (ConflictsRes return ConflictsResult{}, err } + if response.Error.Code != codes.OK { + return ConflictsResult{}, status.Error(response.Error.Code, response.Error.Message) + } + return response, nil } diff --git a/internal/git2go/conflicts_test.go b/internal/git2go/conflicts_test.go index 6aeeb2903..4061598bf 100644 --- a/internal/git2go/conflicts_test.go +++ b/internal/git2go/conflicts_test.go @@ -86,20 +86,20 @@ func TestConflictsResult_Serialization(t *testing.T) { serialized: serializeResult(t, ConflictsResult{ Conflicts: []Conflict{ { - AncestorPath: "dir/ancestor", - OurPath: "dir/our", - TheirPath: "dir/their", - Content: "content", + Ancestor: ConflictEntry{Path: "dir/ancestor", Mode: 0100644}, + Our: ConflictEntry{Path: "dir/our", Mode: 0100644}, + Their: ConflictEntry{Path: "dir/their", Mode: 0100644}, + Content: []byte("content"), }, }, }), expected: ConflictsResult{ Conflicts: []Conflict{ { - AncestorPath: "dir/ancestor", - OurPath: "dir/our", - TheirPath: "dir/their", - Content: "content", + Ancestor: ConflictEntry{Path: "dir/ancestor", Mode: 0100644}, + Our: ConflictEntry{Path: "dir/our", Mode: 0100644}, + Their: ConflictEntry{Path: "dir/their", Mode: 0100644}, + Content: []byte("content"), }, }, }, diff --git a/internal/gitaly/service/conflicts/list_conflict_files.go b/internal/gitaly/service/conflicts/list_conflict_files.go index 7499c9ab1..b1c9d8cb6 100644 --- a/internal/gitaly/service/conflicts/list_conflict_files.go +++ b/internal/gitaly/service/conflicts/list_conflict_files.go @@ -1,17 +1,140 @@ package conflicts import ( + "bytes" + "errors" "fmt" + "io" + "unicode/utf8" + "gitlab.com/gitlab-org/gitaly/internal/git" + "gitlab.com/gitlab-org/gitaly/internal/git2go" "gitlab.com/gitlab-org/gitaly/internal/gitaly/rubyserver" + "gitlab.com/gitlab-org/gitaly/internal/helper" + "gitlab.com/gitlab-org/gitaly/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb" + "gitlab.com/gitlab-org/gitaly/streamio" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) +func (s *server) listConflictFiles(request *gitalypb.ListConflictFilesRequest, stream gitalypb.ConflictsService_ListConflictFilesServer) error { + ctx := stream.Context() + + if err := validateListConflictFilesRequest(request); err != nil { + return helper.ErrInvalidArgument(err) + } + + repo := git.NewRepository(request.Repository) + + ours, err := repo.ResolveRefish(ctx, request.OurCommitOid+"^{commit}") + if err != nil { + return helper.ErrPreconditionFailedf("could not lookup 'our' OID: %s", err) + } + + theirs, err := repo.ResolveRefish(ctx, request.TheirCommitOid+"^{commit}") + if err != nil { + return helper.ErrPreconditionFailedf("could not lookup 'their' OID: %s", err) + } + + repoPath, err := s.locator.GetPath(request.Repository) + if err != nil { + return err + } + + conflicts, err := git2go.ConflictsCommand{ + Repository: repoPath, + Ours: ours, + Theirs: theirs, + }.Run(ctx, s.cfg) + if err != nil { + if errors.Is(err, git2go.ErrInvalidArgument) { + return helper.ErrInvalidArgument(err) + } + return helper.ErrInternal(err) + } + + var conflictFiles []*gitalypb.ConflictFile + msgSize := 0 + + for _, conflict := range conflicts.Conflicts { + if conflict.Their.Path == "" || conflict.Our.Path == "" { + return helper.ErrPreconditionFailedf("conflict side missing") + } + + if !utf8.Valid(conflict.Content) { + return helper.ErrPreconditionFailed(errors.New("unsupported encoding")) + } + + conflictFiles = append(conflictFiles, &gitalypb.ConflictFile{ + ConflictFilePayload: &gitalypb.ConflictFile_Header{ + Header: &gitalypb.ConflictFileHeader{ + CommitOid: request.OurCommitOid, + TheirPath: []byte(conflict.Their.Path), + OurPath: []byte(conflict.Our.Path), + OurMode: conflict.Our.Mode, + }, + }, + }) + + contentReader := bytes.NewReader(conflict.Content) + for { + chunk := make([]byte, streamio.WriteBufferSize-msgSize) + bytesRead, err := contentReader.Read(chunk) + if err != nil && err != io.EOF { + return helper.ErrInternal(err) + } + + if bytesRead > 0 { + conflictFiles = append(conflictFiles, &gitalypb.ConflictFile{ + ConflictFilePayload: &gitalypb.ConflictFile_Content{ + Content: chunk[:bytesRead], + }, + }) + } + + if err == io.EOF { + break + } + + // We don't send a message for each chunk because the content of + // a file may be smaller than the size limit, which means we can + // keep adding data to the message + msgSize += bytesRead + if msgSize < streamio.WriteBufferSize { + continue + } + + if err := stream.Send(&gitalypb.ListConflictFilesResponse{ + Files: conflictFiles, + }); err != nil { + return helper.ErrInternal(err) + } + + conflictFiles = conflictFiles[:0] + msgSize = 0 + } + } + + // Send leftover data, if any + if len(conflictFiles) > 0 { + if err := stream.Send(&gitalypb.ListConflictFilesResponse{ + Files: conflictFiles, + }); err != nil { + return helper.ErrInternal(err) + } + } + + return nil +} + func (s *server) ListConflictFiles(in *gitalypb.ListConflictFilesRequest, stream gitalypb.ConflictsService_ListConflictFilesServer) error { ctx := stream.Context() + if featureflag.IsEnabled(ctx, featureflag.GoListConflictFiles) { + return s.listConflictFiles(in, stream) + } + if err := validateListConflictFilesRequest(in); err != nil { return status.Errorf(codes.InvalidArgument, "ListConflictFiles: %v", err) } diff --git a/internal/gitaly/service/conflicts/list_conflict_files_test.go b/internal/gitaly/service/conflicts/list_conflict_files_test.go index b384f8b8c..56386aba1 100644 --- a/internal/gitaly/service/conflicts/list_conflict_files_test.go +++ b/internal/gitaly/service/conflicts/list_conflict_files_test.go @@ -1,11 +1,17 @@ package conflicts import ( + "bytes" + "context" "io" + "io/ioutil" + "path/filepath" "testing" "github.com/golang/protobuf/proto" "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/internal/git" + "gitlab.com/gitlab-org/gitaly/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/internal/testhelper" "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb" "google.golang.org/grpc/codes" @@ -16,7 +22,29 @@ type conflictFile struct { content []byte } +func testListConflictFiles(t *testing.T, testcase func(t *testing.T, ctx context.Context)) { + featureSets, err := testhelper.NewFeatureSets([]featureflag.FeatureFlag{ + featureflag.GoListConflictFiles, + }) + require.NoError(t, err) + + for _, featureSet := range featureSets { + t.Run("disabled "+featureSet.String(), func(t *testing.T) { + ctx, cancel := testhelper.Context() + defer cancel() + + ctx = featureSet.Disable(ctx) + + testcase(t, ctx) + }) + } +} + func TestSuccessfulListConflictFilesRequest(t *testing.T) { + testListConflictFiles(t, testSuccessfulListConflictFilesRequest) +} + +func testSuccessfulListConflictFilesRequest(t *testing.T, ctx context.Context) { serverSocketPath, stop := runConflictsServer(t) defer stop() @@ -48,9 +76,6 @@ class Conflict end ` - ctx, cancel := testhelper.Context() - defer cancel() - request := &gitalypb.ListConflictFilesRequest{ Repository: testRepo, OurCommitOid: ourCommitOid, @@ -88,11 +113,88 @@ end for i := 0; i < len(expectedFiles); i++ { require.True(t, proto.Equal(receivedFiles[i].header, expectedFiles[i].header)) - require.Equal(t, receivedFiles[i].content, expectedFiles[i].content) + require.Equal(t, expectedFiles[i].content, receivedFiles[i].content) + } +} + +func TestListConflictFilesHugeDiff(t *testing.T) { + testListConflictFiles(t, testListConflictFilesHugeDiff) +} + +func testListConflictFilesHugeDiff(t *testing.T, ctx context.Context) { + serverSocketPath, stop := runConflictsServer(t) + defer stop() + + client, conn := NewConflictsClient(t, serverSocketPath) + defer conn.Close() + + repo, repoPath, cleanupFn := testhelper.NewTestRepoWithWorktree(t) + defer cleanupFn() + + our := buildCommit(t, ctx, repo, repoPath, map[string][]byte{ + "a": bytes.Repeat([]byte("a\n"), 128*1024), + "b": bytes.Repeat([]byte("b\n"), 128*1024), + }) + + their := buildCommit(t, ctx, repo, repoPath, map[string][]byte{ + "a": bytes.Repeat([]byte("x\n"), 128*1024), + "b": bytes.Repeat([]byte("y\n"), 128*1024), + }) + + request := &gitalypb.ListConflictFilesRequest{ + Repository: repo, + OurCommitOid: our, + TheirCommitOid: their, } + + c, err := client.ListConflictFiles(ctx, request) + if err != nil { + t.Fatal(err) + } + + receivedFiles := getConflictFiles(t, c) + require.Len(t, receivedFiles, 2) + require.True(t, + proto.Equal(&gitalypb.ConflictFileHeader{ + CommitOid: our, + OurMode: int32(0100644), + OurPath: []byte("a"), + TheirPath: []byte("a"), + }, receivedFiles[0].header), + ) + + require.True(t, + proto.Equal(&gitalypb.ConflictFileHeader{ + CommitOid: our, + OurMode: int32(0100644), + OurPath: []byte("b"), + TheirPath: []byte("b"), + }, receivedFiles[1].header), + ) +} + +func buildCommit(t *testing.T, ctx context.Context, repo *gitalypb.Repository, repoPath string, files map[string][]byte) string { + for file, contents := range files { + filePath := filepath.Join(repoPath, file) + require.NoError(t, ioutil.WriteFile(filePath, contents, 0666)) + testhelper.MustRunCommand(t, nil, "git", "-C", repoPath, "add", filePath) + } + + testhelper.MustRunCommand(t, nil, "git", "-C", repoPath, "commit", "-m", "message") + + oid, err := git.NewRepository(repo).ResolveRefish(ctx, "HEAD") + require.NoError(t, err) + + testhelper.MustRunCommand(t, nil, "git", "-C", repoPath, "reset", "--hard", "HEAD~") + + return oid } func TestListConflictFilesFailedPrecondition(t *testing.T) { + testListConflictFiles(t, testListConflictFilesFailedPrecondition) +} + +func testListConflictFilesFailedPrecondition(t *testing.T, ctx context.Context) { serverSocketPath, stop := runConflictsServer(t) defer stop() @@ -102,9 +204,6 @@ func TestListConflictFilesFailedPrecondition(t *testing.T) { testRepo, _, cleanupFn := testhelper.NewTestRepo(t) defer cleanupFn() - ctx, cancel := testhelper.Context() - defer cancel() - testCases := []struct { desc string ourCommitOid string @@ -158,6 +257,10 @@ func TestListConflictFilesFailedPrecondition(t *testing.T) { } func TestFailedListConflictFilesRequestDueToValidation(t *testing.T) { + testListConflictFiles(t, testFailedListConflictFilesRequestDueToValidation) +} + +func testFailedListConflictFilesRequestDueToValidation(t *testing.T, ctx context.Context) { serverSocketPath, stop := runConflictsServer(t) defer stop() @@ -206,9 +309,6 @@ func TestFailedListConflictFilesRequestDueToValidation(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.desc, func(t *testing.T) { - ctx, cancel := testhelper.Context() - defer cancel() - c, _ := client.ListConflictFiles(ctx, testCase.request) testhelper.RequireGrpcError(t, drainListConflictFilesResponse(c), testCase.code) }) diff --git a/internal/gitaly/service/conflicts/server.go b/internal/gitaly/service/conflicts/server.go index d339dcca4..91bd935d3 100644 --- a/internal/gitaly/service/conflicts/server.go +++ b/internal/gitaly/service/conflicts/server.go @@ -1,15 +1,23 @@ package conflicts import ( + "gitlab.com/gitlab-org/gitaly/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/internal/gitaly/rubyserver" + "gitlab.com/gitlab-org/gitaly/internal/storage" "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb" ) type server struct { - ruby *rubyserver.Server + ruby *rubyserver.Server + cfg config.Cfg + locator storage.Locator } // NewServer creates a new instance of a grpc ConflictsServer -func NewServer(rs *rubyserver.Server) gitalypb.ConflictsServiceServer { - return &server{ruby: rs} +func NewServer(rs *rubyserver.Server, cfg config.Cfg, locator storage.Locator) gitalypb.ConflictsServiceServer { + return &server{ + ruby: rs, + cfg: cfg, + locator: locator, + } } diff --git a/internal/gitaly/service/conflicts/testhelper_test.go b/internal/gitaly/service/conflicts/testhelper_test.go index e952327a1..9b3f8fdaa 100644 --- a/internal/gitaly/service/conflicts/testhelper_test.go +++ b/internal/gitaly/service/conflicts/testhelper_test.go @@ -26,6 +26,8 @@ var RubyServer = &rubyserver.Server{} func testMain(m *testing.M) int { defer testhelper.MustHaveNoChildProcess() + testhelper.ConfigureGitalyGit2Go() + tempDir, err := ioutil.TempDir("", "gitaly") if err != nil { log.Fatal(err) @@ -45,8 +47,9 @@ func testMain(m *testing.M) int { func runConflictsServer(t *testing.T) (string, func()) { srv := testhelper.NewServer(t, nil, nil) + locator := config.NewLocator(config.Config) - gitalypb.RegisterConflictsServiceServer(srv.GrpcServer(), NewServer(RubyServer)) + gitalypb.RegisterConflictsServiceServer(srv.GrpcServer(), NewServer(RubyServer, config.Config, locator)) reflection.Register(srv.GrpcServer()) require.NoError(t, srv.Start()) diff --git a/internal/gitaly/service/register.go b/internal/gitaly/service/register.go index 34f732339..0a6756ba8 100644 --- a/internal/gitaly/service/register.go +++ b/internal/gitaly/service/register.go @@ -85,7 +85,7 @@ func RegisterAll(grpcServer *grpc.Server, cfg config.Cfg, rubyServer *rubyserver smarthttp.WithPackfileNegotiationMetrics(smarthttpPackfileNegotiationMetrics), )) gitalypb.RegisterWikiServiceServer(grpcServer, wiki.NewServer(rubyServer, locator)) - gitalypb.RegisterConflictsServiceServer(grpcServer, conflicts.NewServer(rubyServer)) + gitalypb.RegisterConflictsServiceServer(grpcServer, conflicts.NewServer(rubyServer, cfg, locator)) gitalypb.RegisterRemoteServiceServer(grpcServer, remote.NewServer(rubyServer, locator)) gitalypb.RegisterServerServiceServer(grpcServer, server.NewServer(cfg.Storages)) gitalypb.RegisterObjectPoolServiceServer(grpcServer, objectpool.NewServer(locator)) diff --git a/internal/metadata/featureflag/feature_flags.go b/internal/metadata/featureflag/feature_flags.go index d0f9f8eb9..9f1af69e4 100644 --- a/internal/metadata/featureflag/feature_flags.go +++ b/internal/metadata/featureflag/feature_flags.go @@ -36,6 +36,8 @@ var ( GoUserDeleteBranch = FeatureFlag{Name: "go_user_delete_branch", OnByDefault: false} // GoUserSquash enable the Go implementation of UserSquash GoUserSquash = FeatureFlag{Name: "go_user_squash", OnByDefault: false} + // GoListConflictFiles enables the Go implementation of ListConflictFiles + GoListConflictFiles = FeatureFlag{Name: "go_list_conflict_files", OnByDefault: false} ) // All includes all feature flags. @@ -51,6 +53,7 @@ var All = []FeatureFlag{ GoUserFFBranch, GoUserDeleteBranch, GoUserSquash, + GoListConflictFiles, } const ( |