git-svn-id: http://redmine.rubyforge.org/svn/trunk@947 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/0.6.1
@@ -95,6 +95,11 @@ class RepositoriesController < ApplicationController | |||
end | |||
end | |||
def annotate | |||
@annotate = @repository.scm.annotate(@path, @rev) | |||
show_error and return if @annotate.nil? || @annotate.empty? | |||
end | |||
def revision | |||
@changeset = @repository.changesets.find_by_revision(@rev) | |||
raise ChangesetNotFound unless @changeset |
@@ -33,6 +33,10 @@ class Repository < ActiveRecord::Base | |||
def supports_cat? | |||
scm.supports_cat? | |||
end | |||
def supports_annotate? | |||
scm.supports_annotate? | |||
end | |||
def entries(path=nil, identifier=nil) | |||
scm.entries(path, identifier) |
@@ -0,0 +1,26 @@ | |||
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2> | |||
<% colors = Hash.new {|k,v| k[v] = (k.size % 12) } %> | |||
<div class="autoscroll"> | |||
<table class="filecontent annotate CodeRay"> | |||
<tbody> | |||
<% line_num = 1 %> | |||
<% syntax_highlight(@path, to_utf8(@annotate.content)).each_line do |line| %> | |||
<% revision = @annotate.revisions[line_num-1] %> | |||
<tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>"> | |||
<th class="line-num"><%= line_num %></th> | |||
<td class="revision"> | |||
<%= (revision.identifier ? link_to(revision.identifier, :action => 'revision', :id => @project, :rev => revision.identifier) : revision.revision) if revision %></td> | |||
<td class="author"><%= h(revision.author) if revision %></td> | |||
<td class="line-code"><pre><%= line %></pre></td> | |||
</tr> | |||
<% line_num += 1 %> | |||
<% end %> | |||
<tbody> | |||
</table> | |||
</div> | |||
<% content_for :header_tags do %> | |||
<%= stylesheet_link_tag 'scm' %> | |||
<% end %> |
@@ -2,14 +2,17 @@ | |||
<h3><%=h @entry.name %></h3> | |||
<% if @repository.supports_cat? %> | |||
<p> | |||
<% if @entry.is_text? %> | |||
<%= link_to l(:button_view), {:action => 'entry', :id => @project, :path => @path, :rev => @rev } %> | | |||
<% if @repository.supports_cat? %> | |||
<%= link_to l(:button_view), {:action => 'entry', :id => @project, :path => @path, :rev => @rev } %> | | |||
<% end %> | |||
<% if @repository.supports_annotate? %> | |||
<%= link_to l(:button_annotate), {:action => 'annotate', :id => @project, :path => @path, :rev => @rev } %> | | |||
<% end %> | |||
<% end %> | |||
<%= link_to l(:button_download), {:action => 'entry', :id => @project, :path => @path, :rev => @rev, :format => 'raw' } %> | |||
<%= link_to(l(:button_download), {:action => 'entry', :id => @project, :path => @path, :rev => @rev, :format => 'raw' }) if @repository.supports_cat? %> | |||
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %> | |||
</p> | |||
<% end %> | |||
<%= render :partial => 'revisions', :locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }%> |
@@ -2,11 +2,6 @@ | |||
<div class="autoscroll"> | |||
<table class="filecontent CodeRay"> | |||
<thead> | |||
<tr> | |||
<th colspan="2" class="filename"><%= @path %></th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<% line_num = 1 %> | |||
<% syntax_highlight(@path, to_utf8(@content)).each_line do |line| %> |
@@ -24,6 +24,7 @@ ActionController::Routing::Routes.draw do |map| | |||
omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes' | |||
omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff' | |||
omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry' | |||
omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate' | |||
end | |||
# Allow downloading Web Service WSDL as a file with an extension |
@@ -547,3 +547,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Ihr Konto wurde erstellt und wartet jetzt auf die Geneh | |||
field_time_zone: Zeitzone | |||
text_caracters_minimum: Muss mindestens %d Zeichen lang sein. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -489,6 +489,7 @@ button_reset: Reset | |||
button_rename: Rename | |||
button_change_password: Change password | |||
button_copy: Copy | |||
button_annotate: Annotate | |||
status_active: active | |||
status_registered: registered |
@@ -550,3 +550,4 @@ label_registration_manual_activation: activación manual de cuenta | |||
notice_account_pending: "Su cuenta ha sido creada y está pendiende de la aprobación por parte de administrador" | |||
setting_time_format: Formato de hora | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -489,6 +489,7 @@ button_reset: Réinitialiser | |||
button_rename: Renommer | |||
button_change_password: Changer de mot de passe | |||
button_copy: Copier | |||
button_annotate: Annoter | |||
status_active: actif | |||
status_registered: enregistré |
@@ -547,3 +547,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -548,3 +548,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -548,3 +548,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Twoje konto zostało utworzone i oczekuje na zatwierdze | |||
field_time_zone: Strefa czasowa | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -547,3 +547,4 @@ notice_account_pending: "Ваш аккаунт уже создан и ожида | |||
field_time_zone: Часовой пояс | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -548,3 +548,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -548,3 +548,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -550,3 +550,4 @@ notice_account_pending: "Your account was created and is now pending administrat | |||
field_time_zone: Time zone | |||
text_caracters_minimum: Must be at least %d characters long. | |||
setting_bcc_recipients: Blind carbon copy recipients (bcc) | |||
button_annotate: Annotate |
@@ -76,7 +76,7 @@ Redmine::AccessControl.map do |map| | |||
map.project_module :repository do |map| | |||
map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member | |||
map.permission :browse_repository, :repositories => [:show, :browse, :entry, :changes, :diff, :stats, :graph] | |||
map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph] | |||
map.permission :view_changesets, :repositories => [:show, :revisions, :revision] | |||
end | |||
@@ -38,6 +38,10 @@ module Redmine | |||
def supports_cat? | |||
true | |||
end | |||
def supports_annotate? | |||
respond_to?('annotate') | |||
end | |||
def root_url | |||
@root_url | |||
@@ -76,7 +80,7 @@ module Redmine | |||
def cat(path, identifier=nil) | |||
return nil | |||
end | |||
def with_leading_slash(path) | |||
path ||= '' | |||
(path[0,1]!="/") ? "/#{path}" : path | |||
@@ -237,7 +241,7 @@ module Redmine | |||
# Initialize with a Diff file and the type of Diff View | |||
# The type view must be inline or sbs (side_by_side) | |||
def initialize (type="inline") | |||
def initialize(type="inline") | |||
@parsing = false | |||
@nb_line = 1 | |||
@start = false | |||
@@ -312,7 +316,7 @@ module Redmine | |||
CGI.escapeHTML(line) | |||
end | |||
def parse_line (line, type="inline") | |||
def parse_line(line, type="inline") | |||
if line[0, 1] == "+" | |||
diff = sbs? type, 'add' | |||
@before = 'add' | |||
@@ -348,6 +352,28 @@ module Redmine | |||
end | |||
end | |||
end | |||
class Annotate | |||
attr_reader :lines, :revisions | |||
def initialize | |||
@lines = [] | |||
@revisions = [] | |||
end | |||
def add_line(line, revision) | |||
@lines << line | |||
@revisions << revision | |||
end | |||
def content | |||
content = lines.join("\n") | |||
end | |||
def empty? | |||
lines.empty? | |||
end | |||
end | |||
end | |||
end | |||
end |
@@ -268,7 +268,25 @@ module Redmine | |||
rescue Errno::ENOENT => e | |||
raise CommandFailed | |||
end | |||
def annotate(path, identifier=nil) | |||
identifier = (identifier) ? identifier : "HEAD" | |||
logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}" | |||
path_with_project="#{url}#{with_leading_slash(path)}" | |||
cmd = "#{CVS_BIN} -d #{root_url} rannotate -r#{identifier} #{path_with_project}" | |||
blame = Annotate.new | |||
shellout(cmd) do |io| | |||
io.each_line do |line| | |||
next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$} | |||
blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip)) | |||
end | |||
end | |||
return nil if $? && $?.exitstatus != 0 | |||
blame | |||
rescue Errno::ENOENT => e | |||
raise CommandFailed | |||
end | |||
private | |||
# convert a date/time into the CVS-format |
@@ -157,6 +157,25 @@ module Redmine | |||
rescue Errno::ENOENT => e | |||
raise CommandFailed | |||
end | |||
def annotate(path, identifier=nil) | |||
path ||= '' | |||
cmd = "#{HG_BIN} -R #{target('')}" | |||
cmd << " annotate -n -u" | |||
cmd << " -r #{identifier.to_i}" if identifier | |||
cmd << " #{target(path)}" | |||
blame = Annotate.new | |||
shellout(cmd) do |io| | |||
io.each_line do |line| | |||
next unless line =~ %r{^([^:]+)\s(\d+):(.*)$} | |||
blame.add_line($3.rstrip, Revision.new(:identifier => $2.to_i, :author => $1.strip)) | |||
end | |||
end | |||
return nil if $? && $?.exitstatus != 0 | |||
blame | |||
rescue Errno::ENOENT => e | |||
raise CommandFailed | |||
end | |||
end | |||
end | |||
end |
@@ -173,6 +173,23 @@ module Redmine | |||
raise CommandFailed | |||
end | |||
def annotate(path, identifier=nil) | |||
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" | |||
cmd = "#{SVN_BIN} blame #{target(path)}@#{identifier}" | |||
cmd << credentials_string | |||
blame = Annotate.new | |||
shellout(cmd) do |io| | |||
io.each_line do |line| | |||
next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$} | |||
blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip)) | |||
end | |||
end | |||
return nil if $? && $?.exitstatus != 0 | |||
blame | |||
rescue Errno::ENOENT => e | |||
raise CommandFailed | |||
end | |||
private | |||
def credentials_string |
@@ -2,20 +2,52 @@ | |||
table.filecontent { border: 1px solid #ccc; border-collapse: collapse; width:98%; } | |||
table.filecontent th { border: 1px solid #ccc; background-color: #eee; } | |||
table.filecontent th.filename { background-color: #ddc; text-align: left; } | |||
div.action_M { background: #fd8 } | |||
div.action_D { background: #f88 } | |||
div.action_A { background: #bfb } | |||
table.filecontent tr.spacing { border: 1px solid #d7d7d7; } | |||
table.filecontent .line-num { | |||
table.filecontent th.line-num { | |||
border: 1px solid #d7d7d7; | |||
font-size: 0.8em; | |||
text-align: right; | |||
width: 3em; | |||
width: 2%; | |||
padding-right: 3px; | |||
} | |||
/* 12 different colors for the annonate view */ | |||
table.annotate tr.bloc-0 {background: #FFFFBF;} | |||
table.annotate tr.bloc-1 {background: #EABFFF;} | |||
table.annotate tr.bloc-2 {background: #BFFFFF;} | |||
table.annotate tr.bloc-3 {background: #FFD9BF;} | |||
table.annotate tr.bloc-4 {background: #E6FFBF;} | |||
table.annotate tr.bloc-5 {background: #BFCFFF;} | |||
table.annotate tr.bloc-6 {background: #FFBFEF;} | |||
table.annotate tr.bloc-7 {background: #FFE6BF;} | |||
table.annotate tr.bloc-8 {background: #FFE680;} | |||
table.annotate tr.bloc-9 {background: #AA80FF;} | |||
table.annotate tr.bloc-10 {background: #FFBFDC;} | |||
table.annotate tr.bloc-11 {background: #BFE4FF;} | |||
table.annotate td.revision { | |||
text-align: center; | |||
width: 2%; | |||
padding-left: 1em; | |||
background: inherit; | |||
} | |||
table.annotate td.author { | |||
text-align: center; | |||
border-right: 1px solid #d7d7d7; | |||
white-space: nowrap; | |||
padding-left: 1em; | |||
padding-right: 1em; | |||
width: 3%; | |||
background: inherit; | |||
} | |||
table.annotate td.line-code { background-color: #fafafa; } | |||
div.action_M { background: #fd8 } | |||
div.action_D { background: #f88 } | |||
div.action_A { background: #bfb } | |||
/************* Coderay styles *************/ | |||
table.CodeRay { |