~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

  • Committer: Robert Collins
  • Date: 2005-12-02 03:23:47 UTC
  • Revision ID: robertc@robertcollins.net-20051202032347-ba08123207a945a0
Test for Jeff Bailey.

Show diffs side-by-side

added added

removed removed

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