~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

  • Committer: Jelmer Vernooij
  • Date: 2010-08-02 18:36:31 UTC
  • mto: This revision was merged to the branch mainline in revision 5389.
  • Revision ID: jelmer@samba.org-20100802183631-d0trc5r8parg4v63
Fix some tests.

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