diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 826a48fdcf922d83de51772fb1cb8581296d826a..1d0ab403f12b22fb1f8d58d309428f40f8e0f42d 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -126,9 +126,23 @@ def create_branch_change_note(issuable, branch_type, old_branch, new_branch) override :handle_quick_actions def handle_quick_actions(merge_request) super + + # Ensure this parameter does not get used as an attribute + rebase = params.delete(:rebase) + + if rebase + rebase_from_quick_action(merge_request) + # Ignore "/merge" if "/rebase" is used to avoid an unexpected race + params.delete(:merge) + end + merge_from_quick_action(merge_request) if params[:merge] end + def rebase_from_quick_action(merge_request) + merge_request.rebase_async(current_user.id) + end + def merge_from_quick_action(merge_request) last_diff_sha = params.delete(:merge) diff --git a/changelogs/unreleased/sh-quick-action-rebase.yml b/changelogs/unreleased/sh-quick-action-rebase.yml new file mode 100644 index 0000000000000000000000000000000000000000..a790d15228a0b438c08220661710150a2f8c1a36 --- /dev/null +++ b/changelogs/unreleased/sh-quick-action-rebase.yml @@ -0,0 +1,5 @@ +--- +title: Add a quick action for /rebase +merge_request: 49800 +author: +type: added diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 9f849051f40d2ad9f1665e11c5609623758b630e..c758b2775d2a6cb72e9a8c920126ad7479a84074 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -56,6 +56,7 @@ The following quick actions are applicable to descriptions, discussions and thre | `/promote` | ✓ | | | Promote issue to epic. **(PREMIUM)** | | `/publish` | ✓ | | | Publish issue to an associated [Status Page](../../operations/incident_management/status_page.md) ([Introduced in GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30906)) **(ULTIMATE)** | | `/reassign @user1 @user2` | ✓ | ✓ | | Replace current assignees with those specified. **(STARTER)** | +| `/rebase` | | ✓ | | Rebase source branch. This will schedule a background task that attempt to rebase the changes in the source branch on the latest commit of the target branch. If `/rebase` is used, `/merge` will be ignored to avoid a race condition where the source branch is merged or deleted before it is rebased. | | `/relabel ~label1 ~label2` | ✓ | ✓ | ✓ | Replace current labels with those specified. | | `/relate #issue1 #issue2` | ✓ | | | Mark issues as related. **(STARTER)** | | `/remove_child_epic ` | | | ✓ | Remove child epic from ``. The `` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). **(ULTIMATE)** | diff --git a/lib/gitlab/quick_actions/merge_request_actions.rb b/lib/gitlab/quick_actions/merge_request_actions.rb index aafd493549b001fcf67edaef80c2fa72f5ce8d47..9ac8b98f595dc07149e0ccf9f3721bad9548343c 100644 --- a/lib/gitlab/quick_actions/merge_request_actions.rb +++ b/lib/gitlab/quick_actions/merge_request_actions.rb @@ -38,6 +38,32 @@ module MergeRequestActions @updates[:merge] = params[:merge_request_diff_head_sha] end + types MergeRequest + desc do + _('Rebase source branch') + end + explanation do + _('Rebase source branch on the target branch.') + end + condition do + merge_request = quick_action_target + + next false unless merge_request.source_branch_exists? + + access_check = ::Gitlab::UserAccess + .new(current_user, container: merge_request.source_project) + + access_check.can_push_to_branch?(merge_request.source_branch) + end + command :rebase do + # This will be used to avoid simultaneous "/merge" and "/rebase" actions + @updates[:rebase] = true + + branch = quick_action_target.source_branch + + @execution_message[:rebase] = _('Scheduled a rebase of branch %{branch}.') % { branch: branch } + end + desc 'Toggle the Draft status' explanation do noun = quick_action_target.to_ability_name.humanize(capitalize: false) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0a9615bf3697a4e7ab469f77fe804b68a1684480..b41d3d6367a10b03fa44afb63eb5c0e917e5a2a9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -22975,6 +22975,12 @@ msgstr "" msgid "Rebase in progress" msgstr "" +msgid "Rebase source branch" +msgstr "" + +msgid "Rebase source branch on the target branch." +msgstr "" + msgid "Receive alerts from manually configured Prometheus servers." msgstr "" @@ -24328,6 +24334,9 @@ msgstr "" msgid "Scheduled Deletion At - %{permanent_deletion_time}" msgstr "" +msgid "Scheduled a rebase of branch %{branch}." +msgstr "" + msgid "Scheduled to merge this merge request (%{strategy})." msgstr "" diff --git a/spec/features/merge_request/user_uses_quick_actions_spec.rb b/spec/features/merge_request/user_uses_quick_actions_spec.rb index 04a2e046f42d91664ff088c6a66d03a05b783922..b48659353ec01ccb79d56bce5ce0ef1ca714e6f4 100644 --- a/spec/features/merge_request/user_uses_quick_actions_spec.rb +++ b/spec/features/merge_request/user_uses_quick_actions_spec.rb @@ -41,5 +41,6 @@ end it_behaves_like 'merge quick action' + it_behaves_like 'rebase quick action' end end diff --git a/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb new file mode 100644 index 0000000000000000000000000000000000000000..2e8db16ad68fd98557ac2a03f83e7e306b3bf815 --- /dev/null +++ b/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'rebase quick action' do + context 'when updating the description' do + before do + sign_in(user) + visit edit_project_merge_request_path(project, merge_request) + end + + it 'rebases the MR', :sidekiq_inline do + fill_in('Description', with: '/rebase') + click_button('Save changes') + + expect(page).not_to have_content('commit behind the target branch') + expect(merge_request.reload).not_to be_merged + end + + it 'ignores /merge if /rebase is specified', :sidekiq_inline do + fill_in('Description', with: "/merge\n/rebase") + click_button('Save changes') + + expect(page).not_to have_content('commit behind the target branch') + expect(merge_request.reload).not_to be_merged + end + end + + context 'when creating a new note' do + context 'when the current user can rebase the MR' do + before do + sign_in(user) + visit project_merge_request_path(project, merge_request) + end + + it 'rebase the MR', :sidekiq_inline do + add_note("/rebase") + + expect(page).to have_content "Scheduled a rebase of branch #{merge_request.source_branch}." + end + end + + context 'when the current user cannot rebase the MR' do + before do + project.add_guest(guest) + sign_in(guest) + visit project_merge_request_path(project, merge_request) + end + + it 'does not rebase the MR' do + add_note("/rebase") + + expect(page).not_to have_content 'Your commands have been executed!' + end + end + end +end