/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

  • Committer: Jelmer Vernooij
  • Date: 2010-08-17 22:24:01 UTC
  • mfrom: (5379 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5389.
  • Revision ID: jelmer@samba.org-20100817222401-x6iblsx36pu6bi3r
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2010 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""ControlDir is the basic control directory class.
 
18
 
 
19
The ControlDir class is the base for the control directory used
 
20
by all bzr and foreign formats. For the ".bzr" implementation,
 
21
see bzrlib.bzrdir.BzrDir.
 
22
"""
 
23
 
 
24
from bzrlib.lazy_import import lazy_import
 
25
lazy_import(globals(), """
 
26
import textwrap
 
27
 
 
28
from bzrlib import (
 
29
    errors,
 
30
    graph,
 
31
    registry,
 
32
    revision as _mod_revision,
 
33
    symbol_versioning,
 
34
    urlutils,
 
35
    )
 
36
from bzrlib.push import (
 
37
    PushResult,
 
38
    )
 
39
from bzrlib.trace import (
 
40
    mutter,
 
41
    )
 
42
from bzrlib.transport import (
 
43
    get_transport,
 
44
    local,
 
45
    )
 
46
 
 
47
""")
 
48
 
 
49
 
 
50
class ControlComponent(object):
 
51
    """Abstract base class for control directory components.
 
52
 
 
53
    This provides interfaces that are common across bzrdirs,
 
54
    repositories, branches, and workingtree control directories.
 
55
 
 
56
    They all expose two urls and transports: the *user* URL is the
 
57
    one that stops above the control directory (eg .bzr) and that
 
58
    should normally be used in messages, and the *control* URL is
 
59
    under that in eg .bzr/checkout and is used to read the control
 
60
    files.
 
61
 
 
62
    This can be used as a mixin and is intended to fit with
 
63
    foreign formats.
 
64
    """
 
65
 
 
66
    @property
 
67
    def control_transport(self):
 
68
        raise NotImplementedError
 
69
 
 
70
    @property
 
71
    def control_url(self):
 
72
        return self.control_transport.base
 
73
 
 
74
    @property
 
75
    def user_transport(self):
 
76
        raise NotImplementedError
 
77
 
 
78
    @property
 
79
    def user_url(self):
 
80
        return self.user_transport.base
 
81
 
 
82
 
 
83
class ControlDir(ControlComponent):
 
84
    """A control directory."""
 
85
 
 
86
    def can_convert_format(self):
 
87
        """Return true if this bzrdir is one whose format we can convert from."""
 
88
        return True
 
89
 
 
90
    def list_branches(self):
 
91
        """Return a sequence of all branches local to this control directory.
 
92
 
 
93
        """
 
94
        try:
 
95
            return [self.open_branch()]
 
96
        except (errors.NotBranchError, errors.NoRepositoryPresent):
 
97
            return []
 
98
 
 
99
    def is_control_filename(self, filename):
 
100
        """True if filename is the name of a path which is reserved for bzrdir's.
 
101
 
 
102
        :param filename: A filename within the root transport of this bzrdir.
 
103
 
 
104
        This is true IF and ONLY IF the filename is part of the namespace reserved
 
105
        for bzr control dirs. Currently this is the '.bzr' directory in the root
 
106
        of the root_transport. it is expected that plugins will need to extend
 
107
        this in the future - for instance to make bzr talk with svn working
 
108
        trees.
 
109
        """
 
110
        raise NotImplementedError(self.is_control_filename)
 
111
 
 
112
    def needs_format_conversion(self, format=None):
 
113
        """Return true if this bzrdir needs convert_format run on it.
 
114
 
 
115
        For instance, if the repository format is out of date but the
 
116
        branch and working tree are not, this should return True.
 
117
 
 
118
        :param format: Optional parameter indicating a specific desired
 
119
                       format we plan to arrive at.
 
120
        """
 
121
        raise NotImplementedError(self.needs_format_conversion)
 
122
 
 
123
    def destroy_repository(self):
 
124
        """Destroy the repository in this BzrDir"""
 
125
        raise NotImplementedError(self.destroy_repository)
 
126
 
 
127
    def create_branch(self, name=None):
 
128
        """Create a branch in this BzrDir.
 
129
 
 
130
        :param name: Name of the colocated branch to create, None for
 
131
            the default branch.
 
132
 
 
133
        The bzrdir's format will control what branch format is created.
 
134
        For more control see BranchFormatXX.create(a_bzrdir).
 
135
        """
 
136
        raise NotImplementedError(self.create_branch)
 
137
 
 
138
    def destroy_branch(self, name=None):
 
139
        """Destroy a branch in this BzrDir.
 
140
 
 
141
        :param name: Name of the branch to destroy, None for the default 
 
142
            branch.
 
143
        """
 
144
        raise NotImplementedError(self.destroy_branch)
 
145
 
 
146
    def create_workingtree(self, revision_id=None, from_branch=None,
 
147
        accelerator_tree=None, hardlink=False):
 
148
        """Create a working tree at this BzrDir.
 
149
 
 
150
        :param revision_id: create it as of this revision id.
 
151
        :param from_branch: override bzrdir branch (for lightweight checkouts)
 
152
        :param accelerator_tree: A tree which can be used for retrieving file
 
153
            contents more quickly than the revision tree, i.e. a workingtree.
 
154
            The revision tree will be used for cases where accelerator_tree's
 
155
            content is different.
 
156
        """
 
157
        raise NotImplementedError(self.create_workingtree)
 
158
 
 
159
    def destroy_workingtree(self):
 
160
        """Destroy the working tree at this BzrDir.
 
161
 
 
162
        Formats that do not support this may raise UnsupportedOperation.
 
163
        """
 
164
        raise NotImplementedError(self.destroy_workingtree)
 
165
 
 
166
    def destroy_workingtree_metadata(self):
 
167
        """Destroy the control files for the working tree at this BzrDir.
 
168
 
 
169
        The contents of working tree files are not affected.
 
170
        Formats that do not support this may raise UnsupportedOperation.
 
171
        """
 
172
        raise NotImplementedError(self.destroy_workingtree_metadata)
 
173
 
 
174
    def get_branch_reference(self, name=None):
 
175
        """Return the referenced URL for the branch in this bzrdir.
 
176
 
 
177
        :param name: Optional colocated branch name
 
178
        :raises NotBranchError: If there is no Branch.
 
179
        :raises NoColocatedBranchSupport: If a branch name was specified
 
180
            but colocated branches are not supported.
 
181
        :return: The URL the branch in this bzrdir references if it is a
 
182
            reference branch, or None for regular branches.
 
183
        """
 
184
        if name is not None:
 
185
            raise errors.NoColocatedBranchSupport(self)
 
186
        return None
 
187
 
 
188
    def get_branch_transport(self, branch_format, name=None):
 
189
        """Get the transport for use by branch format in this BzrDir.
 
190
 
 
191
        Note that bzr dirs that do not support format strings will raise
 
192
        IncompatibleFormat if the branch format they are given has
 
193
        a format string, and vice versa.
 
194
 
 
195
        If branch_format is None, the transport is returned with no
 
196
        checking. If it is not None, then the returned transport is
 
197
        guaranteed to point to an existing directory ready for use.
 
198
        """
 
199
        raise NotImplementedError(self.get_branch_transport)
 
200
 
 
201
    def get_repository_transport(self, repository_format):
 
202
        """Get the transport for use by repository format in this BzrDir.
 
203
 
 
204
        Note that bzr dirs that do not support format strings will raise
 
205
        IncompatibleFormat if the repository format they are given has
 
206
        a format string, and vice versa.
 
207
 
 
208
        If repository_format is None, the transport is returned with no
 
209
        checking. If it is not None, then the returned transport is
 
210
        guaranteed to point to an existing directory ready for use.
 
211
        """
 
212
        raise NotImplementedError(self.get_repository_transport)
 
213
 
 
214
    def get_workingtree_transport(self, tree_format):
 
215
        """Get the transport for use by workingtree format in this BzrDir.
 
216
 
 
217
        Note that bzr dirs that do not support format strings will raise
 
218
        IncompatibleFormat if the workingtree format they are given has a
 
219
        format string, and vice versa.
 
220
 
 
221
        If workingtree_format is None, the transport is returned with no
 
222
        checking. If it is not None, then the returned transport is
 
223
        guaranteed to point to an existing directory ready for use.
 
224
        """
 
225
        raise NotImplementedError(self.get_workingtree_transport)
 
226
 
 
227
    def open_branch(self, name=None, unsupported=False,
 
228
                    ignore_fallbacks=False):
 
229
        """Open the branch object at this BzrDir if one is present.
 
230
 
 
231
        If unsupported is True, then no longer supported branch formats can
 
232
        still be opened.
 
233
 
 
234
        TODO: static convenience version of this?
 
235
        """
 
236
        raise NotImplementedError(self.open_branch)
 
237
 
 
238
    def open_repository(self, _unsupported=False):
 
239
        """Open the repository object at this BzrDir if one is present.
 
240
 
 
241
        This will not follow the Branch object pointer - it's strictly a direct
 
242
        open facility. Most client code should use open_branch().repository to
 
243
        get at a repository.
 
244
 
 
245
        :param _unsupported: a private parameter, not part of the api.
 
246
        TODO: static convenience version of this?
 
247
        """
 
248
        raise NotImplementedError(self.open_repository)
 
249
 
 
250
    def find_repository(self):
 
251
        """Find the repository that should be used.
 
252
 
 
253
        This does not require a branch as we use it to find the repo for
 
254
        new branches as well as to hook existing branches up to their
 
255
        repository.
 
256
        """
 
257
        raise NotImplementedError(self.find_repository)
 
258
 
 
259
    def open_workingtree(self, _unsupported=False,
 
260
                         recommend_upgrade=True, from_branch=None):
 
261
        """Open the workingtree object at this BzrDir if one is present.
 
262
 
 
263
        :param recommend_upgrade: Optional keyword parameter, when True (the
 
264
            default), emit through the ui module a recommendation that the user
 
265
            upgrade the working tree when the workingtree being opened is old
 
266
            (but still fully supported).
 
267
        :param from_branch: override bzrdir branch (for lightweight checkouts)
 
268
        """
 
269
        raise NotImplementedError(self.open_workingtree)
 
270
 
 
271
    def has_branch(self, name=None):
 
272
        """Tell if this bzrdir contains a branch.
 
273
 
 
274
        Note: if you're going to open the branch, you should just go ahead
 
275
        and try, and not ask permission first.  (This method just opens the
 
276
        branch and discards it, and that's somewhat expensive.)
 
277
        """
 
278
        try:
 
279
            self.open_branch(name)
 
280
            return True
 
281
        except errors.NotBranchError:
 
282
            return False
 
283
 
 
284
    def has_workingtree(self):
 
285
        """Tell if this bzrdir contains a working tree.
 
286
 
 
287
        This will still raise an exception if the bzrdir has a workingtree that
 
288
        is remote & inaccessible.
 
289
 
 
290
        Note: if you're going to open the working tree, you should just go ahead
 
291
        and try, and not ask permission first.  (This method just opens the
 
292
        workingtree and discards it, and that's somewhat expensive.)
 
293
        """
 
294
        try:
 
295
            self.open_workingtree(recommend_upgrade=False)
 
296
            return True
 
297
        except errors.NoWorkingTree:
 
298
            return False
 
299
 
 
300
    def cloning_metadir(self, require_stacking=False):
 
301
        """Produce a metadir suitable for cloning or sprouting with.
 
302
 
 
303
        These operations may produce workingtrees (yes, even though they're
 
304
        "cloning" something that doesn't have a tree), so a viable workingtree
 
305
        format must be selected.
 
306
 
 
307
        :require_stacking: If True, non-stackable formats will be upgraded
 
308
            to similar stackable formats.
 
309
        :returns: a BzrDirFormat with all component formats either set
 
310
            appropriately or set to None if that component should not be
 
311
            created.
 
312
        """
 
313
        raise NotImplementedError(self.cloning_metadir)
 
314
 
 
315
    def checkout_metadir(self):
 
316
        """Produce a metadir suitable for checkouts of this controldir."""
 
317
        return self.cloning_metadir()
 
318
 
 
319
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
320
               recurse='down', possible_transports=None,
 
321
               accelerator_tree=None, hardlink=False, stacked=False,
 
322
               source_branch=None, create_tree_if_local=True):
 
323
        """Create a copy of this bzrdir prepared for use as a new line of
 
324
        development.
 
325
 
 
326
        If url's last component does not exist, it will be created.
 
327
 
 
328
        Attributes related to the identity of the source branch like
 
329
        branch nickname will be cleaned, a working tree is created
 
330
        whether one existed before or not; and a local branch is always
 
331
        created.
 
332
 
 
333
        if revision_id is not None, then the clone operation may tune
 
334
            itself to download less data.
 
335
        :param accelerator_tree: A tree which can be used for retrieving file
 
336
            contents more quickly than the revision tree, i.e. a workingtree.
 
337
            The revision tree will be used for cases where accelerator_tree's
 
338
            content is different.
 
339
        :param hardlink: If true, hard-link files from accelerator_tree,
 
340
            where possible.
 
341
        :param stacked: If true, create a stacked branch referring to the
 
342
            location of this control directory.
 
343
        :param create_tree_if_local: If true, a working-tree will be created
 
344
            when working locally.
 
345
        """
 
346
        target_transport = get_transport(url, possible_transports)
 
347
        target_transport.ensure_base()
 
348
        cloning_format = self.cloning_metadir(stacked)
 
349
        # Create/update the result branch
 
350
        result = cloning_format.initialize_on_transport(target_transport)
 
351
        # if a stacked branch wasn't requested, we don't create one
 
352
        # even if the origin was stacked
 
353
        stacked_branch_url = None
 
354
        if source_branch is not None:
 
355
            if stacked:
 
356
                stacked_branch_url = self.root_transport.base
 
357
            source_repository = source_branch.repository
 
358
        else:
 
359
            try:
 
360
                source_branch = self.open_branch()
 
361
                source_repository = source_branch.repository
 
362
                if stacked:
 
363
                    stacked_branch_url = self.root_transport.base
 
364
            except errors.NotBranchError:
 
365
                source_branch = None
 
366
                try:
 
367
                    source_repository = self.open_repository()
 
368
                except errors.NoRepositoryPresent:
 
369
                    source_repository = 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
        is_stacked = stacked or (len(result_repo._fallback_repositories) != 0)
 
374
        if is_new_repo and revision_id is not None and not is_stacked:
 
375
            fetch_spec = graph.PendingAncestryResult(
 
376
                [revision_id], source_repository)
 
377
        else:
 
378
            fetch_spec = None
 
379
        if source_repository is not None:
 
380
            # Fetch while stacked to prevent unstacked fetch from
 
381
            # Branch.sprout.
 
382
            if fetch_spec is None:
 
383
                result_repo.fetch(source_repository, revision_id=revision_id)
 
384
            else:
 
385
                result_repo.fetch(source_repository, fetch_spec=fetch_spec)
 
386
 
 
387
        if source_branch is None:
 
388
            # this is for sprouting a bzrdir 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
        mutter("created new branch %r" % (result_branch,))
 
396
 
 
397
        # Create/update the result working tree
 
398
        if (create_tree_if_local and
 
399
            isinstance(target_transport, local.LocalTransport) and
 
400
            (result_repo is None or result_repo.make_working_trees())):
 
401
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
 
402
                hardlink=hardlink)
 
403
            wt.lock_write()
 
404
            try:
 
405
                if wt.path2id('') is None:
 
406
                    try:
 
407
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
408
                    except errors.NoWorkingTree:
 
409
                        pass
 
410
            finally:
 
411
                wt.unlock()
 
412
        else:
 
413
            wt = None
 
414
        if recurse == 'down':
 
415
            if wt is not None:
 
416
                basis = wt.basis_tree()
 
417
                basis.lock_read()
 
418
                subtrees = basis.iter_references()
 
419
            elif result_branch is not None:
 
420
                basis = result_branch.basis_tree()
 
421
                basis.lock_read()
 
422
                subtrees = basis.iter_references()
 
423
            elif source_branch is not None:
 
424
                basis = source_branch.basis_tree()
 
425
                basis.lock_read()
 
426
                subtrees = basis.iter_references()
 
427
            else:
 
428
                subtrees = []
 
429
                basis = None
 
430
            try:
 
431
                for path, file_id in subtrees:
 
432
                    target = urlutils.join(url, urlutils.escape(path))
 
433
                    sublocation = source_branch.reference_parent(file_id, path)
 
434
                    sublocation.bzrdir.sprout(target,
 
435
                        basis.get_reference_revision(file_id, path),
 
436
                        force_new_repo=force_new_repo, recurse=recurse,
 
437
                        stacked=stacked)
 
438
            finally:
 
439
                if basis is not None:
 
440
                    basis.unlock()
 
441
        return result
 
442
 
 
443
    def push_branch(self, source, revision_id=None, overwrite=False, 
 
444
        remember=False, create_prefix=False):
 
445
        """Push the source branch into this BzrDir."""
 
446
        br_to = None
 
447
        # If we can open a branch, use its direct repository, otherwise see
 
448
        # if there is a repository without a branch.
 
449
        try:
 
450
            br_to = self.open_branch()
 
451
        except errors.NotBranchError:
 
452
            # Didn't find a branch, can we find a repository?
 
453
            repository_to = self.find_repository()
 
454
        else:
 
455
            # Found a branch, so we must have found a repository
 
456
            repository_to = br_to.repository
 
457
 
 
458
        push_result = PushResult()
 
459
        push_result.source_branch = source
 
460
        if br_to is None:
 
461
            # We have a repository but no branch, copy the revisions, and then
 
462
            # create a branch.
 
463
            repository_to.fetch(source.repository, revision_id=revision_id)
 
464
            br_to = source.clone(self, revision_id=revision_id)
 
465
            if source.get_push_location() is None or remember:
 
466
                source.set_push_location(br_to.base)
 
467
            push_result.stacked_on = None
 
468
            push_result.branch_push_result = None
 
469
            push_result.old_revno = None
 
470
            push_result.old_revid = _mod_revision.NULL_REVISION
 
471
            push_result.target_branch = br_to
 
472
            push_result.master_branch = None
 
473
            push_result.workingtree_updated = False
 
474
        else:
 
475
            # We have successfully opened the branch, remember if necessary:
 
476
            if source.get_push_location() is None or remember:
 
477
                source.set_push_location(br_to.base)
 
478
            try:
 
479
                tree_to = self.open_workingtree()
 
480
            except errors.NotLocalUrl:
 
481
                push_result.branch_push_result = source.push(br_to, 
 
482
                    overwrite, stop_revision=revision_id)
 
483
                push_result.workingtree_updated = False
 
484
            except errors.NoWorkingTree:
 
485
                push_result.branch_push_result = source.push(br_to,
 
486
                    overwrite, stop_revision=revision_id)
 
487
                push_result.workingtree_updated = None # Not applicable
 
488
            else:
 
489
                tree_to.lock_write()
 
490
                try:
 
491
                    push_result.branch_push_result = source.push(
 
492
                        tree_to.branch, overwrite, stop_revision=revision_id)
 
493
                    tree_to.update()
 
494
                finally:
 
495
                    tree_to.unlock()
 
496
                push_result.workingtree_updated = True
 
497
            push_result.old_revno = push_result.branch_push_result.old_revno
 
498
            push_result.old_revid = push_result.branch_push_result.old_revid
 
499
            push_result.target_branch = \
 
500
                push_result.branch_push_result.target_branch
 
501
        return push_result
 
502
 
 
503
 
 
504
class ControlDirFormat(object):
 
505
    """An encapsulation of the initialization and open routines for a format.
 
506
 
 
507
    Formats provide three things:
 
508
     * An initialization routine,
 
509
     * a format string,
 
510
     * an open routine.
 
511
 
 
512
    Formats are placed in a dict by their format string for reference
 
513
    during bzrdir opening. These should be subclasses of BzrDirFormat
 
514
    for consistency.
 
515
 
 
516
    Once a format is deprecated, just deprecate the initialize and open
 
517
    methods on the format class. Do not deprecate the object, as the
 
518
    object will be created every system load.
 
519
 
 
520
    :cvar colocated_branches: Whether this formats supports colocated branches.
 
521
    """
 
522
 
 
523
    _default_format = None
 
524
    """The default format used for new control directories."""
 
525
 
 
526
    _formats = []
 
527
    """The registered control formats - .bzr, ....
 
528
 
 
529
    This is a list of ControlDirFormat objects.
 
530
    """
 
531
 
 
532
    _server_probers = []
 
533
    """The registered server format probers, e.g. RemoteBzrProber.
 
534
 
 
535
    This is a list of Prober-derived classes.
 
536
    """
 
537
 
 
538
    _probers = []
 
539
    """The registered format probers, e.g. BzrProber.
 
540
 
 
541
    This is a list of Prober-derived classes.
 
542
    """
 
543
 
 
544
    colocated_branches = False
 
545
    """Whether co-located branches are supported for this control dir format.
 
546
    """
 
547
 
 
548
    def get_format_description(self):
 
549
        """Return the short description for this format."""
 
550
        raise NotImplementedError(self.get_format_description)
 
551
 
 
552
    def get_converter(self, format=None):
 
553
        """Return the converter to use to convert bzrdirs needing converts.
 
554
 
 
555
        This returns a bzrlib.bzrdir.Converter object.
 
556
 
 
557
        This should return the best upgrader to step this format towards the
 
558
        current default format. In the case of plugins we can/should provide
 
559
        some means for them to extend the range of returnable converters.
 
560
 
 
561
        :param format: Optional format to override the default format of the
 
562
                       library.
 
563
        """
 
564
        raise NotImplementedError(self.get_converter)
 
565
 
 
566
    def is_supported(self):
 
567
        """Is this format supported?
 
568
 
 
569
        Supported formats must be initializable and openable.
 
570
        Unsupported formats may not support initialization or committing or
 
571
        some other features depending on the reason for not being supported.
 
572
        """
 
573
        return True
 
574
 
 
575
    def same_model(self, target_format):
 
576
        return (self.repository_format.rich_root_data ==
 
577
            target_format.rich_root_data)
 
578
 
 
579
    @classmethod
 
580
    def register_format(klass, format):
 
581
        """Register a format that does not use '.bzr' for its control dir.
 
582
 
 
583
        """
 
584
        klass._formats.append(format)
 
585
 
 
586
    @classmethod
 
587
    def register_prober(klass, prober):
 
588
        """Register a prober that can look for a control dir.
 
589
 
 
590
        """
 
591
        klass._probers.append(prober)
 
592
 
 
593
    @classmethod
 
594
    def unregister_prober(klass, prober):
 
595
        """Unregister a prober.
 
596
 
 
597
        """
 
598
        klass._probers.remove(prober)
 
599
 
 
600
    @classmethod
 
601
    def register_server_prober(klass, prober):
 
602
        """Register a control format prober for client-server environments.
 
603
 
 
604
        These probers will be used before ones registered with
 
605
        register_prober.  This gives implementations that decide to the
 
606
        chance to grab it before anything looks at the contents of the format
 
607
        file.
 
608
        """
 
609
        klass._server_probers.append(prober)
 
610
 
 
611
    def __str__(self):
 
612
        # Trim the newline
 
613
        return self.get_format_description().rstrip()
 
614
 
 
615
    @classmethod
 
616
    def unregister_format(klass, format):
 
617
        klass._formats.remove(format)
 
618
 
 
619
    @classmethod
 
620
    def known_formats(klass):
 
621
        """Return all the known formats.
 
622
        """
 
623
        return set(klass._formats)
 
624
 
 
625
    @classmethod
 
626
    def find_format(klass, transport, _server_formats=True):
 
627
        """Return the format present at transport."""
 
628
        if _server_formats:
 
629
            _probers = klass._server_probers + klass._probers
 
630
        else:
 
631
            _probers = klass._probers
 
632
        for prober_kls in _probers:
 
633
            prober = prober_kls()
 
634
            try:
 
635
                return prober.probe_transport(transport)
 
636
            except errors.NotBranchError:
 
637
                # this format does not find a control dir here.
 
638
                pass
 
639
        raise errors.NotBranchError(path=transport.base)
 
640
 
 
641
    def initialize(self, url, possible_transports=None):
 
642
        """Create a control dir at this url and return an opened copy.
 
643
 
 
644
        While not deprecated, this method is very specific and its use will
 
645
        lead to many round trips to setup a working environment. See
 
646
        initialize_on_transport_ex for a [nearly] all-in-one method.
 
647
 
 
648
        Subclasses should typically override initialize_on_transport
 
649
        instead of this method.
 
650
        """
 
651
        return self.initialize_on_transport(get_transport(url,
 
652
                                                          possible_transports))
 
653
    def initialize_on_transport(self, transport):
 
654
        """Initialize a new bzrdir in the base directory of a Transport."""
 
655
        raise NotImplementedError(self.initialize_on_transport)
 
656
 
 
657
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
658
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
659
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
660
        shared_repo=False, vfs_only=False):
 
661
        """Create this format on transport.
 
662
 
 
663
        The directory to initialize will be created.
 
664
 
 
665
        :param force_new_repo: Do not use a shared repository for the target,
 
666
                               even if one is available.
 
667
        :param create_prefix: Create any missing directories leading up to
 
668
            to_transport.
 
669
        :param use_existing_dir: Use an existing directory if one exists.
 
670
        :param stacked_on: A url to stack any created branch on, None to follow
 
671
            any target stacking policy.
 
672
        :param stack_on_pwd: If stack_on is relative, the location it is
 
673
            relative to.
 
674
        :param repo_format_name: If non-None, a repository will be
 
675
            made-or-found. Should none be found, or if force_new_repo is True
 
676
            the repo_format_name is used to select the format of repository to
 
677
            create.
 
678
        :param make_working_trees: Control the setting of make_working_trees
 
679
            for a new shared repository when one is made. None to use whatever
 
680
            default the format has.
 
681
        :param shared_repo: Control whether made repositories are shared or
 
682
            not.
 
683
        :param vfs_only: If True do not attempt to use a smart server
 
684
        :return: repo, bzrdir, require_stacking, repository_policy. repo is
 
685
            None if none was created or found, bzrdir is always valid.
 
686
            require_stacking is the result of examining the stacked_on
 
687
            parameter and any stacking policy found for the target.
 
688
        """
 
689
        raise NotImplementedError(self.initialize_on_transport_ex)
 
690
 
 
691
    def network_name(self):
 
692
        """A simple byte string uniquely identifying this format for RPC calls.
 
693
 
 
694
        Bzr control formats use this disk format string to identify the format
 
695
        over the wire. Its possible that other control formats have more
 
696
        complex detection requirements, so we permit them to use any unique and
 
697
        immutable string they desire.
 
698
        """
 
699
        raise NotImplementedError(self.network_name)
 
700
 
 
701
    def open(self, transport, _found=False):
 
702
        """Return an instance of this format for the dir transport points at.
 
703
        """
 
704
        raise NotImplementedError(self.open)
 
705
 
 
706
    @classmethod
 
707
    def _set_default_format(klass, format):
 
708
        """Set default format (for testing behavior of defaults only)"""
 
709
        klass._default_format = format
 
710
 
 
711
    @classmethod
 
712
    def get_default_format(klass):
 
713
        """Return the current default format."""
 
714
        return klass._default_format
 
715
 
 
716
 
 
717
class Prober(object):
 
718
    """Abstract class that can be used to detect a particular kind of 
 
719
    control directory.
 
720
 
 
721
    At the moment this just contains a single method to probe a particular 
 
722
    transport, but it may be extended in the future to e.g. avoid 
 
723
    multiple levels of probing for Subversion repositories.
 
724
    """
 
725
 
 
726
    def probe_transport(self, transport):
 
727
        """Return the controldir style format present in a directory.
 
728
 
 
729
        :raise UnknownFormatError: If a control dir was found but is
 
730
            in an unknown format.
 
731
        :raise NotBranchError: If no control directory was found.
 
732
        :return: A ControlDirFormat instance.
 
733
        """
 
734
        raise NotImplementedError(self.probe_transport)
 
735
 
 
736
 
 
737
class BzrDirFormatInfo(object):
 
738
 
 
739
    def __init__(self, native, deprecated, hidden, experimental):
 
740
        self.deprecated = deprecated
 
741
        self.native = native
 
742
        self.hidden = hidden
 
743
        self.experimental = experimental
 
744
 
 
745
 
 
746
class ControlDirFormatRegistry(registry.Registry):
 
747
    """Registry of user-selectable BzrDir subformats.
 
748
 
 
749
    Differs from ControlDirFormat._formats in that it provides sub-formats,
 
750
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
751
    """
 
752
 
 
753
    def __init__(self):
 
754
        """Create a BzrDirFormatRegistry."""
 
755
        self._aliases = set()
 
756
        self._registration_order = list()
 
757
        super(ControlDirFormatRegistry, self).__init__()
 
758
 
 
759
    def aliases(self):
 
760
        """Return a set of the format names which are aliases."""
 
761
        return frozenset(self._aliases)
 
762
 
 
763
    def register(self, key, factory, help, native=True, deprecated=False,
 
764
                 hidden=False, experimental=False, alias=False):
 
765
        """Register a BzrDirFormat factory.
 
766
 
 
767
        The factory must be a callable that takes one parameter: the key.
 
768
        It must produce an instance of the BzrDirFormat when called.
 
769
 
 
770
        This function mainly exists to prevent the info object from being
 
771
        supplied directly.
 
772
        """
 
773
        registry.Registry.register(self, key, factory, help,
 
774
            BzrDirFormatInfo(native, deprecated, hidden, experimental))
 
775
        if alias:
 
776
            self._aliases.add(key)
 
777
        self._registration_order.append(key)
 
778
 
 
779
    def register_lazy(self, key, module_name, member_name, help, native=True,
 
780
        deprecated=False, hidden=False, experimental=False, alias=False):
 
781
        registry.Registry.register_lazy(self, key, module_name, member_name,
 
782
            help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
 
783
        if alias:
 
784
            self._aliases.add(key)
 
785
        self._registration_order.append(key)
 
786
 
 
787
    def set_default(self, key):
 
788
        """Set the 'default' key to be a clone of the supplied key.
 
789
 
 
790
        This method must be called once and only once.
 
791
        """
 
792
        registry.Registry.register(self, 'default', self.get(key),
 
793
            self.get_help(key), info=self.get_info(key))
 
794
        self._aliases.add('default')
 
795
 
 
796
    def set_default_repository(self, key):
 
797
        """Set the FormatRegistry default and Repository default.
 
798
 
 
799
        This is a transitional method while Repository.set_default_format
 
800
        is deprecated.
 
801
        """
 
802
        if 'default' in self:
 
803
            self.remove('default')
 
804
        self.set_default(key)
 
805
        format = self.get('default')()
 
806
 
 
807
    def make_bzrdir(self, key):
 
808
        return self.get(key)()
 
809
 
 
810
    def help_topic(self, topic):
 
811
        output = ""
 
812
        default_realkey = None
 
813
        default_help = self.get_help('default')
 
814
        help_pairs = []
 
815
        for key in self._registration_order:
 
816
            if key == 'default':
 
817
                continue
 
818
            help = self.get_help(key)
 
819
            if help == default_help:
 
820
                default_realkey = key
 
821
            else:
 
822
                help_pairs.append((key, help))
 
823
 
 
824
        def wrapped(key, help, info):
 
825
            if info.native:
 
826
                help = '(native) ' + help
 
827
            return ':%s:\n%s\n\n' % (key,
 
828
                textwrap.fill(help, initial_indent='    ',
 
829
                    subsequent_indent='    ',
 
830
                    break_long_words=False))
 
831
        if default_realkey is not None:
 
832
            output += wrapped(default_realkey, '(default) %s' % default_help,
 
833
                              self.get_info('default'))
 
834
        deprecated_pairs = []
 
835
        experimental_pairs = []
 
836
        for key, help in help_pairs:
 
837
            info = self.get_info(key)
 
838
            if info.hidden:
 
839
                continue
 
840
            elif info.deprecated:
 
841
                deprecated_pairs.append((key, help))
 
842
            elif info.experimental:
 
843
                experimental_pairs.append((key, help))
 
844
            else:
 
845
                output += wrapped(key, help, info)
 
846
        output += "\nSee :doc:`formats-help` for more about storage formats."
 
847
        other_output = ""
 
848
        if len(experimental_pairs) > 0:
 
849
            other_output += "Experimental formats are shown below.\n\n"
 
850
            for key, help in experimental_pairs:
 
851
                info = self.get_info(key)
 
852
                other_output += wrapped(key, help, info)
 
853
        else:
 
854
            other_output += \
 
855
                "No experimental formats are available.\n\n"
 
856
        if len(deprecated_pairs) > 0:
 
857
            other_output += "\nDeprecated formats are shown below.\n\n"
 
858
            for key, help in deprecated_pairs:
 
859
                info = self.get_info(key)
 
860
                other_output += wrapped(key, help, info)
 
861
        else:
 
862
            other_output += \
 
863
                "\nNo deprecated formats are available.\n\n"
 
864
        other_output += \
 
865
                "\nSee :doc:`formats-help` for more about storage formats."
 
866
 
 
867
        if topic == 'other-formats':
 
868
            return other_output
 
869
        else:
 
870
            return output
 
871
 
 
872
 
 
873
# Please register new formats after old formats so that formats
 
874
# appear in chronological order and format descriptions can build
 
875
# on previous ones.
 
876
format_registry = ControlDirFormatRegistry()