/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 breezy/controldir.py

  • Committer: Jelmer Vernooij
  • Date: 2017-12-04 23:01:39 UTC
  • mto: This revision was merged to the branch mainline in revision 6839.
  • Revision ID: jelmer@jelmer.uk-20171204230139-1sc3c18ikwewdejm
Remove bytes_to_gzip; work with chunks instead.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2010, 2011, 2012 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 breezy.bzrdir.BzrDir.
 
22
 
 
23
"""
 
24
 
 
25
from __future__ import absolute_import
 
26
 
 
27
from .lazy_import import lazy_import
 
28
lazy_import(globals(), """
 
29
import textwrap
 
30
 
 
31
from breezy import (
 
32
    branch as _mod_branch,
 
33
    hooks,
 
34
    revision as _mod_revision,
 
35
    transport as _mod_transport,
 
36
    trace,
 
37
    ui,
 
38
    urlutils,
 
39
    )
 
40
from breezy.transport import local
 
41
from breezy.push import (
 
42
    PushResult,
 
43
    )
 
44
 
 
45
from breezy.i18n import gettext
 
46
""")
 
47
 
 
48
from . import (
 
49
    errors,
 
50
    registry,
 
51
    )
 
52
 
 
53
 
 
54
class MustHaveWorkingTree(errors.BzrError):
 
55
 
 
56
    _fmt = "Branching '%(url)s'(%(format)s) must create a working tree."
 
57
 
 
58
    def __init__(self, format, url):
 
59
        errors.BzrError.__init__(self, format=format, url=url)
 
60
 
 
61
 
 
62
class ControlComponent(object):
 
63
    """Abstract base class for control directory components.
 
64
 
 
65
    This provides interfaces that are common across controldirs,
 
66
    repositories, branches, and workingtree control directories.
 
67
 
 
68
    They all expose two urls and transports: the *user* URL is the
 
69
    one that stops above the control directory (eg .bzr) and that
 
70
    should normally be used in messages, and the *control* URL is
 
71
    under that in eg .bzr/checkout and is used to read the control
 
72
    files.
 
73
 
 
74
    This can be used as a mixin and is intended to fit with
 
75
    foreign formats.
 
76
    """
 
77
 
 
78
    @property
 
79
    def control_transport(self):
 
80
        raise NotImplementedError
 
81
 
 
82
    @property
 
83
    def control_url(self):
 
84
        return self.control_transport.base
 
85
 
 
86
    @property
 
87
    def user_transport(self):
 
88
        raise NotImplementedError
 
89
 
 
90
    @property
 
91
    def user_url(self):
 
92
        return self.user_transport.base
 
93
 
 
94
 
 
95
class ControlDir(ControlComponent):
 
96
    """A control directory.
 
97
 
 
98
    While this represents a generic control directory, there are a few
 
99
    features that are present in this interface that are currently only
 
100
    supported by one of its implementations, BzrDir.
 
101
 
 
102
    These features (bound branches, stacked branches) are currently only
 
103
    supported by Bazaar, but could be supported by other version control
 
104
    systems as well. Implementations are required to raise the appropriate
 
105
    exceptions when an operation is requested that is not supported.
 
106
 
 
107
    This also makes life easier for API users who can rely on the
 
108
    implementation always allowing a particular feature to be requested but
 
109
    raising an exception when it is not supported, rather than requiring the
 
110
    API users to check for magic attributes to see what features are supported.
 
111
    """
 
112
 
 
113
    def can_convert_format(self):
 
114
        """Return true if this controldir is one whose format we can convert
 
115
        from."""
 
116
        return True
 
117
 
 
118
    def list_branches(self):
 
119
        """Return a sequence of all branches local to this control directory.
 
120
 
 
121
        """
 
122
        return list(self.get_branches().values())
 
123
 
 
124
    def get_branches(self):
 
125
        """Get all branches in this control directory, as a dictionary.
 
126
        
 
127
        :return: Dictionary mapping branch names to instances.
 
128
        """
 
129
        try:
 
130
           return { "": self.open_branch() }
 
131
        except (errors.NotBranchError, errors.NoRepositoryPresent):
 
132
           return {}
 
133
 
 
134
    def is_control_filename(self, filename):
 
135
        """True if filename is the name of a path which is reserved for
 
136
        controldirs.
 
137
 
 
138
        :param filename: A filename within the root transport of this
 
139
            controldir.
 
140
 
 
141
        This is true IF and ONLY IF the filename is part of the namespace reserved
 
142
        for bzr control dirs. Currently this is the '.bzr' directory in the root
 
143
        of the root_transport. it is expected that plugins will need to extend
 
144
        this in the future - for instance to make bzr talk with svn working
 
145
        trees.
 
146
        """
 
147
        raise NotImplementedError(self.is_control_filename)
 
148
 
 
149
    def needs_format_conversion(self, format=None):
 
150
        """Return true if this controldir needs convert_format run on it.
 
151
 
 
152
        For instance, if the repository format is out of date but the
 
153
        branch and working tree are not, this should return True.
 
154
 
 
155
        :param format: Optional parameter indicating a specific desired
 
156
                       format we plan to arrive at.
 
157
        """
 
158
        raise NotImplementedError(self.needs_format_conversion)
 
159
 
 
160
    def create_repository(self, shared=False):
 
161
        """Create a new repository in this control directory.
 
162
 
 
163
        :param shared: If a shared repository should be created
 
164
        :return: The newly created repository
 
165
        """
 
166
        raise NotImplementedError(self.create_repository)
 
167
 
 
168
    def destroy_repository(self):
 
169
        """Destroy the repository in this ControlDir."""
 
170
        raise NotImplementedError(self.destroy_repository)
 
171
 
 
172
    def create_branch(self, name=None, repository=None,
 
173
                      append_revisions_only=None):
 
174
        """Create a branch in this ControlDir.
 
175
 
 
176
        :param name: Name of the colocated branch to create, None for
 
177
            the user selected branch or "" for the active branch.
 
178
        :param append_revisions_only: Whether this branch should only allow
 
179
            appending new revisions to its history.
 
180
 
 
181
        The controldirs format will control what branch format is created.
 
182
        For more control see BranchFormatXX.create(a_controldir).
 
183
        """
 
184
        raise NotImplementedError(self.create_branch)
 
185
 
 
186
    def destroy_branch(self, name=None):
 
187
        """Destroy a branch in this ControlDir.
 
188
 
 
189
        :param name: Name of the branch to destroy, None for the 
 
190
            user selected branch or "" for the active branch.
 
191
        :raise NotBranchError: When the branch does not exist
 
192
        """
 
193
        raise NotImplementedError(self.destroy_branch)
 
194
 
 
195
    def create_workingtree(self, revision_id=None, from_branch=None,
 
196
        accelerator_tree=None, hardlink=False):
 
197
        """Create a working tree at this ControlDir.
 
198
 
 
199
        :param revision_id: create it as of this revision id.
 
200
        :param from_branch: override controldir branch 
 
201
            (for lightweight checkouts)
 
202
        :param accelerator_tree: A tree which can be used for retrieving file
 
203
            contents more quickly than the revision tree, i.e. a workingtree.
 
204
            The revision tree will be used for cases where accelerator_tree's
 
205
            content is different.
 
206
        """
 
207
        raise NotImplementedError(self.create_workingtree)
 
208
 
 
209
    def destroy_workingtree(self):
 
210
        """Destroy the working tree at this ControlDir.
 
211
 
 
212
        Formats that do not support this may raise UnsupportedOperation.
 
213
        """
 
214
        raise NotImplementedError(self.destroy_workingtree)
 
215
 
 
216
    def destroy_workingtree_metadata(self):
 
217
        """Destroy the control files for the working tree at this ControlDir.
 
218
 
 
219
        The contents of working tree files are not affected.
 
220
        Formats that do not support this may raise UnsupportedOperation.
 
221
        """
 
222
        raise NotImplementedError(self.destroy_workingtree_metadata)
 
223
 
 
224
    def find_branch_format(self, name=None):
 
225
        """Find the branch 'format' for this controldir.
 
226
 
 
227
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
228
        """
 
229
        raise NotImplementedError(self.find_branch_format)
 
230
 
 
231
    def get_branch_reference(self, name=None):
 
232
        """Return the referenced URL for the branch in this controldir.
 
233
 
 
234
        :param name: Optional colocated branch name
 
235
        :raises NotBranchError: If there is no Branch.
 
236
        :raises NoColocatedBranchSupport: If a branch name was specified
 
237
            but colocated branches are not supported.
 
238
        :return: The URL the branch in this controldir references if it is a
 
239
            reference branch, or None for regular branches.
 
240
        """
 
241
        if name is not None:
 
242
            raise errors.NoColocatedBranchSupport(self)
 
243
        return None
 
244
 
 
245
    def set_branch_reference(self, target_branch, name=None):
 
246
        """Set the referenced URL for the branch in this controldir.
 
247
 
 
248
        :param name: Optional colocated branch name
 
249
        :param target_branch: Branch to reference
 
250
        :raises NoColocatedBranchSupport: If a branch name was specified
 
251
            but colocated branches are not supported.
 
252
        :return: The referencing branch
 
253
        """
 
254
        raise NotImplementedError(self.set_branch_reference)
 
255
 
 
256
    def open_branch(self, name=None, unsupported=False,
 
257
                    ignore_fallbacks=False, possible_transports=None):
 
258
        """Open the branch object at this ControlDir if one is present.
 
259
 
 
260
        :param unsupported: if True, then no longer supported branch formats can
 
261
            still be opened.
 
262
        :param ignore_fallbacks: Whether to open fallback repositories
 
263
        :param possible_transports: Transports to use for opening e.g.
 
264
            fallback repositories.
 
265
        """
 
266
        raise NotImplementedError(self.open_branch)
 
267
 
 
268
    def open_repository(self, _unsupported=False):
 
269
        """Open the repository object at this ControlDir if one is present.
 
270
 
 
271
        This will not follow the Branch object pointer - it's strictly a direct
 
272
        open facility. Most client code should use open_branch().repository to
 
273
        get at a repository.
 
274
 
 
275
        :param _unsupported: a private parameter, not part of the api.
 
276
        """
 
277
        raise NotImplementedError(self.open_repository)
 
278
 
 
279
    def find_repository(self):
 
280
        """Find the repository that should be used.
 
281
 
 
282
        This does not require a branch as we use it to find the repo for
 
283
        new branches as well as to hook existing branches up to their
 
284
        repository.
 
285
        """
 
286
        raise NotImplementedError(self.find_repository)
 
287
 
 
288
    def open_workingtree(self, unsupported=False,
 
289
                         recommend_upgrade=True, from_branch=None):
 
290
        """Open the workingtree object at this ControlDir if one is present.
 
291
 
 
292
        :param recommend_upgrade: Optional keyword parameter, when True (the
 
293
            default), emit through the ui module a recommendation that the user
 
294
            upgrade the working tree when the workingtree being opened is old
 
295
            (but still fully supported).
 
296
        :param from_branch: override controldir branch (for lightweight
 
297
            checkouts)
 
298
        """
 
299
        raise NotImplementedError(self.open_workingtree)
 
300
 
 
301
    def has_branch(self, name=None):
 
302
        """Tell if this controldir contains a branch.
 
303
 
 
304
        Note: if you're going to open the branch, you should just go ahead
 
305
        and try, and not ask permission first.  (This method just opens the
 
306
        branch and discards it, and that's somewhat expensive.)
 
307
        """
 
308
        try:
 
309
            self.open_branch(name, ignore_fallbacks=True)
 
310
            return True
 
311
        except errors.NotBranchError:
 
312
            return False
 
313
 
 
314
    def _get_selected_branch(self):
 
315
        """Return the name of the branch selected by the user.
 
316
 
 
317
        :return: Name of the branch selected by the user, or "".
 
318
        """
 
319
        branch = self.root_transport.get_segment_parameters().get("branch")
 
320
        if branch is None:
 
321
            branch = ""
 
322
        return urlutils.unescape(branch)
 
323
 
 
324
    def has_workingtree(self):
 
325
        """Tell if this controldir contains a working tree.
 
326
 
 
327
        This will still raise an exception if the controldir has a workingtree
 
328
        that is remote & inaccessible.
 
329
 
 
330
        Note: if you're going to open the working tree, you should just go ahead
 
331
        and try, and not ask permission first.  (This method just opens the
 
332
        workingtree and discards it, and that's somewhat expensive.)
 
333
        """
 
334
        try:
 
335
            self.open_workingtree(recommend_upgrade=False)
 
336
            return True
 
337
        except errors.NoWorkingTree:
 
338
            return False
 
339
 
 
340
    def cloning_metadir(self, require_stacking=False):
 
341
        """Produce a metadir suitable for cloning or sprouting with.
 
342
 
 
343
        These operations may produce workingtrees (yes, even though they're
 
344
        "cloning" something that doesn't have a tree), so a viable workingtree
 
345
        format must be selected.
 
346
 
 
347
        :require_stacking: If True, non-stackable formats will be upgraded
 
348
            to similar stackable formats.
 
349
        :returns: a ControlDirFormat with all component formats either set
 
350
            appropriately or set to None if that component should not be
 
351
            created.
 
352
        """
 
353
        raise NotImplementedError(self.cloning_metadir)
 
354
 
 
355
    def checkout_metadir(self):
 
356
        """Produce a metadir suitable for checkouts of this controldir.
 
357
 
 
358
        :returns: A ControlDirFormat with all component formats
 
359
            either set appropriately or set to None if that component
 
360
            should not be created.
 
361
        """
 
362
        return self.cloning_metadir()
 
363
 
 
364
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
365
               recurse='down', possible_transports=None,
 
366
               accelerator_tree=None, hardlink=False, stacked=False,
 
367
               source_branch=None, create_tree_if_local=True):
 
368
        """Create a copy of this controldir prepared for use as a new line of
 
369
        development.
 
370
 
 
371
        If url's last component does not exist, it will be created.
 
372
 
 
373
        Attributes related to the identity of the source branch like
 
374
        branch nickname will be cleaned, a working tree is created
 
375
        whether one existed before or not; and a local branch is always
 
376
        created.
 
377
 
 
378
        :param revision_id: if revision_id is not None, then the clone
 
379
            operation may tune itself to download less data.
 
380
        :param accelerator_tree: A tree which can be used for retrieving file
 
381
            contents more quickly than the revision tree, i.e. a workingtree.
 
382
            The revision tree will be used for cases where accelerator_tree's
 
383
            content is different.
 
384
        :param hardlink: If true, hard-link files from accelerator_tree,
 
385
            where possible.
 
386
        :param stacked: If true, create a stacked branch referring to the
 
387
            location of this control directory.
 
388
        :param create_tree_if_local: If true, a working-tree will be created
 
389
            when working locally.
 
390
        """
 
391
        raise NotImplementedError(self.sprout)
 
392
 
 
393
    def push_branch(self, source, revision_id=None, overwrite=False, 
 
394
        remember=False, create_prefix=False):
 
395
        """Push the source branch into this ControlDir."""
 
396
        br_to = None
 
397
        # If we can open a branch, use its direct repository, otherwise see
 
398
        # if there is a repository without a branch.
 
399
        try:
 
400
            br_to = self.open_branch()
 
401
        except errors.NotBranchError:
 
402
            # Didn't find a branch, can we find a repository?
 
403
            repository_to = self.find_repository()
 
404
        else:
 
405
            # Found a branch, so we must have found a repository
 
406
            repository_to = br_to.repository
 
407
 
 
408
        push_result = PushResult()
 
409
        push_result.source_branch = source
 
410
        if br_to is None:
 
411
            # We have a repository but no branch, copy the revisions, and then
 
412
            # create a branch.
 
413
            if revision_id is None:
 
414
                # No revision supplied by the user, default to the branch
 
415
                # revision
 
416
                revision_id = source.last_revision()
 
417
            repository_to.fetch(source.repository, revision_id=revision_id)
 
418
            br_to = source.clone(self, revision_id=revision_id)
 
419
            if source.get_push_location() is None or remember:
 
420
                # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
 
421
                source.set_push_location(br_to.base)
 
422
            push_result.stacked_on = None
 
423
            push_result.branch_push_result = None
 
424
            push_result.old_revno = None
 
425
            push_result.old_revid = _mod_revision.NULL_REVISION
 
426
            push_result.target_branch = br_to
 
427
            push_result.master_branch = None
 
428
            push_result.workingtree_updated = False
 
429
        else:
 
430
            # We have successfully opened the branch, remember if necessary:
 
431
            if source.get_push_location() is None or remember:
 
432
                # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
 
433
                source.set_push_location(br_to.base)
 
434
            try:
 
435
                tree_to = self.open_workingtree()
 
436
            except errors.NotLocalUrl:
 
437
                push_result.branch_push_result = source.push(br_to, 
 
438
                    overwrite, stop_revision=revision_id)
 
439
                push_result.workingtree_updated = False
 
440
            except errors.NoWorkingTree:
 
441
                push_result.branch_push_result = source.push(br_to,
 
442
                    overwrite, stop_revision=revision_id)
 
443
                push_result.workingtree_updated = None # Not applicable
 
444
            else:
 
445
                tree_to.lock_write()
 
446
                try:
 
447
                    push_result.branch_push_result = source.push(
 
448
                        tree_to.branch, overwrite, stop_revision=revision_id)
 
449
                    tree_to.update()
 
450
                finally:
 
451
                    tree_to.unlock()
 
452
                push_result.workingtree_updated = True
 
453
            push_result.old_revno = push_result.branch_push_result.old_revno
 
454
            push_result.old_revid = push_result.branch_push_result.old_revid
 
455
            push_result.target_branch = \
 
456
                push_result.branch_push_result.target_branch
 
457
        return push_result
 
458
 
 
459
    def _get_tree_branch(self, name=None):
 
460
        """Return the branch and tree, if any, for this controldir.
 
461
 
 
462
        :param name: Name of colocated branch to open.
 
463
 
 
464
        Return None for tree if not present or inaccessible.
 
465
        Raise NotBranchError if no branch is present.
 
466
        :return: (tree, branch)
 
467
        """
 
468
        try:
 
469
            tree = self.open_workingtree()
 
470
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
471
            tree = None
 
472
            branch = self.open_branch(name=name)
 
473
        else:
 
474
            if name is not None:
 
475
                branch = self.open_branch(name=name)
 
476
            else:
 
477
                branch = tree.branch
 
478
        return tree, branch
 
479
 
 
480
    def get_config(self):
 
481
        """Get configuration for this ControlDir."""
 
482
        raise NotImplementedError(self.get_config)
 
483
 
 
484
    def check_conversion_target(self, target_format):
 
485
        """Check that a controldir as a whole can be converted to a new format."""
 
486
        raise NotImplementedError(self.check_conversion_target)
 
487
 
 
488
    def clone(self, url, revision_id=None, force_new_repo=False,
 
489
              preserve_stacking=False):
 
490
        """Clone this controldir and its contents to url verbatim.
 
491
 
 
492
        :param url: The url create the clone at.  If url's last component does
 
493
            not exist, it will be created.
 
494
        :param revision_id: The tip revision-id to use for any branch or
 
495
            working tree.  If not None, then the clone operation may tune
 
496
            itself to download less data.
 
497
        :param force_new_repo: Do not use a shared repository for the target
 
498
                               even if one is available.
 
499
        :param preserve_stacking: When cloning a stacked branch, stack the
 
500
            new branch on top of the other branch's stacked-on branch.
 
501
        """
 
502
        return self.clone_on_transport(_mod_transport.get_transport(url),
 
503
                                       revision_id=revision_id,
 
504
                                       force_new_repo=force_new_repo,
 
505
                                       preserve_stacking=preserve_stacking)
 
506
 
 
507
    def clone_on_transport(self, transport, revision_id=None,
 
508
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
 
509
        create_prefix=False, use_existing_dir=True, no_tree=False):
 
510
        """Clone this controldir and its contents to transport verbatim.
 
511
 
 
512
        :param transport: The transport for the location to produce the clone
 
513
            at.  If the target directory does not exist, it will be created.
 
514
        :param revision_id: The tip revision-id to use for any branch or
 
515
            working tree.  If not None, then the clone operation may tune
 
516
            itself to download less data.
 
517
        :param force_new_repo: Do not use a shared repository for the target,
 
518
                               even if one is available.
 
519
        :param preserve_stacking: When cloning a stacked branch, stack the
 
520
            new branch on top of the other branch's stacked-on branch.
 
521
        :param create_prefix: Create any missing directories leading up to
 
522
            to_transport.
 
523
        :param use_existing_dir: Use an existing directory if one exists.
 
524
        :param no_tree: If set to true prevents creation of a working tree.
 
525
        """
 
526
        raise NotImplementedError(self.clone_on_transport)
 
527
 
 
528
    @classmethod
 
529
    def find_controldirs(klass, transport, evaluate=None, list_current=None):
 
530
        """Find control dirs recursively from current location.
 
531
 
 
532
        This is intended primarily as a building block for more sophisticated
 
533
        functionality, like finding trees under a directory, or finding
 
534
        branches that use a given repository.
 
535
 
 
536
        :param evaluate: An optional callable that yields recurse, value,
 
537
            where recurse controls whether this controldir is recursed into
 
538
            and value is the value to yield.  By default, all bzrdirs
 
539
            are recursed into, and the return value is the controldir.
 
540
        :param list_current: if supplied, use this function to list the current
 
541
            directory, instead of Transport.list_dir
 
542
        :return: a generator of found bzrdirs, or whatever evaluate returns.
 
543
        """
 
544
        if list_current is None:
 
545
            def list_current(transport):
 
546
                return transport.list_dir('')
 
547
        if evaluate is None:
 
548
            def evaluate(controldir):
 
549
                return True, controldir
 
550
 
 
551
        pending = [transport]
 
552
        while len(pending) > 0:
 
553
            current_transport = pending.pop()
 
554
            recurse = True
 
555
            try:
 
556
                controldir = klass.open_from_transport(current_transport)
 
557
            except (errors.NotBranchError, errors.PermissionDenied):
 
558
                pass
 
559
            else:
 
560
                recurse, value = evaluate(controldir)
 
561
                yield value
 
562
            try:
 
563
                subdirs = list_current(current_transport)
 
564
            except (errors.NoSuchFile, errors.PermissionDenied):
 
565
                continue
 
566
            if recurse:
 
567
                for subdir in sorted(subdirs, reverse=True):
 
568
                    pending.append(current_transport.clone(subdir))
 
569
 
 
570
    @classmethod
 
571
    def find_branches(klass, transport):
 
572
        """Find all branches under a transport.
 
573
 
 
574
        This will find all branches below the transport, including branches
 
575
        inside other branches.  Where possible, it will use
 
576
        Repository.find_branches.
 
577
 
 
578
        To list all the branches that use a particular Repository, see
 
579
        Repository.find_branches
 
580
        """
 
581
        def evaluate(controldir):
 
582
            try:
 
583
                repository = controldir.open_repository()
 
584
            except errors.NoRepositoryPresent:
 
585
                pass
 
586
            else:
 
587
                return False, ([], repository)
 
588
            return True, (controldir.list_branches(), None)
 
589
        ret = []
 
590
        for branches, repo in klass.find_controldirs(
 
591
                transport, evaluate=evaluate):
 
592
            if repo is not None:
 
593
                ret.extend(repo.find_branches())
 
594
            if branches is not None:
 
595
                ret.extend(branches)
 
596
        return ret
 
597
 
 
598
    @classmethod
 
599
    def create_branch_and_repo(klass, base, force_new_repo=False, format=None):
 
600
        """Create a new ControlDir, Branch and Repository at the url 'base'.
 
601
 
 
602
        This will use the current default ControlDirFormat unless one is
 
603
        specified, and use whatever
 
604
        repository format that that uses via controldir.create_branch and
 
605
        create_repository. If a shared repository is available that is used
 
606
        preferentially.
 
607
 
 
608
        The created Branch object is returned.
 
609
 
 
610
        :param base: The URL to create the branch at.
 
611
        :param force_new_repo: If True a new repository is always created.
 
612
        :param format: If supplied, the format of branch to create.  If not
 
613
            supplied, the default is used.
 
614
        """
 
615
        controldir = klass.create(base, format)
 
616
        controldir._find_or_create_repository(force_new_repo)
 
617
        return controldir.create_branch()
 
618
 
 
619
    @classmethod
 
620
    def create_branch_convenience(klass, base, force_new_repo=False,
 
621
                                  force_new_tree=None, format=None,
 
622
                                  possible_transports=None):
 
623
        """Create a new ControlDir, Branch and Repository at the url 'base'.
 
624
 
 
625
        This is a convenience function - it will use an existing repository
 
626
        if possible, can be told explicitly whether to create a working tree or
 
627
        not.
 
628
 
 
629
        This will use the current default ControlDirFormat unless one is
 
630
        specified, and use whatever
 
631
        repository format that that uses via ControlDir.create_branch and
 
632
        create_repository. If a shared repository is available that is used
 
633
        preferentially. Whatever repository is used, its tree creation policy
 
634
        is followed.
 
635
 
 
636
        The created Branch object is returned.
 
637
        If a working tree cannot be made due to base not being a file:// url,
 
638
        no error is raised unless force_new_tree is True, in which case no
 
639
        data is created on disk and NotLocalUrl is raised.
 
640
 
 
641
        :param base: The URL to create the branch at.
 
642
        :param force_new_repo: If True a new repository is always created.
 
643
        :param force_new_tree: If True or False force creation of a tree or
 
644
                               prevent such creation respectively.
 
645
        :param format: Override for the controldir format to create.
 
646
        :param possible_transports: An optional reusable transports list.
 
647
        """
 
648
        if force_new_tree:
 
649
            # check for non local urls
 
650
            t = _mod_transport.get_transport(base, possible_transports)
 
651
            if not isinstance(t, local.LocalTransport):
 
652
                raise errors.NotLocalUrl(base)
 
653
        controldir = klass.create(base, format, possible_transports)
 
654
        repo = controldir._find_or_create_repository(force_new_repo)
 
655
        result = controldir.create_branch()
 
656
        if force_new_tree or (repo.make_working_trees() and
 
657
                              force_new_tree is None):
 
658
            try:
 
659
                controldir.create_workingtree()
 
660
            except errors.NotLocalUrl:
 
661
                pass
 
662
        return result
 
663
 
 
664
    @classmethod
 
665
    def create_standalone_workingtree(klass, base, format=None):
 
666
        """Create a new ControlDir, WorkingTree, Branch and Repository at 'base'.
 
667
 
 
668
        'base' must be a local path or a file:// url.
 
669
 
 
670
        This will use the current default ControlDirFormat unless one is
 
671
        specified, and use whatever
 
672
        repository format that that uses for bzrdirformat.create_workingtree,
 
673
        create_branch and create_repository.
 
674
 
 
675
        :param format: Override for the controldir format to create.
 
676
        :return: The WorkingTree object.
 
677
        """
 
678
        t = _mod_transport.get_transport(base)
 
679
        if not isinstance(t, local.LocalTransport):
 
680
            raise errors.NotLocalUrl(base)
 
681
        controldir = klass.create_branch_and_repo(base,
 
682
                                               force_new_repo=True,
 
683
                                               format=format).controldir
 
684
        return controldir.create_workingtree()
 
685
 
 
686
    @classmethod
 
687
    def open_unsupported(klass, base):
 
688
        """Open a branch which is not supported."""
 
689
        return klass.open(base, _unsupported=True)
 
690
 
 
691
    @classmethod
 
692
    def open(klass, base, possible_transports=None, probers=None,
 
693
             _unsupported=False):
 
694
        """Open an existing controldir, rooted at 'base' (url).
 
695
 
 
696
        :param _unsupported: a private parameter to the ControlDir class.
 
697
        """
 
698
        t = _mod_transport.get_transport(base, possible_transports)
 
699
        return klass.open_from_transport(t, probers=probers,
 
700
                _unsupported=_unsupported)
 
701
 
 
702
    @classmethod
 
703
    def open_from_transport(klass, transport, _unsupported=False,
 
704
                            probers=None):
 
705
        """Open a controldir within a particular directory.
 
706
 
 
707
        :param transport: Transport containing the controldir.
 
708
        :param _unsupported: private.
 
709
        """
 
710
        for hook in klass.hooks['pre_open']:
 
711
            hook(transport)
 
712
        # Keep initial base since 'transport' may be modified while following
 
713
        # the redirections.
 
714
        base = transport.base
 
715
        def find_format(transport):
 
716
            return transport, ControlDirFormat.find_format(transport,
 
717
                probers=probers)
 
718
 
 
719
        def redirected(transport, e, redirection_notice):
 
720
            redirected_transport = transport._redirected_to(e.source, e.target)
 
721
            if redirected_transport is None:
 
722
                raise errors.NotBranchError(base)
 
723
            trace.note(gettext('{0} is{1} redirected to {2}').format(
 
724
                 transport.base, e.permanently, redirected_transport.base))
 
725
            return redirected_transport
 
726
 
 
727
        try:
 
728
            transport, format = _mod_transport.do_catching_redirections(
 
729
                find_format, transport, redirected)
 
730
        except errors.TooManyRedirections:
 
731
            raise errors.NotBranchError(base)
 
732
 
 
733
        format.check_support_status(_unsupported)
 
734
        return format.open(transport, _found=True)
 
735
 
 
736
    @classmethod
 
737
    def open_containing(klass, url, possible_transports=None):
 
738
        """Open an existing branch which contains url.
 
739
 
 
740
        :param url: url to search from.
 
741
 
 
742
        See open_containing_from_transport for more detail.
 
743
        """
 
744
        transport = _mod_transport.get_transport(url, possible_transports)
 
745
        return klass.open_containing_from_transport(transport)
 
746
 
 
747
    @classmethod
 
748
    def open_containing_from_transport(klass, a_transport):
 
749
        """Open an existing branch which contains a_transport.base.
 
750
 
 
751
        This probes for a branch at a_transport, and searches upwards from there.
 
752
 
 
753
        Basically we keep looking up until we find the control directory or
 
754
        run into the root.  If there isn't one, raises NotBranchError.
 
755
        If there is one and it is either an unrecognised format or an unsupported
 
756
        format, UnknownFormatError or UnsupportedFormatError are raised.
 
757
        If there is one, it is returned, along with the unused portion of url.
 
758
 
 
759
        :return: The ControlDir that contains the path, and a Unicode path
 
760
                for the rest of the URL.
 
761
        """
 
762
        # this gets the normalised url back. I.e. '.' -> the full path.
 
763
        url = a_transport.base
 
764
        while True:
 
765
            try:
 
766
                result = klass.open_from_transport(a_transport)
 
767
                return result, urlutils.unescape(a_transport.relpath(url))
 
768
            except errors.NotBranchError as e:
 
769
                pass
 
770
            except errors.PermissionDenied:
 
771
                pass
 
772
            try:
 
773
                new_t = a_transport.clone('..')
 
774
            except urlutils.InvalidURLJoin:
 
775
                # reached the root, whatever that may be
 
776
                raise errors.NotBranchError(path=url)
 
777
            if new_t.base == a_transport.base:
 
778
                # reached the root, whatever that may be
 
779
                raise errors.NotBranchError(path=url)
 
780
            a_transport = new_t
 
781
 
 
782
    @classmethod
 
783
    def open_tree_or_branch(klass, location):
 
784
        """Return the branch and working tree at a location.
 
785
 
 
786
        If there is no tree at the location, tree will be None.
 
787
        If there is no branch at the location, an exception will be
 
788
        raised
 
789
        :return: (tree, branch)
 
790
        """
 
791
        controldir = klass.open(location)
 
792
        return controldir._get_tree_branch()
 
793
 
 
794
    @classmethod
 
795
    def open_containing_tree_or_branch(klass, location,
 
796
            possible_transports=None):
 
797
        """Return the branch and working tree contained by a location.
 
798
 
 
799
        Returns (tree, branch, relpath).
 
800
        If there is no tree at containing the location, tree will be None.
 
801
        If there is no branch containing the location, an exception will be
 
802
        raised
 
803
        relpath is the portion of the path that is contained by the branch.
 
804
        """
 
805
        controldir, relpath = klass.open_containing(location,
 
806
            possible_transports=possible_transports)
 
807
        tree, branch = controldir._get_tree_branch()
 
808
        return tree, branch, relpath
 
809
 
 
810
    @classmethod
 
811
    def open_containing_tree_branch_or_repository(klass, location):
 
812
        """Return the working tree, branch and repo contained by a location.
 
813
 
 
814
        Returns (tree, branch, repository, relpath).
 
815
        If there is no tree containing the location, tree will be None.
 
816
        If there is no branch containing the location, branch will be None.
 
817
        If there is no repository containing the location, repository will be
 
818
        None.
 
819
        relpath is the portion of the path that is contained by the innermost
 
820
        ControlDir.
 
821
 
 
822
        If no tree, branch or repository is found, a NotBranchError is raised.
 
823
        """
 
824
        controldir, relpath = klass.open_containing(location)
 
825
        try:
 
826
            tree, branch = controldir._get_tree_branch()
 
827
        except errors.NotBranchError:
 
828
            try:
 
829
                repo = controldir.find_repository()
 
830
                return None, None, repo, relpath
 
831
            except (errors.NoRepositoryPresent):
 
832
                raise errors.NotBranchError(location)
 
833
        return tree, branch, branch.repository, relpath
 
834
 
 
835
    @classmethod
 
836
    def create(klass, base, format=None, possible_transports=None):
 
837
        """Create a new ControlDir at the url 'base'.
 
838
 
 
839
        :param format: If supplied, the format of branch to create.  If not
 
840
            supplied, the default is used.
 
841
        :param possible_transports: If supplied, a list of transports that
 
842
            can be reused to share a remote connection.
 
843
        """
 
844
        if klass is not ControlDir:
 
845
            raise AssertionError("ControlDir.create always creates the"
 
846
                "default format, not one of %r" % klass)
 
847
        t = _mod_transport.get_transport(base, possible_transports)
 
848
        t.ensure_base()
 
849
        if format is None:
 
850
            format = ControlDirFormat.get_default_format()
 
851
        return format.initialize_on_transport(t)
 
852
 
 
853
 
 
854
class ControlDirHooks(hooks.Hooks):
 
855
    """Hooks for ControlDir operations."""
 
856
 
 
857
    def __init__(self):
 
858
        """Create the default hooks."""
 
859
        hooks.Hooks.__init__(self, "breezy.controldir", "ControlDir.hooks")
 
860
        self.add_hook('pre_open',
 
861
            "Invoked before attempting to open a ControlDir with the transport "
 
862
            "that the open will use.", (1, 14))
 
863
        self.add_hook('post_repo_init',
 
864
            "Invoked after a repository has been initialized. "
 
865
            "post_repo_init is called with a "
 
866
            "breezy.controldir.RepoInitHookParams.",
 
867
            (2, 2))
 
868
 
 
869
# install the default hooks
 
870
ControlDir.hooks = ControlDirHooks()
 
871
 
 
872
 
 
873
class ControlComponentFormat(object):
 
874
    """A component that can live inside of a control directory."""
 
875
 
 
876
    upgrade_recommended = False
 
877
 
 
878
    def get_format_description(self):
 
879
        """Return the short description for this format."""
 
880
        raise NotImplementedError(self.get_format_description)
 
881
 
 
882
    def is_supported(self):
 
883
        """Is this format supported?
 
884
 
 
885
        Supported formats must be initializable and openable.
 
886
        Unsupported formats may not support initialization or committing or
 
887
        some other features depending on the reason for not being supported.
 
888
        """
 
889
        return True
 
890
 
 
891
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
892
        basedir=None):
 
893
        """Give an error or warning on old formats.
 
894
 
 
895
        :param allow_unsupported: If true, allow opening
 
896
            formats that are strongly deprecated, and which may
 
897
            have limited functionality.
 
898
 
 
899
        :param recommend_upgrade: If true (default), warn
 
900
            the user through the ui object that they may wish
 
901
            to upgrade the object.
 
902
        """
 
903
        if not allow_unsupported and not self.is_supported():
 
904
            # see open_downlevel to open legacy branches.
 
905
            raise errors.UnsupportedFormatError(format=self)
 
906
        if recommend_upgrade and self.upgrade_recommended:
 
907
            ui.ui_factory.recommend_upgrade(
 
908
                self.get_format_description(), basedir)
 
909
 
 
910
    @classmethod
 
911
    def get_format_string(cls):
 
912
        raise NotImplementedError(cls.get_format_string)
 
913
 
 
914
 
 
915
class ControlComponentFormatRegistry(registry.FormatRegistry):
 
916
    """A registry for control components (branch, workingtree, repository)."""
 
917
 
 
918
    def __init__(self, other_registry=None):
 
919
        super(ControlComponentFormatRegistry, self).__init__(other_registry)
 
920
        self._extra_formats = []
 
921
 
 
922
    def register(self, format):
 
923
        """Register a new format."""
 
924
        super(ControlComponentFormatRegistry, self).register(
 
925
            format.get_format_string(), format)
 
926
 
 
927
    def remove(self, format):
 
928
        """Remove a registered format."""
 
929
        super(ControlComponentFormatRegistry, self).remove(
 
930
            format.get_format_string())
 
931
 
 
932
    def register_extra(self, format):
 
933
        """Register a format that can not be used in a metadir.
 
934
 
 
935
        This is mainly useful to allow custom repository formats, such as older
 
936
        Bazaar formats and foreign formats, to be tested.
 
937
        """
 
938
        self._extra_formats.append(registry._ObjectGetter(format))
 
939
 
 
940
    def remove_extra(self, format):
 
941
        """Remove an extra format.
 
942
        """
 
943
        self._extra_formats.remove(registry._ObjectGetter(format))
 
944
 
 
945
    def register_extra_lazy(self, module_name, member_name):
 
946
        """Register a format lazily.
 
947
        """
 
948
        self._extra_formats.append(
 
949
            registry._LazyObjectGetter(module_name, member_name))
 
950
 
 
951
    def _get_extra(self):
 
952
        """Return all "extra" formats, not usable in meta directories."""
 
953
        result = []
 
954
        for getter in self._extra_formats:
 
955
            f = getter.get_obj()
 
956
            if callable(f):
 
957
                f = f()
 
958
            result.append(f)
 
959
        return result
 
960
 
 
961
    def _get_all(self):
 
962
        """Return all formats, even those not usable in metadirs.
 
963
        """
 
964
        result = []
 
965
        for name in self.keys():
 
966
            fmt = self.get(name)
 
967
            if callable(fmt):
 
968
                fmt = fmt()
 
969
            result.append(fmt)
 
970
        return result + self._get_extra()
 
971
 
 
972
    def _get_all_modules(self):
 
973
        """Return a set of the modules providing objects."""
 
974
        modules = set()
 
975
        for name in self.keys():
 
976
            modules.add(self._get_module(name))
 
977
        for getter in self._extra_formats:
 
978
            modules.add(getter.get_module())
 
979
        return modules
 
980
 
 
981
 
 
982
class Converter(object):
 
983
    """Converts a disk format object from one format to another."""
 
984
 
 
985
    def convert(self, to_convert, pb):
 
986
        """Perform the conversion of to_convert, giving feedback via pb.
 
987
 
 
988
        :param to_convert: The disk object to convert.
 
989
        :param pb: a progress bar to use for progress information.
 
990
        """
 
991
 
 
992
    def step(self, message):
 
993
        """Update the pb by a step."""
 
994
        self.count +=1
 
995
        self.pb.update(message, self.count, self.total)
 
996
 
 
997
 
 
998
class ControlDirFormat(object):
 
999
    """An encapsulation of the initialization and open routines for a format.
 
1000
 
 
1001
    Formats provide three things:
 
1002
     * An initialization routine,
 
1003
     * a format string,
 
1004
     * an open routine.
 
1005
 
 
1006
    Formats are placed in a dict by their format string for reference
 
1007
    during controldir opening. These should be subclasses of ControlDirFormat
 
1008
    for consistency.
 
1009
 
 
1010
    Once a format is deprecated, just deprecate the initialize and open
 
1011
    methods on the format class. Do not deprecate the object, as the
 
1012
    object will be created every system load.
 
1013
 
 
1014
    :cvar colocated_branches: Whether this formats supports colocated branches.
 
1015
    :cvar supports_workingtrees: This control directory can co-exist with a
 
1016
        working tree.
 
1017
    """
 
1018
 
 
1019
    _default_format = None
 
1020
    """The default format used for new control directories."""
 
1021
 
 
1022
    _server_probers = []
 
1023
    """The registered server format probers, e.g. RemoteBzrProber.
 
1024
 
 
1025
    This is a list of Prober-derived classes.
 
1026
    """
 
1027
 
 
1028
    _probers = []
 
1029
    """The registered format probers, e.g. BzrProber.
 
1030
 
 
1031
    This is a list of Prober-derived classes.
 
1032
    """
 
1033
 
 
1034
    colocated_branches = False
 
1035
    """Whether co-located branches are supported for this control dir format.
 
1036
    """
 
1037
 
 
1038
    supports_workingtrees = True
 
1039
    """Whether working trees can exist in control directories of this format.
 
1040
    """
 
1041
 
 
1042
    fixed_components = False
 
1043
    """Whether components can not change format independent of the control dir.
 
1044
    """
 
1045
 
 
1046
    upgrade_recommended = False
 
1047
    """Whether an upgrade from this format is recommended."""
 
1048
 
 
1049
    def get_format_description(self):
 
1050
        """Return the short description for this format."""
 
1051
        raise NotImplementedError(self.get_format_description)
 
1052
 
 
1053
    def get_converter(self, format=None):
 
1054
        """Return the converter to use to convert controldirs needing converts.
 
1055
 
 
1056
        This returns a breezy.controldir.Converter object.
 
1057
 
 
1058
        This should return the best upgrader to step this format towards the
 
1059
        current default format. In the case of plugins we can/should provide
 
1060
        some means for them to extend the range of returnable converters.
 
1061
 
 
1062
        :param format: Optional format to override the default format of the
 
1063
                       library.
 
1064
        """
 
1065
        raise NotImplementedError(self.get_converter)
 
1066
 
 
1067
    def is_supported(self):
 
1068
        """Is this format supported?
 
1069
 
 
1070
        Supported formats must be openable.
 
1071
        Unsupported formats may not support initialization or committing or
 
1072
        some other features depending on the reason for not being supported.
 
1073
        """
 
1074
        return True
 
1075
 
 
1076
    def is_initializable(self):
 
1077
        """Whether new control directories of this format can be initialized.
 
1078
        """
 
1079
        return self.is_supported()
 
1080
 
 
1081
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
1082
        basedir=None):
 
1083
        """Give an error or warning on old formats.
 
1084
 
 
1085
        :param allow_unsupported: If true, allow opening
 
1086
            formats that are strongly deprecated, and which may
 
1087
            have limited functionality.
 
1088
 
 
1089
        :param recommend_upgrade: If true (default), warn
 
1090
            the user through the ui object that they may wish
 
1091
            to upgrade the object.
 
1092
        """
 
1093
        if not allow_unsupported and not self.is_supported():
 
1094
            # see open_downlevel to open legacy branches.
 
1095
            raise errors.UnsupportedFormatError(format=self)
 
1096
        if recommend_upgrade and self.upgrade_recommended:
 
1097
            ui.ui_factory.recommend_upgrade(
 
1098
                self.get_format_description(), basedir)
 
1099
 
 
1100
    def same_model(self, target_format):
 
1101
        return (self.repository_format.rich_root_data ==
 
1102
            target_format.rich_root_data)
 
1103
 
 
1104
    @classmethod
 
1105
    def register_prober(klass, prober):
 
1106
        """Register a prober that can look for a control dir.
 
1107
 
 
1108
        """
 
1109
        klass._probers.append(prober)
 
1110
 
 
1111
    @classmethod
 
1112
    def unregister_prober(klass, prober):
 
1113
        """Unregister a prober.
 
1114
 
 
1115
        """
 
1116
        klass._probers.remove(prober)
 
1117
 
 
1118
    @classmethod
 
1119
    def register_server_prober(klass, prober):
 
1120
        """Register a control format prober for client-server environments.
 
1121
 
 
1122
        These probers will be used before ones registered with
 
1123
        register_prober.  This gives implementations that decide to the
 
1124
        chance to grab it before anything looks at the contents of the format
 
1125
        file.
 
1126
        """
 
1127
        klass._server_probers.append(prober)
 
1128
 
 
1129
    def __str__(self):
 
1130
        # Trim the newline
 
1131
        return self.get_format_description().rstrip()
 
1132
 
 
1133
    @classmethod
 
1134
    def all_probers(klass):
 
1135
        return klass._server_probers + klass._probers
 
1136
 
 
1137
    @classmethod
 
1138
    def known_formats(klass):
 
1139
        """Return all the known formats.
 
1140
        """
 
1141
        result = set()
 
1142
        for prober_kls in klass.all_probers():
 
1143
            result.update(prober_kls.known_formats())
 
1144
        return result
 
1145
 
 
1146
    @classmethod
 
1147
    def find_format(klass, transport, probers=None):
 
1148
        """Return the format present at transport."""
 
1149
        if probers is None:
 
1150
            probers = klass.all_probers()
 
1151
        for prober_kls in probers:
 
1152
            prober = prober_kls()
 
1153
            try:
 
1154
                return prober.probe_transport(transport)
 
1155
            except errors.NotBranchError:
 
1156
                # this format does not find a control dir here.
 
1157
                pass
 
1158
        raise errors.NotBranchError(path=transport.base)
 
1159
 
 
1160
    def initialize(self, url, possible_transports=None):
 
1161
        """Create a control dir at this url and return an opened copy.
 
1162
 
 
1163
        While not deprecated, this method is very specific and its use will
 
1164
        lead to many round trips to setup a working environment. See
 
1165
        initialize_on_transport_ex for a [nearly] all-in-one method.
 
1166
 
 
1167
        Subclasses should typically override initialize_on_transport
 
1168
        instead of this method.
 
1169
        """
 
1170
        return self.initialize_on_transport(
 
1171
            _mod_transport.get_transport(url, possible_transports))
 
1172
 
 
1173
    def initialize_on_transport(self, transport):
 
1174
        """Initialize a new controldir in the base directory of a Transport."""
 
1175
        raise NotImplementedError(self.initialize_on_transport)
 
1176
 
 
1177
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
1178
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
1179
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
1180
        shared_repo=False, vfs_only=False):
 
1181
        """Create this format on transport.
 
1182
 
 
1183
        The directory to initialize will be created.
 
1184
 
 
1185
        :param force_new_repo: Do not use a shared repository for the target,
 
1186
                               even if one is available.
 
1187
        :param create_prefix: Create any missing directories leading up to
 
1188
            to_transport.
 
1189
        :param use_existing_dir: Use an existing directory if one exists.
 
1190
        :param stacked_on: A url to stack any created branch on, None to follow
 
1191
            any target stacking policy.
 
1192
        :param stack_on_pwd: If stack_on is relative, the location it is
 
1193
            relative to.
 
1194
        :param repo_format_name: If non-None, a repository will be
 
1195
            made-or-found. Should none be found, or if force_new_repo is True
 
1196
            the repo_format_name is used to select the format of repository to
 
1197
            create.
 
1198
        :param make_working_trees: Control the setting of make_working_trees
 
1199
            for a new shared repository when one is made. None to use whatever
 
1200
            default the format has.
 
1201
        :param shared_repo: Control whether made repositories are shared or
 
1202
            not.
 
1203
        :param vfs_only: If True do not attempt to use a smart server
 
1204
        :return: repo, controldir, require_stacking, repository_policy. repo is
 
1205
            None if none was created or found, controldir is always valid.
 
1206
            require_stacking is the result of examining the stacked_on
 
1207
            parameter and any stacking policy found for the target.
 
1208
        """
 
1209
        raise NotImplementedError(self.initialize_on_transport_ex)
 
1210
 
 
1211
    def network_name(self):
 
1212
        """A simple byte string uniquely identifying this format for RPC calls.
 
1213
 
 
1214
        Bzr control formats use this disk format string to identify the format
 
1215
        over the wire. Its possible that other control formats have more
 
1216
        complex detection requirements, so we permit them to use any unique and
 
1217
        immutable string they desire.
 
1218
        """
 
1219
        raise NotImplementedError(self.network_name)
 
1220
 
 
1221
    def open(self, transport, _found=False):
 
1222
        """Return an instance of this format for the dir transport points at.
 
1223
        """
 
1224
        raise NotImplementedError(self.open)
 
1225
 
 
1226
    @classmethod
 
1227
    def _set_default_format(klass, format):
 
1228
        """Set default format (for testing behavior of defaults only)"""
 
1229
        klass._default_format = format
 
1230
 
 
1231
    @classmethod
 
1232
    def get_default_format(klass):
 
1233
        """Return the current default format."""
 
1234
        return klass._default_format
 
1235
 
 
1236
    def supports_transport(self, transport):
 
1237
        """Check if this format can be opened over a particular transport.
 
1238
        """
 
1239
        raise NotImplementedError(self.supports_transport)
 
1240
 
 
1241
 
 
1242
class Prober(object):
 
1243
    """Abstract class that can be used to detect a particular kind of
 
1244
    control directory.
 
1245
 
 
1246
    At the moment this just contains a single method to probe a particular
 
1247
    transport, but it may be extended in the future to e.g. avoid
 
1248
    multiple levels of probing for Subversion repositories.
 
1249
 
 
1250
    See BzrProber and RemoteBzrProber in breezy.bzrdir for the
 
1251
    probers that detect .bzr/ directories and Bazaar smart servers,
 
1252
    respectively.
 
1253
 
 
1254
    Probers should be registered using the register_server_prober or
 
1255
    register_prober methods on ControlDirFormat.
 
1256
    """
 
1257
 
 
1258
    def probe_transport(self, transport):
 
1259
        """Return the controldir style format present in a directory.
 
1260
 
 
1261
        :raise UnknownFormatError: If a control dir was found but is
 
1262
            in an unknown format.
 
1263
        :raise NotBranchError: If no control directory was found.
 
1264
        :return: A ControlDirFormat instance.
 
1265
        """
 
1266
        raise NotImplementedError(self.probe_transport)
 
1267
 
 
1268
    @classmethod
 
1269
    def known_formats(klass):
 
1270
        """Return the control dir formats known by this prober.
 
1271
 
 
1272
        Multiple probers can return the same formats, so this should
 
1273
        return a set.
 
1274
 
 
1275
        :return: A set of known formats.
 
1276
        """
 
1277
        raise NotImplementedError(klass.known_formats)
 
1278
 
 
1279
 
 
1280
class ControlDirFormatInfo(object):
 
1281
 
 
1282
    def __init__(self, native, deprecated, hidden, experimental):
 
1283
        self.deprecated = deprecated
 
1284
        self.native = native
 
1285
        self.hidden = hidden
 
1286
        self.experimental = experimental
 
1287
 
 
1288
 
 
1289
class ControlDirFormatRegistry(registry.Registry):
 
1290
    """Registry of user-selectable ControlDir subformats.
 
1291
 
 
1292
    Differs from ControlDirFormat._formats in that it provides sub-formats,
 
1293
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
1294
    """
 
1295
 
 
1296
    def __init__(self):
 
1297
        """Create a ControlDirFormatRegistry."""
 
1298
        self._aliases = set()
 
1299
        self._registration_order = list()
 
1300
        super(ControlDirFormatRegistry, self).__init__()
 
1301
 
 
1302
    def aliases(self):
 
1303
        """Return a set of the format names which are aliases."""
 
1304
        return frozenset(self._aliases)
 
1305
 
 
1306
    def register(self, key, factory, help, native=True, deprecated=False,
 
1307
                 hidden=False, experimental=False, alias=False):
 
1308
        """Register a ControlDirFormat factory.
 
1309
 
 
1310
        The factory must be a callable that takes one parameter: the key.
 
1311
        It must produce an instance of the ControlDirFormat when called.
 
1312
 
 
1313
        This function mainly exists to prevent the info object from being
 
1314
        supplied directly.
 
1315
        """
 
1316
        registry.Registry.register(self, key, factory, help,
 
1317
            ControlDirFormatInfo(native, deprecated, hidden, experimental))
 
1318
        if alias:
 
1319
            self._aliases.add(key)
 
1320
        self._registration_order.append(key)
 
1321
 
 
1322
    def register_lazy(self, key, module_name, member_name, help, native=True,
 
1323
        deprecated=False, hidden=False, experimental=False, alias=False):
 
1324
        registry.Registry.register_lazy(self, key, module_name, member_name,
 
1325
            help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
 
1326
        if alias:
 
1327
            self._aliases.add(key)
 
1328
        self._registration_order.append(key)
 
1329
 
 
1330
    def set_default(self, key):
 
1331
        """Set the 'default' key to be a clone of the supplied key.
 
1332
 
 
1333
        This method must be called once and only once.
 
1334
        """
 
1335
        registry.Registry.register(self, 'default', self.get(key),
 
1336
            self.get_help(key), info=self.get_info(key))
 
1337
        self._aliases.add('default')
 
1338
 
 
1339
    def set_default_repository(self, key):
 
1340
        """Set the FormatRegistry default and Repository default.
 
1341
 
 
1342
        This is a transitional method while Repository.set_default_format
 
1343
        is deprecated.
 
1344
        """
 
1345
        if 'default' in self:
 
1346
            self.remove('default')
 
1347
        self.set_default(key)
 
1348
        format = self.get('default')()
 
1349
 
 
1350
    def make_controldir(self, key):
 
1351
        return self.get(key)()
 
1352
 
 
1353
    def help_topic(self, topic):
 
1354
        output = ""
 
1355
        default_realkey = None
 
1356
        default_help = self.get_help('default')
 
1357
        help_pairs = []
 
1358
        for key in self._registration_order:
 
1359
            if key == 'default':
 
1360
                continue
 
1361
            help = self.get_help(key)
 
1362
            if help == default_help:
 
1363
                default_realkey = key
 
1364
            else:
 
1365
                help_pairs.append((key, help))
 
1366
 
 
1367
        def wrapped(key, help, info):
 
1368
            if info.native:
 
1369
                help = '(native) ' + help
 
1370
            return ':%s:\n%s\n\n' % (key,
 
1371
                textwrap.fill(help, initial_indent='    ',
 
1372
                    subsequent_indent='    ',
 
1373
                    break_long_words=False))
 
1374
        if default_realkey is not None:
 
1375
            output += wrapped(default_realkey, '(default) %s' % default_help,
 
1376
                              self.get_info('default'))
 
1377
        deprecated_pairs = []
 
1378
        experimental_pairs = []
 
1379
        for key, help in help_pairs:
 
1380
            info = self.get_info(key)
 
1381
            if info.hidden:
 
1382
                continue
 
1383
            elif info.deprecated:
 
1384
                deprecated_pairs.append((key, help))
 
1385
            elif info.experimental:
 
1386
                experimental_pairs.append((key, help))
 
1387
            else:
 
1388
                output += wrapped(key, help, info)
 
1389
        output += "\nSee :doc:`formats-help` for more about storage formats."
 
1390
        other_output = ""
 
1391
        if len(experimental_pairs) > 0:
 
1392
            other_output += "Experimental formats are shown below.\n\n"
 
1393
            for key, help in experimental_pairs:
 
1394
                info = self.get_info(key)
 
1395
                other_output += wrapped(key, help, info)
 
1396
        else:
 
1397
            other_output += \
 
1398
                "No experimental formats are available.\n\n"
 
1399
        if len(deprecated_pairs) > 0:
 
1400
            other_output += "\nDeprecated formats are shown below.\n\n"
 
1401
            for key, help in deprecated_pairs:
 
1402
                info = self.get_info(key)
 
1403
                other_output += wrapped(key, help, info)
 
1404
        else:
 
1405
            other_output += \
 
1406
                "\nNo deprecated formats are available.\n\n"
 
1407
        other_output += \
 
1408
                "\nSee :doc:`formats-help` for more about storage formats."
 
1409
 
 
1410
        if topic == 'other-formats':
 
1411
            return other_output
 
1412
        else:
 
1413
            return output
 
1414
 
 
1415
 
 
1416
class RepoInitHookParams(object):
 
1417
    """Object holding parameters passed to `*_repo_init` hooks.
 
1418
 
 
1419
    There are 4 fields that hooks may wish to access:
 
1420
 
 
1421
    :ivar repository: Repository created
 
1422
    :ivar format: Repository format
 
1423
    :ivar bzrdir: The controldir for the repository
 
1424
    :ivar shared: The repository is shared
 
1425
    """
 
1426
 
 
1427
    def __init__(self, repository, format, controldir, shared):
 
1428
        """Create a group of RepoInitHook parameters.
 
1429
 
 
1430
        :param repository: Repository created
 
1431
        :param format: Repository format
 
1432
        :param controldir: The controldir for the repository
 
1433
        :param shared: The repository is shared
 
1434
        """
 
1435
        self.repository = repository
 
1436
        self.format = format
 
1437
        self.controldir = controldir
 
1438
        self.shared = shared
 
1439
 
 
1440
    def __eq__(self, other):
 
1441
        return self.__dict__ == other.__dict__
 
1442
 
 
1443
    def __repr__(self):
 
1444
        if self.repository:
 
1445
            return "<%s for %s>" % (self.__class__.__name__,
 
1446
                self.repository)
 
1447
        else:
 
1448
            return "<%s for %s>" % (self.__class__.__name__,
 
1449
                self.controldir)
 
1450
 
 
1451
 
 
1452
def is_control_filename(filename):
 
1453
    """Check if filename is used for control directories."""
 
1454
    # TODO(jelmer): Allow registration by other VCSes
 
1455
    return filename == '.bzr'
 
1456
 
 
1457
 
 
1458
class RepositoryAcquisitionPolicy(object):
 
1459
    """Abstract base class for repository acquisition policies.
 
1460
 
 
1461
    A repository acquisition policy decides how a ControlDir acquires a repository
 
1462
    for a branch that is being created.  The most basic policy decision is
 
1463
    whether to create a new repository or use an existing one.
 
1464
    """
 
1465
    def __init__(self, stack_on, stack_on_pwd, require_stacking):
 
1466
        """Constructor.
 
1467
 
 
1468
        :param stack_on: A location to stack on
 
1469
        :param stack_on_pwd: If stack_on is relative, the location it is
 
1470
            relative to.
 
1471
        :param require_stacking: If True, it is a failure to not stack.
 
1472
        """
 
1473
        self._stack_on = stack_on
 
1474
        self._stack_on_pwd = stack_on_pwd
 
1475
        self._require_stacking = require_stacking
 
1476
 
 
1477
    def configure_branch(self, branch):
 
1478
        """Apply any configuration data from this policy to the branch.
 
1479
 
 
1480
        Default implementation sets repository stacking.
 
1481
        """
 
1482
        if self._stack_on is None:
 
1483
            return
 
1484
        if self._stack_on_pwd is None:
 
1485
            stack_on = self._stack_on
 
1486
        else:
 
1487
            try:
 
1488
                stack_on = urlutils.rebase_url(self._stack_on,
 
1489
                    self._stack_on_pwd,
 
1490
                    branch.user_url)
 
1491
            except urlutils.InvalidRebaseURLs:
 
1492
                stack_on = self._get_full_stack_on()
 
1493
        try:
 
1494
            branch.set_stacked_on_url(stack_on)
 
1495
        except (_mod_branch.UnstackableBranchFormat,
 
1496
                errors.UnstackableRepositoryFormat):
 
1497
            if self._require_stacking:
 
1498
                raise
 
1499
 
 
1500
    def requires_stacking(self):
 
1501
        """Return True if this policy requires stacking."""
 
1502
        return self._stack_on is not None and self._require_stacking
 
1503
 
 
1504
    def _get_full_stack_on(self):
 
1505
        """Get a fully-qualified URL for the stack_on location."""
 
1506
        if self._stack_on is None:
 
1507
            return None
 
1508
        if self._stack_on_pwd is None:
 
1509
            return self._stack_on
 
1510
        else:
 
1511
            return urlutils.join(self._stack_on_pwd, self._stack_on)
 
1512
 
 
1513
    def _add_fallback(self, repository, possible_transports=None):
 
1514
        """Add a fallback to the supplied repository, if stacking is set."""
 
1515
        stack_on = self._get_full_stack_on()
 
1516
        if stack_on is None:
 
1517
            return
 
1518
        try:
 
1519
            stacked_dir = ControlDir.open(
 
1520
                    stack_on, possible_transports=possible_transports)
 
1521
        except errors.JailBreak:
 
1522
            # We keep the stacking details, but we are in the server code so
 
1523
            # actually stacking is not needed.
 
1524
            return
 
1525
        try:
 
1526
            stacked_repo = stacked_dir.open_branch().repository
 
1527
        except errors.NotBranchError:
 
1528
            stacked_repo = stacked_dir.open_repository()
 
1529
        try:
 
1530
            repository.add_fallback_repository(stacked_repo)
 
1531
        except errors.UnstackableRepositoryFormat:
 
1532
            if self._require_stacking:
 
1533
                raise
 
1534
        else:
 
1535
            self._require_stacking = True
 
1536
 
 
1537
    def acquire_repository(self, make_working_trees=None, shared=False,
 
1538
            possible_transports=None):
 
1539
        """Acquire a repository for this controlrdir.
 
1540
 
 
1541
        Implementations may create a new repository or use a pre-exising
 
1542
        repository.
 
1543
 
 
1544
        :param make_working_trees: If creating a repository, set
 
1545
            make_working_trees to this value (if non-None)
 
1546
        :param shared: If creating a repository, make it shared if True
 
1547
        :return: A repository, is_new_flag (True if the repository was
 
1548
            created).
 
1549
        """
 
1550
        raise NotImplementedError(RepositoryAcquisitionPolicy.acquire_repository)
 
1551
 
 
1552
 
 
1553
# Please register new formats after old formats so that formats
 
1554
# appear in chronological order and format descriptions can build
 
1555
# on previous ones.
 
1556
format_registry = ControlDirFormatRegistry()
 
1557
 
 
1558
network_format_registry = registry.FormatRegistry()
 
1559
"""Registry of formats indexed by their network name.
 
1560
 
 
1561
The network name for a ControlDirFormat is an identifier that can be used when
 
1562
referring to formats with smart server operations. See
 
1563
ControlDirFormat.network_name() for more detail.
 
1564
"""