Commit 68526805 authored by Sean McGivern's avatar Sean McGivern

Merge branch '33705-merge-request-rebase-api' into 'master'

Add a rebase API endpoint for merge requests

Closes #33705

See merge request gitlab-org/gitlab-ce!23296
parents c07183f0 04ceb69f
---
title: Add a rebase API endpoint for merge requests
merge_request: 23296
author:
type: added
...@@ -408,6 +408,7 @@ Parameters: ...@@ -408,6 +408,7 @@ Parameters:
- `merge_request_iid` (required) - The internal ID of the merge request - `merge_request_iid` (required) - The internal ID of the merge request
- `render_html` (optional) - If `true` response includes rendered HTML for title and description - `render_html` (optional) - If `true` response includes rendered HTML for title and description
- `include_diverged_commits_count` (optional) - If `true` response includes the commits behind the target branch - `include_diverged_commits_count` (optional) - If `true` response includes the commits behind the target branch
- `include_rebase_in_progress` (optional) - If `true` response includes whether a rebase operation is in progress
```json ```json
{ {
...@@ -461,6 +462,7 @@ Parameters: ...@@ -461,6 +462,7 @@ Parameters:
}, },
"merge_when_pipeline_succeeds": true, "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged", "merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888", "sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null, "merge_commit_sha": null,
"user_notes_count": 1, "user_notes_count": 1,
...@@ -505,7 +507,8 @@ Parameters: ...@@ -505,7 +507,8 @@ Parameters:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
}, },
"diverged_commits_count": 2 "diverged_commits_count": 2,
"rebase_in_progress": false
} }
``` ```
...@@ -773,6 +776,7 @@ POST /projects/:id/merge_requests ...@@ -773,6 +776,7 @@ POST /projects/:id/merge_requests
}, },
"merge_when_pipeline_succeeds": true, "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged", "merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888", "sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null, "merge_commit_sha": null,
"user_notes_count": 1, "user_notes_count": 1,
...@@ -900,6 +904,7 @@ Must include at least one non-required attribute from above. ...@@ -900,6 +904,7 @@ Must include at least one non-required attribute from above.
}, },
"merge_when_pipeline_succeeds": true, "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged", "merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888", "sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null, "merge_commit_sha": null,
"user_notes_count": 1, "user_notes_count": 1,
...@@ -1043,6 +1048,7 @@ Parameters: ...@@ -1043,6 +1048,7 @@ Parameters:
}, },
"merge_when_pipeline_succeeds": true, "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged", "merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888", "sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null, "merge_commit_sha": null,
"user_notes_count": 1, "user_notes_count": 1,
...@@ -1158,6 +1164,7 @@ Parameters: ...@@ -1158,6 +1164,7 @@ Parameters:
}, },
"merge_when_pipeline_succeeds": false, "merge_when_pipeline_succeeds": false,
"merge_status": "can_be_merged", "merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888", "sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null, "merge_commit_sha": null,
"user_notes_count": 1, "user_notes_count": 1,
...@@ -1206,6 +1213,62 @@ Parameters: ...@@ -1206,6 +1213,62 @@ Parameters:
} }
``` ```
## Rebase a merge request
Automatically rebase the `source_branch` of the merge request against its
`target_branch`.
If you don't have permissions to push to the merge request's source branch -
you'll get a `403 Forbidden` response.
```
PUT /projects/:id/merge_requests/:merge_request_iid/rebase
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/76/merge_requests/1/rebase
```
This is an asynchronous request. The API will return an empty `202 Accepted`
response if the request is enqueued successfully.
You can poll the [Get single MR](#get-single-mr) endpoint with the
`include_rebase_in_progress` parameter to check the status of the
asynchronous request.
If the rebase operation is ongoing, the response will include the following:
```json
{
"rebase_in_progress": true
"merge_error": null
}
```
Once the rebase operation has completed successfully, the response will include
the following:
```json
{
"rebase_in_progress": false,
"merge_error": null,
}
```
If the rebase operation fails, the response will include the following:
```json
{
"rebase_in_progress": false,
"merge_error": "Rebase failed. Please rebase locally",
}
```
## Comments on merge requests ## Comments on merge requests
Comments are done via the [notes](notes.md) resource. Comments are done via the [notes](notes.md) resource.
......
...@@ -715,6 +715,10 @@ module API ...@@ -715,6 +715,10 @@ module API
expose :diff_refs, using: Entities::DiffRefs expose :diff_refs, using: Entities::DiffRefs
# Allow the status of a rebase to be determined
expose :merge_error
expose :rebase_in_progress?, as: :rebase_in_progress, if: -> (_, options) { options[:include_rebase_in_progress] }
expose :diverged_commits_count, as: :diverged_commits_count, if: -> (_, options) { options[:include_diverged_commits_count] } expose :diverged_commits_count, as: :diverged_commits_count, if: -> (_, options) { options[:include_diverged_commits_count] }
def build_available?(options) def build_available?(options)
......
...@@ -74,6 +74,19 @@ module API ...@@ -74,6 +74,19 @@ module API
options options
end end
def authorize_push_to_merge_request!(merge_request)
forbidden!('Source branch does not exist') unless
merge_request.source_branch_exists?
user_access = Gitlab::UserAccess.new(
current_user,
project: merge_request.source_project
)
forbidden!('Cannot push to source branch') unless
user_access.can_push_to_branch?(merge_request.source_branch)
end
params :merge_requests_params do params :merge_requests_params do
optional :state, type: String, values: %w[opened closed locked merged all], default: 'all', optional :state, type: String, values: %w[opened closed locked merged all], default: 'all',
desc: 'Return opened, closed, locked, merged, or all merge requests' desc: 'Return opened, closed, locked, merged, or all merge requests'
...@@ -239,6 +252,7 @@ module API ...@@ -239,6 +252,7 @@ module API
requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request' requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML' optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML'
optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch' optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch'
optional :include_rebase_in_progress, type: Boolean, desc: 'Returns whether a rebase operation is ongoing '
end end
desc 'Get a single merge request' do desc 'Get a single merge request' do
success Entities::MergeRequest success Entities::MergeRequest
...@@ -246,7 +260,13 @@ module API ...@@ -246,7 +260,13 @@ module API
get ':id/merge_requests/:merge_request_iid' do get ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html], include_diverged_commits_count: params[:include_diverged_commits_count] present merge_request,
with: Entities::MergeRequest,
current_user: current_user,
project: user_project,
render_html: params[:render_html],
include_diverged_commits_count: params[:include_diverged_commits_count],
include_rebase_in_progress: params[:include_rebase_in_progress]
end end
desc 'Get the participants of a merge request' do desc 'Get the participants of a merge request' do
...@@ -378,6 +398,19 @@ module API ...@@ -378,6 +398,19 @@ module API
.cancel(merge_request) .cancel(merge_request)
end end
desc 'Rebase the merge request against its target branch' do
detail 'This feature was added in GitLab 11.6'
end
put ':id/merge_requests/:merge_request_iid/rebase' do
merge_request = find_project_merge_request(params[:merge_request_iid])
authorize_push_to_merge_request!(merge_request)
RebaseWorker.perform_async(merge_request.id, current_user.id)
status :accepted
end
desc 'List issues that will be closed on merge' do desc 'List issues that will be closed on merge' do
success Entities::MRNote success Entities::MRNote
end end
......
...@@ -359,6 +359,8 @@ describe API::MergeRequests do ...@@ -359,6 +359,8 @@ describe API::MergeRequests do
expect(json_response['should_close_merge_request']).to be_falsy expect(json_response['should_close_merge_request']).to be_falsy
expect(json_response['force_close_merge_request']).to be_falsy expect(json_response['force_close_merge_request']).to be_falsy
expect(json_response['changes_count']).to eq(merge_request.merge_request_diff.real_size) expect(json_response['changes_count']).to eq(merge_request.merge_request_diff.real_size)
expect(json_response['merge_error']).to eq(merge_request.merge_error)
expect(json_response).not_to include('rebase_in_progress')
end end
it 'exposes description and title html when render_html is true' do it 'exposes description and title html when render_html is true' do
...@@ -369,6 +371,14 @@ describe API::MergeRequests do ...@@ -369,6 +371,14 @@ describe API::MergeRequests do
expect(json_response).to include('title_html', 'description_html') expect(json_response).to include('title_html', 'description_html')
end end
it 'exposes rebase_in_progress when include_rebase_in_progress is true' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), include_rebase_in_progress: true
expect(response).to have_gitlab_http_status(200)
expect(json_response).to include('rebase_in_progress')
end
context 'merge_request_metrics' do context 'merge_request_metrics' do
before do before do
merge_request.metrics.update!(merged_by: user, merge_request.metrics.update!(merged_by: user,
...@@ -1181,6 +1191,26 @@ describe API::MergeRequests do ...@@ -1181,6 +1191,26 @@ describe API::MergeRequests do
end end
end end
describe 'PUT :id/merge_requests/:merge_request_iid/rebase' do
it 'enqueues a rebase of the merge request against the target branch' do
Sidekiq::Testing.fake! do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", user)
end
expect(response).to have_gitlab_http_status(202)
expect(RebaseWorker.jobs.size).to eq(1)
end
it 'returns 403 if the user cannot push to the branch' do
guest = create(:user)
project.add_guest(guest)
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", guest)
expect(response).to have_gitlab_http_status(403)
end
end
describe 'Time tracking' do describe 'Time tracking' do
let(:issuable) { merge_request } let(:issuable) { merge_request }
......
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