add local cache repository for speed purpose (subversion) or for browsing a external repository

for SCM that can handle only local copy (git).


git-svn-id: http://redmine.rubyforge.org/svn/branches/nbc@1899 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Nicolas Chuche
2008-09-22 18:46:46 +00:00
parent 9b94342bc3
commit 89b8bf3dc5
41 changed files with 216 additions and 12 deletions

View File

@@ -146,7 +146,8 @@ module RepositoriesHelper
content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='repository[password]';",
:onchange => "this.name='repository[password]';"))
:onchange => "this.name='repository[password]';")) +
content_tag('p', form.check_box(:cache))
end
def darcs_field_tags(form, repository)

View File

@@ -1,3 +1,4 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
@@ -19,14 +20,14 @@ class Repository < ActiveRecord::Base
belongs_to :project
has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
has_many :changes, :through => :changesets
# Raw SQL to delete changesets and changes in the database
# has_many :changesets, :dependent => :destroy is too slow for big repositories
before_destroy :clear_changesets
# Checks if the SCM is enabled when creating a repository
validate_on_create { |r| r.errors.add(:type, :activerecord_error_invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
# Removes leading and trailing whitespace
def url=(arg)
write_attribute(:url, arg ? arg.to_s.strip : nil)
@@ -38,7 +39,8 @@ class Repository < ActiveRecord::Base
end
def scm
@scm ||= self.scm_adapter.new url, root_url, login, password
init_cache if cache_path.blank? and respond_to?(:init_cache)
@scm ||= self.scm_adapter.new(url, root_url, login, password, cache_path)
update_attribute(:root_url, @scm.root_url) if root_url.blank?
@scm
end
@@ -122,9 +124,28 @@ class Repository < ActiveRecord::Base
rescue
nil
end
def remove_cache
scm.remove_cache if cache
end
def create_or_sync_cache
begin
scm.create_cache
rescue => e
# clean if problem in creation
scm.remove_cache
end
scm.synchronize
end
private
def repositories_cache_directory
dir = Setting.repositories_cache_directory.gsub(/^([^#{File::SEPARATOR}].*)/, RAILS_ROOT + '/\1')
return dir if File.directory?(dir)
end
def before_save
# Strips url and root_url
url.strip!

View File

@@ -21,6 +21,17 @@ class Repository::Git < Repository
attr_protected :root_url
validates_presence_of :url
before_destroy :remove_cache
def init_cache
return unless dir = repositories_cache_directory
# we need to use a cache only if repository isn't local and dir exists
if url[/^(rsync|https?|git|ssh):\/\//]
update_attribute(:cache_path, dir + project.identifier)
update_attribute(:cache, true)
end
end
def scm_adapter
Redmine::Scm::Adapters::GitAdapter
end
@@ -36,6 +47,8 @@ class Repository::Git < Repository
end
def fetch_changesets
create_or_sync_cache if cache
scm_info = scm.info
if scm_info
# latest revision found in database

View File

@@ -22,6 +22,16 @@ class Repository::Subversion < Repository
validates_presence_of :url
validates_format_of :url, :with => /^(http|https|svn|svn\+ssh|file):\/\/.+/i
before_destroy :remove_cache
def init_cache
return unless dir = repositories_cache_directory
# we need to use a cache only if repository isn't local and dir exists
if cache and url[/^(svn|https?|svn\+ssh):\/\//]
update_attribute(:cache_path, dir + project.identifier)
end
end
def scm_adapter
Redmine::Scm::Adapters::SubversionAdapter
end
@@ -41,6 +51,8 @@ class Repository::Subversion < Repository
end
def fetch_changesets
create_or_sync_cache if cache
scm_info = scm.info
if scm_info
# latest revision found in database

View File

@@ -161,4 +161,14 @@ private
setting = find_by_name(name)
setting ||= new(:name => name, :value => @@available_settings[name]['default']) if @@available_settings.has_key? name
end
protected
def validate
if self.name.to_s == "repositories_cache_directory" and not File.directory?(self.value.to_s)
logger.error("Le repertoire #{self.value.to_s} n'existe pas")
errors.add("Le repertoire #{self.value.to_s} n'existe pas")
end
end
end

View File

@@ -17,6 +17,9 @@
<p><label><%= l(:setting_repositories_encodings) %></label>
<%= text_field_tag 'settings[repositories_encodings]', Setting.repositories_encodings, :size => 60 %><br /><em><%= l(:text_comma_separated) %></em></p>
<p><label><%= l(:setting_repositories_cache_directory) %></label>
<%= text_field_tag 'settings[repositories_cache_directory]', Setting.repositories_cache_directory, :size => 60 %></p>
<p><label><%= l(:setting_commit_logs_encoding) %></label>
<%= select_tag 'settings[commit_logs_encoding]', options_for_select(Setting::ENCODINGS, Setting.commit_logs_encoding) %></p>
</div>

View File

@@ -124,6 +124,8 @@ sequential_project_identifiers:
# multiple values accepted, comma separated
repositories_encodings:
default: ''
repositories_cache_directory:
default: 'tmp/scm/'
# encoding used to convert commit logs to UTF-8
commit_logs_encoding:
default: 'UTF-8'

View File

@@ -0,0 +1,12 @@
class AddLocalCopy < ActiveRecord::Migration
def self.up
add_column :repositories, :cache, :boolean
add_column :repositories, :cache_path, :string, :limit => 255, :default => ""
end
def self.down
remove_column :repositories, :cache
remove_column :repositories, :cache_path
end
end

View File

@@ -640,3 +640,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -642,3 +642,5 @@ field_comments: Comment
setting_commit_logs_encoding: Commit messages encoding
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -645,3 +645,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ default_activity_development: Udvikling
enumeration_issue_priorities: Sagsprioriteter
enumeration_doc_categories: Dokumentkategorier
enumeration_activities: Aktiviteter (tidsregistrering)
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ default_activity_development: Entwicklung
enumeration_issue_priorities: Ticket-Prioritäten
enumeration_doc_categories: Dokumentenkategorien
enumeration_activities: Aktivitäten (Zeiterfassung)
field_cache: Local cache
setting_repositories_cache_directory: Cache directory for repositories

View File

@@ -184,6 +184,7 @@ field_searchable: Searchable
field_default_value: Default value
field_comments_sorting: Display comments
field_parent_title: Parent page
field_cache: Local cache
setting_app_title: Application title
setting_app_subtitle: Application subtitle
@@ -221,6 +222,7 @@ setting_enabled_scm: Enabled SCM
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
setting_sequential_project_identifiers: Generate sequential project identifiers
setting_repositories_cache_directory: Cache directory for repositories
project_module_issue_tracking: Issue tracking
project_module_time_tracking: Time tracking

View File

@@ -643,3 +643,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -640,3 +640,5 @@ setting_commit_logs_encoding: Commit messages encoding
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -221,6 +221,7 @@ setting_enabled_scm: SCM activés
setting_mail_handler_api_enabled: "Activer le WS pour la réception d'emails"
setting_mail_handler_api_key: Clé de protection de l'API
setting_sequential_project_identifiers: Générer des identifiants de projet séquentiels
setting_repositories_cache_directory: Répertoire du cache pour les dépôts
project_module_issue_tracking: Suivi des demandes
project_module_time_tracking: Suivi du temps passé
@@ -641,3 +642,4 @@ enumeration_issue_priorities: Priorités des demandes
enumeration_doc_categories: Catégories des documents
enumeration_activities: Activités (suivi du temps)
notice_unable_delete_version: Unable to delete version
field_cache: Cache local

View File

@@ -640,3 +640,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ setting_sequential_project_identifiers: Szekvenciális projekt azonosítók gene
notice_unable_delete_version: A verziót nem lehet törölni
label_renamed: átnevezve
label_copied: lemásolva
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -640,3 +640,5 @@ setting_sequential_project_identifiers: Genera progetti con identificativi in se
notice_unable_delete_version: Impossibile cancellare la versione
label_renamed: rinominato
label_copied: copiato
field_cache: Local cache
setting_repositories_cache_directory: Cache directory for repositories

View File

@@ -641,3 +641,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -640,3 +640,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -642,3 +642,5 @@ button_quote: Cituoti
notice_unable_delete_version: Neimanoma panaikinti versiją
label_renamed: pervardintas
label_copied: nukopijuotas
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -643,3 +643,5 @@ setting_sequential_project_identifiers: Genereer sequentiele project identiteite
notice_unable_delete_version: Onmogelijk om deze versie te verwijderen.
label_renamed: hernoemt
label_copied: gekopieerd
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -640,3 +640,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ enumeration_activities: Atividades (time tracking)
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -640,3 +640,5 @@ setting_sequential_project_identifiers: Gerar identificador sequencial
notice_unable_delete_version: Impossível apagar esta versão
label_renamed: renomeado
label_copied: copiado
field_cache: Local cache
setting_repositories_cache_directory: Cache directory for repositories

View File

@@ -640,3 +640,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -673,3 +673,5 @@ text_user_wrote: '%s написал(а):'
text_wiki_destroy_confirmation: Вы уверены, что хотите удалить данную Wiki и все содержимое?
text_workflow_edit: Выберите роль и трекер для редактирования последовательности состояний
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -643,3 +643,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ general_csv_decimal_separator: '.'
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -642,3 +642,5 @@ setting_sequential_project_identifiers: Generate sequential project identifiers
notice_unable_delete_version: Unable to delete version
label_renamed: renamed
label_copied: copied
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ default_activity_development: 開發
enumeration_issue_priorities: 項目優先權
enumeration_doc_categories: 文件分類
enumeration_activities: 活動 (時間追蹤)
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -641,3 +641,5 @@ default_activity_development: 开发
enumeration_issue_priorities: 问题优先级
enumeration_doc_categories: 文档类别
enumeration_activities: 活动(时间跟踪)
setting_repositories_cache_directory: Cache directory for repositories
field_cache: Local cache

View File

@@ -47,11 +47,17 @@ module Redmine
end
end
def initialize(url, root_url=nil, login=nil, password=nil)
def initialize(url, root_url=nil, login=nil, password=nil, cache_path=nil)
@url = url
@login = login if login && !login.empty?
@password = (password || "") if @login
@root_url = root_url.blank? ? retrieve_root_url : root_url
if cache_path.blank?
@root_url = root_url.blank? ? retrieve_root_url : root_url
else
@orig_url = @url
@url = @root_url = cache_path
end
end
def adapter_name
@@ -145,7 +151,22 @@ module Redmine
end
end
def remove_cache
remove_directory(@root_url) if not @orig_url.blank? and File.directory?(@root_url)
end
private
def remove_directory(path)
Dir.entries(path).each do |f|
next if %w[. ..].include?(f)
name = "#{path}/#{f}"
File.directory?(name) ? remove_directory(name) : File.unlink(name)
end
Dir.rmdir path
rescue Errno::ENOENT => e
logger.error(e.to_s)
end
def retrieve_root_url
info = self.info
info ? info.root_url : nil
@@ -194,7 +215,7 @@ module Redmine
self.class.strip_credential(cmd)
end
end
class Entries < Array
def sort_by_name
sort {|x,y|

View File

@@ -25,6 +25,11 @@ module Redmine
# Git executable name
GIT_BIN = "git"
def initialize(url, root_url=nil, login=nil, password=nil, cache_path=nil)
super(url, root_url, login, password, cache_path)
@url += "/.git/" unless cache_path.blank?
end
# Get the revision of a particuliar file
def get_rev (rev,path)
@@ -263,9 +268,20 @@ module Redmine
return nil if $? && $?.exitstatus != 0
cat
end
def create_cache
cmd = "#{GIT_BIN} clone #{@orig_url} #{@root_url}"
shellout(cmd) { |io| io.read }
end
def synchronize
return unless File.directory?(@url)
cmd = "#{GIT_BIN} --git-dir #{@url} pull"
shellout(cmd)
end
end
end
end
end

View File

@@ -25,7 +25,7 @@ module Redmine
# SVN executable name
SVN_BIN = "svn"
class << self
def client_version
@@client_version ||= (svn_binary_version || [])
@@ -213,6 +213,18 @@ module Redmine
return nil if $? && $?.exitstatus != 0
blame
end
def create_cache
return if @orig_url.blank?
cmd = "#{SVN_BIN} checkout --non-interactive #{@orig_url} #{@root_url}"
shellout(cmd) { |io| io.read }
end
def synchronize
return if @orig_url.blank?
cmd = "#{SVN_BIN} update --non-interactive"
Dir.chdir(@root_url) { shellout(cmd) { |io| io.read } }
end
private
@@ -222,6 +234,7 @@ module Redmine
str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
str
end
end
end
end

View File

@@ -127,4 +127,26 @@ class RepositoryTest < Test::Unit::TestCase
assert_equal ':pserver:login:password@host:/path/to/the/repository', repository.url
assert_equal 'foo', repository.root_url
end
def test_local_cache
dir = Setting.repositories_cache_directory.gsub(/^([^#{File::SEPARATOR}].*)/, RAILS_ROOT + '/\1')
project = projects(:projects_001)
repository = Repository::Git.new(:project => Project.find_by_name(project.name), :url => "git://github.com/olabini/paipr.git")
repository.scm
assert_equal(dir + project.identifier, repository.cache_path)
repository = Repository::Git.new(:project => Project.find(:first), :url => "/var/cache/git/paipr/.git")
repository.init_cache
assert repository.cache_path.blank?
repository = Repository::Subversion.new(:project => Project.find(:first), :url => "svn://github.com/olabini/paipr.git")
repository.init_cache
assert repository.cache_path.blank?
repository = Repository::Subversion.new(:project => Project.find_by_name(project.name), :url => "svn://github.com/olabini/paipr.git", :cache => true)
repository.init_cache
assert_equal(dir + project.identifier, repository.cache_path)
end
end