diff --git a/Gemfile b/Gemfile
index e61d9b2ff7dd7ff9cbe53d0762b8d68702197b41..d7aa463d8308462db1d630265a665032d46fe600 100644
--- a/Gemfile
+++ b/Gemfile
@@ -178,6 +178,7 @@ gem "gitlab_emoji", "~> 0.0.1.1"
 gem "gon", '~> 5.0.0'
 gem 'nprogress-rails'
 gem 'request_store'
+gem "virtus"
 
 group :development do
   gem "annotate", "~> 2.6.0.beta2"
diff --git a/Gemfile.lock b/Gemfile.lock
index 1b94f063845ba4a1e4e3a54d3dde6820c0195670..500e80ce4ee4193c9141bf5aaf589b2caa985127 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -692,5 +692,6 @@ DEPENDENCIES
   unicorn (~> 4.6.3)
   unicorn-worker-killer
   version_sorter
+  virtus
   webmock
   wikicloth (= 0.8.1)
diff --git a/app/assets/javascripts/diff.js.coffee b/app/assets/javascripts/diff.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..dbe00c487dc4bdf69a05c6a0fee2baf7c3863f8c
--- /dev/null
+++ b/app/assets/javascripts/diff.js.coffee
@@ -0,0 +1,46 @@
+class Diff
+  UNFOLD_COUNT = 20
+  constructor: ->
+    $(document).on('click', '.js-unfold', (event) =>
+      target = $(event.target)
+      unfoldBottom = target.hasClass('js-unfold-bottom')
+      unfold = true
+
+      [old_line, line_number] = @lineNumbers(target.parent())
+      offset = line_number - old_line
+
+      if unfoldBottom
+        line_number += 1
+        since = line_number
+        to = line_number + UNFOLD_COUNT
+      else
+        [prev_old_line, prev_new_line] = @lineNumbers(target.parent().prev())
+        line_number -= 1
+        to = line_number
+        if line_number - UNFOLD_COUNT > prev_new_line + 1
+          since = line_number - UNFOLD_COUNT
+        else
+          since = prev_new_line + 1
+          unfold = false
+
+      link = target.parents('.diff-file').attr('data-blob-diff-path')
+      params =
+        since: since
+        to: to
+        bottom: unfoldBottom
+        offset: offset
+        unfold: unfold
+
+      $.get(link, params, (response) =>
+        target.parent().replaceWith(response)
+      )
+    )
+
+  lineNumbers: (line) ->
+    return ([0, 0]) unless line.children().length
+    lines = line.children().slice(0, 2)
+    line_numbers = ($(l).attr('data-linenumber') for l in lines)
+    (parseInt(line_number) for line_number in line_numbers)
+
+
+@Diff = Diff
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index ff68b520ad6eaba5243d249b65f164038e16f660..89bb475a8a63a2a1a8c28266f43483bc91123bc5 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -23,13 +23,21 @@ class Dispatcher
         new Issue()
       when 'projects:milestones:show'
         new Milestone()
-      when 'projects:issues:new', 'projects:merge_requests:new'
+      when 'projects:issues:new'
         GitLab.GfmAutoComplete.setup()
+      when 'projects:merge_requests:new'
+        GitLab.GfmAutoComplete.setup()
+        new Diff()
+      when 'projects:merge_requests:show'
+        new Diff()
+      when "projects:merge_requests:diffs"
+        new Diff()
       when 'dashboard:show'
         new Dashboard()
         new Activities()
       when 'projects:commit:show'
         new Commit()
+        new Diff()
       when 'groups:show', 'projects:show'
         new Activities()
       when 'projects:new', 'projects:edit'
diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss
index 88b188dbe8d02a05e127a61a854442fbf21047af..488d06919b020fbed3ae4992fade70d809c51cd3 100644
--- a/app/assets/stylesheets/sections/diff.scss
+++ b/app/assets/stylesheets/sections/diff.scss
@@ -48,6 +48,9 @@
         background-color: #8F8;
       }
     }
+    .unfold {
+      cursor: pointer;
+    }
 
     .file-mode-changed {
       padding: 10px;
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index db3d173b98d6491b484f083b860829d6cd5ffbf1..7009e3b1bc81e9facc69e9429337753d57f5df56 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -25,6 +25,21 @@ def destroy
     end
   end
 
+  def diff
+    @form = UnfoldForm.new(params)
+    @lines = @blob.data.lines[@form.since - 1..@form.to - 1]
+
+    if @form.bottom?
+      @match_line = ''
+    else
+      lines_length = @lines.length - 1
+      line = [@form.since, lines_length].join(',')
+      @match_line = "@@ -#{line}+#{line} @@"
+    end
+
+    render layout: false
+  end
+
   private
 
   def blob
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 9a8b3928bf4798a90b5e78407482623975b817e1..f61aa259154c2a4964b06c122fe028608fb3f72a 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -232,4 +232,16 @@ def commit_person_link(commit, options = {})
   def diff_file_mode_changed?(diff)
     diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
   end
+
+  def unfold_bottom_class(bottom)
+    (bottom) ? 'js-unfold-bottom' : ''
+  end
+
+  def view_file_btn(commit_sha, diff, project)
+    link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)),
+            class: 'btn btn-small view-file js-view-file' do
+      raw('View file @') + content_tag(:span, commit_sha[0..6],
+                                       class: 'commit-short-id')
+    end
+  end
 end
diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..cfb91d6568a3d8a3874ff1e753d292e3aefa413a
--- /dev/null
+++ b/app/views/projects/blob/diff.html.haml
@@ -0,0 +1,19 @@
+- if @lines.present?
+  - if @form.unfold? && @form.since != 1 && !@form.bottom?
+    %tr.line_holder{ id: @form.since }
+      = render "projects/commits/diffs/match_line", {line: @match_line,
+        line_old: @form.since, line_new: @form.since, bottom: false}
+
+  - @lines.each_with_index do |line, index|
+    - line_new = index + @form.since
+    - line_old = line_new - @form.offset
+    %tr.line_holder
+      %td.old_line.diff-line-num{data: {linenumber: line_old}}
+        = link_to raw(line_old), "#"
+      %td.new_line= link_to raw(line_new) , "#"
+      %td.line_content.noteable_line= line
+
+  - if @form.unfold? && @form.bottom? && @form.to < @blob.loc
+    %tr.line_holder{ id: @form.to }
+      = render "projects/commits/diffs/match_line", {line: @match_line,
+        line_old: @form.to, line_new: @form.to, bottom: true}
diff --git a/app/views/projects/commits/_diff_file.html.haml b/app/views/projects/commits/_diff_file.html.haml
index 9cbcb84aead8b4e393e2984b89e43ed1d5b6c19b..6e6107c88493af7d140d13b6a8746f8f9d5d940d 100644
--- a/app/views/projects/commits/_diff_file.html.haml
+++ b/app/views/projects/commits/_diff_file.html.haml
@@ -1,15 +1,15 @@
 - file = project.repository.blob_for_diff(@commit, diff)
 - return unless file
-.diff-file{id: "diff-#{i}"}
+- blob_diff_path = diff_project_blob_path(project,
+  tree_join(@commit.id, diff.new_path))
+.diff-file{id: "diff-#{i}", data: {blob_diff_path: blob_diff_path }}
   .diff-header{id: "file-path-#{hexdigest(diff.new_path || diff.old_path)}"}
     - if diff.deleted_file
       %span= diff.old_path
 
       .diff-btn-group
         - if @commit.parent_ids.present?
-          = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), { class: 'btn btn-small view-file' } do
-            View file @
-            %span.commit-short-id= @commit.short_id(6)
+          = view_file_btn(@commit.parent_id, diff, project)
     - else
       %span= diff.new_path
       - if diff_file_mode_changed?(diff)
@@ -26,10 +26,7 @@
             Edit
           &nbsp;
 
-        = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), { class: 'btn btn-small view-file' } do
-          View file @
-          %span.commit-short-id= @commit.short_id(6)
-
+        = view_file_btn(@commit.id, diff, project)
 
   .diff-content
     -# Skipp all non non-supported blobs
diff --git a/app/views/projects/commits/_text_file.html.haml b/app/views/projects/commits/_text_file.html.haml
index f5b0d711416b1647e1df48b08b1dceee0bab0928..756481c1b21690e0cc4f0b3d341ed1fa064c4425 100644
--- a/app/views/projects/commits/_text_file.html.haml
+++ b/app/views/projects/commits/_text_file.html.haml
@@ -3,18 +3,20 @@
   %a.supp_diff_link Changes suppressed. Click to show
 
 %table.text-file{class: "#{'hide' if too_big}"}
+  - last_line = 0
   - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
+    - last_line = line_new
     %tr.line_holder{ id: line_code, class: "#{type}" }
       - if type == "match"
-        %td.old_line= "..."
-        %td.new_line= "..."
-        %td.line_content.matched= line
+        = render "projects/commits/diffs/match_line", {line: line,
+          line_old: line_old, line_new: line_new, bottom: false}
       - else
         %td.old_line
           = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
           - if @comments_allowed
             = link_to_new_diff_note(line_code)
-        %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
+        %td.new_line{data: {linenumber: line_new}}
+          = link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
         %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)
 
     - if @reply_allowed
@@ -22,6 +24,10 @@
       - unless comments.empty?
         = render "projects/notes/diff_notes_with_reply", notes: comments, line: line
 
+  - if last_line > 0
+    = render "projects/commits/diffs/match_line", {line: "",
+      line_old: last_line, line_new: last_line, bottom: true}
+
 - if diff.diff.blank? && diff_file_mode_changed?(diff)
   .file-mode-changed
     File mode changed
diff --git a/app/views/projects/commits/diffs/_match_line.html.haml b/app/views/projects/commits/diffs/_match_line.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4ebe3379733feaaa9fd6bbab6f0c21aa370f0058
--- /dev/null
+++ b/app/views/projects/commits/diffs/_match_line.html.haml
@@ -0,0 +1,7 @@
+%td.old_line.diff-line-num.unfold.js-unfold{data: {linenumber: line_old},
+ class: unfold_bottom_class(bottom)}
+  \...
+%td.new_line.diff-line-num.unfold.js-unfold{data: {linenumber: line_new},
+ class: unfold_bottom_class(bottom)}
+  \...
+%td.line_content.matched= line
diff --git a/config/routes.rb b/config/routes.rb
index 261fbb50e3824ba2856eda4e3082050eded6c9f4..ce66ea999514bae5f7d648c07be40e3ce3b2d3d7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -193,7 +193,9 @@
     end
 
     scope module: :projects do
-      resources :blob,      only: [:show, :destroy], constraints: {id: /.+/}
+      resources :blob, only: [:show, :destroy], constraints: { id: /.+/ } do
+        get :diff, on: :member
+      end
       resources :raw,       only: [:show], constraints: {id: /.+/}
       resources :tree,      only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
       resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index d4c71ba336e0c99eafbe05844a7c0fd746d0b87a..8b6c296dfe61ce3c7cf5c7d6e25b9326ff6c2b80 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -139,3 +139,11 @@ Feature: Project Merge Requests
     And I click link "Show inline discussion" of the second file
     Then I should see a comment like "Line is wrong" in the second file
     And I should still see a comment like "Line is correct" in the first file
+
+  @javascript
+  Scenario: I unfold diff
+    Given project "Shop" have "Bug NS-05" open merge request with diffs inside
+    And I visit merge request page "Bug NS-05"
+    And I switch to the diff tab
+    And I unfold diff
+    Then I should see additional file lines
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index f0007a039e44284be386ef315ec423d9af788022..05d3e5067c573a5d37d833f7ca6c0939ebd5a6e0 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -242,6 +242,14 @@ class ProjectMergeRequests < Spinach::FeatureSteps
     end
   end
 
+  step 'I unfold diff' do
+    first('.js-unfold').click
+  end
+
+  step 'I should see additional file lines' do
+    expect(first('.text-file')).to have_content('.bundle')
+  end
+
   def project
     @project ||= Project.find_by!(name: "Shop")
   end
diff --git a/lib/gitlab/diff_parser.rb b/lib/gitlab/diff_parser.rb
index 14bbb328637bc5acdb8051f52be7ec521587ef19..b244295027ef19015c11024b139c337812348c23 100644
--- a/lib/gitlab/diff_parser.rb
+++ b/lib/gitlab/diff_parser.rb
@@ -30,7 +30,7 @@ def each
           line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
 
           next if line_old == 1 && line_new == 1 #top of file
-          yield(full_line, type, nil, nil, nil)
+          yield(full_line, type, nil, line_new, line_old)
           next
         else
           type = identification_type(line)
diff --git a/lib/gt_one_coercion.rb b/lib/gt_one_coercion.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ef2dc09767cc09a5864ddc0b9420a492a9df9f02
--- /dev/null
+++ b/lib/gt_one_coercion.rb
@@ -0,0 +1,5 @@
+class GtOneCoercion < Virtus::Attribute
+  def coerce(value)
+    [1, value.to_i].max
+  end
+end
diff --git a/lib/unfold_form.rb b/lib/unfold_form.rb
new file mode 100644
index 0000000000000000000000000000000000000000..46b12beeaaf364bd50c6dff38c43ee33bdbafe88
--- /dev/null
+++ b/lib/unfold_form.rb
@@ -0,0 +1,11 @@
+require_relative 'gt_one_coercion'
+
+class UnfoldForm
+  include Virtus.model
+
+  attribute :since, GtOneCoercion
+  attribute :to, GtOneCoercion
+  attribute :bottom, Boolean
+  attribute :unfold, Boolean, default: true
+  attribute :offset, Integer
+end