Revision 947
Added by Jean-Philippe Lang over 17 years ago
trunk/app/controllers/repositories_controller.rb | ||
---|---|---|
95 | 95 |
end |
96 | 96 |
end |
97 | 97 |
|
98 |
def annotate |
|
99 |
@annotate = @repository.scm.annotate(@path, @rev) |
|
100 |
show_error and return if @annotate.nil? || @annotate.empty? |
|
101 |
end |
|
102 |
|
|
98 | 103 |
def revision |
99 | 104 |
@changeset = @repository.changesets.find_by_revision(@rev) |
100 | 105 |
raise ChangesetNotFound unless @changeset |
trunk/app/models/repository.rb | ||
---|---|---|
33 | 33 |
def supports_cat? |
34 | 34 |
scm.supports_cat? |
35 | 35 |
end |
36 |
|
|
37 |
def supports_annotate? |
|
38 |
scm.supports_annotate? |
|
39 |
end |
|
36 | 40 |
|
37 | 41 |
def entries(path=nil, identifier=nil) |
38 | 42 |
scm.entries(path, identifier) |
trunk/app/views/repositories/annotate.rhtml | ||
---|---|---|
1 |
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2> |
|
2 |
|
|
3 |
<% colors = Hash.new {|k,v| k[v] = (k.size % 12) } %> |
|
4 |
|
|
5 |
<div class="autoscroll"> |
|
6 |
<table class="filecontent annotate CodeRay"> |
|
7 |
<tbody> |
|
8 |
<% line_num = 1 %> |
|
9 |
<% syntax_highlight(@path, to_utf8(@annotate.content)).each_line do |line| %> |
|
10 |
<% revision = @annotate.revisions[line_num-1] %> |
|
11 |
<tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>"> |
|
12 |
<th class="line-num"><%= line_num %></th> |
|
13 |
<td class="revision"> |
|
14 |
<%= (revision.identifier ? link_to(revision.identifier, :action => 'revision', :id => @project, :rev => revision.identifier) : revision.revision) if revision %></td> |
|
15 |
<td class="author"><%= h(revision.author) if revision %></td> |
|
16 |
<td class="line-code"><pre><%= line %></pre></td> |
|
17 |
</tr> |
|
18 |
<% line_num += 1 %> |
|
19 |
<% end %> |
|
20 |
<tbody> |
|
21 |
</table> |
|
22 |
</div> |
|
23 |
|
|
24 |
<% content_for :header_tags do %> |
|
25 |
<%= stylesheet_link_tag 'scm' %> |
|
26 |
<% end %> |
|
0 | 27 |
trunk/app/views/repositories/changes.rhtml | ||
---|---|---|
2 | 2 |
|
3 | 3 |
<h3><%=h @entry.name %></h3> |
4 | 4 |
|
5 |
<% if @repository.supports_cat? %> |
|
6 | 5 |
<p> |
7 | 6 |
<% if @entry.is_text? %> |
8 |
<%= link_to l(:button_view), {:action => 'entry', :id => @project, :path => @path, :rev => @rev } %> | |
|
7 |
<% if @repository.supports_cat? %> |
|
8 |
<%= link_to l(:button_view), {:action => 'entry', :id => @project, :path => @path, :rev => @rev } %> | |
|
9 |
<% end %> |
|
10 |
<% if @repository.supports_annotate? %> |
|
11 |
<%= link_to l(:button_annotate), {:action => 'annotate', :id => @project, :path => @path, :rev => @rev } %> | |
|
12 |
<% end %> |
|
9 | 13 |
<% end %> |
10 |
<%= link_to l(:button_download), {:action => 'entry', :id => @project, :path => @path, :rev => @rev, :format => 'raw' } %>
|
|
14 |
<%= link_to(l(:button_download), {:action => 'entry', :id => @project, :path => @path, :rev => @rev, :format => 'raw' }) if @repository.supports_cat? %>
|
|
11 | 15 |
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %> |
12 | 16 |
</p> |
13 |
<% end %> |
|
14 | 17 |
|
15 | 18 |
<%= render :partial => 'revisions', :locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }%> |
trunk/app/views/repositories/entry.rhtml | ||
---|---|---|
2 | 2 |
|
3 | 3 |
<div class="autoscroll"> |
4 | 4 |
<table class="filecontent CodeRay"> |
5 |
<thead> |
|
6 |
<tr> |
|
7 |
<th colspan="2" class="filename"><%= @path %></th> |
|
8 |
</tr> |
|
9 |
</thead> |
|
10 | 5 |
<tbody> |
11 | 6 |
<% line_num = 1 %> |
12 | 7 |
<% syntax_highlight(@path, to_utf8(@content)).each_line do |line| %> |
trunk/config/routes.rb | ||
---|---|---|
24 | 24 |
omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes' |
25 | 25 |
omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff' |
26 | 26 |
omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry' |
27 |
omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate' |
|
27 | 28 |
end |
28 | 29 |
|
29 | 30 |
# Allow downloading Web Service WSDL as a file with an extension |
trunk/lang/bg.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Time zone |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/cs.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Time zone |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/de.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Zeitzone |
548 | 548 |
text_caracters_minimum: Muss mindestens %d Zeichen lang sein. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/en.yml | ||
---|---|---|
489 | 489 |
button_rename: Rename |
490 | 490 |
button_change_password: Change password |
491 | 491 |
button_copy: Copy |
492 |
button_annotate: Annotate |
|
492 | 493 |
|
493 | 494 |
status_active: active |
494 | 495 |
status_registered: registered |
trunk/lang/es.yml | ||
---|---|---|
550 | 550 |
notice_account_pending: "Su cuenta ha sido creada y está pendiende de la aprobación por parte de administrador" |
551 | 551 |
setting_time_format: Formato de hora |
552 | 552 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
553 |
button_annotate: Annotate |
trunk/lang/fr.yml | ||
---|---|---|
489 | 489 |
button_rename: Renommer |
490 | 490 |
button_change_password: Changer de mot de passe |
491 | 491 |
button_copy: Copier |
492 |
button_annotate: Annoter |
|
492 | 493 |
|
493 | 494 |
status_active: actif |
494 | 495 |
status_registered: enregistré |
trunk/lang/he.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Time zone |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/it.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Time zone |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/ja.yml | ||
---|---|---|
548 | 548 |
field_time_zone: Time zone |
549 | 549 |
text_caracters_minimum: Must be at least %d characters long. |
550 | 550 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
551 |
button_annotate: Annotate |
trunk/lang/ko.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Time zone |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/nl.yml | ||
---|---|---|
548 | 548 |
field_time_zone: Time zone |
549 | 549 |
text_caracters_minimum: Must be at least %d characters long. |
550 | 550 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
551 |
button_annotate: Annotate |
trunk/lang/pl.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Strefa czasowa |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/pt-br.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Time zone |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/pt.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Time zone |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/ro.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Time zone |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/ru.yml | ||
---|---|---|
547 | 547 |
field_time_zone: Часовой пояс |
548 | 548 |
text_caracters_minimum: Must be at least %d characters long. |
549 | 549 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
550 |
button_annotate: Annotate |
trunk/lang/sr.yml | ||
---|---|---|
548 | 548 |
field_time_zone: Time zone |
549 | 549 |
text_caracters_minimum: Must be at least %d characters long. |
550 | 550 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
551 |
button_annotate: Annotate |
trunk/lang/sv.yml | ||
---|---|---|
548 | 548 |
field_time_zone: Time zone |
549 | 549 |
text_caracters_minimum: Must be at least %d characters long. |
550 | 550 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
551 |
button_annotate: Annotate |
trunk/lang/zh.yml | ||
---|---|---|
550 | 550 |
field_time_zone: Time zone |
551 | 551 |
text_caracters_minimum: Must be at least %d characters long. |
552 | 552 |
setting_bcc_recipients: Blind carbon copy recipients (bcc) |
553 |
button_annotate: Annotate |
trunk/lib/redmine/scm/adapters/abstract_adapter.rb | ||
---|---|---|
38 | 38 |
def supports_cat? |
39 | 39 |
true |
40 | 40 |
end |
41 |
|
|
42 |
def supports_annotate? |
|
43 |
respond_to?('annotate') |
|
44 |
end |
|
41 | 45 |
|
42 | 46 |
def root_url |
43 | 47 |
@root_url |
... | ... | |
76 | 80 |
def cat(path, identifier=nil) |
77 | 81 |
return nil |
78 | 82 |
end |
79 |
|
|
83 |
|
|
80 | 84 |
def with_leading_slash(path) |
81 | 85 |
path ||= '' |
82 | 86 |
(path[0,1]!="/") ? "/#{path}" : path |
... | ... | |
237 | 241 |
|
238 | 242 |
# Initialize with a Diff file and the type of Diff View |
239 | 243 |
# The type view must be inline or sbs (side_by_side) |
240 |
def initialize (type="inline")
|
|
244 |
def initialize(type="inline") |
|
241 | 245 |
@parsing = false |
242 | 246 |
@nb_line = 1 |
243 | 247 |
@start = false |
... | ... | |
312 | 316 |
CGI.escapeHTML(line) |
313 | 317 |
end |
314 | 318 |
|
315 |
def parse_line (line, type="inline")
|
|
319 |
def parse_line(line, type="inline") |
|
316 | 320 |
if line[0, 1] == "+" |
317 | 321 |
diff = sbs? type, 'add' |
318 | 322 |
@before = 'add' |
... | ... | |
348 | 352 |
end |
349 | 353 |
end |
350 | 354 |
end |
355 |
|
|
356 |
class Annotate |
|
357 |
attr_reader :lines, :revisions |
|
358 |
|
|
359 |
def initialize |
|
360 |
@lines = [] |
|
361 |
@revisions = [] |
|
362 |
end |
|
363 |
|
|
364 |
def add_line(line, revision) |
|
365 |
@lines << line |
|
366 |
@revisions << revision |
|
367 |
end |
|
368 |
|
|
369 |
def content |
|
370 |
content = lines.join("\n") |
|
371 |
end |
|
372 |
|
|
373 |
def empty? |
|
374 |
lines.empty? |
|
375 |
end |
|
376 |
end |
|
351 | 377 |
end |
352 | 378 |
end |
353 | 379 |
end |
trunk/lib/redmine/scm/adapters/cvs_adapter.rb | ||
---|---|---|
268 | 268 |
rescue Errno::ENOENT => e |
269 | 269 |
raise CommandFailed |
270 | 270 |
end |
271 |
|
|
271 |
|
|
272 |
def annotate(path, identifier=nil) |
|
273 |
identifier = (identifier) ? identifier : "HEAD" |
|
274 |
logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}" |
|
275 |
path_with_project="#{url}#{with_leading_slash(path)}" |
|
276 |
cmd = "#{CVS_BIN} -d #{root_url} rannotate -r#{identifier} #{path_with_project}" |
|
277 |
blame = Annotate.new |
|
278 |
shellout(cmd) do |io| |
|
279 |
io.each_line do |line| |
|
280 |
next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$} |
|
281 |
blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip)) |
|
282 |
end |
|
283 |
end |
|
284 |
return nil if $? && $?.exitstatus != 0 |
|
285 |
blame |
|
286 |
rescue Errno::ENOENT => e |
|
287 |
raise CommandFailed |
|
288 |
end |
|
289 |
|
|
272 | 290 |
private |
273 | 291 |
|
274 | 292 |
# convert a date/time into the CVS-format |
trunk/lib/redmine/scm/adapters/mercurial_adapter.rb | ||
---|---|---|
157 | 157 |
rescue Errno::ENOENT => e |
158 | 158 |
raise CommandFailed |
159 | 159 |
end |
160 |
|
|
161 |
def annotate(path, identifier=nil) |
|
162 |
path ||= '' |
|
163 |
cmd = "#{HG_BIN} -R #{target('')}" |
|
164 |
cmd << " annotate -n -u" |
|
165 |
cmd << " -r #{identifier.to_i}" if identifier |
|
166 |
cmd << " #{target(path)}" |
|
167 |
blame = Annotate.new |
|
168 |
shellout(cmd) do |io| |
|
169 |
io.each_line do |line| |
|
170 |
next unless line =~ %r{^([^:]+)\s(\d+):(.*)$} |
|
171 |
blame.add_line($3.rstrip, Revision.new(:identifier => $2.to_i, :author => $1.strip)) |
|
172 |
end |
|
173 |
end |
|
174 |
return nil if $? && $?.exitstatus != 0 |
|
175 |
blame |
|
176 |
rescue Errno::ENOENT => e |
|
177 |
raise CommandFailed |
|
178 |
end |
|
160 | 179 |
end |
161 | 180 |
end |
162 | 181 |
end |
trunk/lib/redmine/scm/adapters/subversion_adapter.rb | ||
---|---|---|
173 | 173 |
raise CommandFailed |
174 | 174 |
end |
175 | 175 |
|
176 |
def annotate(path, identifier=nil) |
|
177 |
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" |
|
178 |
cmd = "#{SVN_BIN} blame #{target(path)}@#{identifier}" |
|
179 |
cmd << credentials_string |
|
180 |
blame = Annotate.new |
|
181 |
shellout(cmd) do |io| |
|
182 |
io.each_line do |line| |
|
183 |
next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$} |
|
184 |
blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip)) |
|
185 |
end |
|
186 |
end |
|
187 |
return nil if $? && $?.exitstatus != 0 |
|
188 |
blame |
|
189 |
rescue Errno::ENOENT => e |
|
190 |
raise CommandFailed |
|
191 |
end |
|
192 |
|
|
176 | 193 |
private |
177 | 194 |
|
178 | 195 |
def credentials_string |
trunk/lib/redmine.rb | ||
---|---|---|
76 | 76 |
|
77 | 77 |
map.project_module :repository do |map| |
78 | 78 |
map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member |
79 |
map.permission :browse_repository, :repositories => [:show, :browse, :entry, :changes, :diff, :stats, :graph] |
|
79 |
map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
|
|
80 | 80 |
map.permission :view_changesets, :repositories => [:show, :revisions, :revision] |
81 | 81 |
end |
82 | 82 |
|
trunk/public/stylesheets/scm.css | ||
---|---|---|
2 | 2 |
table.filecontent { border: 1px solid #ccc; border-collapse: collapse; width:98%; } |
3 | 3 |
table.filecontent th { border: 1px solid #ccc; background-color: #eee; } |
4 | 4 |
table.filecontent th.filename { background-color: #ddc; text-align: left; } |
5 |
div.action_M { background: #fd8 } |
|
6 |
div.action_D { background: #f88 } |
|
7 |
div.action_A { background: #bfb } |
|
8 |
|
|
9 | 5 |
table.filecontent tr.spacing { border: 1px solid #d7d7d7; } |
10 |
|
|
11 |
table.filecontent .line-num { |
|
6 |
table.filecontent th.line-num { |
|
12 | 7 |
border: 1px solid #d7d7d7; |
13 | 8 |
font-size: 0.8em; |
14 | 9 |
text-align: right; |
15 |
width: 3em;
|
|
10 |
width: 2%;
|
|
16 | 11 |
padding-right: 3px; |
17 | 12 |
} |
18 | 13 |
|
14 |
/* 12 different colors for the annonate view */ |
|
15 |
table.annotate tr.bloc-0 {background: #FFFFBF;} |
|
16 |
table.annotate tr.bloc-1 {background: #EABFFF;} |
|
17 |
table.annotate tr.bloc-2 {background: #BFFFFF;} |
|
18 |
table.annotate tr.bloc-3 {background: #FFD9BF;} |
|
19 |
table.annotate tr.bloc-4 {background: #E6FFBF;} |
|
20 |
table.annotate tr.bloc-5 {background: #BFCFFF;} |
|
21 |
table.annotate tr.bloc-6 {background: #FFBFEF;} |
|
22 |
table.annotate tr.bloc-7 {background: #FFE6BF;} |
|
23 |
table.annotate tr.bloc-8 {background: #FFE680;} |
|
24 |
table.annotate tr.bloc-9 {background: #AA80FF;} |
|
25 |
table.annotate tr.bloc-10 {background: #FFBFDC;} |
|
26 |
table.annotate tr.bloc-11 {background: #BFE4FF;} |
|
27 |
|
|
28 |
table.annotate td.revision { |
|
29 |
text-align: center; |
|
30 |
width: 2%; |
|
31 |
padding-left: 1em; |
|
32 |
background: inherit; |
|
33 |
} |
|
34 |
|
|
35 |
table.annotate td.author { |
|
36 |
text-align: center; |
|
37 |
border-right: 1px solid #d7d7d7; |
|
38 |
white-space: nowrap; |
|
39 |
padding-left: 1em; |
|
40 |
padding-right: 1em; |
|
41 |
width: 3%; |
|
42 |
background: inherit; |
|
43 |
} |
|
44 |
|
|
45 |
table.annotate td.line-code { background-color: #fafafa; } |
|
46 |
|
|
47 |
div.action_M { background: #fd8 } |
|
48 |
div.action_D { background: #f88 } |
|
49 |
div.action_A { background: #bfb } |
|
50 |
|
|
19 | 51 |
/************* Coderay styles *************/ |
20 | 52 |
|
21 | 53 |
table.CodeRay { |
Also available in: Unified diff
Added Annotate/Blame view for Subversion, CVS and Mercurial repositories.