/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-02 23:53:16 UTC
  • mto: This revision was merged to the branch mainline in revision 5389.
  • Revision ID: jelmer@samba.org-20100802235316-v6a6p0r2a1os0dsn
baseĀ ControlDirĀ onĀ ControlComponent.

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