~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

(gz) Remove bzrlib/util/elementtree/ package (Martin Packman)

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
"""
24
24
 
 
25
from __future__ import absolute_import
 
26
 
25
27
from bzrlib.lazy_import import lazy_import
26
28
lazy_import(globals(), """
27
29
import textwrap
28
30
 
29
31
from bzrlib import (
30
32
    errors,
 
33
    hooks,
31
34
    revision as _mod_revision,
32
35
    transport as _mod_transport,
 
36
    trace,
33
37
    ui,
 
38
    urlutils,
34
39
    )
 
40
from bzrlib.transport import local
35
41
from bzrlib.push import (
36
42
    PushResult,
37
43
    )
38
44
 
 
45
from bzrlib.i18n import gettext
39
46
""")
40
47
 
41
48
from bzrlib import registry
74
81
        return self.user_transport.base
75
82
 
76
83
 
77
 
 
78
84
class ControlDir(ControlComponent):
79
85
    """A control directory.
80
86
 
102
108
        """Return a sequence of all branches local to this control directory.
103
109
 
104
110
        """
 
111
        return self.get_branches().values()
 
112
 
 
113
    def get_branches(self):
 
114
        """Get all branches in this control directory, as a dictionary.
 
115
        
 
116
        :return: Dictionary mapping branch names to instances.
 
117
        """
105
118
        try:
106
 
            return [self.open_branch()]
 
119
           return { None: self.open_branch() }
107
120
        except (errors.NotBranchError, errors.NoRepositoryPresent):
108
 
            return []
 
121
           return {}
109
122
 
110
123
    def is_control_filename(self, filename):
111
124
        """True if filename is the name of a path which is reserved for
145
158
        """Destroy the repository in this ControlDir."""
146
159
        raise NotImplementedError(self.destroy_repository)
147
160
 
148
 
    def create_branch(self, name=None, repository=None):
 
161
    def create_branch(self, name=None, repository=None,
 
162
                      append_revisions_only=None):
149
163
        """Create a branch in this ControlDir.
150
164
 
151
165
        :param name: Name of the colocated branch to create, None for
152
166
            the default branch.
 
167
        :param append_revisions_only: Whether this branch should only allow
 
168
            appending new revisions to its history.
153
169
 
154
170
        The controldirs format will control what branch format is created.
155
171
        For more control see BranchFormatXX.create(a_controldir).
193
209
        """
194
210
        raise NotImplementedError(self.destroy_workingtree_metadata)
195
211
 
 
212
    def find_branch_format(self, name=None):
 
213
        """Find the branch 'format' for this controldir.
 
214
 
 
215
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
216
        """
 
217
        raise NotImplementedError(self.find_branch_format)
 
218
 
196
219
    def get_branch_reference(self, name=None):
197
220
        """Return the referenced URL for the branch in this controldir.
198
221
 
208
231
        return None
209
232
 
210
233
    def open_branch(self, name=None, unsupported=False,
211
 
                    ignore_fallbacks=False):
 
234
                    ignore_fallbacks=False, possible_transports=None):
212
235
        """Open the branch object at this ControlDir if one is present.
213
236
 
214
 
        If unsupported is True, then no longer supported branch formats can
215
 
        still be opened.
216
 
 
217
 
        TODO: static convenience version of this?
 
237
        :param unsupported: if True, then no longer supported branch formats can
 
238
            still be opened.
 
239
        :param ignore_fallbacks: Whether to open fallback repositories
 
240
        :param possible_transports: Transports to use for opening e.g.
 
241
            fallback repositories.
218
242
        """
219
243
        raise NotImplementedError(self.open_branch)
220
244
 
226
250
        get at a repository.
227
251
 
228
252
        :param _unsupported: a private parameter, not part of the api.
229
 
 
230
 
        TODO: static convenience version of this?
231
253
        """
232
254
        raise NotImplementedError(self.open_repository)
233
255
 
240
262
        """
241
263
        raise NotImplementedError(self.find_repository)
242
264
 
243
 
    def open_workingtree(self, _unsupported=False,
 
265
    def open_workingtree(self, unsupported=False,
244
266
                         recommend_upgrade=True, from_branch=None):
245
267
        """Open the workingtree object at this ControlDir if one is present.
246
268
 
261
283
        branch and discards it, and that's somewhat expensive.)
262
284
        """
263
285
        try:
264
 
            self.open_branch(name)
 
286
            self.open_branch(name, ignore_fallbacks=True)
265
287
            return True
266
288
        except errors.NotBranchError:
267
289
            return False
268
290
 
 
291
    def _get_selected_branch(self):
 
292
        """Return the name of the branch selected by the user.
 
293
 
 
294
        :return: Name of the branch selected by the user, or None.
 
295
        """
 
296
        branch = self.root_transport.get_segment_parameters().get("branch")
 
297
        if branch is not None:
 
298
            branch = urlutils.unescape(branch)
 
299
        return branch
 
300
 
269
301
    def has_workingtree(self):
270
302
        """Tell if this controldir contains a working tree.
271
303
 
298
330
        raise NotImplementedError(self.cloning_metadir)
299
331
 
300
332
    def checkout_metadir(self):
301
 
        """Produce a metadir suitable for checkouts of this controldir."""
 
333
        """Produce a metadir suitable for checkouts of this controldir.
 
334
 
 
335
        :returns: A ControlDirFormat with all component formats
 
336
            either set appropriately or set to None if that component
 
337
            should not be created.
 
338
        """
302
339
        return self.cloning_metadir()
303
340
 
304
341
    def sprout(self, url, revision_id=None, force_new_repo=False,
395
432
        return push_result
396
433
 
397
434
    def _get_tree_branch(self, name=None):
398
 
        """Return the branch and tree, if any, for this bzrdir.
 
435
        """Return the branch and tree, if any, for this controldir.
399
436
 
400
437
        :param name: Name of colocated branch to open.
401
438
 
420
457
        raise NotImplementedError(self.get_config)
421
458
 
422
459
    def check_conversion_target(self, target_format):
423
 
        """Check that a bzrdir as a whole can be converted to a new format."""
 
460
        """Check that a controldir as a whole can be converted to a new format."""
424
461
        raise NotImplementedError(self.check_conversion_target)
425
462
 
426
463
    def clone(self, url, revision_id=None, force_new_repo=False,
427
464
              preserve_stacking=False):
428
 
        """Clone this bzrdir and its contents to url verbatim.
 
465
        """Clone this controldir and its contents to url verbatim.
429
466
 
430
467
        :param url: The url create the clone at.  If url's last component does
431
468
            not exist, it will be created.
445
482
    def clone_on_transport(self, transport, revision_id=None,
446
483
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
447
484
        create_prefix=False, use_existing_dir=True, no_tree=False):
448
 
        """Clone this bzrdir and its contents to transport verbatim.
 
485
        """Clone this controldir and its contents to transport verbatim.
449
486
 
450
487
        :param transport: The transport for the location to produce the clone
451
488
            at.  If the target directory does not exist, it will be created.
463
500
        """
464
501
        raise NotImplementedError(self.clone_on_transport)
465
502
 
 
503
    @classmethod
 
504
    def find_bzrdirs(klass, transport, evaluate=None, list_current=None):
 
505
        """Find control dirs recursively from current location.
 
506
 
 
507
        This is intended primarily as a building block for more sophisticated
 
508
        functionality, like finding trees under a directory, or finding
 
509
        branches that use a given repository.
 
510
 
 
511
        :param evaluate: An optional callable that yields recurse, value,
 
512
            where recurse controls whether this controldir is recursed into
 
513
            and value is the value to yield.  By default, all bzrdirs
 
514
            are recursed into, and the return value is the controldir.
 
515
        :param list_current: if supplied, use this function to list the current
 
516
            directory, instead of Transport.list_dir
 
517
        :return: a generator of found bzrdirs, or whatever evaluate returns.
 
518
        """
 
519
        if list_current is None:
 
520
            def list_current(transport):
 
521
                return transport.list_dir('')
 
522
        if evaluate is None:
 
523
            def evaluate(controldir):
 
524
                return True, controldir
 
525
 
 
526
        pending = [transport]
 
527
        while len(pending) > 0:
 
528
            current_transport = pending.pop()
 
529
            recurse = True
 
530
            try:
 
531
                controldir = klass.open_from_transport(current_transport)
 
532
            except (errors.NotBranchError, errors.PermissionDenied):
 
533
                pass
 
534
            else:
 
535
                recurse, value = evaluate(controldir)
 
536
                yield value
 
537
            try:
 
538
                subdirs = list_current(current_transport)
 
539
            except (errors.NoSuchFile, errors.PermissionDenied):
 
540
                continue
 
541
            if recurse:
 
542
                for subdir in sorted(subdirs, reverse=True):
 
543
                    pending.append(current_transport.clone(subdir))
 
544
 
 
545
    @classmethod
 
546
    def find_branches(klass, transport):
 
547
        """Find all branches under a transport.
 
548
 
 
549
        This will find all branches below the transport, including branches
 
550
        inside other branches.  Where possible, it will use
 
551
        Repository.find_branches.
 
552
 
 
553
        To list all the branches that use a particular Repository, see
 
554
        Repository.find_branches
 
555
        """
 
556
        def evaluate(controldir):
 
557
            try:
 
558
                repository = controldir.open_repository()
 
559
            except errors.NoRepositoryPresent:
 
560
                pass
 
561
            else:
 
562
                return False, ([], repository)
 
563
            return True, (controldir.list_branches(), None)
 
564
        ret = []
 
565
        for branches, repo in klass.find_bzrdirs(
 
566
                transport, evaluate=evaluate):
 
567
            if repo is not None:
 
568
                ret.extend(repo.find_branches())
 
569
            if branches is not None:
 
570
                ret.extend(branches)
 
571
        return ret
 
572
 
 
573
    @classmethod
 
574
    def create_branch_and_repo(klass, base, force_new_repo=False, format=None):
 
575
        """Create a new ControlDir, Branch and Repository at the url 'base'.
 
576
 
 
577
        This will use the current default ControlDirFormat unless one is
 
578
        specified, and use whatever
 
579
        repository format that that uses via controldir.create_branch and
 
580
        create_repository. If a shared repository is available that is used
 
581
        preferentially.
 
582
 
 
583
        The created Branch object is returned.
 
584
 
 
585
        :param base: The URL to create the branch at.
 
586
        :param force_new_repo: If True a new repository is always created.
 
587
        :param format: If supplied, the format of branch to create.  If not
 
588
            supplied, the default is used.
 
589
        """
 
590
        controldir = klass.create(base, format)
 
591
        controldir._find_or_create_repository(force_new_repo)
 
592
        return controldir.create_branch()
 
593
 
 
594
    @classmethod
 
595
    def create_branch_convenience(klass, base, force_new_repo=False,
 
596
                                  force_new_tree=None, format=None,
 
597
                                  possible_transports=None):
 
598
        """Create a new ControlDir, Branch and Repository at the url 'base'.
 
599
 
 
600
        This is a convenience function - it will use an existing repository
 
601
        if possible, can be told explicitly whether to create a working tree or
 
602
        not.
 
603
 
 
604
        This will use the current default ControlDirFormat unless one is
 
605
        specified, and use whatever
 
606
        repository format that that uses via ControlDir.create_branch and
 
607
        create_repository. If a shared repository is available that is used
 
608
        preferentially. Whatever repository is used, its tree creation policy
 
609
        is followed.
 
610
 
 
611
        The created Branch object is returned.
 
612
        If a working tree cannot be made due to base not being a file:// url,
 
613
        no error is raised unless force_new_tree is True, in which case no
 
614
        data is created on disk and NotLocalUrl is raised.
 
615
 
 
616
        :param base: The URL to create the branch at.
 
617
        :param force_new_repo: If True a new repository is always created.
 
618
        :param force_new_tree: If True or False force creation of a tree or
 
619
                               prevent such creation respectively.
 
620
        :param format: Override for the controldir format to create.
 
621
        :param possible_transports: An optional reusable transports list.
 
622
        """
 
623
        if force_new_tree:
 
624
            # check for non local urls
 
625
            t = _mod_transport.get_transport(base, possible_transports)
 
626
            if not isinstance(t, local.LocalTransport):
 
627
                raise errors.NotLocalUrl(base)
 
628
        controldir = klass.create(base, format, possible_transports)
 
629
        repo = controldir._find_or_create_repository(force_new_repo)
 
630
        result = controldir.create_branch()
 
631
        if force_new_tree or (repo.make_working_trees() and
 
632
                              force_new_tree is None):
 
633
            try:
 
634
                controldir.create_workingtree()
 
635
            except errors.NotLocalUrl:
 
636
                pass
 
637
        return result
 
638
 
 
639
    @classmethod
 
640
    def create_standalone_workingtree(klass, base, format=None):
 
641
        """Create a new ControlDir, WorkingTree, Branch and Repository at 'base'.
 
642
 
 
643
        'base' must be a local path or a file:// url.
 
644
 
 
645
        This will use the current default ControlDirFormat unless one is
 
646
        specified, and use whatever
 
647
        repository format that that uses for bzrdirformat.create_workingtree,
 
648
        create_branch and create_repository.
 
649
 
 
650
        :param format: Override for the controldir format to create.
 
651
        :return: The WorkingTree object.
 
652
        """
 
653
        t = _mod_transport.get_transport(base)
 
654
        if not isinstance(t, local.LocalTransport):
 
655
            raise errors.NotLocalUrl(base)
 
656
        controldir = klass.create_branch_and_repo(base,
 
657
                                               force_new_repo=True,
 
658
                                               format=format).bzrdir
 
659
        return controldir.create_workingtree()
 
660
 
 
661
    @classmethod
 
662
    def open_unsupported(klass, base):
 
663
        """Open a branch which is not supported."""
 
664
        return klass.open(base, _unsupported=True)
 
665
 
 
666
    @classmethod
 
667
    def open(klass, base, possible_transports=None, probers=None,
 
668
             _unsupported=False):
 
669
        """Open an existing controldir, rooted at 'base' (url).
 
670
 
 
671
        :param _unsupported: a private parameter to the ControlDir class.
 
672
        """
 
673
        t = _mod_transport.get_transport(base, possible_transports)
 
674
        return klass.open_from_transport(t, probers=probers,
 
675
                _unsupported=_unsupported)
 
676
 
 
677
    @classmethod
 
678
    def open_from_transport(klass, transport, _unsupported=False,
 
679
                            probers=None):
 
680
        """Open a controldir within a particular directory.
 
681
 
 
682
        :param transport: Transport containing the controldir.
 
683
        :param _unsupported: private.
 
684
        """
 
685
        for hook in klass.hooks['pre_open']:
 
686
            hook(transport)
 
687
        # Keep initial base since 'transport' may be modified while following
 
688
        # the redirections.
 
689
        base = transport.base
 
690
        def find_format(transport):
 
691
            return transport, ControlDirFormat.find_format(transport,
 
692
                probers=probers)
 
693
 
 
694
        def redirected(transport, e, redirection_notice):
 
695
            redirected_transport = transport._redirected_to(e.source, e.target)
 
696
            if redirected_transport is None:
 
697
                raise errors.NotBranchError(base)
 
698
            trace.note(gettext('{0} is{1} redirected to {2}').format(
 
699
                 transport.base, e.permanently, redirected_transport.base))
 
700
            return redirected_transport
 
701
 
 
702
        try:
 
703
            transport, format = _mod_transport.do_catching_redirections(
 
704
                find_format, transport, redirected)
 
705
        except errors.TooManyRedirections:
 
706
            raise errors.NotBranchError(base)
 
707
 
 
708
        format.check_support_status(_unsupported)
 
709
        return format.open(transport, _found=True)
 
710
 
 
711
    @classmethod
 
712
    def open_containing(klass, url, possible_transports=None):
 
713
        """Open an existing branch which contains url.
 
714
 
 
715
        :param url: url to search from.
 
716
 
 
717
        See open_containing_from_transport for more detail.
 
718
        """
 
719
        transport = _mod_transport.get_transport(url, possible_transports)
 
720
        return klass.open_containing_from_transport(transport)
 
721
 
 
722
    @classmethod
 
723
    def open_containing_from_transport(klass, a_transport):
 
724
        """Open an existing branch which contains a_transport.base.
 
725
 
 
726
        This probes for a branch at a_transport, and searches upwards from there.
 
727
 
 
728
        Basically we keep looking up until we find the control directory or
 
729
        run into the root.  If there isn't one, raises NotBranchError.
 
730
        If there is one and it is either an unrecognised format or an unsupported
 
731
        format, UnknownFormatError or UnsupportedFormatError are raised.
 
732
        If there is one, it is returned, along with the unused portion of url.
 
733
 
 
734
        :return: The ControlDir that contains the path, and a Unicode path
 
735
                for the rest of the URL.
 
736
        """
 
737
        # this gets the normalised url back. I.e. '.' -> the full path.
 
738
        url = a_transport.base
 
739
        while True:
 
740
            try:
 
741
                result = klass.open_from_transport(a_transport)
 
742
                return result, urlutils.unescape(a_transport.relpath(url))
 
743
            except errors.NotBranchError, e:
 
744
                pass
 
745
            except errors.PermissionDenied:
 
746
                pass
 
747
            try:
 
748
                new_t = a_transport.clone('..')
 
749
            except errors.InvalidURLJoin:
 
750
                # reached the root, whatever that may be
 
751
                raise errors.NotBranchError(path=url)
 
752
            if new_t.base == a_transport.base:
 
753
                # reached the root, whatever that may be
 
754
                raise errors.NotBranchError(path=url)
 
755
            a_transport = new_t
 
756
 
 
757
    @classmethod
 
758
    def open_tree_or_branch(klass, location):
 
759
        """Return the branch and working tree at a location.
 
760
 
 
761
        If there is no tree at the location, tree will be None.
 
762
        If there is no branch at the location, an exception will be
 
763
        raised
 
764
        :return: (tree, branch)
 
765
        """
 
766
        controldir = klass.open(location)
 
767
        return controldir._get_tree_branch()
 
768
 
 
769
    @classmethod
 
770
    def open_containing_tree_or_branch(klass, location):
 
771
        """Return the branch and working tree contained by a location.
 
772
 
 
773
        Returns (tree, branch, relpath).
 
774
        If there is no tree at containing the location, tree will be None.
 
775
        If there is no branch containing the location, an exception will be
 
776
        raised
 
777
        relpath is the portion of the path that is contained by the branch.
 
778
        """
 
779
        controldir, relpath = klass.open_containing(location)
 
780
        tree, branch = controldir._get_tree_branch()
 
781
        return tree, branch, relpath
 
782
 
 
783
    @classmethod
 
784
    def open_containing_tree_branch_or_repository(klass, location):
 
785
        """Return the working tree, branch and repo contained by a location.
 
786
 
 
787
        Returns (tree, branch, repository, relpath).
 
788
        If there is no tree containing the location, tree will be None.
 
789
        If there is no branch containing the location, branch will be None.
 
790
        If there is no repository containing the location, repository will be
 
791
        None.
 
792
        relpath is the portion of the path that is contained by the innermost
 
793
        ControlDir.
 
794
 
 
795
        If no tree, branch or repository is found, a NotBranchError is raised.
 
796
        """
 
797
        controldir, relpath = klass.open_containing(location)
 
798
        try:
 
799
            tree, branch = controldir._get_tree_branch()
 
800
        except errors.NotBranchError:
 
801
            try:
 
802
                repo = controldir.find_repository()
 
803
                return None, None, repo, relpath
 
804
            except (errors.NoRepositoryPresent):
 
805
                raise errors.NotBranchError(location)
 
806
        return tree, branch, branch.repository, relpath
 
807
 
 
808
    @classmethod
 
809
    def create(klass, base, format=None, possible_transports=None):
 
810
        """Create a new ControlDir at the url 'base'.
 
811
 
 
812
        :param format: If supplied, the format of branch to create.  If not
 
813
            supplied, the default is used.
 
814
        :param possible_transports: If supplied, a list of transports that
 
815
            can be reused to share a remote connection.
 
816
        """
 
817
        if klass is not ControlDir:
 
818
            raise AssertionError("ControlDir.create always creates the"
 
819
                "default format, not one of %r" % klass)
 
820
        t = _mod_transport.get_transport(base, possible_transports)
 
821
        t.ensure_base()
 
822
        if format is None:
 
823
            format = ControlDirFormat.get_default_format()
 
824
        return format.initialize_on_transport(t)
 
825
 
 
826
 
 
827
class ControlDirHooks(hooks.Hooks):
 
828
    """Hooks for ControlDir operations."""
 
829
 
 
830
    def __init__(self):
 
831
        """Create the default hooks."""
 
832
        hooks.Hooks.__init__(self, "bzrlib.controldir", "ControlDir.hooks")
 
833
        self.add_hook('pre_open',
 
834
            "Invoked before attempting to open a ControlDir with the transport "
 
835
            "that the open will use.", (1, 14))
 
836
        self.add_hook('post_repo_init',
 
837
            "Invoked after a repository has been initialized. "
 
838
            "post_repo_init is called with a "
 
839
            "bzrlib.controldir.RepoInitHookParams.",
 
840
            (2, 2))
 
841
 
 
842
# install the default hooks
 
843
ControlDir.hooks = ControlDirHooks()
 
844
 
466
845
 
467
846
class ControlComponentFormat(object):
468
 
    """A component that can live inside of a .bzr meta directory."""
 
847
    """A component that can live inside of a control directory."""
469
848
 
470
849
    upgrade_recommended = False
471
850
 
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
851
    def get_format_description(self):
477
852
        """Return the short description for this format."""
478
853
        raise NotImplementedError(self.get_format_description)
505
880
            ui.ui_factory.recommend_upgrade(
506
881
                self.get_format_description(), basedir)
507
882
 
 
883
    @classmethod
 
884
    def get_format_string(cls):
 
885
        raise NotImplementedError(cls.get_format_string)
 
886
 
508
887
 
509
888
class ControlComponentFormatRegistry(registry.FormatRegistry):
510
889
    """A registry for control components (branch, workingtree, repository)."""
661
1040
    def is_supported(self):
662
1041
        """Is this format supported?
663
1042
 
664
 
        Supported formats must be initializable and openable.
 
1043
        Supported formats must be openable.
665
1044
        Unsupported formats may not support initialization or committing or
666
1045
        some other features depending on the reason for not being supported.
667
1046
        """
668
1047
        return True
669
1048
 
 
1049
    def is_initializable(self):
 
1050
        """Whether new control directories of this format can be initialized.
 
1051
        """
 
1052
        return self.is_supported()
 
1053
 
670
1054
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
671
1055
        basedir=None):
672
1056
        """Give an error or warning on old formats.
728
1112
        return self.get_format_description().rstrip()
729
1113
 
730
1114
    @classmethod
 
1115
    def all_probers(klass):
 
1116
        return klass._server_probers + klass._probers
 
1117
 
 
1118
    @classmethod
731
1119
    def known_formats(klass):
732
1120
        """Return all the known formats.
733
1121
        """
734
1122
        result = set()
735
 
        for prober_kls in klass._probers + klass._server_probers:
 
1123
        for prober_kls in klass.all_probers():
736
1124
            result.update(prober_kls.known_formats())
737
1125
        return result
738
1126
 
739
1127
    @classmethod
740
 
    def find_format(klass, transport, _server_formats=True):
 
1128
    def find_format(klass, transport, probers=None):
741
1129
        """Return the format present at transport."""
742
 
        if _server_formats:
743
 
            _probers = klass._server_probers + klass._probers
744
 
        else:
745
 
            _probers = klass._probers
746
 
        for prober_kls in _probers:
 
1130
        if probers is None:
 
1131
            probers = klass.all_probers()
 
1132
        for prober_kls in probers:
747
1133
            prober = prober_kls()
748
1134
            try:
749
1135
                return prober.probe_transport(transport)
828
1214
        """Return the current default format."""
829
1215
        return klass._default_format
830
1216
 
 
1217
    def supports_transport(self, transport):
 
1218
        """Check if this format can be opened over a particular transport.
 
1219
        """
 
1220
        raise NotImplementedError(self.supports_transport)
 
1221
 
831
1222
 
832
1223
class Prober(object):
833
1224
    """Abstract class that can be used to detect a particular kind of
856
1247
        raise NotImplementedError(self.probe_transport)
857
1248
 
858
1249
    @classmethod
859
 
    def known_formats(cls):
 
1250
    def known_formats(klass):
860
1251
        """Return the control dir formats known by this prober.
861
1252
 
862
1253
        Multiple probers can return the same formats, so this should
864
1255
 
865
1256
        :return: A set of known formats.
866
1257
        """
867
 
        raise NotImplementedError(cls.known_formats)
 
1258
        raise NotImplementedError(klass.known_formats)
868
1259
 
869
1260
 
870
1261
class ControlDirFormatInfo(object):
1003
1394
            return output
1004
1395
 
1005
1396
 
 
1397
class RepoInitHookParams(object):
 
1398
    """Object holding parameters passed to `*_repo_init` hooks.
 
1399
 
 
1400
    There are 4 fields that hooks may wish to access:
 
1401
 
 
1402
    :ivar repository: Repository created
 
1403
    :ivar format: Repository format
 
1404
    :ivar bzrdir: The controldir for the repository
 
1405
    :ivar shared: The repository is shared
 
1406
    """
 
1407
 
 
1408
    def __init__(self, repository, format, controldir, shared):
 
1409
        """Create a group of RepoInitHook parameters.
 
1410
 
 
1411
        :param repository: Repository created
 
1412
        :param format: Repository format
 
1413
        :param controldir: The controldir for the repository
 
1414
        :param shared: The repository is shared
 
1415
        """
 
1416
        self.repository = repository
 
1417
        self.format = format
 
1418
        self.bzrdir = controldir
 
1419
        self.shared = shared
 
1420
 
 
1421
    def __eq__(self, other):
 
1422
        return self.__dict__ == other.__dict__
 
1423
 
 
1424
    def __repr__(self):
 
1425
        if self.repository:
 
1426
            return "<%s for %s>" % (self.__class__.__name__,
 
1427
                self.repository)
 
1428
        else:
 
1429
            return "<%s for %s>" % (self.__class__.__name__,
 
1430
                self.bzrdir)
 
1431
 
 
1432
 
1006
1433
# Please register new formats after old formats so that formats
1007
1434
# appear in chronological order and format descriptions can build
1008
1435
# on previous ones.