/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: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

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