diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index a17680794cc39c7859fb4666da33b583bd383c57..7d11cd0b6b28e03049e1f9363b371563f5be372b 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -115,7 +115,7 @@ GitLabDropdownFilter = (function() {
     } else {
       elements = this.options.elements();
       if (search_text) {
-        return elements.each(function() {
+        elements.each(function() {
           var $el, matches;
           $el = $(this);
           matches = fuzzaldrinPlus.match($el.text().trim(), search_text);
@@ -128,8 +128,10 @@ GitLabDropdownFilter = (function() {
           }
         });
       } else {
-        return elements.show().removeClass('option-hidden');
+        elements.show().removeClass('option-hidden');
       }
+
+      elements.parent().find('.dropdown-menu-empty-link').toggleClass('hidden', elements.is(':visible'));
     }
   };
 
@@ -732,9 +734,15 @@ GitLabDropdown = (function() {
   GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) {
     if (this.options.filterable) {
       this.dropdown.one('transitionend', () => {
+        const initialScrollTop = $(window).scrollTop();
+
         if (this.dropdown.is('.open')) {
           this.filterInput.focus();
         }
+
+        if ($(window).scrollTop() < initialScrollTop) {
+          $(window).scrollTop(initialScrollTop);
+        }
       });
 
       if (triggerFocus) {
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 122ec138c59cb70ceb653b93b3847ff5f8326c06..e916724b6662cf77b90de18e7f21c4f45c0bcc19 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -86,8 +86,9 @@
       // This is required to handle non-unicode characters in hash
       hash = decodeURIComponent(hash);
 
-      var fixedTabs = document.querySelector('.js-tabs-affix');
-      var fixedNav = document.querySelector('.navbar-gitlab');
+      const fixedTabs = document.querySelector('.js-tabs-affix');
+      const fixedDiffStats = document.querySelector('.js-diff-files-changed.is-stuck');
+      const fixedNav = document.querySelector('.navbar-gitlab');
 
       var adjustment = 0;
       if (fixedNav) adjustment -= fixedNav.offsetHeight;
@@ -104,6 +105,11 @@
         if (fixedTabs) {
           adjustment -= fixedTabs.offsetHeight;
         }
+
+        if (fixedDiffStats) {
+          adjustment -= fixedDiffStats.offsetHeight;
+        }
+
         window.scrollBy(0, adjustment);
       }
     };
diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js
new file mode 100644
index 0000000000000000000000000000000000000000..43a808b6ab3b5bddb4481b2a2597d0f18a32b217
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/sticky.js
@@ -0,0 +1,23 @@
+export const isSticky = (el, scrollY, stickyTop) => {
+  const top = el.offsetTop - scrollY;
+
+  if (top === stickyTop) {
+    el.classList.add('is-stuck');
+  } else {
+    el.classList.remove('is-stuck');
+  }
+};
+
+export default (el) => {
+  if (!el) return;
+
+  const computedStyle = window.getComputedStyle(el);
+
+  if (!/sticky/.test(computedStyle.position)) return;
+
+  const stickyTop = parseInt(computedStyle.top, 10);
+
+  document.addEventListener('scroll', () => isSticky(el, window.scrollY, stickyTop), {
+    passive: true,
+  });
+};
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 7840f05a8aefd5ac25009012014c84ecb0b74ee6..4ffd71d9de5d9c2957d3e5cb2aba20937d1f690a 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -7,6 +7,7 @@ import Cookies from 'js-cookie';
 import './breakpoints';
 import './flash';
 import BlobForkSuggestion from './blob/blob_fork_suggestion';
+import stickyMonitor from './lib/utils/sticky';
 
 /* eslint-disable max-len */
 // MergeRequestTabs
@@ -266,6 +267,10 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
           const $container = $('#diffs');
           $container.html(data.html);
 
+          this.initChangesDropdown();
+
+          stickyMonitor(document.querySelector('.js-diff-files-changed'));
+
           if (typeof gl.diffNotesCompileComponents !== 'undefined') {
             gl.diffNotesCompileComponents();
           }
@@ -314,6 +319,13 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
       });
     }
 
+    initChangesDropdown() {
+      $('.js-diff-stats-dropdown').glDropdown({
+        filterable: true,
+        remoteFilter: false,
+      });
+    }
+
     // Show or hide the loading spinner
     //
     // status - Boolean, true to show, false to hide
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 89dd99831e43fe085bf20d610db9ff13cc118446..bd4bd541c3ac97b2d6f11ee5cf02587277d9218e 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -574,6 +574,7 @@
 
 .dropdown-input-field,
 .default-dropdown-input {
+  display: block;
   width: 100%;
   min-height: 30px;
   padding: 0 7px;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 398fd4444ea26b89e97915aadc51b49109ea60f6..da77346d8b258586f11fc79bc8c93e5214b10a8d 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -395,12 +395,11 @@
   background-color: transparent;
   border: 0;
   color: $gl-link-color;
-  transition: color 0.1s linear;
+  font-weight: 600;
 
   &:hover,
   &:focus {
     outline: none;
-    text-decoration: underline;
     color: $gl-link-hover-color;
   }
 }
@@ -559,3 +558,68 @@
     outline: 0;
   }
 }
+
+.diff-files-changed {
+  .commit-stat-summary {
+    @include new-style-dropdown;
+    z-index: -1;
+
+    @media (min-width: $screen-sm-min) {
+      margin-left: -$gl-padding;
+      padding-left: $gl-padding;
+      background-color: $white-light;
+    }
+  }
+
+  @media (min-width: $screen-sm-min) {
+    position: -webkit-sticky;
+    position: sticky;
+    top: 84px;
+    background-color: $white-light;
+    z-index: 190;
+
+    + .files,
+    + .alert {
+      margin-top: 1px;
+    }
+
+    &:not(.is-stuck) .diff-stats-additions-deletions-collapsed {
+      display: none;
+    }
+
+    &.is-stuck {
+      padding-top: 0;
+      padding-bottom: 0;
+      border-bottom: 1px solid $white-dark;
+      transform: translateY(16px);
+
+      .diff-stats-additions-deletions-expanded,
+      .inline-parallel-buttons {
+        display: none;
+      }
+
+      + .files,
+      + .alert {
+        margin-top: 30px;
+      }
+    }
+  }
+}
+
+.diff-file-changes {
+  width: 450px;
+  z-index: 150;
+
+  @media (min-width: $screen-sm-min) {
+    left: $gl-padding;
+  }
+
+  a {
+    padding-top: 8px;
+    padding-bottom: 8px;
+  }
+}
+
+.diff-file-changes-path {
+  @include str-truncated(78%);
+}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 4693b2434c7744955ce16c5175102064c192f7e2..a4e190945088c063dcccc827afccad5ffb46ca09 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -691,8 +691,10 @@
 }
 
 .mr-version-controls {
+  position: relative;
   background: $gray-light;
   color: $gl-text-color;
+  z-index: 199;
 
   .mr-version-menus-container {
     display: -webkit-flex;
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 91ddd73fac1f374f4b8502c7e129fb980c84577a..087f7f88fb53935b9996ad8bed64ac3403f09d4d 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -148,6 +148,24 @@ module DiffHelper
     options
   end
 
+  def diff_file_changed_icon(diff_file)
+    if diff_file.deleted_file? || diff_file.renamed_file?
+      "minus"
+    elsif diff_file.new_file?
+      "plus"
+    else
+      "adjust"
+    end
+  end
+
+  def diff_file_changed_icon_color(diff_file)
+    if diff_file.deleted_file?
+      "cred"
+    elsif diff_file.new_file?
+      "cgreen"
+    end
+  end
+
   private
 
   def diff_btn(title, name, selected)
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index f9385459a66111b3daace0dbba8ff4757fcc988c..8c8aa4c78f54a24a663e9a6937a570744c5efe3d 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -3,7 +3,7 @@
 - can_create_note = !@diff_notes_disabled && can?(current_user, :create_note, diffs.project)
 - diff_files = diffs.diff_files
 
-.content-block.oneline-block.files-changed
+.content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed
   .inline-parallel-buttons
     - if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? }
       = link_to 'Expand all', url_for(params.merge(expanded: 1, format: nil)), class: 'btn btn-default'
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
index e69c7f20d49848c9c2e0288c5f8483cbc1e2b341..efc0ea31917576868428c21b886ace592753fd4a 100644
--- a/app/views/projects/diffs/_stats.html.haml
+++ b/app/views/projects/diffs/_stats.html.haml
@@ -1,36 +1,34 @@
-.js-toggle-container
-  .commit-stat-summary
-    Showing
-    %button.diff-stats-summary-toggler.js-toggle-button{ type: "button" }
-      %strong= pluralize(diff_files.size, "changed file")
+- sum_added_lines = diff_files.sum(&:added_lines)
+- sum_removed_lines = diff_files.sum(&:removed_lines)
+.commit-stat-summary.dropdown
+  Showing
+  %button.diff-stats-summary-toggler.js-diff-stats-dropdown{ type: "button", data: { toggle: "dropdown" } }<
+    = pluralize(diff_files.size, "changed file")
+    = icon("caret-down", class: "prepend-left-5")
+  %span.diff-stats-additions-deletions-expanded#diff-stats
     with
-    %strong.cgreen #{diff_files.sum(&:added_lines)} additions
+    %strong.cgreen #{sum_added_lines} additions
     and
-    %strong.cred #{diff_files.sum(&:removed_lines)} deletions
-  .file-stats.js-toggle-content.hide
-    %ul
-      - diff_files.each do |diff_file|
-        - file_hash = hexdigest(diff_file.file_path)
-        %li
-          - if diff_file.deleted_file?
-            %span.deleted-file
-              %a{ href: "##{file_hash}" }
-                %i.fa.fa-minus
-                = diff_file.old_path
-          - elsif diff_file.renamed_file?
-            %span.renamed-file
-              %a{ href: "##{file_hash}" }
-                %i.fa.fa-minus
-                = diff_file.old_path
-                &rarr;
-                = diff_file.new_path
-          - elsif diff_file.new_file?
-            %span.new-file
-              %a{ href: "##{file_hash}" }
-                %i.fa.fa-plus
-                = diff_file.new_path
-          - else
-            %span.edit-file
-              %a{ href: "##{file_hash}" }
-                %i.fa.fa-adjust
-                = diff_file.new_path
+    %strong.cred #{sum_removed_lines} deletions
+  .diff-stats-additions-deletions-collapsed.pull-right{ "aria-hidden": "true", "aria-describedby": "diff-stats" }
+    %strong.cgreen<
+      +#{sum_added_lines}
+    %strong.cred<
+      \-#{sum_removed_lines}
+  .dropdown-menu.diff-file-changes
+    = dropdown_filter("Search files")
+    .dropdown-content
+      %ul
+        - diff_files.each do |diff_file|
+          %li
+            %a{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path }
+              = icon("#{diff_file_changed_icon(diff_file)} fw", class: "#{diff_file_changed_icon_color(diff_file)} append-right-5")
+              %span.diff-file-changes-path= diff_file.new_path
+              .pull-right
+                %span.cgreen<
+                  +#{diff_file.added_lines}
+                %span.cred<
+                  \-#{diff_file.removed_lines}
+        %li.dropdown-menu-empty-link.hidden
+          %a{ href: "#" }
+            No files found.
diff --git a/changelogs/unreleased/diff-changed-files-dropdown.yml b/changelogs/unreleased/diff-changed-files-dropdown.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2d2a26ffea2ca8e963429e82cb0e39ab7725ec4f
--- /dev/null
+++ b/changelogs/unreleased/diff-changed-files-dropdown.yml
@@ -0,0 +1,4 @@
+---
+title: Moved diff changed files into a dropdown
+merge_request:
+author:
diff --git a/spec/javascripts/lib/utils/sticky_spec.js b/spec/javascripts/lib/utils/sticky_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..c3ee3ef9825db6cc562113b619776a5806fb8962
--- /dev/null
+++ b/spec/javascripts/lib/utils/sticky_spec.js
@@ -0,0 +1,52 @@
+import { isSticky } from '~/lib/utils/sticky';
+
+describe('sticky', () => {
+  const el = {
+    offsetTop: 0,
+    classList: {},
+  };
+
+  beforeEach(() => {
+    el.offsetTop = 0;
+    el.classList.add = jasmine.createSpy('spy');
+    el.classList.remove = jasmine.createSpy('spy');
+  });
+
+  describe('classList.remove', () => {
+    it('does not call classList.remove when stuck', () => {
+      isSticky(el, 0, 0);
+
+      expect(
+        el.classList.remove,
+      ).not.toHaveBeenCalled();
+    });
+
+    it('calls classList.remove when not stuck', () => {
+      el.offsetTop = 10;
+      isSticky(el, 0, 0);
+
+      expect(
+        el.classList.remove,
+      ).toHaveBeenCalledWith('is-stuck');
+    });
+  });
+
+  describe('classList.add', () => {
+    it('calls classList.add when stuck', () => {
+      isSticky(el, 0, 0);
+
+      expect(
+        el.classList.add,
+      ).toHaveBeenCalledWith('is-stuck');
+    });
+
+    it('does not call classList.add when not stuck', () => {
+      el.offsetTop = 10;
+      isSticky(el, 0, 0);
+
+      expect(
+        el.classList.add,
+      ).not.toHaveBeenCalled();
+    });
+  });
+});