Commit 16f9f50f authored by Jean-Philippe Lang's avatar Jean-Philippe Lang

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
parent 6f1dcdc0
...@@ -519,6 +519,17 @@ class ProjectsController < ApplicationController ...@@ -519,6 +519,17 @@ class ProjectsController < ApplicationController
@show_wiki_edits = 1 @show_wiki_edits = 1
end 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? render :layout => false if request.xhr?
end end
...@@ -581,10 +592,10 @@ class ProjectsController < ApplicationController ...@@ -581,10 +592,10 @@ class ProjectsController < ApplicationController
@question = params[:q] || "" @question = params[:q] || ""
@question.strip! @question.strip!
@all_words = params[:all_words] || (params[:submit] ? false : true) @all_words = params[:all_words] || (params[:submit] ? false : true)
@scope = params[:scope] || (params[:submit] ? [] : %w(issues news documents wiki) ) @scope = params[:scope] || (params[:submit] ? [] : %w(issues changesets news documents wiki) )
if !@question.empty? # tokens must be at least 3 character long
# tokens must be at least 3 character long @tokens = @question.split.uniq.select {|w| w.length > 2 }
@tokens = @question.split.uniq.select {|w| w.length > 2 } if !@tokens.empty?
# no more than 5 tokens to search for # no more than 5 tokens to search for
@tokens.slice! 5..-1 if @tokens.size > 5 @tokens.slice! 5..-1 if @tokens.size > 5
# strings used in sql like statement # strings used in sql like statement
...@@ -596,7 +607,10 @@ class ProjectsController < ApplicationController ...@@ -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.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.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.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(" ") @question = @tokens.join(" ")
else
@question = ""
end end
end end
......
# redMine - project management software # 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 # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
...@@ -20,9 +20,16 @@ class RepositoriesController < ApplicationController ...@@ -20,9 +20,16 @@ class RepositoriesController < ApplicationController
before_filter :find_project, :authorize before_filter :find_project, :authorize
def show def show
# get entries for the browse frame
@entries = @repository.scm.entries('') @entries = @repository.scm.entries('')
show_error and return unless @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 end
def browse def browse
...@@ -31,9 +38,11 @@ class RepositoriesController < ApplicationController ...@@ -31,9 +38,11 @@ class RepositoriesController < ApplicationController
end end
def revisions def revisions
@entry = @repository.scm.entry(@path, @rev) unless @path == ''
@revisions = @repository.scm.revisions(@path, @rev) @entry = @repository.scm.entry(@path, @rev)
show_error and return unless @entry && @revisions show_error and return unless @entry
end
@changesets = @repository.changesets_for_path(@path)
end end
def entry def entry
...@@ -45,9 +54,8 @@ class RepositoriesController < ApplicationController ...@@ -45,9 +54,8 @@ class RepositoriesController < ApplicationController
end end
def revision def revision
@revisions = @repository.scm.revisions '', @rev, @rev, :with_paths => true @changeset = @repository.changesets.find_by_revision(@rev)
show_error and return unless @revisions show_error and return unless @changeset
@revision = @revisions.first
end end
def diff def diff
......
# 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
# 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
# redMine - project management software # 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 # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
...@@ -17,6 +17,11 @@ ...@@ -17,6 +17,11 @@
class Repository < ActiveRecord::Base class Repository < ActiveRecord::Base
belongs_to :project 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_presence_of :url
validates_format_of :url, :with => /^(http|https|svn|file):\/\/.+/i validates_format_of :url, :with => /^(http|https|svn|file):\/\/.+/i
...@@ -27,10 +32,55 @@ class Repository < ActiveRecord::Base ...@@ -27,10 +32,55 @@ class Repository < ActiveRecord::Base
end end
def url=(str) def url=(str)
unless str == self.url super if root_url.blank?
self.attributes = {:root_url => nil } end
@scm = nil
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 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
end end
# redMine - project management software # 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 # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
...@@ -39,20 +39,27 @@ module SvnRepos ...@@ -39,20 +39,27 @@ module SvnRepos
@url @url
end end
# finds the root url of the svn repository # get info about the svn repository
def retrieve_root_url def info
cmd = "svn info --xml #{target('')}" cmd = "svn info --xml #{target('')}"
cmd << " --username #{@login} --password #{@password}" if @login cmd << " --username #{@login} --password #{@password}" if @login
root_url = nil info = nil
shellout(cmd) do |io| shellout(cmd) do |io|
begin begin
doc = REXML::Document.new(io) 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 rescue
end end
end end
return nil if $? && $?.exitstatus != 0 return nil if $? && $?.exitstatus != 0
root_url info
rescue Errno::ENOENT => e rescue Errno::ENOENT => e
return nil return nil
end end
...@@ -83,7 +90,7 @@ module SvnRepos ...@@ -83,7 +90,7 @@ module SvnRepos
:lastrev => Revision.new({ :lastrev => Revision.new({
:identifier => entry.elements['commit'].attributes['revision'], :identifier => entry.elements['commit'].attributes['revision'],
:time => Time.parse(entry.elements['commit'].elements['date'].text), :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 end
...@@ -112,13 +119,15 @@ module SvnRepos ...@@ -112,13 +119,15 @@ module SvnRepos
paths = [] paths = []
logentry.elements.each("paths/path") do |path| logentry.elements.each("paths/path") do |path|
paths << {:action => path.attributes['action'], paths << {:action => path.attributes['action'],
:path => path.text :path => path.text,
:from_path => path.attributes['copyfrom-path'],
:from_revision => path.attributes['copyfrom-rev']
} }
end end
paths.sort! { |x,y| x[:path] <=> y[:path] } paths.sort! { |x,y| x[:path] <=> y[:path] }
revisions << Revision.new({:identifier => logentry.attributes['revision'], 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), :time => Time.parse(logentry.elements['date'].text),
:message => logentry.elements['msg'].text, :message => logentry.elements['msg'].text,
:paths => paths :paths => paths
...@@ -171,7 +180,12 @@ module SvnRepos ...@@ -171,7 +180,12 @@ module SvnRepos
raise CommandFailed raise CommandFailed
end end
private private
def retrieve_root_url
info = self.info
info ? info.root_url : nil
end
def target(path) def target(path)
path ||= "" path ||= ""
base = path.match(/^\//) ? root_url : url base = path.match(/^\//) ? root_url : url
...@@ -207,6 +221,14 @@ module SvnRepos ...@@ -207,6 +221,14 @@ module SvnRepos
end end
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 class Entry
attr_accessor :name, :path, :kind, :size, :lastrev attr_accessor :name, :path, :kind, :size, :lastrev
def initialize(attributes={}) def initialize(attributes={})
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
<%= hidden_field_tag "repository_enabled", 0 %> <%= hidden_field_tag "repository_enabled", 0 %>
<div id="repository"> <div id="repository">
<% fields_for :repository, @project.repository, { :builder => TabularFormBuilder, :lang => current_language} do |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.text_field :login, :size => 30 %></p>
<p><%= repository.password_field :password, :size => 30 %></p> <p><%= repository.password_field :password, :size => 30 %></p>
<% end %> <% end %>
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
<%= select_year(@year, :prefix => "year", :discard_type => true) %></p> <%= select_year(@year, :prefix => "year", :discard_type => true) %></p>
<p> <p>
<%= check_box_tag 'show_issues', 1, @show_issues %><%= hidden_field_tag 'show_issues', 0, :id => nil %> <%=l(:label_issue_plural)%><br /> <%= 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_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_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 /> <%= 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 @@ ...@@ -39,6 +40,9 @@
<% elsif e.is_a? WikiContent.versioned_class %> <% 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 /> <%= 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 %> <% 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 %> <% end %>
</p></li> </p></li>
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
<% form_tag({:action => 'search', :id => @project}, :method => :get) do %> <% form_tag({:action => 'search', :id => @project}, :method => :get) do %>
<p><%= text_field_tag 'q', @question, :size => 30 %> <p><%= text_field_tag 'q', @question, :size => 30 %>
<%= check_box_tag 'scope[]', 'issues', (@scope.include? 'issues') %> <label><%= l(:label_issue_plural) %></label> <%= 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[]', 'news', (@scope.include? 'news') %> <label><%= l(:label_news_plural) %></label>
<%= check_box_tag 'scope[]', 'documents', (@scope.include? 'documents') %> <label><%= l(:label_document_plural) %></label> <%= check_box_tag 'scope[]', 'documents', (@scope.include? 'documents') %> <label><%= l(:label_document_plural) %></label>
<% if @project.wiki %> <% if @project.wiki %>
...@@ -36,6 +39,10 @@ ...@@ -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 /> <%=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 /> <%= highlight_tokens(e.content.text, @tokens) %><br />
<i><%= e.content.author ? e.content.author.name : "Anonymous" %>, <%= format_time(e.content.updated_on) %></i> <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 %> <% end %>
</p></li> </p></li>
<% end %> <% end %>
......
<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
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
<% end %> <% end %>
</div> </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> <p><em><%= @changeset.committer %>, <%= format_time(@changeset.committed_on) %></em></p>
<%= textilizable @revision.message %> <%= textilizable @changeset.comment %>
<div style="float:right;"> <div style="float:right;">
<div class="square action_A"></div> <div style="float:left;"><%= l(:label_added) %>&nbsp;</div> <div class="square action_A"></div> <div style="float:left;"><%= l(:label_added) %>&nbsp;</div>
...@@ -19,19 +19,19 @@ ...@@ -19,19 +19,19 @@
<h3><%= l(:label_attachment_plural) %></h3> <h3><%= l(:label_attachment_plural) %></h3>
<table class="list"> <table class="list">
<tbody> <tbody>
<% @revision.paths.each do |path| %> <% @changeset.changes.each do |change| %>
<tr class="<%= cycle 'odd', 'even' %>"> <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> <td>
<% if path[:action] == "M" %> <% if change.action == "M" %>
<%= link_to 'View diff', :action => 'diff', :id => @project, :path => path[:path], :rev => @revision.identifier %> <%= link_to 'View diff', :action => 'diff', :id => @project, :path => change.path, :rev => @changeset.revision %>
<% end %> <% end %>
</td> </td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
<p><%= lwr(:label_modification, @revision.paths.length) %></p> <p><%= lwr(:label_modification, @changeset.changes.length) %></p>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %> <%= stylesheet_link_tag "scm" %>
......
...@@ -5,36 +5,17 @@ ...@@ -5,36 +5,17 @@
<% end %> <% end %>
</div> </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> <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> <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 %> <% end %>
<h3>Revisions</h3> <h3>Revisions</h3>
<table class="list"> <%= render :partial => 'revisions', :locals => {:project => @project, :path => @path, :changesets => @changesets, :entry => @entry }%>
<thead><tr> <p><%= lwr(:label_modification, @changesets.length) %></p>
<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>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %> <%= stylesheet_link_tag "scm" %>
......
<h2><%= l(:label_repository) %></h2> <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> <h3><%= l(:label_browse) %></h3>
<%= render :partial => 'dir_list' %> <%= 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 %> <% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %> <%= stylesheet_link_tag "scm" %>
<% end %> <% end %>
\ No newline at end of file
...@@ -45,6 +45,9 @@ ...@@ -45,6 +45,9 @@
<p><label><%= l(:setting_feeds_limit) %></label> <p><label><%= l(:setting_feeds_limit) %></label>
<%= text_field_tag 'settings[feeds_limit]', Setting.feeds_limit, :size => 6 %></p> <%= 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> </div>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
</div> </div>
......
...@@ -50,3 +50,5 @@ host_name: ...@@ -50,3 +50,5 @@ host_name:
feeds_limit: feeds_limit:
format: int format: int
default: 15 default: 15
autofetch_changesets:
default: 1
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
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
...@@ -160,6 +160,7 @@ setting_host_name: Host Name ...@@ -160,6 +160,7 @@ setting_host_name: Host Name
setting_text_formatting: Textformatierung setting_text_formatting: Textformatierung
setting_wiki_compression: Wiki Historie komprimieren setting_wiki_compression: Wiki Historie komprimieren
setting_feeds_limit: Limit Feed Inhalt setting_feeds_limit: Limit Feed Inhalt
setting_autofetch_changesets: Autofetch SVN commits
label_user: Benutzer label_user: Benutzer
label_user_plural: Benutzer label_user_plural: Benutzer
...@@ -315,6 +316,7 @@ label_added: hinzugefügt ...@@ -315,6 +316,7 @@ label_added: hinzugefügt
label_modified: geändert label_modified: geändert
label_deleted: gelöscht label_deleted: gelöscht
label_latest_revision: Aktuelleste Revision label_latest_revision: Aktuelleste Revision
label_latest_revision_plural: Aktuelleste Revisionen
label_view_revisions: Revisionen anzeigen label_view_revisions: Revisionen anzeigen
label_max_size: Maximale Größe label_max_size: Maximale Größe
label_on: von label_on: von
......
...@@ -160,6 +160,7 @@ setting_host_name: Host name ...@@ -160,6 +160,7 @@ setting_host_name: Host name
setting_text_formatting: Text formatting setting_text_formatting: Text formatting
setting_wiki_compression: Wiki history compression setting_wiki_compression: Wiki history compression
setting_feeds_limit: Feed content limit setting_feeds_limit: Feed content limit
setting_autofetch_changesets: Autofetch SVN commits
label_user: User label_user: User
label_user_plural: Users label_user_plural: Users
...@@ -315,6 +316,7 @@ label_added: added ...@@ -315,6 +316,7 @@ label_added: added
label_modified: modified label_modified: modified
label_deleted: deleted label_deleted: deleted
label_latest_revision: Latest revision label_latest_revision: Latest revision
label_latest_revision_plural: Latest revisions
label_view_revisions: View revisions label_view_revisions: View revisions
label_max_size: Maximum size label_max_size: Maximum size
label_on: 'on' label_on: 'on'
......
...@@ -160,6 +160,7 @@ setting_host_name: Nombre de anfitrión ...@@ -160,6 +160,7 @@ setting_host_name: Nombre de anfitrión
setting_text_formatting: Formato de texto setting_text_formatting: Formato de texto
setting_wiki_compression: Compresión de la historia de Wiki setting_wiki_compression: Compresión de la historia de Wiki
setting_feeds_limit: Feed content limit setting_feeds_limit: Feed content limit
setting_autofetch_changesets: Autofetch SVN commits
label_user: Usuario label_user: Usuario
label_user_plural: Usuarios label_user_plural: Usuarios
...@@ -315,6 +316,7 @@ label_added: agregado ...@@ -315,6 +316,7 @@ label_added: agregado
label_modified: modificado label_modified: modificado
label_deleted: suprimido label_deleted: suprimido
label_latest_revision: La revisión más última label_latest_revision: La revisión más última
label_latest_revision_plural: Latest revisions
label_view_revisions: Ver las revisiones label_view_revisions: Ver las revisiones
label_max_size: Tamaño máximo label_max_size: Tamaño máximo
label_on: en label_on: en
......
...@@ -160,6 +160,7 @@ setting_host_name: Nom d'hôte ...@@ -160,6 +160,7 @@ setting_host_name: Nom d'hôte
setting_text_formatting: Formatage du texte setting_text_formatting: Formatage du texte
setting_wiki_compression: Compression historique wiki setting_wiki_compression: Compression historique wiki
setting_feeds_limit: Limite du contenu des flux RSS setting_feeds_limit: Limite du contenu des flux RSS
setting_autofetch_changesets: Récupération auto. des commits SVN
label_user: Utilisateur label_user: Utilisateur
label_user_plural: Utilisateurs label_user_plural: Utilisateurs
...@@ -315,6 +316,7 @@ label_added: ajouté ...@@ -315,6 +316,7 @@ label_added: ajouté
label_modified: modifié label_modified: modifié
label_deleted: supprimé label_deleted: supprimé
label_latest_revision: Dernière révision label_latest_revision: Dernière révision
label_latest_revision_plural: Dernières révisions
label_view_revisions: Voir les révisions label_view_revisions: Voir les révisions
label_max_size: Taille maximale label_max_size: Taille maximale
label_on: sur label_on: sur
......
...@@ -160,6 +160,7 @@ setting_host_name: Nome host ...@@ -160,6 +160,7 @@ setting_host_name: Nome host
setting_text_formatting: Formattazione testo setting_text_formatting: Formattazione testo
setting_wiki_compression: Compressione di storia di Wiki setting_wiki_compression: Compressione di storia di Wiki
setting_feeds_limit: Feed content limit setting_feeds_limit: Feed content limit
setting_autofetch_changesets: Autofetch SVN commits
label_user: Utente label_user: Utente
label_user_plural: Utenti label_user_plural: Utenti
...@@ -315,6 +316,7 @@ label_added: aggiunto ...@@ -315,6 +316,7 @@ label_added: aggiunto
label_modified: modificato label_modified: modificato
label_deleted: eliminato label_deleted: eliminato
label_latest_revision: Ultima versione label_latest_revision: Ultima versione
label_latest_revision_plural: Latest revisions
label_view_revisions: Mostra versioni label_view_revisions: Mostra versioni
label_max_size: Dimensione massima label_max_size: Dimensione massima
label_on: 'on' label_on: 'on'
......
...@@ -161,6 +161,7 @@ setting_host_name: ホスト名 ...@@ -161,6 +161,7 @@ setting_host_name: ホスト名
setting_text_formatting: テキストの書式 setting_text_formatting: テキストの書式
setting_wiki_compression: Wiki history compression setting_wiki_compression: Wiki history compression
setting_feeds_limit: Feed content limit setting_feeds_limit: Feed content limit
setting_autofetch_changesets: Autofetch SVN commits
label_user: ユーザ label_user: ユーザ
label_user_plural: ユーザ label_user_plural: ユーザ
...@@ -316,6 +317,7 @@ label_added: 追加された ...@@ -316,6 +317,7 @@ label_added: 追加された
label_modified: 変更された label_modified: 変更された
label_deleted: 削除された label_deleted: 削除された
label_latest_revision: 最新リビジョン label_latest_revision: 最新リビジョン
label_latest_revision_plural: Latest revisions
label_view_revisions: リビジョンを見る label_view_revisions: リビジョンを見る
label_max_size: 最大サイズ label_max_size: 最大サイズ
label_on: label_on:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment