diff --git a/Gemfile b/Gemfile index 24640164f..fdffdb31c 100644 --- a/Gemfile +++ b/Gemfile @@ -29,7 +29,7 @@ gem 'state_machines-activerecord' gem 'redis-rails' gem 'grack', git: 'git://github.com/rosa-abf/grack.git', require: 'git_http' -gem 'grit', git: 'git://github.com/rosa-abf/grit.git', tag: '2.6.16' +gem 'grit', git: 'git://github.com/rosa-abf/grit.git', tag: '2.6.17' gem 'charlock_holmes' gem 'github-linguist', '3.1.5', require: 'linguist' gem 'diff-display' diff --git a/Gemfile.lock b/Gemfile.lock index 4dbc5f2cf..52a43a070 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,8 +34,8 @@ GIT GIT remote: git://github.com/rosa-abf/grit.git - revision: a9548c92188cc307e7af1dd41a733e7000a783a9 - tag: 2.6.16 + revision: b733f0ceefb44b18a9dec8f509ba5493dab59e4e + tag: 2.6.17 specs: grit (2.5.0) diff-lcs (~> 1.1) diff --git a/app/assets/javascripts/angularjs/comments/comments_controller.js.coffee b/app/assets/javascripts/angularjs/comments/comments_controller.js.coffee index 2cce12bb0..d5fb3380d 100644 --- a/app/assets/javascripts/angularjs/comments/comments_controller.js.coffee +++ b/app/assets/javascripts/angularjs/comments/comments_controller.js.coffee @@ -75,15 +75,22 @@ CommentsController = (Comment, Preview, confirmMessage, $scope, compileHTML, $ro else false - vm.add = -> + vm.add = ($event)-> + $event.preventDefault() + $event.stopPropagation() + vm.processing = true - promise = Comment.add(vm.project, vm.commentable, vm.new_body) - promise.then (response) -> - element = compileHTML.run($scope, response.data.html) + Comment.add(vm.project, vm.commentable, vm.new_body) + .success (data) -> + element = compileHTML.run($scope, data.html) list.append(element) vm.new_body = '' - location.hash = "#comment" + response.data.id; + location.hash = "#comment" + data.id; + vm.processing = false + $.notify(data.message, 'success') + .error (data) -> + $.notify(data.message, 'error') vm.processing = false false @@ -91,8 +98,8 @@ CommentsController = (Comment, Preview, confirmMessage, $scope, compileHTML, $ro vm.remove = (id) -> return false unless confirmMessage.show() vm.processing = true - promise = Comment.remove(vm.project, vm.commentable, id) - promise.then () -> + Comment.remove(vm.project, vm.commentable, id) + .success (data)-> parent = $('#comment'+id+',#diff-comment'+id).parents('tr.line-comments') if parent.find('.line-comment').length is 1 # there is only one line comment, remove all line @@ -100,16 +107,21 @@ CommentsController = (Comment, Preview, confirmMessage, $scope, compileHTML, $ro else $('#comment'+id+',#diff-comment'+id+',#update-comment'+id).remove() + $.notify(data.message, 'success') + vm.processing = false + .error (data)-> + $.notify(data.message, 'error') vm.processing = false false vm.update = (id) -> vm.processing = true - promise = Comment.update(vm.project, vm.commentable, id) - promise.then (response) -> - form = $('#comment'+id+ ' .md_and_cm.cm-s-default').html(response.data.body) + Comment.update(vm.project, vm.commentable, id) + .success (data) -> + form = $('#comment'+id+ ' .md_and_cm.cm-s-default').html(data.body) + $.notify(data.message, 'success') vm.processing = false form = $('.open-comment.comment-'+id) if form.length is 1 @@ -117,6 +129,9 @@ CommentsController = (Comment, Preview, confirmMessage, $scope, compileHTML, $ro return true else return false + .error (data) -> + $.notify(data.message, 'error') + vm.processing = false vm.showInlineForm = ($event, params = {}) -> line_comments = findInlineComments($event, params) diff --git a/app/assets/javascripts/angularjs/pull_requests/pull_request_controller.js.coffee b/app/assets/javascripts/angularjs/pull_requests/pull_request_controller.js.coffee index 97772a8e4..b72a85eb3 100644 --- a/app/assets/javascripts/angularjs/pull_requests/pull_request_controller.js.coffee +++ b/app/assets/javascripts/angularjs/pull_requests/pull_request_controller.js.coffee @@ -108,9 +108,8 @@ PullRequestController = (dataservice, $http, ApiPullRequest, ApiProject, DateTim promise = ApiPullRequest.get_diff(vm.pull_params) promise.then (response) -> diff.html(null) - #html = compileHTML.run($scope, response.data) - #diff.html(html) $rootScope.$broadcast('compile_html', { element: diff, html: response.data }) + $('[data-toggle="tooltip"]').tooltip() vm.processing = false vm.is_diff_updated = true false diff --git a/app/assets/javascripts/extra/diff.js.coffee b/app/assets/javascripts/extra/diff.js.coffee index 77b3e59cd..dc811ae69 100644 --- a/app/assets/javascripts/extra/diff.js.coffee +++ b/app/assets/javascripts/extra/diff.js.coffee @@ -1,5 +1,5 @@ $(document).ready -> - $(document).on 'click', '#diff_header .panel-body table a', -> + $(document).on 'click', '#diff_header .panel-body li.list-group-item a', -> href = $(this).attr('href') $(".diff_data.collapse#"+href.slice(1)+"_content").collapse('show') diff --git a/app/assets/stylesheets/views/diff.sass b/app/assets/stylesheets/views/diff.sass index 9d32cfe4a..5e6448143 100644 --- a/app/assets/stylesheets/views/diff.sass +++ b/app/assets/stylesheets/views/diff.sass @@ -3,7 +3,7 @@ overflow-x: auto table.table.diff.inline - //border: 1px solid #DDD + margin-bottom: 0 tr.changes pre @@ -66,17 +66,6 @@ table.table.diff.inline .line-comment, #new_inline_comment max-width: 700px - td.diff-image - text-align: center - - span.diff-image - text-align: center - margin: 0 - padding: 0 - - img - margin-top: 5px - div.file div.top min-height: 28px background: #ededed diff --git a/app/controllers/projects/comments_controller.rb b/app/controllers/projects/comments_controller.rb index dc6a31cd6..d90cd05a6 100644 --- a/app/controllers/projects/comments_controller.rb +++ b/app/controllers/projects/comments_controller.rb @@ -6,15 +6,19 @@ class Projects::CommentsController < Projects::BaseController include CommentsHelper def create - if !@comment.set_additional_data params - render json: { - error: I18n.t("flash.comment.save_error"), - message: @comment.errors.full_messages - } - elsif @comment.save - render :create - else - render json: { error: I18n.t("flash.comment.save_error") }, status: 422 + respond_to do |format| + if !@comment.set_additional_data params + format.json { + render json: { + message: I18n.t("flash.comment.save_error"), + error: @comment.errors.full_messages + } + } + elsif @comment.save + format.json {} + else + format.json { render json: { message: I18n.t("flash.comment.save_error") }, status: 422 } + end end end @@ -22,17 +26,24 @@ class Projects::CommentsController < Projects::BaseController end def update - status, message = if @comment.update_attributes(params[:comment]) - [200, view_context.markdown(@comment.body)] - else - [422, 'error'] + respond_to do |format| + if @comment.update_attributes(params[:comment]) + format.json { render json: {message:t('flash.comment.updated'), body: view_context.markdown(@comment.body)} } + else + format.json { render json: {message:t('flash.comment.error_in_updating')}, status: 422 } + end end - render json: {body: message}, status: status end def destroy - @comment.destroy - render json: nil + respond_to do |format| + if @comment.present? && @comment.destroy + format.json { render json: {message: I18n.t('flash.comment.destroyed')} } + else + format.json { + render json: {message: t('flash.comment.error_in_deleting')}, status: 422 } + end + end end protected diff --git a/app/controllers/projects/git/commits_controller.rb b/app/controllers/projects/git/commits_controller.rb index 3dd5b5295..5072521dd 100644 --- a/app/controllers/projects/git/commits_controller.rb +++ b/app/controllers/projects/git/commits_controller.rb @@ -14,7 +14,7 @@ class Projects::Git::CommitsController < Projects::Git::BaseController respond_to do |format| format.html - format.diff { render text: (@commit.diffs.map(&:diff).join("\n") rescue ''), content_type: "text/plain" } + format.diff { render text: (@commit.show.map(&:diff).join("\n") rescue ''), content_type: "text/plain" } format.patch { render text: (@commit.to_patch rescue ''), content_type: "text/plain" } end end diff --git a/app/helpers/commit_helper.rb b/app/helpers/commit_helper.rb index f3282580b..a031fc950 100644 --- a/app/helpers/commit_helper.rb +++ b/app/helpers/commit_helper.rb @@ -1,23 +1,31 @@ module CommitHelper MAX_FILES_WITHOUT_COLLAPSE = 25 - def render_commit_stats(stats) - res = [""] + def render_commit_stats(options = {}) + stats = options[:stats] + diff = options[:diff] + repo = options[:repo] + commit = options[:commit] + parent_commit = commit.parents.try(:first) + + res = ["" - res << "" - res << "" + file_name = get_filename_in_diff(diff[ind], filename) + file_status = t "layout.projects.diff.#{get_file_status_in_diff(diff[ind])}" + res << "
  • " + res << "
    " + res << "" + res << render_file_changes(diff: diff[ind], adds: adds, deletes: deletes, total: total, + repo: repo, commit: commit, parent_commit: parent_commit, file_status: file_status) + res << "" ind +=1 end - res << "
  • #{h(filename.rtruncate 120)}" - res << I18n.t("layout.projects.inline_changes_count", count: total).strip + - " (" + - I18n.t("layout.projects.inline_additions_count", count: adds).strip + - ", " + - I18n.t("layout.projects.inline_deletions_count", count: deletes).strip + - ")" - res << "
    " + res << "" wrap_commit_header_list(stats, res) end @@ -73,9 +81,116 @@ module CommitHelper Russian.p(commits_count, *commits_pluralization_arr) end + def is_file_open_in_diff(blob, diff) + return true if blob.binary? && blob.render_as == :image + return true if diff.diff.blank? && diff.a_mode != diff.b_mode + diff.diff.present? && diff.diff.split("\n").count <= DiffHelper::MAX_LINES_WITHOUT_COLLAPSE + end + + def file_blob_in_diff(repo, commit_id, diff) + return if repo.nil? || commit_id.nil? || diff.nil? + tree = repo.tree(commit_id) + blob = diff.renamed_file ? (tree / diff.b_path) : (tree / (diff.a_path || diff.b_path)) + blob || diff.a_blob || diff.b_blob + end + + def get_commit_id_for_file(diff, commit, parent_commit) + diff.deleted_file ? parent_commit.id : commit.id + end + + def get_file_status_in_diff(diff) + if diff.renamed_file + :renamed_file + elsif diff.new_file + :new_file + elsif diff.deleted_file + :deleted_file + else + :changed_file + end + end + + def get_filename_in_diff(diff, filename) + if diff.renamed_file + "#{diff.a_path.rtruncate 50} => #{diff.b_path.rtruncate 50}" + else + filename.rtruncate(100) + end + end + protected def commits_pluralization_arr pluralize ||= t('layout.commits.pluralize').map {|base, title| title.to_s} end + + def render_file_changes(options = {}) + diff = options[:diff] + adds = options[:adds] + deletes = options[:deletes] + total = options[:total] + repo = options[:repo] + file_status = options[:file_status] + commit_id = get_commit_id_for_file(diff, options[:commit], options[:parent_commit]) + blob = file_blob_in_diff(repo, commit_id, diff) + + res = '' + res << "
    " + res << "
    " + if blob.binary? + res << "#{t 'layout.projects.diff.binary'} #{file_status}" + elsif total > 0 + res << "+#{adds} -#{deletes}" + else # total == 0 + res << "#{t 'layout.projects.diff.without_changes'}" + end + res << "
    " + res << "
    " + + res << "
    " + res << render_progress_bar(adds, deletes, total, blob) + res << "
    " + + end + + def render_progress_bar(adds, deletes, total, blob) + res = '' + pluses = 0 + minuses = 0 + + if total > 0 + pluses = ((adds/(adds+deletes).to_f)*100).round + minuses = 100 - pluses + end + + title = if total >0 + t 'layout.projects.inline_changes_count', count: total + elsif !blob.binary? + t 'layout.projects.diff.without_changes' + else + 'BIN' + end + + res << "
    " + res << "
    " + res << "
    " + res << "
    " + res + end + + def diff_file_icon(diff) + icon = case get_file_status_in_diff(diff) + when :renamed_file + 'fa-caret-square-o-right text-info' + when :new_file + 'fa-plus-square text-success' + when :deleted_file + 'fa-minus-square text-danger' + when :changed_file + 'fa-pencil-square text-primary' + else + 'fa-exclamation-circle text-danger' + end + "" + end end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index afa4b193a..f698214ce 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -1,26 +1,41 @@ module DiffHelper - MAX_FILES_WITHOUT_COLLAPSE = 25 + include CommitHelper + MAX_LINES_WITHOUT_COLLAPSE = 50 - def render_diff_stats(stats) - res = [""] + def render_diff_stats(options = {}) + stats = options[:stats] + diff = options[:diff] + repo = options[:repo] + commit = options[:commit] + parent_commit = options[:common_ancestor] + + res = ["" - res << "" - res << "" + adds = stat.additions + deletes = stat.deletions + total = adds + deletes + file_name = get_filename_in_diff(diff[ind], stat.filename) + file_status = t "layout.projects.diff.#{get_file_status_in_diff(diff[ind])}" + + res << "
  • " + res << "
    " + res << "" + res << render_file_changes(diff: diff[ind], adds: adds, deletes: deletes, total: total, + repo: repo, commit: commit, parent_commit: parent_commit, file_status: file_status) + res << "" + ind +=1 end - res << '
  • #{link_to stat.filename.rtruncate(120), "#diff-#{ind}"}" - res << I18n.t("layout.projects.inline_changes_count", count: stat.additions + stat.deletions).strip + - " (" + - I18n.t("layout.projects.inline_additions_count", count: stat.additions).strip + - ", " + - I18n.t("layout.projects.inline_deletions_count", count: stat.deletions).strip + - ")" - res << "
    ' - wrap_header_list(stats, res) + res << "" + + wrap_diff_header_list(stats, res) end - def wrap_header_list(stats, list) + def wrap_diff_header_list(stats, list) is_stats_open = stats.count <= MAX_FILES_WITHOUT_COLLAPSE ? 'in' : '' res = ["
    "] res << "
    " diff --git a/app/models/comment.rb b/app/models/comment.rb index e513fb53e..8b0f1d269 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -65,7 +65,7 @@ class Comment < ActiveRecord::Base end return data[:actual] = true if commentable_type == 'Grit::Commit' filepath, line_number = data[:path], data[:line] - diff_path = (diff || commentable.diffs ).select {|d| d.a_path == data[:path]} + diff_path = (diff || commentable.show ).select {|d| d.a_path == data[:path]} comment_line = data[:line].to_i # NB! also dont create a comment to the diff header return data[:actual] = false if diff_path.blank? || comment_line == 0 diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 3d16a87f7..cf920d2c7 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -163,9 +163,8 @@ class PullRequest < ActiveRecord::Base @diff_stats ||= repo.diff_stats(to_commit.id, from_commit.id) end - # FIXME maybe move to warpc/grit? def diff - @diff ||= Grit::Commit.diff(repo, to_commit.id, from_commit.id) + @diff ||= repo.diff(to_commit.id, from_commit.id) end def set_user_and_time user diff --git a/app/views/projects/comments/_add.html.slim b/app/views/projects/comments/_add.html.slim index 6c75e40d0..54dd8d5a8 100644 --- a/app/views/projects/comments/_add.html.slim +++ b/app/views/projects/comments/_add.html.slim @@ -13,7 +13,7 @@ h3 ang_model: 'new_body' hr button.btn.btn-primary[ ng-disabled = 'commentsCtrl.isDisabledNewCommentButton()' - ng-click = 'commentsCtrl.add()' ] + ng-click = 'commentsCtrl.add($event)' ] = t('layout.create') .panel-footer => t('layout.comments.notifications_are') diff --git a/app/views/projects/comments/create.json.jbuilder b/app/views/projects/comments/create.json.jbuilder index 9360e4013..92866965b 100644 --- a/app/views/projects/comments/create.json.jbuilder +++ b/app/views/projects/comments/create.json.jbuilder @@ -1,2 +1,3 @@ json.id @comment.id json.html render partial: 'projects/comments/line_comment.html.slim' +json.message t('flash.comment.saved') diff --git a/app/views/projects/git/commits/_commits.html.slim b/app/views/projects/git/commits/_commits.html.slim index a8bbc424e..660ef3f6e 100644 --- a/app/views/projects/git/commits/_commits.html.slim +++ b/app/views/projects/git/commits/_commits.html.slim @@ -12,6 +12,6 @@ ul.list-group - commits.each_with_index do |commit| li.list-group-item - - GitPresenters::CommitAsMessagePresenter.present(commit, project: @project) do |presenter| + - GitPresenters::CommitAsMessagePresenter.present(commit, project: project) do |presenter| == render 'shared/feed_commit', presenter: presenter, item_no: counter - counter += 1 diff --git a/app/views/projects/git/commits/_diff.html.slim b/app/views/projects/git/commits/_diff.html.slim index 2d3189d13..a885078ac 100644 --- a/app/views/projects/git/commits/_diff.html.slim +++ b/app/views/projects/git/commits/_diff.html.slim @@ -1,9 +1,8 @@ -- commit_id = diff.deleted_file ? parent_commit.try(:id) : @commit.id +- parent_commit = defined?(common_ancestor) ? common_ancestor : commit.parents.try(:first) +- commit_id = get_commit_id_for_file diff, commit, parent_commit - diff_counter_content = "diff-#{diff_counter}_content" - -- if diff.diff.present? - - blob = @project.repo.tree(commit_id) / diff.a_path - - is_file_open = 'in' if !blob.binary? && diff.diff.split("\n").count <= DiffHelper::MAX_LINES_WITHOUT_COLLAPSE +- blob = file_blob_in_diff(@project.repo, commit_id, diff) +- is_file_open = 'in' if is_file_open_in_diff(blob, diff) .file.offset10 a name = "diff-#{diff_counter}" @@ -14,23 +13,23 @@ aria-expanded = 'true' aria-controls = diff_counter_content ] span.fa class= (is_file_open ? 'fa-chevron-down' : 'fa-chevron-up') - =< h(diff.a_path.rtruncate 120) + =< diff_file_icon(diff).html_safe + =< get_filename_in_diff(diff, diff.a_path) - if diff.b_path.present? button.btn.btn-link.pull-right = link_to "view file @ #{shortest_hash_id(commit_id)}", blob_path(@project, commit_id, diff.b_path) .clearfix - - if diff.diff.present? - .diff_data.collapse[ id = diff_counter_content class = is_file_open ] + .diff_data.collapse id= diff_counter_content class= is_file_open + - if blob.respond_to?(:render_as) && blob.render_as == :image && diff.diff.present? + - old_blob = file_blob_in_diff(@project.repo, parent_commit.try(:id), diff) + == render 'show_image', diff: diff, blob: blob, old_blob: old_blob - - if blob.render_as == :image - table.table.diff.inline.table-bordered[ cellspacing = 0 cellpadding = 0 ] - tr - td.diff-image - span.diff-image - img[ src = "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}" - style = 'max-width: 600px;' ] - - elsif !blob.binary? - - if (@project.repo.tree(commit_id) / diff.b_path).nil? - = "a_path=#{diff.a_path}; b_path=#{diff.b_path}" - == render_diff(diff, diff_counter: diff_counter, comments: @comments) + - if diff.a_mode != diff.b_mode && diff.diff.blank? + == render 'file_change_mode', blob: blob, diff: diff + + - elsif diff.diff.blank? && diff.renamed_file + == render 'file_removed_not_changed', blob: blob + + - elsif !blob.binary? + == render_diff(diff, diff_counter: diff_counter, comments: @comments) diff --git a/app/views/projects/git/commits/_file_change_mode.html.slim b/app/views/projects/git/commits/_file_change_mode.html.slim new file mode 100644 index 000000000..d63782724 --- /dev/null +++ b/app/views/projects/git/commits/_file_change_mode.html.slim @@ -0,0 +1,10 @@ +.table-responsive.overflow-auto + table.table.diff.inline.table-borderless cellspacing= 0 cellpadding= 0 + tr.header + td.line_numbers[] ... + td.line_numbers[] ... + td.header.text-danger= "oldmode #{diff.a_mode}" + tr.header + td.line_numbers[] ... + td.line_numbers[] ... + td.header.text-success= "newmode #{diff.b_mode}" \ No newline at end of file diff --git a/app/views/projects/git/commits/_file_removed_not_changed.html.slim b/app/views/projects/git/commits/_file_removed_not_changed.html.slim new file mode 100644 index 000000000..2d1136c6f --- /dev/null +++ b/app/views/projects/git/commits/_file_removed_not_changed.html.slim @@ -0,0 +1,9 @@ +.table-responsive.overflow-auto + table.table.diff.inline.table-borderless cellspacing= 0 cellpadding= 0 + tr.header + td.line_numbers[] ... + td.line_numbers[] ... + td.header + strong.text-primary + - binary = t('layout.projects.diff.binary') if blob.binary? + = "#{binary} #{ t 'layout.projects.diff.renamed_file'} #{t 'layout.projects.diff.without_changes'}" diff --git a/app/views/projects/git/commits/_show.html.slim b/app/views/projects/git/commits/_show.html.slim index bc9187fba..fa38dfc13 100644 --- a/app/views/projects/git/commits/_show.html.slim +++ b/app/views/projects/git/commits/_show.html.slim @@ -1,6 +1,7 @@ - begin - - stats = @commit.stats - = render_commit_stats(stats) + - diffs = @commit.show + - stats = Grit::CommitStats.find_all(@project.repo, @commit.sha, max_count: 1, skip: 0)[0][-1] + = render_commit_stats(stats: stats, diff: @commit.show, repo: @project.repo, commit: @commit) .pull-right => link_to 'raw diff', commit_path(@project, @commit.id, :diff) @@ -8,6 +9,6 @@ =< link_to 'patch', commit_path(@project, @commit.id, :patch) .clearfix - == render partial: 'diff', collection: @commit.diffs, locals: { parent_commit: @commit.parents.try(:first) } + == render partial: 'diff', collection: diffs, locals: { commit: @commit } - rescue Grit::Git::GitTimeout h3.text-danger= t('layout.git.repositories.commit_diff_too_big') diff --git a/app/views/projects/git/commits/_show_image.html.slim b/app/views/projects/git/commits/_show_image.html.slim new file mode 100644 index 000000000..b5ff26323 --- /dev/null +++ b/app/views/projects/git/commits/_show_image.html.slim @@ -0,0 +1,18 @@ +.row + - if get_file_status_in_diff(diff) == :changed_file + .col-md-6.col-xs-12 + p.text-center.text-danger= t 'layout.projects.diff.deleted_file' + img[ class= 'img-responsive center-block' + src= "data:#{old_blob.mime_type};base64,#{Base64.encode64(old_blob.data)}" + style= 'border: 1px solid red;' ] + + .col-md-6.col-xs-12 + p.text-center.text-success= t 'layout.projects.diff.new_file' + img[ class= 'img-responsive center-block' + src= "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}" + style= 'border: 1px solid green;' ] + - else + .col-xs-12 + img[ class= 'img-responsive center-block offset10' + src= "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}" + style= 'border: 1px solid #87CEFA;' ] diff --git a/app/views/projects/git/commits/diff.html.slim b/app/views/projects/git/commits/diff.html.slim index 7a04db0b4..0016380b3 100644 --- a/app/views/projects/git/commits/diff.html.slim +++ b/app/views/projects/git/commits/diff.html.slim @@ -12,18 +12,13 @@ h3= title #repo-wrapper - .leftside - -total_additions = @stats.inject(0) {|sum, n| sum + n.additions} - -total_deletions = @stats.inject(0) {|sum, n| sum + n.deletions} - h5= t("layout.projects.diff_show_header", - files: t("layout.projects.commit_files_count", count: @stats.count), - additions: t("layout.projects.commit_additions_count", count: total_additions), - deletions: t("layout.projects.commit_deletions_count", count: total_deletions)) - -begin - == render_diff_stats @stats - - diffs = Grit::Commit.diff(@project.repo, @common_ancestor.id, @commit.id) - == render partial: 'projects/git/commits/diff', collection: diffs, locals: { parent_commit: @common_ancestor } + - diffs = @project.repo.diff @common_ancestor.id, @commit.id + == render_diff_stats(stats: @stats, diff: diffs, repo: @project.repo, + commit: @commit, common_ancestor: @common_ancestor) + + == render partial: 'projects/git/commits/diff', collection: diffs, + locals: { commit: @commit, common_ancestor: @common_ancestor} - rescue Grit::Git::GitTimeout p= t 'layout.git.repositories.commit_diff_too_big' diff --git a/app/views/projects/pull_requests/_diff_tab.html.slim b/app/views/projects/pull_requests/_diff_tab.html.slim index 095c93fe9..cf42ee54d 100644 --- a/app/views/projects/pull_requests/_diff_tab.html.slim +++ b/app/views/projects/pull_requests/_diff_tab.html.slim @@ -1,6 +1,8 @@ -begin - == render_diff_stats(@stats) - == render partial: 'pull_diff', collection: @pull.diff + - diff = @pull.diff + == render_diff_stats(stats: @stats, diff: diff, repo: @pull.repo, + commit: @pull.from_commit, common_ancestor: @pull.to_commit) + == render partial: 'pull_diff', collection: diff - rescue => ex -if ex.try(:message) == 'Grit::Git::GitTimeout' p= t 'layout.git.repositories.commit_diff_too_big' diff --git a/app/views/projects/pull_requests/_pull_diff.html.slim b/app/views/projects/pull_requests/_pull_diff.html.slim index 7e3b9c67c..66ff9e01f 100644 --- a/app/views/projects/pull_requests/_pull_diff.html.slim +++ b/app/views/projects/pull_requests/_pull_diff.html.slim @@ -1,10 +1,11 @@ -- commit_id = pull_diff.deleted_file ? @pull.to_commit.id : @pull.from_commit.id -- diff_counter = "diff-#{pull_diff_counter}" -- diff_counter_content = "#{diff_counter}_content" +- diff = pull_diff +- diff_counter = "diff-#{pull_diff_counter}" --if pull_diff.diff.present? - - blob = @pull.repo.tree(commit_id) / pull_diff.b_path - - is_file_open = 'in' if !blob.binary? && pull_diff.diff.split("\n").count <= DiffHelper::MAX_LINES_WITHOUT_COLLAPSE +- parent_commit = @pull.to_commit +- commit_id = get_commit_id_for_file diff, @pull.from_commit, parent_commit +- diff_counter_content = "#{diff_counter}_content" +- blob = file_blob_in_diff(@pull.repo, commit_id, diff) +- is_file_open = 'in' if is_file_open_in_diff(blob, diff) .file.offset10 a name = diff_counter @@ -15,19 +16,23 @@ aria-expanded = 'true' aria-controls = diff_counter_content ] span.fa class= (is_file_open ? 'fa-chevron-down' : 'fa-chevron-up') - =< h(pull_diff.renamed_file ? "#{pull_diff.a_path.rtruncate 60}=>#{pull_diff.b_path.rtruncate 60}" : pull_diff.b_path.rtruncate(120)) + =< diff_file_icon(diff).html_safe + =< get_filename_in_diff(diff, diff.a_path) - if @pull.from_project.present? button.btn.btn-link.pull-right = link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@pull.from_project, commit_id, pull_diff.b_path) .clearfix - -if pull_diff.diff.present? - .diff_data.collapse[ id = diff_counter_content class = is_file_open ] - - if blob.render_as == :image - table.table.diff.inline.table-bordered[ cellspacing = 0 cellpadding = 0 ] - tr - td.diff-image - span.diff-image - img[ src = "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}" - style = 'max-width: 600px;' ] + .diff_data.collapse id= diff_counter_content class= is_file_open + - if blob.render_as == :image && diff.diff.present? + - old_blob = file_blob_in_diff(@project.repo, parent_commit.id, diff) + == render 'projects/git/commits/show_image', diff: diff, blob: blob, old_blob: old_blob + + - if diff.a_mode != diff.b_mode && diff.diff.blank? + == render 'file_change_mode', blob: blob, diff: diff + + - elsif diff.diff.blank? && diff.renamed_file + == render 'projects/git/commits/file_removed_not_changed', blob: blob + + - elsif !blob.binary? == render_diff(pull_diff, diff_counter: pull_diff_counter, comments: @comments) unless blob.binary? diff --git a/config/locales/models/comment.en.yml b/config/locales/models/comment.en.yml index ed1f6f504..e4045a103 100644 --- a/config/locales/models/comment.en.yml +++ b/config/locales/models/comment.en.yml @@ -47,6 +47,9 @@ en: saved: Comment saved save_error: Error while saving comment destroyed: Comment deleted + error_in_deleting: Error deleting comment + updated: Comment updated + error_in_updating: Error updating comment activerecord: attributes: diff --git a/config/locales/models/comment.ru.yml b/config/locales/models/comment.ru.yml index 0b8c199a4..f7fb6c92b 100644 --- a/config/locales/models/comment.ru.yml +++ b/config/locales/models/comment.ru.yml @@ -43,9 +43,12 @@ ru: flash: comment: - saved: Комментарий успешно сохранен + saved: Комментарий сохранен save_error: Ошибка сохранения комментария destroyed: Комментарий удален + error_in_deleting: Ошибка при удалении комментария + updated: Комментарий обновлен + error_in_updating: Ошибка при обновлении комментария activerecord: attributes: diff --git a/config/locales/models/project.en.yml b/config/locales/models/project.en.yml index 0666784c5..8dbdcc6c6 100644 --- a/config/locales/models/project.en.yml +++ b/config/locales/models/project.en.yml @@ -94,6 +94,14 @@ en: public: Public private: Private + diff: + binary: Binary + new_file: file added + deleted_file: file removed + renamed_file: file renamed + changed_file: file changed + without_changes: without changes + flash: project: mass_import_added_to_queue: Mass import added to queue diff --git a/config/locales/models/project.ru.yml b/config/locales/models/project.ru.yml index 357299af9..dab880340 100644 --- a/config/locales/models/project.ru.yml +++ b/config/locales/models/project.ru.yml @@ -95,6 +95,14 @@ ru: public: Публичные private: Приватные + diff: + binary: Бинарный + new_file: файл добавлен + deleted_file: файл удален + renamed_file: файл переименован + changed_file: файл изменен + without_changes: без изменений + flash: project: mass_import_added_to_queue: Массовый импорт добавлен в очередь diff --git a/config/locales/pluralization.en.yml b/config/locales/pluralization.en.yml index 5baff28e7..9b966f340 100644 --- a/config/locales/pluralization.en.yml +++ b/config/locales/pluralization.en.yml @@ -11,12 +11,6 @@ en: one: "%{count} deletion" other: "%{count} deletions" - inline_additions_count: - one: "%{count} addition" - other: "%{count} additions" - inline_deletions_count: - one: "%{count} deletion" - other: "%{count} deletions" inline_changes_count: one: "%{count} change" other: "%{count} changes" diff --git a/config/locales/pluralization.ru.yml b/config/locales/pluralization.ru.yml index 2ec2d215c..06a6842c9 100644 --- a/config/locales/pluralization.ru.yml +++ b/config/locales/pluralization.ru.yml @@ -14,14 +14,6 @@ ru: few: "%{count} удалениями" many: "%{count} удалениями" - inline_additions_count: - one: "%{count} добавление" - few: "%{count} добавления" - many: "%{count} добавлений" - inline_deletions_count: - one: "%{count} удаление" - few: "%{count} удаления" - many: "%{count} удалений" inline_changes_count: one: "%{count} изменение" few: "%{count} изменения" diff --git a/lib/ext/core/string.rb b/lib/ext/core/string.rb index b22e3dee6..b8837a085 100644 --- a/lib/ext/core/string.rb +++ b/lib/ext/core/string.rb @@ -7,7 +7,9 @@ class String force_encoding(default_encoding) else # should encode options = {invalid: :replace, undef: :replace, replace: ''} - if (detected = detect_encoding) && detected[:encoding] + if encoding.name == 'UTF-8' + encode!(default_encoding, 'UTF-8', options) + elsif (detected = detect_encoding) && detected[:encoding] force_encoding(detected[:encoding]).encode!(default_encoding, detected[:encoding], options) end scrub('') diff --git a/lib/ext/git/grit.rb b/lib/ext/git/grit.rb index 62ca5dc6a..4763e6e00 100644 --- a/lib/ext/git/grit.rb +++ b/lib/ext/git/grit.rb @@ -63,8 +63,7 @@ module Grit end def diff(a, b, *paths) - diff = self.git.native('diff', {M: true}, "#{a}...#{b}", '--', *paths) - + diff = self.git.native('diff', {}, "#{a}...#{b}", '--', *paths) if diff =~ /diff --git a/ diff = diff.sub(/.*?(diff --git a)/m, '\1') else diff --git a/lib/ext/posix_spawn.rb b/lib/ext/posix_spawn.rb new file mode 100644 index 000000000..bd708da07 --- /dev/null +++ b/lib/ext/posix_spawn.rb @@ -0,0 +1,101 @@ +require 'posix/spawn' + +module POSIX + module Spawn + class Child + include POSIX::Spawn + private + # Start a select loop writing any input on the child's stdin and reading + # any output from the child's stdout or stderr. + # + # input - String input to write on stdin. May be nil. + # stdin - The write side IO object for the child's stdin stream. + # stdout - The read side IO object for the child's stdout stream. + # stderr - The read side IO object for the child's stderr stream. + # timeout - An optional Numeric specifying the total number of seconds + # the read/write operations should occur for. + # + # Returns an [out, err] tuple where both elements are strings with all + # data written to the stdout and stderr streams, respectively. + # Raises TimeoutExceeded when all data has not been read / written within + # the duration specified in the timeout argument. + # Raises MaximumOutputExceeded when the total number of bytes output + # exceeds the amount specified by the max argument. + def read_and_write(input, stdin, stdout, stderr, timeout=nil, max=nil) + max = nil if max && max <= 0 + @out, @err = '', '' + offset = 0 + + # force all string and IO encodings to BINARY under 1.9 for now + #if @out.respond_to?(:force_encoding) and stdin.respond_to?(:set_encoding) + # [stdin, stdout, stderr].each do |fd| + # fd.set_encoding('BINARY', 'BINARY') + # end + # @out.force_encoding('BINARY') + # @err.force_encoding('BINARY') + # input = input.dup.force_encoding('BINARY') if input + #end + + timeout = nil if timeout && timeout <= 0.0 + @runtime = 0.0 + start = Time.now + + readers = [stdout, stderr] + writers = + if input + [stdin] + else + stdin.close + [] + end + slice_method = input.respond_to?(:byteslice) ? :byteslice : :slice + t = timeout + + while readers.any? || writers.any? + ready = IO.select(readers, writers, readers + writers, t) + raise TimeoutExceeded if ready.nil? + + # write to stdin stream + ready[1].each do |fd| + begin + boom = nil + size = fd.write_nonblock(input) + input = input.send(slice_method, size..-1) + rescue Errno::EPIPE => boom + rescue Errno::EAGAIN, Errno::EINTR + end + if boom || input.bytesize == 0 + stdin.close + writers.delete(stdin) + end + end + + # read from stdout and stderr streams + ready[0].each do |fd| + buf = (fd == stdout) ? @out : @err + begin + buf << fd.readpartial(BUFSIZE) + rescue Errno::EAGAIN, Errno::EINTR + rescue EOFError + readers.delete(fd) + fd.close + end + end + + # keep tabs on the total amount of time we've spent here + @runtime = Time.now - start + if timeout + t = timeout - @runtime + raise TimeoutExceeded if t < 0.0 + end + + # maybe we've hit our max output + if max && ready[0].any? && (@out.size + @err.size) > max + raise MaximumOutputExceeded + end + end + [@out.mb_chars.default_encoding!, @err.mb_chars.default_encoding!] + end + end + end +end