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