diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2020-09-21 14:14:35 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2020-09-22 11:14:26 +0300 |
commit | fe8b701dac4a47ff490e7c3742cf1cf1a898febe (patch) | |
tree | e066880b978e39af28fd5508d020c65021486025 /internal/gitaly | |
parent | 542161b021db9c7304edc446b153c8d7478f237b (diff) |
operations: Implement UserMergeToRef
This commit ports the operations.UserMergeToRef RPC from Ruby to Go. The
Go implementation is as-of now hidden behind a new feature flag
"gitaly_go_user_merge_to_ref".
Diffstat (limited to 'internal/gitaly')
-rw-r--r-- | internal/gitaly/service/operations/merge.go | 66 | ||||
-rw-r--r-- | internal/gitaly/service/operations/merge_test.go | 21 |
2 files changed, 78 insertions, 9 deletions
diff --git a/internal/gitaly/service/operations/merge.go b/internal/gitaly/service/operations/merge.go index 2a19c9171..c6a94f3c1 100644 --- a/internal/gitaly/service/operations/merge.go +++ b/internal/gitaly/service/operations/merge.go @@ -318,11 +318,77 @@ func validateUserMergeToRefRequest(in *gitalypb.UserMergeToRefRequest) error { return nil } +// userMergeToRef overwrites the given TargetRef to point to either Branch or +// FirstParentRef. Afterwards, it performs a merge of SourceSHA with either +// Branch or FirstParentRef and updates TargetRef to the merge commit. +func (s *server) userMergeToRef(ctx context.Context, request *gitalypb.UserMergeToRefRequest) (*gitalypb.UserMergeToRefResponse, error) { + repoPath, err := s.locator.GetPath(request.Repository) + if err != nil { + return nil, err + } + + repo := git.NewRepository(request.Repository) + + refName := string(request.Branch) + if request.FirstParentRef != nil { + refName = string(request.FirstParentRef) + } + + ref, err := repo.ResolveRefish(ctx, refName) + if err != nil { + //nolint:stylecheck + return nil, helper.ErrInvalidArgument(errors.New("Invalid merge source")) + } + + sourceRef, err := repo.ResolveRefish(ctx, request.SourceSha) + if err != nil { + //nolint:stylecheck + return nil, helper.ErrInvalidArgument(errors.New("Invalid merge source")) + } + + // First, overwrite the reference with the target reference. + if err := repo.UpdateRef(ctx, string(request.TargetRef), ref, ""); err != nil { + return nil, updateRefError{reference: string(request.TargetRef)} + } + + // Now, we create the merge commit... + merge, err := git2go.MergeCommand{ + Repository: repoPath, + AuthorName: string(request.User.Name), + AuthorMail: string(request.User.Email), + Message: string(request.Message), + Ours: ref, + Theirs: sourceRef, + }.Run(ctx, s.cfg) + if err != nil { + if errors.Is(err, git2go.ErrInvalidArgument) { + return nil, helper.ErrInvalidArgument(err) + } + //nolint:stylecheck + return nil, fmt.Errorf("Failed to create merge commit for source_sha %s and target_sha %s at %s", sourceRef, string(request.TargetRef), refName) + } + + // ... and move branch from target ref to the merge commit. The Ruby + // implementation doesn't invoke hooks, so we don't either. + if err := repo.UpdateRef(ctx, string(request.TargetRef), merge.CommitID, ref); err != nil { + //nolint:stylecheck + return nil, helper.ErrPreconditionFailed(fmt.Errorf("Could not update %s. Please refresh and try again", string(request.TargetRef))) + } + + return &gitalypb.UserMergeToRefResponse{ + CommitId: merge.CommitID, + }, nil +} + func (s *server) UserMergeToRef(ctx context.Context, in *gitalypb.UserMergeToRefRequest) (*gitalypb.UserMergeToRefResponse, error) { if err := validateUserMergeToRefRequest(in); err != nil { return nil, helper.ErrInvalidArgument(err) } + if featureflag.IsEnabled(ctx, featureflag.GoUserMergeBranch) { + return s.userMergeToRef(ctx, in) + } + client, err := s.ruby.OperationServiceClient(ctx) if err != nil { return nil, helper.ErrInternal(err) diff --git a/internal/gitaly/service/operations/merge_test.go b/internal/gitaly/service/operations/merge_test.go index dd4a56fe6..3b4ef9e12 100644 --- a/internal/gitaly/service/operations/merge_test.go +++ b/internal/gitaly/service/operations/merge_test.go @@ -475,6 +475,10 @@ func TestFailedUserFFBranchDueToHooks(t *testing.T) { } func TestSuccessfulUserMergeToRefRequest(t *testing.T) { + testWithFeature(t, featureflag.GoUserMergeToRef, testSuccessfulUserMergeToRefRequest) +} + +func testSuccessfulUserMergeToRefRequest(t *testing.T, ctx context.Context) { serverSocketPath, stop := runOperationServiceServer(t) defer stop() @@ -536,9 +540,6 @@ func TestSuccessfulUserMergeToRefRequest(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.desc, func(t *testing.T) { - ctx, cancel := testhelper.Context() - defer cancel() - request := &gitalypb.UserMergeToRefRequest{ Repository: testRepo, User: testCase.user, @@ -584,6 +585,10 @@ func TestSuccessfulUserMergeToRefRequest(t *testing.T) { } func TestFailedUserMergeToRefRequest(t *testing.T) { + testWithFeature(t, featureflag.GoUserMergeToRef, testFailedUserMergeToRefRequest) +} + +func testFailedUserMergeToRefRequest(t *testing.T, ctx context.Context) { serverSocketPath, stop := runOperationServiceServer(t) defer stop() @@ -669,9 +674,6 @@ func TestFailedUserMergeToRefRequest(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.desc, func(t *testing.T) { - ctx, cancel := testhelper.Context() - defer cancel() - request := &gitalypb.UserMergeToRefRequest{ Repository: testCase.repo, User: testCase.user, @@ -686,6 +688,10 @@ func TestFailedUserMergeToRefRequest(t *testing.T) { } func TestUserMergeToRefIgnoreHooksRequest(t *testing.T) { + testWithFeature(t, featureflag.GoUserMergeToRef, testUserMergeToRefIgnoreHooksRequest) +} + +func testUserMergeToRefIgnoreHooksRequest(t *testing.T, ctx context.Context) { serverSocketPath, stop := runOperationServiceServer(t) defer stop() @@ -717,9 +723,6 @@ func TestUserMergeToRefIgnoreHooksRequest(t *testing.T) { require.NoError(t, err) defer remove() - ctx, cancel := testhelper.Context() - defer cancel() - resp, err := client.UserMergeToRef(ctx, request) require.NoError(t, err) require.Empty(t, resp.PreReceiveError) |