~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

  • Committer: Jelmer Vernooij
  • Date: 2011-05-10 07:46:15 UTC
  • mfrom: (5844 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5845.
  • Revision ID: jelmer@samba.org-20110510074615-eptod049ndjxc4i7
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import textwrap
28
28
 
29
29
from bzrlib import (
30
 
    cleanup,
31
30
    errors,
32
 
    fetch,
33
 
    graph,
34
31
    revision as _mod_revision,
35
32
    transport as _mod_transport,
36
 
    urlutils,
 
33
    ui,
37
34
    )
38
35
from bzrlib.push import (
39
36
    PushResult,
40
37
    )
41
 
from bzrlib.trace import (
42
 
    mutter,
43
 
    )
44
 
from bzrlib.transport import (
45
 
    local,
46
 
    )
47
38
 
48
39
""")
49
40
 
83
74
        return self.user_transport.base
84
75
 
85
76
 
 
77
 
86
78
class ControlDir(ControlComponent):
87
79
    """A control directory.
88
80
 
335
327
        :param create_tree_if_local: If true, a working-tree will be created
336
328
            when working locally.
337
329
        """
338
 
        operation = cleanup.OperationWithCleanups(self._sprout)
339
 
        return operation.run(url, revision_id=revision_id,
340
 
            force_new_repo=force_new_repo, recurse=recurse,
341
 
            possible_transports=possible_transports,
342
 
            accelerator_tree=accelerator_tree, hardlink=hardlink,
343
 
            stacked=stacked, source_branch=source_branch,
344
 
            create_tree_if_local=create_tree_if_local)
345
 
 
346
 
    def _sprout(self, op, url, revision_id=None, force_new_repo=False,
347
 
               recurse='down', possible_transports=None,
348
 
               accelerator_tree=None, hardlink=False, stacked=False,
349
 
               source_branch=None, create_tree_if_local=True):
350
 
        add_cleanup = op.add_cleanup
351
 
        fetch_spec_factory = fetch.FetchSpecFactory()
352
 
        if revision_id is not None:
353
 
            fetch_spec_factory.add_revision_ids([revision_id])
354
 
            fetch_spec_factory.source_branch_stop_revision_id = revision_id
355
 
        target_transport = _mod_transport.get_transport(url,
356
 
            possible_transports)
357
 
        target_transport.ensure_base()
358
 
        cloning_format = self.cloning_metadir(stacked)
359
 
        # Create/update the result branch
360
 
        result = cloning_format.initialize_on_transport(target_transport)
361
 
        source_branch, source_repository = self._find_source_repo(
362
 
            add_cleanup, source_branch)
363
 
        fetch_spec_factory.source_branch = source_branch
364
 
        # if a stacked branch wasn't requested, we don't create one
365
 
        # even if the origin was stacked
366
 
        if stacked and source_branch is not None:
367
 
            stacked_branch_url = self.root_transport.base
368
 
        else:
369
 
            stacked_branch_url = None
370
 
        repository_policy = result.determine_repository_policy(
371
 
            force_new_repo, stacked_branch_url, require_stacking=stacked)
372
 
        result_repo, is_new_repo = repository_policy.acquire_repository()
373
 
        add_cleanup(result_repo.lock_write().unlock)
374
 
        fetch_spec_factory.source_repo = source_repository
375
 
        fetch_spec_factory.target_repo = result_repo
376
 
        if stacked or (len(result_repo._fallback_repositories) != 0):
377
 
            target_repo_kind = fetch.TargetRepoKinds.STACKED
378
 
        elif is_new_repo:
379
 
            target_repo_kind = fetch.TargetRepoKinds.EMPTY
380
 
        else:
381
 
            target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
382
 
        fetch_spec_factory.target_repo_kind = target_repo_kind
383
 
        if source_repository is not None:
384
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
385
 
            result_repo.fetch(source_repository, fetch_spec=fetch_spec)
386
 
 
387
 
        if source_branch is None:
388
 
            # this is for sprouting a controldir without a branch; is that
389
 
            # actually useful?
390
 
            # Not especially, but it's part of the contract.
391
 
            result_branch = result.create_branch()
392
 
        else:
393
 
            result_branch = source_branch.sprout(result,
394
 
                revision_id=revision_id, repository_policy=repository_policy,
395
 
                repository=result_repo)
396
 
        mutter("created new branch %r" % (result_branch,))
397
 
 
398
 
        # Create/update the result working tree
399
 
        if (create_tree_if_local and
400
 
            isinstance(target_transport, local.LocalTransport) and
401
 
            (result_repo is None or result_repo.make_working_trees())):
402
 
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
403
 
                hardlink=hardlink, from_branch=result_branch)
404
 
            wt.lock_write()
405
 
            try:
406
 
                if wt.path2id('') is None:
407
 
                    try:
408
 
                        wt.set_root_id(self.open_workingtree.get_root_id())
409
 
                    except errors.NoWorkingTree:
410
 
                        pass
411
 
            finally:
412
 
                wt.unlock()
413
 
        else:
414
 
            wt = None
415
 
        if recurse == 'down':
416
 
            basis = None
417
 
            if wt is not None:
418
 
                basis = wt.basis_tree()
419
 
            elif result_branch is not None:
420
 
                basis = result_branch.basis_tree()
421
 
            elif source_branch is not None:
422
 
                basis = source_branch.basis_tree()
423
 
            if basis is not None:
424
 
                add_cleanup(basis.lock_read().unlock)
425
 
                subtrees = basis.iter_references()
426
 
            else:
427
 
                subtrees = []
428
 
            for path, file_id in subtrees:
429
 
                target = urlutils.join(url, urlutils.escape(path))
430
 
                sublocation = source_branch.reference_parent(file_id, path)
431
 
                sublocation.bzrdir.sprout(target,
432
 
                    basis.get_reference_revision(file_id, path),
433
 
                    force_new_repo=force_new_repo, recurse=recurse,
434
 
                    stacked=stacked)
435
 
        return result
436
 
 
437
 
    def _find_source_repo(self, add_cleanup, source_branch):
438
 
        """Find the source branch and repo for a sprout operation.
439
 
        
440
 
        This is helper intended for use by _sprout.
441
 
 
442
 
        :returns: (source_branch, source_repository).  Either or both may be
443
 
            None.  If not None, they will be read-locked (and their unlock(s)
444
 
            scheduled via the add_cleanup param).
445
 
        """
446
 
        if source_branch is not None:
447
 
            add_cleanup(source_branch.lock_read().unlock)
448
 
            return source_branch, source_branch.repository
449
 
        try:
450
 
            source_branch = self.open_branch()
451
 
            source_repository = source_branch.repository
452
 
        except errors.NotBranchError:
453
 
            source_branch = None
454
 
            try:
455
 
                source_repository = self.open_repository()
456
 
            except errors.NoRepositoryPresent:
457
 
                source_repository = None
458
 
            else:
459
 
                add_cleanup(source_repository.lock_read().unlock)
460
 
        else:
461
 
            add_cleanup(source_branch.lock_read().unlock)
462
 
        return source_branch, source_repository
 
330
        raise NotImplementedError(self.sprout)
463
331
 
464
332
    def push_branch(self, source, revision_id=None, overwrite=False, 
465
333
        remember=False, create_prefix=False):
481
349
        if br_to is None:
482
350
            # We have a repository but no branch, copy the revisions, and then
483
351
            # create a branch.
 
352
            if revision_id is None:
 
353
                # No revision supplied by the user, default to the branch
 
354
                # revision
 
355
                revision_id = source.last_revision()
484
356
            repository_to.fetch(source.repository, revision_id=revision_id)
485
357
            br_to = source.clone(self, revision_id=revision_id)
486
358
            if source.get_push_location() is None or remember:
594
466
class ControlComponentFormat(object):
595
467
    """A component that can live inside of a .bzr meta directory."""
596
468
 
 
469
    upgrade_recommended = False
 
470
 
597
471
    def get_format_string(self):
598
472
        """Return the format of this format, if usable in meta directories."""
599
473
        raise NotImplementedError(self.get_format_string)
602
476
        """Return the short description for this format."""
603
477
        raise NotImplementedError(self.get_format_description)
604
478
 
 
479
    def is_supported(self):
 
480
        """Is this format supported?
 
481
 
 
482
        Supported formats must be initializable and openable.
 
483
        Unsupported formats may not support initialization or committing or
 
484
        some other features depending on the reason for not being supported.
 
485
        """
 
486
        return True
 
487
 
 
488
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
489
        basedir=None):
 
490
        """Give an error or warning on old formats.
 
491
 
 
492
        :param allow_unsupported: If true, allow opening
 
493
            formats that are strongly deprecated, and which may
 
494
            have limited functionality.
 
495
 
 
496
        :param recommend_upgrade: If true (default), warn
 
497
            the user through the ui object that they may wish
 
498
            to upgrade the object.
 
499
        """
 
500
        if not allow_unsupported and not self.is_supported():
 
501
            # see open_downlevel to open legacy branches.
 
502
            raise errors.UnsupportedFormatError(format=self)
 
503
        if recommend_upgrade and self.upgrade_recommended:
 
504
            ui.ui_factory.recommend_upgrade(
 
505
                self.get_format_description(), basedir)
 
506
 
605
507
 
606
508
class ControlComponentFormatRegistry(registry.FormatRegistry):
607
509
    """A registry for control components (branch, workingtree, repository)."""
734
636
    """Whether components can not change format independent of the control dir.
735
637
    """
736
638
 
 
639
    upgrade_recommended = False
 
640
    """Whether an upgrade from this format is recommended."""
 
641
 
737
642
    def get_format_description(self):
738
643
        """Return the short description for this format."""
739
644
        raise NotImplementedError(self.get_format_description)
761
666
        """
762
667
        return True
763
668
 
 
669
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
670
        basedir=None):
 
671
        """Give an error or warning on old formats.
 
672
 
 
673
        :param allow_unsupported: If true, allow opening
 
674
            formats that are strongly deprecated, and which may
 
675
            have limited functionality.
 
676
 
 
677
        :param recommend_upgrade: If true (default), warn
 
678
            the user through the ui object that they may wish
 
679
            to upgrade the object.
 
680
        """
 
681
        if not allow_unsupported and not self.is_supported():
 
682
            # see open_downlevel to open legacy branches.
 
683
            raise errors.UnsupportedFormatError(format=self)
 
684
        if recommend_upgrade and self.upgrade_recommended:
 
685
            ui.ui_factory.recommend_upgrade(
 
686
                self.get_format_description(), basedir)
 
687
 
764
688
    def same_model(self, target_format):
765
689
        return (self.repository_format.rich_root_data ==
766
690
            target_format.rich_root_data)