1# ==================================================================== 2# Licensed to the Apache Software Foundation (ASF) under one 3# or more contributor license agreements. See the NOTICE file 4# distributed with this work for additional information 5# regarding copyright ownership. The ASF licenses this file 6# to you under the Apache License, Version 2.0 (the 7# "License"); you may not use this file except in compliance 8# with the License. You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, 13# software distributed under the License is distributed on an 14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15# KIND, either express or implied. See the License for the 16# specific language governing permissions and limitations 17# under the License. 18# ==================================================================== 19 20require "English" 21require "svn/error" 22require "svn/util" 23require "svn/core" 24require "svn/delta" 25require "svn/ext/wc" 26 27module Svn 28 module Wc 29 Util.set_constants(Ext::Wc, self) 30 Util.set_methods(Ext::Wc, self) 31 self.swig_init_asp_dot_net_hack() 32 33 @@alias_targets = %w(parse_externals_description 34 ensure_adm cleanup) 35 class << self 36 @@alias_targets.each do |target| 37 alias_method "_#{target}", target 38 end 39 end 40 @@alias_targets.each do |target| 41 alias_method "_#{target}", target 42 end 43 @@alias_targets = nil 44 45 module_function 46 def locked?(path) 47 Wc.locked(path) 48 end 49 50 def ensure_adm(*args) 51 AdmAccess.ensure(*args) 52 end 53 54 # For backward compatibility 55 def parse_externals_description(*args) 56 ExternalsDescription.parse(*args) 57 end 58 59 def actual_target(path) 60 Wc.get_actual_target(path) 61 end 62 63 def normal_prop?(name) 64 Wc.is_normal_prop(name) 65 end 66 67 def wc_prop?(name) 68 Wc.is_wc_prop(name) 69 end 70 71 def entry_prop?(name) 72 Wc.is_entry_prop(name) 73 end 74 75 def pristine_copy_path(path) 76 Wc.get_pristine_copy_path(path) 77 end 78 79 def default_ignores(config) 80 Wc.get_default_ignores(config) 81 end 82 83 def cleanup(path, diff3_cmd=nil, cancel_func=nil) 84 Wc.cleanup2(path, diff3_cmd, cancel_func) 85 end 86 87 def ignore?(path, patterns) 88 Wc.match_ignore_list(path, patterns) 89 end 90 91 module ExternalsDescription 92 module_function 93 def parse(parent_dir, desc, canonicalize_url=true) 94 Wc.parse_externals_description3(parent_dir, desc, canonicalize_url) 95 end 96 end 97 98 class ExternalItem 99 class << self 100 undef new 101 end 102 end 103 104 AdmAccess = SWIG::TYPE_p_svn_wc_adm_access_t 105 class AdmAccess 106 class << self 107 def ensure(path, uuid, url, repos, revision, depth=nil) 108 Wc.ensure_adm3(path, uuid, url, repos, revision, depth) 109 end 110 111 def open(associated, path, write_lock=true, 112 depth=-1, cancel_func=nil, &block) 113 _open(:adm_open3, associated, path, write_lock, 114 depth, cancel_func, &block) 115 end 116 117 def probe_open(associated, path, write_lock=true, depth=-1, 118 cancel_func=nil, &block) 119 _open(:adm_probe_open3, associated, path, write_lock, 120 depth, cancel_func, &block) 121 end 122 123 def open_anchor(path, write_lock=true, depth=-1, 124 cancel_func=nil, &block) 125 _open(:adm_open_anchor, path, write_lock, depth, 126 cancel_func, &block) 127 end 128 129 private 130 def _open(name, *args, &block) 131 results = Wc.__send__(name, *args, &block) 132 adm, *rest = results 133 134 if block_given? 135 begin 136 yield *results 137 ensure 138 adm.close 139 end 140 else 141 results 142 end 143 end 144 end 145 146 attr_accessor :traversal_info 147 148 def open(*args, &block) 149 self.class.open(self, *args, &block) 150 end 151 152 def probe_open(*args, &block) 153 self.class.probe_open(self, *args, &block) 154 end 155 156 def retrieve(path) 157 Wc.adm_retrieve(self, path) 158 end 159 160 def probe_retrieve(path) 161 Wc.adm_probe_retrieve(self, path) 162 end 163 164 def probe_try(path, write_lock, depth, &cancel_func) 165 Wc.adm_probe_try3(self, path, write_lock, depth, cancel_func) 166 end 167 168 def close 169 Wc.adm_close(self) 170 end 171 172 def path 173 Wc.adm_access_path(self) 174 end 175 176 def locked? 177 Wc.adm_locked(self) 178 end 179 180 def has_binary_prop?(path) 181 Wc.has_binary_prop(path, self) 182 end 183 184 def text_modified?(filename, force=false) 185 Wc.text_modified_p(filename, force, self) 186 end 187 188 def props_modified?(path) 189 Wc.props_modified_p(path, self) 190 end 191 192 def entry(path, show_hidden=false) 193 Entry.new(path, self, show_hidden, Svn::Core::Pool.new) 194 end 195 196 def read_entries(show_hidden=false) 197 Wc.entries_read(self, show_hidden, Svn::Core::Pool.new) 198 end 199 200 def ancestry(path) 201 Wc.get_ancestry(path, self) 202 end 203 204 def walk_entries(path, callbacks, show_hidden=false, cancel_func=nil, 205 depth=nil) 206 Wc.walk_entries3(path, self, callbacks, depth, show_hidden, 207 cancel_func) 208 end 209 210 def mark_missing_deleted(path) 211 Wc.mark_missing_deleted(path, self) 212 end 213 214 def maybe_set_repos_root(path, repos) 215 Wc.maybe_set_repos_root(self, path, repos) 216 end 217 218 def status(path) 219 Wc.status2(path, self) 220 end 221 222 def status_editor(target, config, recurse=true, 223 get_all=true, no_ignore=true, 224 cancel_func=nil, traversal_info=nil) 225 traversal_info ||= _traversal_info 226 status_func = Proc.new do |path, status| 227 yield(path, status) 228 end 229 ret = Wc.get_status_editor2(self, target, config, recurse, 230 get_all, no_ignore, status_func, 231 cancel_func, traversal_info) 232 editor, editor_baton, set_lock_baton = *ret 233 editor.instance_variable_set("@__status_fun__", status_func) 234 editor.baton = editor_baton 235 def set_lock_baton.set_repos_locks(locks, repos_root) 236 Wc.status_set_repos_locks(self, locks, repos_root) 237 end 238 [editor, set_lock_baton] 239 end 240 241 def copy(src, dst_basename, cancel_func=nil, notify_func=nil) 242 Wc.copy2(src, self, dst_basename, cancel_func, notify_func) 243 end 244 245 def delete(path, cancel_func=nil, notify_func=nil, keep_local=false) 246 Wc.delete3(path, self, cancel_func, notify_func, keep_local) 247 end 248 249 def add(path, copyfrom_url=nil, copyfrom_rev=0, 250 cancel_func=nil, notify_func=nil) 251 Wc.add2(path, self, copyfrom_url, copyfrom_rev, 252 cancel_func, notify_func) 253 end 254 255 def add_repos_file(dst_path, new_text_path, new_props, 256 copyfrom_url=nil, copyfrom_rev=0) 257 Wc.add_repos_file(dst_path, self, new_text_path, 258 new_props, copyfrom_url, copyfrom_rev) 259 end 260 261 def add_repos_file2(dst_path, new_text_base_path, new_base_props, 262 new_text_path=nil, new_props=nil, 263 copyfrom_url=nil, copyfrom_rev=0) 264 Wc.add_repos_file2(dst_path, self, 265 new_text_base_path, new_text_path, 266 new_base_props, new_props, 267 copyfrom_url, copyfrom_rev) 268 end 269 270 def remove_from_revision_control(name, destroy_wf=true, 271 instant_error=true, 272 cancel_func=nil) 273 Wc.remove_from_revision_control(self, name, 274 destroy_wf, 275 instant_error, 276 cancel_func) 277 end 278 279 def resolved_conflict(path, resolve_text=true, 280 resolve_props=true, recurse=true, 281 notify_func=nil, cancel_func=nil) 282 Wc.resolved_conflict2(path, self, resolve_text, 283 resolve_props, recurse, 284 notify_func, cancel_func) 285 end 286 287 def process_committed(path, new_revnum, rev_date=nil, rev_author=nil, 288 wcprop_changes=[], recurse=true, 289 remove_lock=true, digest=nil, 290 remove_changelist=false) 291 Wc.process_committed4(path, self, recurse, 292 new_revnum, rev_date, 293 rev_author, wcprop_changes, 294 remove_lock, remove_changelist, digest) 295 end 296 297 def crawl_revisions(path, reporter, restore_files=true, 298 depth=nil, use_commit_times=true, 299 notify_func=nil, traversal_info=nil) 300 traversal_info ||= _traversal_info 301 Wc.crawl_revisions3(path, self, reporter, reporter.baton, 302 restore_files, depth, use_commit_times, 303 notify_func, traversal_info) 304 end 305 306 def wc_root?(path) 307 Wc.is_wc_root(path, self) 308 end 309 310 def update_editor(target, target_revision=nil, use_commit_times=nil, 311 depth=nil, allow_unver_obstruction=nil, diff3_cmd=nil, 312 notify_func=nil, cancel_func=nil, traversal_info=nil, 313 preserved_exts=nil) 314 update_editor2(:target => target, 315 :target_revision => target_revision, 316 :use_commit_times => use_commit_times, 317 :depth => depth, 318 :allow_unver_obstruction => allow_unver_obstruction, 319 :diff3_cmd => diff3_cmd, 320 :notify_func => notify_func, 321 :cancel_func => cancel_func, 322 :traversal_info => traversal_info, 323 :preserved_exts => preserved_exts ) 324 end 325 326 UPDATE_EDITOR2_REQUIRED_ARGUMENTS_KEYS = [:target] 327 def update_editor2(arguments={}) 328 arguments = arguments.reject {|k, v| v.nil?} 329 optional_arguments_defaults = { 330 :target_revision => nil, 331 :use_commit_times => true, 332 :depth => nil, 333 :depth_is_sticky => false, 334 :allow_unver_obstruction => false, 335 :diff3_cmd => nil, 336 :notify_func => nil, 337 :cancel_func => nil, 338 :conflict_func => nil, 339 :traversal_info => _traversal_info, 340 :preserved_exts => [] 341 } 342 343 arguments = optional_arguments_defaults.merge(arguments) 344 Util.validate_options(arguments, 345 optional_arguments_defaults.keys, 346 UPDATE_EDITOR2_REQUIRED_ARGUMENTS_KEYS) 347 348 # TODO(rb support fetch_fun): implement support for the fetch_func 349 # callback. 350 arguments[:fetch_func] = nil 351 352 results = Wc.get_update_editor3(arguments[:target_revision], self, 353 arguments[:target], 354 arguments[:use_commit_times], 355 arguments[:depth], 356 arguments[:depth_is_sticky], 357 arguments[:allow_unver_obstruction], 358 arguments[:notify_func], 359 arguments[:cancel_func], 360 arguments[:conflict_func], 361 arguments[:fetch_func], 362 arguments[:diff3_cmd], 363 arguments[:preserved_exts], 364 arguments[:traversal_info]) 365 target_revision_address, editor, editor_baton = results 366 editor.__send__(:target_revision_address=, target_revision_address) 367 editor.baton = editor_baton 368 editor 369 end 370 371 def switch_editor(target, switch_url, target_revision=nil, 372 use_commit_times=nil, depth=nil, 373 allow_unver_obstruction=nil, diff3_cmd=nil, 374 notify_func=nil, cancel_func=nil, traversal_info=nil, 375 preserved_exts=nil) 376 switch_editor2(:target => target, 377 :switch_url => switch_url, 378 :target_revision => target_revision, 379 :use_commit_times => use_commit_times, 380 :depth => depth, 381 :allow_unver_obstruction => allow_unver_obstruction, 382 :diff3_cmd => diff3_cmd, 383 :notify_func => notify_func, 384 :cancel_func => cancel_func, 385 :traversal_info => traversal_info, 386 :preserved_exts => preserved_exts ) 387 end 388 389 SWITCH_EDITOR2_REQUIRED_ARGUMENTS_KEYS = [:target, :switch_url] 390 def switch_editor2(arguments={}) 391 arguments = arguments.reject {|k, v| v.nil?} 392 optional_arguments_defaults = { 393 :target_revision => nil, 394 :use_commit_times => true, 395 :depth => nil, 396 :depth_is_sticky => false, 397 :allow_unver_obstruction => false, 398 :diff3_cmd => nil, 399 :notify_func => nil, 400 :cancel_func => nil, 401 :conflict_func => nil, 402 :traversal_info => _traversal_info, 403 :preserved_exts => [] 404 } 405 arguments = optional_arguments_defaults.merge(arguments) 406 Util.validate_options(arguments, 407 optional_arguments_defaults.keys, 408 SWITCH_EDITOR2_REQUIRED_ARGUMENTS_KEYS) 409 410 results = Wc.get_switch_editor3(arguments[:target_revision], self, 411 arguments[:target], 412 arguments[:switch_url], 413 arguments[:use_commit_times], 414 arguments[:depth], 415 arguments[:depth_is_sticky], 416 arguments[:allow_unver_obstruction], 417 arguments[:notify_func], 418 arguments[:cancel_func], 419 arguments[:conflict_func], 420 arguments[:diff3_cmd], 421 arguments[:preserved_exts], 422 arguments[:traversal_info]) 423 target_revision_address, editor, editor_baton = results 424 editor.__send__(:target_revision_address=, target_revision_address) 425 editor.baton = editor_baton 426 editor 427 end 428 429 def prop_list(path) 430 Wc.prop_list(path, self) 431 end 432 433 def prop(name, path) 434 Wc.prop_get(name, path, self) 435 end 436 437 def set_prop(name, value, path, skip_checks=false) 438 Wc.prop_set2(name, value, path, self, skip_checks) 439 end 440 441 def diff_editor(target, callbacks, depth=nil, 442 ignore_ancestry=true, use_text_base=false, 443 reverse_order=false, cancel_func=nil) 444 callbacks_wrapper = DiffCallbacksWrapper.new(callbacks) 445 args = [target, callbacks_wrapper, depth, ignore_ancestry, 446 use_text_base, reverse_order, cancel_func] 447 diff_editor2(*args) 448 end 449 450 def diff_editor2(target, callbacks, depth=nil, 451 ignore_ancestry=true, use_text_base=false, 452 reverse_order=false, cancel_func=nil, changelists=nil) 453 editor, editor_baton = Wc.get_diff_editor4(self, target, callbacks, 454 depth, ignore_ancestry, 455 use_text_base, reverse_order, 456 cancel_func, changelists) 457 editor.baton = editor_baton 458 editor 459 end 460 461 def diff(target, callbacks, recurse=true, ignore_ancestry=true) 462 callbacks_wrapper = DiffCallbacksWrapper.new(callbacks) 463 args = [target, callbacks_wrapper, recurse, ignore_ancestry] 464 diff2(*args) 465 end 466 467 def diff2(target, callbacks, recurse=true, ignore_ancestry=true) 468 Wc.diff3(self, target, callbacks, recurse, ignore_ancestry) 469 end 470 471 def prop_diffs(path) 472 Wc.get_prop_diffs(path, self) 473 end 474 475 def merge(left, right, merge_target, left_label, 476 right_label, target_label, dry_run=false, 477 diff3_cmd=nil, merge_options=nil) 478 Wc.merge2(left, right, merge_target, self, 479 left_label, right_label, target_label, 480 dry_run, diff3_cmd, merge_options) 481 end 482 483 def merge_props(path, baseprops, propchanges, base_merge=true, 484 dry_run=false) 485 Wc.merge_props(path, self, baseprops, propchanges, 486 base_merge, dry_run) 487 end 488 489 def merge_prop_diffs(path, propchanges, base_merge=true, 490 dry_run=false) 491 Wc.merge_prop_diffs(path, self, propchanges, 492 base_merge, dry_run) 493 end 494 495 def relocate(path, from, to, recurse=true, old_validator=nil, &validator) 496 if validator.nil? and !old_validator.nil? 497 validator = Proc.new do |uuid, url, root_url| 498 old_validator.call(uuid, 499 root_url ? root_url : url, 500 root_url ? true : false) 501 end 502 end 503 Wc.relocate3(path, self, from, to, recurse, validator) 504 end 505 506 def revert(path, recurse=true, use_commit_times=true, 507 cancel_func=nil, notify_func=nil) 508 Wc.revert2(path, self, recurse, use_commit_times, 509 cancel_func, notify_func) 510 end 511 512 def translated_file(src, versioned_file, flags) 513 temp = Wc.translated_file2(src, versioned_file, self, flags) 514 temp.close 515 path = temp.path 516 path.instance_variable_set("@__temp__", temp) 517 path 518 end 519 520 def translated_file2(src, versioned_file, flags) 521 Wc.translated_file2(src, versioned_file, self, flags) 522 end 523 524 def translated_stream(path, versioned_file, flags) 525 Wc.translated_stream(path, versioned_file, self, flags) 526 end 527 528 def transmit_text_deltas(path, editor, file_baton, fulltext=false) 529 editor.baton = file_baton 530 Wc.transmit_text_deltas(path, self, fulltext, editor) 531 end 532 533 def transmit_text_deltas2(path, editor, fulltext=false) 534 Wc.transmit_text_deltas2(path, self, fulltext, editor) 535 end 536 537 def transmit_prop_deltas(path, entry, editor, baton=nil) 538 editor.baton = baton if baton 539 Wc.transmit_prop_deltas(path, self, entry, editor) 540 end 541 542 def ignores(config) 543 Wc.get_ignores(config, self) 544 end 545 546 def add_lock(path, lock) 547 Wc.add_lock(path, lock, self) 548 end 549 550 def remove_lock(path) 551 Wc.remove_lock(path, self) 552 end 553 554 def set_changelist(path, changelist_name, cancel_func=nil, 555 notify_func=nil) 556 Wc.set_changelist(path, changelist_name, self, cancel_func, 557 notify_func) 558 end 559 560 private 561 def _traversal_info 562 @traversal_info ||= nil 563 end 564 end 565 566 class DiffCallbacksWrapper 567 def initialize(original) 568 @original = original 569 end 570 571 def file_changed(access, path, tmpfile1, tmpfile2, rev1, 572 rev2, mimetype1, mimetype2, 573 prop_changes, original_props) 574 prop_changes = Util.hash_to_prop_array(prop_changes) 575 @original.file_changed(access, path, tmpfile1, tmpfile2, rev1, 576 rev2, mimetype1, mimetype2, 577 prop_changes, original_props) 578 end 579 580 def file_added(access, path, tmpfile1, tmpfile2, rev1, 581 rev2, mimetype1, mimetype2, 582 prop_changes, original_props) 583 prop_changes = Util.hash_to_prop_array(prop_changes) 584 @original.file_added(access, path, tmpfile1, tmpfile2, rev1, 585 rev2, mimetype1, mimetype2, 586 prop_changes, original_props) 587 end 588 589 def dir_props_changed(access, path, prop_changes, original_props) 590 prop_changes = Util.hash_to_prop_array(prop_changes) 591 @original.dir_props_changed(access, path, prop_changes, original_props) 592 end 593 594 def method_missing(method, *args, &block) 595 @original.__send__(method, *args, &block) 596 end 597 end 598 599 class TraversalInfo 600 def edited_externals 601 Wc.edited_externals(self) 602 end 603 end 604 605 class Entry 606 def dup 607 Wc.entry_dup(self, Svn::Core::Pool.new) 608 end 609 610 def conflicted(dir_path) 611 Wc.conflicted_p(dir_path, self) 612 end 613 614 def conflicted?(dir_path) 615 conflicted(dir_path).any? {|x| x} 616 end 617 618 def text_conflicted?(dir_path) 619 conflicted(dir_path)[0] 620 end 621 622 def prop_conflicted?(dir_path) 623 conflicted(dir_path)[1] 624 end 625 626 def dir? 627 kind == Core::NODE_DIR 628 end 629 630 def file? 631 kind == Core::NODE_FILE 632 end 633 634 def add? 635 schedule == SCHEDULE_ADD 636 end 637 638 def normal? 639 schedule == SCHEDULE_NORMAL 640 end 641 end 642 643 class Status2 644 def dup 645 Wc.dup_status2(self, Core::Pool.new) 646 end 647 648 def text_added? 649 text_status == STATUS_ADDED 650 end 651 652 def text_normal? 653 text_status == STATUS_NORMAL 654 end 655 end 656 657 class Notify 658 def dup 659 Wc.dup_notify(self, Core::Pool.new) 660 end 661 662 def commit_added? 663 action == NOTIFY_COMMIT_ADDED 664 end 665 666 def commit_deleted? 667 action == NOTIFY_COMMIT_DELETED 668 end 669 670 def commit_postfix_txdelta? 671 action == NOTIFY_COMMIT_POSTFIX_TXDELTA 672 end 673 674 def add? 675 action == NOTIFY_ADD 676 end 677 678 def locked? 679 lock_state = NOTIFY_LOCK_STATE_LOCKED 680 end 681 682 def unlocked? 683 lock_state = NOTIFY_LOCK_STATE_UNLOCKED 684 end 685 end 686 687 class RevisionStatus 688 alias _initialize initialize 689 def initialize(wc_path, trail_url, committed, cancel_func=nil) 690 _initialize(wc_path, trail_url, committed, cancel_func) 691 end 692 end 693 694 class CommittedQueue 695 def push(access, path, recurse=true, wcprop_changes={}, remove_lock=true, 696 remove_changelist=false, digest=nil) 697 Wc.queue_committed(self, path, access, recurse, wcprop_changes, 698 remove_lock, remove_changelist, digest) 699 self 700 end 701 702 def process(access, new_rev, rev_date=nil, rev_author=nil) 703 rev_date = rev_date.to_svn_format if rev_date.is_a?(Time) 704 Wc.process_committed_queue(self, access, new_rev, rev_date, rev_author) 705 end 706 end 707 708 Context = SWIG::TYPE_p_svn_wc_context_t 709 # A context is not associated with a particular working copy, but as 710 # operations are performed, will load the appropriate working copy 711 # information. 712 class Context 713 class << self 714 715 # Creates an new instance of Context. 716 # 717 # ==== arguments 718 # 719 # * <tt>:config</tt> <i>(default=>nil)</i> A \ 720 # Svn::Core::Config with options that apply to this Context. 721 def new(arguments={}) 722 optional_arguments_defaults = { :config => nil } 723 arguments = optional_arguments_defaults.merge(arguments) 724 context = Wc.context_create(arguments[:config]) 725 return context 726 end 727 728 # Creates an new instance of Context for use in the block, the context 729 # is destroyed when the block completes. 730 # 731 # ==== arguments 732 # 733 # see new. 734 def create(arguments={}) 735 context = new(arguments) 736 begin 737 yield context 738 ensure 739 context.destroy if context 740 end 741 end 742 743 end 744 745 # Destroys the context, releasing any acquired resources. 746 # The context is unavailable for any further operations. 747 def destroy 748 Wc.context_destroy(self) 749 end 750 end 751 752 end 753end 754