diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 129355851ba72720c432ebd18f3ec9c422cb2d77..410e24847273ff98965bb7a8411eb35b4ca8a224 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 acf03eb7bec3174ed5337c9133c505ca3a31e6de..94b81080f1da306d1b03a050eb2a1ba5b7455562 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 0000000000000000000000000000000000000000..d14f435a4a4d0c9342f59fd63402a0964a66bf3b
--- /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 0000000000000000000000000000000000000000..fa60f1db80988b4f6ddda578868e9ef5ce339e66
--- /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 16bee25a76473a59dbcc8cdb3fbea5febb5c0954..19b12f372eb24dfc39ee8f689af82cf910d85f9a 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 2fd907319602d52c674e671cab49aaa1262c4956..610b67a200cecf00ed6f3f081bcc095a2168c7fd 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 85f3f2966e207313b663933f780a2432637ce5e1..ded2271970fe7e621cfd0bbd0e774be04cc3f59e 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 b1d131aa9ebddaf9f906cbd9457cd7a7d42530f8..69bfa6028aa93ad5c5a313ae5864764bc2b5ef47 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 056effd475b7fa88f5b201a975502cc20802b5b0..8b35dce3b0bcf81a1fa86279f6a373d81eb55bd7 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 0000000000000000000000000000000000000000..d9bf7cc7c50f567f263ce07135cd6614a400d3ea
--- /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 22e965090d6f01fdb6237b9ba3d281ebbbe45504..3f28f3a9c3610d9b95872b768993e1bf00affe3b 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 8e300a9da3ad4a6969763097631410408cc1f39e..52564472d184b21686024a63062769fd5ffd5e77 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 9c2adc9293109f58e87f78d2dd8793ae04b98e69..e7d71cb7975222cf3df7b0aca069ea3b14d3a2e7 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 4bdea07b97eb4aa2498dd4988ccfe21bdf001049..223df7a9d639215c9b5e2023dfa8d623b1cb0f92 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 c3292e5164037d8e267489d07e0a23d0abdf2c57..5942f499b8d5d066c8ef47ad37db8c3a5d366e45 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 0000000000000000000000000000000000000000..a78c8e36f087dcf87e434a5ce83643292fed3dfb
--- /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 0000000000000000000000000000000000000000..fa0cfac3f759bf846b90bbea32e1b4a7ea315900
--- /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 9bd293559dd5596a7300b617c853b222ebb309e7..3046dceb64b6d29de22e0aab32f54e7928c21611 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 72f75597b8b54a3e8e7c297519461d1411a66d79..6ed10aa88c1a77cd59592a0903d95939b2ea6108 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 a8eb1d0d310ca3072713a9e770e870ede7646b7d..a0a04562e069aa93721df064bb0632760e85c32a 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 dbce4b0c7cc986f33fbffe8d023f9144f99e4e57..9a2195b119a0f7b3c7af0606071d7bce83388440 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 e6316e0dd7511f848c9cebd4f0e77067a12d6fba..df89120ff43bb9455583d9867785ef48b76d9474 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 83e681c0df446a5721c636cfad706b0f8227b148..57d3df41bfd5364273382473302c2ff9b3698513 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: ä»–