##// END OF EJS Templates
SCM browser:...
Jean-Philippe Lang -
r2738:a4d7a03b1428
parent child
Show More
@@ -1,319 +1,321
1 # redMine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'SVG/Graph/Bar'
18 require 'SVG/Graph/Bar'
19 require 'SVG/Graph/BarHorizontal'
19 require 'SVG/Graph/BarHorizontal'
20 require 'digest/sha1'
20 require 'digest/sha1'
21
21
22 class ChangesetNotFound < Exception; end
22 class ChangesetNotFound < Exception; end
23 class InvalidRevisionParam < Exception; end
23 class InvalidRevisionParam < Exception; end
24
24
25 class RepositoriesController < ApplicationController
25 class RepositoriesController < ApplicationController
26 menu_item :repository
26 menu_item :repository
27 before_filter :find_repository, :except => :edit
27 before_filter :find_repository, :except => :edit
28 before_filter :find_project, :only => :edit
28 before_filter :find_project, :only => :edit
29 before_filter :authorize
29 before_filter :authorize
30 accept_key_auth :revisions
30 accept_key_auth :revisions
31
31
32 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
32 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
33
33
34 def edit
34 def edit
35 @repository = @project.repository
35 @repository = @project.repository
36 if !@repository
36 if !@repository
37 @repository = Repository.factory(params[:repository_scm])
37 @repository = Repository.factory(params[:repository_scm])
38 @repository.project = @project if @repository
38 @repository.project = @project if @repository
39 end
39 end
40 if request.post? && @repository
40 if request.post? && @repository
41 @repository.attributes = params[:repository]
41 @repository.attributes = params[:repository]
42 @repository.save
42 @repository.save
43 end
43 end
44 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
44 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
45 end
45 end
46
46
47 def committers
47 def committers
48 @committers = @repository.committers
48 @committers = @repository.committers
49 @users = @project.users
49 @users = @project.users
50 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
50 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
51 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
51 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
52 @users.compact!
52 @users.compact!
53 @users.sort!
53 @users.sort!
54 if request.post? && params[:committers].is_a?(Hash)
54 if request.post? && params[:committers].is_a?(Hash)
55 # Build a hash with repository usernames as keys and corresponding user ids as values
55 # Build a hash with repository usernames as keys and corresponding user ids as values
56 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
56 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
57 flash[:notice] = l(:notice_successful_update)
57 flash[:notice] = l(:notice_successful_update)
58 redirect_to :action => 'committers', :id => @project
58 redirect_to :action => 'committers', :id => @project
59 end
59 end
60 end
60 end
61
61
62 def destroy
62 def destroy
63 @repository.destroy
63 @repository.destroy
64 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
64 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
65 end
65 end
66
66
67 def show
67 def show
68 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
68 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
69
69
70 @entries = @repository.entries(@path, @rev)
70 @entries = @repository.entries(@path, @rev)
71 if request.xhr?
71 if request.xhr?
72 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
72 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
73 else
73 else
74 show_error_not_found and return unless @entries
74 show_error_not_found and return unless @entries
75 @changesets = @repository.latest_changesets(@path, @rev)
75 if @path.blank?
76 @changesets = @repository.latest_changesets(@path, @rev)
77 end
76 @properties = @repository.properties(@path, @rev)
78 @properties = @repository.properties(@path, @rev)
77 render :action => 'show'
79 render :action => 'show'
78 end
80 end
79 end
81 end
80
82
81 alias_method :browse, :show
83 alias_method :browse, :show
82
84
83 def changes
85 def changes
84 @entry = @repository.entry(@path, @rev)
86 @entry = @repository.entry(@path, @rev)
85 show_error_not_found and return unless @entry
87 show_error_not_found and return unless @entry
86 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
88 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
87 @properties = @repository.properties(@path, @rev)
89 @properties = @repository.properties(@path, @rev)
88 end
90 end
89
91
90 def revisions
92 def revisions
91 @changeset_count = @repository.changesets.count
93 @changeset_count = @repository.changesets.count
92 @changeset_pages = Paginator.new self, @changeset_count,
94 @changeset_pages = Paginator.new self, @changeset_count,
93 per_page_option,
95 per_page_option,
94 params['page']
96 params['page']
95 @changesets = @repository.changesets.find(:all,
97 @changesets = @repository.changesets.find(:all,
96 :limit => @changeset_pages.items_per_page,
98 :limit => @changeset_pages.items_per_page,
97 :offset => @changeset_pages.current.offset,
99 :offset => @changeset_pages.current.offset,
98 :include => [:user, :repository])
100 :include => [:user, :repository])
99
101
100 respond_to do |format|
102 respond_to do |format|
101 format.html { render :layout => false if request.xhr? }
103 format.html { render :layout => false if request.xhr? }
102 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
104 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
103 end
105 end
104 end
106 end
105
107
106 def entry
108 def entry
107 @entry = @repository.entry(@path, @rev)
109 @entry = @repository.entry(@path, @rev)
108 show_error_not_found and return unless @entry
110 show_error_not_found and return unless @entry
109
111
110 # If the entry is a dir, show the browser
112 # If the entry is a dir, show the browser
111 browse and return if @entry.is_dir?
113 show and return if @entry.is_dir?
112
114
113 @content = @repository.cat(@path, @rev)
115 @content = @repository.cat(@path, @rev)
114 show_error_not_found and return unless @content
116 show_error_not_found and return unless @content
115 if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
117 if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
116 # Force the download
118 # Force the download
117 send_data @content, :filename => @path.split('/').last
119 send_data @content, :filename => @path.split('/').last
118 else
120 else
119 # Prevent empty lines when displaying a file with Windows style eol
121 # Prevent empty lines when displaying a file with Windows style eol
120 @content.gsub!("\r\n", "\n")
122 @content.gsub!("\r\n", "\n")
121 end
123 end
122 end
124 end
123
125
124 def annotate
126 def annotate
125 @entry = @repository.entry(@path, @rev)
127 @entry = @repository.entry(@path, @rev)
126 show_error_not_found and return unless @entry
128 show_error_not_found and return unless @entry
127
129
128 @annotate = @repository.scm.annotate(@path, @rev)
130 @annotate = @repository.scm.annotate(@path, @rev)
129 render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
131 render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
130 end
132 end
131
133
132 def revision
134 def revision
133 @changeset = @repository.changesets.find(:first, :conditions => ["revision LIKE ?", @rev + '%'])
135 @changeset = @repository.changesets.find(:first, :conditions => ["revision LIKE ?", @rev + '%'])
134 raise ChangesetNotFound unless @changeset
136 raise ChangesetNotFound unless @changeset
135
137
136 respond_to do |format|
138 respond_to do |format|
137 format.html
139 format.html
138 format.js {render :layout => false}
140 format.js {render :layout => false}
139 end
141 end
140 rescue ChangesetNotFound
142 rescue ChangesetNotFound
141 show_error_not_found
143 show_error_not_found
142 end
144 end
143
145
144 def diff
146 def diff
145 if params[:format] == 'diff'
147 if params[:format] == 'diff'
146 @diff = @repository.diff(@path, @rev, @rev_to)
148 @diff = @repository.diff(@path, @rev, @rev_to)
147 show_error_not_found and return unless @diff
149 show_error_not_found and return unless @diff
148 filename = "changeset_r#{@rev}"
150 filename = "changeset_r#{@rev}"
149 filename << "_r#{@rev_to}" if @rev_to
151 filename << "_r#{@rev_to}" if @rev_to
150 send_data @diff.join, :filename => "#{filename}.diff",
152 send_data @diff.join, :filename => "#{filename}.diff",
151 :type => 'text/x-patch',
153 :type => 'text/x-patch',
152 :disposition => 'attachment'
154 :disposition => 'attachment'
153 else
155 else
154 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
156 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
155 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
157 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
156
158
157 # Save diff type as user preference
159 # Save diff type as user preference
158 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
160 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
159 User.current.pref[:diff_type] = @diff_type
161 User.current.pref[:diff_type] = @diff_type
160 User.current.preference.save
162 User.current.preference.save
161 end
163 end
162
164
163 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
165 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
164 unless read_fragment(@cache_key)
166 unless read_fragment(@cache_key)
165 @diff = @repository.diff(@path, @rev, @rev_to)
167 @diff = @repository.diff(@path, @rev, @rev_to)
166 show_error_not_found unless @diff
168 show_error_not_found unless @diff
167 end
169 end
168 end
170 end
169 end
171 end
170
172
171 def stats
173 def stats
172 end
174 end
173
175
174 def graph
176 def graph
175 data = nil
177 data = nil
176 case params[:graph]
178 case params[:graph]
177 when "commits_per_month"
179 when "commits_per_month"
178 data = graph_commits_per_month(@repository)
180 data = graph_commits_per_month(@repository)
179 when "commits_per_author"
181 when "commits_per_author"
180 data = graph_commits_per_author(@repository)
182 data = graph_commits_per_author(@repository)
181 end
183 end
182 if data
184 if data
183 headers["Content-Type"] = "image/svg+xml"
185 headers["Content-Type"] = "image/svg+xml"
184 send_data(data, :type => "image/svg+xml", :disposition => "inline")
186 send_data(data, :type => "image/svg+xml", :disposition => "inline")
185 else
187 else
186 render_404
188 render_404
187 end
189 end
188 end
190 end
189
191
190 private
192 private
191 def find_project
193 def find_project
192 @project = Project.find(params[:id])
194 @project = Project.find(params[:id])
193 rescue ActiveRecord::RecordNotFound
195 rescue ActiveRecord::RecordNotFound
194 render_404
196 render_404
195 end
197 end
196
198
197 def find_repository
199 def find_repository
198 @project = Project.find(params[:id])
200 @project = Project.find(params[:id])
199 @repository = @project.repository
201 @repository = @project.repository
200 render_404 and return false unless @repository
202 render_404 and return false unless @repository
201 @path = params[:path].join('/') unless params[:path].nil?
203 @path = params[:path].join('/') unless params[:path].nil?
202 @path ||= ''
204 @path ||= ''
203 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
205 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
204 @rev_to = params[:rev_to]
206 @rev_to = params[:rev_to]
205 rescue ActiveRecord::RecordNotFound
207 rescue ActiveRecord::RecordNotFound
206 render_404
208 render_404
207 rescue InvalidRevisionParam
209 rescue InvalidRevisionParam
208 show_error_not_found
210 show_error_not_found
209 end
211 end
210
212
211 def show_error_not_found
213 def show_error_not_found
212 render_error l(:error_scm_not_found)
214 render_error l(:error_scm_not_found)
213 end
215 end
214
216
215 # Handler for Redmine::Scm::Adapters::CommandFailed exception
217 # Handler for Redmine::Scm::Adapters::CommandFailed exception
216 def show_error_command_failed(exception)
218 def show_error_command_failed(exception)
217 render_error l(:error_scm_command_failed, exception.message)
219 render_error l(:error_scm_command_failed, exception.message)
218 end
220 end
219
221
220 def graph_commits_per_month(repository)
222 def graph_commits_per_month(repository)
221 @date_to = Date.today
223 @date_to = Date.today
222 @date_from = @date_to << 11
224 @date_from = @date_to << 11
223 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
225 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
224 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
226 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
225 commits_by_month = [0] * 12
227 commits_by_month = [0] * 12
226 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
228 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
227
229
228 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
230 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
229 changes_by_month = [0] * 12
231 changes_by_month = [0] * 12
230 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
232 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
231
233
232 fields = []
234 fields = []
233 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
235 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
234
236
235 graph = SVG::Graph::Bar.new(
237 graph = SVG::Graph::Bar.new(
236 :height => 300,
238 :height => 300,
237 :width => 800,
239 :width => 800,
238 :fields => fields.reverse,
240 :fields => fields.reverse,
239 :stack => :side,
241 :stack => :side,
240 :scale_integers => true,
242 :scale_integers => true,
241 :step_x_labels => 2,
243 :step_x_labels => 2,
242 :show_data_values => false,
244 :show_data_values => false,
243 :graph_title => l(:label_commits_per_month),
245 :graph_title => l(:label_commits_per_month),
244 :show_graph_title => true
246 :show_graph_title => true
245 )
247 )
246
248
247 graph.add_data(
249 graph.add_data(
248 :data => commits_by_month[0..11].reverse,
250 :data => commits_by_month[0..11].reverse,
249 :title => l(:label_revision_plural)
251 :title => l(:label_revision_plural)
250 )
252 )
251
253
252 graph.add_data(
254 graph.add_data(
253 :data => changes_by_month[0..11].reverse,
255 :data => changes_by_month[0..11].reverse,
254 :title => l(:label_change_plural)
256 :title => l(:label_change_plural)
255 )
257 )
256
258
257 graph.burn
259 graph.burn
258 end
260 end
259
261
260 def graph_commits_per_author(repository)
262 def graph_commits_per_author(repository)
261 commits_by_author = repository.changesets.count(:all, :group => :committer)
263 commits_by_author = repository.changesets.count(:all, :group => :committer)
262 commits_by_author.sort! {|x, y| x.last <=> y.last}
264 commits_by_author.sort! {|x, y| x.last <=> y.last}
263
265
264 changes_by_author = repository.changes.count(:all, :group => :committer)
266 changes_by_author = repository.changes.count(:all, :group => :committer)
265 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
267 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
266
268
267 fields = commits_by_author.collect {|r| r.first}
269 fields = commits_by_author.collect {|r| r.first}
268 commits_data = commits_by_author.collect {|r| r.last}
270 commits_data = commits_by_author.collect {|r| r.last}
269 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
271 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
270
272
271 fields = fields + [""]*(10 - fields.length) if fields.length<10
273 fields = fields + [""]*(10 - fields.length) if fields.length<10
272 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
274 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
273 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
275 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
274
276
275 # Remove email adress in usernames
277 # Remove email adress in usernames
276 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
278 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
277
279
278 graph = SVG::Graph::BarHorizontal.new(
280 graph = SVG::Graph::BarHorizontal.new(
279 :height => 400,
281 :height => 400,
280 :width => 800,
282 :width => 800,
281 :fields => fields,
283 :fields => fields,
282 :stack => :side,
284 :stack => :side,
283 :scale_integers => true,
285 :scale_integers => true,
284 :show_data_values => false,
286 :show_data_values => false,
285 :rotate_y_labels => false,
287 :rotate_y_labels => false,
286 :graph_title => l(:label_commits_per_author),
288 :graph_title => l(:label_commits_per_author),
287 :show_graph_title => true
289 :show_graph_title => true
288 )
290 )
289
291
290 graph.add_data(
292 graph.add_data(
291 :data => commits_data,
293 :data => commits_data,
292 :title => l(:label_revision_plural)
294 :title => l(:label_revision_plural)
293 )
295 )
294
296
295 graph.add_data(
297 graph.add_data(
296 :data => changes_data,
298 :data => changes_data,
297 :title => l(:label_change_plural)
299 :title => l(:label_change_plural)
298 )
300 )
299
301
300 graph.burn
302 graph.burn
301 end
303 end
302
304
303 end
305 end
304
306
305 class Date
307 class Date
306 def months_ago(date = Date.today)
308 def months_ago(date = Date.today)
307 (date.year - self.year)*12 + (date.month - self.month)
309 (date.year - self.year)*12 + (date.month - self.month)
308 end
310 end
309
311
310 def weeks_ago(date = Date.today)
312 def weeks_ago(date = Date.today)
311 (date.year - self.year)*52 + (date.cweek - self.cweek)
313 (date.year - self.year)*52 + (date.cweek - self.cweek)
312 end
314 end
313 end
315 end
314
316
315 class String
317 class String
316 def with_leading_slash
318 def with_leading_slash
317 starts_with?('/') ? self : "/#{self}"
319 starts_with?('/') ? self : "/#{self}"
318 end
320 end
319 end
321 end
@@ -1,30 +1,32
1 <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
1 <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
2
2
3 <div class="contextual">
3 <div class="contextual">
4 <%= render :partial => 'navigation' %>
4 <%= render :partial => 'navigation' %>
5 </div>
5 </div>
6
6
7 <h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
7 <h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
8
8
9 <% if !@entries.nil? && authorize_for('repositories', 'browse') %>
9 <% if !@entries.nil? && authorize_for('repositories', 'browse') %>
10 <%= render :partial => 'dir_list' %>
10 <%= render :partial => 'dir_list' %>
11 <% end %>
11 <% end %>
12
12
13 <% if !@changesets.empty? && authorize_for('repositories', 'revisions') %>
13 <%= render_properties(@properties) %>
14
15 <% if @changesets && !@changesets.empty? && authorize_for('repositories', 'revisions') %>
14 <h3><%= l(:label_latest_revision_plural) %></h3>
16 <h3><%= l(:label_latest_revision_plural) %></h3>
15 <%= render :partial => 'revisions', :locals => {:project => @project, :path => '', :revisions => @changesets, :entry => nil }%>
17 <%= render :partial => 'revisions', :locals => {:project => @project, :path => '', :revisions => @changesets, :entry => nil }%>
16 <p><%= link_to l(:label_view_all_revisions), :action => 'revisions', :id => @project %></p>
18 <p><%= link_to l(:label_view_all_revisions), :action => 'revisions', :id => @project %></p>
17 <% content_for :header_tags do %>
19 <% content_for :header_tags do %>
18 <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :action => 'revisions', :id => @project, :page => nil, :key => User.current.rss_key})) %>
20 <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :action => 'revisions', :id => @project, :page => nil, :key => User.current.rss_key})) %>
19 <% end %>
21 <% end %>
20
22
21 <% other_formats_links do |f| %>
23 <% other_formats_links do |f| %>
22 <%= f.link_to 'Atom', :url => {:action => 'revisions', :id => @project, :key => User.current.rss_key} %>
24 <%= f.link_to 'Atom', :url => {:action => 'revisions', :id => @project, :key => User.current.rss_key} %>
23 <% end %>
25 <% end %>
24 <% end %>
26 <% end %>
25
27
26 <% content_for :header_tags do %>
28 <% content_for :header_tags do %>
27 <%= stylesheet_link_tag "scm" %>
29 <%= stylesheet_link_tag "scm" %>
28 <% end %>
30 <% end %>
29
31
30 <% html_title(l(:label_repository)) -%>
32 <% html_title(l(:label_repository)) -%>
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now