Commit e1bdb165d147ee65e89d9dacd442d4467d29a86c

Authored by Dmitriy Zaporozhets
2 parents 71d31a38 01ff084a

Merge pull request #5014 from bladealslayer/feature/big-commit-improvements

Improved large commit handling.
app/assets/javascripts/merge_requests.js.coffee
@@ -11,7 +11,7 @@ class MergeRequest @@ -11,7 +11,7 @@ class MergeRequest
11 11
12 constructor: (@opts) -> 12 constructor: (@opts) ->
13 this.$el = $('.merge-request') 13 this.$el = $('.merge-request')
14 - @diffs_loaded = false 14 + @diffs_loaded = if @opts.action == 'diffs' then true else false
15 @commits_loaded = false 15 @commits_loaded = false
16 16
17 this.activateTab(@opts.action) 17 this.activateTab(@opts.action)
app/contexts/commit_load_context.rb
@@ -20,7 +20,8 @@ class CommitLoadContext < BaseContext @@ -20,7 +20,8 @@ class CommitLoadContext < BaseContext
20 result[:notes_count] = project.notes.for_commit_id(commit.id).count 20 result[:notes_count] = project.notes.for_commit_id(commit.id).count
21 21
22 begin 22 begin
23 - result[:suppress_diff] = true if commit.diffs.size > Commit::DIFF_SAFE_SIZE && !params[:force_show_diff] 23 + result[:suppress_diff] = true if commit.diff_suppress? && !params[:force_show_diff]
  24 + result[:force_suppress_diff] = commit.diff_force_suppress?
24 rescue Grit::Git::GitTimeout 25 rescue Grit::Git::GitTimeout
25 result[:suppress_diff] = true 26 result[:suppress_diff] = true
26 result[:status] = :huge_commit 27 result[:status] = :huge_commit
app/controllers/projects/commit_controller.rb
@@ -18,6 +18,7 @@ class Projects::CommitController < Projects::ApplicationController @@ -18,6 +18,7 @@ class Projects::CommitController < Projects::ApplicationController
18 end 18 end
19 19
20 @suppress_diff = result[:suppress_diff] 20 @suppress_diff = result[:suppress_diff]
  21 + @force_suppress_diff = result[:force_suppress_diff]
21 22
22 @note = result[:note] 23 @note = result[:note]
23 @line_notes = result[:line_notes] 24 @line_notes = result[:line_notes]
app/controllers/projects/compare_controller.rb
@@ -15,6 +15,10 @@ class Projects::CompareController < Projects::ApplicationController @@ -15,6 +15,10 @@ class Projects::CompareController < Projects::ApplicationController
15 @diffs = compare.diffs 15 @diffs = compare.diffs
16 @refs_are_same = compare.same 16 @refs_are_same = compare.same
17 @line_notes = [] 17 @line_notes = []
  18 +
  19 + diff_line_count = Commit::diff_line_count(@diffs)
  20 + @suppress_diff = Commit::diff_suppress?(@diffs, diff_line_count) && !params[:force_show_diff]
  21 + @force_suppress_diff = Commit::diff_force_suppress?(@diffs, diff_line_count)
18 end 22 end
19 23
20 def create 24 def create
app/controllers/projects/merge_requests_controller.rb
@@ -40,6 +40,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController @@ -40,6 +40,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
40 @comments_target = {noteable_type: 'MergeRequest', 40 @comments_target = {noteable_type: 'MergeRequest',
41 noteable_id: @merge_request.id} 41 noteable_id: @merge_request.id}
42 @line_notes = @merge_request.notes.where("line_code is not null") 42 @line_notes = @merge_request.notes.where("line_code is not null")
  43 +
  44 + diff_line_count = Commit::diff_line_count(@merge_request.diffs)
  45 + @suppress_diff = Commit::diff_suppress?(@merge_request.diffs, diff_line_count) && !params[:force_show_diff]
  46 + @force_suppress_diff = Commit::diff_force_suppress?(@merge_request.diffs, diff_line_count)
43 end 47 end
44 48
45 def new 49 def new
app/models/commit.rb
@@ -6,15 +6,41 @@ class Commit @@ -6,15 +6,41 @@ class Commit
6 6
7 attr_mentionable :safe_message 7 attr_mentionable :safe_message
8 8
9 - # Safe amount of files with diffs in one commit to render 9 + # Safe amount of changes (files and lines) in one commit to render
10 # Used to prevent 500 error on huge commits by suppressing diff 10 # Used to prevent 500 error on huge commits by suppressing diff
11 # 11 #
12 - DIFF_SAFE_SIZE = 100 12 + # User can force display of diff above this size
  13 + DIFF_SAFE_FILES = 100
  14 + DIFF_SAFE_LINES = 5000
  15 + # Commits above this size will not be rendered in HTML
  16 + DIFF_HARD_LIMIT_FILES = 500
  17 + DIFF_HARD_LIMIT_LINES = 10000
13 18
14 def self.decorate(commits) 19 def self.decorate(commits)
15 commits.map { |c| self.new(c) } 20 commits.map { |c| self.new(c) }
16 end 21 end
17 22
  23 + # Calculate number of lines to render for diffs
  24 + def self.diff_line_count(diffs)
  25 + diffs.reduce(0){|sum, d| sum + d.diff.lines.count}
  26 + end
  27 +
  28 + def self.diff_suppress?(diffs, line_count = nil)
  29 + # optimize - check file count first
  30 + return true if diffs.size > DIFF_SAFE_FILES
  31 +
  32 + line_count ||= Commit::diff_line_count(diffs)
  33 + line_count > DIFF_SAFE_LINES
  34 + end
  35 +
  36 + def self.diff_force_suppress?(diffs, line_count = nil)
  37 + # optimize - check file count first
  38 + return true if diffs.size > DIFF_HARD_LIMIT_FILES
  39 +
  40 + line_count ||= Commit::diff_line_count(diffs)
  41 + line_count > DIFF_HARD_LIMIT_LINES
  42 + end
  43 +
18 attr_accessor :raw 44 attr_accessor :raw
19 45
20 def initialize(raw_commit) 46 def initialize(raw_commit)
@@ -27,6 +53,19 @@ class Commit @@ -27,6 +53,19 @@ class Commit
27 @raw.id 53 @raw.id
28 end 54 end
29 55
  56 + def diff_line_count
  57 + @diff_line_count ||= Commit::diff_line_count(self.diffs)
  58 + @diff_line_count
  59 + end
  60 +
  61 + def diff_suppress?
  62 + Commit::diff_suppress?(self.diffs, diff_line_count)
  63 + end
  64 +
  65 + def diff_force_suppress?
  66 + Commit::diff_force_suppress?(self.diffs, diff_line_count)
  67 + end
  68 +
30 # Returns a string describing the commit for use in a link title 69 # Returns a string describing the commit for use in a link title
31 # 70 #
32 # Example 71 # Example
app/views/projects/commits/_diffs.html.haml
  1 +- @suppress_diff ||= @suppress_diff || @force_suppress_diff
1 - if @suppress_diff 2 - if @suppress_diff
2 .alert.alert-block 3 .alert.alert-block
3 %p 4 %p
4 - %strong Warning! Large commit with more than #{Commit::DIFF_SAFE_SIZE} files changed.  
5 - %p To preserve performance the diff is not shown. 5 + %strong Warning! This is a large diff.
6 %p 6 %p
7 - But if you still want to see the diff  
8 - = link_to "click this link", project_commit_path(project, @commit, force_show_diff: true), class: "underlined_link" 7 + To preserve performance the diff is not shown.
  8 + - if current_controller?(:commit) or current_controller?(:merge_requests)
  9 + Please, download the diff as
  10 + - if current_controller?(:commit)
  11 + = link_to "plain diff", project_commit_path(@project, @commit, format: :diff), class: "underlined_link"
  12 + or
  13 + = link_to "email patch", project_commit_path(@project, @commit, format: :patch), class: "underlined_link"
  14 + - else
  15 + = link_to "plain diff", project_merge_request_path(@project, @merge_request, format: :diff), class: "underlined_link"
  16 + or
  17 + = link_to "email patch", project_merge_request_path(@project, @merge_request, format: :patch), class: "underlined_link"
  18 + instead.
  19 + - unless @force_suppress_diff
  20 + %p
  21 + If you still want to see the diff
  22 + = link_to "click this link", url_for(force_show_diff: true), class: "underlined_link"
9 23
10 %p.commit-stat-summary 24 %p.commit-stat-summary
11 Showing 25 Showing
features/project/commits/commits.feature
@@ -27,3 +27,11 @@ Feature: Project Browse commits @@ -27,3 +27,11 @@ Feature: Project Browse commits
27 Scenario: I browse commits stats 27 Scenario: I browse commits stats
28 Given I visit my project's commits stats page 28 Given I visit my project's commits stats page
29 Then I see commits stats 29 Then I see commits stats
  30 +
  31 + Scenario: I browse big commit
  32 + Given I visit big commit page
  33 + Then I see big commit warning
  34 +
  35 + Scenario: I browse huge commit
  36 + Given I visit huge commit page
  37 + Then I see huge commit message
features/steps/project/project_browse_commits.rb
@@ -58,4 +58,24 @@ class ProjectBrowseCommits < Spinach::FeatureSteps @@ -58,4 +58,24 @@ class ProjectBrowseCommits < Spinach::FeatureSteps
58 page.should have_content 'Total commits' 58 page.should have_content 'Total commits'
59 page.should have_content 'Authors' 59 page.should have_content 'Authors'
60 end 60 end
  61 +
  62 + Given 'I visit big commit page' do
  63 + visit project_commit_path(@project, BigCommits::BIG_COMMIT_ID)
  64 + end
  65 +
  66 + Then 'I see big commit warning' do
  67 + page.should have_content BigCommits::BIG_COMMIT_MESSAGE
  68 + page.should have_content "Warning! This is a large diff"
  69 + page.should have_content "If you still want to see the diff"
  70 + end
  71 +
  72 + Given 'I visit huge commit page' do
  73 + visit project_commit_path(@project, BigCommits::HUGE_COMMIT_ID)
  74 + end
  75 +
  76 + Then 'I see huge commit message' do
  77 + page.should have_content BigCommits::HUGE_COMMIT_MESSAGE
  78 + page.should have_content "Warning! This is a large diff"
  79 + page.should_not have_content "If you still want to see the diff"
  80 + end
61 end 81 end
features/support/env.rb
@@ -14,7 +14,7 @@ require 'spinach/capybara' @@ -14,7 +14,7 @@ require 'spinach/capybara'
14 require 'sidekiq/testing/inline' 14 require 'sidekiq/testing/inline'
15 15
16 16
17 -%w(valid_commit select2_helper chosen_helper test_env).each do |f| 17 +%w(valid_commit big_commits select2_helper chosen_helper test_env).each do |f|
18 require Rails.root.join('spec', 'support', f) 18 require Rails.root.join('spec', 'support', f)
19 end 19 end
20 20
spec/support/big_commits.rb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +module BigCommits
  2 + HUGE_COMMIT_ID = "7f92534f767fa20357a11c63f973ae3b79cc5b85"
  3 + HUGE_COMMIT_MESSAGE = "pybments.rb version up. gitignore improved"
  4 +
  5 + BIG_COMMIT_ID = "d62200cad430565bd9f80befaf329297120330b5"
  6 + BIG_COMMIT_MESSAGE = "clean-up code"
  7 +end
  8 +