2019-07-07 11:18:12 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module MergeRequests
|
2019-09-30 21:07:59 +05:30
|
|
|
# Performs the merge between source SHA and the target branch or the specified first parent ref. Instead
|
2019-07-07 11:18:12 +05:30
|
|
|
# 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
|
|
|
|
# target branch would have if we used the regular `MergeService`, but without
|
|
|
|
# every side-effect that comes with it (MR updates, mails, source branch
|
|
|
|
# deletion, etc). This service should be kept idempotent (i.e. can
|
|
|
|
# be executed regardless of the `target_ref` current state).
|
|
|
|
#
|
|
|
|
class MergeToRefService < MergeRequests::MergeBaseService
|
2019-09-30 21:07:59 +05:30
|
|
|
extend ::Gitlab::Utils::Override
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
def execute(merge_request, cache_merge_to_ref_calls = false)
|
2019-07-07 11:18:12 +05:30
|
|
|
@merge_request = merge_request
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
error_check!
|
2019-07-07 11:18:12 +05:30
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
commit_id = commit(cache_merge_to_ref_calls)
|
2019-07-07 11:18:12 +05:30
|
|
|
|
|
|
|
raise_error('Conflicts detected during merge') unless commit_id
|
|
|
|
|
|
|
|
commit = project.commit(commit_id)
|
|
|
|
target_id, source_id = commit.parent_ids
|
|
|
|
|
|
|
|
success(commit_id: commit.id,
|
|
|
|
target_id: target_id,
|
|
|
|
source_id: source_id)
|
2019-09-30 21:07:59 +05:30
|
|
|
rescue MergeError, ArgumentError => error
|
2019-07-07 11:18:12 +05:30
|
|
|
error(error.message)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
override :source
|
|
|
|
def source
|
|
|
|
merge_request.diff_head_sha
|
2019-07-07 11:18:12 +05:30
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
override :error_check!
|
2019-07-07 11:18:12 +05:30
|
|
|
def error_check!
|
2019-09-30 21:07:59 +05:30
|
|
|
check_source
|
2019-07-07 11:18:12 +05:30
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
##
|
|
|
|
# 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
|
|
|
|
params[:target_ref] || merge_request.merge_ref_path
|
2019-07-07 11:18:12 +05:30
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
##
|
|
|
|
# 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
|
2019-07-07 11:18:12 +05:30
|
|
|
end
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
##
|
|
|
|
# The parameter `allow_conflicts` is a flag whether merge conflicts should be merged into diff
|
|
|
|
# Default is false
|
|
|
|
def allow_conflicts
|
|
|
|
params[:allow_conflicts] || false
|
|
|
|
end
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
def commit(cache_merge_to_ref_calls = false)
|
|
|
|
if cache_merge_to_ref_calls
|
2021-10-27 15:23:28 +05:30
|
|
|
Rails.cache.fetch(cache_key, expires_in: 1.day) do
|
|
|
|
extracted_merge_to_ref
|
|
|
|
end
|
|
|
|
else
|
|
|
|
extracted_merge_to_ref
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def extracted_merge_to_ref
|
2021-04-29 21:17:54 +05:30
|
|
|
repository.merge_to_ref(current_user,
|
|
|
|
source_sha: source,
|
|
|
|
branch: merge_request.target_branch,
|
|
|
|
target_ref: target_ref,
|
|
|
|
message: commit_message,
|
|
|
|
first_parent_ref: first_parent_ref,
|
|
|
|
allow_conflicts: allow_conflicts)
|
2020-04-08 14:13:33 +05:30
|
|
|
rescue Gitlab::Git::PreReceiveError, Gitlab::Git::CommandError => error
|
2019-07-07 11:18:12 +05:30
|
|
|
raise MergeError, error.message
|
|
|
|
end
|
2021-10-27 15:23:28 +05:30
|
|
|
|
|
|
|
def cache_key
|
|
|
|
[:merge_to_ref_service, project.full_path, merge_request.target_branch_sha, merge_request.source_branch_sha]
|
|
|
|
end
|
2019-07-07 11:18:12 +05:30
|
|
|
end
|
|
|
|
end
|