summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2007-03-25 12:12:15 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2007-03-25 12:12:15 +0000
commit16f9f50f50dbac49b1f791c7c2892420a354bc43 (patch)
treeefaebfb9dd13bde6d45afa36b39063772d9116de
parent6f1dcdc08f401923ac77254ca22fd520582bcf6f (diff)
downloadredmine-16f9f50f50dbac49b1f791c7c2892420a354bc43.tar.gz
redmine-16f9f50f50dbac49b1f791c7c2892420a354bc43.zip
SVN commits are now stored in the database, and added to the activity view and the search engine.
New commits are automatically retrieved and stored when consulting the repository in the app. This behaviour can be disabled by unchecking 'Autofecth commits' in configuration settings. Commits can be fetched offline by running (recommanded at least for the initial import of past commits): ruby script/runner "Repository.fetch_changesets" It will load commits for all of the repositories. git-svn-id: http://redmine.rubyforge.org/svn/trunk@377 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r--app/controllers/projects_controller.rb22
-rw-r--r--app/controllers/repositories_controller.rb24
-rw-r--r--app/models/change.rb22
-rw-r--r--app/models/changeset.rb25
-rw-r--r--app/models/repository.rb60
-rw-r--r--app/models/svn_repos.rb42
-rw-r--r--app/views/projects/_form.rhtml2
-rw-r--r--app/views/projects/activity.rhtml4
-rw-r--r--app/views/projects/search.rhtml7
-rw-r--r--app/views/repositories/_revisions.rhtml20
-rw-r--r--app/views/repositories/revision.rhtml16
-rw-r--r--app/views/repositories/revisions.rhtml27
-rw-r--r--app/views/repositories/show.rhtml15
-rw-r--r--app/views/settings/edit.rhtml3
-rw-r--r--config/settings.yml2
-rw-r--r--db/migrate/034_create_changesets.rb16
-rw-r--r--db/migrate/035_create_changes.rb16
-rw-r--r--lang/de.yml2
-rw-r--r--lang/en.yml2
-rw-r--r--lang/es.yml2
-rw-r--r--lang/fr.yml2
-rw-r--r--lang/it.yml2
-rw-r--r--lang/ja.yml2
23 files changed, 267 insertions, 68 deletions
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 129355851..410e24847 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -519,6 +519,17 @@ class ProjectsController < ApplicationController
@show_wiki_edits = 1
end
+ unless @project.repository.nil? || params[:show_changesets] == "0"
+ @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to]).each { |i|
+ def i.created_on
+ self.committed_on
+ end
+ @events_by_day[i.created_on.to_date] ||= []
+ @events_by_day[i.created_on.to_date] << i
+ }
+ @show_changesets = 1
+ end
+
render :layout => false if request.xhr?
end
@@ -581,10 +592,10 @@ class ProjectsController < ApplicationController
@question = params[:q] || ""
@question.strip!
@all_words = params[:all_words] || (params[:submit] ? false : true)
- @scope = params[:scope] || (params[:submit] ? [] : %w(issues news documents wiki) )
- if !@question.empty?
- # tokens must be at least 3 character long
- @tokens = @question.split.uniq.select {|w| w.length > 2 }
+ @scope = params[:scope] || (params[:submit] ? [] : %w(issues changesets news documents wiki) )
+ # tokens must be at least 3 character long
+ @tokens = @question.split.uniq.select {|w| w.length > 2 }
+ if !@tokens.empty?
# no more than 5 tokens to search for
@tokens.slice! 5..-1 if @tokens.size > 5
# strings used in sql like statement
@@ -596,7 +607,10 @@ class ProjectsController < ApplicationController
@results += @project.news.find(:all, :limit => limit, :conditions => [ (["(LOWER(title) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort], :include => :author ) if @scope.include? 'news'
@results += @project.documents.find(:all, :limit => limit, :conditions => [ (["(LOWER(title) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @scope.include? 'documents'
@results += @project.wiki.pages.find(:all, :limit => limit, :include => :content, :conditions => [ (["(LOWER(title) like ? OR LOWER(text) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @project.wiki && @scope.include?('wiki')
+ @results += @project.repository.changesets.find(:all, :limit => limit, :conditions => [ (["(LOWER(comment) like ?)"] * like_tokens.size).join(operator), * (like_tokens).sort] ) if @project.repository && @scope.include?('changesets')
@question = @tokens.join(" ")
+ else
+ @question = ""
end
end
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index acf03eb7b..94b81080f 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -1,5 +1,5 @@
# redMine - project management software
-# Copyright (C) 2006 Jean-Philippe Lang
+# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -20,9 +20,16 @@ class RepositoriesController < ApplicationController
before_filter :find_project, :authorize
def show
+ # get entries for the browse frame
@entries = @repository.scm.entries('')
show_error and return unless @entries
- @latest_revision = @entries.revisions.latest
+ # check if new revisions have been committed in the repository
+ scm_latestrev = @entries.revisions.latest
+ if Setting.autofetch_changesets? && scm_latestrev && ((@repository.latest_changeset.nil?) || (@repository.latest_changeset.revision < scm_latestrev.identifier.to_i))
+ @repository.fetch_changesets
+ @repository.reload
+ end
+ @changesets = @repository.changesets.find(:all, :limit => 5, :order => "committed_on DESC")
end
def browse
@@ -31,9 +38,11 @@ class RepositoriesController < ApplicationController
end
def revisions
- @entry = @repository.scm.entry(@path, @rev)
- @revisions = @repository.scm.revisions(@path, @rev)
- show_error and return unless @entry && @revisions
+ unless @path == ''
+ @entry = @repository.scm.entry(@path, @rev)
+ show_error and return unless @entry
+ end
+ @changesets = @repository.changesets_for_path(@path)
end
def entry
@@ -45,9 +54,8 @@ class RepositoriesController < ApplicationController
end
def revision
- @revisions = @repository.scm.revisions '', @rev, @rev, :with_paths => true
- show_error and return unless @revisions
- @revision = @revisions.first
+ @changeset = @repository.changesets.find_by_revision(@rev)
+ show_error and return unless @changeset
end
def diff
diff --git a/app/models/change.rb b/app/models/change.rb
new file mode 100644
index 000000000..d14f435a4
--- /dev/null
+++ b/app/models/change.rb
@@ -0,0 +1,22 @@
+# redMine - project management software
+# Copyright (C) 2006-2007 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class Change < ActiveRecord::Base
+ belongs_to :changeset
+
+ validates_presence_of :changeset_id, :action, :path
+end
diff --git a/app/models/changeset.rb b/app/models/changeset.rb
new file mode 100644
index 000000000..fa60f1db8
--- /dev/null
+++ b/app/models/changeset.rb
@@ -0,0 +1,25 @@
+# redMine - project management software
+# Copyright (C) 2006-2007 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class Changeset < ActiveRecord::Base
+ belongs_to :repository
+ has_many :changes, :dependent => :delete_all
+
+ validates_presence_of :repository_id, :revision, :committed_on
+ validates_numericality_of :revision, :only_integer => true
+ validates_uniqueness_of :revision, :scope => :repository_id
+end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 16bee25a7..19b12f372 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1,5 +1,5 @@
# redMine - project management software
-# Copyright (C) 2006 Jean-Philippe Lang
+# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,6 +17,11 @@
class Repository < ActiveRecord::Base
belongs_to :project
+ has_many :changesets, :dependent => :destroy, :order => 'revision DESC'
+ has_one :latest_changeset, :class_name => 'Changeset', :foreign_key => :repository_id, :order => 'revision DESC'
+
+ attr_protected :root_url
+
validates_presence_of :url
validates_format_of :url, :with => /^(http|https|svn|file):\/\/.+/i
@@ -27,10 +32,55 @@ class Repository < ActiveRecord::Base
end
def url=(str)
- unless str == self.url
- self.attributes = {:root_url => nil }
- @scm = nil
+ super if root_url.blank?
+ end
+
+ def changesets_for_path(path="")
+ path = "/#{path}%"
+ path = url.gsub(/^#{root_url}/, '') + path if root_url && root_url != url
+ path.squeeze!("/")
+ changesets.find(:all, :include => :changes,
+ :conditions => ["#{Change.table_name}.path LIKE ?", path])
+ end
+
+ def fetch_changesets
+ scm_info = scm.info
+ if scm_info
+ lastrev_identifier = scm_info.lastrev.identifier.to_i
+ if latest_changeset.nil? || latest_changeset.revision < lastrev_identifier
+ logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
+ identifier_from = latest_changeset ? latest_changeset.revision + 1 : 1
+ while (identifier_from <= lastrev_identifier)
+ # loads changesets by batches of 200
+ identifier_to = [identifier_from + 199, lastrev_identifier].min
+ revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
+ transaction do
+ revisions.reverse_each do |revision|
+ changeset = Changeset.create(:repository => self,
+ :revision => revision.identifier,
+ :committer => revision.author,
+ :committed_on => revision.time,
+ :comment => revision.message)
+
+ revision.paths.each do |change|
+ Change.create(:changeset => changeset,
+ :action => change[:action],
+ :path => change[:path],
+ :from_path => change[:from_path],
+ :from_revision => change[:from_revision])
+ end
+ end
+ end
+ identifier_from = identifier_to + 1
+ end
+ end
end
- super
+ end
+
+ # fetch new changesets for all repositories
+ # can be called periodically by an external script
+ # eg. ruby script/runner "Repository.fetch_changesets"
+ def self.fetch_changesets
+ find(:all).each(&:fetch_changesets)
end
end
diff --git a/app/models/svn_repos.rb b/app/models/svn_repos.rb
index 2fd907319..610b67a20 100644
--- a/app/models/svn_repos.rb
+++ b/app/models/svn_repos.rb
@@ -1,5 +1,5 @@
# redMine - project management software
-# Copyright (C) 2006 Jean-Philippe Lang
+# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -39,20 +39,27 @@ module SvnRepos
@url
end
- # finds the root url of the svn repository
- def retrieve_root_url
+ # get info about the svn repository
+ def info
cmd = "svn info --xml #{target('')}"
cmd << " --username #{@login} --password #{@password}" if @login
- root_url = nil
+ info = nil
shellout(cmd) do |io|
begin
doc = REXML::Document.new(io)
- root_url = doc.elements["info/entry/repository/root"].text
+ #root_url = doc.elements["info/entry/repository/root"].text
+ info = Info.new({:root_url => doc.elements["info/entry/repository/root"].text,
+ :lastrev => Revision.new({
+ :identifier => doc.elements["info/entry/commit"].attributes['revision'],
+ :time => Time.parse(doc.elements["info/entry/commit/date"].text),
+ :author => (doc.elements["info/entry/commit/author"] ? doc.elements["info/entry/commit/author"].text : "")
+ })
+ })
rescue
end
end
return nil if $? && $?.exitstatus != 0
- root_url
+ info
rescue Errno::ENOENT => e
return nil
end
@@ -83,7 +90,7 @@ module SvnRepos
:lastrev => Revision.new({
:identifier => entry.elements['commit'].attributes['revision'],
:time => Time.parse(entry.elements['commit'].elements['date'].text),
- :author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "anonymous")
+ :author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "")
})
})
end
@@ -112,13 +119,15 @@ module SvnRepos
paths = []
logentry.elements.each("paths/path") do |path|
paths << {:action => path.attributes['action'],
- :path => path.text
+ :path => path.text,
+ :from_path => path.attributes['copyfrom-path'],
+ :from_revision => path.attributes['copyfrom-rev']
}
end
paths.sort! { |x,y| x[:path] <=> y[:path] }
revisions << Revision.new({:identifier => logentry.attributes['revision'],
- :author => (logentry.elements['author'] ? logentry.elements['author'].text : "anonymous"),
+ :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""),
:time => Time.parse(logentry.elements['date'].text),
:message => logentry.elements['msg'].text,
:paths => paths
@@ -171,7 +180,12 @@ module SvnRepos
raise CommandFailed
end
- private
+ private
+ def retrieve_root_url
+ info = self.info
+ info ? info.root_url : nil
+ end
+
def target(path)
path ||= ""
base = path.match(/^\//) ? root_url : url
@@ -207,6 +221,14 @@ module SvnRepos
end
end
+ class Info
+ attr_accessor :root_url, :lastrev
+ def initialize(attributes={})
+ self.root_url = attributes[:root_url] if attributes[:root_url]
+ self.lastrev = attributes[:lastrev]
+ end
+ end
+
class Entry
attr_accessor :name, :path, :kind, :size, :lastrev
def initialize(attributes={})
diff --git a/app/views/projects/_form.rhtml b/app/views/projects/_form.rhtml
index 85f3f2966..ded227197 100644
--- a/app/views/projects/_form.rhtml
+++ b/app/views/projects/_form.rhtml
@@ -30,7 +30,7 @@
<%= hidden_field_tag "repository_enabled", 0 %>
<div id="repository">
<% fields_for :repository, @project.repository, { :builder => TabularFormBuilder, :lang => current_language} do |repository| %>
-<p><%= repository.text_field :url, :size => 60, :required => true %><br />(http://, https://, svn://, file:///)</p>
+<p><%= repository.text_field :url, :size => 60, :required => true, :disabled => (@project.repository && !@project.repository.root_url.blank?) %><br />(http://, https://, svn://, file:///)</p>
<p><%= repository.text_field :login, :size => 30 %></p>
<p><%= repository.password_field :password, :size => 30 %></p>
<% end %>
diff --git a/app/views/projects/activity.rhtml b/app/views/projects/activity.rhtml
index b1d131aa9..69bfa6028 100644
--- a/app/views/projects/activity.rhtml
+++ b/app/views/projects/activity.rhtml
@@ -7,6 +7,7 @@
<%= select_year(@year, :prefix => "year", :discard_type => true) %></p>
<p>
<%= check_box_tag 'show_issues', 1, @show_issues %><%= hidden_field_tag 'show_issues', 0, :id => nil %> <%=l(:label_issue_plural)%><br />
+ <% if @project.repository %><%= check_box_tag 'show_changesets', 1, @show_changesets %><%= hidden_field_tag 'show_changesets', 0, :id => nil %> <%=l(:label_revision_plural)%><br /><% end %>
<%= check_box_tag 'show_news', 1, @show_news %><%= hidden_field_tag 'show_news', 0, :id => nil %> <%=l(:label_news_plural)%><br />
<%= check_box_tag 'show_files', 1, @show_files %><%= hidden_field_tag 'show_files', 0, :id => nil %> <%=l(:label_attachment_plural)%><br />
<%= check_box_tag 'show_documents', 1, @show_documents %><%= hidden_field_tag 'show_documents', 0, :id => nil %> <%=l(:label_document_plural)%><br />
@@ -39,6 +40,9 @@
<% elsif e.is_a? WikiContent.versioned_class %>
<%= e.created_on.strftime("%H:%M") %> <%=l(:label_wiki_edit)%>: <%= link_to h(WikiPage.pretty_title(e.title)), :controller => 'wiki', :page => e.title %> (<%= link_to '#' + e.version.to_s, :controller => 'wiki', :page => e.title, :version => e.version %>)<br />
<% unless e.comment.blank? %><em><%=h e.comment %></em><% end %>
+ <% elsif e.is_a? Changeset %>
+ <%= e.created_on.strftime("%H:%M") %> <%=l(:label_revision)%> <%= link_to h(e.revision), :controller => 'repositories', :action => 'revision', :id => @project, :rev => e.revision %><br />
+ <em><%=h e.committer %><%= h(": #{e.comment}") unless e.comment.blank? %></em>
<% end %>
</p></li>
diff --git a/app/views/projects/search.rhtml b/app/views/projects/search.rhtml
index 056effd47..8b35dce3b 100644
--- a/app/views/projects/search.rhtml
+++ b/app/views/projects/search.rhtml
@@ -4,6 +4,9 @@
<% form_tag({:action => 'search', :id => @project}, :method => :get) do %>
<p><%= text_field_tag 'q', @question, :size => 30 %>
<%= check_box_tag 'scope[]', 'issues', (@scope.include? 'issues') %> <label><%= l(:label_issue_plural) %></label>
+<% if @project.repository %>
+<%= check_box_tag 'scope[]', 'changesets', (@scope.include? 'changesets') %> <label><%= l(:label_revision_plural) %></label>
+<% end %>
<%= check_box_tag 'scope[]', 'news', (@scope.include? 'news') %> <label><%= l(:label_news_plural) %></label>
<%= check_box_tag 'scope[]', 'documents', (@scope.include? 'documents') %> <label><%= l(:label_document_plural) %></label>
<% if @project.wiki %>
@@ -36,6 +39,10 @@
<%=l(:label_wiki)%>: <%= link_to highlight_tokens(h(e.pretty_title), @tokens), :controller => 'wiki', :action => 'index', :id => @project, :page => e.title %><br />
<%= highlight_tokens(e.content.text, @tokens) %><br />
<i><%= e.content.author ? e.content.author.name : "Anonymous" %>, <%= format_time(e.content.updated_on) %></i>
+ <% elsif e.is_a? Changeset %>
+ <%=l(:label_revision)%> <%= link_to h(e.revision), :controller => 'repositories', :action => 'revision', :id => @project, :rev => e.revision %><br />
+ <%= highlight_tokens(e.comment, @tokens) %><br />
+ <em><%= e.committer.blank? ? e.committer : "Anonymous" %>, <%= format_time(e.committed_on) %></em>
<% end %>
</p></li>
<% end %>
diff --git a/app/views/repositories/_revisions.rhtml b/app/views/repositories/_revisions.rhtml
new file mode 100644
index 000000000..d9bf7cc7c
--- /dev/null
+++ b/app/views/repositories/_revisions.rhtml
@@ -0,0 +1,20 @@
+<table class="list">
+<thead><tr>
+<th>#</th>
+<th><%= l(:field_author) %></th>
+<th><%= l(:label_date) %></th>
+<th><%= l(:field_comment) %></th>
+<th></th>
+</tr></thead>
+<tbody>
+<% changesets.each do |changeset| %>
+<tr class="<%= cycle 'odd', 'even' %>">
+<th align="center"><%= link_to changeset.revision, :action => 'revision', :id => project, :rev => changeset.revision %></th>
+<td align="center"><em><%=h changeset.committer %></em></td>
+<td align="center"><%= format_time(changeset.committed_on) %></td>
+<td style="width:70%"><%= textilizable(changeset.comment) %></td>
+<td align="center"><%= link_to 'Diff', :action => 'diff', :id => project, :path => path, :rev => changeset.revision if entry && entry.is_file? && changeset != changesets.last %></td>
+</tr>
+<% end %>
+</tbody>
+</table> \ No newline at end of file
diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml
index 22e965090..3f28f3a9c 100644
--- a/app/views/repositories/revision.rhtml
+++ b/app/views/repositories/revision.rhtml
@@ -5,10 +5,10 @@
<% end %>
</div>
-<h2><%= l(:label_revision) %> <%= @revision.identifier %></h2>
+<h2><%= l(:label_revision) %> <%= @changeset.revision %></h2>
-<p><em><%= @revision.author %>, <%= format_time(@revision.time) %></em></p>
-<%= textilizable @revision.message %>
+<p><em><%= @changeset.committer %>, <%= format_time(@changeset.committed_on) %></em></p>
+<%= textilizable @changeset.comment %>
<div style="float:right;">
<div class="square action_A"></div> <div style="float:left;"><%= l(:label_added) %>&nbsp;</div>
@@ -19,19 +19,19 @@
<h3><%= l(:label_attachment_plural) %></h3>
<table class="list">
<tbody>
-<% @revision.paths.each do |path| %>
+<% @changeset.changes.each do |change| %>
<tr class="<%= cycle 'odd', 'even' %>">
-<td><div class="square action_<%= path[:action] %>"></div> <%= path[:path] %></td>
+<td><div class="square action_<%= change.action %>"></div> <%= change.path %></td>
<td>
-<% if path[:action] == "M" %>
-<%= link_to 'View diff', :action => 'diff', :id => @project, :path => path[:path], :rev => @revision.identifier %>
+<% if change.action == "M" %>
+<%= link_to 'View diff', :action => 'diff', :id => @project, :path => change.path, :rev => @changeset.revision %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
-<p><%= lwr(:label_modification, @revision.paths.length) %></p>
+<p><%= lwr(:label_modification, @changeset.changes.length) %></p>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>
diff --git a/app/views/repositories/revisions.rhtml b/app/views/repositories/revisions.rhtml
index 8e300a9da..52564472d 100644
--- a/app/views/repositories/revisions.rhtml
+++ b/app/views/repositories/revisions.rhtml
@@ -5,36 +5,17 @@
<% end %>
</div>
-<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => @entry.kind, :revision => @rev } %></h2>
+<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
-<% if @entry.is_file? %>
+<% if @entry && @entry.is_file? %>
<h3><%=h @entry.name %></h3>
<p><%= link_to 'Download', {:action => 'entry', :id => @project, :path => @path, :rev => @rev, :format => 'raw' }, :class => "icon file" %> (<%= number_to_human_size @entry.size %>)</p>
<% end %>
<h3>Revisions</h3>
-<table class="list">
-<thead><tr>
-<th>#</th>
-<th><%= l(:field_author) %></th>
-<th><%= l(:label_date) %></th>
-<th><%= l(:field_description) %></th>
-<th></th>
-</tr></thead>
-<tbody>
-<% @revisions.each do |revision| %>
-<tr class="<%= cycle 'odd', 'even' %>">
-<th align="center"><%= link_to revision.identifier, :action => 'revision', :id => @project, :rev => revision.identifier %></th>
-<td align="center"><em><%=h revision.author %></em></td>
-<td align="center"><%= format_time(revision.time) %></td>
-<td style="width:70%"><%= textilizable(revision.message) %></td>
-<td align="center"><%= link_to 'Diff', :action => 'diff', :id => @project, :path => @path, :rev => revision.identifier if @entry.is_file? && revision != @revisions.last %></td>
-</tr>
-<% end %>
-</tbody>
-</table>
-<p><%= lwr(:label_modification, @revisions.length) %></p>
+<%= render :partial => 'revisions', :locals => {:project => @project, :path => @path, :changesets => @changesets, :entry => @entry }%>
+<p><%= lwr(:label_modification, @changesets.length) %></p>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>
diff --git a/app/views/repositories/show.rhtml b/app/views/repositories/show.rhtml
index 9c2adc929..e7d71cb79 100644
--- a/app/views/repositories/show.rhtml
+++ b/app/views/repositories/show.rhtml
@@ -1,17 +1,14 @@
<h2><%= l(:label_repository) %></h2>
-<h3><%= l(:label_revision_plural) %></h3>
-<% if @latest_revision %>
- <p><%= l(:label_latest_revision) %>:
- <%= link_to @latest_revision.identifier, :action => 'revision', :id => @project, :rev => @latest_revision.identifier %><br />
- <em><%= @latest_revision.author %>, <%= format_time(@latest_revision.time) %></em></p>
-<% end %>
-<p><%= link_to l(:label_view_revisions), :action => 'revisions', :id => @project %></p>
-
-
<h3><%= l(:label_browse) %></h3>
<%= render :partial => 'dir_list' %>
+<% unless @changesets.empty? %>
+<h3><%= l(:label_latest_revision_plural) %></h3>
+<%= render :partial => 'revisions', :locals => {:project => @project, :path => '', :changesets => @changesets, :entry => nil }%>
+<p><%= link_to l(:label_view_revisions), :action => 'revisions', :id => @project %></p>
+<% end %>
+
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>
<% end %> \ No newline at end of file
diff --git a/app/views/settings/edit.rhtml b/app/views/settings/edit.rhtml
index 4bdea07b9..223df7a9d 100644
--- a/app/views/settings/edit.rhtml
+++ b/app/views/settings/edit.rhtml
@@ -45,6 +45,9 @@
<p><label><%= l(:setting_feeds_limit) %></label>
<%= text_field_tag 'settings[feeds_limit]', Setting.feeds_limit, :size => 6 %></p>
+<p><label><%= l(:setting_autofetch_changesets) %></label>
+<%= check_box_tag 'settings[autofetch_changesets]', 1, Setting.autofetch_changesets? %><%= hidden_field_tag 'settings[autofetch_changesets]', 0 %></p>
+
</div>
<%= submit_tag l(:button_save) %>
</div>
diff --git a/config/settings.yml b/config/settings.yml
index c3292e516..5942f499b 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -50,3 +50,5 @@ host_name:
feeds_limit:
format: int
default: 15
+autofetch_changesets:
+ default: 1
diff --git a/db/migrate/034_create_changesets.rb b/db/migrate/034_create_changesets.rb
new file mode 100644
index 000000000..a78c8e36f
--- /dev/null
+++ b/db/migrate/034_create_changesets.rb
@@ -0,0 +1,16 @@
+class CreateChangesets < ActiveRecord::Migration
+ def self.up
+ create_table :changesets do |t|
+ t.column :repository_id, :integer, :null => false
+ t.column :revision, :integer, :null => false
+ t.column :committer, :string, :limit => 30
+ t.column :committed_on, :datetime, :null => false
+ t.column :comment, :text
+ end
+ add_index :changesets, [:repository_id, :revision], :unique => true, :name => :changesets_repos_rev
+ end
+
+ def self.down
+ drop_table :changesets
+ end
+end
diff --git a/db/migrate/035_create_changes.rb b/db/migrate/035_create_changes.rb
new file mode 100644
index 000000000..fa0cfac3f
--- /dev/null
+++ b/db/migrate/035_create_changes.rb
@@ -0,0 +1,16 @@
+class CreateChanges < ActiveRecord::Migration
+ def self.up
+ create_table :changes do |t|
+ t.column :changeset_id, :integer, :null => false
+ t.column :action, :string, :limit => 1, :default => "", :null => false
+ t.column :path, :string, :default => "", :null => false
+ t.column :from_path, :string
+ t.column :from_revision, :integer
+ end
+ add_index :changes, [:changeset_id], :name => :changesets_changeset_id
+ end
+
+ def self.down
+ drop_table :changes
+ end
+end
diff --git a/lang/de.yml b/lang/de.yml
index 9bd293559..3046dceb6 100644
--- a/lang/de.yml
+++ b/lang/de.yml
@@ -160,6 +160,7 @@ setting_host_name: Host Name
setting_text_formatting: Textformatierung
setting_wiki_compression: Wiki Historie komprimieren
setting_feeds_limit: Limit Feed Inhalt
+setting_autofetch_changesets: Autofetch SVN commits
label_user: Benutzer
label_user_plural: Benutzer
@@ -315,6 +316,7 @@ label_added: hinzugefügt
label_modified: geändert
label_deleted: gelöscht
label_latest_revision: Aktuelleste Revision
+label_latest_revision_plural: Aktuelleste Revisionen
label_view_revisions: Revisionen anzeigen
label_max_size: Maximale Größe
label_on: von
diff --git a/lang/en.yml b/lang/en.yml
index 72f75597b..6ed10aa88 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -160,6 +160,7 @@ setting_host_name: Host name
setting_text_formatting: Text formatting
setting_wiki_compression: Wiki history compression
setting_feeds_limit: Feed content limit
+setting_autofetch_changesets: Autofetch SVN commits
label_user: User
label_user_plural: Users
@@ -315,6 +316,7 @@ label_added: added
label_modified: modified
label_deleted: deleted
label_latest_revision: Latest revision
+label_latest_revision_plural: Latest revisions
label_view_revisions: View revisions
label_max_size: Maximum size
label_on: 'on'
diff --git a/lang/es.yml b/lang/es.yml
index a8eb1d0d3..a0a04562e 100644
--- a/lang/es.yml
+++ b/lang/es.yml
@@ -160,6 +160,7 @@ setting_host_name: Nombre de anfitrión
setting_text_formatting: Formato de texto
setting_wiki_compression: Compresión de la historia de Wiki
setting_feeds_limit: Feed content limit
+setting_autofetch_changesets: Autofetch SVN commits
label_user: Usuario
label_user_plural: Usuarios
@@ -315,6 +316,7 @@ label_added: agregado
label_modified: modificado
label_deleted: suprimido
label_latest_revision: La revisión más última
+label_latest_revision_plural: Latest revisions
label_view_revisions: Ver las revisiones
label_max_size: Tamaño máximo
label_on: en
diff --git a/lang/fr.yml b/lang/fr.yml
index dbce4b0c7..9a2195b11 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -160,6 +160,7 @@ setting_host_name: Nom d'hôte
setting_text_formatting: Formatage du texte
setting_wiki_compression: Compression historique wiki
setting_feeds_limit: Limite du contenu des flux RSS
+setting_autofetch_changesets: Récupération auto. des commits SVN
label_user: Utilisateur
label_user_plural: Utilisateurs
@@ -315,6 +316,7 @@ label_added: ajouté
label_modified: modifié
label_deleted: supprimé
label_latest_revision: Dernière révision
+label_latest_revision_plural: Dernières révisions
label_view_revisions: Voir les révisions
label_max_size: Taille maximale
label_on: sur
diff --git a/lang/it.yml b/lang/it.yml
index e6316e0dd..df89120ff 100644
--- a/lang/it.yml
+++ b/lang/it.yml
@@ -160,6 +160,7 @@ setting_host_name: Nome host
setting_text_formatting: Formattazione testo
setting_wiki_compression: Compressione di storia di Wiki
setting_feeds_limit: Feed content limit
+setting_autofetch_changesets: Autofetch SVN commits
label_user: Utente
label_user_plural: Utenti
@@ -315,6 +316,7 @@ label_added: aggiunto
label_modified: modificato
label_deleted: eliminato
label_latest_revision: Ultima versione
+label_latest_revision_plural: Latest revisions
label_view_revisions: Mostra versioni
label_max_size: Dimensione massima
label_on: 'on'
diff --git a/lang/ja.yml b/lang/ja.yml
index 83e681c0d..57d3df41b 100644
--- a/lang/ja.yml
+++ b/lang/ja.yml
@@ -161,6 +161,7 @@ setting_host_name: ホスト名
setting_text_formatting: テキストの書式
setting_wiki_compression: Wiki history compression
setting_feeds_limit: Feed content limit
+setting_autofetch_changesets: Autofetch SVN commits
label_user: ユーザ
label_user_plural: ユーザ
@@ -316,6 +317,7 @@ label_added: 追加された
label_modified: 変更された
label_deleted: 削除された
label_latest_revision: 最新リビジョン
+label_latest_revision_plural: Latest revisions
label_view_revisions: リビジョンを見る
label_max_size: 最大サイズ
label_on: 他