Index: test/functional/admin/repositories_controller_test.rb =================================================================== --- test/functional/admin/repositories_controller_test.rb (revision 456) +++ test/functional/admin/repositories_controller_test.rb (working copy) @@ -115,6 +115,23 @@ xhr :get, :repository_test, :path => RAILS_ROOT + '/tmp/svn_test' assert_response :success assert_match /Success/, @response.body + + xhr :get, :repository_test, :path => "file://#{RAILS_ROOT}/tmp/svn_test" + assert_response :success + assert_match /Success/, @response.body + + xhr :get, :repository_test, :path => "file://#{RAILS_ROOT}/invalid_path" + assert_response :success + assert_match /Failure.+?not exist/, @response.body + + retro_google_code = "http://retrospectiva.googlecode.com" + xhr :get, :repository_test, :path => "#{retro_google_code}/invalid_path" + assert_response :success + assert_match /Failure.+?not contain a valid repository/, @response.body + + xhr :get, :repository_test, :path => "#{retro_google_code}/svn" + assert_response :success + assert_match /Success/, @response.body end def test_repository_test_access Index: app/models/repository/subversion/node.rb =================================================================== --- app/models/repository/subversion/node.rb (revision 456) +++ app/models/repository/subversion/node.rb (working copy) @@ -6,26 +6,8 @@ def initialize(repos, path, selected_revision = nil, skip_content = false) super(repos, path, selected_revision) - - begin - root = repos.fs.root(self.selected_revision) - rescue # no such revision - raise Repository::RevisionNotFound - end - - @node_type = root.check_path(self.path) - raise_invalid_node_error! unless exists? - @size = dir? ? 0 : root.file_length(path).to_i - @revision = root.node_created_rev(path) - @proplist = repos.fs.proplist(revision) - - @properties = root.node_proplist(path) - @sub_node_names = dir? ? root.dir_entries(path).keys : [] - @mime_type = verify_mime_type - - @content = read_content(root) unless skip_content - root.close + retrieve_info(skip_content) end def revision @@ -33,15 +15,15 @@ end def author - @proplist[Svn::Core::PROP_REVISION_AUTHOR] || '' + @author || '' end def date - @proplist[Svn::Core::PROP_REVISION_DATE] + @date end def log - @proplist[Svn::Core::PROP_REVISION_LOG] || '' + @log || '' end def dir? @@ -50,7 +32,8 @@ def sub_nodes @sub_node_names.collect do |name| - self.class.new(repos, File.join(path, name), selected_revision, true) + sub_path = path.blank? ? name : "#{path}/#{name}" + self.class.new(repos, sub_path, selected_revision, true) end.sort_by { |node| [node.content_code, node.name.downcase] } end @@ -91,12 +74,56 @@ end end - def read_content(root) + def read_content(url, peg_rev) return nil if dir? - content = root.file_contents(path) {|s| s.read } + content = @repos.context.cat(url, peg_rev, peg_rev) charset = mime_type.blank? ? nil : mime_type.slice(%r{charset=([A-Za-z0-9\-_]+)}, 1) convert_to_utf8(content, charset || 'utf-8') end - + + private + def retrieve_info(skip_content) + url = @repos.url(path) + begin + @repos.info(path, selected_revision) do |info| + @node_type = info.kind + @revision = info.last_changed_rev + end + rescue Svn::Error::RA_ILLEGAL_URL + raise_invalid_node_error! + end + + raise_invalid_node_error! unless exists? + + peg_rev = nil + begin + @repos.context.log(url, @revision, selected_revision, 1, + true, true, selected_revision) do |*args| + changed_paths, rev, author, date, message = args + peg_rev = rev + @author = author + @date = date + @log = message + end + rescue Svn::Error::FS_NOT_FOUND + raise_invalid_node_error! + end + + @properties = {} + properties = @repos.context.prop_list(url, peg_rev, peg_rev, false)[0] + @properties = properties.props if properties + @sub_node_names = [] + @repos.context.list(url, peg_rev, peg_rev) do |*args| + path, dirent, lock, abs_path = args + if path.blank? + @size = dirent.size + else + @sub_node_names << path + end + end + @mime_type = verify_mime_type + + @content = read_content(url, peg_rev) unless skip_content + end end Index: app/models/repository/subversion.rb =================================================================== --- app/models/repository/subversion.rb (revision 456) +++ app/models/repository/subversion.rb (working copy) @@ -2,35 +2,49 @@ # Copyright (C) 2008 Dimitrij Denissenko # Please read LICENSE document for more information. #++ + +require 'pathname' + class Repository::Subversion < Repository::Abstract def latest_revision - @latest_revision ||= fs.youngest_rev + @latest_revision ||= info { |_info| _info.rev } end def unified_diff(path, revision_a, revision_b) diff = '' begin - root_a = self.fs.root(revision_a.to_i) - root_b = self.fs.root(revision_b.to_i) - - differ = Svn::Fs::FileDiff.new(root_a, path, root_b, path) - unless differ.binary? - caption_a = "Revision #{root_a.node_created_rev(path)}" - caption_b = "Revision #{root_b.node_created_rev(path)}" - diff = differ.unified(caption_a, caption_b) - differ = nil + diff_file = Tempfile.new("diff") + diff_error_file = Tempfile.new("diff-error") + target_url = url(path) + context.diff([], target_url, revision_a.to_i, target_url, revision_b.to_i, + diff_file.path, diff_error_file.path) + diff_file.open + diff = diff_file.read + diff_start = diff.index(/^@/) + if diff_start + diff = ["--- Revision #{revision_a}", + "+++ Revision #{revision_b}", + diff[diff_start..-1]].join("\n") + else + diff = "" end - root_a.close - root_b.close rescue end diff end def history(path, revision = nil) - fs.history(path, 0, (revision || latest_revision).to_i).map(&:last) - rescue Svn::Error::FS_NOT_FOUND # or Svn::Error? + revision ||= latest_revision + revision = revision.to_i + revisions = [] + context.log(url(path), 0, revision, 0, true, true, revision) do |*args| + changed_paths, rev, author, date, message = args + revisions << rev + end + revisions.reverse + rescue Svn::Error::FS_NOT_FOUND, Svn::Error::CLIENT_UNRELATED_RESOURCES + # or Svn::Error? [] end @@ -118,15 +132,27 @@ end end - def fs - self.class.reload_repository_fs_cache unless self.class.repository_fs_cache[self.id] - self.class.repository_fs_cache[self.id] || self.open_fs + def context + @context ||= make_context end - def open_fs - Svn::Repos.open(self.path.chomp('/')).fs + def info(path = nil, revision = nil, &block) + result = nil + context.info(url(path), revision, revision) do |path, info| + result = yield(info) + end + result end + def url(path = nil) + @url ||= ensure_url + if path.blank? + @url + else + "#{@url}/#{path.chomp('/')}" + end + end + protected @@repository_fs_cache = {} @@ -147,33 +173,56 @@ private def create_changeset(rev = self.latest_revision) - root = self.fs.root(rev) - base_root = self.fs.root(rev - 1) - proplist = self.fs.proplist(rev) - - ce = Svn::Delta::ChangedEditor.new(root, base_root) - base_root.dir_delta('', '', root, '', ce) - node_data = {} - node_data[:added] = ce.added_dirs + ce.added_files - node_data[:updated] = ce.updated_dirs + ce.updated_files - node_data[:copied] = ce.copied_dirs + ce.copied_files - node_data[:deleted] = ce.deleted_dirs + ce.deleted_files - - root.close - base_root.close - + changeset = Changeset.new(:revision => rev, :repository => self) + node_data = {:added => [], :updated => [], :copied => [], :deleted => []} + context.log(url, rev, rev - 1, 1, true, true) do |*args| + changed_paths, rev, author, date, message = args + changeset.author = author + changeset.log = message + changeset.revised_at = date + changed_paths.each do |path, changed_path| + path = path[1..-1] + case changed_path.action + when "A" + if changed_path.copyfrom_path + node_data[:copied] << [path, changed_path.copyfrom_path] + else + node_data[:added] << path + end + when "D" + node_data[:deleted] << path + when "R", "M" + node_data[:updated] << path + end + end + end node_data[:moved] = node_data[:copied].inject([]) do |result, cnode| result << cnode if node_data[:deleted].delete(cnode[1]) result end node_data[:copied] = node_data[:copied] - node_data[:moved] - changeset = Changeset.new(:revision => rev, :repository => self) - changeset.author = proplist[Svn::Core::PROP_REVISION_AUTHOR] - changeset.log = proplist[Svn::Core::PROP_REVISION_LOG] - changeset.revised_at = proplist[Svn::Core::PROP_REVISION_DATE] - [changeset, node_data] end + + def ensure_url + _path = path.chomp('/') + if URI.parse(_path).scheme + _path + else + "file://#{Pathname.new(_path).realpath}" + end + end + + def make_context + context = Svn::Client::Context.new + context.add_username_prompt_provider(0) do |*args| + cred, realm, user_name, may_save = args + # cred.username = name + cred.may_save = false + end + # context.auth_baton[Svn::Core::AUTH_PARAM_DEFAULT_USERNAME] = name + context + end + end - Index: app/controllers/admin/repositories_controller.rb =================================================================== --- app/controllers/admin/repositories_controller.rb (revision 456) +++ app/controllers/admin/repositories_controller.rb (working copy) @@ -2,6 +2,9 @@ # Copyright (C) 2007 Dimitrij Denissenko # Please read LICENSE document for more information. #++ + +require 'uri' + class Admin::RepositoriesController < AdminAreaController verify_action :repository_test, :xhr => true @@ -35,9 +38,17 @@ @result = _('Subversion is not enabled.') if ENABLE_SUBVERSION path = params[:path].blank? ? '' : params[:path] - if !File.exists?(path) + local_path = true + begin + uri = URI.parse(path) + scheme = uri.scheme + local_path = scheme.nil? || scheme == "file" + path = uri.path if local_path + rescue URI::Error + end + if local_path && !File.exists?(path) @result = _('Failure! Path does not exist.') - elsif !File.readable?(path) || !File.executable?(path) + elsif local_path && (!File.readable?(path) || !File.executable?(path)) @result = _('Failure! You are not permitted to browse this path.') else repos = Repository::Subversion.new(:name => 'temp', :path => params[:path])