diff --git a/app/services/merge_requests/merge_base_service.rb b/app/services/merge_requests/merge_base_service.rb index 6ac6fcf562a..095bdca5472 100644 --- a/app/services/merge_requests/merge_base_service.rb +++ b/app/services/merge_requests/merge_base_service.rb @@ -15,10 +15,7 @@ module MergeRequests # Overridden in EE. def hooks_validation_error(_merge_request) -<<<<<<< HEAD # No-op -======= ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash end def source @@ -31,7 +28,6 @@ module MergeRequests private -<<<<<<< HEAD # Overridden in EE. def error_check! # No-op @@ -41,8 +37,6 @@ module MergeRequests raise MergeError, message end -======= ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash def handle_merge_error(*args) # No-op end diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb index 43bb9f3f2f2..586652ae44e 100644 --- a/app/services/merge_requests/merge_to_ref_service.rb +++ b/app/services/merge_requests/merge_to_ref_service.rb @@ -14,19 +14,11 @@ module MergeRequests def execute(merge_request) @merge_request = merge_request -<<<<<<< HEAD validate! commit_id = commit raise_error('Conflicts detected during merge') unless commit_id -======= - error_check! - - commit_id = commit - - raise MergeError, 'Conflicts detected during merge' unless commit_id ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash success(commit_id: commit_id) rescue MergeError => error @@ -35,7 +27,6 @@ module MergeRequests private -<<<<<<< HEAD def validate! authorization_check! error_check! @@ -48,11 +39,6 @@ module MergeRequests if Feature.disabled?(:merge_to_tmp_merge_ref_path, project) 'Feature is not enabled' elsif !merge_method_supported? -======= - def error_check! - error = - if !merge_method_supported? ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash "#{project.human_merge_method} to #{target_ref} is currently not supported." elsif !hooks_validation_pass?(merge_request) hooks_validation_error(merge_request) @@ -64,7 +50,6 @@ module MergeRequests 'No source for merge' end -<<<<<<< HEAD raise_error(error) if error end @@ -72,9 +57,6 @@ module MergeRequests unless Ability.allowed?(current_user, :admin_merge_request, project) raise_error("You are not allowed to merge to this ref") end -======= - raise MergeError, error if error ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash end def target_ref diff --git a/changelogs/unreleased/osw-merge-refs-refreshing-api.yml b/changelogs/unreleased/osw-merge-refs-refreshing-api.yml new file mode 100644 index 00000000000..095600cd088 --- /dev/null +++ b/changelogs/unreleased/osw-merge-refs-refreshing-api.yml @@ -0,0 +1,5 @@ +--- +title: API support for MR merge to temporary merge ref path +merge_request: 24918 +author: +type: added diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index e176cdffc5f..ed4b6281acc 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -1101,6 +1101,40 @@ Parameters: } ``` +## Merge to default merge ref path + +Merge the changes between the merge request source and target branches into `refs/merge-requests/:iid/merge` +ref, of the target project repository. This ref will have the state the target branch would have if +a regular merge action was taken. + +This is not a regular merge action given it doesn't change the merge request state in any manner. + +This ref (`refs/merge-requests/:iid/merge`) is **always** overwritten when submitting +requests to this API, so none of its state is kept or used in the process. + +If the merge request has conflicts, is empty or already merged, +you'll get a `400` and a descriptive error message. If you don't have permissions to do so, +you'll get a `403`. + +It returns the HEAD commit of `refs/merge-requests/:iid/merge` in the response body in +case of `200`. + +``` +PUT /projects/:id/merge_requests/:merge_request_iid/merge_to_ref +``` + +Parameters: + +- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user +- `merge_request_iid` (required) - Internal ID of MR +- `merge_commit_message` (optional) - Custom merge commit message + +```json +{ + "commit_id": "854a3a7a17acbcc0bbbea170986df1eb60435f34" +} +``` + ## Cancel Merge When Pipeline Succeeds If you don't have permissions to accept this merge request - you'll get a `401` diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 03f6684226f..44f1e81caf2 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -388,6 +388,31 @@ module API present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project end + desc 'Merge a merge request to its default temporary merge ref path' + params do + optional :merge_commit_message, type: String, desc: 'Custom merge commit message' + end + put ':id/merge_requests/:merge_request_iid/merge_to_ref' do + merge_request = find_project_merge_request(params[:merge_request_iid]) + + authorize! :admin_merge_request, user_project + + merge_params = { + commit_message: params[:merge_commit_message] + } + + result = ::MergeRequests::MergeToRefService + .new(merge_request.target_project, current_user, merge_params) + .execute(merge_request) + + if result[:status] == :success + present result.slice(:commit_id), 200 + else + http_status = result[:http_status] || 400 + render_api_error!(result[:message], http_status) + end + end + desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do success Entities::MergeRequest end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index b4cd3130dc5..273c52bd719 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1001,6 +1001,67 @@ describe API::MergeRequests do end end + describe "PUT /projects/:id/merge_requests/:merge_request_iid/merge_to_ref" do + let(:pipeline) { create(:ci_pipeline_without_jobs) } + let(:url) do + "/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge_to_ref" + end + + it 'returns the generated ID from the merge service in case of success' do + put api(url, user), params: { merge_commit_message: 'Custom message' } + + commit = project.commit(json_response['commit_id']) + + expect(response).to have_gitlab_http_status(200) + expect(json_response['commit_id']).to be_present + expect(commit.message).to eq('Custom message') + end + + it "returns 400 if branch can't be merged" do + merge_request.update!(state: 'merged') + + put api(url, user) + + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']) + .to eq("Merge request is not mergeable to #{merge_request.merge_ref_path}") + end + + it 'returns 403 if user has no permissions to merge to the ref' do + user2 = create(:user) + project.add_reporter(user2) + + put api(url, user2) + + expect(response).to have_gitlab_http_status(403) + expect(json_response['message']).to eq('403 Forbidden') + end + + it 'returns 404 for an invalid merge request IID' do + put api("/projects/#{project.id}/merge_requests/12345/merge_to_ref", user) + + expect(response).to have_gitlab_http_status(404) + end + + it "returns 404 if the merge request id is used instead of iid" do + put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) + + expect(response).to have_gitlab_http_status(404) + end + + it "returns 400 when merge method is not supported" do + merge_request.project.update!(merge_method: 'ff') + + put api(url, user) + + expected_error = + 'Fast-forward to refs/merge-requests/1/merge is currently not supported.' + + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']).to eq(expected_error) + end + end + describe "PUT /projects/:id/merge_requests/:merge_request_iid" do context "to close a MR" do it "returns merge_request" do diff --git a/spec/services/merge_requests/merge_to_ref_service_spec.rb b/spec/services/merge_requests/merge_to_ref_service_spec.rb index f13e3524f42..96f2fde7117 100644 --- a/spec/services/merge_requests/merge_to_ref_service_spec.rb +++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb @@ -3,7 +3,6 @@ require 'spec_helper' describe MergeRequests::MergeToRefService do -<<<<<<< HEAD shared_examples_for 'MergeService for target ref' do it 'target_ref has the same state of target branch' do repo = merge_request.target_project.repository @@ -20,8 +19,6 @@ describe MergeRequests::MergeToRefService do end end -======= ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash set(:user) { create(:user) } let(:merge_request) { create(:merge_request, :simple) } let(:project) { merge_request.project } @@ -77,7 +74,6 @@ describe MergeRequests::MergeToRefService do process_merge_to_ref end -<<<<<<< HEAD it 'returns error when feature is disabled' do stub_feature_flags(merge_to_tmp_merge_ref_path: false) @@ -87,8 +83,6 @@ describe MergeRequests::MergeToRefService do expect(result[:message]).to eq('Feature is not enabled') end -======= ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash it 'returns an error when the failing to process the merge' do allow(project.repository).to receive(:merge_to_ref).and_return(nil) @@ -107,25 +101,6 @@ describe MergeRequests::MergeToRefService do MergeRequests::MergeService.new(project, user, {}) end -<<<<<<< HEAD -======= - shared_examples_for 'MergeService for target ref' do - it 'target_ref has the same state of target branch' do - repo = merge_request.target_project.repository - - process_merge_to_ref - merge_service.execute(merge_request) - - ref_commits = repo.commits(merge_request.merge_ref_path, limit: 3) - target_branch_commits = repo.commits(merge_request.target_branch, limit: 3) - - ref_commits.zip(target_branch_commits).each do |ref_commit, target_branch_commit| - expect(ref_commit.parents).to eq(target_branch_commit.parents) - end - end - end - ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash context 'when merge commit' do it_behaves_like 'MergeService for target ref' end @@ -210,7 +185,6 @@ describe MergeRequests::MergeToRefService do it { expect(todo).not_to be_done } end -<<<<<<< HEAD it 'returns error when user has no authorization to admin the merge request' do unauthorized_user = create(:user) @@ -223,7 +197,5 @@ describe MergeRequests::MergeToRefService do expect(result[:status]).to eq(:error) expect(result[:message]).to eq('You are not allowed to merge to this ref') end -======= ->>>>>>> 89c57ca2673... Support merge to ref for merge-commit and squash end end