8 require "svn/ext/client"
12 Util.set_constants(Ext::Client, self)
13 Util.set_methods(Ext::Client, self)
28 alias_method :wcprop_changes, :incoming_prop_changes
29 alias_method :wcprop_changes=, :incoming_prop_changes=
32 class CommitItemWrapper
37 def incoming_prop_changes
38 if @item.incoming_prop_changes
39 Util.hash_to_prop_array(@item.incoming_prop_changes)
44 alias_method :wcprop_changes, :incoming_prop_changes
46 def method_missing(method, *args, &block)
47 @item.__send__(method, *args, &block)
53 alias repos_root_url repos_root_URL
55 alias _last_changed_date last_changed_date
57 Time.from_apr_time(_last_changed_date)
62 # For backward compatibility
64 # Returns an URI for the item concerned with the instance.
67 # Returns a Hash of properties, such as
68 # <tt>{propname1 => propval1, propname2 => propval2, ...}</tt>.
71 alias_method :node_name, :name
72 alias_method :prop_hash, :props
74 def initialize(name, props)
79 def method_missing(meth, *args)
80 if @props.respond_to?(meth)
81 @props.__send__(meth, *args)
90 alias _auth_baton auth_baton
91 alias _auth_baton= auth_baton=
92 remove_method :auth_baton, :auth_baton=
93 private :_auth_baton, :_auth_baton=
95 include Core::Authenticatable
97 alias _initialize initialize
101 self.auth_baton = Core::AuthBaton.new
105 def auth_baton=(baton)
107 self._auth_baton = auth_baton
110 def checkout(url, path, revision=nil, peg_rev=nil,
111 depth=nil, ignore_externals=false,
112 allow_unver_obstruction=false)
114 Client.checkout3(url, path, peg_rev, revision, depth,
115 ignore_externals, allow_unver_obstruction,
121 paths = paths.first if paths.size == 1 and paths.first.is_a?(Array)
122 Client.mkdir2(normalize_path(paths), self)
125 def commit(targets, recurse=true, keep_locks=false,
126 keep_changelist=false, changelist_name=nil)
127 targets = [targets] unless targets.is_a?(Array)
128 Client.commit4(targets, recurse, keep_locks, keep_changelist,
129 changelist_name, self)
133 def status(path, rev=nil, depth_or_recurse=nil, get_all=false,
134 update=true, no_ignore=false,
135 ignore_externals=false, changelists_names=nil, &status_func)
136 depth = Core::Depth.infinity_or_immediates_from_recurse(depth_or_recurse)
137 changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil?
138 Client.status3(path, rev, status_func,
139 depth, get_all, update, no_ignore,
140 ignore_externals, changelists_names, self)
144 def add(path, recurse=true, force=false, no_ignore=false)
145 Client.add3(path, recurse, force, no_ignore, self)
148 def delete(paths, force=false, keep_local=false)
149 paths = [paths] unless paths.is_a?(Array)
150 Client.delete3(paths, force, keep_local, self)
157 paths = paths.first if paths.size == 1 and paths.first.is_a?(Array)
161 def update(paths, rev="HEAD", depth=nil, ignore_externals=false,
162 allow_unver_obstruction=false, depth_is_sticky=false)
163 paths_is_array = paths.is_a?(Array)
164 paths = [paths] unless paths_is_array
165 result = Client.update3(paths, rev, depth, depth_is_sticky,
166 ignore_externals, allow_unver_obstruction,
168 result = result.first unless paths_is_array
173 def import(path, uri, recurse=true, no_ignore=false)
174 Client.import2(path, uri, !recurse, no_ignore, self)
178 Client.cleanup(dir, self)
181 def relocate(dir, from, to, recurse=true)
182 Client.relocate(dir, from, to, recurse, self)
185 def revert(paths, recurse=true)
186 paths = [paths] unless paths.is_a?(Array)
187 Client.revert(paths, recurse, self)
190 def resolved(path, recurse=true)
191 Client.resolved(path, recurse, self)
194 def propset(name, value, target, depth_or_recurse=nil, force=false,
195 base_revision_for_url=nil, changelists_names=nil)
196 base_revision_for_url ||= Svn::Core::INVALID_REVNUM
197 depth = Core::Depth.infinity_or_empty_from_recurse(depth_or_recurse)
198 changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil?
199 Client.propset3(name, value, target, depth, force,
200 base_revision_for_url, changelists_names, self)
202 alias prop_set propset
206 def propdel(name, *args)
207 propset(name, nil, *args)
209 alias prop_del propdel
213 # Returns a value of a property, with +name+ attached to +target+,
214 # as a Hash such as <tt>{uri1 => value1, uri2 => value2, ...}</tt>.
215 def propget(name, target, rev=nil, peg_rev=nil, depth_or_recurse=nil,
216 changelists_names=nil)
219 depth = Core::Depth.infinity_or_empty_from_recurse(depth_or_recurse)
220 changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil?
221 Client.propget3(name, target, peg_rev, rev, depth, changelists_names, self).first
223 alias prop_get propget
227 # Obsoleted document.
229 # Returns list of properties attached to +target+ as an Array of
230 # Svn::Client::PropListItem.
231 # Paths and URIs are available as +target+.
232 def proplist(target, rev=nil, peg_rev=nil, depth_or_recurse=nil,
233 changelists_names=nil, &block)
237 depth = Core::Depth.infinity_or_empty_from_recurse(depth_or_recurse)
238 receiver = Proc.new do |path, prop_hash|
239 items << PropListItem.new(path, prop_hash)
240 block.call(path, prop_hash) if block
242 changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil?
243 Client.proplist3(target, rev, peg_rev, depth, changelists_names,
247 alias prop_list proplist
251 def copy(src_paths, dst_path, rev_or_copy_as_child=nil,
253 if src_paths.is_a?(Array)
254 copy_as_child = rev_or_copy_as_child
255 if copy_as_child.nil?
256 copy_as_child = src_paths.size == 1 ? false : true
258 src_paths = src_paths.collect do |path|
259 if path.is_a?(CopySource)
266 copy_as_child = false
267 unless src_paths.is_a?(CopySource)
268 src_paths = CopySource.new(src_paths, rev_or_copy_as_child)
270 src_paths = [src_paths]
272 Client.copy4(src_paths, dst_path, copy_as_child, make_parents, self)
276 def move(src_paths, dst_path, force=false, move_as_child=nil,
278 src_paths = [src_paths] unless src_paths.is_a?(Array)
279 move_as_child = src_paths.size == 1 ? false : true if move_as_child.nil?
280 Client.move5(src_paths, dst_path, force, move_as_child, make_parents,
285 def mv_f(src_paths, dst_path, move_as_child=nil)
286 move(src_paths, dst_path, true, move_as_child)
289 def diff(options, path1, rev1, path2, rev2,
290 out_file, err_file, depth=nil,
291 ignore_ancestry=false,
292 no_diff_deleted=false, force=false,
293 header_encoding=nil, relative_to_dir=nil, changelists=nil)
294 header_encoding ||= Core::LOCALE_CHARSET
295 relative_to_dir &&= Core.path_canonicalize(relative_to_dir)
296 Client.diff4(options, path1, rev1, path2, rev2, relative_to_dir,
297 depth, ignore_ancestry,
298 no_diff_deleted, force, header_encoding,
299 out_file, err_file, changelists, self)
302 def diff_peg(options, path, start_rev, end_rev,
303 out_file, err_file, peg_rev=nil,
304 depth=nil, ignore_ancestry=false,
305 no_diff_deleted=false, force=false,
306 header_encoding=nil, relative_to_dir=nil, changelists=nil)
307 header_encoding ||= Core::LOCALE_CHARSET
308 relative_to_dir &&= Core.path_canonicalize(relative_to_dir)
309 Client.diff_peg4(options, path, peg_rev, start_rev, end_rev,
310 relative_to_dir, depth, ignore_ancestry,
311 no_diff_deleted, force, header_encoding,
312 out_file, err_file, changelists, self)
315 # Invokes block once for each item changed between <tt>path1</tt>
316 # at <tt>rev1</tt> and <tt>path2</tt> at <tt>rev2</tt>,
318 # +diff+ is an instance of Svn::Client::DiffSummarize.
319 def diff_summarize(path1, rev1, path2, rev2,
320 depth=nil, ignore_ancestry=true, changelists=nil,
321 &block) # :yields: diff
322 Client.diff_summarize2(path1, rev1, path2, rev2,
323 depth, ignore_ancestry, changelists, block,
327 def diff_summarize_peg(path1, rev1, rev2, peg_rev=nil,
328 depth=nil, ignore_ancestry=true, changelists=nil,
330 Client.diff_summarize_peg2(path1, rev1, rev2, peg_rev,
331 depth, ignore_ancestry, changelists, block,
335 def merge(src1, rev1, src2, rev2, target_wcpath,
336 depth=nil, ignore_ancestry=false,
337 force=false, dry_run=false, options=nil, record_only=false)
338 Client.merge3(src1, rev1, src2, rev2, target_wcpath,
339 depth, ignore_ancestry, force, record_only,
340 dry_run, options, self)
344 def merge_peg(src, rev1, rev2, *rest)
345 merge_peg2(src, [[rev1, rev2]], *rest)
348 def merge_peg2(src, ranges_to_merge, target_wcpath,
349 peg_rev=nil, depth=nil,
350 ignore_ancestry=false, force=false,
351 dry_run=false, options=nil, record_only=false)
352 peg_rev ||= URI(src).scheme ? 'HEAD' : 'WORKING'
353 Client.merge_peg3(src, ranges_to_merge, peg_rev,
354 target_wcpath, depth, ignore_ancestry,
355 force, record_only, dry_run, options, self)
358 # Returns a content of +path+ at +rev+ as a String.
359 def cat(path, rev="HEAD", peg_rev=nil, output=nil)
360 used_string_io = output.nil?
361 output ||= StringIO.new
362 Client.cat2(output, path, peg_rev, rev, self)
371 def lock(targets, comment=nil, steal_lock=false)
372 targets = [targets] unless targets.is_a?(Array)
373 Client.lock(targets, comment, steal_lock, self)
376 def unlock(targets, break_lock=false)
377 targets = [targets] unless targets.is_a?(Array)
378 Client.unlock(targets, break_lock, self)
381 def info(path_or_uri, rev=nil, peg_rev=nil, depth_or_recurse=false,
383 rev ||= URI(path_or_uri).scheme ? "HEAD" : "BASE"
384 depth = Core::Depth.infinity_or_empty_from_recurse(depth_or_recurse)
386 receiver = Proc.new do |path, info|
389 Client.info2(path_or_uri, rev, peg_rev, receiver, depth, changelists,
393 # Returns URL for +path+ as a String.
394 def url_from_path(path)
395 Client.url_from_path(path)
398 def uuid_from_path(path, adm)
399 Client.uuid_from_path(path, adm, self)
402 # Returns UUID for +url+ as a String.
403 def uuid_from_url(url)
404 Client.uuid_from_url(url, self)
407 def open_ra_session(url)
408 Client.open_ra_session(url, self)
411 # Scans revisions from +start_rev+ to +end_rev+ for each path in
412 # +paths+, invokes block once for each revision, and then returns
415 # When +discover_changed_paths+ is +false+ or +nil+, +changed_paths+,
416 # the first block-argument, is +nil+. Otherwise, it is a Hash
417 # containing simple information associated with the revision,
418 # whose keys are paths and values are changes, such as
419 # <tt>{path1 => change1, path2 => change2, ...}</tt>,
420 # where each path is an absolute one in the repository and each
421 # change is a instance of Svn::Core::LogChangedPath.
422 # The rest of the block arguments, +rev+, +author+, +date+, and
423 # +message+ are the revision number, author, date, and the log
424 # message of that revision, respectively.
425 def log(paths, start_rev, end_rev, limit,
426 discover_changed_paths, strict_node_history,
428 paths = [paths] unless paths.is_a?(Array)
429 receiver = Proc.new do |changed_paths, rev, author, date, message|
430 yield(changed_paths, rev, author, date, message)
432 Client.log3(paths, peg_rev, start_rev, end_rev, limit,
433 discover_changed_paths,
438 # Returns log messages, for commits affecting +paths+ from +start_rev+
439 # to +end_rev+, as an Array of String.
440 # You can use URIs as well as paths as +paths+.
441 def log_message(paths, start_rev=nil, end_rev=nil)
443 end_rev ||= start_rev
445 receiver = Proc.new do |changed_paths, rev, author, date, message|
448 log(paths, start_rev, end_rev, 0, false, false) do |*args|
451 if !paths.is_a?(Array) and messages.size == 1
458 def blame(path_or_uri, start_rev=nil, end_rev=nil, peg_rev=nil,
459 diff_options=nil, ignore_mime_type=false)
461 end_rev ||= URI(path_or_uri).scheme ? "HEAD" : "BASE"
463 diff_options ||= Svn::Core::DiffFileOptions.new
464 receiver = Proc.new do |line_no, revision, author, date, line|
465 yield(line_no, revision, author, date, line)
467 Client.blame3(path_or_uri, peg_rev, start_rev,
468 end_rev, diff_options, ignore_mime_type,
475 # Returns a value of a revision property named +name+ for +uri+
476 # at +rev+, as a String.
477 # Both URLs and paths are available as +uri+.
478 def revprop(name, uri, rev)
479 value, = revprop_get(name, uri, rev)
484 # Returns a value of a revision property named +name+ for +uri+
485 # at +rev+, as an Array such as <tt>[value, rev]</tt>.
486 # Both URLs and paths are available as +uri+.
487 def revprop_get(name, uri, rev)
488 result = Client.revprop_get(name, uri, rev, self)
489 if result.is_a?(Array)
495 alias rpget revprop_get
496 alias rpg revprop_get
498 # Sets +value+ as a revision property named +name+ for +uri+ at +rev+.
499 # Both URLs and paths are available as +uri+.
500 def revprop_set(name, value, uri, rev, force=false)
501 Client.revprop_set(name, value, uri, rev, force, self)
503 alias rpset revprop_set
504 alias rps revprop_set
506 # Deletes a revision property, named +name+, for +uri+ at +rev+.
507 # Both URLs and paths are available as +uri+.
508 def revprop_del(name, uri, rev, force=false)
509 Client.revprop_set(name, nil, uri, rev, force, self)
511 alias rpdel revprop_del
512 alias rpd revprop_del
514 # Returns a list of revision properties set for +uri+ at +rev+,
515 # as an Array such as
516 # <tt>[{revprop1 => value1, revprop2 => value2, ...}, rev]</tt>.
517 # Both URLs and paths are available as +uri+.
518 def revprop_list(uri, rev)
519 props, rev = Client.revprop_list(uri, rev, self)
520 if props.has_key?(Svn::Core::PROP_REVISION_DATE)
521 props[Svn::Core::PROP_REVISION_DATE] =
522 Time.from_svn_format(props[Svn::Core::PROP_REVISION_DATE])
526 alias rplist revprop_list
527 alias rpl revprop_list
529 def export(from, to, rev=nil, peg_rev=nil,
530 force=false, ignore_externals=false,
531 depth=nil, native_eol=nil)
532 Client.export4(from, to, rev, peg_rev, force,
533 ignore_externals, depth, native_eol, self)
536 def ls(path_or_uri, rev=nil, peg_rev=nil, recurse=false)
537 rev ||= URI(path_or_uri).scheme ? "HEAD" : "BASE"
539 Client.ls3(path_or_uri, rev, peg_rev, recurse, self)
542 # Invokes block once for each path below +path_or_uri+ at +rev+
544 # +path+ is a relative path from the +path_or_uri+.
545 # +dirent+ is an instance of Svn::Core::Dirent.
546 # +abs_path+ is an absolute path for +path_or_uri+ in the repository.
547 def list(path_or_uri, rev, peg_rev=nil, depth_or_recurse=Core::DEPTH_IMMEDIATES,
548 dirent_fields=nil, fetch_locks=true,
549 &block) # :yields: path, dirent, lock, abs_path
550 depth = Core::Depth.infinity_or_immediates_from_recurse(depth_or_recurse)
551 dirent_fields ||= Core::DIRENT_ALL
552 Client.list2(path_or_uri, peg_rev, rev, depth, dirent_fields,
553 fetch_locks, block, self)
556 def switch(path, uri, peg_rev=nil, rev=nil, depth=nil,
557 ignore_externals=false, allow_unver_obstruction=false,
558 depth_is_sticky=false)
560 Client.switch2(path, uri, peg_rev, rev, depth, depth_is_sticky,
561 ignore_externals, allow_unver_obstruction, self)
564 def set_log_msg_func(callback=Proc.new)
565 callback_wrapper = Proc.new do |items|
566 items = items.collect do |item|
567 item_wrapper = CommitItemWrapper.new(item)
571 set_log_msg_func2(callback_wrapper)
574 def set_log_msg_func2(callback=Proc.new)
575 @log_msg_baton = Client.set_log_msg_func3(self, callback)
578 def set_notify_func(callback=Proc.new)
579 @notify_baton = Client.set_notify_func2(self, callback)
582 def set_cancel_func(callback=Proc.new)
583 @cancel_baton = Client.set_cancel_func(self, callback)
586 def config=(new_config)
587 Client.set_config(self, new_config)
591 Client.get_config(self)
594 def merged(path_or_url, peg_revision=nil)
595 info = Client.mergeinfo_get_merged(path_or_url, peg_revision, self)
596 return nil if info.nil?
597 Core::MergeInfo.new(info)
600 def log_merged(path_or_url, peg_revision, merge_source_url,
601 source_peg_revision, discover_changed_path=true,
602 interested_revision_prop_names=nil,
604 raise ArgumentError, "Block isn't given" if receiver.nil?
605 Client.mergeinfo_log_merged(path_or_url, peg_revision,
606 merge_source_url, source_peg_revision,
607 receiver, discover_changed_path,
608 interested_revision_prop_names,
612 def add_to_changelist(changelist_name, paths, depth=nil, changelists_names=nil)
613 paths = [paths] unless paths.is_a?(Array)
614 changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil?
615 Client.add_to_changelist(paths, changelist_name, depth, changelists_names, self)
618 def changelists(changelists_names, root_path, depth=nil, &block)
619 lists_contents = Hash.new{|h,k| h[k]=[]}
620 changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil?
621 block ||= lambda{|path, changelist| lists_contents[changelist] << path }
622 Client.get_changelists(root_path, changelists_names, depth, block, self)
626 def remove_from_changelists(changelists_names, paths, depth=nil)
627 changelists_names = [changelists_names] unless changelists_names.is_a?(Array) or changelists_names.nil?
628 paths = [paths] unless paths.is_a?(Array)
629 Client.remove_from_changelists(paths, depth, changelists_names, self)
634 set_log_msg_func(nil)
638 %w(log_msg notify cancel).each do |type|
639 private "#{type}_func", "#{type}_baton"
640 private "#{type}_func=", "#{type}_baton="
642 %w(notify).each do |type|
643 private "#{type}_func2", "#{type}_baton2"
644 private "#{type}_func2=", "#{type}_baton2="
647 def normalize_path(paths)
648 paths = [paths] unless paths.is_a?(Array)
649 paths.collect do |path|
650 path.chomp(File::SEPARATOR)
655 # Following methods are also available:
658 # Returns a path concerned with the instance.
660 # Returns +true+ when the instance is a change involving a property
663 alias prop_changed? prop_changed
665 # Returns +true+ when the instance is a normal change.
667 summarize_kind == DIFF_SUMMARIZE_KIND_NORMAL
670 # Returns +true+ when the instance is a change involving addition.
672 summarize_kind == DIFF_SUMMARIZE_KIND_ADDED
675 # Returns +true+ when the instance is a change involving modification.
677 summarize_kind == DIFF_SUMMARIZE_KIND_MODIFIED
680 # Returns +true+ when the instance is a change involving deletion.
682 summarize_kind == DIFF_SUMMARIZE_KIND_DELETED
685 # Returns +true+ when the instance is a change made to no node.
687 node_kind == Core::NODE_NONE
690 # Returns +true+ when the instance is a change made to a file node.
692 node_kind == Core::NODE_FILE
695 # Returns +true+ when the instance is a change made to a directory node.
697 node_kind == Core::NODE_DIR
700 # Returns +true+ when the instance is a change made to an unknown node.
701 def node_kind_unknown?
702 node_kind == Core::NODE_UNKNOWN
707 alias_method :_initialize, :initialize
709 def initialize(path, rev=nil, peg_rev=nil)
710 _initialize(path, rev, peg_rev)