def revision
@changeset = @repository.changesets.find_by_revision(@rev)
raise ChangesetNotFound unless @changeset
- @changes_count = @changeset.changes.size
- @changes_pages = Paginator.new self, @changes_count, 150, params['page']
- @changes = @changeset.changes.find(:all,
- :limit => @changes_pages.items_per_page,
- :offset => @changes_pages.current.offset)
respond_to do |format|
format.html
end
end
+ def render_changeset_changes
+ changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change|
+ case change.action
+ when 'A'
+ # Detects moved/copied files
+ if !change.from_path.blank?
+ change.action = @changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
+ end
+ change
+ when 'D'
+ @changeset.changes.detect {|c| c.from_path == change.path} ? nil : change
+ else
+ change
+ end
+ end.compact
+
+ tree = { }
+ changes.each do |change|
+ p = tree
+ dirs = change.path.to_s.split('/').select {|d| !d.blank?}
+ dirs.each do |dir|
+ p[:s] ||= {}
+ p = p[:s]
+ p[dir] ||= {}
+ p = p[dir]
+ end
+ p[:c] = change
+ end
+
+ render_changes_tree(tree[:s])
+ end
+
+ def render_changes_tree(tree)
+ return '' if tree.nil?
+
+ output = ''
+ output << '<ul>'
+ tree.keys.sort.each do |file|
+ s = !tree[file][:s].nil?
+ c = tree[file][:c]
+
+ style = 'change'
+ style << ' folder' if s
+ style << " change-#{c.action}" if c
+
+ text = h(file)
+ unless c.nil?
+ path_param = to_path_param(@repository.relative_path(c.path))
+ text = link_to(text, :controller => 'repositories',
+ :action => 'entry',
+ :id => @project,
+ :path => path_param,
+ :rev => @changeset.revision) unless s || c.action == 'D'
+ text << " - #{c.revision}" unless c.revision.blank?
+ text << ' (' + link_to('diff', :controller => 'repositories',
+ :action => 'diff',
+ :id => @project,
+ :path => path_param,
+ :rev => @changeset.revision) + ') ' if c.action == 'M'
+ text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank?
+ end
+ output << "<li class='#{style}'>#{text}</li>"
+ output << render_changes_tree(tree[file][:s]) if s
+ end
+ output << '</ul>'
+ output
+ end
+
def to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
@encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
<% end %>
<h3><%= l(:label_attachment_plural) %></h3>
-<div style="float:right;">
-<div class="square action_A"></div> <div style="float:left;"><%= l(:label_added) %> </div>
-<div class="square action_M"></div> <div style="float:left;"><%= l(:label_modified) %> </div>
-<div class="square action_D"></div> <div style="float:left;"><%= l(:label_deleted) %> </div>
-</div>
+<ul id="changes-legend">
+<li class="change change-A"><%= l(:label_added) %></li>
+<li class="change change-M"><%= l(:label_modified) %></li>
+<li class="change change-C"><%= l(:label_copied) %></li>
+<li class="change change-R"><%= l(:label_renamed) %></li>
+<li class="change change-D"><%= l(:label_deleted) %></li>
+</ul>
+
<p><%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %></p>
-<table class="list">
-<tbody>
-<% @changes.each do |change| %>
-<tr class="<%= cycle 'odd', 'even' %>">
-<td><div class="square action_<%= change.action %>"></div>
-<% if change.action == "D" -%>
- <%= change.path -%>
-<% else -%>
- <%= link_to change.path, :action => 'entry', :id => @project, :path => to_path_param(change.relative_path), :rev => @changeset.revision -%>
-<% end -%>
-<%= "(#{change.revision})" unless change.revision.blank? %></td>
-<td align="right">
-<% if change.action == "M" %>
-<%= link_to l(:label_view_diff), :action => 'diff', :id => @project, :path => to_path_param(change.relative_path), :rev => @changeset.revision %>
-<% end %>
-</td>
-</tr>
-<% end %>
-</tbody>
-</table>
-<p class="pagination"><%= pagination_links_full @changes_pages %></p>
+
+<div class="changeset-changes">
+<%= render_changeset_changes %>
+</div>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
notice_unable_delete_version: Unable to delete version.
field_comments: Comment
setting_commit_logs_encoding: Commit messages encoding
+label_renamed: renamed\r
+label_copied: copied\r
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
label_associated_revisions: Associated revisions
label_added: added
label_modified: modified
+label_copied: copied
+label_renamed: renamed
label_deleted: deleted
label_latest_revision: Latest revision
label_latest_revision_plural: Latest revisions
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
setting_sequential_project_identifiers: Generate sequential project identifiers
setting_commit_logs_encoding: Commit messages encoding
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
label_associated_revisions: Révisions associées
label_added: ajouté
label_modified: modifié
+label_copied: copié
+label_renamed: renommé
label_deleted: supprimé
label_latest_revision: Dernière révision
label_latest_revision_plural: Dernières révisions
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Idézet
setting_sequential_project_identifiers: Szekvenciális projekt azonosítók generálása
notice_unable_delete_version: A verziót nem lehet törölni
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
setting_sequential_project_identifiers: Generate sequential project identifiers
button_quote: Quote
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
enumeration_doc_categories: Categorias de documento
enumeration_activities: Atividades (time tracking)
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed\r
+label_copied: copied\r
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
text_user_wrote: '%s написал(а):'
text_wiki_destroy_confirmation: Вы уверены, что хотите удалить данную Wiki и все содержимое?
text_workflow_edit: Выберите роль и трекер для редактирования последовательности состояний
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
setting_commit_logs_encoding: Commit messages encoding
general_csv_decimal_separator: '.'
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
button_quote: Quote
setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
enumeration_issue_priorities: 項目優先權
enumeration_doc_categories: 文件分類
enumeration_activities: 活動 (時間追蹤)
+label_renamed: renamed
+label_copied: copied
enumeration_issue_priorities: 问题优先级
enumeration_doc_categories: 文档类别
enumeration_activities: 活动(时间跟踪)
+label_renamed: renamed
+label_copied: copied
+div.changeset-changes ul { margin: 0; padding: 0; }
+div.changeset-changes ul > ul { margin-left: 18px; padding: 0; }
+
+li.change {
+ list-style-type:none;
+ background-image: url(../images/bullet_black.png);
+ background-position: 1px 1px;
+ background-repeat: no-repeat;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-left: 20px;
+ margin: 0;
+}
+li.change.folder { background-image: url(../images/folder_open.png); }
+li.change.folder.change-A { background-image: url(../images/folder_open_add.png); }
+li.change.folder.change-M { background-image: url(../images/folder_open_orange.png); }
+li.change.change-A { background-image: url(../images/bullet_add.png); }
+li.change.change-M { background-image: url(../images/bullet_orange.png); }
+li.change.change-C { background-image: url(../images/bullet_blue.png); }
+li.change.change-R { background-image: url(../images/bullet_purple.png); }
+li.change.change-D { background-image: url(../images/bullet_delete.png); }
+
+li.change .copied-from { font-style: italic; color: #999; font-size: 0.9em; }
+li.change .copied-from:before { content: " - "}
+
+#changes-legend { float: right; font-size: 0.8em; margin: 0; }
+#changes-legend li { float: left; background-position: 5px 0; }
+
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: #e4e4d4; text-align: left; padding: 0.2em;}
get :revision, :id => 1, :rev => 2
assert_response :success
assert_template 'revision'
- assert_tag :tag => 'tr',
- :child => { :tag => 'td',
+ assert_tag :tag => 'ul',
+ :child => { :tag => 'li',
# link to the entry at rev 2
- :child => { :tag => 'a', :attributes => {:href => 'repositories/entry/ecookbook/test/some/path/in/the/repo?rev=2'},
- :content => %r{/test/some/path/in/the/repo} }
- },
- :child => { :tag => 'td',
- # link to partial diff
- :child => { :tag => 'a', :attributes => { :href => '/repositories/diff/ecookbook/test/some/path/in/the/repo?rev=2' } }
- }
+ :child => { :tag => 'a',
+ :attributes => {:href => '/repositories/entry/ecookbook/test/some/path/in/the/repo?rev=2'},
+ :content => 'repo',
+ # link to partial diff
+ :sibling => { :tag => 'a',
+ :attributes => { :href => '/repositories/diff/ecookbook/test/some/path/in/the/repo?rev=2' }
+ }
+ }
+ }
end
def test_revision_with_repository_pointing_to_a_subdirectory
get :revision, :id => 1, :rev => 2
assert_response :success
assert_template 'revision'
- assert_tag :tag => 'tr',
- :child => { :tag => 'td', :content => %r{/test/some/path/in/the/repo} },
- :child => { :tag => 'td',
- :child => { :tag => 'a', :attributes => { :href => '/repositories/diff/ecookbook/path/in/the/repo?rev=2' } }
- }
+ assert_tag :tag => 'ul',
+ :child => { :tag => 'li',
+ # link to the entry at rev 2
+ :child => { :tag => 'a',
+ :attributes => {:href => '/repositories/entry/ecookbook/path/in/the/repo?rev=2'},
+ :content => 'repo',
+ # link to partial diff
+ :sibling => { :tag => 'a',
+ :attributes => { :href => '/repositories/diff/ecookbook/path/in/the/repo?rev=2' }
+ }
+ }
+ }
end
def test_diff