diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 46efdcf4202b2d6038d4a36c71e4c23fe5dd7aa3..5728afb4c5987c27d29dfa6088348a8ae91fd534 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -206,8 +206,6 @@ function UsersSelect(currentUser, els) {
       return $dropdown.glDropdown({
         showMenuAbove: showMenuAbove,
         data: function(term, callback) {
-          var isAuthorFilter;
-          isAuthorFilter = $('.js-author-search');
           return _this.users(term, options, function(users) {
             // GitLabDropdownFilter returns this.instance
             // GitLabDropdownRemote returns this.options.instance
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
index 8ceb5c36bda69b673f1ec8f90ebce2b604a833b9..9247b1f72de535fac202e7224cca115d6e1083fe 100644
--- a/app/helpers/form_helper.rb
+++ b/app/helpers/form_helper.rb
@@ -16,8 +16,8 @@ module FormHelper
     end
   end
 
-  def issue_dropdown_options(issuable, has_multiple_assignees = true)
-    options = {
+  def issue_assignees_dropdown_options
+    {
       toggle_class: 'js-user-search js-assignee-search js-multiselect js-save-user-data',
       title: 'Select assignee',
       filter: true,
@@ -27,8 +27,8 @@ module FormHelper
         first_user: current_user&.username,
         null_user: true,
         current_user: true,
-        project_id: issuable.project.try(:id),
-        field_name: "#{issuable.class.model_name.param_key}[assignee_ids][]",
+        project_id: @project.id,
+        field_name: 'issue[assignee_ids][]',
         default_label: 'Unassigned',
         'max-select': 1,
         'dropdown-header': 'Assignee',
@@ -38,13 +38,5 @@ module FormHelper
         current_user_info: current_user.to_json(only: [:id, :name])
       }
     }
-
-    if has_multiple_assignees
-      options[:title] = 'Select assignee(s)'
-      options[:data][:'dropdown-header'] = 'Assignee(s)'
-      options[:data].delete(:'max-select')
-    end
-
-    options
   end
 end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 8f15904f0681672adde8f8a430d1f4faca414d66..f39a3bb55a88b6c455c10b19550f4f97ccbf4bb2 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -126,6 +126,18 @@ module SearchHelper
     search_path(options)
   end
 
+  def search_filter_input_options(type)
+    {
+      id: "filtered-search-#{type}",
+      placeholder: 'Search or filter results...',
+      data: {
+        'project-id' => @project.id,
+        'username-params' => @users.to_json(only: [:id, :username]),
+        'base-endpoint' => namespace_project_path(@project.namespace, @project)
+      }
+    }
+  end
+
   # Sanitize a HTML field for search display. Most tags are stripped out and the
   # maximum length is set to 200 characters.
   def search_md_sanitize(object, field)
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index d178ee4422b0f8e0f9b741b42d39cde8f713314f..41c8b52527310b3fd7d6756ed5fae347cf3de177 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -102,6 +102,14 @@ module Issuable
     def locking_enabled?
       title_changed? || description_changed?
     end
+
+    def allows_multiple_assignees?
+      false
+    end
+
+    def has_multiple_assignees?
+      assignees.count > 1
+    end
   end
 
   module ClassMethods
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index c099d731082516ddf3850a801181445236fcf0ad..808212c780cbc9b784c6278a67a47c6ede721e14 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -197,11 +197,19 @@ class MergeRequest < ActiveRecord::Base
     }
   end
 
-  # This method is needed for compatibility with issues to not mess view and other code
+  # These method are needed for compatibility with issues to not mess view and other code
   def assignees
     Array(assignee)
   end
 
+  def assignee_ids
+    Array(assignee_id)
+  end
+
+  def assignee_ids=(ids)
+    write_attribute(:assignee_id, ids.last)
+  end
+
   def assignee_or_author?(user)
     author_id == user.id || assignee_id == user.id
   end
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 6816b137361094b4ec6cd9add765bbf6013c98b9..e4dfe87e6140b81b6575d47576ab3c8799caf476 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -92,9 +92,12 @@ module QuickActions
 
     desc 'Assign'
     explanation do |users|
-      "Assigns #{users.first.to_reference}." if users.any?
+      users = issuable.allows_multiple_assignees? ? users : users.take(1)
+      "Assigns #{users.map(&:to_reference).to_sentence}."
+    end
+    params do
+      issuable.allows_multiple_assignees? ? '@user1 @user2' : '@user'
     end
-    params '@user'
     condition do
       current_user.can?(:"admin_#{issuable.to_ability_name}", project)
     end
@@ -104,28 +107,69 @@ module QuickActions
     command :assign do |users|
       next if users.empty?
 
-      if issuable.is_a?(Issue)
-        @updates[:assignee_ids] = [users.last.id]
+      @updates[:assignee_ids] =
+        if issuable.allows_multiple_assignees?
+          issuable.assignees.pluck(:id) + users.map(&:id)
+        else
+          [users.last.id]
+        end
+    end
+
+    desc do
+      if issuable.allows_multiple_assignees?
+        'Remove all or specific assignee(s)'
       else
-        @updates[:assignee_id] = users.last.id
+        'Remove assignee'
       end
     end
-
-    desc 'Remove assignee'
     explanation do
-      "Removes assignee #{issuable.assignees.first.to_reference}."
+      "Removes #{'assignee'.pluralize(issuable.assignees.size)} #{issuable.assignees.map(&:to_reference).to_sentence}."
+    end
+    params do
+      issuable.allows_multiple_assignees? ? '@user1 @user2' : ''
     end
     condition do
       issuable.persisted? &&
         issuable.assignees.any? &&
         current_user.can?(:"admin_#{issuable.to_ability_name}", project)
     end
-    command :unassign do
-      if issuable.is_a?(Issue)
-        @updates[:assignee_ids] = []
-      else
-        @updates[:assignee_id] = nil
-      end
+    parse_params do |unassign_param|
+      # When multiple users are assigned, all will be unassigned if multiple assignees are no longer allowed
+      extract_users(unassign_param) if issuable.allows_multiple_assignees?
+    end
+    command :unassign do |users = nil|
+      @updates[:assignee_ids] =
+        if users&.any?
+          issuable.assignees.pluck(:id) - users.map(&:id)
+        else
+          []
+        end
+    end
+
+    desc do
+      "Change assignee#{'(s)' if issuable.allows_multiple_assignees?}"
+    end
+    explanation do |users|
+      users = issuable.allows_multiple_assignees? ? users : users.take(1)
+      "Change #{'assignee'.pluralize(users.size)} to #{users.map(&:to_reference).to_sentence}."
+    end
+    params do
+      issuable.allows_multiple_assignees? ? '@user1 @user2' : '@user'
+    end
+    condition do
+      issuable.persisted? &&
+        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
+    end
+    parse_params do |assignee_param|
+      extract_users(assignee_param)
+    end
+    command :reassign do |users|
+      @updates[:assignee_ids] =
+        if issuable.allows_multiple_assignees?
+          users.map(&:id)
+        else
+          [users.last.id]
+        end
     end
 
     desc 'Set milestone'
diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml
index e8db868f49b2ef66e8a12a85c9c5898dffb3e972..c314573bdea515c825b88b8c60beae894241820d 100644
--- a/app/views/projects/boards/components/sidebar/_assignee.html.haml
+++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml
@@ -19,10 +19,11 @@
         ":data-name" => "assignee.name",
         ":data-username" => "assignee.username" }
       .dropdown
-        %button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: "button", ref: "assigneeDropdown", data: { toggle: "dropdown", field_name: "issue[assignee_ids][]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, null_user: "true", multi_select: "true", 'max-select' => 1, dropdown: { header: 'Assignee' } },
+        - dropdown_options = issue_assignees_dropdown_options
+        %button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: 'button', ref: 'assigneeDropdown', data: { toggle: 'dropdown', field_name: 'issue[assignee_ids][]', first_user: current_user&.username, current_user: 'true', project_id: @project.id, null_user: 'true', multi_select: 'true', 'dropdown-header': dropdown_options[:data][:'dropdown-header'], 'max-select': dropdown_options[:data][:'max-select'] },
           ":data-issuable-id" => "issue.id",
           ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
-          Select assignee
+          = dropdown_options[:title]
           = icon("chevron-down")
         .dropdown-menu.dropdown-select.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author
           = dropdown_title("Assign to")
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index d3d290692a21dec759fb50337f5c9323e5ebe7a1..ae8905672256b89f41704e5e038d1dad53021cac 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -23,7 +23,7 @@
             .scroll-container
               %ul.tokens-container.list-unstyled
                 %li.input-token
-                  %input.form-control.filtered-search{ id: "filtered-search-#{type.to_s}", placeholder: 'Search or filter results...', data: { 'project-id' => @project.id, 'username-params' => @users.to_json(only: [:id, :username]), 'base-endpoint' => namespace_project_path(@project.namespace, @project) } }
+                  %input.form-control.filtered-search{ search_filter_input_options(type) }
               = icon('filter')
             #js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
               %ul{ data: { dropdown: true } }
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index 2ea5eb960c033de8fb0ae0ecd3d6c35eeeac253c..57392cd7fbb2122629bfe9b01e17291b5f19b4a5 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -37,19 +37,20 @@
   - issuable.assignees.each do |assignee|
     = hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil, data: { avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }
 
-  - options = { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_ids][]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }
-
+  - options = { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: current_user&.username, current_user: true, project_id: @project&.id, author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_ids][]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }
   - title = 'Select assignee'
 
   - if issuable.is_a?(Issue)
     - unless issuable.assignees.any?
       = hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", 0, id: nil
+    - dropdown_options = issue_assignees_dropdown_options
+    - title = dropdown_options[:title]
     - options[:toggle_class] += ' js-multiselect js-save-user-data'
     - data = { field_name: "#{issuable.to_ability_name}[assignee_ids][]" }
     - data[:multi_select] = true
     - data['dropdown-title'] = title
-    - data['dropdown-header'] = 'Assignee'
-    - data['max-select'] = 1
+    - data['dropdown-header'] = dropdown_options[:data][:'dropdown-header']
+    - data['max-select'] = dropdown_options[:data][:'max-select']
     - options[:data].merge!(data)
 
   = dropdown_tag(title, options: options)
diff --git a/app/views/shared/issuable/form/_metadata_issue_assignee.html.haml b/app/views/shared/issuable/form/_metadata_issue_assignee.html.haml
index 77175c839a60bfb4d457f8fa058a30a3aaab99dd..567cde764e26bd0cd033ba6828357d926bc02684 100644
--- a/app/views/shared/issuable/form/_metadata_issue_assignee.html.haml
+++ b/app/views/shared/issuable/form/_metadata_issue_assignee.html.haml
@@ -7,5 +7,5 @@
     - if issuable.assignees.length === 0
       = hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", 0, id: nil, data: { meta: '' }
 
-    = dropdown_tag(users_dropdown_label(issuable.assignees), options: issue_dropdown_options(issuable,false))
+    = dropdown_tag(users_dropdown_label(issuable.assignees), options: issue_assignees_dropdown_options)
   = link_to 'Assign to me', '#', class: "assign-to-me-link #{'hide' if issuable.assignees.include?(current_user)}"
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index b369ef1ff79f708a9746940bd6b1aae6d4183c1f..58f6bd277e4d64e50abf6da291a098ed71e459ac 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -31,8 +31,8 @@ describe 'New/edit issue', :feature, :js do
         # the original method, resulting in infinite recurison when called.
         # This is likely a bug with helper modules included into dynamically generated view classes.
         # To work around this, we have to hold on to and call to the original implementation manually.
-        original_issue_dropdown_options = FormHelper.instance_method(:issue_dropdown_options)
-        allow_any_instance_of(FormHelper).to receive(:issue_dropdown_options).and_wrap_original do |original, *args|
+        original_issue_dropdown_options = FormHelper.instance_method(:issue_assignees_dropdown_options)
+        allow_any_instance_of(FormHelper).to receive(:issue_assignees_dropdown_options).and_wrap_original do |original, *args|
           options = original_issue_dropdown_options.bind(original.receiver).call(*args)
           options[:data][:per_page] = 2
 
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index bb5273074a21c4bc4334f254ae8e594851253656..587d4b83cb4694b5e7da4dc9061fee45f63709a7 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -105,6 +105,22 @@ describe MergeRequest, models: true do
     end
   end
 
+  describe '#assignee_ids' do
+    it 'returns an array of the assigned user id' do
+      subject.assignee_id = 123
+
+      expect(subject.assignee_ids).to eq([123])
+    end
+  end
+
+  describe '#assignee_ids=' do
+    it 'sets assignee_id to the last id in the array' do
+      subject.assignee_ids = [123, 456]
+
+      expect(subject.assignee_id).to eq(456)
+    end
+  end
+
   describe '#assignee_or_author?' do
     let(:user) { create(:user) }
 
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index c9e63efbc145b8f4651afd8da942e9351afd40e8..35373675894393b218bfa7b0b46601bbfc7996b1 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -359,18 +359,18 @@ describe QuickActions::InterpretService, services: true do
       let(:content) { "/assign @#{developer.username}" }
 
       context 'Issue' do
-        it 'fetches assignee and populates assignee_id if content contains /assign' do
+        it 'fetches assignee and populates assignee_ids if content contains /assign' do
           _, updates = service.execute(content, issue)
 
-          expect(updates).to eq(assignee_ids: [developer.id])
+          expect(updates[:assignee_ids]).to match_array([developer.id])
         end
       end
 
       context 'Merge Request' do
-        it 'fetches assignee and populates assignee_id if content contains /assign' do
+        it 'fetches assignee and populates assignee_ids if content contains /assign' do
           _, updates = service.execute(content, merge_request)
 
-          expect(updates).to eq(assignee_id: developer.id)
+          expect(updates).to eq(assignee_ids: [developer.id])
         end
       end
     end
@@ -383,7 +383,7 @@ describe QuickActions::InterpretService, services: true do
       end
 
       context 'Issue' do
-        it 'fetches assignee and populates assignee_id if content contains /assign' do
+        it 'fetches assignee and populates assignee_ids if content contains /assign' do
           _, updates = service.execute(content, issue)
 
           expect(updates[:assignee_ids]).to match_array([developer.id])
@@ -391,10 +391,10 @@ describe QuickActions::InterpretService, services: true do
       end
 
       context 'Merge Request' do
-        it 'fetches assignee and populates assignee_id if content contains /assign' do
+        it 'fetches assignee and populates assignee_ids if content contains /assign' do
           _, updates = service.execute(content, merge_request)
 
-          expect(updates).to eq(assignee_id: developer.id)
+          expect(updates).to eq(assignee_ids: [developer.id])
         end
       end
     end
@@ -422,11 +422,27 @@ describe QuickActions::InterpretService, services: true do
       end
 
       context 'Merge Request' do
-        it 'populates assignee_id: nil if content contains /unassign' do
-          merge_request.update(assignee_id: developer.id)
+        it 'populates assignee_ids: [] if content contains /unassign' do
+          merge_request.update(assignee_ids: [developer.id])
           _, updates = service.execute(content, merge_request)
 
-          expect(updates).to eq(assignee_id: nil)
+          expect(updates).to eq(assignee_ids: [])
+        end
+      end
+    end
+
+    context 'reassign command' do
+      let(:content) { '/reassign' }
+
+      context 'Issue' do
+        it 'reassigns user if content contains /reassign @user' do
+          user = create(:user)
+
+          issue.update(assignee_ids: [developer.id])
+
+          _, updates = service.execute("/reassign @#{user.username}", issue)
+
+          expect(updates).to eq(assignee_ids: [user.id])
         end
       end
     end