~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

  • Committer: Andrew Bennetts
  • Date: 2010-10-13 00:26:41 UTC
  • mto: This revision was merged to the branch mainline in revision 5498.
  • Revision ID: andrew.bennetts@canonical.com-20101013002641-9tlh9k89mlj1666m
Keep docs-plain working.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2010, 2011 Canonical Ltd
 
1
# Copyright (C) 2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
28
28
 
29
29
from bzrlib import (
30
30
    errors,
 
31
    graph,
 
32
    registry,
31
33
    revision as _mod_revision,
32
 
    transport as _mod_transport,
33
 
    ui,
 
34
    symbol_versioning,
 
35
    urlutils,
34
36
    )
35
37
from bzrlib.push import (
36
38
    PushResult,
37
39
    )
 
40
from bzrlib.trace import (
 
41
    mutter,
 
42
    )
 
43
from bzrlib.transport import (
 
44
    get_transport,
 
45
    local,
 
46
    )
38
47
 
39
48
""")
40
49
 
41
 
from bzrlib import registry
42
 
 
43
50
 
44
51
class ControlComponent(object):
45
52
    """Abstract base class for control directory components.
74
81
        return self.user_transport.base
75
82
 
76
83
 
77
 
 
78
84
class ControlDir(ControlComponent):
79
85
    """A control directory.
80
86
 
133
139
        """
134
140
        raise NotImplementedError(self.needs_format_conversion)
135
141
 
136
 
    def create_repository(self, shared=False):
137
 
        """Create a new repository in this control directory.
138
 
 
139
 
        :param shared: If a shared repository should be created
140
 
        :return: The newly created repository
141
 
        """
142
 
        raise NotImplementedError(self.create_repository)
143
 
 
144
142
    def destroy_repository(self):
145
143
        """Destroy the repository in this ControlDir."""
146
144
        raise NotImplementedError(self.destroy_repository)
147
145
 
148
 
    def create_branch(self, name=None, repository=None):
 
146
    def create_branch(self, name=None):
149
147
        """Create a branch in this ControlDir.
150
148
 
151
149
        :param name: Name of the colocated branch to create, None for
207
205
            raise errors.NoColocatedBranchSupport(self)
208
206
        return None
209
207
 
 
208
    def get_branch_transport(self, branch_format, name=None):
 
209
        """Get the transport for use by branch format in this ControlDir.
 
210
 
 
211
        Note that bzr dirs that do not support format strings will raise
 
212
        IncompatibleFormat if the branch format they are given has
 
213
        a format string, and vice versa.
 
214
 
 
215
        If branch_format is None, the transport is returned with no
 
216
        checking. If it is not None, then the returned transport is
 
217
        guaranteed to point to an existing directory ready for use.
 
218
        """
 
219
        raise NotImplementedError(self.get_branch_transport)
 
220
 
 
221
    def get_repository_transport(self, repository_format):
 
222
        """Get the transport for use by repository format in this ControlDir.
 
223
 
 
224
        Note that bzr dirs that do not support format strings will raise
 
225
        IncompatibleFormat if the repository format they are given has
 
226
        a format string, and vice versa.
 
227
 
 
228
        If repository_format is None, the transport is returned with no
 
229
        checking. If it is not None, then the returned transport is
 
230
        guaranteed to point to an existing directory ready for use.
 
231
        """
 
232
        raise NotImplementedError(self.get_repository_transport)
 
233
 
 
234
    def get_workingtree_transport(self, tree_format):
 
235
        """Get the transport for use by workingtree format in this ControlDir.
 
236
 
 
237
        Note that bzr dirs that do not support format strings will raise
 
238
        IncompatibleFormat if the workingtree format they are given has a
 
239
        format string, and vice versa.
 
240
 
 
241
        If workingtree_format is None, the transport is returned with no
 
242
        checking. If it is not None, then the returned transport is
 
243
        guaranteed to point to an existing directory ready for use.
 
244
        """
 
245
        raise NotImplementedError(self.get_workingtree_transport)
 
246
 
210
247
    def open_branch(self, name=None, unsupported=False,
211
248
                    ignore_fallbacks=False):
212
249
        """Open the branch object at this ControlDir if one is present.
226
263
        get at a repository.
227
264
 
228
265
        :param _unsupported: a private parameter, not part of the api.
229
 
 
230
266
        TODO: static convenience version of this?
231
267
        """
232
268
        raise NotImplementedError(self.open_repository)
315
351
        whether one existed before or not; and a local branch is always
316
352
        created.
317
353
 
318
 
        :param revision_id: if revision_id is not None, then the clone
319
 
            operation may tune itself to download less data.
 
354
        if revision_id is not None, then the clone operation may tune
 
355
            itself to download less data.
320
356
        :param accelerator_tree: A tree which can be used for retrieving file
321
357
            contents more quickly than the revision tree, i.e. a workingtree.
322
358
            The revision tree will be used for cases where accelerator_tree's
328
364
        :param create_tree_if_local: If true, a working-tree will be created
329
365
            when working locally.
330
366
        """
331
 
        raise NotImplementedError(self.sprout)
 
367
        target_transport = get_transport(url, possible_transports)
 
368
        target_transport.ensure_base()
 
369
        cloning_format = self.cloning_metadir(stacked)
 
370
        # Create/update the result branch
 
371
        result = cloning_format.initialize_on_transport(target_transport)
 
372
        # if a stacked branch wasn't requested, we don't create one
 
373
        # even if the origin was stacked
 
374
        stacked_branch_url = None
 
375
        if source_branch is not None:
 
376
            if stacked:
 
377
                stacked_branch_url = self.root_transport.base
 
378
            source_repository = source_branch.repository
 
379
        else:
 
380
            try:
 
381
                source_branch = self.open_branch()
 
382
                source_repository = source_branch.repository
 
383
                if stacked:
 
384
                    stacked_branch_url = self.root_transport.base
 
385
            except errors.NotBranchError:
 
386
                source_branch = None
 
387
                try:
 
388
                    source_repository = self.open_repository()
 
389
                except errors.NoRepositoryPresent:
 
390
                    source_repository = None
 
391
        repository_policy = result.determine_repository_policy(
 
392
            force_new_repo, stacked_branch_url, require_stacking=stacked)
 
393
        result_repo, is_new_repo = repository_policy.acquire_repository()
 
394
        is_stacked = stacked or (len(result_repo._fallback_repositories) != 0)
 
395
        if is_new_repo and revision_id is not None and not is_stacked:
 
396
            fetch_spec = graph.PendingAncestryResult(
 
397
                [revision_id], source_repository)
 
398
        else:
 
399
            fetch_spec = None
 
400
        if source_repository is not None:
 
401
            # Fetch while stacked to prevent unstacked fetch from
 
402
            # Branch.sprout.
 
403
            if fetch_spec is None:
 
404
                result_repo.fetch(source_repository, revision_id=revision_id)
 
405
            else:
 
406
                result_repo.fetch(source_repository, fetch_spec=fetch_spec)
 
407
 
 
408
        if source_branch is None:
 
409
            # this is for sprouting a controldir without a branch; is that
 
410
            # actually useful?
 
411
            # Not especially, but it's part of the contract.
 
412
            result_branch = result.create_branch()
 
413
        else:
 
414
            result_branch = source_branch.sprout(result,
 
415
                revision_id=revision_id, repository_policy=repository_policy)
 
416
        mutter("created new branch %r" % (result_branch,))
 
417
 
 
418
        # Create/update the result working tree
 
419
        if (create_tree_if_local and
 
420
            isinstance(target_transport, local.LocalTransport) and
 
421
            (result_repo is None or result_repo.make_working_trees())):
 
422
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
 
423
                hardlink=hardlink)
 
424
            wt.lock_write()
 
425
            try:
 
426
                if wt.path2id('') is None:
 
427
                    try:
 
428
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
429
                    except errors.NoWorkingTree:
 
430
                        pass
 
431
            finally:
 
432
                wt.unlock()
 
433
        else:
 
434
            wt = None
 
435
        if recurse == 'down':
 
436
            if wt is not None:
 
437
                basis = wt.basis_tree()
 
438
                basis.lock_read()
 
439
                subtrees = basis.iter_references()
 
440
            elif result_branch is not None:
 
441
                basis = result_branch.basis_tree()
 
442
                basis.lock_read()
 
443
                subtrees = basis.iter_references()
 
444
            elif source_branch is not None:
 
445
                basis = source_branch.basis_tree()
 
446
                basis.lock_read()
 
447
                subtrees = basis.iter_references()
 
448
            else:
 
449
                subtrees = []
 
450
                basis = None
 
451
            try:
 
452
                for path, file_id in subtrees:
 
453
                    target = urlutils.join(url, urlutils.escape(path))
 
454
                    sublocation = source_branch.reference_parent(file_id, path)
 
455
                    sublocation.bzrdir.sprout(target,
 
456
                        basis.get_reference_revision(file_id, path),
 
457
                        force_new_repo=force_new_repo, recurse=recurse,
 
458
                        stacked=stacked)
 
459
            finally:
 
460
                if basis is not None:
 
461
                    basis.unlock()
 
462
        return result
332
463
 
333
464
    def push_branch(self, source, revision_id=None, overwrite=False, 
334
465
        remember=False, create_prefix=False):
350
481
        if br_to is None:
351
482
            # We have a repository but no branch, copy the revisions, and then
352
483
            # create a branch.
353
 
            if revision_id is None:
354
 
                # No revision supplied by the user, default to the branch
355
 
                # revision
356
 
                revision_id = source.last_revision()
357
484
            repository_to.fetch(source.repository, revision_id=revision_id)
358
485
            br_to = source.clone(self, revision_id=revision_id)
359
486
            if source.get_push_location() is None or remember:
437
564
        :param preserve_stacking: When cloning a stacked branch, stack the
438
565
            new branch on top of the other branch's stacked-on branch.
439
566
        """
440
 
        return self.clone_on_transport(_mod_transport.get_transport(url),
 
567
        return self.clone_on_transport(get_transport(url),
441
568
                                       revision_id=revision_id,
442
569
                                       force_new_repo=force_new_repo,
443
570
                                       preserve_stacking=preserve_stacking)
444
571
 
445
572
    def clone_on_transport(self, transport, revision_id=None,
446
573
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
447
 
        create_prefix=False, use_existing_dir=True, no_tree=False):
 
574
        create_prefix=False, use_existing_dir=True):
448
575
        """Clone this bzrdir and its contents to transport verbatim.
449
576
 
450
577
        :param transport: The transport for the location to produce the clone
459
586
        :param create_prefix: Create any missing directories leading up to
460
587
            to_transport.
461
588
        :param use_existing_dir: Use an existing directory if one exists.
462
 
        :param no_tree: If set to true prevents creation of a working tree.
463
589
        """
464
590
        raise NotImplementedError(self.clone_on_transport)
465
591
 
466
592
 
467
 
class ControlComponentFormat(object):
468
 
    """A component that can live inside of a .bzr meta directory."""
469
 
 
470
 
    upgrade_recommended = False
471
 
 
472
 
    def get_format_string(self):
473
 
        """Return the format of this format, if usable in meta directories."""
474
 
        raise NotImplementedError(self.get_format_string)
475
 
 
476
 
    def get_format_description(self):
477
 
        """Return the short description for this format."""
478
 
        raise NotImplementedError(self.get_format_description)
479
 
 
480
 
    def is_supported(self):
481
 
        """Is this format supported?
482
 
 
483
 
        Supported formats must be initializable and openable.
484
 
        Unsupported formats may not support initialization or committing or
485
 
        some other features depending on the reason for not being supported.
486
 
        """
487
 
        return True
488
 
 
489
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
490
 
        basedir=None):
491
 
        """Give an error or warning on old formats.
492
 
 
493
 
        :param allow_unsupported: If true, allow opening
494
 
            formats that are strongly deprecated, and which may
495
 
            have limited functionality.
496
 
 
497
 
        :param recommend_upgrade: If true (default), warn
498
 
            the user through the ui object that they may wish
499
 
            to upgrade the object.
500
 
        """
501
 
        if not allow_unsupported and not self.is_supported():
502
 
            # see open_downlevel to open legacy branches.
503
 
            raise errors.UnsupportedFormatError(format=self)
504
 
        if recommend_upgrade and self.upgrade_recommended:
505
 
            ui.ui_factory.recommend_upgrade(
506
 
                self.get_format_description(), basedir)
507
 
 
508
 
 
509
 
class ControlComponentFormatRegistry(registry.FormatRegistry):
510
 
    """A registry for control components (branch, workingtree, repository)."""
511
 
 
512
 
    def __init__(self, other_registry=None):
513
 
        super(ControlComponentFormatRegistry, self).__init__(other_registry)
514
 
        self._extra_formats = []
515
 
 
516
 
    def register(self, format):
517
 
        """Register a new format."""
518
 
        super(ControlComponentFormatRegistry, self).register(
519
 
            format.get_format_string(), format)
520
 
 
521
 
    def remove(self, format):
522
 
        """Remove a registered format."""
523
 
        super(ControlComponentFormatRegistry, self).remove(
524
 
            format.get_format_string())
525
 
 
526
 
    def register_extra(self, format):
527
 
        """Register a format that can not be used in a metadir.
528
 
 
529
 
        This is mainly useful to allow custom repository formats, such as older
530
 
        Bazaar formats and foreign formats, to be tested.
531
 
        """
532
 
        self._extra_formats.append(registry._ObjectGetter(format))
533
 
 
534
 
    def remove_extra(self, format):
535
 
        """Remove an extra format.
536
 
        """
537
 
        self._extra_formats.remove(registry._ObjectGetter(format))
538
 
 
539
 
    def register_extra_lazy(self, module_name, member_name):
540
 
        """Register a format lazily.
541
 
        """
542
 
        self._extra_formats.append(
543
 
            registry._LazyObjectGetter(module_name, member_name))
544
 
 
545
 
    def _get_extra(self):
546
 
        """Return all "extra" formats, not usable in meta directories."""
547
 
        result = []
548
 
        for getter in self._extra_formats:
549
 
            f = getter.get_obj()
550
 
            if callable(f):
551
 
                f = f()
552
 
            result.append(f)
553
 
        return result
554
 
 
555
 
    def _get_all(self):
556
 
        """Return all formats, even those not usable in metadirs.
557
 
        """
558
 
        result = []
559
 
        for name in self.keys():
560
 
            fmt = self.get(name)
561
 
            if callable(fmt):
562
 
                fmt = fmt()
563
 
            result.append(fmt)
564
 
        return result + self._get_extra()
565
 
 
566
 
    def _get_all_modules(self):
567
 
        """Return a set of the modules providing objects."""
568
 
        modules = set()
569
 
        for name in self.keys():
570
 
            modules.add(self._get_module(name))
571
 
        for getter in self._extra_formats:
572
 
            modules.add(getter.get_module())
573
 
        return modules
574
 
 
575
 
 
576
 
class Converter(object):
577
 
    """Converts a disk format object from one format to another."""
578
 
 
579
 
    def convert(self, to_convert, pb):
580
 
        """Perform the conversion of to_convert, giving feedback via pb.
581
 
 
582
 
        :param to_convert: The disk object to convert.
583
 
        :param pb: a progress bar to use for progress information.
584
 
        """
585
 
 
586
 
    def step(self, message):
587
 
        """Update the pb by a step."""
588
 
        self.count +=1
589
 
        self.pb.update(message, self.count, self.total)
590
 
 
591
 
 
592
593
class ControlDirFormat(object):
593
594
    """An encapsulation of the initialization and open routines for a format.
594
595
 
613
614
    _default_format = None
614
615
    """The default format used for new control directories."""
615
616
 
 
617
    _formats = []
 
618
    """The registered control formats - .bzr, ....
 
619
 
 
620
    This is a list of ControlDirFormat objects.
 
621
    """
 
622
 
616
623
    _server_probers = []
617
624
    """The registered server format probers, e.g. RemoteBzrProber.
618
625
 
630
637
    """
631
638
 
632
639
    supports_workingtrees = True
633
 
    """Whether working trees can exist in control directories of this format.
634
 
    """
635
 
 
636
 
    fixed_components = False
637
 
    """Whether components can not change format independent of the control dir.
638
 
    """
639
 
 
640
 
    upgrade_recommended = False
641
 
    """Whether an upgrade from this format is recommended."""
642
640
 
643
641
    def get_format_description(self):
644
642
        """Return the short description for this format."""
667
665
        """
668
666
        return True
669
667
 
670
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
671
 
        basedir=None):
672
 
        """Give an error or warning on old formats.
673
 
 
674
 
        :param allow_unsupported: If true, allow opening
675
 
            formats that are strongly deprecated, and which may
676
 
            have limited functionality.
677
 
 
678
 
        :param recommend_upgrade: If true (default), warn
679
 
            the user through the ui object that they may wish
680
 
            to upgrade the object.
681
 
        """
682
 
        if not allow_unsupported and not self.is_supported():
683
 
            # see open_downlevel to open legacy branches.
684
 
            raise errors.UnsupportedFormatError(format=self)
685
 
        if recommend_upgrade and self.upgrade_recommended:
686
 
            ui.ui_factory.recommend_upgrade(
687
 
                self.get_format_description(), basedir)
688
 
 
689
668
    def same_model(self, target_format):
690
669
        return (self.repository_format.rich_root_data ==
691
670
            target_format.rich_root_data)
695
674
        """Register a format that does not use '.bzr' for its control dir.
696
675
 
697
676
        """
698
 
        raise errors.BzrError("ControlDirFormat.register_format() has been "
699
 
            "removed in Bazaar 2.4. Please upgrade your plugins.")
 
677
        klass._formats.append(format)
700
678
 
701
679
    @classmethod
702
680
    def register_prober(klass, prober):
728
706
        return self.get_format_description().rstrip()
729
707
 
730
708
    @classmethod
 
709
    def unregister_format(klass, format):
 
710
        klass._formats.remove(format)
 
711
 
 
712
    @classmethod
731
713
    def known_formats(klass):
732
714
        """Return all the known formats.
733
715
        """
734
 
        result = set()
735
 
        for prober_kls in klass._probers + klass._server_probers:
736
 
            result.update(prober_kls.known_formats())
737
 
        return result
 
716
        return set(klass._formats)
738
717
 
739
718
    @classmethod
740
719
    def find_format(klass, transport, _server_formats=True):
762
741
        Subclasses should typically override initialize_on_transport
763
742
        instead of this method.
764
743
        """
765
 
        return self.initialize_on_transport(
766
 
            _mod_transport.get_transport(url, possible_transports))
767
 
 
 
744
        return self.initialize_on_transport(get_transport(url,
 
745
                                                          possible_transports))
768
746
    def initialize_on_transport(self, transport):
769
747
        """Initialize a new controldir in the base directory of a Transport."""
770
748
        raise NotImplementedError(self.initialize_on_transport)
830
808
 
831
809
 
832
810
class Prober(object):
833
 
    """Abstract class that can be used to detect a particular kind of
 
811
    """Abstract class that can be used to detect a particular kind of 
834
812
    control directory.
835
813
 
836
 
    At the moment this just contains a single method to probe a particular
837
 
    transport, but it may be extended in the future to e.g. avoid
 
814
    At the moment this just contains a single method to probe a particular 
 
815
    transport, but it may be extended in the future to e.g. avoid 
838
816
    multiple levels of probing for Subversion repositories.
839
 
 
840
 
    See BzrProber and RemoteBzrProber in bzrlib.bzrdir for the
841
 
    probers that detect .bzr/ directories and Bazaar smart servers,
842
 
    respectively.
843
 
 
844
 
    Probers should be registered using the register_server_prober or
845
 
    register_prober methods on ControlDirFormat.
846
817
    """
847
818
 
848
819
    def probe_transport(self, transport):
855
826
        """
856
827
        raise NotImplementedError(self.probe_transport)
857
828
 
858
 
    @classmethod
859
 
    def known_formats(cls):
860
 
        """Return the control dir formats known by this prober.
861
 
 
862
 
        Multiple probers can return the same formats, so this should
863
 
        return a set.
864
 
 
865
 
        :return: A set of known formats.
866
 
        """
867
 
        raise NotImplementedError(cls.known_formats)
868
 
 
869
829
 
870
830
class ControlDirFormatInfo(object):
871
831
 
880
840
    """Registry of user-selectable ControlDir subformats.
881
841
 
882
842
    Differs from ControlDirFormat._formats in that it provides sub-formats,
883
 
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
843
    e.g. ControlDirMeta1 with weave repository.  Also, it's more user-oriented.
884
844
    """
885
845
 
886
846
    def __init__(self):