@@ -0,0 +1,26 | |||
|
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 %> |
@@ -95,6 +95,11 class RepositoriesController < ApplicationController | |||
|
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 |
@@ -33,6 +33,10 class Repository < ActiveRecord::Base | |||
|
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) |
@@ -2,14 +2,17 | |||
|
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 |
|
|
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 }%> |
@@ -2,11 +2,6 | |||
|
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| %> |
@@ -24,6 +24,7 ActionController::Routing::Routes.draw do |map| | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Ihr Konto wurde erstellt und wartet jetzt auf die Geneh | |||
|
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 |
@@ -489,6 +489,7 button_reset: Reset | |||
|
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 |
@@ -550,3 +550,4 label_registration_manual_activation: activaciΓ³n manual de cuenta | |||
|
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 |
@@ -489,6 +489,7 button_reset: RΓ©initialiser | |||
|
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Γ© |
@@ -547,3 +547,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -548,3 +548,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -548,3 +548,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Twoje konto zostaΕo utworzone i oczekuje na zatwierdze | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -547,3 +547,4 notice_account_pending: "ΠΠ°Ρ Π°ΠΊΠΊΠ°ΡΠ½Ρ ΡΠΆΠ΅ ΡΠΎΠ·Π΄Π°Π½ ΠΈ ΠΎΠΆΠΈΠ΄Π° | |||
|
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 |
@@ -548,3 +548,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -548,3 +548,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -550,3 +550,4 notice_account_pending: "Your account was created and is now pending administrat | |||
|
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 |
@@ -76,7 +76,7 Redmine::AccessControl.map do |map| | |||
|
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 |
@@ -38,6 +38,10 module Redmine | |||
|
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,7 +80,7 module Redmine | |||
|
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,7 +241,7 module Redmine | |||
|
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 |
|
|
244 | def initialize(type="inline") | |
|
241 | 245 | @parsing = false |
|
242 | 246 | @nb_line = 1 |
|
243 | 247 | @start = false |
@@ -312,7 +316,7 module Redmine | |||
|
312 | 316 | CGI.escapeHTML(line) |
|
313 | 317 | end |
|
314 | 318 | |
|
315 |
def parse_line |
|
|
319 | def parse_line(line, type="inline") | |
|
316 | 320 | if line[0, 1] == "+" |
|
317 | 321 | diff = sbs? type, 'add' |
|
318 | 322 | @before = 'add' |
@@ -348,6 +352,28 module Redmine | |||
|
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 |
@@ -268,7 +268,25 module Redmine | |||
|
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 |
@@ -157,6 +157,25 module Redmine | |||
|
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 |
@@ -173,6 +173,23 module Redmine | |||
|
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 |
@@ -2,20 +2,52 | |||
|
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: |
|
|
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 { |
General Comments 0
You need to be logged in to leave comments.
Login now