diff --git a/CHANGELOG b/CHANGELOG
index 9e9ca9d02d9953721d85d3f0131df3fa201a85bb..d7d9bd3d3498a78487df9ab8bd0503b38c4abb89 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -14,6 +14,7 @@ v 8.9.1
   - Fix GitLab project import issues related to notes and builds
   - Improve performance of searching repository tags by name by using a memorized tag array
   - Fix false truncated warnings with ISO-8559 files
+  - Fix unwanted label unassignment when doing bulk action on issues page
   - Fix 404 when accessing pipelines as guest user on public projects
 
 v 8.9.0
diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee
index d0901be1509eff7981e7592c67f14cc725106b30..6a108c033eafdb22fc7ee28553a4486a6945342a 100644
--- a/app/assets/javascripts/issuable.js.coffee
+++ b/app/assets/javascripts/issuable.js.coffee
@@ -68,12 +68,15 @@ issuable_created = false
     Turbolinks.visit(issuesUrl);
 
   initChecks: ->
+    @issuableBulkActions = $('.bulk-update').data('bulkActions')
+
     $('.check_all_issues').off('click').on('click', ->
       $('.selected_issue').prop('checked', @checked)
       Issuable.checkChanged()
     )
 
-    $('.selected_issue').off('change').on('change', Issuable.checkChanged)
+    $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(@))
+
 
   checkChanged: ->
     checked_issues = $('.selected_issue:checked')
@@ -88,3 +91,6 @@ issuable_created = false
       $('#update_issues_ids').val []
       $('.issues_bulk_update').hide()
       $('.issues-other-filters').show()
+      @issuableBulkActions.willUpdateLabels = false
+
+    return true
diff --git a/app/assets/javascripts/issues-bulk-assignment.js.coffee b/app/assets/javascripts/issues-bulk-assignment.js.coffee
index b454f9389dd789326d75dafd26303ceed476d509..6b0e69dbae7a5e52cbd9c8ce5f93b357b4e17845 100644
--- a/app/assets/javascripts/issues-bulk-assignment.js.coffee
+++ b/app/assets/javascripts/issues-bulk-assignment.js.coffee
@@ -7,6 +7,11 @@ class @IssuableBulkActions
       @issues = @getElement('.issues-list .issue')
     } = opts
 
+    # Save instance
+    @form.data 'bulkActions', @
+
+    @willUpdateLabels = false
+
     @bindEvents()
 
     # Fixes bulk-assign not working when navigating through pages
@@ -87,11 +92,12 @@ class @IssuableBulkActions
         add_label_ids     : []
         remove_label_ids  : []
 
-    @getLabelsToApply().map (id) ->
-      formData.update.add_label_ids.push id
+    if @willUpdateLabels
+      @getLabelsToApply().map (id) ->
+        formData.update.add_label_ids.push id
 
-    @getLabelsToRemove().map (id) ->
-      formData.update.remove_label_ids.push id
+      @getLabelsToRemove().map (id) ->
+        formData.update.remove_label_ids.push id
 
     formData
 
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index 6a10db10eb1254ae73cb363eefdb43dcafc0e3c9..e95fd96a83f234cf4d786ad6d0d6c938d576c97d 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -319,6 +319,8 @@ class @LabelsSelect
 
         multiSelect: $dropdown.hasClass 'js-multiselect'
         clicked: (label) ->
+          _this.enableBulkLabelDropdown()
+
           if $dropdown.hasClass('js-filter-bulk-update')
             return
 
@@ -377,3 +379,8 @@ class @LabelsSelect
       label_ids.push $("#issue_#{issue_id}").data('labels')
 
     _.intersection.apply _, label_ids
+
+  enableBulkLabelDropdown: ->
+    if $('.selected_issue:checked').length
+      issuableBulkActions = $('.bulk-update').data('bulkActions')
+      issuableBulkActions.willUpdateLabels = true
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 7143d0e40f38b4c6d15d5fefb22eb871c805d3ac..afc093cc1f5626c8d943b82b0e84451f2131fbcd 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -10,7 +10,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
   let!(:bug)      { create(:label, project: project, title: 'bug') }
   let!(:feature)  { create(:label, project: project, title: 'feature') }
 
-  context 'as a allowed user', js: true do
+  context 'as an allowed user', js: true do
     before do
       project.team << [user, :master]
 
@@ -164,6 +164,133 @@ feature 'Issues > Labels bulk assignment', feature: true do
         end
       end
     end
+
+    context 'toggling a milestone' do
+      let!(:milestone) { create(:milestone, project: project, title: 'First Release') }
+
+      context 'setting a milestone' do
+        before do
+          issue1.labels << bug
+          issue2.labels << feature
+          visit namespace_project_issues_path(project.namespace, project)
+        end
+
+        it 'labels are kept' do
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+
+          check 'check_all_issues'
+          open_milestone_dropdown(['First Release'])
+          update_issues
+
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
+        end
+      end
+
+      context 'setting a milestone and adding another label' do
+        before do
+          issue1.labels << bug
+
+          visit namespace_project_issues_path(project.namespace, project)
+        end
+
+        it 'existing label is kept and new label is present' do
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+
+          check 'check_all_issues'
+          open_milestone_dropdown ['First Release']
+          open_labels_dropdown ['feature']
+          update_issues
+
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue1.id}")).to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
+        end
+      end
+
+      context 'setting a milestone and removing existing label' do
+        before do
+          issue1.labels << bug
+          issue1.labels << feature
+          issue2.labels << feature
+
+          visit namespace_project_issues_path(project.namespace, project)
+        end
+
+        it 'existing label is kept and new label is present' do
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+
+          check 'check_all_issues'
+          open_milestone_dropdown ['First Release']
+          unmark_labels_in_dropdown ['feature']
+          update_issues
+
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).not_to have_content 'feature'
+          expect(find("#issue_#{issue1.id}")).to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).not_to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
+        end
+      end
+
+      context 'unsetting a milestone' do
+        before do
+          issue1.milestone = milestone
+          issue2.milestone = milestone
+          issue1.save
+          issue2.save
+          issue1.labels << bug
+          issue2.labels << feature
+
+          visit namespace_project_issues_path(project.namespace, project)
+        end
+
+        it 'labels are kept' do
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
+
+          check 'check_all_issues'
+          open_milestone_dropdown(['No Milestone'])
+          update_issues
+
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).not_to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).not_to have_content 'First Release'
+        end
+      end
+    end
+
+    context 'toggling checked issues' do
+      before do
+        issue1.labels << bug
+
+        visit namespace_project_issues_path(project.namespace, project)
+      end
+
+      it do
+        expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+
+        check_issue issue1
+        open_labels_dropdown ['feature']
+        uncheck_issue issue1
+        check_issue issue1
+        update_issues
+        sleep 1 # needed
+
+        expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+        expect(find("#issue_#{issue1.id}")).not_to have_content 'feature'
+      end
+    end
   end
 
   context 'as a guest' do
@@ -181,6 +308,16 @@ feature 'Issues > Labels bulk assignment', feature: true do
     end
   end
 
+  def open_milestone_dropdown(items = [])
+    page.within('.issues_bulk_update') do
+      click_button 'Milestone'
+      wait_for_ajax
+      items.map do |item|
+        click_link item
+      end
+    end
+  end
+
   def open_labels_dropdown(items = [], unmark = false)
     page.within('.issues_bulk_update') do
       click_button 'Label'
@@ -201,12 +338,20 @@ feature 'Issues > Labels bulk assignment', feature: true do
     open_labels_dropdown(items, true)
   end
 
-  def check_issue(issue)
+  def check_issue(issue, uncheck = false)
     page.within('.issues-list') do
-      check "selected_issue_#{issue.id}"
+      if uncheck
+        uncheck "selected_issue_#{issue.id}"
+      else
+        check "selected_issue_#{issue.id}"
+      end
     end
   end
 
+  def uncheck_issue(issue)
+    check_issue(issue, true)
+  end
+
   def update_issues
     click_button 'Update issues'
     wait_for_ajax