Commit ea18cbc1 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'create-merge-train-ref' into 'master'

Extend `MergeToRefService` to create merge ref from an arbitrary ref

See merge request gitlab-org/gitlab-ee!14298
parents 6474984f b0e4c6d4
......@@ -447,7 +447,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 1.32.0', require: 'gitaly'
gem 'gitaly-proto', '~> 1.36.0', require: 'gitaly'
gem 'grpc', '~> 1.19.0'
......
......@@ -328,7 +328,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (1.32.0)
gitaly-proto (1.36.0)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-labkit (0.3.0)
......@@ -1130,7 +1130,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 1.32.0)
gitaly-proto (~> 1.36.0)
github-markup (~> 1.7.0)
gitlab-labkit (~> 0.3.0)
gitlab-license (~> 1.0)
......
......@@ -839,10 +839,10 @@ class Repository
end
end
def merge_to_ref(user, source_sha, merge_request, target_ref, message)
def merge_to_ref(user, source_sha, merge_request, target_ref, message, first_parent_ref)
branch = merge_request.target_branch
raw.merge_to_ref(user, source_sha, branch, target_ref, message)
raw.merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref)
end
def ff_merge(user, source, target_branch, merge_request: nil)
......
# frozen_string_literal: true
module MergeRequests
# Performs the merge between source SHA and the target branch. Instead
# Performs the merge between source SHA and the target branch or the specified first parent ref. Instead
# of writing the result to the MR target branch, it targets the `target_ref`.
#
# Ideally this should leave the `target_ref` state with the same state the
......@@ -56,12 +56,22 @@ module MergeRequests
raise_error(error) if error
end
##
# The parameter `target_ref` is where the merge result will be written.
# Default is the merge ref i.e. `refs/merge-requests/:iid/merge`.
def target_ref
merge_request.merge_ref_path
params[:target_ref] || merge_request.merge_ref_path
end
##
# The parameter `first_parent_ref` is the main line of the merge commit.
# Default is the target branch ref of the merge request.
def first_parent_ref
params[:first_parent_ref] || merge_request.target_branch_ref
end
def commit
repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message)
repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message, first_parent_ref)
rescue Gitlab::Git::PreReceiveError => error
raise MergeError, error.message
end
......
---
title: Extend `MergeToRefService` to create merge ref from an arbitrary ref
merge_request: 30361
author:
type: added
......@@ -536,9 +536,9 @@ module Gitlab
tags.find { |tag| tag.name == name }
end
def merge_to_ref(user, source_sha, branch, target_ref, message)
def merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref)
wrapped_gitaly_errors do
gitaly_operation_client.user_merge_to_ref(user, source_sha, branch, target_ref, message)
gitaly_operation_client.user_merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref)
end
end
......
......@@ -100,14 +100,15 @@ module Gitlab
end
end
def user_merge_to_ref(user, source_sha, branch, target_ref, message)
def user_merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref)
request = Gitaly::UserMergeToRefRequest.new(
repository: @gitaly_repo,
source_sha: source_sha,
branch: encode_binary(branch),
target_ref: encode_binary(target_ref),
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
message: encode_binary(message)
message: encode_binary(message),
first_parent_ref: encode_binary(first_parent_ref)
)
response = GitalyClient.call(@repository.storage, :operation_service, :user_merge_to_ref, request)
......
......@@ -1694,14 +1694,15 @@ describe Gitlab::Git::Repository, :seed_helper do
let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
let(:left_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
let(:right_branch) { 'test-master' }
let(:first_parent_ref) { 'refs/heads/test-master' }
let(:target_ref) { 'refs/merge-requests/999/merge' }
before do
repository.create_branch(right_branch, branch_head) unless repository.branch_exists?(right_branch)
repository.create_branch(right_branch, branch_head) unless repository.ref_exists?(first_parent_ref)
end
def merge_to_ref
repository.merge_to_ref(user, left_sha, right_branch, target_ref, 'Merge message')
repository.merge_to_ref(user, left_sha, right_branch, target_ref, 'Merge message', first_parent_ref)
end
it 'generates a commit in the target_ref' do
......@@ -1716,7 +1717,7 @@ describe Gitlab::Git::Repository, :seed_helper do
end
it 'does not change the right branch HEAD' do
expect { merge_to_ref }.not_to change { repository.find_branch(right_branch).target }
expect { merge_to_ref }.not_to change { repository.commit(first_parent_ref).sha }
end
end
......
......@@ -79,13 +79,13 @@ describe Gitlab::GitalyClient::OperationService do
end
describe '#user_merge_to_ref' do
let(:branch) { 'my-branch' }
let(:first_parent_ref) { 'refs/heads/my-branch' }
let(:source_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
let(:ref) { 'refs/merge-requests/x/merge' }
let(:message) { 'validación' }
let(:response) { Gitaly::UserMergeToRefResponse.new(commit_id: 'new-commit-id') }
subject { client.user_merge_to_ref(user, source_sha, branch, ref, message) }
subject { client.user_merge_to_ref(user, source_sha, nil, ref, message, first_parent_ref) }
it 'sends a user_merge_to_ref message' do
expect_any_instance_of(Gitaly::OperationService::Stub)
......
......@@ -1420,12 +1420,13 @@ describe Repository do
source_project: project)
end
it 'writes merge of source and target to MR merge_ref_path' do
it 'writes merge of source SHA and first parent ref to MR merge_ref_path' do
merge_commit_id = repository.merge_to_ref(user,
merge_request.diff_head_sha,
merge_request,
merge_request.merge_ref_path,
'Custom message')
'Custom message',
merge_request.target_branch_ref)
merge_commit = repository.commit(merge_commit_id)
......
......@@ -22,7 +22,6 @@ describe MergeRequests::MergeToRefService do
shared_examples_for 'successfully merges to ref with merge method' do
it 'writes commit to merge ref' do
repository = project.repository
target_ref = merge_request.merge_ref_path
expect(repository.ref_exists?(target_ref)).to be(false)
......@@ -33,7 +32,7 @@ describe MergeRequests::MergeToRefService do
expect(result[:status]).to eq(:success)
expect(result[:commit_id]).to be_present
expect(result[:source_id]).to eq(merge_request.source_branch_sha)
expect(result[:target_id]).to eq(merge_request.target_branch_sha)
expect(result[:target_id]).to eq(repository.commit(first_parent_ref).sha)
expect(repository.ref_exists?(target_ref)).to be(true)
expect(ref_head.id).to eq(result[:commit_id])
end
......@@ -74,17 +73,22 @@ describe MergeRequests::MergeToRefService do
describe '#execute' do
let(:service) do
described_class.new(project, user, commit_message: 'Awesome message',
should_remove_source_branch: true)
described_class.new(project, user, **params)
end
let(:params) { { commit_message: 'Awesome message', should_remove_source_branch: true } }
def process_merge_to_ref
perform_enqueued_jobs do
service.execute(merge_request)
end
end
it_behaves_like 'successfully merges to ref with merge method'
it_behaves_like 'successfully merges to ref with merge method' do
let(:first_parent_ref) { 'refs/heads/master' }
let(:target_ref) { merge_request.merge_ref_path }
end
it_behaves_like 'successfully evaluates pre-condition checks'
context 'commit history comparison with regular MergeService' do
......@@ -129,14 +133,22 @@ describe MergeRequests::MergeToRefService do
context 'when semi-linear merge method' do
let(:merge_method) { :rebase_merge }
it_behaves_like 'successfully merges to ref with merge method'
it_behaves_like 'successfully merges to ref with merge method' do
let(:first_parent_ref) { 'refs/heads/master' }
let(:target_ref) { merge_request.merge_ref_path }
end
it_behaves_like 'successfully evaluates pre-condition checks'
end
context 'when fast-forward merge method' do
let(:merge_method) { :ff }
it_behaves_like 'successfully merges to ref with merge method'
it_behaves_like 'successfully merges to ref with merge method' do
let(:first_parent_ref) { 'refs/heads/master' }
let(:target_ref) { merge_request.merge_ref_path }
end
it_behaves_like 'successfully evaluates pre-condition checks'
end
......@@ -178,5 +190,34 @@ describe MergeRequests::MergeToRefService do
it { expect(todo).not_to be_done }
end
describe 'cascading merge refs' do
set(:project) { create(:project, :repository) }
let(:params) { { commit_message: 'Cascading merge', first_parent_ref: first_parent_ref, target_ref: target_ref } }
context 'when first merge happens' do
let(:merge_request) do
create(:merge_request, source_project: project, source_branch: 'feature',
target_project: project, target_branch: 'master')
end
it_behaves_like 'successfully merges to ref with merge method' do
let(:first_parent_ref) { 'refs/heads/master' }
let(:target_ref) { 'refs/merge-requests/1/train' }
end
context 'when second merge happens' do
let(:merge_request) do
create(:merge_request, source_project: project, source_branch: 'improve/awesome',
target_project: project, target_branch: 'master')
end
it_behaves_like 'successfully merges to ref with merge method' do
let(:first_parent_ref) { 'refs/merge-requests/1/train' }
let(:target_ref) { 'refs/merge-requests/2/train' }
end
end
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment