1# Copyright (C) 2006-2011 Canonical Ltd 2# 3# This program is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 2 of the License, or 6# (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 16 17"""BzrDir logic. The BzrDir is the basic control directory used by bzr. 18 19At format 7 this was split out into Branch, Repository and Checkout control 20directories. 21 22Note: This module has a lot of ``open`` functions/methods that return 23references to in-memory objects. As a rule, there are no matching ``close`` 24methods. To free any associated resources, simply stop referencing the 25objects returned. 26""" 27 28import sys 29 30from ..lazy_import import lazy_import 31lazy_import(globals(), """ 32import contextlib 33 34from breezy import ( 35 branch as _mod_branch, 36 lockable_files, 37 lockdir, 38 osutils, 39 repository, 40 revision as _mod_revision, 41 transport as _mod_transport, 42 ui, 43 urlutils, 44 win32utils, 45 ) 46from breezy.bzr import ( 47 branch as _mod_bzrbranch, 48 fetch, 49 remote, 50 vf_search, 51 workingtree_3, 52 workingtree_4, 53 ) 54from breezy.bzr import fullhistory as fullhistorybranch 55from breezy.bzr import knitpack_repo 56from breezy.transport import ( 57 do_catching_redirections, 58 local, 59 ) 60from breezy.i18n import gettext 61""") 62 63from ..trace import ( 64 mutter, 65 note, 66 warning, 67 ) 68 69from .. import ( 70 config, 71 controldir, 72 errors, 73 ) 74 75 76class MissingFeature(errors.BzrError): 77 78 _fmt = ("Missing feature %(feature)s not provided by this " 79 "version of Bazaar or any plugin.") 80 81 def __init__(self, feature): 82 self.feature = feature 83 84 85class FeatureAlreadyRegistered(errors.BzrError): 86 87 _fmt = 'The feature %(feature)s has already been registered.' 88 89 def __init__(self, feature): 90 self.feature = feature 91 92 93class BzrDir(controldir.ControlDir): 94 """A .bzr control diretory. 95 96 BzrDir instances let you create or open any of the things that can be 97 found within .bzr - checkouts, branches and repositories. 98 99 :ivar transport: 100 the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/) 101 :ivar root_transport: 102 a transport connected to the directory this bzr was opened from 103 (i.e. the parent directory holding the .bzr directory). 104 105 Everything in the bzrdir should have the same file permissions. 106 107 :cvar hooks: An instance of BzrDirHooks. 108 """ 109 110 def break_lock(self): 111 """Invoke break_lock on the first object in the bzrdir. 112 113 If there is a tree, the tree is opened and break_lock() called. 114 Otherwise, branch is tried, and finally repository. 115 """ 116 # XXX: This seems more like a UI function than something that really 117 # belongs in this class. 118 try: 119 thing_to_unlock = self.open_workingtree() 120 except (errors.NotLocalUrl, errors.NoWorkingTree): 121 try: 122 thing_to_unlock = self.open_branch() 123 except errors.NotBranchError: 124 try: 125 thing_to_unlock = self.open_repository() 126 except errors.NoRepositoryPresent: 127 return 128 thing_to_unlock.break_lock() 129 130 def check_conversion_target(self, target_format): 131 """Check that a bzrdir as a whole can be converted to a new format.""" 132 # The only current restriction is that the repository content can be 133 # fetched compatibly with the target. 134 target_repo_format = target_format.repository_format 135 try: 136 self.open_repository()._format.check_conversion_target( 137 target_repo_format) 138 except errors.NoRepositoryPresent: 139 # No repo, no problem. 140 pass 141 142 def clone_on_transport(self, transport, revision_id=None, 143 force_new_repo=False, preserve_stacking=False, stacked_on=None, 144 create_prefix=False, use_existing_dir=True, no_tree=False, 145 tag_selector=None): 146 """Clone this bzrdir and its contents to transport verbatim. 147 148 :param transport: The transport for the location to produce the clone 149 at. If the target directory does not exist, it will be created. 150 :param revision_id: The tip revision-id to use for any branch or 151 working tree. If not None, then the clone operation may tune 152 itself to download less data. 153 :param force_new_repo: Do not use a shared repository for the target, 154 even if one is available. 155 :param preserve_stacking: When cloning a stacked branch, stack the 156 new branch on top of the other branch's stacked-on branch. 157 :param create_prefix: Create any missing directories leading up to 158 to_transport. 159 :param use_existing_dir: Use an existing directory if one exists. 160 :param no_tree: If set to true prevents creation of a working tree. 161 """ 162 # Overview: put together a broad description of what we want to end up 163 # with; then make as few api calls as possible to do it. 164 165 # We may want to create a repo/branch/tree, if we do so what format 166 # would we want for each: 167 require_stacking = (stacked_on is not None) 168 format = self.cloning_metadir(require_stacking) 169 170 # Figure out what objects we want: 171 try: 172 local_repo = self.find_repository() 173 except errors.NoRepositoryPresent: 174 local_repo = None 175 local_branches = self.get_branches() 176 try: 177 local_active_branch = local_branches[''] 178 except KeyError: 179 pass 180 else: 181 # enable fallbacks when branch is not a branch reference 182 if local_active_branch.repository.has_same_location(local_repo): 183 local_repo = local_active_branch.repository 184 if preserve_stacking: 185 try: 186 stacked_on = local_active_branch.get_stacked_on_url() 187 except (_mod_branch.UnstackableBranchFormat, 188 errors.UnstackableRepositoryFormat, 189 errors.NotStacked): 190 pass 191 # Bug: We create a metadir without knowing if it can support stacking, 192 # we should look up the policy needs first, or just use it as a hint, 193 # or something. 194 if local_repo: 195 make_working_trees = (local_repo.make_working_trees() and 196 not no_tree) 197 want_shared = local_repo.is_shared() 198 repo_format_name = format.repository_format.network_name() 199 else: 200 make_working_trees = False 201 want_shared = False 202 repo_format_name = None 203 204 result_repo, result, require_stacking, repository_policy = \ 205 format.initialize_on_transport_ex( 206 transport, use_existing_dir=use_existing_dir, 207 create_prefix=create_prefix, force_new_repo=force_new_repo, 208 stacked_on=stacked_on, stack_on_pwd=self.root_transport.base, 209 repo_format_name=repo_format_name, 210 make_working_trees=make_working_trees, shared_repo=want_shared) 211 if repo_format_name: 212 try: 213 # If the result repository is in the same place as the 214 # resulting bzr dir, it will have no content, further if the 215 # result is not stacked then we know all content should be 216 # copied, and finally if we are copying up to a specific 217 # revision_id then we can use the pending-ancestry-result which 218 # does not require traversing all of history to describe it. 219 if (result_repo.user_url == result.user_url 220 and not require_stacking 221 and revision_id is not None): 222 fetch_spec = vf_search.PendingAncestryResult( 223 [revision_id], local_repo) 224 result_repo.fetch(local_repo, fetch_spec=fetch_spec) 225 else: 226 result_repo.fetch(local_repo, revision_id=revision_id) 227 finally: 228 result_repo.unlock() 229 else: 230 if result_repo is not None: 231 raise AssertionError('result_repo not None(%r)' % result_repo) 232 # 1 if there is a branch present 233 # make sure its content is available in the target repository 234 # clone it. 235 for name, local_branch in local_branches.items(): 236 local_branch.clone( 237 result, revision_id=(None if name != '' else revision_id), 238 repository_policy=repository_policy, 239 name=name, tag_selector=tag_selector) 240 try: 241 # Cheaper to check if the target is not local, than to try making 242 # the tree and fail. 243 result.root_transport.local_abspath('.') 244 if result_repo is None or result_repo.make_working_trees(): 245 self.open_workingtree().clone(result, revision_id=revision_id) 246 except (errors.NoWorkingTree, errors.NotLocalUrl): 247 pass 248 return result 249 250 # TODO: This should be given a Transport, and should chdir up; otherwise 251 # this will open a new connection. 252 def _make_tail(self, url): 253 t = _mod_transport.get_transport(url) 254 t.ensure_base() 255 256 def determine_repository_policy(self, force_new_repo=False, stack_on=None, 257 stack_on_pwd=None, require_stacking=False): 258 """Return an object representing a policy to use. 259 260 This controls whether a new repository is created, and the format of 261 that repository, or some existing shared repository used instead. 262 263 If stack_on is supplied, will not seek a containing shared repo. 264 265 :param force_new_repo: If True, require a new repository to be created. 266 :param stack_on: If supplied, the location to stack on. If not 267 supplied, a default_stack_on location may be used. 268 :param stack_on_pwd: If stack_on is relative, the location it is 269 relative to. 270 """ 271 def repository_policy(found_bzrdir): 272 stack_on = None 273 stack_on_pwd = None 274 config = found_bzrdir.get_config() 275 stop = False 276 stack_on = config.get_default_stack_on() 277 if stack_on is not None: 278 stack_on_pwd = found_bzrdir.user_url 279 stop = True 280 # does it have a repository ? 281 try: 282 repository = found_bzrdir.open_repository() 283 except errors.NoRepositoryPresent: 284 repository = None 285 else: 286 if (found_bzrdir.user_url != self.user_url and 287 not repository.is_shared()): 288 # Don't look higher, can't use a higher shared repo. 289 repository = None 290 stop = True 291 else: 292 stop = True 293 if not stop: 294 return None, False 295 if repository: 296 return UseExistingRepository( 297 repository, stack_on, stack_on_pwd, 298 require_stacking=require_stacking), True 299 else: 300 return CreateRepository( 301 self, stack_on, stack_on_pwd, 302 require_stacking=require_stacking), True 303 304 if not force_new_repo: 305 if stack_on is None: 306 policy = self._find_containing(repository_policy) 307 if policy is not None: 308 return policy 309 else: 310 try: 311 return UseExistingRepository( 312 self.open_repository(), stack_on, stack_on_pwd, 313 require_stacking=require_stacking) 314 except errors.NoRepositoryPresent: 315 pass 316 return CreateRepository(self, stack_on, stack_on_pwd, 317 require_stacking=require_stacking) 318 319 def _find_or_create_repository(self, force_new_repo): 320 """Create a new repository if needed, returning the repository.""" 321 policy = self.determine_repository_policy(force_new_repo) 322 return policy.acquire_repository()[0] 323 324 def _find_source_repo(self, exit_stack, source_branch): 325 """Find the source branch and repo for a sprout operation. 326 327 This is helper intended for use by _sprout. 328 329 :returns: (source_branch, source_repository). Either or both may be 330 None. If not None, they will be read-locked (and their unlock(s) 331 scheduled via the exit_stack param). 332 """ 333 if source_branch is not None: 334 exit_stack.enter_context(source_branch.lock_read()) 335 return source_branch, source_branch.repository 336 try: 337 source_branch = self.open_branch() 338 source_repository = source_branch.repository 339 except errors.NotBranchError: 340 source_branch = None 341 try: 342 source_repository = self.open_repository() 343 except errors.NoRepositoryPresent: 344 source_repository = None 345 else: 346 exit_stack.enter_context(source_repository.lock_read()) 347 else: 348 exit_stack.enter_context(source_branch.lock_read()) 349 return source_branch, source_repository 350 351 def sprout(self, url, revision_id=None, force_new_repo=False, 352 recurse='down', possible_transports=None, 353 accelerator_tree=None, hardlink=False, stacked=False, 354 source_branch=None, create_tree_if_local=True, 355 lossy=False): 356 """Create a copy of this controldir prepared for use as a new line of 357 development. 358 359 If url's last component does not exist, it will be created. 360 361 Attributes related to the identity of the source branch like 362 branch nickname will be cleaned, a working tree is created 363 whether one existed before or not; and a local branch is always 364 created. 365 366 if revision_id is not None, then the clone operation may tune 367 itself to download less data. 368 369 :param accelerator_tree: A tree which can be used for retrieving file 370 contents more quickly than the revision tree, i.e. a workingtree. 371 The revision tree will be used for cases where accelerator_tree's 372 content is different. 373 :param hardlink: If true, hard-link files from accelerator_tree, 374 where possible. 375 :param stacked: If true, create a stacked branch referring to the 376 location of this control directory. 377 :param create_tree_if_local: If true, a working-tree will be created 378 when working locally. 379 :return: The created control directory 380 """ 381 with contextlib.ExitStack() as stack: 382 fetch_spec_factory = fetch.FetchSpecFactory() 383 if revision_id is not None: 384 fetch_spec_factory.add_revision_ids([revision_id]) 385 fetch_spec_factory.source_branch_stop_revision_id = revision_id 386 if possible_transports is None: 387 possible_transports = [] 388 else: 389 possible_transports = list(possible_transports) + [ 390 self.root_transport] 391 target_transport = _mod_transport.get_transport(url, 392 possible_transports) 393 target_transport.ensure_base() 394 cloning_format = self.cloning_metadir(stacked) 395 # Create/update the result branch 396 try: 397 result = controldir.ControlDir.open_from_transport( 398 target_transport) 399 except errors.NotBranchError: 400 result = cloning_format.initialize_on_transport(target_transport) 401 source_branch, source_repository = self._find_source_repo( 402 stack, source_branch) 403 fetch_spec_factory.source_branch = source_branch 404 # if a stacked branch wasn't requested, we don't create one 405 # even if the origin was stacked 406 if stacked and source_branch is not None: 407 stacked_branch_url = self.root_transport.base 408 else: 409 stacked_branch_url = None 410 repository_policy = result.determine_repository_policy( 411 force_new_repo, stacked_branch_url, require_stacking=stacked) 412 result_repo, is_new_repo = repository_policy.acquire_repository( 413 possible_transports=possible_transports) 414 stack.enter_context(result_repo.lock_write()) 415 fetch_spec_factory.source_repo = source_repository 416 fetch_spec_factory.target_repo = result_repo 417 if stacked or (len(result_repo._fallback_repositories) != 0): 418 target_repo_kind = fetch.TargetRepoKinds.STACKED 419 elif is_new_repo: 420 target_repo_kind = fetch.TargetRepoKinds.EMPTY 421 else: 422 target_repo_kind = fetch.TargetRepoKinds.PREEXISTING 423 fetch_spec_factory.target_repo_kind = target_repo_kind 424 if source_repository is not None: 425 fetch_spec = fetch_spec_factory.make_fetch_spec() 426 result_repo.fetch(source_repository, fetch_spec=fetch_spec) 427 428 if source_branch is None: 429 # this is for sprouting a controldir without a branch; is that 430 # actually useful? 431 # Not especially, but it's part of the contract. 432 result_branch = result.create_branch() 433 if revision_id is not None: 434 result_branch.generate_revision_history(revision_id) 435 else: 436 result_branch = source_branch.sprout( 437 result, revision_id=revision_id, 438 repository_policy=repository_policy, repository=result_repo) 439 mutter("created new branch %r" % (result_branch,)) 440 441 # Create/update the result working tree 442 if (create_tree_if_local and not result.has_workingtree() 443 and isinstance(target_transport, local.LocalTransport) 444 and (result_repo is None or result_repo.make_working_trees()) 445 and result.open_branch( 446 name="", 447 possible_transports=possible_transports).name == result_branch.name): 448 wt = result.create_workingtree( 449 accelerator_tree=accelerator_tree, hardlink=hardlink, 450 from_branch=result_branch) 451 with wt.lock_write(): 452 if not wt.is_versioned(''): 453 try: 454 wt.set_root_id(self.open_workingtree.path2id('')) 455 except errors.NoWorkingTree: 456 pass 457 else: 458 wt = None 459 if recurse == 'down': 460 tree = None 461 if wt is not None: 462 tree = wt 463 basis = tree.basis_tree() 464 stack.enter_context(basis.lock_read()) 465 elif result_branch is not None: 466 basis = tree = result_branch.basis_tree() 467 elif source_branch is not None: 468 basis = tree = source_branch.basis_tree() 469 if tree is not None: 470 stack.enter_context(tree.lock_read()) 471 subtrees = tree.iter_references() 472 else: 473 subtrees = [] 474 for path in subtrees: 475 target = urlutils.join(url, urlutils.escape(path)) 476 sublocation = tree.reference_parent( 477 path, branch=result_branch, 478 possible_transports=possible_transports) 479 if sublocation is None: 480 warning( 481 'Ignoring nested tree %s, parent location unknown.', 482 path) 483 continue 484 sublocation.controldir.sprout( 485 target, basis.get_reference_revision(path), 486 force_new_repo=force_new_repo, recurse=recurse, 487 stacked=stacked) 488 return result 489 490 def _available_backup_name(self, base): 491 """Find a non-existing backup file name based on base. 492 493 See breezy.osutils.available_backup_name about race conditions. 494 """ 495 return osutils.available_backup_name(base, self.root_transport.has) 496 497 def backup_bzrdir(self): 498 """Backup this bzr control directory. 499 500 :return: Tuple with old path name and new path name 501 """ 502 503 with ui.ui_factory.nested_progress_bar(): 504 old_path = self.root_transport.abspath('.bzr') 505 backup_dir = self._available_backup_name('backup.bzr') 506 new_path = self.root_transport.abspath(backup_dir) 507 ui.ui_factory.note( 508 gettext('making backup of {0}\n to {1}').format( 509 urlutils.unescape_for_display(old_path, 'utf-8'), 510 urlutils.unescape_for_display(new_path, 'utf-8'))) 511 self.root_transport.copy_tree('.bzr', backup_dir) 512 return (old_path, new_path) 513 514 def retire_bzrdir(self, limit=10000): 515 """Permanently disable the bzrdir. 516 517 This is done by renaming it to give the user some ability to recover 518 if there was a problem. 519 520 This will have horrible consequences if anyone has anything locked or 521 in use. 522 :param limit: number of times to retry 523 """ 524 i = 0 525 while True: 526 try: 527 to_path = '.bzr.retired.%d' % i 528 self.root_transport.rename('.bzr', to_path) 529 note(gettext("renamed {0} to {1}").format( 530 self.root_transport.abspath('.bzr'), to_path)) 531 return 532 except (errors.TransportError, IOError, errors.PathError): 533 i += 1 534 if i > limit: 535 raise 536 else: 537 pass 538 539 def _find_containing(self, evaluate): 540 """Find something in a containing control directory. 541 542 This method will scan containing control dirs, until it finds what 543 it is looking for, decides that it will never find it, or runs out 544 of containing control directories to check. 545 546 It is used to implement find_repository and 547 determine_repository_policy. 548 549 :param evaluate: A function returning (value, stop). If stop is True, 550 the value will be returned. 551 """ 552 found_bzrdir = self 553 while True: 554 result, stop = evaluate(found_bzrdir) 555 if stop: 556 return result 557 next_transport = found_bzrdir.root_transport.clone('..') 558 if (found_bzrdir.user_url == next_transport.base): 559 # top of the file system 560 return None 561 # find the next containing bzrdir 562 try: 563 found_bzrdir = self.open_containing_from_transport( 564 next_transport)[0] 565 except errors.NotBranchError: 566 return None 567 568 def find_repository(self): 569 """Find the repository that should be used. 570 571 This does not require a branch as we use it to find the repo for 572 new branches as well as to hook existing branches up to their 573 repository. 574 """ 575 def usable_repository(found_bzrdir): 576 # does it have a repository ? 577 try: 578 repository = found_bzrdir.open_repository() 579 except errors.NoRepositoryPresent: 580 return None, False 581 if found_bzrdir.user_url == self.user_url: 582 return repository, True 583 elif repository.is_shared(): 584 return repository, True 585 else: 586 return None, True 587 588 found_repo = self._find_containing(usable_repository) 589 if found_repo is None: 590 raise errors.NoRepositoryPresent(self) 591 return found_repo 592 593 def _find_creation_modes(self): 594 """Determine the appropriate modes for files and directories. 595 596 They're always set to be consistent with the base directory, 597 assuming that this transport allows setting modes. 598 """ 599 # TODO: Do we need or want an option (maybe a config setting) to turn 600 # this off or override it for particular locations? -- mbp 20080512 601 if self._mode_check_done: 602 return 603 self._mode_check_done = True 604 try: 605 st = self.transport.stat('.') 606 except errors.TransportNotPossible: 607 self._dir_mode = None 608 self._file_mode = None 609 else: 610 # Check the directory mode, but also make sure the created 611 # directories and files are read-write for this user. This is 612 # mostly a workaround for filesystems which lie about being able to 613 # write to a directory (cygwin & win32) 614 if (st.st_mode & 0o7777 == 00000): 615 # FTP allows stat but does not return dir/file modes 616 self._dir_mode = None 617 self._file_mode = None 618 else: 619 self._dir_mode = (st.st_mode & 0o7777) | 0o0700 620 # Remove the sticky and execute bits for files 621 self._file_mode = self._dir_mode & ~0o7111 622 623 def _get_file_mode(self): 624 """Return Unix mode for newly created files, or None. 625 """ 626 if not self._mode_check_done: 627 self._find_creation_modes() 628 return self._file_mode 629 630 def _get_dir_mode(self): 631 """Return Unix mode for newly created directories, or None. 632 """ 633 if not self._mode_check_done: 634 self._find_creation_modes() 635 return self._dir_mode 636 637 def get_config(self): 638 """Get configuration for this BzrDir.""" 639 return config.BzrDirConfig(self) 640 641 def _get_config(self): 642 """By default, no configuration is available.""" 643 return None 644 645 def __init__(self, _transport, _format): 646 """Initialize a Bzr control dir object. 647 648 Only really common logic should reside here, concrete classes should be 649 made with varying behaviours. 650 651 :param _format: the format that is creating this BzrDir instance. 652 :param _transport: the transport this dir is based at. 653 """ 654 self._format = _format 655 # these are also under the more standard names of 656 # control_transport and user_transport 657 self.transport = _transport.clone('.bzr') 658 self.root_transport = _transport 659 self._mode_check_done = False 660 661 @property 662 def user_transport(self): 663 return self.root_transport 664 665 @property 666 def control_transport(self): 667 return self.transport 668 669 def _cloning_metadir(self): 670 """Produce a metadir suitable for cloning with. 671 672 :returns: (destination_bzrdir_format, source_repository) 673 """ 674 result_format = self._format.__class__() 675 try: 676 try: 677 branch = self.open_branch(ignore_fallbacks=True) 678 source_repository = branch.repository 679 result_format._branch_format = branch._format 680 except errors.NotBranchError: 681 source_repository = self.open_repository() 682 except errors.NoRepositoryPresent: 683 source_repository = None 684 else: 685 # XXX TODO: This isinstance is here because we have not implemented 686 # the fix recommended in bug # 103195 - to delegate this choice the 687 # repository itself. 688 repo_format = source_repository._format 689 if isinstance(repo_format, remote.RemoteRepositoryFormat): 690 source_repository._ensure_real() 691 repo_format = source_repository._real_repository._format 692 result_format.repository_format = repo_format 693 try: 694 # TODO: Couldn't we just probe for the format in these cases, 695 # rather than opening the whole tree? It would be a little 696 # faster. mbp 20070401 697 tree = self.open_workingtree(recommend_upgrade=False) 698 except (errors.NoWorkingTree, errors.NotLocalUrl): 699 result_format.workingtree_format = None 700 else: 701 result_format.workingtree_format = tree._format.__class__() 702 return result_format, source_repository 703 704 def cloning_metadir(self, require_stacking=False): 705 """Produce a metadir suitable for cloning or sprouting with. 706 707 These operations may produce workingtrees (yes, even though they're 708 "cloning" something that doesn't have a tree), so a viable workingtree 709 format must be selected. 710 711 :require_stacking: If True, non-stackable formats will be upgraded 712 to similar stackable formats. 713 :returns: a ControlDirFormat with all component formats either set 714 appropriately or set to None if that component should not be 715 created. 716 """ 717 format, repository = self._cloning_metadir() 718 if format._workingtree_format is None: 719 # No tree in self. 720 if repository is None: 721 # No repository either 722 return format 723 # We have a repository, so set a working tree? (Why? This seems to 724 # contradict the stated return value in the docstring). 725 tree_format = ( 726 repository._format._matchingcontroldir.workingtree_format) 727 format.workingtree_format = tree_format.__class__() 728 if require_stacking: 729 format.require_stacking() 730 return format 731 732 def get_branch_transport(self, branch_format, name=None): 733 """Get the transport for use by branch format in this BzrDir. 734 735 Note that bzr dirs that do not support format strings will raise 736 IncompatibleFormat if the branch format they are given has 737 a format string, and vice versa. 738 739 If branch_format is None, the transport is returned with no 740 checking. If it is not None, then the returned transport is 741 guaranteed to point to an existing directory ready for use. 742 """ 743 raise NotImplementedError(self.get_branch_transport) 744 745 def get_repository_transport(self, repository_format): 746 """Get the transport for use by repository format in this BzrDir. 747 748 Note that bzr dirs that do not support format strings will raise 749 IncompatibleFormat if the repository format they are given has 750 a format string, and vice versa. 751 752 If repository_format is None, the transport is returned with no 753 checking. If it is not None, then the returned transport is 754 guaranteed to point to an existing directory ready for use. 755 """ 756 raise NotImplementedError(self.get_repository_transport) 757 758 def get_workingtree_transport(self, tree_format): 759 """Get the transport for use by workingtree format in this BzrDir. 760 761 Note that bzr dirs that do not support format strings will raise 762 IncompatibleFormat if the workingtree format they are given has a 763 format string, and vice versa. 764 765 If workingtree_format is None, the transport is returned with no 766 checking. If it is not None, then the returned transport is 767 guaranteed to point to an existing directory ready for use. 768 """ 769 raise NotImplementedError(self.get_workingtree_transport) 770 771 @classmethod 772 def create(cls, base, format=None, possible_transports=None): 773 """Create a new BzrDir at the url 'base'. 774 775 :param format: If supplied, the format of branch to create. If not 776 supplied, the default is used. 777 :param possible_transports: If supplied, a list of transports that 778 can be reused to share a remote connection. 779 """ 780 if cls is not BzrDir: 781 raise AssertionError("BzrDir.create always creates the " 782 "default format, not one of %r" % cls) 783 return controldir.ControlDir.create( 784 base, format=format, possible_transports=possible_transports) 785 786 def __repr__(self): 787 return "<%s at %r>" % (self.__class__.__name__, self.user_url) 788 789 def update_feature_flags(self, updated_flags): 790 """Update the features required by this bzrdir. 791 792 :param updated_flags: Dictionary mapping feature names to necessities 793 A necessity can be None to indicate the feature should be removed 794 """ 795 self.control_files.lock_write() 796 try: 797 self._format._update_feature_flags(updated_flags) 798 self.transport.put_bytes('branch-format', self._format.as_string()) 799 finally: 800 self.control_files.unlock() 801 802 803class BzrDirMeta1(BzrDir): 804 """A .bzr meta version 1 control object. 805 806 This is the first control object where the 807 individual aspects are really split out: there are separate repository, 808 workingtree and branch subdirectories and any subset of the three can be 809 present within a BzrDir. 810 """ 811 812 def _get_branch_path(self, name): 813 """Obtain the branch path to use. 814 815 This uses the API specified branch name first, and then falls back to 816 the branch name specified in the URL. If neither of those is specified, 817 it uses the default branch. 818 819 :param name: Optional branch name to use 820 :return: Relative path to branch 821 """ 822 if name == "": 823 return 'branch' 824 return urlutils.join('branches', urlutils.escape(name)) 825 826 def _read_branch_list(self): 827 """Read the branch list. 828 829 :return: List of branch names. 830 """ 831 try: 832 f = self.control_transport.get('branch-list') 833 except errors.NoSuchFile: 834 return [] 835 836 ret = [] 837 try: 838 for name in f: 839 ret.append(name.rstrip(b"\n").decode('utf-8')) 840 finally: 841 f.close() 842 return ret 843 844 def _write_branch_list(self, branches): 845 """Write out the branch list. 846 847 :param branches: List of utf-8 branch names to write 848 """ 849 self.transport.put_bytes( 850 'branch-list', 851 b"".join([name.encode('utf-8') + b"\n" for name in branches])) 852 853 def __init__(self, _transport, _format): 854 super(BzrDirMeta1, self).__init__(_transport, _format) 855 self.control_files = lockable_files.LockableFiles( 856 self.control_transport, self._format._lock_file_name, 857 self._format._lock_class) 858 859 def can_convert_format(self): 860 """See BzrDir.can_convert_format().""" 861 return True 862 863 def create_branch(self, name=None, repository=None, 864 append_revisions_only=None): 865 """See ControlDir.create_branch.""" 866 if name is None: 867 name = self._get_selected_branch() 868 return self._format.get_branch_format().initialize( 869 self, name=name, repository=repository, 870 append_revisions_only=append_revisions_only) 871 872 def destroy_branch(self, name=None): 873 """See ControlDir.destroy_branch.""" 874 if name is None: 875 name = self._get_selected_branch() 876 path = self._get_branch_path(name) 877 if name != u"": 878 self.control_files.lock_write() 879 try: 880 branches = self._read_branch_list() 881 try: 882 branches.remove(name) 883 except ValueError: 884 raise errors.NotBranchError(name) 885 self._write_branch_list(branches) 886 finally: 887 self.control_files.unlock() 888 try: 889 self.transport.delete_tree(path) 890 except errors.NoSuchFile: 891 raise errors.NotBranchError( 892 path=urlutils.join(self.transport.base, path), controldir=self) 893 894 def create_repository(self, shared=False): 895 """See BzrDir.create_repository.""" 896 return self._format.repository_format.initialize(self, shared) 897 898 def destroy_repository(self): 899 """See BzrDir.destroy_repository.""" 900 try: 901 self.transport.delete_tree('repository') 902 except errors.NoSuchFile: 903 raise errors.NoRepositoryPresent(self) 904 905 def create_workingtree(self, revision_id=None, from_branch=None, 906 accelerator_tree=None, hardlink=False): 907 """See BzrDir.create_workingtree.""" 908 return self._format.workingtree_format.initialize( 909 self, revision_id, from_branch=from_branch, 910 accelerator_tree=accelerator_tree, hardlink=hardlink) 911 912 def destroy_workingtree(self): 913 """See BzrDir.destroy_workingtree.""" 914 wt = self.open_workingtree(recommend_upgrade=False) 915 repository = wt.branch.repository 916 empty = repository.revision_tree(_mod_revision.NULL_REVISION) 917 # We ignore the conflicts returned by wt.revert since we're about to 918 # delete the wt metadata anyway, all that should be left here are 919 # detritus. But see bug #634470 about subtree .bzr dirs. 920 wt.revert(old_tree=empty) 921 self.destroy_workingtree_metadata() 922 923 def destroy_workingtree_metadata(self): 924 self.transport.delete_tree('checkout') 925 926 def find_branch_format(self, name=None): 927 """Find the branch 'format' for this bzrdir. 928 929 This might be a synthetic object for e.g. RemoteBranch and SVN. 930 """ 931 from .branch import BranchFormatMetadir 932 return BranchFormatMetadir.find_format(self, name=name) 933 934 def _get_mkdir_mode(self): 935 """Figure out the mode to use when creating a bzrdir subdir.""" 936 temp_control = lockable_files.LockableFiles( 937 self.transport, '', lockable_files.TransportLock) 938 return temp_control._dir_mode 939 940 def get_branch_reference(self, name=None): 941 """See BzrDir.get_branch_reference().""" 942 from .branch import BranchFormatMetadir 943 format = BranchFormatMetadir.find_format(self, name=name) 944 return format.get_reference(self, name=name) 945 946 def set_branch_reference(self, target_branch, name=None): 947 format = _mod_bzrbranch.BranchReferenceFormat() 948 if (self.control_url == target_branch.controldir.control_url 949 and name == target_branch.name): 950 raise controldir.BranchReferenceLoop(target_branch) 951 return format.initialize(self, target_branch=target_branch, name=name) 952 953 def get_branch_transport(self, branch_format, name=None): 954 """See BzrDir.get_branch_transport().""" 955 if name is None: 956 name = self._get_selected_branch() 957 path = self._get_branch_path(name) 958 # XXX: this shouldn't implicitly create the directory if it's just 959 # promising to get a transport -- mbp 20090727 960 if branch_format is None: 961 return self.transport.clone(path) 962 try: 963 branch_format.get_format_string() 964 except NotImplementedError: 965 raise errors.IncompatibleFormat(branch_format, self._format) 966 if name != "": 967 branches = self._read_branch_list() 968 if name not in branches: 969 self.control_files.lock_write() 970 try: 971 branches = self._read_branch_list() 972 dirname = urlutils.dirname(name) 973 if dirname != u"" and dirname in branches: 974 raise errors.ParentBranchExists(name) 975 child_branches = [ 976 b.startswith(name + u"/") for b in branches] 977 if any(child_branches): 978 raise errors.AlreadyBranchError(name) 979 branches.append(name) 980 self._write_branch_list(branches) 981 finally: 982 self.control_files.unlock() 983 branch_transport = self.transport.clone(path) 984 mode = self._get_mkdir_mode() 985 branch_transport.create_prefix(mode=mode) 986 try: 987 self.transport.mkdir(path, mode=mode) 988 except errors.FileExists: 989 pass 990 return self.transport.clone(path) 991 992 def get_repository_transport(self, repository_format): 993 """See BzrDir.get_repository_transport().""" 994 if repository_format is None: 995 return self.transport.clone('repository') 996 try: 997 repository_format.get_format_string() 998 except NotImplementedError: 999 raise errors.IncompatibleFormat(repository_format, self._format) 1000 try: 1001 self.transport.mkdir('repository', mode=self._get_mkdir_mode()) 1002 except errors.FileExists: 1003 pass 1004 return self.transport.clone('repository') 1005 1006 def get_workingtree_transport(self, workingtree_format): 1007 """See BzrDir.get_workingtree_transport().""" 1008 if workingtree_format is None: 1009 return self.transport.clone('checkout') 1010 try: 1011 workingtree_format.get_format_string() 1012 except NotImplementedError: 1013 raise errors.IncompatibleFormat(workingtree_format, self._format) 1014 try: 1015 self.transport.mkdir('checkout', mode=self._get_mkdir_mode()) 1016 except errors.FileExists: 1017 pass 1018 return self.transport.clone('checkout') 1019 1020 def branch_names(self): 1021 """See ControlDir.branch_names.""" 1022 ret = [] 1023 try: 1024 self.get_branch_reference() 1025 except errors.NotBranchError: 1026 pass 1027 else: 1028 ret.append("") 1029 ret.extend(self._read_branch_list()) 1030 return ret 1031 1032 def get_branches(self): 1033 """See ControlDir.get_branches.""" 1034 ret = {} 1035 try: 1036 ret[""] = self.open_branch(name="") 1037 except (errors.NotBranchError, errors.NoRepositoryPresent): 1038 pass 1039 1040 for name in self._read_branch_list(): 1041 ret[name] = self.open_branch(name=name) 1042 1043 return ret 1044 1045 def has_workingtree(self): 1046 """Tell if this bzrdir contains a working tree. 1047 1048 Note: if you're going to open the working tree, you should just go 1049 ahead and try, and not ask permission first. 1050 """ 1051 from .workingtree import WorkingTreeFormatMetaDir 1052 try: 1053 WorkingTreeFormatMetaDir.find_format_string(self) 1054 except errors.NoWorkingTree: 1055 return False 1056 return True 1057 1058 def needs_format_conversion(self, format): 1059 """See BzrDir.needs_format_conversion().""" 1060 if (not isinstance(self._format, format.__class__) 1061 or self._format.get_format_string() != format.get_format_string()): 1062 # it is not a meta dir format, conversion is needed. 1063 return True 1064 # we might want to push this down to the repository? 1065 try: 1066 if not isinstance(self.open_repository()._format, 1067 format.repository_format.__class__): 1068 # the repository needs an upgrade. 1069 return True 1070 except errors.NoRepositoryPresent: 1071 pass 1072 for branch in self.list_branches(): 1073 if not isinstance(branch._format, 1074 format.get_branch_format().__class__): 1075 # the branch needs an upgrade. 1076 return True 1077 try: 1078 my_wt = self.open_workingtree(recommend_upgrade=False) 1079 if not isinstance(my_wt._format, 1080 format.workingtree_format.__class__): 1081 # the workingtree needs an upgrade. 1082 return True 1083 except (errors.NoWorkingTree, errors.NotLocalUrl): 1084 pass 1085 return False 1086 1087 def open_branch(self, name=None, unsupported=False, 1088 ignore_fallbacks=False, possible_transports=None): 1089 """See ControlDir.open_branch.""" 1090 if name is None: 1091 name = self._get_selected_branch() 1092 format = self.find_branch_format(name=name) 1093 format.check_support_status(unsupported) 1094 if possible_transports is None: 1095 possible_transports = [] 1096 else: 1097 possible_transports = list(possible_transports) 1098 possible_transports.append(self.root_transport) 1099 return format.open(self, name=name, 1100 _found=True, ignore_fallbacks=ignore_fallbacks, 1101 possible_transports=possible_transports) 1102 1103 def open_repository(self, unsupported=False): 1104 """See BzrDir.open_repository.""" 1105 from .repository import RepositoryFormatMetaDir 1106 format = RepositoryFormatMetaDir.find_format(self) 1107 format.check_support_status(unsupported) 1108 return format.open(self, _found=True) 1109 1110 def open_workingtree(self, unsupported=False, 1111 recommend_upgrade=True): 1112 """See BzrDir.open_workingtree.""" 1113 from .workingtree import WorkingTreeFormatMetaDir 1114 format = WorkingTreeFormatMetaDir.find_format(self) 1115 format.check_support_status(unsupported, recommend_upgrade, 1116 basedir=self.root_transport.base) 1117 return format.open(self, _found=True) 1118 1119 def _get_config(self): 1120 return config.TransportConfig(self.transport, 'control.conf') 1121 1122 1123class BzrFormat(object): 1124 """Base class for all formats of things living in metadirs. 1125 1126 This class manages the format string that is stored in the 'format' 1127 or 'branch-format' file. 1128 1129 All classes for (branch-, repository-, workingtree-) formats that 1130 live in meta directories and have their own 'format' file 1131 (i.e. different from .bzr/branch-format) derive from this class, 1132 as well as the relevant base class for their kind 1133 (BranchFormat, WorkingTreeFormat, RepositoryFormat). 1134 1135 Each format is identified by a "format" or "branch-format" file with a 1136 single line containing the base format name and then an optional list of 1137 feature flags. 1138 1139 Feature flags are supported as of bzr 2.5. Setting feature flags on formats 1140 will render them inaccessible to older versions of bzr. 1141 1142 :ivar features: Dictionary mapping feature names to their necessity 1143 """ 1144 1145 _present_features = set() 1146 1147 def __init__(self): 1148 self.features = {} 1149 1150 @classmethod 1151 def register_feature(cls, name): 1152 """Register a feature as being present. 1153 1154 :param name: Name of the feature 1155 """ 1156 if b" " in name: 1157 raise ValueError("spaces are not allowed in feature names") 1158 if name in cls._present_features: 1159 raise FeatureAlreadyRegistered(name) 1160 cls._present_features.add(name) 1161 1162 @classmethod 1163 def unregister_feature(cls, name): 1164 """Unregister a feature.""" 1165 cls._present_features.remove(name) 1166 1167 def check_support_status(self, allow_unsupported, recommend_upgrade=True, 1168 basedir=None): 1169 for name, necessity in self.features.items(): 1170 if name in self._present_features: 1171 continue 1172 if necessity == b"optional": 1173 mutter("ignoring optional missing feature %s", name) 1174 continue 1175 elif necessity == b"required": 1176 raise MissingFeature(name) 1177 else: 1178 mutter("treating unknown necessity as require for %s", 1179 name) 1180 raise MissingFeature(name) 1181 1182 @classmethod 1183 def get_format_string(cls): 1184 """Return the ASCII format string that identifies this format.""" 1185 raise NotImplementedError(cls.get_format_string) 1186 1187 @classmethod 1188 def from_string(cls, text): 1189 format_string = cls.get_format_string() 1190 if not text.startswith(format_string): 1191 raise AssertionError( 1192 "Invalid format header %r for %r" % (text, cls)) 1193 lines = text[len(format_string):].splitlines() 1194 ret = cls() 1195 for lineno, line in enumerate(lines): 1196 try: 1197 (necessity, feature) = line.split(b" ", 1) 1198 except ValueError: 1199 raise errors.ParseFormatError(format=cls, lineno=lineno + 2, 1200 line=line, text=text) 1201 ret.features[feature] = necessity 1202 return ret 1203 1204 def as_string(self): 1205 """Return the string representation of this format. 1206 """ 1207 lines = [self.get_format_string()] 1208 lines.extend([(item[1] + b" " + item[0] + b"\n") 1209 for item in sorted(self.features.items())]) 1210 return b"".join(lines) 1211 1212 @classmethod 1213 def _find_format(klass, registry, kind, format_string): 1214 try: 1215 first_line = format_string[:format_string.index(b"\n") + 1] 1216 except ValueError: 1217 first_line = format_string 1218 try: 1219 cls = registry.get(first_line) 1220 except KeyError: 1221 raise errors.UnknownFormatError(format=first_line, kind=kind) 1222 return cls.from_string(format_string) 1223 1224 def network_name(self): 1225 """A simple byte string uniquely identifying this format for RPC calls. 1226 1227 Metadir branch formats use their format string. 1228 """ 1229 return self.as_string() 1230 1231 def __eq__(self, other): 1232 return (self.__class__ is other.__class__ 1233 and self.features == other.features) 1234 1235 def _update_feature_flags(self, updated_flags): 1236 """Update the feature flags in this format. 1237 1238 :param updated_flags: Updated feature flags 1239 """ 1240 for name, necessity in updated_flags.items(): 1241 if necessity is None: 1242 try: 1243 del self.features[name] 1244 except KeyError: 1245 pass 1246 else: 1247 self.features[name] = necessity 1248 1249 1250class BzrDirFormat(BzrFormat, controldir.ControlDirFormat): 1251 """ControlDirFormat base class for .bzr/ directories. 1252 1253 Formats are placed in a dict by their format string for reference 1254 during bzrdir opening. These should be subclasses of BzrDirFormat 1255 for consistency. 1256 1257 Once a format is deprecated, just deprecate the initialize and open 1258 methods on the format class. Do not deprecate the object, as the 1259 object will be created every system load. 1260 """ 1261 1262 _lock_file_name = 'branch-lock' 1263 1264 # _lock_class must be set in subclasses to the lock type, typ. 1265 # TransportLock or LockDir 1266 1267 def initialize_on_transport(self, transport): 1268 """Initialize a new bzrdir in the base directory of a Transport.""" 1269 try: 1270 # can we hand off the request to the smart server rather than using 1271 # vfs calls? 1272 client_medium = transport.get_smart_medium() 1273 except errors.NoSmartMedium: 1274 return self._initialize_on_transport_vfs(transport) 1275 else: 1276 # Current RPC's only know how to create bzr metadir1 instances, so 1277 # we still delegate to vfs methods if the requested format is not a 1278 # metadir1 1279 if not isinstance(self, BzrDirMetaFormat1): 1280 return self._initialize_on_transport_vfs(transport) 1281 from .remote import RemoteBzrDirFormat 1282 remote_format = RemoteBzrDirFormat() 1283 self._supply_sub_formats_to(remote_format) 1284 return remote_format.initialize_on_transport(transport) 1285 1286 def initialize_on_transport_ex(self, transport, use_existing_dir=False, 1287 create_prefix=False, force_new_repo=False, stacked_on=None, 1288 stack_on_pwd=None, repo_format_name=None, make_working_trees=None, 1289 shared_repo=False, vfs_only=False): 1290 """Create this format on transport. 1291 1292 The directory to initialize will be created. 1293 1294 :param force_new_repo: Do not use a shared repository for the target, 1295 even if one is available. 1296 :param create_prefix: Create any missing directories leading up to 1297 to_transport. 1298 :param use_existing_dir: Use an existing directory if one exists. 1299 :param stacked_on: A url to stack any created branch on, None to follow 1300 any target stacking policy. 1301 :param stack_on_pwd: If stack_on is relative, the location it is 1302 relative to. 1303 :param repo_format_name: If non-None, a repository will be 1304 made-or-found. Should none be found, or if force_new_repo is True 1305 the repo_format_name is used to select the format of repository to 1306 create. 1307 :param make_working_trees: Control the setting of make_working_trees 1308 for a new shared repository when one is made. None to use whatever 1309 default the format has. 1310 :param shared_repo: Control whether made repositories are shared or 1311 not. 1312 :param vfs_only: If True do not attempt to use a smart server 1313 :return: repo, controldir, require_stacking, repository_policy. repo is 1314 None if none was created or found, bzrdir is always valid. 1315 require_stacking is the result of examining the stacked_on 1316 parameter and any stacking policy found for the target. 1317 """ 1318 if not vfs_only: 1319 # Try to hand off to a smart server 1320 try: 1321 client_medium = transport.get_smart_medium() 1322 except errors.NoSmartMedium: 1323 pass 1324 else: 1325 from .remote import RemoteBzrDirFormat 1326 # TODO: lookup the local format from a server hint. 1327 remote_dir_format = RemoteBzrDirFormat() 1328 remote_dir_format._network_name = self.network_name() 1329 self._supply_sub_formats_to(remote_dir_format) 1330 return remote_dir_format.initialize_on_transport_ex( 1331 transport, use_existing_dir=use_existing_dir, 1332 create_prefix=create_prefix, force_new_repo=force_new_repo, 1333 stacked_on=stacked_on, stack_on_pwd=stack_on_pwd, 1334 repo_format_name=repo_format_name, 1335 make_working_trees=make_working_trees, 1336 shared_repo=shared_repo) 1337 # XXX: Refactor the create_prefix/no_create_prefix code into a 1338 # common helper function 1339 # The destination may not exist - if so make it according to policy. 1340 1341 def make_directory(transport): 1342 transport.mkdir('.') 1343 return transport 1344 1345 def redirected(transport, e, redirection_notice): 1346 note(redirection_notice) 1347 return transport._redirected_to(e.source, e.target) 1348 try: 1349 transport = do_catching_redirections(make_directory, transport, 1350 redirected) 1351 except errors.FileExists: 1352 if not use_existing_dir: 1353 raise 1354 except errors.NoSuchFile: 1355 if not create_prefix: 1356 raise 1357 transport.create_prefix() 1358 1359 require_stacking = (stacked_on is not None) 1360 # Now the target directory exists, but doesn't have a .bzr 1361 # directory. So we need to create it, along with any work to create 1362 # all of the dependent branches, etc. 1363 1364 result = self.initialize_on_transport(transport) 1365 if repo_format_name: 1366 try: 1367 # use a custom format 1368 result._format.repository_format = \ 1369 repository.network_format_registry.get(repo_format_name) 1370 except AttributeError: 1371 # The format didn't permit it to be set. 1372 pass 1373 # A repository is desired, either in-place or shared. 1374 repository_policy = result.determine_repository_policy( 1375 force_new_repo, stacked_on, stack_on_pwd, 1376 require_stacking=require_stacking) 1377 result_repo, is_new_repo = repository_policy.acquire_repository( 1378 make_working_trees, shared_repo) 1379 if not require_stacking and repository_policy._require_stacking: 1380 require_stacking = True 1381 result._format.require_stacking() 1382 result_repo.lock_write() 1383 else: 1384 result_repo = None 1385 repository_policy = None 1386 return result_repo, result, require_stacking, repository_policy 1387 1388 def _initialize_on_transport_vfs(self, transport): 1389 """Initialize a new bzrdir using VFS calls. 1390 1391 :param transport: The transport to create the .bzr directory in. 1392 :return: A 1393 """ 1394 # Since we are creating a .bzr directory, inherit the 1395 # mode from the root directory 1396 temp_control = lockable_files.LockableFiles(transport, 1397 '', lockable_files.TransportLock) 1398 try: 1399 temp_control._transport.mkdir('.bzr', 1400 # FIXME: RBC 20060121 don't peek under 1401 # the covers 1402 mode=temp_control._dir_mode) 1403 except errors.FileExists: 1404 raise errors.AlreadyControlDirError(transport.base) 1405 if sys.platform == 'win32' and isinstance(transport, local.LocalTransport): 1406 win32utils.set_file_attr_hidden(transport._abspath('.bzr')) 1407 file_mode = temp_control._file_mode 1408 del temp_control 1409 bzrdir_transport = transport.clone('.bzr') 1410 utf8_files = [('README', 1411 b"This is a Bazaar control directory.\n" 1412 b"Do not change any files in this directory.\n" 1413 b"See http://bazaar.canonical.com/ for more information about Bazaar.\n"), 1414 ('branch-format', self.as_string()), 1415 ] 1416 # NB: no need to escape relative paths that are url safe. 1417 control_files = lockable_files.LockableFiles(bzrdir_transport, 1418 self._lock_file_name, self._lock_class) 1419 control_files.create_lock() 1420 control_files.lock_write() 1421 try: 1422 for (filename, content) in utf8_files: 1423 bzrdir_transport.put_bytes(filename, content, 1424 mode=file_mode) 1425 finally: 1426 control_files.unlock() 1427 return self.open(transport, _found=True) 1428 1429 def open(self, transport, _found=False): 1430 """Return an instance of this format for the dir transport points at. 1431 1432 _found is a private parameter, do not use it. 1433 """ 1434 if not _found: 1435 found_format = controldir.ControlDirFormat.find_format(transport) 1436 if not isinstance(found_format, self.__class__): 1437 raise AssertionError("%s was asked to open %s, but it seems to need " 1438 "format %s" 1439 % (self, transport, found_format)) 1440 # Allow subclasses - use the found format. 1441 self._supply_sub_formats_to(found_format) 1442 return found_format._open(transport) 1443 return self._open(transport) 1444 1445 def _open(self, transport): 1446 """Template method helper for opening BzrDirectories. 1447 1448 This performs the actual open and any additional logic or parameter 1449 passing. 1450 """ 1451 raise NotImplementedError(self._open) 1452 1453 def _supply_sub_formats_to(self, other_format): 1454 """Give other_format the same values for sub formats as this has. 1455 1456 This method is expected to be used when parameterising a 1457 RemoteBzrDirFormat instance with the parameters from a 1458 BzrDirMetaFormat1 instance. 1459 1460 :param other_format: other_format is a format which should be 1461 compatible with whatever sub formats are supported by self. 1462 :return: None. 1463 """ 1464 other_format.features = dict(self.features) 1465 1466 def supports_transport(self, transport): 1467 # bzr formats can be opened over all known transports 1468 return True 1469 1470 def check_support_status(self, allow_unsupported, recommend_upgrade=True, 1471 basedir=None): 1472 controldir.ControlDirFormat.check_support_status(self, 1473 allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade, 1474 basedir=basedir) 1475 BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported, 1476 recommend_upgrade=recommend_upgrade, basedir=basedir) 1477 1478 @classmethod 1479 def is_control_filename(klass, filename): 1480 """True if filename is the name of a path which is reserved for bzrdir's. 1481 1482 :param filename: A filename within the root transport of this bzrdir. 1483 1484 This is true IF and ONLY IF the filename is part of the namespace 1485 reserved for bzr control dirs. Currently this is the '.bzr' directory 1486 in the root of the root_transport. 1487 """ 1488 # this might be better on the BzrDirFormat class because it refers to 1489 # all the possible bzrdir disk formats. 1490 # This method is tested via the workingtree is_control_filename tests- 1491 # it was extracted from WorkingTree.is_control_filename. If the 1492 # method's contract is extended beyond the current trivial 1493 # implementation, please add new tests for it to the appropriate place. 1494 return filename == '.bzr' or filename.startswith('.bzr/') 1495 1496 @classmethod 1497 def get_default_format(klass): 1498 """Return the current default format.""" 1499 return controldir.format_registry.get('bzr')() 1500 1501 1502class BzrDirMetaFormat1(BzrDirFormat): 1503 """Bzr meta control format 1 1504 1505 This is the first format with split out working tree, branch and repository 1506 disk storage. 1507 1508 It has: 1509 1510 - Format 3 working trees [optional] 1511 - Format 5 branches [optional] 1512 - Format 7 repositories [optional] 1513 """ 1514 1515 _lock_class = lockdir.LockDir 1516 1517 fixed_components = False 1518 1519 colocated_branches = True 1520 1521 def __init__(self): 1522 BzrDirFormat.__init__(self) 1523 self._workingtree_format = None 1524 self._branch_format = None 1525 self._repository_format = None 1526 1527 def __eq__(self, other): 1528 if other.__class__ is not self.__class__: 1529 return False 1530 if other.repository_format != self.repository_format: 1531 return False 1532 if other.workingtree_format != self.workingtree_format: 1533 return False 1534 if other.features != self.features: 1535 return False 1536 return True 1537 1538 def __ne__(self, other): 1539 return not self == other 1540 1541 def get_branch_format(self): 1542 if self._branch_format is None: 1543 from .branch import format_registry as branch_format_registry 1544 self._branch_format = branch_format_registry.get_default() 1545 return self._branch_format 1546 1547 def set_branch_format(self, format): 1548 self._branch_format = format 1549 1550 def require_stacking(self, stack_on=None, possible_transports=None, 1551 _skip_repo=False): 1552 """We have a request to stack, try to ensure the formats support it. 1553 1554 :param stack_on: If supplied, it is the URL to a branch that we want to 1555 stack on. Check to see if that format supports stacking before 1556 forcing an upgrade. 1557 """ 1558 # Stacking is desired. requested by the target, but does the place it 1559 # points at support stacking? If it doesn't then we should 1560 # not implicitly upgrade. We check this here. 1561 new_repo_format = None 1562 new_branch_format = None 1563 1564 # a bit of state for get_target_branch so that we don't try to open it 1565 # 2 times, for both repo *and* branch 1566 target = [None, False, None] # target_branch, checked, upgrade anyway 1567 1568 def get_target_branch(): 1569 if target[1]: 1570 # We've checked, don't check again 1571 return target 1572 if stack_on is None: 1573 # No target format, that means we want to force upgrading 1574 target[:] = [None, True, True] 1575 return target 1576 try: 1577 target_dir = BzrDir.open(stack_on, 1578 possible_transports=possible_transports) 1579 except errors.NotBranchError: 1580 # Nothing there, don't change formats 1581 target[:] = [None, True, False] 1582 return target 1583 except errors.JailBreak: 1584 # JailBreak, JFDI and upgrade anyway 1585 target[:] = [None, True, True] 1586 return target 1587 try: 1588 target_branch = target_dir.open_branch() 1589 except errors.NotBranchError: 1590 # No branch, don't upgrade formats 1591 target[:] = [None, True, False] 1592 return target 1593 target[:] = [target_branch, True, False] 1594 return target 1595 1596 if (not _skip_repo 1597 and not self.repository_format.supports_external_lookups): 1598 # We need to upgrade the Repository. 1599 target_branch, _, do_upgrade = get_target_branch() 1600 if target_branch is None: 1601 # We don't have a target branch, should we upgrade anyway? 1602 if do_upgrade: 1603 # stack_on is inaccessible, JFDI. 1604 # TODO: bad monkey, hard-coded formats... 1605 if self.repository_format.rich_root_data: 1606 new_repo_format = knitpack_repo.RepositoryFormatKnitPack5RichRoot() 1607 else: 1608 new_repo_format = knitpack_repo.RepositoryFormatKnitPack5() 1609 else: 1610 # If the target already supports stacking, then we know the 1611 # project is already able to use stacking, so auto-upgrade 1612 # for them 1613 new_repo_format = target_branch.repository._format 1614 if not new_repo_format.supports_external_lookups: 1615 # target doesn't, source doesn't, so don't auto upgrade 1616 # repo 1617 new_repo_format = None 1618 if new_repo_format is not None: 1619 self.repository_format = new_repo_format 1620 note(gettext('Source repository format does not support stacking,' 1621 ' using format:\n %s'), 1622 new_repo_format.get_format_description()) 1623 1624 if not self.get_branch_format().supports_stacking(): 1625 # We just checked the repo, now lets check if we need to 1626 # upgrade the branch format 1627 target_branch, _, do_upgrade = get_target_branch() 1628 if target_branch is None: 1629 if do_upgrade: 1630 # TODO: bad monkey, hard-coded formats... 1631 from .branch import BzrBranchFormat7 1632 new_branch_format = BzrBranchFormat7() 1633 else: 1634 new_branch_format = target_branch._format 1635 if not new_branch_format.supports_stacking(): 1636 new_branch_format = None 1637 if new_branch_format is not None: 1638 # Does support stacking, use its format. 1639 self.set_branch_format(new_branch_format) 1640 note(gettext('Source branch format does not support stacking,' 1641 ' using format:\n %s'), 1642 new_branch_format.get_format_description()) 1643 1644 def get_converter(self, format=None): 1645 """See BzrDirFormat.get_converter().""" 1646 if format is None: 1647 format = BzrDirFormat.get_default_format() 1648 if (isinstance(self, BzrDirMetaFormat1) 1649 and isinstance(format, BzrDirMetaFormat1Colo)): 1650 return ConvertMetaToColo(format) 1651 if (isinstance(self, BzrDirMetaFormat1Colo) 1652 and isinstance(format, BzrDirMetaFormat1)): 1653 return ConvertMetaToColo(format) 1654 if not isinstance(self, format.__class__): 1655 # converting away from metadir is not implemented 1656 raise NotImplementedError(self.get_converter) 1657 return ConvertMetaToMeta(format) 1658 1659 @classmethod 1660 def get_format_string(cls): 1661 """See BzrDirFormat.get_format_string().""" 1662 return b"Bazaar-NG meta directory, format 1\n" 1663 1664 def get_format_description(self): 1665 """See BzrDirFormat.get_format_description().""" 1666 return "Meta directory format 1" 1667 1668 def _open(self, transport): 1669 """See BzrDirFormat._open.""" 1670 # Create a new format instance because otherwise initialisation of new 1671 # metadirs share the global default format object leading to alias 1672 # problems. 1673 format = BzrDirMetaFormat1() 1674 self._supply_sub_formats_to(format) 1675 return BzrDirMeta1(transport, format) 1676 1677 def __return_repository_format(self): 1678 """Circular import protection.""" 1679 if self._repository_format: 1680 return self._repository_format 1681 from .repository import format_registry 1682 return format_registry.get_default() 1683 1684 def _set_repository_format(self, value): 1685 """Allow changing the repository format for metadir formats.""" 1686 self._repository_format = value 1687 1688 repository_format = property(__return_repository_format, 1689 _set_repository_format) 1690 1691 def _supply_sub_formats_to(self, other_format): 1692 """Give other_format the same values for sub formats as this has. 1693 1694 This method is expected to be used when parameterising a 1695 RemoteBzrDirFormat instance with the parameters from a 1696 BzrDirMetaFormat1 instance. 1697 1698 :param other_format: other_format is a format which should be 1699 compatible with whatever sub formats are supported by self. 1700 :return: None. 1701 """ 1702 super(BzrDirMetaFormat1, self)._supply_sub_formats_to(other_format) 1703 if getattr(self, '_repository_format', None) is not None: 1704 other_format.repository_format = self.repository_format 1705 if self._branch_format is not None: 1706 other_format._branch_format = self._branch_format 1707 if self._workingtree_format is not None: 1708 other_format.workingtree_format = self.workingtree_format 1709 1710 def __get_workingtree_format(self): 1711 if self._workingtree_format is None: 1712 from .workingtree import ( 1713 format_registry as wt_format_registry, 1714 ) 1715 self._workingtree_format = wt_format_registry.get_default() 1716 return self._workingtree_format 1717 1718 def __set_workingtree_format(self, wt_format): 1719 self._workingtree_format = wt_format 1720 1721 def __repr__(self): 1722 return "<%r>" % (self.__class__.__name__,) 1723 1724 workingtree_format = property(__get_workingtree_format, 1725 __set_workingtree_format) 1726 1727 1728class BzrDirMetaFormat1Colo(BzrDirMetaFormat1): 1729 """BzrDirMeta1 format with support for colocated branches.""" 1730 1731 colocated_branches = True 1732 1733 @classmethod 1734 def get_format_string(cls): 1735 """See BzrDirFormat.get_format_string().""" 1736 return b"Bazaar meta directory, format 1 (with colocated branches)\n" 1737 1738 def get_format_description(self): 1739 """See BzrDirFormat.get_format_description().""" 1740 return "Meta directory format 1 with support for colocated branches" 1741 1742 def _open(self, transport): 1743 """See BzrDirFormat._open.""" 1744 # Create a new format instance because otherwise initialisation of new 1745 # metadirs share the global default format object leading to alias 1746 # problems. 1747 format = BzrDirMetaFormat1Colo() 1748 self._supply_sub_formats_to(format) 1749 return BzrDirMeta1(transport, format) 1750 1751 1752class ConvertMetaToMeta(controldir.Converter): 1753 """Converts the components of metadirs.""" 1754 1755 def __init__(self, target_format): 1756 """Create a metadir to metadir converter. 1757 1758 :param target_format: The final metadir format that is desired. 1759 """ 1760 self.target_format = target_format 1761 1762 def convert(self, to_convert, pb): 1763 """See Converter.convert().""" 1764 self.controldir = to_convert 1765 with ui.ui_factory.nested_progress_bar() as self.pb: 1766 self.count = 0 1767 self.total = 1 1768 self.step('checking repository format') 1769 try: 1770 repo = self.controldir.open_repository() 1771 except errors.NoRepositoryPresent: 1772 pass 1773 else: 1774 repo_fmt = self.target_format.repository_format 1775 if not isinstance(repo._format, repo_fmt.__class__): 1776 from ..repository import CopyConverter 1777 ui.ui_factory.note(gettext('starting repository conversion')) 1778 if not repo_fmt.supports_overriding_transport: 1779 raise AssertionError( 1780 "Repository in metadir does not support " 1781 "overriding transport") 1782 converter = CopyConverter(self.target_format.repository_format) 1783 converter.convert(repo, pb) 1784 for branch in self.controldir.list_branches(): 1785 # TODO: conversions of Branch and Tree should be done by 1786 # InterXFormat lookups/some sort of registry. 1787 # Avoid circular imports 1788 old = branch._format.__class__ 1789 new = self.target_format.get_branch_format().__class__ 1790 while old != new: 1791 if (old == fullhistorybranch.BzrBranchFormat5 1792 and new in (_mod_bzrbranch.BzrBranchFormat6, 1793 _mod_bzrbranch.BzrBranchFormat7, 1794 _mod_bzrbranch.BzrBranchFormat8)): 1795 branch_converter = _mod_bzrbranch.Converter5to6() 1796 elif (old == _mod_bzrbranch.BzrBranchFormat6 1797 and new in (_mod_bzrbranch.BzrBranchFormat7, 1798 _mod_bzrbranch.BzrBranchFormat8)): 1799 branch_converter = _mod_bzrbranch.Converter6to7() 1800 elif (old == _mod_bzrbranch.BzrBranchFormat7 1801 and new is _mod_bzrbranch.BzrBranchFormat8): 1802 branch_converter = _mod_bzrbranch.Converter7to8() 1803 else: 1804 raise errors.BadConversionTarget("No converter", new, 1805 branch._format) 1806 branch_converter.convert(branch) 1807 branch = self.controldir.open_branch() 1808 old = branch._format.__class__ 1809 try: 1810 tree = self.controldir.open_workingtree(recommend_upgrade=False) 1811 except (errors.NoWorkingTree, errors.NotLocalUrl): 1812 pass 1813 else: 1814 # TODO: conversions of Branch and Tree should be done by 1815 # InterXFormat lookups 1816 if (isinstance(tree, workingtree_3.WorkingTree3) 1817 and not isinstance(tree, workingtree_4.DirStateWorkingTree) 1818 and isinstance(self.target_format.workingtree_format, 1819 workingtree_4.DirStateWorkingTreeFormat)): 1820 workingtree_4.Converter3to4().convert(tree) 1821 if (isinstance(tree, workingtree_4.DirStateWorkingTree) 1822 and not isinstance(tree, workingtree_4.WorkingTree5) 1823 and isinstance(self.target_format.workingtree_format, 1824 workingtree_4.WorkingTreeFormat5)): 1825 workingtree_4.Converter4to5().convert(tree) 1826 if (isinstance(tree, workingtree_4.DirStateWorkingTree) 1827 and not isinstance(tree, workingtree_4.WorkingTree6) 1828 and isinstance(self.target_format.workingtree_format, 1829 workingtree_4.WorkingTreeFormat6)): 1830 workingtree_4.Converter4or5to6().convert(tree) 1831 return to_convert 1832 1833 1834class ConvertMetaToColo(controldir.Converter): 1835 """Add colocated branch support.""" 1836 1837 def __init__(self, target_format): 1838 """Create a converter.that upgrades a metadir to the colo format. 1839 1840 :param target_format: The final metadir format that is desired. 1841 """ 1842 self.target_format = target_format 1843 1844 def convert(self, to_convert, pb): 1845 """See Converter.convert().""" 1846 to_convert.transport.put_bytes('branch-format', 1847 self.target_format.as_string()) 1848 return BzrDir.open_from_transport(to_convert.root_transport) 1849 1850 1851class ConvertMetaToColo(controldir.Converter): 1852 """Convert a 'development-colo' bzrdir to a '2a' bzrdir.""" 1853 1854 def __init__(self, target_format): 1855 """Create a converter that converts a 'development-colo' metadir 1856 to a '2a' metadir. 1857 1858 :param target_format: The final metadir format that is desired. 1859 """ 1860 self.target_format = target_format 1861 1862 def convert(self, to_convert, pb): 1863 """See Converter.convert().""" 1864 to_convert.transport.put_bytes('branch-format', 1865 self.target_format.as_string()) 1866 return BzrDir.open_from_transport(to_convert.root_transport) 1867 1868 1869class CreateRepository(controldir.RepositoryAcquisitionPolicy): 1870 """A policy of creating a new repository""" 1871 1872 def __init__(self, controldir, stack_on=None, stack_on_pwd=None, 1873 require_stacking=False): 1874 """Constructor. 1875 1876 :param controldir: The controldir to create the repository on. 1877 :param stack_on: A location to stack on 1878 :param stack_on_pwd: If stack_on is relative, the location it is 1879 relative to. 1880 """ 1881 super(CreateRepository, self).__init__( 1882 stack_on, stack_on_pwd, require_stacking) 1883 self._controldir = controldir 1884 1885 def acquire_repository(self, make_working_trees=None, shared=False, 1886 possible_transports=None): 1887 """Implementation of RepositoryAcquisitionPolicy.acquire_repository 1888 1889 Creates the desired repository in the controldir we already have. 1890 """ 1891 if possible_transports is None: 1892 possible_transports = [] 1893 else: 1894 possible_transports = list(possible_transports) 1895 possible_transports.append(self._controldir.root_transport) 1896 stack_on = self._get_full_stack_on() 1897 if stack_on: 1898 format = self._controldir._format 1899 format.require_stacking(stack_on=stack_on, 1900 possible_transports=possible_transports) 1901 if not self._require_stacking: 1902 # We have picked up automatic stacking somewhere. 1903 note(gettext('Using default stacking branch {0} at {1}').format( 1904 self._stack_on, self._stack_on_pwd)) 1905 repository = self._controldir.create_repository(shared=shared) 1906 self._add_fallback(repository, 1907 possible_transports=possible_transports) 1908 if make_working_trees is not None: 1909 repository.set_make_working_trees(make_working_trees) 1910 return repository, True 1911 1912 1913class UseExistingRepository(controldir.RepositoryAcquisitionPolicy): 1914 """A policy of reusing an existing repository""" 1915 1916 def __init__(self, repository, stack_on=None, stack_on_pwd=None, 1917 require_stacking=False): 1918 """Constructor. 1919 1920 :param repository: The repository to use. 1921 :param stack_on: A location to stack on 1922 :param stack_on_pwd: If stack_on is relative, the location it is 1923 relative to. 1924 """ 1925 super(UseExistingRepository, self).__init__( 1926 stack_on, stack_on_pwd, require_stacking) 1927 self._repository = repository 1928 1929 def acquire_repository(self, make_working_trees=None, shared=False, 1930 possible_transports=None): 1931 """Implementation of RepositoryAcquisitionPolicy.acquire_repository 1932 1933 Returns an existing repository to use. 1934 """ 1935 if possible_transports is None: 1936 possible_transports = [] 1937 else: 1938 possible_transports = list(possible_transports) 1939 possible_transports.append(self._repository.controldir.transport) 1940 self._add_fallback(self._repository, 1941 possible_transports=possible_transports) 1942 return self._repository, False 1943 1944 1945controldir.ControlDirFormat._default_format = BzrDirMetaFormat1() 1946