require "English" require 'uri' require "svn/error" require "svn/util" require "svn/core" require "svn/wc" require "svn/ra" require "svn/ext/client" module Svn module Client Util.set_constants(Ext::Client, self) Util.set_methods(Ext::Client, self) class CommitItem class << self undef new end end class CommitItem2 class << self undef new end end class CommitItem3 alias_method :wcprop_changes, :incoming_prop_changes alias_method :wcprop_changes=, :incoming_prop_changes= end class CommitItemWrapper def initialize(item) @item = item end def incoming_prop_changes if @item.incoming_prop_changes Util.hash_to_prop_array(@item.incoming_prop_changes) else nil end end alias_method :wcprop_changes, :incoming_prop_changes def method_missing(method, *args, &block) @item.__send__(method, *args, &block) end end class Info alias url URL alias repos_root_url repos_root_URL alias _last_changed_date last_changed_date def last_changed_date Time.from_apr_time(_last_changed_date) end end # For backward compatibility class PropListItem # Returns an URI for the item concerned with the instance. attr_accessor :name # Returns a Hash of properties, such as # {propname1 => propval1, propname2 => propval2, ...}. attr_accessor :props alias_method :node_name, :name alias_method :prop_hash, :props def initialize(name, props) @name = name @props = props end def method_missing(meth, *args) if @props.respond_to?(meth) @props.__send__(meth, *args) else super end end end Context = Ctx class Context alias _auth_baton auth_baton alias _auth_baton= auth_baton= remove_method :auth_baton, :auth_baton= private :_auth_baton, :_auth_baton= include Core::Authenticatable alias _initialize initialize private :_initialize def initialize _initialize self.auth_baton = Core::AuthBaton.new init_callbacks end def auth_baton=(baton) super(baton) self._auth_baton = auth_baton end def checkout(url, path, revision=nil, peg_rev=nil, depth=nil, ignore_externals=false, allow_unver_obstruction=false) revision ||= "HEAD" Client.checkout3(url, path, peg_rev, revision, depth, ignore_externals, allow_unver_obstruction, self) end alias co checkout def mkdir(*paths) paths = paths.first if paths.size == 1 and paths.first.is_a?(Array) mkdir2(:paths => paths) end MKDIR_REQUIRED_ARGUMENTS_KEYS = [:paths] def mkdir2(arguments) optional_arguments_defaults = { :make_parents => false, :revprop_table => {}, } arguments = optional_arguments_defaults.merge(arguments) Util.validate_options(arguments, optional_arguments_defaults.keys, MKDIR_REQUIRED_ARGUMENTS_KEYS) Client.mkdir3(normalize_path(arguments[:paths]), arguments[:make_parents], arguments[:revprop_table], self) end def mkdir_p(*paths) revprop_table = paths.pop if paths.last.is_a?(Hash) paths = paths.first if paths.size == 1 and paths.first.is_a?(Array) mkdir_p2(:paths => paths, :revprop_table => revprop_table || {}) end def mkdir_p2(arguments) mkdir2(arguments.update(:make_parents => true)) end def commit(targets, recurse=true, keep_locks=false, keep_changelist=false, changelist_name=nil, revprop_table=nil) targets = [targets] unless targets.is_a?(Array) Client.commit4(targets, recurse, keep_locks, keep_changelist, changelist_name, revprop_table, self) end alias ci commit def status(path, rev=nil, depth_or_recurse=nil, get_all=false, update=true, no_ignore=false, ignore_externals=false, changelists_names=nil, &status_func) depth = Core::Depth.infinity_or_immediates_from_recurse(depth_or_recurse) changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil? Client.status3(path, rev, status_func, depth, get_all, update, no_ignore, ignore_externals, changelists_names, self) end alias st status def add(path, recurse=true, force=false, no_ignore=false) Client.add3(path, recurse, force, no_ignore, self) end def delete(paths, force=false, keep_local=false, revprop_table=nil) paths = [paths] unless paths.is_a?(Array) Client.delete3(paths, force, keep_local, revprop_table, self) end alias del delete alias remove delete alias rm remove def rm_f(*paths) paths = paths.first if paths.size == 1 and paths.first.is_a?(Array) rm(paths, true) end def update(paths, rev="HEAD", depth=nil, ignore_externals=false, allow_unver_obstruction=false, depth_is_sticky=false) paths_is_array = paths.is_a?(Array) paths = [paths] unless paths_is_array result = Client.update3(paths, rev, depth, depth_is_sticky, ignore_externals, allow_unver_obstruction, self) result = result.first unless paths_is_array result end alias up update def import(path, uri, depth_or_recurse=true, no_ignore=false, revprop_table=nil) depth = Core::Depth.infinity_or_immediates_from_recurse(depth_or_recurse) Client.import3(path, uri, depth, no_ignore, false, revprop_table, self) end def cleanup(dir) Client.cleanup(dir, self) end def relocate(dir, from, to, recurse=true) Client.relocate(dir, from, to, recurse, self) end def revert(paths, recurse=true) paths = [paths] unless paths.is_a?(Array) Client.revert(paths, recurse, self) end def resolved(path, recurse=true) Client.resolved(path, recurse, self) end def propset(name, value, target, depth_or_recurse=nil, force=false, base_revision_for_url=nil, changelists_names=nil, revprop_table=nil) base_revision_for_url ||= Svn::Core::INVALID_REVNUM depth = Core::Depth.infinity_or_empty_from_recurse(depth_or_recurse) changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil? Client.propset3(name, value, target, depth, force, base_revision_for_url, changelists_names, revprop_table, self) end alias prop_set propset alias pset propset alias ps propset def propdel(name, *args) propset(name, nil, *args) end alias prop_del propdel alias pdel propdel alias pd propdel # Returns a value of a property, with +name+ attached to +target+, # as a Hash such as {uri1 => value1, uri2 => value2, ...}. def propget(name, target, rev=nil, peg_rev=nil, depth_or_recurse=nil, changelists_names=nil) rev ||= "HEAD" peg_rev ||= rev depth = Core::Depth.infinity_or_empty_from_recurse(depth_or_recurse) changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil? Client.propget3(name, target, peg_rev, rev, depth, changelists_names, self).first end alias prop_get propget alias pget propget alias pg propget # Obsoleted document. # # Returns list of properties attached to +target+ as an Array of # Svn::Client::PropListItem. # Paths and URIs are available as +target+. def proplist(target, peg_rev=nil, rev=nil, depth_or_recurse=nil, changelists_names=nil, &block) rev ||= "HEAD" peg_rev ||= rev items = [] depth = Core::Depth.infinity_or_empty_from_recurse(depth_or_recurse) receiver = Proc.new do |path, prop_hash| items << PropListItem.new(path, prop_hash) block.call(path, prop_hash) if block end changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil? Client.proplist3(target, peg_rev, rev, depth, changelists_names, receiver, self) items end alias prop_list proplist alias plist proplist alias pl proplist def copy(src_paths, dst_path, rev_or_copy_as_child=nil, make_parents=nil, revprop_table=nil) if src_paths.is_a?(Array) copy_as_child = rev_or_copy_as_child if copy_as_child.nil? copy_as_child = src_paths.size == 1 ? false : true end src_paths = src_paths.collect do |path| if path.is_a?(CopySource) path else CopySource.new(path) end end else copy_as_child = false unless src_paths.is_a?(CopySource) src_paths = CopySource.new(src_paths, rev_or_copy_as_child) end src_paths = [src_paths] end Client.copy4(src_paths, dst_path, copy_as_child, make_parents, revprop_table, self) end alias cp copy def move(src_paths, dst_path, force=false, move_as_child=nil, make_parents=nil, revprop_table=nil) src_paths = [src_paths] unless src_paths.is_a?(Array) move_as_child = src_paths.size == 1 ? false : true if move_as_child.nil? Client.move5(src_paths, dst_path, force, move_as_child, make_parents, revprop_table, self) end alias mv move def mv_f(src_paths, dst_path, move_as_child=nil) move(src_paths, dst_path, true, move_as_child) end def diff(options, path1, rev1, path2, rev2, out_file, err_file, depth=nil, ignore_ancestry=false, no_diff_deleted=false, force=false, header_encoding=nil, relative_to_dir=nil, changelists=nil) header_encoding ||= Core::LOCALE_CHARSET relative_to_dir &&= Core.path_canonicalize(relative_to_dir) Client.diff4(options, path1, rev1, path2, rev2, relative_to_dir, depth, ignore_ancestry, no_diff_deleted, force, header_encoding, out_file, err_file, changelists, self) end def diff_peg(options, path, start_rev, end_rev, out_file, err_file, peg_rev=nil, depth=nil, ignore_ancestry=false, no_diff_deleted=false, force=false, header_encoding=nil, relative_to_dir=nil, changelists=nil) header_encoding ||= Core::LOCALE_CHARSET relative_to_dir &&= Core.path_canonicalize(relative_to_dir) Client.diff_peg4(options, path, peg_rev, start_rev, end_rev, relative_to_dir, depth, ignore_ancestry, no_diff_deleted, force, header_encoding, out_file, err_file, changelists, self) end # Invokes block once for each item changed between path1 # at rev1 and path2 at rev2, # and returns +nil+. # +diff+ is an instance of Svn::Client::DiffSummarize. def diff_summarize(path1, rev1, path2, rev2, depth=nil, ignore_ancestry=true, changelists=nil, &block) # :yields: diff Client.diff_summarize2(path1, rev1, path2, rev2, depth, ignore_ancestry, changelists, block, self) end def diff_summarize_peg(path1, rev1, rev2, peg_rev=nil, depth=nil, ignore_ancestry=true, changelists=nil, &block) Client.diff_summarize_peg2(path1, rev1, rev2, peg_rev, depth, ignore_ancestry, changelists, block, self) end def merge(src1, rev1, src2, rev2, target_wcpath, depth=nil, ignore_ancestry=false, force=false, dry_run=false, options=nil, record_only=false) Client.merge3(src1, rev1, src2, rev2, target_wcpath, depth, ignore_ancestry, force, record_only, dry_run, options, self) end def merge_peg(src, rev1, rev2, *rest) merge_peg2(src, [[rev1, rev2]], *rest) end def merge_peg2(src, ranges_to_merge, target_wcpath, peg_rev=nil, depth=nil, ignore_ancestry=false, force=false, dry_run=false, options=nil, record_only=false) peg_rev ||= URI(src).scheme ? 'HEAD' : 'WORKING' Client.merge_peg3(src, ranges_to_merge, peg_rev, target_wcpath, depth, ignore_ancestry, force, record_only, dry_run, options, self) end # Returns a content of +path+ at +rev+ as a String. def cat(path, rev="HEAD", peg_rev=nil, output=nil) used_string_io = output.nil? output ||= StringIO.new Client.cat2(output, path, peg_rev, rev, self) if used_string_io output.rewind output.read else output end end def lock(targets, comment=nil, steal_lock=false) targets = [targets] unless targets.is_a?(Array) Client.lock(targets, comment, steal_lock, self) end def unlock(targets, break_lock=false) targets = [targets] unless targets.is_a?(Array) Client.unlock(targets, break_lock, self) end def info(path_or_uri, rev=nil, peg_rev=nil, depth_or_recurse=false, changelists=nil) rev ||= URI(path_or_uri).scheme ? "HEAD" : "BASE" depth = Core::Depth.infinity_or_empty_from_recurse(depth_or_recurse) peg_rev ||= rev receiver = Proc.new do |path, info| yield(path, info) end Client.info2(path_or_uri, rev, peg_rev, receiver, depth, changelists, self) end # Returns URL for +path+ as a String. def url_from_path(path) Client.url_from_path(path) end def uuid_from_path(path, adm) Client.uuid_from_path(path, adm, self) end # Returns UUID for +url+ as a String. def uuid_from_url(url) Client.uuid_from_url(url, self) end def open_ra_session(url) Client.open_ra_session(url, self) end # Scans revisions from +start_rev+ to +end_rev+ for each path in # +paths+, invokes block once for each revision, and then returns # +nil+. # # When +discover_changed_paths+ is +false+ or +nil+, +changed_paths+, # the first block-argument, is +nil+. Otherwise, it is a Hash # containing simple information associated with the revision, # whose keys are paths and values are changes, such as # {path1 => change1, path2 => change2, ...}, # where each path is an absolute one in the repository and each # change is a instance of Svn::Core::LogChangedPath. # The rest of the block arguments, +rev+, +author+, +date+, and # +message+ are the revision number, author, date, and the log # message of that revision, respectively. def log(paths, start_rev, end_rev, limit, discover_changed_paths, strict_node_history, peg_rev=nil) paths = [paths] unless paths.is_a?(Array) receiver = Proc.new do |changed_paths, rev, author, date, message| yield(changed_paths, rev, author, date, message) end Client.log3(paths, peg_rev, start_rev, end_rev, limit, discover_changed_paths, strict_node_history, receiver, self) end # Returns log messages, for commits affecting +paths+ from +start_rev+ # to +end_rev+, as an Array of String. # You can use URIs as well as paths as +paths+. def log_message(paths, start_rev=nil, end_rev=nil) start_rev ||= "HEAD" end_rev ||= start_rev messages = [] receiver = Proc.new do |changed_paths, rev, author, date, message| messages << message end log(paths, start_rev, end_rev, 0, false, false) do |*args| receiver.call(*args) end if !paths.is_a?(Array) and messages.size == 1 messages.first else messages end end def blame(path_or_uri, start_rev=nil, end_rev=nil, peg_rev=nil, diff_options=nil, ignore_mime_type=false) start_rev ||= 1 end_rev ||= URI(path_or_uri).scheme ? "HEAD" : "BASE" peg_rev ||= end_rev diff_options ||= Svn::Core::DiffFileOptions.new receiver = Proc.new do |line_no, revision, author, date, line| yield(line_no, revision, author, date, line) end Client.blame3(path_or_uri, peg_rev, start_rev, end_rev, diff_options, ignore_mime_type, receiver, self) end alias praise blame alias annotate blame alias ann annotate # Returns a value of a revision property named +name+ for +uri+ # at +rev+, as a String. # Both URLs and paths are available as +uri+. def revprop(name, uri, rev) value, = revprop_get(name, uri, rev) value end alias rp revprop # Returns a value of a revision property named +name+ for +uri+ # at +rev+, as an Array such as [value, rev]. # Both URLs and paths are available as +uri+. def revprop_get(name, uri, rev) result = Client.revprop_get(name, uri, rev, self) if result.is_a?(Array) result else [nil, result] end end alias rpget revprop_get alias rpg revprop_get # Sets +value+ as a revision property named +name+ for +uri+ at +rev+. # Both URLs and paths are available as +uri+. def revprop_set(name, value, uri, rev, force=false) Client.revprop_set(name, value, uri, rev, force, self) end alias rpset revprop_set alias rps revprop_set # Deletes a revision property, named +name+, for +uri+ at +rev+. # Both URLs and paths are available as +uri+. def revprop_del(name, uri, rev, force=false) Client.revprop_set(name, nil, uri, rev, force, self) end alias rpdel revprop_del alias rpd revprop_del # Returns a list of revision properties set for +uri+ at +rev+, # as an Array such as # [{revprop1 => value1, revprop2 => value2, ...}, rev]. # Both URLs and paths are available as +uri+. def revprop_list(uri, rev) props, rev = Client.revprop_list(uri, rev, self) if props.has_key?(Svn::Core::PROP_REVISION_DATE) props[Svn::Core::PROP_REVISION_DATE] = Time.from_svn_format(props[Svn::Core::PROP_REVISION_DATE]) end [props, rev] end alias rplist revprop_list alias rpl revprop_list def export(from, to, rev=nil, peg_rev=nil, force=false, ignore_externals=false, depth=nil, native_eol=nil) Client.export4(from, to, rev, peg_rev, force, ignore_externals, depth, native_eol, self) end def ls(path_or_uri, rev=nil, peg_rev=nil, recurse=false) rev ||= URI(path_or_uri).scheme ? "HEAD" : "BASE" peg_rev ||= rev Client.ls3(path_or_uri, rev, peg_rev, recurse, self) end # Invokes block once for each path below +path_or_uri+ at +rev+ # and returns +nil+. # +path+ is a relative path from the +path_or_uri+. # +dirent+ is an instance of Svn::Core::Dirent. # +abs_path+ is an absolute path for +path_or_uri+ in the repository. def list(path_or_uri, rev, peg_rev=nil, depth_or_recurse=Core::DEPTH_IMMEDIATES, dirent_fields=nil, fetch_locks=true, &block) # :yields: path, dirent, lock, abs_path depth = Core::Depth.infinity_or_immediates_from_recurse(depth_or_recurse) dirent_fields ||= Core::DIRENT_ALL Client.list2(path_or_uri, peg_rev, rev, depth, dirent_fields, fetch_locks, block, self) end def switch(path, uri, peg_rev=nil, rev=nil, depth=nil, ignore_externals=false, allow_unver_obstruction=false, depth_is_sticky=false) Client.switch2(path, uri, peg_rev, rev, depth, depth_is_sticky, ignore_externals, allow_unver_obstruction, self) end def set_log_msg_func(callback=Proc.new) callback_wrapper = Proc.new do |items| items = items.collect do |item| item_wrapper = CommitItemWrapper.new(item) end callback.call(items) end set_log_msg_func2(callback_wrapper) end def set_log_msg_func2(callback=Proc.new) @log_msg_baton = Client.set_log_msg_func3(self, callback) end def set_notify_func(callback=Proc.new) @notify_baton = Client.set_notify_func2(self, callback) end def set_cancel_func(callback=Proc.new) @cancel_baton = Client.set_cancel_func(self, callback) end def config=(new_config) Client.set_config(self, new_config) end def config Client.get_config(self) end def merged(path_or_url, peg_revision=nil) info = Client.mergeinfo_get_merged(path_or_url, peg_revision, self) return nil if info.nil? Core::MergeInfo.new(info) end def log_merged(path_or_url, peg_revision, merge_source_url, source_peg_revision, discover_changed_path=true, interested_revision_prop_names=nil, &receiver) raise ArgumentError, "Block isn't given" if receiver.nil? Client.mergeinfo_log_merged(path_or_url, peg_revision, merge_source_url, source_peg_revision, receiver, discover_changed_path, interested_revision_prop_names, self) end def add_to_changelist(changelist_name, paths, depth=nil, changelists_names=nil) paths = [paths] unless paths.is_a?(Array) changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil? Client.add_to_changelist(paths, changelist_name, depth, changelists_names, self) end def changelists(changelists_names, root_path, depth=nil, &block) lists_contents = Hash.new{|h,k| h[k]=[]} changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil? block ||= lambda{|path, changelist| lists_contents[changelist] << path } Client.get_changelists(root_path, changelists_names, depth, block, self) lists_contents end def remove_from_changelists(changelists_names, paths, depth=nil) changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil? paths = [paths] unless paths.is_a?(Array) Client.remove_from_changelists(paths, depth, changelists_names, self) end private def init_callbacks set_log_msg_func(nil) set_notify_func(nil) set_cancel_func(nil) end %w(log_msg notify cancel).each do |type| private "#{type}_func", "#{type}_baton" private "#{type}_func=", "#{type}_baton=" end %w(notify).each do |type| private "#{type}_func2", "#{type}_baton2" private "#{type}_func2=", "#{type}_baton2=" end def normalize_path(paths) paths = [paths] unless paths.is_a?(Array) paths.collect do |path| path.chomp(File::SEPARATOR) end end end # Following methods are also available: # # [path] # Returns a path concerned with the instance. # [prop_changed?] # Returns +true+ when the instance is a change involving a property # change. class DiffSummarize alias prop_changed? prop_changed # Returns +true+ when the instance is a normal change. def kind_normal? summarize_kind == DIFF_SUMMARIZE_KIND_NORMAL end # Returns +true+ when the instance is a change involving addition. def kind_added? summarize_kind == DIFF_SUMMARIZE_KIND_ADDED end # Returns +true+ when the instance is a change involving modification. def kind_modified? summarize_kind == DIFF_SUMMARIZE_KIND_MODIFIED end # Returns +true+ when the instance is a change involving deletion. def kind_deleted? summarize_kind == DIFF_SUMMARIZE_KIND_DELETED end # Returns +true+ when the instance is a change made to no node. def node_kind_none? node_kind == Core::NODE_NONE end # Returns +true+ when the instance is a change made to a file node. def node_kind_file? node_kind == Core::NODE_FILE end # Returns +true+ when the instance is a change made to a directory node. def node_kind_dir? node_kind == Core::NODE_DIR end # Returns +true+ when the instance is a change made to an unknown node. def node_kind_unknown? node_kind == Core::NODE_UNKNOWN end end class CopySource alias_method :_initialize, :initialize private :_initialize def initialize(path, rev=nil, peg_rev=nil) _initialize(path, rev, peg_rev) end end end end