diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 2f96e2d66..78576856d 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -118,11 +118,6 @@ class RepositoriesController < ApplicationController
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
diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb
index 852ed18d7..1a82bb8ce 100644
--- a/app/helpers/repositories_helper.rb
+++ b/app/helpers/repositories_helper.rb
@@ -32,6 +32,74 @@ module RepositoriesHelper
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 << '
'
+ 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 << "- #{text}
"
+ output << render_changes_tree(tree[file][:s]) if s
+ end
+ output << '
'
+ 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)
diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml
index 80ac3bd1a..123fccf26 100644
--- a/app/views/repositories/revision.rhtml
+++ b/app/views/repositories/revision.rhtml
@@ -36,33 +36,19 @@
<% end %>
<%= l(:label_attachment_plural) %>
-
-
<%= l(:label_added) %>
-
<%= l(:label_modified) %>
-
<%= l(:label_deleted) %>
-
+
+- <%= l(:label_added) %>
+- <%= l(:label_modified) %>
+- <%= l(:label_copied) %>
+- <%= l(:label_renamed) %>
+- <%= l(:label_deleted) %>
+
+
<%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %>
-
-
-<% @changes.each do |change| %>
-
-
-<% 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? %> |
-
-<% 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 %>
- |
-
-<% end %>
-
-
-
+
+
+<%= render_changeset_changes %>
+
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>
diff --git a/lang/bg.yml b/lang/bg.yml
index c5c4e0e22..e072ad93b 100644
--- a/lang/bg.yml
+++ b/lang/bg.yml
@@ -638,3 +638,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/ca.yml b/lang/ca.yml
index ee9e17705..895f213ca 100644
--- a/lang/ca.yml
+++ b/lang/ca.yml
@@ -640,3 +640,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version.
field_comments: Comment
setting_commit_logs_encoding: Commit messages encoding
+label_renamed: renamed
+label_copied: copied
diff --git a/lang/cs.yml b/lang/cs.yml
index 67cfba0e1..51486aec7 100644
--- a/lang/cs.yml
+++ b/lang/cs.yml
@@ -643,3 +643,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/da.yml b/lang/da.yml
index ce8b8504c..2461d5836 100644
--- a/lang/da.yml
+++ b/lang/da.yml
@@ -640,3 +640,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/de.yml b/lang/de.yml
index dd1b74bf9..7ee142528 100644
--- a/lang/de.yml
+++ b/lang/de.yml
@@ -639,3 +639,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/en.yml b/lang/en.yml
index 7e8ba9f52..5ad1e5588 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -405,6 +405,8 @@ label_revision_plural: Revisions
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
diff --git a/lang/es.yml b/lang/es.yml
index d8f5417ae..320a169fa 100644
--- a/lang/es.yml
+++ b/lang/es.yml
@@ -641,3 +641,5 @@ setting_commit_logs_encoding: Codificación de los mensajes de commit
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
diff --git a/lang/fi.yml b/lang/fi.yml
index b63802e67..1953fdfe5 100644
--- a/lang/fi.yml
+++ b/lang/fi.yml
@@ -638,3 +638,5 @@ button_quote: Quote
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
diff --git a/lang/fr.yml b/lang/fr.yml
index 872266e3c..eb52d5a57 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -404,6 +404,8 @@ label_revision_plural: Révisions
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
diff --git a/lang/he.yml b/lang/he.yml
index 8cd68c102..7a6c62ef6 100644
--- a/lang/he.yml
+++ b/lang/he.yml
@@ -638,3 +638,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/hu.yml b/lang/hu.yml
index fa23e7059..2b1f3c32e 100644
--- a/lang/hu.yml
+++ b/lang/hu.yml
@@ -639,3 +639,5 @@ setting_commit_logs_encoding: Commit üzenetek kódlapja
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
diff --git a/lang/it.yml b/lang/it.yml
index 5507e6bbc..259135886 100644
--- a/lang/it.yml
+++ b/lang/it.yml
@@ -638,3 +638,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/ja.yml b/lang/ja.yml
index c9470f91f..c38c2ef70 100644
--- a/lang/ja.yml
+++ b/lang/ja.yml
@@ -639,3 +639,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/ko.yml b/lang/ko.yml
index 38b5a48af..27adf167c 100644
--- a/lang/ko.yml
+++ b/lang/ko.yml
@@ -638,3 +638,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/lt.yml b/lang/lt.yml
index 7c62bf155..d9d193352 100644
--- a/lang/lt.yml
+++ b/lang/lt.yml
@@ -640,3 +640,5 @@ setting_commit_logs_encoding: Commit pranėšimų koduotė
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
diff --git a/lang/nl.yml b/lang/nl.yml
index e8cab52b2..746f242ee 100644
--- a/lang/nl.yml
+++ b/lang/nl.yml
@@ -639,3 +639,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/no.yml b/lang/no.yml
index ce4741314..2b78826d0 100644
--- a/lang/no.yml
+++ b/lang/no.yml
@@ -639,3 +639,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/pl.yml b/lang/pl.yml
index 05ff1afb8..6bdac481c 100644
--- a/lang/pl.yml
+++ b/lang/pl.yml
@@ -638,3 +638,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/pt-br.yml b/lang/pt-br.yml
index c40c87a76..a4bbefe01 100644
--- a/lang/pt-br.yml
+++ b/lang/pt-br.yml
@@ -639,3 +639,5 @@ enumeration_issue_priorities: Prioridade das tarefas
enumeration_doc_categories: Categorias de documento
enumeration_activities: Atividades (time tracking)
notice_unable_delete_version: Unable to delete version
+label_renamed: renamed
+label_copied: copied
diff --git a/lang/pt.yml b/lang/pt.yml
index 37e9ad6b2..98cde780a 100644
--- a/lang/pt.yml
+++ b/lang/pt.yml
@@ -638,3 +638,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/ro.yml b/lang/ro.yml
index ebf1c0a1f..855662c58 100644
--- a/lang/ro.yml
+++ b/lang/ro.yml
@@ -638,3 +638,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/ru.yml b/lang/ru.yml
index 5a010ea5c..9ef18711b 100644
--- a/lang/ru.yml
+++ b/lang/ru.yml
@@ -670,3 +670,5 @@ text_user_mail_option: "Для невыбранных проектов, Вы б
text_user_wrote: '%s написал(а):'
text_wiki_destroy_confirmation: Вы уверены, что хотите удалить данную Wiki и все содержимое?
text_workflow_edit: Выберите роль и трекер для редактирования последовательности состояний
+label_renamed: renamed
+label_copied: copied
diff --git a/lang/sr.yml b/lang/sr.yml
index f7ddfd728..574470e1b 100644
--- a/lang/sr.yml
+++ b/lang/sr.yml
@@ -639,3 +639,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/sv.yml b/lang/sv.yml
index b4e621c93..1e7d292d6 100644
--- a/lang/sv.yml
+++ b/lang/sv.yml
@@ -639,3 +639,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/th.yml b/lang/th.yml
index e2d8a0a4f..0ad6b9b93 100644
--- a/lang/th.yml
+++ b/lang/th.yml
@@ -641,3 +641,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/tr.yml b/lang/tr.yml
index 9fb5b63ca..7cb2a9e63 100644
--- a/lang/tr.yml
+++ b/lang/tr.yml
@@ -639,3 +639,5 @@ setting_mail_handler_api_key: API key
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
diff --git a/lang/uk.yml b/lang/uk.yml
index 8e8541f10..055d27ed9 100644
--- a/lang/uk.yml
+++ b/lang/uk.yml
@@ -640,3 +640,5 @@ setting_commit_logs_encoding: Commit messages encoding
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
diff --git a/lang/zh-tw.yml b/lang/zh-tw.yml
index 9de70f00b..a7a7767f1 100644
--- a/lang/zh-tw.yml
+++ b/lang/zh-tw.yml
@@ -639,3 +639,5 @@ default_activity_development: 開發
enumeration_issue_priorities: 項目優先權
enumeration_doc_categories: 文件分類
enumeration_activities: 活動 (時間追蹤)
+label_renamed: renamed
+label_copied: copied
diff --git a/lang/zh.yml b/lang/zh.yml
index f66651cbb..ed84eeb57 100644
--- a/lang/zh.yml
+++ b/lang/zh.yml
@@ -639,3 +639,5 @@ default_activity_development: 开发
enumeration_issue_priorities: 问题优先级
enumeration_doc_categories: 文档类别
enumeration_activities: 活动(时间跟踪)
+label_renamed: renamed
+label_copied: copied
diff --git a/public/images/bullet_add.png b/public/images/bullet_add.png
new file mode 100644
index 000000000..41ff8335b
Binary files /dev/null and b/public/images/bullet_add.png differ
diff --git a/public/images/bullet_black.png b/public/images/bullet_black.png
new file mode 100644
index 000000000..57619706d
Binary files /dev/null and b/public/images/bullet_black.png differ
diff --git a/public/images/bullet_blue.png b/public/images/bullet_blue.png
new file mode 100644
index 000000000..a7651ec8a
Binary files /dev/null and b/public/images/bullet_blue.png differ
diff --git a/public/images/bullet_delete.png b/public/images/bullet_delete.png
new file mode 100644
index 000000000..bd6271b24
Binary files /dev/null and b/public/images/bullet_delete.png differ
diff --git a/public/images/bullet_orange.png b/public/images/bullet_orange.png
new file mode 100644
index 000000000..638b1ef3c
Binary files /dev/null and b/public/images/bullet_orange.png differ
diff --git a/public/images/bullet_purple.png b/public/images/bullet_purple.png
new file mode 100644
index 000000000..52ba5036b
Binary files /dev/null and b/public/images/bullet_purple.png differ
diff --git a/public/images/folder_open_add.png b/public/images/folder_open_add.png
new file mode 100644
index 000000000..1ce4dcaef
Binary files /dev/null and b/public/images/folder_open_add.png differ
diff --git a/public/images/folder_open_orange.png b/public/images/folder_open_orange.png
new file mode 100644
index 000000000..fb909ba95
Binary files /dev/null and b/public/images/folder_open_orange.png differ
diff --git a/public/stylesheets/scm.css b/public/stylesheets/scm.css
index d5a879bf1..ecd319307 100644
--- a/public/stylesheets/scm.css
+++ b/public/stylesheets/scm.css
@@ -1,4 +1,32 @@
+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;}
diff --git a/test/functional/repositories_subversion_controller_test.rb b/test/functional/repositories_subversion_controller_test.rb
index 8320be7e7..245a170d1 100644
--- a/test/functional/repositories_subversion_controller_test.rb
+++ b/test/functional/repositories_subversion_controller_test.rb
@@ -125,16 +125,18 @@ class RepositoriesSubversionControllerTest < Test::Unit::TestCase
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
@@ -145,11 +147,18 @@ class RepositoriesSubversionControllerTest < Test::Unit::TestCase
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