| # ==================================================================== |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| # ==================================================================== |
| |
| require "English" |
| require "tempfile" |
| require "svn/error" |
| require "svn/util" |
| require "svn/delta" |
| require "svn/ext/fs" |
| |
| module Svn |
| module Fs |
| Util.set_constants(Ext::Fs, self) |
| Util.set_methods(Ext::Fs, self) |
| |
| @@fs_pool = Svn::Core::Pool.new |
| initialize(@@fs_pool) |
| |
| class << self |
| def modules |
| print_modules("") |
| end |
| end |
| |
| @@alias_targets = %w(create delete open hotcopy recover) |
| class << self |
| @@alias_targets.each do |target| |
| alias_method "_#{target}", target |
| end |
| end |
| @@alias_targets.each do |target| |
| alias_method "_#{target}", target |
| end |
| @@alias_targets = nil |
| |
| module_function |
| def create(path, config={}, &block) |
| _create(path, config, &block) |
| end |
| |
| def delete(path) |
| Fs.delete_fs(path) |
| end |
| |
| def open(path, config={}, &block) |
| _open(path, config, &block) |
| end |
| |
| def hotcopy(src, dest, clean=false) |
| _hotcopy(src, dest, clean) |
| end |
| |
| def recover(path, &cancel_func) |
| _recover(path, cancel_func) |
| end |
| |
| FileSystem = SWIG::TYPE_p_svn_fs_t |
| class FileSystem |
| class << self |
| # For backward compatibility |
| def create(*args, &block) |
| Fs.create(*args, &block) |
| end |
| |
| def delete(*args, &block) |
| Fs.delete(*args, &block) |
| end |
| |
| def open(*args, &block) |
| Fs.open(*args, &block) |
| end |
| alias new open |
| |
| def hotcopy(*args, &block) |
| Fs.hotcopy(*args, &block) |
| end |
| |
| def recover(*args, &block) |
| Fs.recover(*args, &block) |
| end |
| end |
| |
| def set_warning_func(&func) |
| Fs.set_warning_func_wrapper(self, func) |
| end |
| |
| def path |
| Fs.path(self) |
| end |
| |
| def open_txn(name) |
| Fs.open_txn(self, name) |
| end |
| |
| def purge_txn(id) |
| Fs.purge_txn(self, id) |
| end |
| |
| def transaction(rev=nil, flags=0) |
| txn = Fs.begin_txn2(self, rev || youngest_rev, flags) |
| |
| if block_given? |
| yield(txn) |
| txn.commit if transactions.include?(txn.name) |
| else |
| txn |
| end |
| end |
| |
| def youngest_revision |
| Fs.youngest_rev(self) |
| end |
| alias_method :youngest_rev, :youngest_revision |
| |
| def deleted_revision(path, start_rev, end_rev) |
| Repos.deleted_rev(self, path, start_rev, end_rev) |
| end |
| alias_method :deleted_rev, :deleted_revision |
| |
| def prop(name, rev=nil) |
| value = Fs.revision_prop(self, rev || youngest_rev, name) |
| if value and name == Svn::Core::PROP_REVISION_DATE |
| Time.parse_svn_format(value) |
| else |
| value |
| end |
| end |
| |
| def set_prop(name, value, rev=nil) |
| Fs.change_rev_prop(self, rev || youngest_rev, name, value) |
| end |
| |
| def proplist(rev=nil) |
| list = Fs.revision_proplist(self, rev || youngest_rev) |
| date_str = list[Svn::Core::PROP_REVISION_DATE] |
| if date_str |
| list[Svn::Core::PROP_REVISION_DATE] = Time.parse_svn_format(date_str) |
| end |
| list |
| end |
| |
| def transactions |
| Fs.list_transactions(self) |
| end |
| |
| def root(rev=nil) |
| Fs.revision_root(self, rev || youngest_rev) |
| end |
| |
| def access |
| Fs.get_access(self) |
| end |
| |
| def access=(new_access) |
| Fs.set_access(self, new_access) |
| end |
| |
| def deltify_revision(rev=nil) |
| Fs.deltify_revision(self, rev || youngest_rev) |
| end |
| |
| def uuid |
| Fs.get_uuid(self) |
| end |
| |
| def uuid=(new_uuid) |
| Fs.set_uuid(self, new_uuid) |
| end |
| |
| def lock(path, token=nil, comment=nil, dav_comment=true, |
| expiration_date=nil, current_rev=nil, steal_lock=false) |
| if expiration_date |
| expiration_date = expiration_date.to_apr_time |
| else |
| expiration_date = 0 |
| end |
| current_rev ||= youngest_rev |
| Fs.lock(self, path, token, comment, dav_comment, |
| expiration_date, current_rev, steal_lock) |
| end |
| |
| def unlock(path, token, break_lock=false) |
| Fs.unlock(self, path, token, break_lock) |
| end |
| |
| def generate_lock_token |
| Fs.generate_lock_token(self) |
| end |
| |
| def get_lock(path) |
| Fs.get_lock(self, path) |
| end |
| |
| def get_locks(path) |
| locks = [] |
| receiver = Proc.new do |lock| |
| locks << lock |
| yield(lock) if block_given? |
| end |
| Fs.get_locks(self, path, receiver) |
| locks |
| end |
| |
| def history(path, start_rev, end_rev, |
| cross_copies=true, authz_read_func=nil) |
| hist = [] |
| history_func = Proc.new do |path, revision| |
| yield(path, revision) if block_given? |
| hist << [path, revision] |
| end |
| Repos.history2(self, path, history_func, |
| authz_read_func, start_rev, end_rev, |
| cross_copies) |
| hist |
| end |
| |
| def trace_node_locations(fs_path, location_revisions, |
| peg_rev=nil, &authz_read_func) |
| peg_rev ||= youngest_rev |
| Repos.trace_node_locations(self, fs_path, peg_rev, |
| location_revisions, |
| authz_read_func) |
| end |
| end |
| |
| Access = SWIG::TYPE_p_svn_fs_access_t |
| class Access |
| class << self |
| def new(username) |
| Fs.create_access(username) |
| end |
| end |
| |
| def username |
| Fs.access_get_username(self) |
| end |
| |
| def add_lock_token(token) |
| Fs.access_add_lock_token(self, token) |
| end |
| end |
| |
| Transaction = SWIG::TYPE_p_svn_fs_txn_t |
| class Transaction |
| |
| def name |
| Fs.txn_name(self) |
| end |
| |
| def prop(name) |
| value = Fs.txn_prop(self, name) |
| if name == Svn::Core::PROP_REVISION_DATE and value |
| value = Time.parse_svn_format(value) |
| end |
| value |
| end |
| |
| def set_prop(name, value, validate=true) |
| if validate |
| Repos.fs_change_txn_prop(self, name, value) |
| else |
| Fs.change_txn_prop(self, name, value) |
| end |
| end |
| |
| def base_revision |
| Fs.txn_base_revision(self) |
| end |
| |
| def root |
| Fs.txn_root(self) |
| end |
| |
| def proplist |
| list = Fs.txn_proplist(self) |
| date_str = list[Svn::Core::PROP_REVISION_DATE] |
| if list[Svn::Core::PROP_REVISION_DATE] |
| list[Svn::Core::PROP_REVISION_DATE] = Time.parse_svn_format(date_str) |
| end |
| list |
| end |
| |
| def abort |
| Fs.abort_txn(self) |
| end |
| |
| def commit |
| result = Fs.commit_txn(self) |
| if result.is_a?(Array) |
| result |
| else |
| [nil, result] |
| end |
| end |
| end |
| |
| |
| Root = SWIG::TYPE_p_svn_fs_root_t |
| class Root |
| attr_reader :editor |
| |
| def dir?(path) |
| Fs.is_dir(self, path) |
| end |
| |
| def file?(path) |
| Fs.is_file(self, path) |
| end |
| |
| def base_revision |
| Fs.txn_root_base_revision(self) |
| end |
| |
| def revision |
| Fs.revision_root_revision(self) |
| end |
| |
| def name |
| Fs.txn_root_name(self) |
| end |
| |
| def fs |
| Fs.root_fs_wrapper(self) |
| end |
| |
| def node_id(path) |
| Fs.node_id(self, path) |
| end |
| |
| def node_created_rev(path) |
| Fs.node_created_rev(self, path) |
| end |
| |
| def node_created_path(path) |
| Fs.node_created_path(self, path) |
| end |
| |
| def node_prop(path, key) |
| Fs.node_prop(self, path, key) |
| end |
| |
| def set_node_prop(path, key, value, validate=true) |
| if validate |
| Repos.fs_change_node_prop(self, path, key, value) |
| else |
| Fs.change_node_prop(self, path, key, value) |
| end |
| end |
| |
| def node_proplist(path) |
| Fs.node_proplist(self, path) |
| end |
| alias node_prop_list node_proplist |
| |
| def check_path(path) |
| Fs.check_path(self, path) |
| end |
| |
| def file_length(path) |
| Fs.file_length(self, path) |
| end |
| |
| def file_md5_checksum(path) |
| Fs.file_md5_checksum(self, path) |
| end |
| |
| def file_contents(path) |
| stream = Fs.file_contents(self, path) |
| if block_given? |
| begin |
| yield stream |
| ensure |
| stream.close |
| end |
| else |
| stream |
| end |
| end |
| |
| def close |
| Fs.close_root(self) |
| end |
| |
| def dir_entries(path) |
| entries = {} |
| Fs.dir_entries(self, path).each do |name, entry| |
| entries[name] = entry |
| end |
| entries |
| end |
| |
| def dir_delta(src_path, src_entry, tgt_root, tgt_path, |
| editor, text_deltas=false, recurse=true, |
| entry_props=false, ignore_ancestry=false, |
| &authz_read_func) |
| Repos.dir_delta(self, src_path, src_entry, tgt_root, |
| tgt_path, editor, authz_read_func, |
| text_deltas, recurse, entry_props, |
| ignore_ancestry) |
| end |
| |
| def replay(editor, base_dir=nil, low_water_mark=nil, send_deltas=true, |
| &callback) |
| base_dir ||= "" |
| low_water_mark ||= Core::INVALID_REVNUM |
| Repos.replay2(self, base_dir, low_water_mark, send_deltas, |
| editor, callback) |
| end |
| |
| def copied_from(path) |
| Fs.copied_from(self, path) |
| end |
| |
| def txn_root? |
| Fs.is_txn_root(self) |
| end |
| |
| def revision_root? |
| Fs.is_revision_root(self) |
| end |
| |
| def paths_changed |
| Fs.paths_changed(self) |
| end |
| |
| def node_history(path) |
| Fs.node_history(self, path) |
| end |
| |
| def props_changed?(path1, root2, path2) |
| Fs.props_changed(self, path1, root2, path2) |
| end |
| |
| def merge(target_path, source_root, source_path, |
| ancestor_root, ancestor_path) |
| Fs.merge(source_root, source_path, self, target_path, |
| ancestor_root, ancestor_path) |
| end |
| |
| def make_dir(path) |
| Fs.make_dir(self, path) |
| end |
| |
| def delete(path) |
| Fs._delete(self, path) |
| end |
| |
| def copy(to_path, from_root, from_path) |
| Fs.copy(from_root, from_path, self, to_path) |
| end |
| |
| def revision_link(from_root, path) |
| Fs.revision_link(from_root, self, path) |
| end |
| |
| def make_file(path) |
| Fs.make_file(self, path) |
| end |
| |
| def apply_textdelta(path, base_checksum=nil, result_checksum=nil) |
| handler, handler_baton = Fs.apply_textdelta(self, path, |
| base_checksum, |
| result_checksum) |
| handler.baton = handler_baton |
| handler |
| end |
| |
| def apply_text(path, result_checksum=nil) |
| Fs.apply_text(self, path, result_checksum) |
| end |
| |
| def contents_changed?(path1, root2, path2) |
| Fs.contents_changed(self, path1, root2, path2) |
| end |
| |
| def file_delta_stream(arg1, arg2, target_path) |
| if arg1.is_a?(self.class) |
| source_root = arg1 |
| source_path = arg2 |
| target_root = self |
| else |
| source_root = self |
| source_path = arg1 |
| target_root = arg2 |
| end |
| Fs.get_file_delta_stream(source_root, source_path, |
| target_root, target_path) |
| end |
| |
| def stat(path) |
| Repos.stat(self, path) |
| end |
| |
| def committed_info(path) |
| Repos.get_committed_info(self, path) |
| end |
| |
| def closest_copy(path) |
| Fs.closest_copy(self, path) |
| end |
| |
| def mergeinfo(paths, inherit=nil, include_descendants=false) |
| paths = [paths] unless paths.is_a?(Array) |
| Fs.get_mergeinfo(self, paths, inherit, include_descendants) |
| end |
| |
| def change_mergeinfo(path, info) |
| Fs.change_mergeinfo(self, path, info) |
| end |
| end |
| |
| History = SWIG::TYPE_p_svn_fs_history_t |
| |
| class History |
| def location |
| Fs.history_location(self) |
| end |
| |
| def prev(cross_copies=true) |
| Fs.history_prev(self, cross_copies) |
| end |
| end |
| |
| |
| DirectoryEntry = Dirent |
| |
| Id = SWIG::TYPE_p_svn_fs_id_t |
| class Id |
| def to_s |
| unparse |
| end |
| |
| def unparse |
| Fs.unparse_id(self) |
| end |
| |
| def compare(other) |
| Fs.compare_ids(self, other) |
| end |
| |
| def related?(other) |
| Fs.check_related(self, other) |
| end |
| end |
| |
| class PathChange |
| def modify? |
| change_kind == Svn::Fs::PATH_CHANGE_MODIFY |
| end |
| |
| def add? |
| change_kind == Svn::Fs::PATH_CHANGE_ADD |
| end |
| |
| def text_mod? |
| text_mod |
| end |
| end |
| |
| class FileDiff |
| |
| def initialize(root1, path1, root2, path2) |
| @tempfile1 = nil |
| @tempfile2 = nil |
| |
| @binary = nil |
| |
| @root1 = root1 |
| @path1 = path1 |
| @root2 = root2 |
| @path2 = path2 |
| end |
| |
| def binary? |
| if @binary.nil? |
| @binary = _binary?(@root1, @path1) |
| @binary ||= _binary?(@root2, @path2) |
| end |
| @binary |
| end |
| |
| def files |
| if @tempfile1 |
| [@tempfile1, @tempfile2] |
| else |
| @tempfile1 = Tempfile.new("svn_fs") |
| @tempfile2 = Tempfile.new("svn_fs") |
| |
| begin |
| dump_contents(@tempfile1, @root1, @path1) |
| dump_contents(@tempfile2, @root2, @path2) |
| ensure |
| @tempfile1.close |
| @tempfile2.close |
| end |
| |
| [@tempfile1, @tempfile2] |
| end |
| end |
| |
| def diff |
| files |
| @diff ||= Core::Diff.file_diff(@tempfile1.path, @tempfile2.path) |
| end |
| |
| def unified(label1, label2, header_encoding=nil) |
| if diff and diff.diff? |
| diff.unified(label1, label2, header_encoding) |
| else |
| "" |
| end |
| end |
| |
| private |
| def dump_contents(open_tempfile, root, path) |
| if root and path |
| root.file_contents(path) do |stream| |
| open_tempfile.print(stream.read) |
| end |
| end |
| end |
| |
| def _binary?(root, path) |
| if root and path |
| prop = root.node_prop(path, Core::PROP_MIME_TYPE) |
| prop and Core.binary_mime_type?(prop) |
| end |
| end |
| end |
| end |
| end |