Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2020-09-21 14:14:35 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2020-09-22 11:14:26 +0300
commitfe8b701dac4a47ff490e7c3742cf1cf1a898febe (patch)
treee066880b978e39af28fd5508d020c65021486025
parent542161b021db9c7304edc446b153c8d7478f237b (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".
-rw-r--r--changelogs/unreleased/pks-go-user-merge-to-ref.yml5
-rw-r--r--internal/gitaly/service/operations/merge.go66
-rw-r--r--internal/gitaly/service/operations/merge_test.go21
-rw-r--r--internal/metadata/featureflag/feature_flags.go3
4 files changed, 86 insertions, 9 deletions
diff --git a/changelogs/unreleased/pks-go-user-merge-to-ref.yml b/changelogs/unreleased/pks-go-user-merge-to-ref.yml
new file mode 100644
index 000000000..4a206a734
--- /dev/null
+++ b/changelogs/unreleased/pks-go-user-merge-to-ref.yml
@@ -0,0 +1,5 @@
+---
+title: Port operations.UserMergeToRef to Go
+merge_request: 2580
+author:
+type: performance
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)
diff --git a/internal/metadata/featureflag/feature_flags.go b/internal/metadata/featureflag/feature_flags.go
index 836db2201..43b34c8a4 100644
--- a/internal/metadata/featureflag/feature_flags.go
+++ b/internal/metadata/featureflag/feature_flags.go
@@ -36,6 +36,8 @@ var (
LogCommandStats = FeatureFlag{Name: "log_command_stats", OnByDefault: false}
// GoUserMergeBranch enables the Go implementation of UserMergeBranch
GoUserMergeBranch = FeatureFlag{Name: "go_user_merge_branch", OnByDefault: false}
+ // GoUserMergeToRef enable the Go implementation of UserMergeToRef
+ GoUserMergeToRef = FeatureFlag{Name: "go_user_merge_to_ref", OnByDefault: false}
)
// All includes all feature flags.
@@ -51,6 +53,7 @@ var All = []FeatureFlag{
ReferenceTransactionHook,
RubyReferenceTransactionHook,
GoUserMergeBranch,
+ GoUserMergeToRef,
}
const (