Merge branch 'master' into 465-use-pundit-instead-of-cancan
This commit is contained in:
commit
c7d5ca7305
2
Gemfile
2
Gemfile
|
@ -29,7 +29,7 @@ gem 'state_machines-activerecord'
|
||||||
gem 'redis-rails'
|
gem 'redis-rails'
|
||||||
|
|
||||||
gem 'grack', git: 'git://github.com/rosa-abf/grack.git', require: 'git_http'
|
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 'charlock_holmes'
|
||||||
gem 'github-linguist', '3.1.5', require: 'linguist'
|
gem 'github-linguist', '3.1.5', require: 'linguist'
|
||||||
gem 'diff-display'
|
gem 'diff-display'
|
||||||
|
|
|
@ -34,8 +34,8 @@ GIT
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/rosa-abf/grit.git
|
remote: git://github.com/rosa-abf/grit.git
|
||||||
revision: a9548c92188cc307e7af1dd41a733e7000a783a9
|
revision: b733f0ceefb44b18a9dec8f509ba5493dab59e4e
|
||||||
tag: 2.6.16
|
tag: 2.6.17
|
||||||
specs:
|
specs:
|
||||||
grit (2.5.0)
|
grit (2.5.0)
|
||||||
diff-lcs (~> 1.1)
|
diff-lcs (~> 1.1)
|
||||||
|
|
|
@ -75,15 +75,22 @@ CommentsController = (Comment, Preview, confirmMessage, $scope, compileHTML, $ro
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
|
||||||
vm.add = ->
|
vm.add = ($event)->
|
||||||
|
$event.preventDefault()
|
||||||
|
$event.stopPropagation()
|
||||||
|
|
||||||
vm.processing = true
|
vm.processing = true
|
||||||
promise = Comment.add(vm.project, vm.commentable, vm.new_body)
|
Comment.add(vm.project, vm.commentable, vm.new_body)
|
||||||
promise.then (response) ->
|
.success (data) ->
|
||||||
element = compileHTML.run($scope, response.data.html)
|
element = compileHTML.run($scope, data.html)
|
||||||
list.append(element)
|
list.append(element)
|
||||||
|
|
||||||
vm.new_body = ''
|
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
|
vm.processing = false
|
||||||
|
|
||||||
false
|
false
|
||||||
|
@ -91,8 +98,8 @@ CommentsController = (Comment, Preview, confirmMessage, $scope, compileHTML, $ro
|
||||||
vm.remove = (id) ->
|
vm.remove = (id) ->
|
||||||
return false unless confirmMessage.show()
|
return false unless confirmMessage.show()
|
||||||
vm.processing = true
|
vm.processing = true
|
||||||
promise = Comment.remove(vm.project, vm.commentable, id)
|
Comment.remove(vm.project, vm.commentable, id)
|
||||||
promise.then () ->
|
.success (data)->
|
||||||
parent = $('#comment'+id+',#diff-comment'+id).parents('tr.line-comments')
|
parent = $('#comment'+id+',#diff-comment'+id).parents('tr.line-comments')
|
||||||
if parent.find('.line-comment').length is 1
|
if parent.find('.line-comment').length is 1
|
||||||
# there is only one line comment, remove all line
|
# there is only one line comment, remove all line
|
||||||
|
@ -100,16 +107,21 @@ CommentsController = (Comment, Preview, confirmMessage, $scope, compileHTML, $ro
|
||||||
else
|
else
|
||||||
$('#comment'+id+',#diff-comment'+id+',#update-comment'+id).remove()
|
$('#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
|
vm.processing = false
|
||||||
|
|
||||||
false
|
false
|
||||||
|
|
||||||
vm.update = (id) ->
|
vm.update = (id) ->
|
||||||
vm.processing = true
|
vm.processing = true
|
||||||
promise = Comment.update(vm.project, vm.commentable, id)
|
Comment.update(vm.project, vm.commentable, id)
|
||||||
promise.then (response) ->
|
.success (data) ->
|
||||||
form = $('#comment'+id+ ' .md_and_cm.cm-s-default').html(response.data.body)
|
form = $('#comment'+id+ ' .md_and_cm.cm-s-default').html(data.body)
|
||||||
|
|
||||||
|
$.notify(data.message, 'success')
|
||||||
vm.processing = false
|
vm.processing = false
|
||||||
form = $('.open-comment.comment-'+id)
|
form = $('.open-comment.comment-'+id)
|
||||||
if form.length is 1
|
if form.length is 1
|
||||||
|
@ -117,6 +129,9 @@ CommentsController = (Comment, Preview, confirmMessage, $scope, compileHTML, $ro
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
|
.error (data) ->
|
||||||
|
$.notify(data.message, 'error')
|
||||||
|
vm.processing = false
|
||||||
|
|
||||||
vm.showInlineForm = ($event, params = {}) ->
|
vm.showInlineForm = ($event, params = {}) ->
|
||||||
line_comments = findInlineComments($event, params)
|
line_comments = findInlineComments($event, params)
|
||||||
|
|
|
@ -108,9 +108,8 @@ PullRequestController = (dataservice, $http, ApiPullRequest, ApiProject, DateTim
|
||||||
promise = ApiPullRequest.get_diff(vm.pull_params)
|
promise = ApiPullRequest.get_diff(vm.pull_params)
|
||||||
promise.then (response) ->
|
promise.then (response) ->
|
||||||
diff.html(null)
|
diff.html(null)
|
||||||
#html = compileHTML.run($scope, response.data)
|
|
||||||
#diff.html(html)
|
|
||||||
$rootScope.$broadcast('compile_html', { element: diff, html: response.data })
|
$rootScope.$broadcast('compile_html', { element: diff, html: response.data })
|
||||||
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
vm.processing = false
|
vm.processing = false
|
||||||
vm.is_diff_updated = true
|
vm.is_diff_updated = true
|
||||||
false
|
false
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
$(document).ready ->
|
$(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')
|
href = $(this).attr('href')
|
||||||
$(".diff_data.collapse#"+href.slice(1)+"_content").collapse('show')
|
$(".diff_data.collapse#"+href.slice(1)+"_content").collapse('show')
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
overflow-x: auto
|
overflow-x: auto
|
||||||
|
|
||||||
table.table.diff.inline
|
table.table.diff.inline
|
||||||
//border: 1px solid #DDD
|
margin-bottom: 0
|
||||||
|
|
||||||
tr.changes
|
tr.changes
|
||||||
pre
|
pre
|
||||||
|
@ -66,17 +66,6 @@ table.table.diff.inline
|
||||||
.line-comment, #new_inline_comment
|
.line-comment, #new_inline_comment
|
||||||
max-width: 700px
|
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
|
div.file div.top
|
||||||
min-height: 28px
|
min-height: 28px
|
||||||
background: #ededed
|
background: #ededed
|
||||||
|
|
|
@ -6,15 +6,19 @@ class Projects::CommentsController < Projects::BaseController
|
||||||
include CommentsHelper
|
include CommentsHelper
|
||||||
|
|
||||||
def create
|
def create
|
||||||
if !@comment.set_additional_data params
|
respond_to do |format|
|
||||||
render json: {
|
if !@comment.set_additional_data params
|
||||||
error: I18n.t("flash.comment.save_error"),
|
format.json {
|
||||||
message: @comment.errors.full_messages
|
render json: {
|
||||||
}
|
message: I18n.t("flash.comment.save_error"),
|
||||||
elsif @comment.save
|
error: @comment.errors.full_messages
|
||||||
render :create
|
}
|
||||||
else
|
}
|
||||||
render json: { error: I18n.t("flash.comment.save_error") }, status: 422
|
elsif @comment.save
|
||||||
|
format.json {}
|
||||||
|
else
|
||||||
|
format.json { render json: { message: I18n.t("flash.comment.save_error") }, status: 422 }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,17 +26,24 @@ class Projects::CommentsController < Projects::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
status, message = if @comment.update_attributes(params[:comment])
|
respond_to do |format|
|
||||||
[200, view_context.markdown(@comment.body)]
|
if @comment.update_attributes(params[:comment])
|
||||||
else
|
format.json { render json: {message:t('flash.comment.updated'), body: view_context.markdown(@comment.body)} }
|
||||||
[422, 'error']
|
else
|
||||||
|
format.json { render json: {message:t('flash.comment.error_in_updating')}, status: 422 }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
render json: {body: message}, status: status
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@comment.destroy
|
respond_to do |format|
|
||||||
render json: nil
|
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
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Projects::Git::CommitsController < Projects::Git::BaseController
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
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" }
|
format.patch { render text: (@commit.to_patch rescue ''), content_type: "text/plain" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,23 +1,31 @@
|
||||||
module CommitHelper
|
module CommitHelper
|
||||||
MAX_FILES_WITHOUT_COLLAPSE = 25
|
MAX_FILES_WITHOUT_COLLAPSE = 25
|
||||||
|
|
||||||
def render_commit_stats(stats)
|
def render_commit_stats(options = {})
|
||||||
res = ["<table class='table table-responsive boffset0'>"]
|
stats = options[:stats]
|
||||||
|
diff = options[:diff]
|
||||||
|
repo = options[:repo]
|
||||||
|
commit = options[:commit]
|
||||||
|
parent_commit = commit.parents.try(:first)
|
||||||
|
|
||||||
|
res = ["<ul class='list-group boffset0'>"]
|
||||||
ind=0
|
ind=0
|
||||||
stats.files.each do |filename, adds, deletes, total|
|
stats.files.each do |filename, adds, deletes, total|
|
||||||
res << "<tr>"
|
file_name = get_filename_in_diff(diff[ind], filename)
|
||||||
res << "<td><a href='#diff-#{ind}'>#{h(filename.rtruncate 120)}</a></td>"
|
file_status = t "layout.projects.diff.#{get_file_status_in_diff(diff[ind])}"
|
||||||
res << "<td class='diffstat'>"
|
res << "<li class='list-group-item'>"
|
||||||
res << I18n.t("layout.projects.inline_changes_count", count: total).strip +
|
res << "<div class='row'>"
|
||||||
" (" +
|
res << "<div class='col-sm-8'>"
|
||||||
I18n.t("layout.projects.inline_additions_count", count: adds).strip +
|
res << "<a href='#diff-#{ind}' data-toggle='tooltip' data-placement='top' title='#{file_status}'>"
|
||||||
", " +
|
res << "#{diff_file_icon(diff[ind])} #{h(file_name)}"
|
||||||
I18n.t("layout.projects.inline_deletions_count", count: deletes).strip +
|
res << "</a></div>"
|
||||||
")"
|
res << render_file_changes(diff: diff[ind], adds: adds, deletes: deletes, total: total,
|
||||||
res << "</td>"
|
repo: repo, commit: commit, parent_commit: parent_commit, file_status: file_status)
|
||||||
|
res << "</div"
|
||||||
|
res << "</li>"
|
||||||
ind +=1
|
ind +=1
|
||||||
end
|
end
|
||||||
res << "</table>"
|
res << "</ul>"
|
||||||
|
|
||||||
wrap_commit_header_list(stats, res)
|
wrap_commit_header_list(stats, res)
|
||||||
end
|
end
|
||||||
|
@ -73,9 +81,116 @@ module CommitHelper
|
||||||
Russian.p(commits_count, *commits_pluralization_arr)
|
Russian.p(commits_count, *commits_pluralization_arr)
|
||||||
end
|
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
|
protected
|
||||||
|
|
||||||
def commits_pluralization_arr
|
def commits_pluralization_arr
|
||||||
pluralize ||= t('layout.commits.pluralize').map {|base, title| title.to_s}
|
pluralize ||= t('layout.commits.pluralize').map {|base, title| title.to_s}
|
||||||
end
|
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 << "<div class='col-sm-3'>"
|
||||||
|
res << "<div class='pull-right'>"
|
||||||
|
if blob.binary?
|
||||||
|
res << "<strong class='text-primary'>#{t 'layout.projects.diff.binary'} #{file_status}</strong>"
|
||||||
|
elsif total > 0
|
||||||
|
res << "<strong class='text-success'>+#{adds}</strong> <strong class='text-danger'>-#{deletes}</strong>"
|
||||||
|
else # total == 0
|
||||||
|
res << "<strong class='text-primary'>#{t 'layout.projects.diff.without_changes'}</strong>"
|
||||||
|
end
|
||||||
|
res << "</div>"
|
||||||
|
res << "</div>"
|
||||||
|
|
||||||
|
res << "<div class='col-sm-1'>"
|
||||||
|
res << render_progress_bar(adds, deletes, total, blob)
|
||||||
|
res << "</div>"
|
||||||
|
|
||||||
|
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 << "<div class='progress' style='margin-bottom: 0' data-toggle='tooltip' data-placement='top' title='#{title}'>"
|
||||||
|
res << "<div class='progress-bar progress-bar-success' style='width: #{pluses}%'></div>"
|
||||||
|
res << "<div class='progress-bar progress-bar-danger' style='width: #{minuses}%'></div>"
|
||||||
|
res << "</div>"
|
||||||
|
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
|
||||||
|
"<i class='fa #{icon}'></i>"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,26 +1,41 @@
|
||||||
module DiffHelper
|
module DiffHelper
|
||||||
MAX_FILES_WITHOUT_COLLAPSE = 25
|
include CommitHelper
|
||||||
|
|
||||||
MAX_LINES_WITHOUT_COLLAPSE = 50
|
MAX_LINES_WITHOUT_COLLAPSE = 50
|
||||||
|
|
||||||
def render_diff_stats(stats)
|
def render_diff_stats(options = {})
|
||||||
res = ["<table class='table table-responsive boffset0'>"]
|
stats = options[:stats]
|
||||||
|
diff = options[:diff]
|
||||||
|
repo = options[:repo]
|
||||||
|
commit = options[:commit]
|
||||||
|
parent_commit = options[:common_ancestor]
|
||||||
|
|
||||||
|
res = ["<ul class='list-group boffset0'>"]
|
||||||
stats.each_with_index do |stat, ind|
|
stats.each_with_index do |stat, ind|
|
||||||
res << "<tr>"
|
adds = stat.additions
|
||||||
res << "<td>#{link_to stat.filename.rtruncate(120), "#diff-#{ind}"}</td>"
|
deletes = stat.deletions
|
||||||
res << "<td class='diffstat'>"
|
total = adds + deletes
|
||||||
res << I18n.t("layout.projects.inline_changes_count", count: stat.additions + stat.deletions).strip +
|
file_name = get_filename_in_diff(diff[ind], stat.filename)
|
||||||
" (" +
|
file_status = t "layout.projects.diff.#{get_file_status_in_diff(diff[ind])}"
|
||||||
I18n.t("layout.projects.inline_additions_count", count: stat.additions).strip +
|
|
||||||
", " +
|
res << "<li class='list-group-item'>"
|
||||||
I18n.t("layout.projects.inline_deletions_count", count: stat.deletions).strip +
|
res << "<div class='row'>"
|
||||||
")"
|
res << "<div class='col-sm-8'>"
|
||||||
res << "</td>"
|
res << "<a href='#diff-#{ind}' data-toggle='tooltip' data-placement='top' title='#{file_status}'>"
|
||||||
|
res << "#{diff_file_icon(diff[ind])} #{h(file_name)}"
|
||||||
|
res << "</a></div>"
|
||||||
|
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 << "</div"
|
||||||
|
res << "</li>"
|
||||||
|
ind +=1
|
||||||
end
|
end
|
||||||
res << '</table>'
|
res << "</ul>"
|
||||||
wrap_header_list(stats, res)
|
|
||||||
|
wrap_diff_header_list(stats, res)
|
||||||
end
|
end
|
||||||
|
|
||||||
def wrap_header_list(stats, list)
|
def wrap_diff_header_list(stats, list)
|
||||||
is_stats_open = stats.count <= MAX_FILES_WITHOUT_COLLAPSE ? 'in' : ''
|
is_stats_open = stats.count <= MAX_FILES_WITHOUT_COLLAPSE ? 'in' : ''
|
||||||
res = ["<div class='panel-group' id='diff_header' role='tablist' aria-multiselectable='false'>"]
|
res = ["<div class='panel-group' id='diff_header' role='tablist' aria-multiselectable='false'>"]
|
||||||
res << "<div class='panel panel-default'>"
|
res << "<div class='panel panel-default'>"
|
||||||
|
|
|
@ -65,7 +65,7 @@ class Comment < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
return data[:actual] = true if commentable_type == 'Grit::Commit'
|
return data[:actual] = true if commentable_type == 'Grit::Commit'
|
||||||
filepath, line_number = data[:path], data[:line]
|
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
|
comment_line = data[:line].to_i
|
||||||
# NB! also dont create a comment to the diff header
|
# NB! also dont create a comment to the diff header
|
||||||
return data[:actual] = false if diff_path.blank? || comment_line == 0
|
return data[:actual] = false if diff_path.blank? || comment_line == 0
|
||||||
|
|
|
@ -163,9 +163,8 @@ class PullRequest < ActiveRecord::Base
|
||||||
@diff_stats ||= repo.diff_stats(to_commit.id, from_commit.id)
|
@diff_stats ||= repo.diff_stats(to_commit.id, from_commit.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME maybe move to warpc/grit?
|
|
||||||
def diff
|
def diff
|
||||||
@diff ||= Grit::Commit.diff(repo, to_commit.id, from_commit.id)
|
@diff ||= repo.diff(to_commit.id, from_commit.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_user_and_time user
|
def set_user_and_time user
|
||||||
|
|
|
@ -13,7 +13,7 @@ h3
|
||||||
ang_model: 'new_body'
|
ang_model: 'new_body'
|
||||||
hr
|
hr
|
||||||
button.btn.btn-primary[ ng-disabled = 'commentsCtrl.isDisabledNewCommentButton()'
|
button.btn.btn-primary[ ng-disabled = 'commentsCtrl.isDisabledNewCommentButton()'
|
||||||
ng-click = 'commentsCtrl.add()' ]
|
ng-click = 'commentsCtrl.add($event)' ]
|
||||||
= t('layout.create')
|
= t('layout.create')
|
||||||
.panel-footer
|
.panel-footer
|
||||||
=> t('layout.comments.notifications_are')
|
=> t('layout.comments.notifications_are')
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
json.id @comment.id
|
json.id @comment.id
|
||||||
json.html render partial: 'projects/comments/line_comment.html.slim'
|
json.html render partial: 'projects/comments/line_comment.html.slim'
|
||||||
|
json.message t('flash.comment.saved')
|
||||||
|
|
|
@ -12,6 +12,6 @@
|
||||||
ul.list-group
|
ul.list-group
|
||||||
- commits.each_with_index do |commit|
|
- commits.each_with_index do |commit|
|
||||||
li.list-group-item
|
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
|
== render 'shared/feed_commit', presenter: presenter, item_no: counter
|
||||||
- counter += 1
|
- counter += 1
|
||||||
|
|
|
@ -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"
|
- diff_counter_content = "diff-#{diff_counter}_content"
|
||||||
|
- blob = file_blob_in_diff(@project.repo, commit_id, diff)
|
||||||
- if diff.diff.present?
|
- is_file_open = 'in' if is_file_open_in_diff(blob, diff)
|
||||||
- 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
|
|
||||||
|
|
||||||
.file.offset10
|
.file.offset10
|
||||||
a name = "diff-#{diff_counter}"
|
a name = "diff-#{diff_counter}"
|
||||||
|
@ -14,23 +13,23 @@
|
||||||
aria-expanded = 'true'
|
aria-expanded = 'true'
|
||||||
aria-controls = diff_counter_content ]
|
aria-controls = diff_counter_content ]
|
||||||
span.fa class= (is_file_open ? 'fa-chevron-down' : 'fa-chevron-up')
|
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?
|
- if diff.b_path.present?
|
||||||
button.btn.btn-link.pull-right
|
button.btn.btn-link.pull-right
|
||||||
= link_to "view file @ #{shortest_hash_id(commit_id)}", blob_path(@project, commit_id, diff.b_path)
|
= link_to "view file @ #{shortest_hash_id(commit_id)}", blob_path(@project, commit_id, diff.b_path)
|
||||||
.clearfix
|
.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
|
- if diff.a_mode != diff.b_mode && diff.diff.blank?
|
||||||
table.table.diff.inline.table-bordered[ cellspacing = 0 cellpadding = 0 ]
|
== render 'file_change_mode', blob: blob, diff: diff
|
||||||
tr
|
|
||||||
td.diff-image
|
- elsif diff.diff.blank? && diff.renamed_file
|
||||||
span.diff-image
|
== render 'file_removed_not_changed', blob: blob
|
||||||
img[ src = "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"
|
|
||||||
style = 'max-width: 600px;' ]
|
- elsif !blob.binary?
|
||||||
- elsif !blob.binary?
|
== render_diff(diff, diff_counter: diff_counter, comments: @comments)
|
||||||
- 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)
|
|
||||||
|
|
|
@ -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}"
|
|
@ -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'}"
|
|
@ -1,6 +1,7 @@
|
||||||
- begin
|
- begin
|
||||||
- stats = @commit.stats
|
- diffs = @commit.show
|
||||||
= render_commit_stats(stats)
|
- 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
|
.pull-right
|
||||||
=> link_to 'raw diff', commit_path(@project, @commit.id, :diff)
|
=> link_to 'raw diff', commit_path(@project, @commit.id, :diff)
|
||||||
|
@ -8,6 +9,6 @@
|
||||||
=< link_to 'patch', commit_path(@project, @commit.id, :patch)
|
=< link_to 'patch', commit_path(@project, @commit.id, :patch)
|
||||||
.clearfix
|
.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
|
- rescue Grit::Git::GitTimeout
|
||||||
h3.text-danger= t('layout.git.repositories.commit_diff_too_big')
|
h3.text-danger= t('layout.git.repositories.commit_diff_too_big')
|
||||||
|
|
|
@ -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;' ]
|
|
@ -12,18 +12,13 @@
|
||||||
h3= title
|
h3= title
|
||||||
|
|
||||||
#repo-wrapper
|
#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
|
-begin
|
||||||
== render_diff_stats @stats
|
- diffs = @project.repo.diff @common_ancestor.id, @commit.id
|
||||||
- diffs = Grit::Commit.diff(@project.repo, @common_ancestor.id, @commit.id)
|
== render_diff_stats(stats: @stats, diff: diffs, repo: @project.repo,
|
||||||
== render partial: 'projects/git/commits/diff', collection: diffs, locals: { parent_commit: @common_ancestor }
|
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
|
- rescue Grit::Git::GitTimeout
|
||||||
p= t 'layout.git.repositories.commit_diff_too_big'
|
p= t 'layout.git.repositories.commit_diff_too_big'
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
-begin
|
-begin
|
||||||
== render_diff_stats(@stats)
|
- diff = @pull.diff
|
||||||
== render partial: 'pull_diff', collection: @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
|
- rescue => ex
|
||||||
-if ex.try(:message) == 'Grit::Git::GitTimeout'
|
-if ex.try(:message) == 'Grit::Git::GitTimeout'
|
||||||
p= t 'layout.git.repositories.commit_diff_too_big'
|
p= t 'layout.git.repositories.commit_diff_too_big'
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
- commit_id = pull_diff.deleted_file ? @pull.to_commit.id : @pull.from_commit.id
|
- diff = pull_diff
|
||||||
- diff_counter = "diff-#{pull_diff_counter}"
|
- diff_counter = "diff-#{pull_diff_counter}"
|
||||||
- diff_counter_content = "#{diff_counter}_content"
|
|
||||||
|
|
||||||
-if pull_diff.diff.present?
|
- parent_commit = @pull.to_commit
|
||||||
- blob = @pull.repo.tree(commit_id) / pull_diff.b_path
|
- commit_id = get_commit_id_for_file diff, @pull.from_commit, parent_commit
|
||||||
- is_file_open = 'in' if !blob.binary? && pull_diff.diff.split("\n").count <= DiffHelper::MAX_LINES_WITHOUT_COLLAPSE
|
- 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
|
.file.offset10
|
||||||
a name = diff_counter
|
a name = diff_counter
|
||||||
|
@ -15,19 +16,23 @@
|
||||||
aria-expanded = 'true'
|
aria-expanded = 'true'
|
||||||
aria-controls = diff_counter_content ]
|
aria-controls = diff_counter_content ]
|
||||||
span.fa class= (is_file_open ? 'fa-chevron-down' : 'fa-chevron-up')
|
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?
|
- if @pull.from_project.present?
|
||||||
button.btn.btn-link.pull-right
|
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)
|
= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@pull.from_project, commit_id, pull_diff.b_path)
|
||||||
.clearfix
|
.clearfix
|
||||||
|
|
||||||
-if pull_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.render_as == :image && diff.diff.present?
|
||||||
- if blob.render_as == :image
|
- old_blob = file_blob_in_diff(@project.repo, parent_commit.id, diff)
|
||||||
table.table.diff.inline.table-bordered[ cellspacing = 0 cellpadding = 0 ]
|
== render 'projects/git/commits/show_image', diff: diff, blob: blob, old_blob: old_blob
|
||||||
tr
|
|
||||||
td.diff-image
|
- if diff.a_mode != diff.b_mode && diff.diff.blank?
|
||||||
span.diff-image
|
== render 'file_change_mode', blob: blob, diff: diff
|
||||||
img[ src = "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"
|
|
||||||
style = 'max-width: 600px;' ]
|
- 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?
|
== render_diff(pull_diff, diff_counter: pull_diff_counter, comments: @comments) unless blob.binary?
|
||||||
|
|
|
@ -47,6 +47,9 @@ en:
|
||||||
saved: Comment saved
|
saved: Comment saved
|
||||||
save_error: Error while saving comment
|
save_error: Error while saving comment
|
||||||
destroyed: Comment deleted
|
destroyed: Comment deleted
|
||||||
|
error_in_deleting: Error deleting comment
|
||||||
|
updated: Comment updated
|
||||||
|
error_in_updating: Error updating comment
|
||||||
|
|
||||||
activerecord:
|
activerecord:
|
||||||
attributes:
|
attributes:
|
||||||
|
|
|
@ -43,9 +43,12 @@ ru:
|
||||||
|
|
||||||
flash:
|
flash:
|
||||||
comment:
|
comment:
|
||||||
saved: Комментарий успешно сохранен
|
saved: Комментарий сохранен
|
||||||
save_error: Ошибка сохранения комментария
|
save_error: Ошибка сохранения комментария
|
||||||
destroyed: Комментарий удален
|
destroyed: Комментарий удален
|
||||||
|
error_in_deleting: Ошибка при удалении комментария
|
||||||
|
updated: Комментарий обновлен
|
||||||
|
error_in_updating: Ошибка при обновлении комментария
|
||||||
|
|
||||||
activerecord:
|
activerecord:
|
||||||
attributes:
|
attributes:
|
||||||
|
|
|
@ -94,6 +94,14 @@ en:
|
||||||
public: Public
|
public: Public
|
||||||
private: Private
|
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:
|
flash:
|
||||||
project:
|
project:
|
||||||
mass_import_added_to_queue: Mass import added to queue
|
mass_import_added_to_queue: Mass import added to queue
|
||||||
|
|
|
@ -95,6 +95,14 @@ ru:
|
||||||
public: Публичные
|
public: Публичные
|
||||||
private: Приватные
|
private: Приватные
|
||||||
|
|
||||||
|
diff:
|
||||||
|
binary: Бинарный
|
||||||
|
new_file: файл добавлен
|
||||||
|
deleted_file: файл удален
|
||||||
|
renamed_file: файл переименован
|
||||||
|
changed_file: файл изменен
|
||||||
|
without_changes: без изменений
|
||||||
|
|
||||||
flash:
|
flash:
|
||||||
project:
|
project:
|
||||||
mass_import_added_to_queue: Массовый импорт добавлен в очередь
|
mass_import_added_to_queue: Массовый импорт добавлен в очередь
|
||||||
|
|
|
@ -11,12 +11,6 @@ en:
|
||||||
one: "%{count} deletion"
|
one: "%{count} deletion"
|
||||||
other: "%{count} deletions"
|
other: "%{count} deletions"
|
||||||
|
|
||||||
inline_additions_count:
|
|
||||||
one: "%{count} addition"
|
|
||||||
other: "%{count} additions"
|
|
||||||
inline_deletions_count:
|
|
||||||
one: "%{count} deletion"
|
|
||||||
other: "%{count} deletions"
|
|
||||||
inline_changes_count:
|
inline_changes_count:
|
||||||
one: "%{count} change"
|
one: "%{count} change"
|
||||||
other: "%{count} changes"
|
other: "%{count} changes"
|
||||||
|
|
|
@ -14,14 +14,6 @@ ru:
|
||||||
few: "%{count} удалениями"
|
few: "%{count} удалениями"
|
||||||
many: "%{count} удалениями"
|
many: "%{count} удалениями"
|
||||||
|
|
||||||
inline_additions_count:
|
|
||||||
one: "%{count} добавление"
|
|
||||||
few: "%{count} добавления"
|
|
||||||
many: "%{count} добавлений"
|
|
||||||
inline_deletions_count:
|
|
||||||
one: "%{count} удаление"
|
|
||||||
few: "%{count} удаления"
|
|
||||||
many: "%{count} удалений"
|
|
||||||
inline_changes_count:
|
inline_changes_count:
|
||||||
one: "%{count} изменение"
|
one: "%{count} изменение"
|
||||||
few: "%{count} изменения"
|
few: "%{count} изменения"
|
||||||
|
|
|
@ -7,7 +7,9 @@ class String
|
||||||
force_encoding(default_encoding)
|
force_encoding(default_encoding)
|
||||||
else # should encode
|
else # should encode
|
||||||
options = {invalid: :replace, undef: :replace, replace: ''}
|
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)
|
force_encoding(detected[:encoding]).encode!(default_encoding, detected[:encoding], options)
|
||||||
end
|
end
|
||||||
scrub('')
|
scrub('')
|
||||||
|
|
|
@ -63,8 +63,7 @@ module Grit
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff(a, b, *paths)
|
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/
|
if diff =~ /diff --git a/
|
||||||
diff = diff.sub(/.*?(diff --git a)/m, '\1')
|
diff = diff.sub(/.*?(diff --git a)/m, '\1')
|
||||||
else
|
else
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue