##// END OF EJS Templates
SCM browser: ability to download raw unified diffs....
Jean-Philippe Lang -
r1500:b78b62df8d1f
parent child
Show More
@@ -1,314 +1,324
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 layout 'base'
26 layout 'base'
27 menu_item :repository
27 menu_item :repository
28 before_filter :find_repository, :except => :edit
28 before_filter :find_repository, :except => :edit
29 before_filter :find_project, :only => :edit
29 before_filter :find_project, :only => :edit
30 before_filter :authorize
30 before_filter :authorize
31 accept_key_auth :revisions
31 accept_key_auth :revisions
32
32
33 def edit
33 def edit
34 @repository = @project.repository
34 @repository = @project.repository
35 if !@repository
35 if !@repository
36 @repository = Repository.factory(params[:repository_scm])
36 @repository = Repository.factory(params[:repository_scm])
37 @repository.project = @project if @repository
37 @repository.project = @project if @repository
38 end
38 end
39 if request.post? && @repository
39 if request.post? && @repository
40 @repository.attributes = params[:repository]
40 @repository.attributes = params[:repository]
41 @repository.save
41 @repository.save
42 end
42 end
43 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
43 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
44 end
44 end
45
45
46 def destroy
46 def destroy
47 @repository.destroy
47 @repository.destroy
48 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
48 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
49 end
49 end
50
50
51 def show
51 def show
52 # check if new revisions have been committed in the repository
52 # check if new revisions have been committed in the repository
53 @repository.fetch_changesets if Setting.autofetch_changesets?
53 @repository.fetch_changesets if Setting.autofetch_changesets?
54 # root entries
54 # root entries
55 @entries = @repository.entries('', @rev)
55 @entries = @repository.entries('', @rev)
56 # latest changesets
56 # latest changesets
57 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
57 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
58 show_error_not_found unless @entries || @changesets.any?
58 show_error_not_found unless @entries || @changesets.any?
59 rescue Redmine::Scm::Adapters::CommandFailed => e
59 rescue Redmine::Scm::Adapters::CommandFailed => e
60 show_error_command_failed(e.message)
60 show_error_command_failed(e.message)
61 end
61 end
62
62
63 def browse
63 def browse
64 @entries = @repository.entries(@path, @rev)
64 @entries = @repository.entries(@path, @rev)
65 if request.xhr?
65 if request.xhr?
66 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
66 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
67 else
67 else
68 show_error_not_found and return unless @entries
68 show_error_not_found and return unless @entries
69 render :action => 'browse'
69 render :action => 'browse'
70 end
70 end
71 rescue Redmine::Scm::Adapters::CommandFailed => e
71 rescue Redmine::Scm::Adapters::CommandFailed => e
72 show_error_command_failed(e.message)
72 show_error_command_failed(e.message)
73 end
73 end
74
74
75 def changes
75 def changes
76 @entry = @repository.scm.entry(@path, @rev)
76 @entry = @repository.scm.entry(@path, @rev)
77 show_error_not_found and return unless @entry
77 show_error_not_found and return unless @entry
78 @changesets = @repository.changesets_for_path(@path)
78 @changesets = @repository.changesets_for_path(@path)
79 rescue Redmine::Scm::Adapters::CommandFailed => e
79 rescue Redmine::Scm::Adapters::CommandFailed => e
80 show_error_command_failed(e.message)
80 show_error_command_failed(e.message)
81 end
81 end
82
82
83 def revisions
83 def revisions
84 @changeset_count = @repository.changesets.count
84 @changeset_count = @repository.changesets.count
85 @changeset_pages = Paginator.new self, @changeset_count,
85 @changeset_pages = Paginator.new self, @changeset_count,
86 per_page_option,
86 per_page_option,
87 params['page']
87 params['page']
88 @changesets = @repository.changesets.find(:all,
88 @changesets = @repository.changesets.find(:all,
89 :limit => @changeset_pages.items_per_page,
89 :limit => @changeset_pages.items_per_page,
90 :offset => @changeset_pages.current.offset)
90 :offset => @changeset_pages.current.offset)
91
91
92 respond_to do |format|
92 respond_to do |format|
93 format.html { render :layout => false if request.xhr? }
93 format.html { render :layout => false if request.xhr? }
94 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
94 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
95 end
95 end
96 end
96 end
97
97
98 def entry
98 def entry
99 @entry = @repository.scm.entry(@path, @rev)
99 @entry = @repository.scm.entry(@path, @rev)
100 show_error_not_found and return unless @entry
100 show_error_not_found and return unless @entry
101
101
102 # If the entry is a dir, show the browser
102 # If the entry is a dir, show the browser
103 browse and return if @entry.is_dir?
103 browse and return if @entry.is_dir?
104
104
105 @content = @repository.scm.cat(@path, @rev)
105 @content = @repository.scm.cat(@path, @rev)
106 show_error_not_found and return unless @content
106 show_error_not_found and return unless @content
107 if 'raw' == params[:format] || @content.is_binary_data?
107 if 'raw' == params[:format] || @content.is_binary_data?
108 # Force the download if it's a binary file
108 # Force the download if it's a binary file
109 send_data @content, :filename => @path.split('/').last
109 send_data @content, :filename => @path.split('/').last
110 else
110 else
111 # Prevent empty lines when displaying a file with Windows style eol
111 # Prevent empty lines when displaying a file with Windows style eol
112 @content.gsub!("\r\n", "\n")
112 @content.gsub!("\r\n", "\n")
113 end
113 end
114 rescue Redmine::Scm::Adapters::CommandFailed => e
114 rescue Redmine::Scm::Adapters::CommandFailed => e
115 show_error_command_failed(e.message)
115 show_error_command_failed(e.message)
116 end
116 end
117
117
118 def annotate
118 def annotate
119 @annotate = @repository.scm.annotate(@path, @rev)
119 @annotate = @repository.scm.annotate(@path, @rev)
120 render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
120 render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
121 rescue Redmine::Scm::Adapters::CommandFailed => e
121 rescue Redmine::Scm::Adapters::CommandFailed => e
122 show_error_command_failed(e.message)
122 show_error_command_failed(e.message)
123 end
123 end
124
124
125 def revision
125 def revision
126 @changeset = @repository.changesets.find_by_revision(@rev)
126 @changeset = @repository.changesets.find_by_revision(@rev)
127 raise ChangesetNotFound unless @changeset
127 raise ChangesetNotFound unless @changeset
128 @changes_count = @changeset.changes.size
128 @changes_count = @changeset.changes.size
129 @changes_pages = Paginator.new self, @changes_count, 150, params['page']
129 @changes_pages = Paginator.new self, @changes_count, 150, params['page']
130 @changes = @changeset.changes.find(:all,
130 @changes = @changeset.changes.find(:all,
131 :limit => @changes_pages.items_per_page,
131 :limit => @changes_pages.items_per_page,
132 :offset => @changes_pages.current.offset)
132 :offset => @changes_pages.current.offset)
133
133
134 respond_to do |format|
134 respond_to do |format|
135 format.html
135 format.html
136 format.js {render :layout => false}
136 format.js {render :layout => false}
137 end
137 end
138 rescue ChangesetNotFound
138 rescue ChangesetNotFound
139 show_error_not_found
139 show_error_not_found
140 rescue Redmine::Scm::Adapters::CommandFailed => e
140 rescue Redmine::Scm::Adapters::CommandFailed => e
141 show_error_command_failed(e.message)
141 show_error_command_failed(e.message)
142 end
142 end
143
143
144 def diff
144 def diff
145 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
145 if params[:format] == 'diff'
146 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
147
148 # Save diff type as user preference
149 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
150 User.current.pref[:diff_type] = @diff_type
151 User.current.preference.save
152 end
153
154 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
155 unless read_fragment(@cache_key)
156 @diff = @repository.diff(@path, @rev, @rev_to)
146 @diff = @repository.diff(@path, @rev, @rev_to)
157 show_error_not_found unless @diff
147 show_error_not_found and return unless @diff
148 filename = "changeset_r#{@rev}"
149 filename << "_r#{@rev_to}" if @rev_to
150 send_data @diff.join, :filename => "#{filename}.diff",
151 :type => 'text/x-patch',
152 :disposition => 'attachment'
153 else
154 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
155 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
156
157 # Save diff type as user preference
158 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
159 User.current.pref[:diff_type] = @diff_type
160 User.current.preference.save
161 end
162
163 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
164 unless read_fragment(@cache_key)
165 @diff = @repository.diff(@path, @rev, @rev_to)
166 show_error_not_found unless @diff
167 end
158 end
168 end
159 rescue Redmine::Scm::Adapters::CommandFailed => e
169 rescue Redmine::Scm::Adapters::CommandFailed => e
160 show_error_command_failed(e.message)
170 show_error_command_failed(e.message)
161 end
171 end
162
172
163 def stats
173 def stats
164 end
174 end
165
175
166 def graph
176 def graph
167 data = nil
177 data = nil
168 case params[:graph]
178 case params[:graph]
169 when "commits_per_month"
179 when "commits_per_month"
170 data = graph_commits_per_month(@repository)
180 data = graph_commits_per_month(@repository)
171 when "commits_per_author"
181 when "commits_per_author"
172 data = graph_commits_per_author(@repository)
182 data = graph_commits_per_author(@repository)
173 end
183 end
174 if data
184 if data
175 headers["Content-Type"] = "image/svg+xml"
185 headers["Content-Type"] = "image/svg+xml"
176 send_data(data, :type => "image/svg+xml", :disposition => "inline")
186 send_data(data, :type => "image/svg+xml", :disposition => "inline")
177 else
187 else
178 render_404
188 render_404
179 end
189 end
180 end
190 end
181
191
182 private
192 private
183 def find_project
193 def find_project
184 @project = Project.find(params[:id])
194 @project = Project.find(params[:id])
185 rescue ActiveRecord::RecordNotFound
195 rescue ActiveRecord::RecordNotFound
186 render_404
196 render_404
187 end
197 end
188
198
189 REV_PARAM_RE = %r{^[a-f0-9]*$}
199 REV_PARAM_RE = %r{^[a-f0-9]*$}
190
200
191 def find_repository
201 def find_repository
192 @project = Project.find(params[:id])
202 @project = Project.find(params[:id])
193 @repository = @project.repository
203 @repository = @project.repository
194 render_404 and return false unless @repository
204 render_404 and return false unless @repository
195 @path = params[:path].join('/') unless params[:path].nil?
205 @path = params[:path].join('/') unless params[:path].nil?
196 @path ||= ''
206 @path ||= ''
197 @rev = params[:rev]
207 @rev = params[:rev]
198 @rev_to = params[:rev_to]
208 @rev_to = params[:rev_to]
199 raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
209 raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
200 rescue ActiveRecord::RecordNotFound
210 rescue ActiveRecord::RecordNotFound
201 render_404
211 render_404
202 rescue InvalidRevisionParam
212 rescue InvalidRevisionParam
203 show_error_not_found
213 show_error_not_found
204 end
214 end
205
215
206 def show_error_not_found
216 def show_error_not_found
207 render_error l(:error_scm_not_found)
217 render_error l(:error_scm_not_found)
208 end
218 end
209
219
210 def show_error_command_failed(msg)
220 def show_error_command_failed(msg)
211 render_error l(:error_scm_command_failed, msg)
221 render_error l(:error_scm_command_failed, msg)
212 end
222 end
213
223
214 def graph_commits_per_month(repository)
224 def graph_commits_per_month(repository)
215 @date_to = Date.today
225 @date_to = Date.today
216 @date_from = @date_to << 11
226 @date_from = @date_to << 11
217 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
227 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
218 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
228 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
219 commits_by_month = [0] * 12
229 commits_by_month = [0] * 12
220 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
230 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
221
231
222 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
232 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
223 changes_by_month = [0] * 12
233 changes_by_month = [0] * 12
224 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
234 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
225
235
226 fields = []
236 fields = []
227 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
237 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
228 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
238 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
229
239
230 graph = SVG::Graph::Bar.new(
240 graph = SVG::Graph::Bar.new(
231 :height => 300,
241 :height => 300,
232 :width => 500,
242 :width => 500,
233 :fields => fields.reverse,
243 :fields => fields.reverse,
234 :stack => :side,
244 :stack => :side,
235 :scale_integers => true,
245 :scale_integers => true,
236 :step_x_labels => 2,
246 :step_x_labels => 2,
237 :show_data_values => false,
247 :show_data_values => false,
238 :graph_title => l(:label_commits_per_month),
248 :graph_title => l(:label_commits_per_month),
239 :show_graph_title => true
249 :show_graph_title => true
240 )
250 )
241
251
242 graph.add_data(
252 graph.add_data(
243 :data => commits_by_month[0..11].reverse,
253 :data => commits_by_month[0..11].reverse,
244 :title => l(:label_revision_plural)
254 :title => l(:label_revision_plural)
245 )
255 )
246
256
247 graph.add_data(
257 graph.add_data(
248 :data => changes_by_month[0..11].reverse,
258 :data => changes_by_month[0..11].reverse,
249 :title => l(:label_change_plural)
259 :title => l(:label_change_plural)
250 )
260 )
251
261
252 graph.burn
262 graph.burn
253 end
263 end
254
264
255 def graph_commits_per_author(repository)
265 def graph_commits_per_author(repository)
256 commits_by_author = repository.changesets.count(:all, :group => :committer)
266 commits_by_author = repository.changesets.count(:all, :group => :committer)
257 commits_by_author.sort! {|x, y| x.last <=> y.last}
267 commits_by_author.sort! {|x, y| x.last <=> y.last}
258
268
259 changes_by_author = repository.changes.count(:all, :group => :committer)
269 changes_by_author = repository.changes.count(:all, :group => :committer)
260 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
270 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
261
271
262 fields = commits_by_author.collect {|r| r.first}
272 fields = commits_by_author.collect {|r| r.first}
263 commits_data = commits_by_author.collect {|r| r.last}
273 commits_data = commits_by_author.collect {|r| r.last}
264 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
274 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
265
275
266 fields = fields + [""]*(10 - fields.length) if fields.length<10
276 fields = fields + [""]*(10 - fields.length) if fields.length<10
267 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
277 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
268 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
278 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
269
279
270 # Remove email adress in usernames
280 # Remove email adress in usernames
271 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
281 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
272
282
273 graph = SVG::Graph::BarHorizontal.new(
283 graph = SVG::Graph::BarHorizontal.new(
274 :height => 300,
284 :height => 300,
275 :width => 500,
285 :width => 500,
276 :fields => fields,
286 :fields => fields,
277 :stack => :side,
287 :stack => :side,
278 :scale_integers => true,
288 :scale_integers => true,
279 :show_data_values => false,
289 :show_data_values => false,
280 :rotate_y_labels => false,
290 :rotate_y_labels => false,
281 :graph_title => l(:label_commits_per_author),
291 :graph_title => l(:label_commits_per_author),
282 :show_graph_title => true
292 :show_graph_title => true
283 )
293 )
284
294
285 graph.add_data(
295 graph.add_data(
286 :data => commits_data,
296 :data => commits_data,
287 :title => l(:label_revision_plural)
297 :title => l(:label_revision_plural)
288 )
298 )
289
299
290 graph.add_data(
300 graph.add_data(
291 :data => changes_data,
301 :data => changes_data,
292 :title => l(:label_change_plural)
302 :title => l(:label_change_plural)
293 )
303 )
294
304
295 graph.burn
305 graph.burn
296 end
306 end
297
307
298 end
308 end
299
309
300 class Date
310 class Date
301 def months_ago(date = Date.today)
311 def months_ago(date = Date.today)
302 (date.year - self.year)*12 + (date.month - self.month)
312 (date.year - self.year)*12 + (date.month - self.month)
303 end
313 end
304
314
305 def weeks_ago(date = Date.today)
315 def weeks_ago(date = Date.today)
306 (date.year - self.year)*52 + (date.cweek - self.cweek)
316 (date.year - self.year)*52 + (date.cweek - self.cweek)
307 end
317 end
308 end
318 end
309
319
310 class String
320 class String
311 def with_leading_slash
321 def with_leading_slash
312 starts_with?('/') ? self : "/#{self}"
322 starts_with?('/') ? self : "/#{self}"
313 end
323 end
314 end
324 end
@@ -1,91 +1,96
1 <h2><%= l(:label_revision) %> <%= format_revision(@rev) %> <%= @path.gsub(/^.*\//, '') %></h2>
1 <h2><%= l(:label_revision) %> <%= format_revision(@rev) %> <%= @path.gsub(/^.*\//, '') %></h2>
2
2
3 <!-- Choose view type -->
3 <!-- Choose view type -->
4 <% form_tag({ :controller => 'repositories', :action => 'diff'}, :method => 'get') do %>
4 <% form_tag({ :controller => 'repositories', :action => 'diff'}, :method => 'get') do %>
5 <% params.each do |k, p| %>
5 <% params.each do |k, p| %>
6 <% if k != "type" %>
6 <% if k != "type" %>
7 <%= hidden_field_tag(k,p) %>
7 <%= hidden_field_tag(k,p) %>
8 <% end %>
8 <% end %>
9 <% end %>
9 <% end %>
10 <p><label><%= l(:label_view_diff) %></label>
10 <p><label><%= l(:label_view_diff) %></label>
11 <%= select_tag 'type', options_for_select([[l(:label_diff_inline), "inline"], [l(:label_diff_side_by_side), "sbs"]], @diff_type), :onchange => "if (this.value != '') {this.form.submit()}" %></p>
11 <%= select_tag 'type', options_for_select([[l(:label_diff_inline), "inline"], [l(:label_diff_side_by_side), "sbs"]], @diff_type), :onchange => "if (this.value != '') {this.form.submit()}" %></p>
12 <% end %>
12 <% end %>
13
13
14 <% cache(@cache_key) do -%>
14 <% cache(@cache_key) do -%>
15 <% Redmine::UnifiedDiff.new(@diff, @diff_type).each do |table_file| -%>
15 <% Redmine::UnifiedDiff.new(@diff, @diff_type).each do |table_file| -%>
16 <div class="autoscroll">
16 <div class="autoscroll">
17 <% if @diff_type == 'sbs' -%>
17 <% if @diff_type == 'sbs' -%>
18 <table class="filecontent CodeRay">
18 <table class="filecontent CodeRay">
19 <thead>
19 <thead>
20 <tr><th colspan="4" class="filename"><%= table_file.file_name %></th></tr>
20 <tr><th colspan="4" class="filename"><%= table_file.file_name %></th></tr>
21 <tr>
21 <tr>
22 <th colspan="2">@<%= format_revision @rev %></th>
22 <th colspan="2">@<%= format_revision @rev %></th>
23 <th colspan="2">@<%= format_revision @rev_to %></th>
23 <th colspan="2">@<%= format_revision @rev_to %></th>
24 </tr>
24 </tr>
25 </thead>
25 </thead>
26 <tbody>
26 <tbody>
27 <% prev_line_left, prev_line_right = nil, nil -%>
27 <% prev_line_left, prev_line_right = nil, nil -%>
28 <% table_file.keys.sort.each do |key| -%>
28 <% table_file.keys.sort.each do |key| -%>
29 <% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
29 <% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
30 <tr class="spacing"><td colspan="4"></td></tr>
30 <tr class="spacing"><td colspan="4"></td></tr>
31 <% end -%>
31 <% end -%>
32 <tr>
32 <tr>
33 <th class="line-num"><%= table_file[key].nb_line_left %></th>
33 <th class="line-num"><%= table_file[key].nb_line_left %></th>
34 <td class="line-code <%= table_file[key].type_diff_left %>">
34 <td class="line-code <%= table_file[key].type_diff_left %>">
35 <pre><%=to_utf8 table_file[key].line_left %></pre>
35 <pre><%=to_utf8 table_file[key].line_left %></pre>
36 </td>
36 </td>
37 <th class="line-num"><%= table_file[key].nb_line_right %></th>
37 <th class="line-num"><%= table_file[key].nb_line_right %></th>
38 <td class="line-code <%= table_file[key].type_diff_right %>">
38 <td class="line-code <%= table_file[key].type_diff_right %>">
39 <pre><%=to_utf8 table_file[key].line_right %></pre>
39 <pre><%=to_utf8 table_file[key].line_right %></pre>
40 </td>
40 </td>
41 </tr>
41 </tr>
42 <% prev_line_left, prev_line_right = table_file[key].nb_line_left.to_i, table_file[key].nb_line_right.to_i -%>
42 <% prev_line_left, prev_line_right = table_file[key].nb_line_left.to_i, table_file[key].nb_line_right.to_i -%>
43 <% end -%>
43 <% end -%>
44 </tbody>
44 </tbody>
45 </table>
45 </table>
46
46
47 <% else -%>
47 <% else -%>
48 <table class="filecontent CodeRay">
48 <table class="filecontent CodeRay">
49 <thead>
49 <thead>
50 <tr><th colspan="3" class="filename"><%= table_file.file_name %></th></tr>
50 <tr><th colspan="3" class="filename"><%= table_file.file_name %></th></tr>
51 <tr>
51 <tr>
52 <th>@<%= format_revision @rev %></th>
52 <th>@<%= format_revision @rev %></th>
53 <th>@<%= format_revision @rev_to %></th>
53 <th>@<%= format_revision @rev_to %></th>
54 <th></th>
54 <th></th>
55 </tr>
55 </tr>
56 </thead>
56 </thead>
57 <tbody>
57 <tbody>
58 <% prev_line_left, prev_line_right = nil, nil -%>
58 <% prev_line_left, prev_line_right = nil, nil -%>
59 <% table_file.keys.sort.each do |key, line| %>
59 <% table_file.keys.sort.each do |key, line| %>
60 <% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
60 <% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
61 <tr class="spacing"><td colspan="3"></td></tr>
61 <tr class="spacing"><td colspan="3"></td></tr>
62 <% end -%>
62 <% end -%>
63 <tr>
63 <tr>
64 <th class="line-num"><%= table_file[key].nb_line_left %></th>
64 <th class="line-num"><%= table_file[key].nb_line_left %></th>
65 <th class="line-num"><%= table_file[key].nb_line_right %></th>
65 <th class="line-num"><%= table_file[key].nb_line_right %></th>
66 <% if table_file[key].line_left.empty? -%>
66 <% if table_file[key].line_left.empty? -%>
67 <td class="line-code <%= table_file[key].type_diff_right %>">
67 <td class="line-code <%= table_file[key].type_diff_right %>">
68 <pre><%=to_utf8 table_file[key].line_right %></pre>
68 <pre><%=to_utf8 table_file[key].line_right %></pre>
69 </td>
69 </td>
70 <% else -%>
70 <% else -%>
71 <td class="line-code <%= table_file[key].type_diff_left %>">
71 <td class="line-code <%= table_file[key].type_diff_left %>">
72 <pre><%=to_utf8 table_file[key].line_left %></pre>
72 <pre><%=to_utf8 table_file[key].line_left %></pre>
73 </td>
73 </td>
74 <% end -%>
74 <% end -%>
75 </tr>
75 </tr>
76 <% prev_line_left = table_file[key].nb_line_left.to_i if table_file[key].nb_line_left.to_i > 0 -%>
76 <% prev_line_left = table_file[key].nb_line_left.to_i if table_file[key].nb_line_left.to_i > 0 -%>
77 <% prev_line_right = table_file[key].nb_line_right.to_i if table_file[key].nb_line_right.to_i > 0 -%>
77 <% prev_line_right = table_file[key].nb_line_right.to_i if table_file[key].nb_line_right.to_i > 0 -%>
78 <% end -%>
78 <% end -%>
79 </tbody>
79 </tbody>
80 </table>
80 </table>
81 <% end -%>
81 <% end -%>
82
82
83 </div>
83 </div>
84 <% end -%>
84 <% end -%>
85 <% end -%>
85 <% end -%>
86
86
87 <p class="other-formats">
88 <%= l(:label_export_to) %>
89 <span><%= link_to 'Unified diff', params.merge(:format => 'diff') %></span>
90 </p>
91
87 <% html_title(with_leading_slash(@path), 'Diff') -%>
92 <% html_title(with_leading_slash(@path), 'Diff') -%>
88
93
89 <% content_for :header_tags do %>
94 <% content_for :header_tags do %>
90 <%= stylesheet_link_tag "scm" %>
95 <%= stylesheet_link_tag "scm" %>
91 <% end %>
96 <% end %>
General Comments 0
You need to be logged in to leave comments. Login now