diff options
author | Robert Speicher <rspeicher@gmail.com> | 2018-11-07 07:27:14 +0300 |
---|---|---|
committer | Robert Speicher <rspeicher@gmail.com> | 2018-11-09 18:26:38 +0300 |
commit | 2331d3af63122e7b2419bce2e5e6e5bdc63cd2d8 (patch) | |
tree | 8961f5dec1589228b6b3652a901174f4d8126af5 | |
parent | cc12b57c3ff6a46583abecc76e366e59426cf14c (diff) |
Add revert to commits API
-rw-r--r-- | changelogs/unreleased/rs-revert-api.yml | 5 | ||||
-rw-r--r-- | doc/api/commits.md | 42 | ||||
-rw-r--r-- | lib/api/commits.rb | 34 | ||||
-rw-r--r-- | spec/requests/api/commits_spec.rb | 112 |
4 files changed, 193 insertions, 0 deletions
diff --git a/changelogs/unreleased/rs-revert-api.yml b/changelogs/unreleased/rs-revert-api.yml new file mode 100644 index 00000000000..c07b2fe624c --- /dev/null +++ b/changelogs/unreleased/rs-revert-api.yml @@ -0,0 +1,5 @@ +--- +title: Add revert to commits API +merge_request: 22919 +author: +type: added diff --git a/doc/api/commits.md b/doc/api/commits.md index 9b7ca4b6e70..994eefa423f 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -288,6 +288,47 @@ Example response: } ``` +## Revert a commit + +> [Introduced][ce-22919] in GitLab 11.6. + +Reverts a commit in a given branch. + +``` +POST /projects/:id/repository/commits/:sha/revert +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | +| `sha` | string | yes | Commit SHA to revert | +| `branch` | string | yes | Target branch name | + +```bash +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "branch=master" "https://gitlab.example.com/api/v4/projects/5/repository/commits/a738f717824ff53aebad8b090c1b79a14f2bd9e8/revert" +``` + +Example response: + +```json +{ + "id":"8b090c1b79a14f2bd9e8a738f717824ff53aebad", + "short_id": "8b090c1b", + "title":"Revert \"Feature added\"", + "created_at":"2018-11-08T15:55:26.000Z", + "parent_ids":["a738f717824ff53aebad8b090c1b79a14f2bd9e8"], + "message":"Revert \"Feature added\"\n\nThis reverts commit a738f717824ff53aebad8b090c1b79a14f2bd9e8", + "author_name":"Administrator", + "author_email":"admin@example.com", + "authored_date":"2018-11-08T15:55:26.000Z", + "committer_name":"Administrator", + "committer_email":"admin@example.com", + "committed_date":"2018-11-08T15:55:26.000Z" +} +``` + ## Get the diff of a commit Get the diff of a commit in a project. @@ -619,3 +660,4 @@ Example response: [ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047 [ce-15026]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15026 [ce-18004]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18004 +[ce-22919]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22919 diff --git a/lib/api/commits.rb b/lib/api/commits.rb index e59abd3e3d0..1b228069005 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -204,6 +204,40 @@ module API end end + desc 'Revert a commit in a branch' do + detail 'This feature was introduced in GitLab 11.6' + success Entities::Commit + end + params do + requires :sha, type: String, desc: 'Commit SHA to revert' + requires :branch, type: String, desc: 'Target branch name', allow_blank: false + end + post ':id/repository/commits/:sha/revert', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do + authorize_push_to_branch!(params[:branch]) + + commit = user_project.commit(params[:sha]) + not_found!('Commit') unless commit + + find_branch!(params[:branch]) + + commit_params = { + commit: commit, + start_branch: params[:branch], + branch_name: params[:branch] + } + + result = ::Commits::RevertService + .new(user_project, current_user, commit_params) + .execute + + if result[:status] == :success + present user_project.repository.commit(result[:result]), + with: Entities::Commit + else + render_api_error!(result[:message], 400) + end + end + desc 'Get all references a commit is pushed to' do detail 'This feature was introduced in GitLab 10.6' success Entities::BasicRef diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 2963dea634a..329d069ef3d 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -1208,6 +1208,118 @@ describe API::Commits do end end + describe 'POST :id/repository/commits/:sha/revert' do + let(:commit_id) { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' } + let(:commit) { project.commit(commit_id) } + let(:branch) { 'master' } + let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/revert" } + + shared_examples_for 'ref revert' do + context 'when ref exists' do + it 'reverts the ref commit' do + post api(route, current_user), branch: branch + + expect(response).to have_gitlab_http_status(201) + expect(response).to match_response_schema('public_api/v4/commit/basic') + + expect(json_response['message']).to eq(commit.revert_message(user)) + expect(json_response['author_name']).to eq(user.name) + expect(json_response['committer_name']).to eq(user.name) + expect(json_response['parent_ids']).to contain_exactly(commit_id) + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { post api(route, current_user), branch: branch } + end + end + end + + context 'when unauthenticated', 'and project is public' do + let(:project) { create(:project, :public, :repository) } + + it_behaves_like '403 response' do + let(:request) { post api(route), branch: branch } + end + end + + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { post api(route), branch: branch } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as an owner' do + let(:current_user) { user } + + it_behaves_like 'ref revert' + + context 'when ref does not exist' do + let(:commit_id) { 'unknown' } + + it_behaves_like '404 response' do + let(:request) { post api(route, current_user), branch: branch } + let(:message) { '404 Commit Not Found' } + end + end + + context 'when branch is missing' do + it_behaves_like '400 response' do + let(:request) { post api(route, current_user) } + end + end + + context 'when branch is empty' do + ['', ' '].each do |branch| + it_behaves_like '400 response' do + let(:request) { post api(route, current_user), branch: branch } + end + end + end + + context 'when branch does not exist' do + it_behaves_like '404 response' do + let(:request) { post api(route, current_user), branch: 'foo' } + let(:message) { '404 Branch Not Found' } + end + end + + context 'when ref contains a dot' do + let(:commit_id) { branch_with_dot.name } + let(:commit) { project.repository.commit(commit_id) } + + it_behaves_like '400 response' do + let(:request) { post api(route, current_user) } + end + end + end + + context 'when authenticated', 'as a developer' do + let(:current_user) { user } + + before do + project.add_developer(user) + end + + context 'when branch is protected' do + before do + create(:protected_branch, project: project, name: 'feature') + end + + it 'returns 400 if you are not allowed to push to the target branch' do + post api(route, current_user), branch: 'feature' + + expect(response).to have_gitlab_http_status(:forbidden) + expect(json_response['message']).to match(/You are not allowed to push into this branch/) + end + end + end + end + describe 'POST /projects/:id/repository/commits/:sha/comments' do let(:commit) { project.repository.commit } let(:commit_id) { commit.id } |