Commit 5bc38041 authored by jplang's avatar jplang

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: https://svn.redmine.org/redmine/trunk@377 e93f8b46-1217-0410-a6f0-8f06a7374b81
parent 5fea36de
......@@ -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
......
# 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
......
# 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
# 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
# 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={})
......
......@@ -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 %>
......
......@@ -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>
......
......@@ -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 %>
......
<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 @@
<% 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" %>
......
......@@ -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" %>
......
<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
......@@ -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>
......
......@@ -50,3 +50,5 @@ host_name:
feeds_limit:
format: int
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
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
......
......@@ -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'
......
......@@ -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
......
......@@ -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
......