~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

  • Committer: Jelmer Vernooij
  • Date: 2012-02-20 12:19:29 UTC
  • mfrom: (6437.23.11 2.5)
  • mto: (6581.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 6582.
  • Revision ID: jelmer@samba.org-20120220121929-7ni2psvjoatm1yp4
Merge bzr/2.5.

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,
34
38
    urlutils,
35
39
    )
 
40
from bzrlib.transport import local
36
41
from bzrlib.push import (
37
42
    PushResult,
38
43
    )
39
44
 
 
45
from bzrlib.i18n import gettext
40
46
""")
41
47
 
42
48
from bzrlib import registry
75
81
        return self.user_transport.base
76
82
 
77
83
 
78
 
 
79
84
class ControlDir(ControlComponent):
80
85
    """A control directory.
81
86
 
103
108
        """Return a sequence of all branches local to this control directory.
104
109
 
105
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
        """
106
118
        try:
107
 
            return [self.open_branch()]
 
119
           return { "": self.open_branch() }
108
120
        except (errors.NotBranchError, errors.NoRepositoryPresent):
109
 
            return []
 
121
           return {}
110
122
 
111
123
    def is_control_filename(self, filename):
112
124
        """True if filename is the name of a path which is reserved for
146
158
        """Destroy the repository in this ControlDir."""
147
159
        raise NotImplementedError(self.destroy_repository)
148
160
 
149
 
    def create_branch(self, name=None, repository=None):
 
161
    def create_branch(self, name=None, repository=None,
 
162
                      append_revisions_only=None):
150
163
        """Create a branch in this ControlDir.
151
164
 
152
165
        :param name: Name of the colocated branch to create, None for
153
 
            the default branch.
 
166
            the user selected branch or "" for the active branch.
 
167
        :param append_revisions_only: Whether this branch should only allow
 
168
            appending new revisions to its history.
154
169
 
155
170
        The controldirs format will control what branch format is created.
156
171
        For more control see BranchFormatXX.create(a_controldir).
160
175
    def destroy_branch(self, name=None):
161
176
        """Destroy a branch in this ControlDir.
162
177
 
163
 
        :param name: Name of the branch to destroy, None for the default 
164
 
            branch.
 
178
        :param name: Name of the branch to destroy, None for the 
 
179
            user selected branch or "" for the active branch.
 
180
        :raise NotBranchError: When the branch does not exist
165
181
        """
166
182
        raise NotImplementedError(self.destroy_branch)
167
183
 
195
211
        raise NotImplementedError(self.destroy_workingtree_metadata)
196
212
 
197
213
    def find_branch_format(self, name=None):
198
 
        """Find the branch 'format' for this bzrdir.
 
214
        """Find the branch 'format' for this controldir.
199
215
 
200
216
        This might be a synthetic object for e.g. RemoteBranch and SVN.
201
217
        """
215
231
            raise errors.NoColocatedBranchSupport(self)
216
232
        return None
217
233
 
 
234
    def set_branch_reference(self, target_branch, name=None):
 
235
        """Set the referenced URL for the branch in this controldir.
 
236
 
 
237
        :param name: Optional colocated branch name
 
238
        :param target_branch: Branch to reference
 
239
        :raises NoColocatedBranchSupport: If a branch name was specified
 
240
            but colocated branches are not supported.
 
241
        :return: The referencing branch
 
242
        """
 
243
        raise NotImplementedError(self.set_branch_reference)
 
244
 
218
245
    def open_branch(self, name=None, unsupported=False,
219
 
                    ignore_fallbacks=False):
 
246
                    ignore_fallbacks=False, possible_transports=None):
220
247
        """Open the branch object at this ControlDir if one is present.
221
248
 
222
 
        If unsupported is True, then no longer supported branch formats can
223
 
        still be opened.
224
 
 
225
 
        TODO: static convenience version of this?
 
249
        :param unsupported: if True, then no longer supported branch formats can
 
250
            still be opened.
 
251
        :param ignore_fallbacks: Whether to open fallback repositories
 
252
        :param possible_transports: Transports to use for opening e.g.
 
253
            fallback repositories.
226
254
        """
227
255
        raise NotImplementedError(self.open_branch)
228
256
 
234
262
        get at a repository.
235
263
 
236
264
        :param _unsupported: a private parameter, not part of the api.
237
 
 
238
 
        TODO: static convenience version of this?
239
265
        """
240
266
        raise NotImplementedError(self.open_repository)
241
267
 
248
274
        """
249
275
        raise NotImplementedError(self.find_repository)
250
276
 
251
 
    def open_workingtree(self, _unsupported=False,
 
277
    def open_workingtree(self, unsupported=False,
252
278
                         recommend_upgrade=True, from_branch=None):
253
279
        """Open the workingtree object at this ControlDir if one is present.
254
280
 
269
295
        branch and discards it, and that's somewhat expensive.)
270
296
        """
271
297
        try:
272
 
            self.open_branch(name)
 
298
            self.open_branch(name, ignore_fallbacks=True)
273
299
            return True
274
300
        except errors.NotBranchError:
275
301
            return False
277
303
    def _get_selected_branch(self):
278
304
        """Return the name of the branch selected by the user.
279
305
 
280
 
        :return: Name of the branch selected by the user, or None.
 
306
        :return: Name of the branch selected by the user, or "".
281
307
        """
282
308
        branch = self.root_transport.get_segment_parameters().get("branch")
283
 
        if branch is not None:
284
 
            branch = urlutils.unescape(branch)
285
 
        return branch
 
309
        if branch is None:
 
310
            branch = ""
 
311
        return urlutils.unescape(branch)
286
312
 
287
313
    def has_workingtree(self):
288
314
        """Tell if this controldir contains a working tree.
316
342
        raise NotImplementedError(self.cloning_metadir)
317
343
 
318
344
    def checkout_metadir(self):
319
 
        """Produce a metadir suitable for checkouts of this controldir."""
 
345
        """Produce a metadir suitable for checkouts of this controldir.
 
346
 
 
347
        :returns: A ControlDirFormat with all component formats
 
348
            either set appropriately or set to None if that component
 
349
            should not be created.
 
350
        """
320
351
        return self.cloning_metadir()
321
352
 
322
353
    def sprout(self, url, revision_id=None, force_new_repo=False,
413
444
        return push_result
414
445
 
415
446
    def _get_tree_branch(self, name=None):
416
 
        """Return the branch and tree, if any, for this bzrdir.
 
447
        """Return the branch and tree, if any, for this controldir.
417
448
 
418
449
        :param name: Name of colocated branch to open.
419
450
 
438
469
        raise NotImplementedError(self.get_config)
439
470
 
440
471
    def check_conversion_target(self, target_format):
441
 
        """Check that a bzrdir as a whole can be converted to a new format."""
 
472
        """Check that a controldir as a whole can be converted to a new format."""
442
473
        raise NotImplementedError(self.check_conversion_target)
443
474
 
444
475
    def clone(self, url, revision_id=None, force_new_repo=False,
445
476
              preserve_stacking=False):
446
 
        """Clone this bzrdir and its contents to url verbatim.
 
477
        """Clone this controldir and its contents to url verbatim.
447
478
 
448
479
        :param url: The url create the clone at.  If url's last component does
449
480
            not exist, it will be created.
463
494
    def clone_on_transport(self, transport, revision_id=None,
464
495
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
465
496
        create_prefix=False, use_existing_dir=True, no_tree=False):
466
 
        """Clone this bzrdir and its contents to transport verbatim.
 
497
        """Clone this controldir and its contents to transport verbatim.
467
498
 
468
499
        :param transport: The transport for the location to produce the clone
469
500
            at.  If the target directory does not exist, it will be created.
481
512
        """
482
513
        raise NotImplementedError(self.clone_on_transport)
483
514
 
 
515
    @classmethod
 
516
    def find_bzrdirs(klass, transport, evaluate=None, list_current=None):
 
517
        """Find control dirs recursively from current location.
 
518
 
 
519
        This is intended primarily as a building block for more sophisticated
 
520
        functionality, like finding trees under a directory, or finding
 
521
        branches that use a given repository.
 
522
 
 
523
        :param evaluate: An optional callable that yields recurse, value,
 
524
            where recurse controls whether this controldir is recursed into
 
525
            and value is the value to yield.  By default, all bzrdirs
 
526
            are recursed into, and the return value is the controldir.
 
527
        :param list_current: if supplied, use this function to list the current
 
528
            directory, instead of Transport.list_dir
 
529
        :return: a generator of found bzrdirs, or whatever evaluate returns.
 
530
        """
 
531
        if list_current is None:
 
532
            def list_current(transport):
 
533
                return transport.list_dir('')
 
534
        if evaluate is None:
 
535
            def evaluate(controldir):
 
536
                return True, controldir
 
537
 
 
538
        pending = [transport]
 
539
        while len(pending) > 0:
 
540
            current_transport = pending.pop()
 
541
            recurse = True
 
542
            try:
 
543
                controldir = klass.open_from_transport(current_transport)
 
544
            except (errors.NotBranchError, errors.PermissionDenied):
 
545
                pass
 
546
            else:
 
547
                recurse, value = evaluate(controldir)
 
548
                yield value
 
549
            try:
 
550
                subdirs = list_current(current_transport)
 
551
            except (errors.NoSuchFile, errors.PermissionDenied):
 
552
                continue
 
553
            if recurse:
 
554
                for subdir in sorted(subdirs, reverse=True):
 
555
                    pending.append(current_transport.clone(subdir))
 
556
 
 
557
    @classmethod
 
558
    def find_branches(klass, transport):
 
559
        """Find all branches under a transport.
 
560
 
 
561
        This will find all branches below the transport, including branches
 
562
        inside other branches.  Where possible, it will use
 
563
        Repository.find_branches.
 
564
 
 
565
        To list all the branches that use a particular Repository, see
 
566
        Repository.find_branches
 
567
        """
 
568
        def evaluate(controldir):
 
569
            try:
 
570
                repository = controldir.open_repository()
 
571
            except errors.NoRepositoryPresent:
 
572
                pass
 
573
            else:
 
574
                return False, ([], repository)
 
575
            return True, (controldir.list_branches(), None)
 
576
        ret = []
 
577
        for branches, repo in klass.find_bzrdirs(
 
578
                transport, evaluate=evaluate):
 
579
            if repo is not None:
 
580
                ret.extend(repo.find_branches())
 
581
            if branches is not None:
 
582
                ret.extend(branches)
 
583
        return ret
 
584
 
 
585
    @classmethod
 
586
    def create_branch_and_repo(klass, base, force_new_repo=False, format=None):
 
587
        """Create a new ControlDir, Branch and Repository at the url 'base'.
 
588
 
 
589
        This will use the current default ControlDirFormat unless one is
 
590
        specified, and use whatever
 
591
        repository format that that uses via controldir.create_branch and
 
592
        create_repository. If a shared repository is available that is used
 
593
        preferentially.
 
594
 
 
595
        The created Branch object is returned.
 
596
 
 
597
        :param base: The URL to create the branch at.
 
598
        :param force_new_repo: If True a new repository is always created.
 
599
        :param format: If supplied, the format of branch to create.  If not
 
600
            supplied, the default is used.
 
601
        """
 
602
        controldir = klass.create(base, format)
 
603
        controldir._find_or_create_repository(force_new_repo)
 
604
        return controldir.create_branch()
 
605
 
 
606
    @classmethod
 
607
    def create_branch_convenience(klass, base, force_new_repo=False,
 
608
                                  force_new_tree=None, format=None,
 
609
                                  possible_transports=None):
 
610
        """Create a new ControlDir, Branch and Repository at the url 'base'.
 
611
 
 
612
        This is a convenience function - it will use an existing repository
 
613
        if possible, can be told explicitly whether to create a working tree or
 
614
        not.
 
615
 
 
616
        This will use the current default ControlDirFormat unless one is
 
617
        specified, and use whatever
 
618
        repository format that that uses via ControlDir.create_branch and
 
619
        create_repository. If a shared repository is available that is used
 
620
        preferentially. Whatever repository is used, its tree creation policy
 
621
        is followed.
 
622
 
 
623
        The created Branch object is returned.
 
624
        If a working tree cannot be made due to base not being a file:// url,
 
625
        no error is raised unless force_new_tree is True, in which case no
 
626
        data is created on disk and NotLocalUrl is raised.
 
627
 
 
628
        :param base: The URL to create the branch at.
 
629
        :param force_new_repo: If True a new repository is always created.
 
630
        :param force_new_tree: If True or False force creation of a tree or
 
631
                               prevent such creation respectively.
 
632
        :param format: Override for the controldir format to create.
 
633
        :param possible_transports: An optional reusable transports list.
 
634
        """
 
635
        if force_new_tree:
 
636
            # check for non local urls
 
637
            t = _mod_transport.get_transport(base, possible_transports)
 
638
            if not isinstance(t, local.LocalTransport):
 
639
                raise errors.NotLocalUrl(base)
 
640
        controldir = klass.create(base, format, possible_transports)
 
641
        repo = controldir._find_or_create_repository(force_new_repo)
 
642
        result = controldir.create_branch()
 
643
        if force_new_tree or (repo.make_working_trees() and
 
644
                              force_new_tree is None):
 
645
            try:
 
646
                controldir.create_workingtree()
 
647
            except errors.NotLocalUrl:
 
648
                pass
 
649
        return result
 
650
 
 
651
    @classmethod
 
652
    def create_standalone_workingtree(klass, base, format=None):
 
653
        """Create a new ControlDir, WorkingTree, Branch and Repository at 'base'.
 
654
 
 
655
        'base' must be a local path or a file:// url.
 
656
 
 
657
        This will use the current default ControlDirFormat unless one is
 
658
        specified, and use whatever
 
659
        repository format that that uses for bzrdirformat.create_workingtree,
 
660
        create_branch and create_repository.
 
661
 
 
662
        :param format: Override for the controldir format to create.
 
663
        :return: The WorkingTree object.
 
664
        """
 
665
        t = _mod_transport.get_transport(base)
 
666
        if not isinstance(t, local.LocalTransport):
 
667
            raise errors.NotLocalUrl(base)
 
668
        controldir = klass.create_branch_and_repo(base,
 
669
                                               force_new_repo=True,
 
670
                                               format=format).bzrdir
 
671
        return controldir.create_workingtree()
 
672
 
 
673
    @classmethod
 
674
    def open_unsupported(klass, base):
 
675
        """Open a branch which is not supported."""
 
676
        return klass.open(base, _unsupported=True)
 
677
 
 
678
    @classmethod
 
679
    def open(klass, base, possible_transports=None, probers=None,
 
680
             _unsupported=False):
 
681
        """Open an existing controldir, rooted at 'base' (url).
 
682
 
 
683
        :param _unsupported: a private parameter to the ControlDir class.
 
684
        """
 
685
        t = _mod_transport.get_transport(base, possible_transports)
 
686
        return klass.open_from_transport(t, probers=probers,
 
687
                _unsupported=_unsupported)
 
688
 
 
689
    @classmethod
 
690
    def open_from_transport(klass, transport, _unsupported=False,
 
691
                            probers=None):
 
692
        """Open a controldir within a particular directory.
 
693
 
 
694
        :param transport: Transport containing the controldir.
 
695
        :param _unsupported: private.
 
696
        """
 
697
        for hook in klass.hooks['pre_open']:
 
698
            hook(transport)
 
699
        # Keep initial base since 'transport' may be modified while following
 
700
        # the redirections.
 
701
        base = transport.base
 
702
        def find_format(transport):
 
703
            return transport, ControlDirFormat.find_format(transport,
 
704
                probers=probers)
 
705
 
 
706
        def redirected(transport, e, redirection_notice):
 
707
            redirected_transport = transport._redirected_to(e.source, e.target)
 
708
            if redirected_transport is None:
 
709
                raise errors.NotBranchError(base)
 
710
            trace.note(gettext('{0} is{1} redirected to {2}').format(
 
711
                 transport.base, e.permanently, redirected_transport.base))
 
712
            return redirected_transport
 
713
 
 
714
        try:
 
715
            transport, format = _mod_transport.do_catching_redirections(
 
716
                find_format, transport, redirected)
 
717
        except errors.TooManyRedirections:
 
718
            raise errors.NotBranchError(base)
 
719
 
 
720
        format.check_support_status(_unsupported)
 
721
        return format.open(transport, _found=True)
 
722
 
 
723
    @classmethod
 
724
    def open_containing(klass, url, possible_transports=None):
 
725
        """Open an existing branch which contains url.
 
726
 
 
727
        :param url: url to search from.
 
728
 
 
729
        See open_containing_from_transport for more detail.
 
730
        """
 
731
        transport = _mod_transport.get_transport(url, possible_transports)
 
732
        return klass.open_containing_from_transport(transport)
 
733
 
 
734
    @classmethod
 
735
    def open_containing_from_transport(klass, a_transport):
 
736
        """Open an existing branch which contains a_transport.base.
 
737
 
 
738
        This probes for a branch at a_transport, and searches upwards from there.
 
739
 
 
740
        Basically we keep looking up until we find the control directory or
 
741
        run into the root.  If there isn't one, raises NotBranchError.
 
742
        If there is one and it is either an unrecognised format or an unsupported
 
743
        format, UnknownFormatError or UnsupportedFormatError are raised.
 
744
        If there is one, it is returned, along with the unused portion of url.
 
745
 
 
746
        :return: The ControlDir that contains the path, and a Unicode path
 
747
                for the rest of the URL.
 
748
        """
 
749
        # this gets the normalised url back. I.e. '.' -> the full path.
 
750
        url = a_transport.base
 
751
        while True:
 
752
            try:
 
753
                result = klass.open_from_transport(a_transport)
 
754
                return result, urlutils.unescape(a_transport.relpath(url))
 
755
            except errors.NotBranchError, e:
 
756
                pass
 
757
            except errors.PermissionDenied:
 
758
                pass
 
759
            try:
 
760
                new_t = a_transport.clone('..')
 
761
            except errors.InvalidURLJoin:
 
762
                # reached the root, whatever that may be
 
763
                raise errors.NotBranchError(path=url)
 
764
            if new_t.base == a_transport.base:
 
765
                # reached the root, whatever that may be
 
766
                raise errors.NotBranchError(path=url)
 
767
            a_transport = new_t
 
768
 
 
769
    @classmethod
 
770
    def open_tree_or_branch(klass, location):
 
771
        """Return the branch and working tree at a location.
 
772
 
 
773
        If there is no tree at the location, tree will be None.
 
774
        If there is no branch at the location, an exception will be
 
775
        raised
 
776
        :return: (tree, branch)
 
777
        """
 
778
        controldir = klass.open(location)
 
779
        return controldir._get_tree_branch()
 
780
 
 
781
    @classmethod
 
782
    def open_containing_tree_or_branch(klass, location,
 
783
            possible_transports=None):
 
784
        """Return the branch and working tree contained by a location.
 
785
 
 
786
        Returns (tree, branch, relpath).
 
787
        If there is no tree at containing the location, tree will be None.
 
788
        If there is no branch containing the location, an exception will be
 
789
        raised
 
790
        relpath is the portion of the path that is contained by the branch.
 
791
        """
 
792
        controldir, relpath = klass.open_containing(location,
 
793
            possible_transports=possible_transports)
 
794
        tree, branch = controldir._get_tree_branch()
 
795
        return tree, branch, relpath
 
796
 
 
797
    @classmethod
 
798
    def open_containing_tree_branch_or_repository(klass, location):
 
799
        """Return the working tree, branch and repo contained by a location.
 
800
 
 
801
        Returns (tree, branch, repository, relpath).
 
802
        If there is no tree containing the location, tree will be None.
 
803
        If there is no branch containing the location, branch will be None.
 
804
        If there is no repository containing the location, repository will be
 
805
        None.
 
806
        relpath is the portion of the path that is contained by the innermost
 
807
        ControlDir.
 
808
 
 
809
        If no tree, branch or repository is found, a NotBranchError is raised.
 
810
        """
 
811
        controldir, relpath = klass.open_containing(location)
 
812
        try:
 
813
            tree, branch = controldir._get_tree_branch()
 
814
        except errors.NotBranchError:
 
815
            try:
 
816
                repo = controldir.find_repository()
 
817
                return None, None, repo, relpath
 
818
            except (errors.NoRepositoryPresent):
 
819
                raise errors.NotBranchError(location)
 
820
        return tree, branch, branch.repository, relpath
 
821
 
 
822
    @classmethod
 
823
    def create(klass, base, format=None, possible_transports=None):
 
824
        """Create a new ControlDir at the url 'base'.
 
825
 
 
826
        :param format: If supplied, the format of branch to create.  If not
 
827
            supplied, the default is used.
 
828
        :param possible_transports: If supplied, a list of transports that
 
829
            can be reused to share a remote connection.
 
830
        """
 
831
        if klass is not ControlDir:
 
832
            raise AssertionError("ControlDir.create always creates the"
 
833
                "default format, not one of %r" % klass)
 
834
        t = _mod_transport.get_transport(base, possible_transports)
 
835
        t.ensure_base()
 
836
        if format is None:
 
837
            format = ControlDirFormat.get_default_format()
 
838
        return format.initialize_on_transport(t)
 
839
 
 
840
 
 
841
class ControlDirHooks(hooks.Hooks):
 
842
    """Hooks for ControlDir operations."""
 
843
 
 
844
    def __init__(self):
 
845
        """Create the default hooks."""
 
846
        hooks.Hooks.__init__(self, "bzrlib.controldir", "ControlDir.hooks")
 
847
        self.add_hook('pre_open',
 
848
            "Invoked before attempting to open a ControlDir with the transport "
 
849
            "that the open will use.", (1, 14))
 
850
        self.add_hook('post_repo_init',
 
851
            "Invoked after a repository has been initialized. "
 
852
            "post_repo_init is called with a "
 
853
            "bzrlib.controldir.RepoInitHookParams.",
 
854
            (2, 2))
 
855
 
 
856
# install the default hooks
 
857
ControlDir.hooks = ControlDirHooks()
 
858
 
484
859
 
485
860
class ControlComponentFormat(object):
486
 
    """A component that can live inside of a .bzr meta directory."""
 
861
    """A component that can live inside of a control directory."""
487
862
 
488
863
    upgrade_recommended = False
489
864
 
490
 
    def get_format_string(self):
491
 
        """Return the format of this format, if usable in meta directories."""
492
 
        raise NotImplementedError(self.get_format_string)
493
 
 
494
865
    def get_format_description(self):
495
866
        """Return the short description for this format."""
496
867
        raise NotImplementedError(self.get_format_description)
523
894
            ui.ui_factory.recommend_upgrade(
524
895
                self.get_format_description(), basedir)
525
896
 
 
897
    @classmethod
 
898
    def get_format_string(cls):
 
899
        raise NotImplementedError(cls.get_format_string)
 
900
 
526
901
 
527
902
class ControlComponentFormatRegistry(registry.FormatRegistry):
528
903
    """A registry for control components (branch, workingtree, repository)."""
679
1054
    def is_supported(self):
680
1055
        """Is this format supported?
681
1056
 
682
 
        Supported formats must be initializable and openable.
 
1057
        Supported formats must be openable.
683
1058
        Unsupported formats may not support initialization or committing or
684
1059
        some other features depending on the reason for not being supported.
685
1060
        """
686
1061
        return True
687
1062
 
 
1063
    def is_initializable(self):
 
1064
        """Whether new control directories of this format can be initialized.
 
1065
        """
 
1066
        return self.is_supported()
 
1067
 
688
1068
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
689
1069
        basedir=None):
690
1070
        """Give an error or warning on old formats.
746
1126
        return self.get_format_description().rstrip()
747
1127
 
748
1128
    @classmethod
 
1129
    def all_probers(klass):
 
1130
        return klass._server_probers + klass._probers
 
1131
 
 
1132
    @classmethod
749
1133
    def known_formats(klass):
750
1134
        """Return all the known formats.
751
1135
        """
752
1136
        result = set()
753
 
        for prober_kls in klass._probers + klass._server_probers:
 
1137
        for prober_kls in klass.all_probers():
754
1138
            result.update(prober_kls.known_formats())
755
1139
        return result
756
1140
 
757
1141
    @classmethod
758
 
    def find_format(klass, transport, _server_formats=True):
 
1142
    def find_format(klass, transport, probers=None):
759
1143
        """Return the format present at transport."""
760
 
        if _server_formats:
761
 
            _probers = klass._server_probers + klass._probers
762
 
        else:
763
 
            _probers = klass._probers
764
 
        for prober_kls in _probers:
 
1144
        if probers is None:
 
1145
            probers = klass.all_probers()
 
1146
        for prober_kls in probers:
765
1147
            prober = prober_kls()
766
1148
            try:
767
1149
                return prober.probe_transport(transport)
846
1228
        """Return the current default format."""
847
1229
        return klass._default_format
848
1230
 
 
1231
    def supports_transport(self, transport):
 
1232
        """Check if this format can be opened over a particular transport.
 
1233
        """
 
1234
        raise NotImplementedError(self.supports_transport)
 
1235
 
849
1236
 
850
1237
class Prober(object):
851
1238
    """Abstract class that can be used to detect a particular kind of
874
1261
        raise NotImplementedError(self.probe_transport)
875
1262
 
876
1263
    @classmethod
877
 
    def known_formats(cls):
 
1264
    def known_formats(klass):
878
1265
        """Return the control dir formats known by this prober.
879
1266
 
880
1267
        Multiple probers can return the same formats, so this should
882
1269
 
883
1270
        :return: A set of known formats.
884
1271
        """
885
 
        raise NotImplementedError(cls.known_formats)
 
1272
        raise NotImplementedError(klass.known_formats)
886
1273
 
887
1274
 
888
1275
class ControlDirFormatInfo(object):
1021
1408
            return output
1022
1409
 
1023
1410
 
 
1411
class RepoInitHookParams(object):
 
1412
    """Object holding parameters passed to `*_repo_init` hooks.
 
1413
 
 
1414
    There are 4 fields that hooks may wish to access:
 
1415
 
 
1416
    :ivar repository: Repository created
 
1417
    :ivar format: Repository format
 
1418
    :ivar bzrdir: The controldir for the repository
 
1419
    :ivar shared: The repository is shared
 
1420
    """
 
1421
 
 
1422
    def __init__(self, repository, format, controldir, shared):
 
1423
        """Create a group of RepoInitHook parameters.
 
1424
 
 
1425
        :param repository: Repository created
 
1426
        :param format: Repository format
 
1427
        :param controldir: The controldir for the repository
 
1428
        :param shared: The repository is shared
 
1429
        """
 
1430
        self.repository = repository
 
1431
        self.format = format
 
1432
        self.bzrdir = controldir
 
1433
        self.shared = shared
 
1434
 
 
1435
    def __eq__(self, other):
 
1436
        return self.__dict__ == other.__dict__
 
1437
 
 
1438
    def __repr__(self):
 
1439
        if self.repository:
 
1440
            return "<%s for %s>" % (self.__class__.__name__,
 
1441
                self.repository)
 
1442
        else:
 
1443
            return "<%s for %s>" % (self.__class__.__name__,
 
1444
                self.bzrdir)
 
1445
 
 
1446
 
1024
1447
# Please register new formats after old formats so that formats
1025
1448
# appear in chronological order and format descriptions can build
1026
1449
# on previous ones.