/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Martin Pool
  • Date: 2007-03-23 21:46:46 UTC
  • mto: (2323.5.2 0.15)
  • mto: This revision was merged to the branch mainline in revision 2390.
  • Revision ID: mbp@sourcefrog.net-20070323214646-zfvwiprkvbhuvz6o
Don't warn about old wt format when getting bzr's version

This fixes selftest when bzr is run from an old-format tree 
(as pqm does)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
 
18
 
 
19
At format 7 this was split out into Branch, Repository and Checkout control
 
20
directories.
 
21
"""
 
22
 
 
23
# TODO: remove unittest dependency; put that stuff inside the test suite
 
24
 
 
25
# TODO: The Format probe_transport seems a bit redundant with just trying to
 
26
# open the bzrdir. -- mbp
 
27
#
 
28
# TODO: Can we move specific formats into separate modules to make this file
 
29
# smaller?
 
30
 
 
31
from cStringIO import StringIO
 
32
import os
 
33
import textwrap
 
34
 
 
35
from bzrlib.lazy_import import lazy_import
 
36
lazy_import(globals(), """
 
37
from copy import deepcopy
 
38
from stat import S_ISDIR
 
39
import unittest
 
40
 
 
41
import bzrlib
 
42
from bzrlib import (
 
43
    errors,
 
44
    lockable_files,
 
45
    lockdir,
 
46
    registry,
 
47
    revision as _mod_revision,
 
48
    symbol_versioning,
 
49
    ui,
 
50
    urlutils,
 
51
    xml4,
 
52
    xml5,
 
53
    workingtree,
 
54
    workingtree_4,
 
55
    )
 
56
from bzrlib.osutils import (
 
57
    safe_unicode,
 
58
    sha_strings,
 
59
    sha_string,
 
60
    )
 
61
from bzrlib.store.revision.text import TextRevisionStore
 
62
from bzrlib.store.text import TextStore
 
63
from bzrlib.store.versioned import WeaveStore
 
64
from bzrlib.transactions import WriteTransaction
 
65
from bzrlib.transport import get_transport
 
66
from bzrlib.weave import Weave
 
67
""")
 
68
 
 
69
from bzrlib.trace import mutter, note
 
70
from bzrlib.transport.local import LocalTransport
 
71
 
 
72
 
 
73
class BzrDir(object):
 
74
    """A .bzr control diretory.
 
75
    
 
76
    BzrDir instances let you create or open any of the things that can be
 
77
    found within .bzr - checkouts, branches and repositories.
 
78
    
 
79
    transport
 
80
        the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
 
81
    root_transport
 
82
        a transport connected to the directory this bzr was opened from.
 
83
    """
 
84
 
 
85
    def break_lock(self):
 
86
        """Invoke break_lock on the first object in the bzrdir.
 
87
 
 
88
        If there is a tree, the tree is opened and break_lock() called.
 
89
        Otherwise, branch is tried, and finally repository.
 
90
        """
 
91
        try:
 
92
            thing_to_unlock = self.open_workingtree()
 
93
        except (errors.NotLocalUrl, errors.NoWorkingTree):
 
94
            try:
 
95
                thing_to_unlock = self.open_branch()
 
96
            except errors.NotBranchError:
 
97
                try:
 
98
                    thing_to_unlock = self.open_repository()
 
99
                except errors.NoRepositoryPresent:
 
100
                    return
 
101
        thing_to_unlock.break_lock()
 
102
 
 
103
    def can_convert_format(self):
 
104
        """Return true if this bzrdir is one whose format we can convert from."""
 
105
        return True
 
106
 
 
107
    def check_conversion_target(self, target_format):
 
108
        target_repo_format = target_format.repository_format
 
109
        source_repo_format = self._format.repository_format
 
110
        source_repo_format.check_conversion_target(target_repo_format)
 
111
 
 
112
    @staticmethod
 
113
    def _check_supported(format, allow_unsupported,
 
114
        recommend_upgrade=True,
 
115
        basedir=None):
 
116
        """Give an error or warning on old formats.
 
117
 
 
118
        :param format: may be any kind of format - workingtree, branch, 
 
119
        or repository.
 
120
 
 
121
        :param allow_unsupported: If true, allow opening 
 
122
        formats that are strongly deprecated, and which may 
 
123
        have limited functionality.
 
124
 
 
125
        :param recommend_upgrade: If true (default), warn
 
126
        the user through the ui object that they may wish
 
127
        to upgrade the object.
 
128
        """
 
129
        # TODO: perhaps move this into the format itself.
 
130
        # mbp 20070323
 
131
        if not allow_unsupported and not format.is_supported():
 
132
            # see open_downlevel to open legacy branches.
 
133
            raise errors.UnsupportedFormatError(format=format)
 
134
        if recommend_upgrade \
 
135
            and getattr(format, 'upgrade_recommended', False):
 
136
            ui.ui_factory.recommend_upgrade(
 
137
                format.get_format_description(),
 
138
                basedir)
 
139
 
 
140
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
141
        """Clone this bzrdir and its contents to url verbatim.
 
142
 
 
143
        If urls last component does not exist, it will be created.
 
144
 
 
145
        if revision_id is not None, then the clone operation may tune
 
146
            itself to download less data.
 
147
        :param force_new_repo: Do not use a shared repository for the target 
 
148
                               even if one is available.
 
149
        """
 
150
        self._make_tail(url)
 
151
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
152
        result = self._format.initialize(url)
 
153
        try:
 
154
            local_repo = self.find_repository()
 
155
        except errors.NoRepositoryPresent:
 
156
            local_repo = None
 
157
        if local_repo:
 
158
            # may need to copy content in
 
159
            if force_new_repo:
 
160
                result_repo = local_repo.clone(
 
161
                    result,
 
162
                    revision_id=revision_id,
 
163
                    basis=basis_repo)
 
164
                result_repo.set_make_working_trees(local_repo.make_working_trees())
 
165
            else:
 
166
                try:
 
167
                    result_repo = result.find_repository()
 
168
                    # fetch content this dir needs.
 
169
                    if basis_repo:
 
170
                        # XXX FIXME RBC 20060214 need tests for this when the basis
 
171
                        # is incomplete
 
172
                        result_repo.fetch(basis_repo, revision_id=revision_id)
 
173
                    result_repo.fetch(local_repo, revision_id=revision_id)
 
174
                except errors.NoRepositoryPresent:
 
175
                    # needed to make one anyway.
 
176
                    result_repo = local_repo.clone(
 
177
                        result,
 
178
                        revision_id=revision_id,
 
179
                        basis=basis_repo)
 
180
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
 
181
        # 1 if there is a branch present
 
182
        #   make sure its content is available in the target repository
 
183
        #   clone it.
 
184
        try:
 
185
            self.open_branch().clone(result, revision_id=revision_id)
 
186
        except errors.NotBranchError:
 
187
            pass
 
188
        try:
 
189
            self.open_workingtree().clone(result, basis=basis_tree)
 
190
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
191
            pass
 
192
        return result
 
193
 
 
194
    def _get_basis_components(self, basis):
 
195
        """Retrieve the basis components that are available at basis."""
 
196
        if basis is None:
 
197
            return None, None, None
 
198
        try:
 
199
            basis_tree = basis.open_workingtree()
 
200
            basis_branch = basis_tree.branch
 
201
            basis_repo = basis_branch.repository
 
202
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
203
            basis_tree = None
 
204
            try:
 
205
                basis_branch = basis.open_branch()
 
206
                basis_repo = basis_branch.repository
 
207
            except errors.NotBranchError:
 
208
                basis_branch = None
 
209
                try:
 
210
                    basis_repo = basis.open_repository()
 
211
                except errors.NoRepositoryPresent:
 
212
                    basis_repo = None
 
213
        return basis_repo, basis_branch, basis_tree
 
214
 
 
215
    # TODO: This should be given a Transport, and should chdir up; otherwise
 
216
    # this will open a new connection.
 
217
    def _make_tail(self, url):
 
218
        head, tail = urlutils.split(url)
 
219
        if tail and tail != '.':
 
220
            t = get_transport(head)
 
221
            try:
 
222
                t.mkdir(tail)
 
223
            except errors.FileExists:
 
224
                pass
 
225
 
 
226
    # TODO: Should take a Transport
 
227
    @classmethod
 
228
    def create(cls, base, format=None):
 
229
        """Create a new BzrDir at the url 'base'.
 
230
        
 
231
        This will call the current default formats initialize with base
 
232
        as the only parameter.
 
233
 
 
234
        :param format: If supplied, the format of branch to create.  If not
 
235
            supplied, the default is used.
 
236
        """
 
237
        if cls is not BzrDir:
 
238
            raise AssertionError("BzrDir.create always creates the default"
 
239
                " format, not one of %r" % cls)
 
240
        head, tail = urlutils.split(base)
 
241
        if tail and tail != '.':
 
242
            t = get_transport(head)
 
243
            try:
 
244
                t.mkdir(tail)
 
245
            except errors.FileExists:
 
246
                pass
 
247
        if format is None:
 
248
            format = BzrDirFormat.get_default_format()
 
249
        return format.initialize(safe_unicode(base))
 
250
 
 
251
    def create_branch(self):
 
252
        """Create a branch in this BzrDir.
 
253
 
 
254
        The bzrdirs format will control what branch format is created.
 
255
        For more control see BranchFormatXX.create(a_bzrdir).
 
256
        """
 
257
        raise NotImplementedError(self.create_branch)
 
258
 
 
259
    @staticmethod
 
260
    def create_branch_and_repo(base, force_new_repo=False, format=None):
 
261
        """Create a new BzrDir, Branch and Repository at the url 'base'.
 
262
 
 
263
        This will use the current default BzrDirFormat, and use whatever 
 
264
        repository format that that uses via bzrdir.create_branch and
 
265
        create_repository. If a shared repository is available that is used
 
266
        preferentially.
 
267
 
 
268
        The created Branch object is returned.
 
269
 
 
270
        :param base: The URL to create the branch at.
 
271
        :param force_new_repo: If True a new repository is always created.
 
272
        """
 
273
        bzrdir = BzrDir.create(base, format)
 
274
        bzrdir._find_or_create_repository(force_new_repo)
 
275
        return bzrdir.create_branch()
 
276
 
 
277
    def _find_or_create_repository(self, force_new_repo):
 
278
        """Create a new repository if needed, returning the repository."""
 
279
        if force_new_repo:
 
280
            return self.create_repository()
 
281
        try:
 
282
            return self.find_repository()
 
283
        except errors.NoRepositoryPresent:
 
284
            return self.create_repository()
 
285
        
 
286
    @staticmethod
 
287
    def create_branch_convenience(base, force_new_repo=False,
 
288
                                  force_new_tree=None, format=None):
 
289
        """Create a new BzrDir, Branch and Repository at the url 'base'.
 
290
 
 
291
        This is a convenience function - it will use an existing repository
 
292
        if possible, can be told explicitly whether to create a working tree or
 
293
        not.
 
294
 
 
295
        This will use the current default BzrDirFormat, and use whatever 
 
296
        repository format that that uses via bzrdir.create_branch and
 
297
        create_repository. If a shared repository is available that is used
 
298
        preferentially. Whatever repository is used, its tree creation policy
 
299
        is followed.
 
300
 
 
301
        The created Branch object is returned.
 
302
        If a working tree cannot be made due to base not being a file:// url,
 
303
        no error is raised unless force_new_tree is True, in which case no 
 
304
        data is created on disk and NotLocalUrl is raised.
 
305
 
 
306
        :param base: The URL to create the branch at.
 
307
        :param force_new_repo: If True a new repository is always created.
 
308
        :param force_new_tree: If True or False force creation of a tree or 
 
309
                               prevent such creation respectively.
 
310
        :param format: Override for the for the bzrdir format to create
 
311
        """
 
312
        if force_new_tree:
 
313
            # check for non local urls
 
314
            t = get_transport(safe_unicode(base))
 
315
            if not isinstance(t, LocalTransport):
 
316
                raise errors.NotLocalUrl(base)
 
317
        bzrdir = BzrDir.create(base, format)
 
318
        repo = bzrdir._find_or_create_repository(force_new_repo)
 
319
        result = bzrdir.create_branch()
 
320
        if force_new_tree or (repo.make_working_trees() and 
 
321
                              force_new_tree is None):
 
322
            try:
 
323
                bzrdir.create_workingtree()
 
324
            except errors.NotLocalUrl:
 
325
                pass
 
326
        return result
 
327
        
 
328
    @staticmethod
 
329
    def create_repository(base, shared=False, format=None):
 
330
        """Create a new BzrDir and Repository at the url 'base'.
 
331
 
 
332
        If no format is supplied, this will default to the current default
 
333
        BzrDirFormat by default, and use whatever repository format that that
 
334
        uses for bzrdirformat.create_repository.
 
335
 
 
336
        :param shared: Create a shared repository rather than a standalone
 
337
                       repository.
 
338
        The Repository object is returned.
 
339
 
 
340
        This must be overridden as an instance method in child classes, where
 
341
        it should take no parameters and construct whatever repository format
 
342
        that child class desires.
 
343
        """
 
344
        bzrdir = BzrDir.create(base, format)
 
345
        return bzrdir.create_repository(shared)
 
346
 
 
347
    @staticmethod
 
348
    def create_standalone_workingtree(base, format=None):
 
349
        """Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
 
350
 
 
351
        'base' must be a local path or a file:// url.
 
352
 
 
353
        This will use the current default BzrDirFormat, and use whatever 
 
354
        repository format that that uses for bzrdirformat.create_workingtree,
 
355
        create_branch and create_repository.
 
356
 
 
357
        :return: The WorkingTree object.
 
358
        """
 
359
        t = get_transport(safe_unicode(base))
 
360
        if not isinstance(t, LocalTransport):
 
361
            raise errors.NotLocalUrl(base)
 
362
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
 
363
                                               force_new_repo=True,
 
364
                                               format=format).bzrdir
 
365
        return bzrdir.create_workingtree()
 
366
 
 
367
    def create_workingtree(self, revision_id=None):
 
368
        """Create a working tree at this BzrDir.
 
369
        
 
370
        revision_id: create it as of this revision id.
 
371
        """
 
372
        raise NotImplementedError(self.create_workingtree)
 
373
 
 
374
    def retire_bzrdir(self):
 
375
        """Permanently disable the bzrdir.
 
376
 
 
377
        This is done by renaming it to give the user some ability to recover
 
378
        if there was a problem.
 
379
 
 
380
        This will have horrible consequences if anyone has anything locked or
 
381
        in use.
 
382
        """
 
383
        for i in xrange(10000):
 
384
            try:
 
385
                to_path = '.bzr.retired.%d' % i
 
386
                self.root_transport.rename('.bzr', to_path)
 
387
                note("renamed %s to %s"
 
388
                    % (self.root_transport.abspath('.bzr'), to_path))
 
389
                break
 
390
            except (errors.TransportError, IOError, errors.PathError):
 
391
                pass
 
392
 
 
393
    def destroy_workingtree(self):
 
394
        """Destroy the working tree at this BzrDir.
 
395
 
 
396
        Formats that do not support this may raise UnsupportedOperation.
 
397
        """
 
398
        raise NotImplementedError(self.destroy_workingtree)
 
399
 
 
400
    def destroy_workingtree_metadata(self):
 
401
        """Destroy the control files for the working tree at this BzrDir.
 
402
 
 
403
        The contents of working tree files are not affected.
 
404
        Formats that do not support this may raise UnsupportedOperation.
 
405
        """
 
406
        raise NotImplementedError(self.destroy_workingtree_metadata)
 
407
 
 
408
    def find_repository(self):
 
409
        """Find the repository that should be used for a_bzrdir.
 
410
 
 
411
        This does not require a branch as we use it to find the repo for
 
412
        new branches as well as to hook existing branches up to their
 
413
        repository.
 
414
        """
 
415
        try:
 
416
            return self.open_repository()
 
417
        except errors.NoRepositoryPresent:
 
418
            pass
 
419
        next_transport = self.root_transport.clone('..')
 
420
        while True:
 
421
            # find the next containing bzrdir
 
422
            try:
 
423
                found_bzrdir = BzrDir.open_containing_from_transport(
 
424
                    next_transport)[0]
 
425
            except errors.NotBranchError:
 
426
                # none found
 
427
                raise errors.NoRepositoryPresent(self)
 
428
            # does it have a repository ?
 
429
            try:
 
430
                repository = found_bzrdir.open_repository()
 
431
            except errors.NoRepositoryPresent:
 
432
                next_transport = found_bzrdir.root_transport.clone('..')
 
433
                if (found_bzrdir.root_transport.base == next_transport.base):
 
434
                    # top of the file system
 
435
                    break
 
436
                else:
 
437
                    continue
 
438
            if ((found_bzrdir.root_transport.base == 
 
439
                 self.root_transport.base) or repository.is_shared()):
 
440
                return repository
 
441
            else:
 
442
                raise errors.NoRepositoryPresent(self)
 
443
        raise errors.NoRepositoryPresent(self)
 
444
 
 
445
    def get_branch_transport(self, branch_format):
 
446
        """Get the transport for use by branch format in this BzrDir.
 
447
 
 
448
        Note that bzr dirs that do not support format strings will raise
 
449
        IncompatibleFormat if the branch format they are given has
 
450
        a format string, and vice versa.
 
451
 
 
452
        If branch_format is None, the transport is returned with no 
 
453
        checking. if it is not None, then the returned transport is
 
454
        guaranteed to point to an existing directory ready for use.
 
455
        """
 
456
        raise NotImplementedError(self.get_branch_transport)
 
457
        
 
458
    def get_repository_transport(self, repository_format):
 
459
        """Get the transport for use by repository format in this BzrDir.
 
460
 
 
461
        Note that bzr dirs that do not support format strings will raise
 
462
        IncompatibleFormat if the repository format they are given has
 
463
        a format string, and vice versa.
 
464
 
 
465
        If repository_format is None, the transport is returned with no 
 
466
        checking. if it is not None, then the returned transport is
 
467
        guaranteed to point to an existing directory ready for use.
 
468
        """
 
469
        raise NotImplementedError(self.get_repository_transport)
 
470
        
 
471
    def get_workingtree_transport(self, tree_format):
 
472
        """Get the transport for use by workingtree format in this BzrDir.
 
473
 
 
474
        Note that bzr dirs that do not support format strings will raise
 
475
        IncompatibleFormat if the workingtree format they are given has a
 
476
        format string, and vice versa.
 
477
 
 
478
        If workingtree_format is None, the transport is returned with no 
 
479
        checking. if it is not None, then the returned transport is
 
480
        guaranteed to point to an existing directory ready for use.
 
481
        """
 
482
        raise NotImplementedError(self.get_workingtree_transport)
 
483
        
 
484
    def __init__(self, _transport, _format):
 
485
        """Initialize a Bzr control dir object.
 
486
        
 
487
        Only really common logic should reside here, concrete classes should be
 
488
        made with varying behaviours.
 
489
 
 
490
        :param _format: the format that is creating this BzrDir instance.
 
491
        :param _transport: the transport this dir is based at.
 
492
        """
 
493
        self._format = _format
 
494
        self.transport = _transport.clone('.bzr')
 
495
        self.root_transport = _transport
 
496
 
 
497
    def is_control_filename(self, filename):
 
498
        """True if filename is the name of a path which is reserved for bzrdir's.
 
499
        
 
500
        :param filename: A filename within the root transport of this bzrdir.
 
501
 
 
502
        This is true IF and ONLY IF the filename is part of the namespace reserved
 
503
        for bzr control dirs. Currently this is the '.bzr' directory in the root
 
504
        of the root_transport. it is expected that plugins will need to extend
 
505
        this in the future - for instance to make bzr talk with svn working
 
506
        trees.
 
507
        """
 
508
        # this might be better on the BzrDirFormat class because it refers to 
 
509
        # all the possible bzrdir disk formats. 
 
510
        # This method is tested via the workingtree is_control_filename tests- 
 
511
        # it was extracted from WorkingTree.is_control_filename. If the methods
 
512
        # contract is extended beyond the current trivial  implementation please
 
513
        # add new tests for it to the appropriate place.
 
514
        return filename == '.bzr' or filename.startswith('.bzr/')
 
515
 
 
516
    def needs_format_conversion(self, format=None):
 
517
        """Return true if this bzrdir needs convert_format run on it.
 
518
        
 
519
        For instance, if the repository format is out of date but the 
 
520
        branch and working tree are not, this should return True.
 
521
 
 
522
        :param format: Optional parameter indicating a specific desired
 
523
                       format we plan to arrive at.
 
524
        """
 
525
        raise NotImplementedError(self.needs_format_conversion)
 
526
 
 
527
    @staticmethod
 
528
    def open_unsupported(base):
 
529
        """Open a branch which is not supported."""
 
530
        return BzrDir.open(base, _unsupported=True)
 
531
        
 
532
    @staticmethod
 
533
    def open(base, _unsupported=False):
 
534
        """Open an existing bzrdir, rooted at 'base' (url)
 
535
        
 
536
        _unsupported is a private parameter to the BzrDir class.
 
537
        """
 
538
        t = get_transport(base)
 
539
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
 
540
 
 
541
    @staticmethod
 
542
    def open_from_transport(transport, _unsupported=False):
 
543
        """Open a bzrdir within a particular directory.
 
544
 
 
545
        :param transport: Transport containing the bzrdir.
 
546
        :param _unsupported: private.
 
547
        """
 
548
        format = BzrDirFormat.find_format(transport)
 
549
        BzrDir._check_supported(format, _unsupported,
 
550
            basedir=transport.base)
 
551
        return format.open(transport, _found=True)
 
552
 
 
553
    def open_branch(self, unsupported=False):
 
554
        """Open the branch object at this BzrDir if one is present.
 
555
 
 
556
        If unsupported is True, then no longer supported branch formats can
 
557
        still be opened.
 
558
        
 
559
        TODO: static convenience version of this?
 
560
        """
 
561
        raise NotImplementedError(self.open_branch)
 
562
 
 
563
    @staticmethod
 
564
    def open_containing(url):
 
565
        """Open an existing branch which contains url.
 
566
        
 
567
        :param url: url to search from.
 
568
        See open_containing_from_transport for more detail.
 
569
        """
 
570
        return BzrDir.open_containing_from_transport(get_transport(url))
 
571
    
 
572
    @staticmethod
 
573
    def open_containing_from_transport(a_transport):
 
574
        """Open an existing branch which contains a_transport.base
 
575
 
 
576
        This probes for a branch at a_transport, and searches upwards from there.
 
577
 
 
578
        Basically we keep looking up until we find the control directory or
 
579
        run into the root.  If there isn't one, raises NotBranchError.
 
580
        If there is one and it is either an unrecognised format or an unsupported 
 
581
        format, UnknownFormatError or UnsupportedFormatError are raised.
 
582
        If there is one, it is returned, along with the unused portion of url.
 
583
 
 
584
        :return: The BzrDir that contains the path, and a Unicode path 
 
585
                for the rest of the URL.
 
586
        """
 
587
        # this gets the normalised url back. I.e. '.' -> the full path.
 
588
        url = a_transport.base
 
589
        while True:
 
590
            try:
 
591
                result = BzrDir.open_from_transport(a_transport)
 
592
                return result, urlutils.unescape(a_transport.relpath(url))
 
593
            except errors.NotBranchError, e:
 
594
                pass
 
595
            new_t = a_transport.clone('..')
 
596
            if new_t.base == a_transport.base:
 
597
                # reached the root, whatever that may be
 
598
                raise errors.NotBranchError(path=url)
 
599
            a_transport = new_t
 
600
 
 
601
    @classmethod
 
602
    def open_containing_tree_or_branch(klass, location):
 
603
        """Return the branch and working tree contained by a location.
 
604
 
 
605
        Returns (tree, branch, relpath).
 
606
        If there is no tree at containing the location, tree will be None.
 
607
        If there is no branch containing the location, an exception will be
 
608
        raised
 
609
        relpath is the portion of the path that is contained by the branch.
 
610
        """
 
611
        bzrdir, relpath = klass.open_containing(location)
 
612
        try:
 
613
            tree = bzrdir.open_workingtree()
 
614
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
615
            tree = None
 
616
            branch = bzrdir.open_branch()
 
617
        else:
 
618
            branch = tree.branch
 
619
        return tree, branch, relpath
 
620
 
 
621
    def open_repository(self, _unsupported=False):
 
622
        """Open the repository object at this BzrDir if one is present.
 
623
 
 
624
        This will not follow the Branch object pointer - its strictly a direct
 
625
        open facility. Most client code should use open_branch().repository to
 
626
        get at a repository.
 
627
 
 
628
        _unsupported is a private parameter, not part of the api.
 
629
        TODO: static convenience version of this?
 
630
        """
 
631
        raise NotImplementedError(self.open_repository)
 
632
 
 
633
    def open_workingtree(self, _unsupported=False):
 
634
        """Open the workingtree object at this BzrDir if one is present.
 
635
        
 
636
        TODO: static convenience version of this?
 
637
        """
 
638
        raise NotImplementedError(self.open_workingtree)
 
639
 
 
640
    def has_branch(self):
 
641
        """Tell if this bzrdir contains a branch.
 
642
        
 
643
        Note: if you're going to open the branch, you should just go ahead
 
644
        and try, and not ask permission first.  (This method just opens the 
 
645
        branch and discards it, and that's somewhat expensive.) 
 
646
        """
 
647
        try:
 
648
            self.open_branch()
 
649
            return True
 
650
        except errors.NotBranchError:
 
651
            return False
 
652
 
 
653
    def has_workingtree(self):
 
654
        """Tell if this bzrdir contains a working tree.
 
655
 
 
656
        This will still raise an exception if the bzrdir has a workingtree that
 
657
        is remote & inaccessible.
 
658
        
 
659
        Note: if you're going to open the working tree, you should just go ahead
 
660
        and try, and not ask permission first.  (This method just opens the 
 
661
        workingtree and discards it, and that's somewhat expensive.) 
 
662
        """
 
663
        try:
 
664
            self.open_workingtree(recommend_upgrade=False)
 
665
            return True
 
666
        except errors.NoWorkingTree:
 
667
            return False
 
668
 
 
669
    def _cloning_metadir(self, basis=None):
 
670
        def related_repository(bzrdir):
 
671
            try:
 
672
                branch = bzrdir.open_branch()
 
673
                return branch.repository
 
674
            except errors.NotBranchError:
 
675
                source_branch = None
 
676
                return bzrdir.open_repository()
 
677
        result_format = self._format.__class__()
 
678
        try:
 
679
            try:
 
680
                source_repository = related_repository(self)
 
681
            except errors.NoRepositoryPresent:
 
682
                if basis is None:
 
683
                    raise
 
684
                source_repository = related_repository(self)
 
685
            result_format.repository_format = source_repository._format
 
686
        except errors.NoRepositoryPresent:
 
687
            source_repository = None
 
688
        try:
 
689
            tree = self.open_workingtree()
 
690
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
691
            result_format.workingtree_format = None
 
692
        else:
 
693
            result_format.workingtree_format = tree._format.__class__()
 
694
        return result_format, source_repository
 
695
 
 
696
    def cloning_metadir(self, basis=None):
 
697
        """Produce a metadir suitable for cloning or sprouting with.
 
698
 
 
699
        These operations may produce workingtrees (yes, even though they're
 
700
        "cloning" something that doesn't have a tree, so a viable workingtree
 
701
        format must be selected.
 
702
        """
 
703
        format, repository = self._cloning_metadir()
 
704
        if format._workingtree_format is None:
 
705
            if repository is None:
 
706
                return format
 
707
            tree_format = repository._format._matchingbzrdir.workingtree_format
 
708
            format.workingtree_format = tree_format.__class__()
 
709
        return format
 
710
 
 
711
    def checkout_metadir(self):
 
712
        return self.cloning_metadir()
 
713
 
 
714
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False,
 
715
               recurse='down'):
 
716
        """Create a copy of this bzrdir prepared for use as a new line of
 
717
        development.
 
718
 
 
719
        If urls last component does not exist, it will be created.
 
720
 
 
721
        Attributes related to the identity of the source branch like
 
722
        branch nickname will be cleaned, a working tree is created
 
723
        whether one existed before or not; and a local branch is always
 
724
        created.
 
725
 
 
726
        if revision_id is not None, then the clone operation may tune
 
727
            itself to download less data.
 
728
        """
 
729
        self._make_tail(url)
 
730
        cloning_format = self.cloning_metadir(basis)
 
731
        result = cloning_format.initialize(url)
 
732
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
733
        try:
 
734
            source_branch = self.open_branch()
 
735
            source_repository = source_branch.repository
 
736
        except errors.NotBranchError:
 
737
            source_branch = None
 
738
            try:
 
739
                source_repository = self.open_repository()
 
740
            except errors.NoRepositoryPresent:
 
741
                # copy the entire basis one if there is one
 
742
                # but there is no repository.
 
743
                source_repository = basis_repo
 
744
        if force_new_repo:
 
745
            result_repo = None
 
746
        else:
 
747
            try:
 
748
                result_repo = result.find_repository()
 
749
            except errors.NoRepositoryPresent:
 
750
                result_repo = None
 
751
        if source_repository is None and result_repo is not None:
 
752
            pass
 
753
        elif source_repository is None and result_repo is None:
 
754
            # no repo available, make a new one
 
755
            result.create_repository()
 
756
        elif source_repository is not None and result_repo is None:
 
757
            # have source, and want to make a new target repo
 
758
            # we don't clone the repo because that preserves attributes
 
759
            # like is_shared(), and we have not yet implemented a 
 
760
            # repository sprout().
 
761
            result_repo = result.create_repository()
 
762
        if result_repo is not None:
 
763
            # fetch needed content into target.
 
764
            if basis_repo:
 
765
                # XXX FIXME RBC 20060214 need tests for this when the basis
 
766
                # is incomplete
 
767
                result_repo.fetch(basis_repo, revision_id=revision_id)
 
768
            if source_repository is not None:
 
769
                result_repo.fetch(source_repository, revision_id=revision_id)
 
770
        if source_branch is not None:
 
771
            source_branch.sprout(result, revision_id=revision_id)
 
772
        else:
 
773
            result.create_branch()
 
774
        # TODO: jam 20060426 we probably need a test in here in the
 
775
        #       case that the newly sprouted branch is a remote one
 
776
        if result_repo is None or result_repo.make_working_trees():
 
777
            wt = result.create_workingtree()
 
778
            wt.lock_write()
 
779
            try:
 
780
                if wt.path2id('') is None:
 
781
                    try:
 
782
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
783
                    except errors.NoWorkingTree:
 
784
                        pass
 
785
            finally:
 
786
                wt.unlock()
 
787
        else:
 
788
            wt = None
 
789
        if recurse == 'down':
 
790
            if wt is not None:
 
791
                basis = wt.basis_tree()
 
792
                basis.lock_read()
 
793
                subtrees = basis.iter_references()
 
794
                recurse_branch = wt.branch
 
795
            elif source_branch is not None:
 
796
                basis = source_branch.basis_tree()
 
797
                basis.lock_read()
 
798
                subtrees = basis.iter_references()
 
799
                recurse_branch = source_branch
 
800
            else:
 
801
                subtrees = []
 
802
                basis = None
 
803
            try:
 
804
                for path, file_id in subtrees:
 
805
                    target = urlutils.join(url, urlutils.escape(path))
 
806
                    sublocation = source_branch.reference_parent(file_id, path)
 
807
                    sublocation.bzrdir.sprout(target,
 
808
                        basis.get_reference_revision(file_id, path),
 
809
                        force_new_repo=force_new_repo, recurse=recurse)
 
810
            finally:
 
811
                if basis is not None:
 
812
                    basis.unlock()
 
813
        return result
 
814
 
 
815
 
 
816
class BzrDirPreSplitOut(BzrDir):
 
817
    """A common class for the all-in-one formats."""
 
818
 
 
819
    def __init__(self, _transport, _format):
 
820
        """See BzrDir.__init__."""
 
821
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
 
822
        assert self._format._lock_class == lockable_files.TransportLock
 
823
        assert self._format._lock_file_name == 'branch-lock'
 
824
        self._control_files = lockable_files.LockableFiles(
 
825
                                            self.get_branch_transport(None),
 
826
                                            self._format._lock_file_name,
 
827
                                            self._format._lock_class)
 
828
 
 
829
    def break_lock(self):
 
830
        """Pre-splitout bzrdirs do not suffer from stale locks."""
 
831
        raise NotImplementedError(self.break_lock)
 
832
 
 
833
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
834
        """See BzrDir.clone()."""
 
835
        from bzrlib.workingtree import WorkingTreeFormat2
 
836
        self._make_tail(url)
 
837
        result = self._format._initialize_for_clone(url)
 
838
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
839
        self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
840
        from_branch = self.open_branch()
 
841
        from_branch.clone(result, revision_id=revision_id)
 
842
        try:
 
843
            self.open_workingtree().clone(result, basis=basis_tree)
 
844
        except errors.NotLocalUrl:
 
845
            # make a new one, this format always has to have one.
 
846
            try:
 
847
                WorkingTreeFormat2().initialize(result)
 
848
            except errors.NotLocalUrl:
 
849
                # but we cannot do it for remote trees.
 
850
                to_branch = result.open_branch()
 
851
                WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
 
852
        return result
 
853
 
 
854
    def create_branch(self):
 
855
        """See BzrDir.create_branch."""
 
856
        return self.open_branch()
 
857
 
 
858
    def create_repository(self, shared=False):
 
859
        """See BzrDir.create_repository."""
 
860
        if shared:
 
861
            raise errors.IncompatibleFormat('shared repository', self._format)
 
862
        return self.open_repository()
 
863
 
 
864
    def create_workingtree(self, revision_id=None):
 
865
        """See BzrDir.create_workingtree."""
 
866
        # this looks buggy but is not -really-
 
867
        # because this format creates the workingtree when the bzrdir is
 
868
        # created
 
869
        # clone and sprout will have set the revision_id
 
870
        # and that will have set it for us, its only
 
871
        # specific uses of create_workingtree in isolation
 
872
        # that can do wonky stuff here, and that only
 
873
        # happens for creating checkouts, which cannot be 
 
874
        # done on this format anyway. So - acceptable wart.
 
875
        result = self.open_workingtree(recommend_upgrade=False)
 
876
        if revision_id is not None:
 
877
            if revision_id == _mod_revision.NULL_REVISION:
 
878
                result.set_parent_ids([])
 
879
            else:
 
880
                result.set_parent_ids([revision_id])
 
881
        return result
 
882
 
 
883
    def destroy_workingtree(self):
 
884
        """See BzrDir.destroy_workingtree."""
 
885
        raise errors.UnsupportedOperation(self.destroy_workingtree, self)
 
886
 
 
887
    def destroy_workingtree_metadata(self):
 
888
        """See BzrDir.destroy_workingtree_metadata."""
 
889
        raise errors.UnsupportedOperation(self.destroy_workingtree_metadata, 
 
890
                                          self)
 
891
 
 
892
    def get_branch_transport(self, branch_format):
 
893
        """See BzrDir.get_branch_transport()."""
 
894
        if branch_format is None:
 
895
            return self.transport
 
896
        try:
 
897
            branch_format.get_format_string()
 
898
        except NotImplementedError:
 
899
            return self.transport
 
900
        raise errors.IncompatibleFormat(branch_format, self._format)
 
901
 
 
902
    def get_repository_transport(self, repository_format):
 
903
        """See BzrDir.get_repository_transport()."""
 
904
        if repository_format is None:
 
905
            return self.transport
 
906
        try:
 
907
            repository_format.get_format_string()
 
908
        except NotImplementedError:
 
909
            return self.transport
 
910
        raise errors.IncompatibleFormat(repository_format, self._format)
 
911
 
 
912
    def get_workingtree_transport(self, workingtree_format):
 
913
        """See BzrDir.get_workingtree_transport()."""
 
914
        if workingtree_format is None:
 
915
            return self.transport
 
916
        try:
 
917
            workingtree_format.get_format_string()
 
918
        except NotImplementedError:
 
919
            return self.transport
 
920
        raise errors.IncompatibleFormat(workingtree_format, self._format)
 
921
 
 
922
    def needs_format_conversion(self, format=None):
 
923
        """See BzrDir.needs_format_conversion()."""
 
924
        # if the format is not the same as the system default,
 
925
        # an upgrade is needed.
 
926
        if format is None:
 
927
            format = BzrDirFormat.get_default_format()
 
928
        return not isinstance(self._format, format.__class__)
 
929
 
 
930
    def open_branch(self, unsupported=False):
 
931
        """See BzrDir.open_branch."""
 
932
        from bzrlib.branch import BzrBranchFormat4
 
933
        format = BzrBranchFormat4()
 
934
        self._check_supported(format, unsupported)
 
935
        return format.open(self, _found=True)
 
936
 
 
937
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
938
        """See BzrDir.sprout()."""
 
939
        from bzrlib.workingtree import WorkingTreeFormat2
 
940
        self._make_tail(url)
 
941
        result = self._format._initialize_for_clone(url)
 
942
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
943
        try:
 
944
            self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
945
        except errors.NoRepositoryPresent:
 
946
            pass
 
947
        try:
 
948
            self.open_branch().sprout(result, revision_id=revision_id)
 
949
        except errors.NotBranchError:
 
950
            pass
 
951
        # we always want a working tree
 
952
        WorkingTreeFormat2().initialize(result)
 
953
        return result
 
954
 
 
955
 
 
956
class BzrDir4(BzrDirPreSplitOut):
 
957
    """A .bzr version 4 control object.
 
958
    
 
959
    This is a deprecated format and may be removed after sept 2006.
 
960
    """
 
961
 
 
962
    def create_repository(self, shared=False):
 
963
        """See BzrDir.create_repository."""
 
964
        return self._format.repository_format.initialize(self, shared)
 
965
 
 
966
    def needs_format_conversion(self, format=None):
 
967
        """Format 4 dirs are always in need of conversion."""
 
968
        return True
 
969
 
 
970
    def open_repository(self):
 
971
        """See BzrDir.open_repository."""
 
972
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
 
973
        return RepositoryFormat4().open(self, _found=True)
 
974
 
 
975
 
 
976
class BzrDir5(BzrDirPreSplitOut):
 
977
    """A .bzr version 5 control object.
 
978
 
 
979
    This is a deprecated format and may be removed after sept 2006.
 
980
    """
 
981
 
 
982
    def open_repository(self):
 
983
        """See BzrDir.open_repository."""
 
984
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
 
985
        return RepositoryFormat5().open(self, _found=True)
 
986
 
 
987
    def open_workingtree(self, _unsupported=False,
 
988
            recommend_upgrade=True):
 
989
        """See BzrDir.create_workingtree."""
 
990
        from bzrlib.workingtree import WorkingTreeFormat2
 
991
        wt_format = WorkingTreeFormat2()
 
992
        # we don't warn here about upgrades; that ought to be handled for the
 
993
        # bzrdir as a whole
 
994
        return wt_format.open(self, _found=True)
 
995
 
 
996
 
 
997
class BzrDir6(BzrDirPreSplitOut):
 
998
    """A .bzr version 6 control object.
 
999
 
 
1000
    This is a deprecated format and may be removed after sept 2006.
 
1001
    """
 
1002
 
 
1003
    def open_repository(self):
 
1004
        """See BzrDir.open_repository."""
 
1005
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
 
1006
        return RepositoryFormat6().open(self, _found=True)
 
1007
 
 
1008
    def open_workingtree(self, _unsupported=False,
 
1009
        recommend_upgrade=True):
 
1010
        """See BzrDir.create_workingtree."""
 
1011
        # we don't warn here about upgrades; that ought to be handled for the
 
1012
        # bzrdir as a whole
 
1013
        from bzrlib.workingtree import WorkingTreeFormat2
 
1014
        return WorkingTreeFormat2().open(self, _found=True)
 
1015
 
 
1016
 
 
1017
class BzrDirMeta1(BzrDir):
 
1018
    """A .bzr meta version 1 control object.
 
1019
    
 
1020
    This is the first control object where the 
 
1021
    individual aspects are really split out: there are separate repository,
 
1022
    workingtree and branch subdirectories and any subset of the three can be
 
1023
    present within a BzrDir.
 
1024
    """
 
1025
 
 
1026
    def can_convert_format(self):
 
1027
        """See BzrDir.can_convert_format()."""
 
1028
        return True
 
1029
 
 
1030
    def create_branch(self):
 
1031
        """See BzrDir.create_branch."""
 
1032
        return self._format.get_branch_format().initialize(self)
 
1033
 
 
1034
    def create_repository(self, shared=False):
 
1035
        """See BzrDir.create_repository."""
 
1036
        return self._format.repository_format.initialize(self, shared)
 
1037
 
 
1038
    def create_workingtree(self, revision_id=None):
 
1039
        """See BzrDir.create_workingtree."""
 
1040
        from bzrlib.workingtree import WorkingTreeFormat
 
1041
        return self._format.workingtree_format.initialize(self, revision_id)
 
1042
 
 
1043
    def destroy_workingtree(self):
 
1044
        """See BzrDir.destroy_workingtree."""
 
1045
        wt = self.open_workingtree(recommend_upgrade=False)
 
1046
        repository = wt.branch.repository
 
1047
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1048
        wt.revert([], old_tree=empty)
 
1049
        self.destroy_workingtree_metadata()
 
1050
 
 
1051
    def destroy_workingtree_metadata(self):
 
1052
        self.transport.delete_tree('checkout')
 
1053
 
 
1054
    def _get_mkdir_mode(self):
 
1055
        """Figure out the mode to use when creating a bzrdir subdir."""
 
1056
        temp_control = lockable_files.LockableFiles(self.transport, '',
 
1057
                                     lockable_files.TransportLock)
 
1058
        return temp_control._dir_mode
 
1059
 
 
1060
    def get_branch_transport(self, branch_format):
 
1061
        """See BzrDir.get_branch_transport()."""
 
1062
        if branch_format is None:
 
1063
            return self.transport.clone('branch')
 
1064
        try:
 
1065
            branch_format.get_format_string()
 
1066
        except NotImplementedError:
 
1067
            raise errors.IncompatibleFormat(branch_format, self._format)
 
1068
        try:
 
1069
            self.transport.mkdir('branch', mode=self._get_mkdir_mode())
 
1070
        except errors.FileExists:
 
1071
            pass
 
1072
        return self.transport.clone('branch')
 
1073
 
 
1074
    def get_repository_transport(self, repository_format):
 
1075
        """See BzrDir.get_repository_transport()."""
 
1076
        if repository_format is None:
 
1077
            return self.transport.clone('repository')
 
1078
        try:
 
1079
            repository_format.get_format_string()
 
1080
        except NotImplementedError:
 
1081
            raise errors.IncompatibleFormat(repository_format, self._format)
 
1082
        try:
 
1083
            self.transport.mkdir('repository', mode=self._get_mkdir_mode())
 
1084
        except errors.FileExists:
 
1085
            pass
 
1086
        return self.transport.clone('repository')
 
1087
 
 
1088
    def get_workingtree_transport(self, workingtree_format):
 
1089
        """See BzrDir.get_workingtree_transport()."""
 
1090
        if workingtree_format is None:
 
1091
            return self.transport.clone('checkout')
 
1092
        try:
 
1093
            workingtree_format.get_format_string()
 
1094
        except NotImplementedError:
 
1095
            raise errors.IncompatibleFormat(workingtree_format, self._format)
 
1096
        try:
 
1097
            self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
 
1098
        except errors.FileExists:
 
1099
            pass
 
1100
        return self.transport.clone('checkout')
 
1101
 
 
1102
    def needs_format_conversion(self, format=None):
 
1103
        """See BzrDir.needs_format_conversion()."""
 
1104
        if format is None:
 
1105
            format = BzrDirFormat.get_default_format()
 
1106
        if not isinstance(self._format, format.__class__):
 
1107
            # it is not a meta dir format, conversion is needed.
 
1108
            return True
 
1109
        # we might want to push this down to the repository?
 
1110
        try:
 
1111
            if not isinstance(self.open_repository()._format,
 
1112
                              format.repository_format.__class__):
 
1113
                # the repository needs an upgrade.
 
1114
                return True
 
1115
        except errors.NoRepositoryPresent:
 
1116
            pass
 
1117
        try:
 
1118
            if not isinstance(self.open_branch()._format,
 
1119
                              format.get_branch_format().__class__):
 
1120
                # the branch needs an upgrade.
 
1121
                return True
 
1122
        except errors.NotBranchError:
 
1123
            pass
 
1124
        try:
 
1125
            my_wt = self.open_workingtree(recommend_upgrade=False)
 
1126
            if not isinstance(my_wt._format,
 
1127
                              format.workingtree_format.__class__):
 
1128
                # the workingtree needs an upgrade.
 
1129
                return True
 
1130
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1131
            pass
 
1132
        return False
 
1133
 
 
1134
    def open_branch(self, unsupported=False):
 
1135
        """See BzrDir.open_branch."""
 
1136
        from bzrlib.branch import BranchFormat
 
1137
        format = BranchFormat.find_format(self)
 
1138
        self._check_supported(format, unsupported)
 
1139
        return format.open(self, _found=True)
 
1140
 
 
1141
    def open_repository(self, unsupported=False):
 
1142
        """See BzrDir.open_repository."""
 
1143
        from bzrlib.repository import RepositoryFormat
 
1144
        format = RepositoryFormat.find_format(self)
 
1145
        self._check_supported(format, unsupported)
 
1146
        return format.open(self, _found=True)
 
1147
 
 
1148
    def open_workingtree(self, unsupported=False,
 
1149
            recommend_upgrade=True):
 
1150
        """See BzrDir.open_workingtree."""
 
1151
        from bzrlib.workingtree import WorkingTreeFormat
 
1152
        format = WorkingTreeFormat.find_format(self)
 
1153
        self._check_supported(format, unsupported,
 
1154
            recommend_upgrade,
 
1155
            basedir=self.root_transport.base)
 
1156
        return format.open(self, _found=True)
 
1157
 
 
1158
 
 
1159
class BzrDirFormat(object):
 
1160
    """An encapsulation of the initialization and open routines for a format.
 
1161
 
 
1162
    Formats provide three things:
 
1163
     * An initialization routine,
 
1164
     * a format string,
 
1165
     * an open routine.
 
1166
 
 
1167
    Formats are placed in an dict by their format string for reference 
 
1168
    during bzrdir opening. These should be subclasses of BzrDirFormat
 
1169
    for consistency.
 
1170
 
 
1171
    Once a format is deprecated, just deprecate the initialize and open
 
1172
    methods on the format class. Do not deprecate the object, as the 
 
1173
    object will be created every system load.
 
1174
    """
 
1175
 
 
1176
    _default_format = None
 
1177
    """The default format used for new .bzr dirs."""
 
1178
 
 
1179
    _formats = {}
 
1180
    """The known formats."""
 
1181
 
 
1182
    _control_formats = []
 
1183
    """The registered control formats - .bzr, ....
 
1184
    
 
1185
    This is a list of BzrDirFormat objects.
 
1186
    """
 
1187
 
 
1188
    _lock_file_name = 'branch-lock'
 
1189
 
 
1190
    # _lock_class must be set in subclasses to the lock type, typ.
 
1191
    # TransportLock or LockDir
 
1192
 
 
1193
    @classmethod
 
1194
    def find_format(klass, transport):
 
1195
        """Return the format present at transport."""
 
1196
        for format in klass._control_formats:
 
1197
            try:
 
1198
                return format.probe_transport(transport)
 
1199
            except errors.NotBranchError:
 
1200
                # this format does not find a control dir here.
 
1201
                pass
 
1202
        raise errors.NotBranchError(path=transport.base)
 
1203
 
 
1204
    @classmethod
 
1205
    def probe_transport(klass, transport):
 
1206
        """Return the .bzrdir style transport present at URL."""
 
1207
        try:
 
1208
            format_string = transport.get(".bzr/branch-format").read()
 
1209
        except errors.NoSuchFile:
 
1210
            raise errors.NotBranchError(path=transport.base)
 
1211
 
 
1212
        try:
 
1213
            return klass._formats[format_string]
 
1214
        except KeyError:
 
1215
            raise errors.UnknownFormatError(format=format_string)
 
1216
 
 
1217
    @classmethod
 
1218
    def get_default_format(klass):
 
1219
        """Return the current default format."""
 
1220
        return klass._default_format
 
1221
 
 
1222
    def get_format_string(self):
 
1223
        """Return the ASCII format string that identifies this format."""
 
1224
        raise NotImplementedError(self.get_format_string)
 
1225
 
 
1226
    def get_format_description(self):
 
1227
        """Return the short description for this format."""
 
1228
        raise NotImplementedError(self.get_format_description)
 
1229
 
 
1230
    def get_converter(self, format=None):
 
1231
        """Return the converter to use to convert bzrdirs needing converts.
 
1232
 
 
1233
        This returns a bzrlib.bzrdir.Converter object.
 
1234
 
 
1235
        This should return the best upgrader to step this format towards the
 
1236
        current default format. In the case of plugins we can/should provide
 
1237
        some means for them to extend the range of returnable converters.
 
1238
 
 
1239
        :param format: Optional format to override the default format of the 
 
1240
                       library.
 
1241
        """
 
1242
        raise NotImplementedError(self.get_converter)
 
1243
 
 
1244
    def initialize(self, url):
 
1245
        """Create a bzr control dir at this url and return an opened copy.
 
1246
        
 
1247
        Subclasses should typically override initialize_on_transport
 
1248
        instead of this method.
 
1249
        """
 
1250
        return self.initialize_on_transport(get_transport(url))
 
1251
 
 
1252
    def initialize_on_transport(self, transport):
 
1253
        """Initialize a new bzrdir in the base directory of a Transport."""
 
1254
        # Since we don't have a .bzr directory, inherit the
 
1255
        # mode from the root directory
 
1256
        temp_control = lockable_files.LockableFiles(transport,
 
1257
                            '', lockable_files.TransportLock)
 
1258
        temp_control._transport.mkdir('.bzr',
 
1259
                                      # FIXME: RBC 20060121 don't peek under
 
1260
                                      # the covers
 
1261
                                      mode=temp_control._dir_mode)
 
1262
        file_mode = temp_control._file_mode
 
1263
        del temp_control
 
1264
        mutter('created control directory in ' + transport.base)
 
1265
        control = transport.clone('.bzr')
 
1266
        utf8_files = [('README', 
 
1267
                       "This is a Bazaar-NG control directory.\n"
 
1268
                       "Do not change any files in this directory.\n"),
 
1269
                      ('branch-format', self.get_format_string()),
 
1270
                      ]
 
1271
        # NB: no need to escape relative paths that are url safe.
 
1272
        control_files = lockable_files.LockableFiles(control,
 
1273
                            self._lock_file_name, self._lock_class)
 
1274
        control_files.create_lock()
 
1275
        control_files.lock_write()
 
1276
        try:
 
1277
            for file, content in utf8_files:
 
1278
                control_files.put_utf8(file, content)
 
1279
        finally:
 
1280
            control_files.unlock()
 
1281
        return self.open(transport, _found=True)
 
1282
 
 
1283
    def is_supported(self):
 
1284
        """Is this format supported?
 
1285
 
 
1286
        Supported formats must be initializable and openable.
 
1287
        Unsupported formats may not support initialization or committing or 
 
1288
        some other features depending on the reason for not being supported.
 
1289
        """
 
1290
        return True
 
1291
 
 
1292
    def same_model(self, target_format):
 
1293
        return (self.repository_format.rich_root_data == 
 
1294
            target_format.rich_root_data)
 
1295
 
 
1296
    @classmethod
 
1297
    def known_formats(klass):
 
1298
        """Return all the known formats.
 
1299
        
 
1300
        Concrete formats should override _known_formats.
 
1301
        """
 
1302
        # There is double indirection here to make sure that control 
 
1303
        # formats used by more than one dir format will only be probed 
 
1304
        # once. This can otherwise be quite expensive for remote connections.
 
1305
        result = set()
 
1306
        for format in klass._control_formats:
 
1307
            result.update(format._known_formats())
 
1308
        return result
 
1309
    
 
1310
    @classmethod
 
1311
    def _known_formats(klass):
 
1312
        """Return the known format instances for this control format."""
 
1313
        return set(klass._formats.values())
 
1314
 
 
1315
    def open(self, transport, _found=False):
 
1316
        """Return an instance of this format for the dir transport points at.
 
1317
        
 
1318
        _found is a private parameter, do not use it.
 
1319
        """
 
1320
        if not _found:
 
1321
            found_format = BzrDirFormat.find_format(transport)
 
1322
            if not isinstance(found_format, self.__class__):
 
1323
                raise AssertionError("%s was asked to open %s, but it seems to need "
 
1324
                        "format %s" 
 
1325
                        % (self, transport, found_format))
 
1326
        return self._open(transport)
 
1327
 
 
1328
    def _open(self, transport):
 
1329
        """Template method helper for opening BzrDirectories.
 
1330
 
 
1331
        This performs the actual open and any additional logic or parameter
 
1332
        passing.
 
1333
        """
 
1334
        raise NotImplementedError(self._open)
 
1335
 
 
1336
    @classmethod
 
1337
    def register_format(klass, format):
 
1338
        klass._formats[format.get_format_string()] = format
 
1339
 
 
1340
    @classmethod
 
1341
    def register_control_format(klass, format):
 
1342
        """Register a format that does not use '.bzrdir' for its control dir.
 
1343
 
 
1344
        TODO: This should be pulled up into a 'ControlDirFormat' base class
 
1345
        which BzrDirFormat can inherit from, and renamed to register_format 
 
1346
        there. It has been done without that for now for simplicity of
 
1347
        implementation.
 
1348
        """
 
1349
        klass._control_formats.append(format)
 
1350
 
 
1351
    @classmethod
 
1352
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
 
1353
    def set_default_format(klass, format):
 
1354
        klass._set_default_format(format)
 
1355
 
 
1356
    @classmethod
 
1357
    def _set_default_format(klass, format):
 
1358
        """Set default format (for testing behavior of defaults only)"""
 
1359
        klass._default_format = format
 
1360
 
 
1361
    def __str__(self):
 
1362
        return self.get_format_string()[:-1]
 
1363
 
 
1364
    @classmethod
 
1365
    def unregister_format(klass, format):
 
1366
        assert klass._formats[format.get_format_string()] is format
 
1367
        del klass._formats[format.get_format_string()]
 
1368
 
 
1369
    @classmethod
 
1370
    def unregister_control_format(klass, format):
 
1371
        klass._control_formats.remove(format)
 
1372
 
 
1373
 
 
1374
# register BzrDirFormat as a control format
 
1375
BzrDirFormat.register_control_format(BzrDirFormat)
 
1376
 
 
1377
 
 
1378
class BzrDirFormat4(BzrDirFormat):
 
1379
    """Bzr dir format 4.
 
1380
 
 
1381
    This format is a combined format for working tree, branch and repository.
 
1382
    It has:
 
1383
     - Format 1 working trees [always]
 
1384
     - Format 4 branches [always]
 
1385
     - Format 4 repositories [always]
 
1386
 
 
1387
    This format is deprecated: it indexes texts using a text it which is
 
1388
    removed in format 5; write support for this format has been removed.
 
1389
    """
 
1390
 
 
1391
    _lock_class = lockable_files.TransportLock
 
1392
 
 
1393
    def get_format_string(self):
 
1394
        """See BzrDirFormat.get_format_string()."""
 
1395
        return "Bazaar-NG branch, format 0.0.4\n"
 
1396
 
 
1397
    def get_format_description(self):
 
1398
        """See BzrDirFormat.get_format_description()."""
 
1399
        return "All-in-one format 4"
 
1400
 
 
1401
    def get_converter(self, format=None):
 
1402
        """See BzrDirFormat.get_converter()."""
 
1403
        # there is one and only one upgrade path here.
 
1404
        return ConvertBzrDir4To5()
 
1405
        
 
1406
    def initialize_on_transport(self, transport):
 
1407
        """Format 4 branches cannot be created."""
 
1408
        raise errors.UninitializableFormat(self)
 
1409
 
 
1410
    def is_supported(self):
 
1411
        """Format 4 is not supported.
 
1412
 
 
1413
        It is not supported because the model changed from 4 to 5 and the
 
1414
        conversion logic is expensive - so doing it on the fly was not 
 
1415
        feasible.
 
1416
        """
 
1417
        return False
 
1418
 
 
1419
    def _open(self, transport):
 
1420
        """See BzrDirFormat._open."""
 
1421
        return BzrDir4(transport, self)
 
1422
 
 
1423
    def __return_repository_format(self):
 
1424
        """Circular import protection."""
 
1425
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
 
1426
        return RepositoryFormat4()
 
1427
    repository_format = property(__return_repository_format)
 
1428
 
 
1429
 
 
1430
class BzrDirFormat5(BzrDirFormat):
 
1431
    """Bzr control format 5.
 
1432
 
 
1433
    This format is a combined format for working tree, branch and repository.
 
1434
    It has:
 
1435
     - Format 2 working trees [always] 
 
1436
     - Format 4 branches [always] 
 
1437
     - Format 5 repositories [always]
 
1438
       Unhashed stores in the repository.
 
1439
    """
 
1440
 
 
1441
    _lock_class = lockable_files.TransportLock
 
1442
 
 
1443
    def get_format_string(self):
 
1444
        """See BzrDirFormat.get_format_string()."""
 
1445
        return "Bazaar-NG branch, format 5\n"
 
1446
 
 
1447
    def get_format_description(self):
 
1448
        """See BzrDirFormat.get_format_description()."""
 
1449
        return "All-in-one format 5"
 
1450
 
 
1451
    def get_converter(self, format=None):
 
1452
        """See BzrDirFormat.get_converter()."""
 
1453
        # there is one and only one upgrade path here.
 
1454
        return ConvertBzrDir5To6()
 
1455
 
 
1456
    def _initialize_for_clone(self, url):
 
1457
        return self.initialize_on_transport(get_transport(url), _cloning=True)
 
1458
        
 
1459
    def initialize_on_transport(self, transport, _cloning=False):
 
1460
        """Format 5 dirs always have working tree, branch and repository.
 
1461
        
 
1462
        Except when they are being cloned.
 
1463
        """
 
1464
        from bzrlib.branch import BzrBranchFormat4
 
1465
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
 
1466
        from bzrlib.workingtree import WorkingTreeFormat2
 
1467
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
 
1468
        RepositoryFormat5().initialize(result, _internal=True)
 
1469
        if not _cloning:
 
1470
            branch = BzrBranchFormat4().initialize(result)
 
1471
            try:
 
1472
                WorkingTreeFormat2().initialize(result)
 
1473
            except errors.NotLocalUrl:
 
1474
                # Even though we can't access the working tree, we need to
 
1475
                # create its control files.
 
1476
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
 
1477
        return result
 
1478
 
 
1479
    def _open(self, transport):
 
1480
        """See BzrDirFormat._open."""
 
1481
        return BzrDir5(transport, self)
 
1482
 
 
1483
    def __return_repository_format(self):
 
1484
        """Circular import protection."""
 
1485
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
 
1486
        return RepositoryFormat5()
 
1487
    repository_format = property(__return_repository_format)
 
1488
 
 
1489
 
 
1490
class BzrDirFormat6(BzrDirFormat):
 
1491
    """Bzr control format 6.
 
1492
 
 
1493
    This format is a combined format for working tree, branch and repository.
 
1494
    It has:
 
1495
     - Format 2 working trees [always] 
 
1496
     - Format 4 branches [always] 
 
1497
     - Format 6 repositories [always]
 
1498
    """
 
1499
 
 
1500
    _lock_class = lockable_files.TransportLock
 
1501
 
 
1502
    def get_format_string(self):
 
1503
        """See BzrDirFormat.get_format_string()."""
 
1504
        return "Bazaar-NG branch, format 6\n"
 
1505
 
 
1506
    def get_format_description(self):
 
1507
        """See BzrDirFormat.get_format_description()."""
 
1508
        return "All-in-one format 6"
 
1509
 
 
1510
    def get_converter(self, format=None):
 
1511
        """See BzrDirFormat.get_converter()."""
 
1512
        # there is one and only one upgrade path here.
 
1513
        return ConvertBzrDir6ToMeta()
 
1514
        
 
1515
    def _initialize_for_clone(self, url):
 
1516
        return self.initialize_on_transport(get_transport(url), _cloning=True)
 
1517
 
 
1518
    def initialize_on_transport(self, transport, _cloning=False):
 
1519
        """Format 6 dirs always have working tree, branch and repository.
 
1520
        
 
1521
        Except when they are being cloned.
 
1522
        """
 
1523
        from bzrlib.branch import BzrBranchFormat4
 
1524
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
 
1525
        from bzrlib.workingtree import WorkingTreeFormat2
 
1526
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
 
1527
        RepositoryFormat6().initialize(result, _internal=True)
 
1528
        if not _cloning:
 
1529
            branch = BzrBranchFormat4().initialize(result)
 
1530
            try:
 
1531
                WorkingTreeFormat2().initialize(result)
 
1532
            except errors.NotLocalUrl:
 
1533
                # Even though we can't access the working tree, we need to
 
1534
                # create its control files.
 
1535
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
 
1536
        return result
 
1537
 
 
1538
    def _open(self, transport):
 
1539
        """See BzrDirFormat._open."""
 
1540
        return BzrDir6(transport, self)
 
1541
 
 
1542
    def __return_repository_format(self):
 
1543
        """Circular import protection."""
 
1544
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
 
1545
        return RepositoryFormat6()
 
1546
    repository_format = property(__return_repository_format)
 
1547
 
 
1548
 
 
1549
class BzrDirMetaFormat1(BzrDirFormat):
 
1550
    """Bzr meta control format 1
 
1551
 
 
1552
    This is the first format with split out working tree, branch and repository
 
1553
    disk storage.
 
1554
    It has:
 
1555
     - Format 3 working trees [optional]
 
1556
     - Format 5 branches [optional]
 
1557
     - Format 7 repositories [optional]
 
1558
    """
 
1559
 
 
1560
    _lock_class = lockdir.LockDir
 
1561
 
 
1562
    def __init__(self):
 
1563
        self._workingtree_format = None
 
1564
        self._branch_format = None
 
1565
 
 
1566
    def __eq__(self, other):
 
1567
        if other.__class__ is not self.__class__:
 
1568
            return False
 
1569
        if other.repository_format != self.repository_format:
 
1570
            return False
 
1571
        if other.workingtree_format != self.workingtree_format:
 
1572
            return False
 
1573
        return True
 
1574
 
 
1575
    def __ne__(self, other):
 
1576
        return not self == other
 
1577
 
 
1578
    def get_branch_format(self):
 
1579
        if self._branch_format is None:
 
1580
            from bzrlib.branch import BranchFormat
 
1581
            self._branch_format = BranchFormat.get_default_format()
 
1582
        return self._branch_format
 
1583
 
 
1584
    def set_branch_format(self, format):
 
1585
        self._branch_format = format
 
1586
 
 
1587
    def get_converter(self, format=None):
 
1588
        """See BzrDirFormat.get_converter()."""
 
1589
        if format is None:
 
1590
            format = BzrDirFormat.get_default_format()
 
1591
        if not isinstance(self, format.__class__):
 
1592
            # converting away from metadir is not implemented
 
1593
            raise NotImplementedError(self.get_converter)
 
1594
        return ConvertMetaToMeta(format)
 
1595
 
 
1596
    def get_format_string(self):
 
1597
        """See BzrDirFormat.get_format_string()."""
 
1598
        return "Bazaar-NG meta directory, format 1\n"
 
1599
 
 
1600
    def get_format_description(self):
 
1601
        """See BzrDirFormat.get_format_description()."""
 
1602
        return "Meta directory format 1"
 
1603
 
 
1604
    def _open(self, transport):
 
1605
        """See BzrDirFormat._open."""
 
1606
        return BzrDirMeta1(transport, self)
 
1607
 
 
1608
    def __return_repository_format(self):
 
1609
        """Circular import protection."""
 
1610
        if getattr(self, '_repository_format', None):
 
1611
            return self._repository_format
 
1612
        from bzrlib.repository import RepositoryFormat
 
1613
        return RepositoryFormat.get_default_format()
 
1614
 
 
1615
    def __set_repository_format(self, value):
 
1616
        """Allow changint the repository format for metadir formats."""
 
1617
        self._repository_format = value
 
1618
 
 
1619
    repository_format = property(__return_repository_format, __set_repository_format)
 
1620
 
 
1621
    def __get_workingtree_format(self):
 
1622
        if self._workingtree_format is None:
 
1623
            from bzrlib.workingtree import WorkingTreeFormat
 
1624
            self._workingtree_format = WorkingTreeFormat.get_default_format()
 
1625
        return self._workingtree_format
 
1626
 
 
1627
    def __set_workingtree_format(self, wt_format):
 
1628
        self._workingtree_format = wt_format
 
1629
 
 
1630
    workingtree_format = property(__get_workingtree_format,
 
1631
                                  __set_workingtree_format)
 
1632
 
 
1633
 
 
1634
BzrDirFormat.register_format(BzrDirFormat4())
 
1635
BzrDirFormat.register_format(BzrDirFormat5())
 
1636
BzrDirFormat.register_format(BzrDirFormat6())
 
1637
__default_format = BzrDirMetaFormat1()
 
1638
BzrDirFormat.register_format(__default_format)
 
1639
BzrDirFormat._default_format = __default_format
 
1640
 
 
1641
 
 
1642
class BzrDirTestProviderAdapter(object):
 
1643
    """A tool to generate a suite testing multiple bzrdir formats at once.
 
1644
 
 
1645
    This is done by copying the test once for each transport and injecting
 
1646
    the transport_server, transport_readonly_server, and bzrdir_format
 
1647
    classes into each copy. Each copy is also given a new id() to make it
 
1648
    easy to identify.
 
1649
    """
 
1650
 
 
1651
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1652
        self._transport_server = transport_server
 
1653
        self._transport_readonly_server = transport_readonly_server
 
1654
        self._formats = formats
 
1655
    
 
1656
    def adapt(self, test):
 
1657
        result = unittest.TestSuite()
 
1658
        for format in self._formats:
 
1659
            new_test = deepcopy(test)
 
1660
            new_test.transport_server = self._transport_server
 
1661
            new_test.transport_readonly_server = self._transport_readonly_server
 
1662
            new_test.bzrdir_format = format
 
1663
            def make_new_test_id():
 
1664
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
 
1665
                return lambda: new_id
 
1666
            new_test.id = make_new_test_id()
 
1667
            result.addTest(new_test)
 
1668
        return result
 
1669
 
 
1670
 
 
1671
class Converter(object):
 
1672
    """Converts a disk format object from one format to another."""
 
1673
 
 
1674
    def convert(self, to_convert, pb):
 
1675
        """Perform the conversion of to_convert, giving feedback via pb.
 
1676
 
 
1677
        :param to_convert: The disk object to convert.
 
1678
        :param pb: a progress bar to use for progress information.
 
1679
        """
 
1680
 
 
1681
    def step(self, message):
 
1682
        """Update the pb by a step."""
 
1683
        self.count +=1
 
1684
        self.pb.update(message, self.count, self.total)
 
1685
 
 
1686
 
 
1687
class ConvertBzrDir4To5(Converter):
 
1688
    """Converts format 4 bzr dirs to format 5."""
 
1689
 
 
1690
    def __init__(self):
 
1691
        super(ConvertBzrDir4To5, self).__init__()
 
1692
        self.converted_revs = set()
 
1693
        self.absent_revisions = set()
 
1694
        self.text_count = 0
 
1695
        self.revisions = {}
 
1696
        
 
1697
    def convert(self, to_convert, pb):
 
1698
        """See Converter.convert()."""
 
1699
        self.bzrdir = to_convert
 
1700
        self.pb = pb
 
1701
        self.pb.note('starting upgrade from format 4 to 5')
 
1702
        if isinstance(self.bzrdir.transport, LocalTransport):
 
1703
            self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
 
1704
        self._convert_to_weaves()
 
1705
        return BzrDir.open(self.bzrdir.root_transport.base)
 
1706
 
 
1707
    def _convert_to_weaves(self):
 
1708
        self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
 
1709
        try:
 
1710
            # TODO permissions
 
1711
            stat = self.bzrdir.transport.stat('weaves')
 
1712
            if not S_ISDIR(stat.st_mode):
 
1713
                self.bzrdir.transport.delete('weaves')
 
1714
                self.bzrdir.transport.mkdir('weaves')
 
1715
        except errors.NoSuchFile:
 
1716
            self.bzrdir.transport.mkdir('weaves')
 
1717
        # deliberately not a WeaveFile as we want to build it up slowly.
 
1718
        self.inv_weave = Weave('inventory')
 
1719
        # holds in-memory weaves for all files
 
1720
        self.text_weaves = {}
 
1721
        self.bzrdir.transport.delete('branch-format')
 
1722
        self.branch = self.bzrdir.open_branch()
 
1723
        self._convert_working_inv()
 
1724
        rev_history = self.branch.revision_history()
 
1725
        # to_read is a stack holding the revisions we still need to process;
 
1726
        # appending to it adds new highest-priority revisions
 
1727
        self.known_revisions = set(rev_history)
 
1728
        self.to_read = rev_history[-1:]
 
1729
        while self.to_read:
 
1730
            rev_id = self.to_read.pop()
 
1731
            if (rev_id not in self.revisions
 
1732
                and rev_id not in self.absent_revisions):
 
1733
                self._load_one_rev(rev_id)
 
1734
        self.pb.clear()
 
1735
        to_import = self._make_order()
 
1736
        for i, rev_id in enumerate(to_import):
 
1737
            self.pb.update('converting revision', i, len(to_import))
 
1738
            self._convert_one_rev(rev_id)
 
1739
        self.pb.clear()
 
1740
        self._write_all_weaves()
 
1741
        self._write_all_revs()
 
1742
        self.pb.note('upgraded to weaves:')
 
1743
        self.pb.note('  %6d revisions and inventories', len(self.revisions))
 
1744
        self.pb.note('  %6d revisions not present', len(self.absent_revisions))
 
1745
        self.pb.note('  %6d texts', self.text_count)
 
1746
        self._cleanup_spare_files_after_format4()
 
1747
        self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
 
1748
 
 
1749
    def _cleanup_spare_files_after_format4(self):
 
1750
        # FIXME working tree upgrade foo.
 
1751
        for n in 'merged-patches', 'pending-merged-patches':
 
1752
            try:
 
1753
                ## assert os.path.getsize(p) == 0
 
1754
                self.bzrdir.transport.delete(n)
 
1755
            except errors.NoSuchFile:
 
1756
                pass
 
1757
        self.bzrdir.transport.delete_tree('inventory-store')
 
1758
        self.bzrdir.transport.delete_tree('text-store')
 
1759
 
 
1760
    def _convert_working_inv(self):
 
1761
        inv = xml4.serializer_v4.read_inventory(
 
1762
                    self.branch.control_files.get('inventory'))
 
1763
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
 
1764
        # FIXME inventory is a working tree change.
 
1765
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
 
1766
 
 
1767
    def _write_all_weaves(self):
 
1768
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
 
1769
        weave_transport = self.bzrdir.transport.clone('weaves')
 
1770
        weaves = WeaveStore(weave_transport, prefixed=False)
 
1771
        transaction = WriteTransaction()
 
1772
 
 
1773
        try:
 
1774
            i = 0
 
1775
            for file_id, file_weave in self.text_weaves.items():
 
1776
                self.pb.update('writing weave', i, len(self.text_weaves))
 
1777
                weaves._put_weave(file_id, file_weave, transaction)
 
1778
                i += 1
 
1779
            self.pb.update('inventory', 0, 1)
 
1780
            controlweaves._put_weave('inventory', self.inv_weave, transaction)
 
1781
            self.pb.update('inventory', 1, 1)
 
1782
        finally:
 
1783
            self.pb.clear()
 
1784
 
 
1785
    def _write_all_revs(self):
 
1786
        """Write all revisions out in new form."""
 
1787
        self.bzrdir.transport.delete_tree('revision-store')
 
1788
        self.bzrdir.transport.mkdir('revision-store')
 
1789
        revision_transport = self.bzrdir.transport.clone('revision-store')
 
1790
        # TODO permissions
 
1791
        _revision_store = TextRevisionStore(TextStore(revision_transport,
 
1792
                                                      prefixed=False,
 
1793
                                                      compressed=True))
 
1794
        try:
 
1795
            transaction = WriteTransaction()
 
1796
            for i, rev_id in enumerate(self.converted_revs):
 
1797
                self.pb.update('write revision', i, len(self.converted_revs))
 
1798
                _revision_store.add_revision(self.revisions[rev_id], transaction)
 
1799
        finally:
 
1800
            self.pb.clear()
 
1801
            
 
1802
    def _load_one_rev(self, rev_id):
 
1803
        """Load a revision object into memory.
 
1804
 
 
1805
        Any parents not either loaded or abandoned get queued to be
 
1806
        loaded."""
 
1807
        self.pb.update('loading revision',
 
1808
                       len(self.revisions),
 
1809
                       len(self.known_revisions))
 
1810
        if not self.branch.repository.has_revision(rev_id):
 
1811
            self.pb.clear()
 
1812
            self.pb.note('revision {%s} not present in branch; '
 
1813
                         'will be converted as a ghost',
 
1814
                         rev_id)
 
1815
            self.absent_revisions.add(rev_id)
 
1816
        else:
 
1817
            rev = self.branch.repository._revision_store.get_revision(rev_id,
 
1818
                self.branch.repository.get_transaction())
 
1819
            for parent_id in rev.parent_ids:
 
1820
                self.known_revisions.add(parent_id)
 
1821
                self.to_read.append(parent_id)
 
1822
            self.revisions[rev_id] = rev
 
1823
 
 
1824
    def _load_old_inventory(self, rev_id):
 
1825
        assert rev_id not in self.converted_revs
 
1826
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
 
1827
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
 
1828
        inv.revision_id = rev_id
 
1829
        rev = self.revisions[rev_id]
 
1830
        if rev.inventory_sha1:
 
1831
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
 
1832
                'inventory sha mismatch for {%s}' % rev_id
 
1833
        return inv
 
1834
 
 
1835
    def _load_updated_inventory(self, rev_id):
 
1836
        assert rev_id in self.converted_revs
 
1837
        inv_xml = self.inv_weave.get_text(rev_id)
 
1838
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
1839
        return inv
 
1840
 
 
1841
    def _convert_one_rev(self, rev_id):
 
1842
        """Convert revision and all referenced objects to new format."""
 
1843
        rev = self.revisions[rev_id]
 
1844
        inv = self._load_old_inventory(rev_id)
 
1845
        present_parents = [p for p in rev.parent_ids
 
1846
                           if p not in self.absent_revisions]
 
1847
        self._convert_revision_contents(rev, inv, present_parents)
 
1848
        self._store_new_weave(rev, inv, present_parents)
 
1849
        self.converted_revs.add(rev_id)
 
1850
 
 
1851
    def _store_new_weave(self, rev, inv, present_parents):
 
1852
        # the XML is now updated with text versions
 
1853
        if __debug__:
 
1854
            entries = inv.iter_entries()
 
1855
            entries.next()
 
1856
            for path, ie in entries:
 
1857
                assert getattr(ie, 'revision', None) is not None, \
 
1858
                    'no revision on {%s} in {%s}' % \
 
1859
                    (file_id, rev.revision_id)
 
1860
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
 
1861
        new_inv_sha1 = sha_string(new_inv_xml)
 
1862
        self.inv_weave.add_lines(rev.revision_id, 
 
1863
                                 present_parents,
 
1864
                                 new_inv_xml.splitlines(True))
 
1865
        rev.inventory_sha1 = new_inv_sha1
 
1866
 
 
1867
    def _convert_revision_contents(self, rev, inv, present_parents):
 
1868
        """Convert all the files within a revision.
 
1869
 
 
1870
        Also upgrade the inventory to refer to the text revision ids."""
 
1871
        rev_id = rev.revision_id
 
1872
        mutter('converting texts of revision {%s}',
 
1873
               rev_id)
 
1874
        parent_invs = map(self._load_updated_inventory, present_parents)
 
1875
        entries = inv.iter_entries()
 
1876
        entries.next()
 
1877
        for path, ie in entries:
 
1878
            self._convert_file_version(rev, ie, parent_invs)
 
1879
 
 
1880
    def _convert_file_version(self, rev, ie, parent_invs):
 
1881
        """Convert one version of one file.
 
1882
 
 
1883
        The file needs to be added into the weave if it is a merge
 
1884
        of >=2 parents or if it's changed from its parent.
 
1885
        """
 
1886
        file_id = ie.file_id
 
1887
        rev_id = rev.revision_id
 
1888
        w = self.text_weaves.get(file_id)
 
1889
        if w is None:
 
1890
            w = Weave(file_id)
 
1891
            self.text_weaves[file_id] = w
 
1892
        text_changed = False
 
1893
        previous_entries = ie.find_previous_heads(parent_invs,
 
1894
                                                  None,
 
1895
                                                  None,
 
1896
                                                  entry_vf=w)
 
1897
        for old_revision in previous_entries:
 
1898
                # if this fails, its a ghost ?
 
1899
                assert old_revision in self.converted_revs, \
 
1900
                    "Revision {%s} not in converted_revs" % old_revision
 
1901
        self.snapshot_ie(previous_entries, ie, w, rev_id)
 
1902
        del ie.text_id
 
1903
        assert getattr(ie, 'revision', None) is not None
 
1904
 
 
1905
    def snapshot_ie(self, previous_revisions, ie, w, rev_id):
 
1906
        # TODO: convert this logic, which is ~= snapshot to
 
1907
        # a call to:. This needs the path figured out. rather than a work_tree
 
1908
        # a v4 revision_tree can be given, or something that looks enough like
 
1909
        # one to give the file content to the entry if it needs it.
 
1910
        # and we need something that looks like a weave store for snapshot to 
 
1911
        # save against.
 
1912
        #ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
 
1913
        if len(previous_revisions) == 1:
 
1914
            previous_ie = previous_revisions.values()[0]
 
1915
            if ie._unchanged(previous_ie):
 
1916
                ie.revision = previous_ie.revision
 
1917
                return
 
1918
        if ie.has_text():
 
1919
            text = self.branch.repository.text_store.get(ie.text_id)
 
1920
            file_lines = text.readlines()
 
1921
            assert sha_strings(file_lines) == ie.text_sha1
 
1922
            assert sum(map(len, file_lines)) == ie.text_size
 
1923
            w.add_lines(rev_id, previous_revisions, file_lines)
 
1924
            self.text_count += 1
 
1925
        else:
 
1926
            w.add_lines(rev_id, previous_revisions, [])
 
1927
        ie.revision = rev_id
 
1928
 
 
1929
    def _make_order(self):
 
1930
        """Return a suitable order for importing revisions.
 
1931
 
 
1932
        The order must be such that an revision is imported after all
 
1933
        its (present) parents.
 
1934
        """
 
1935
        todo = set(self.revisions.keys())
 
1936
        done = self.absent_revisions.copy()
 
1937
        order = []
 
1938
        while todo:
 
1939
            # scan through looking for a revision whose parents
 
1940
            # are all done
 
1941
            for rev_id in sorted(list(todo)):
 
1942
                rev = self.revisions[rev_id]
 
1943
                parent_ids = set(rev.parent_ids)
 
1944
                if parent_ids.issubset(done):
 
1945
                    # can take this one now
 
1946
                    order.append(rev_id)
 
1947
                    todo.remove(rev_id)
 
1948
                    done.add(rev_id)
 
1949
        return order
 
1950
 
 
1951
 
 
1952
class ConvertBzrDir5To6(Converter):
 
1953
    """Converts format 5 bzr dirs to format 6."""
 
1954
 
 
1955
    def convert(self, to_convert, pb):
 
1956
        """See Converter.convert()."""
 
1957
        self.bzrdir = to_convert
 
1958
        self.pb = pb
 
1959
        self.pb.note('starting upgrade from format 5 to 6')
 
1960
        self._convert_to_prefixed()
 
1961
        return BzrDir.open(self.bzrdir.root_transport.base)
 
1962
 
 
1963
    def _convert_to_prefixed(self):
 
1964
        from bzrlib.store import TransportStore
 
1965
        self.bzrdir.transport.delete('branch-format')
 
1966
        for store_name in ["weaves", "revision-store"]:
 
1967
            self.pb.note("adding prefixes to %s" % store_name)
 
1968
            store_transport = self.bzrdir.transport.clone(store_name)
 
1969
            store = TransportStore(store_transport, prefixed=True)
 
1970
            for urlfilename in store_transport.list_dir('.'):
 
1971
                filename = urlutils.unescape(urlfilename)
 
1972
                if (filename.endswith(".weave") or
 
1973
                    filename.endswith(".gz") or
 
1974
                    filename.endswith(".sig")):
 
1975
                    file_id = os.path.splitext(filename)[0]
 
1976
                else:
 
1977
                    file_id = filename
 
1978
                prefix_dir = store.hash_prefix(file_id)
 
1979
                # FIXME keep track of the dirs made RBC 20060121
 
1980
                try:
 
1981
                    store_transport.move(filename, prefix_dir + '/' + filename)
 
1982
                except errors.NoSuchFile: # catches missing dirs strangely enough
 
1983
                    store_transport.mkdir(prefix_dir)
 
1984
                    store_transport.move(filename, prefix_dir + '/' + filename)
 
1985
        self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
 
1986
 
 
1987
 
 
1988
class ConvertBzrDir6ToMeta(Converter):
 
1989
    """Converts format 6 bzr dirs to metadirs."""
 
1990
 
 
1991
    def convert(self, to_convert, pb):
 
1992
        """See Converter.convert()."""
 
1993
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
 
1994
        from bzrlib.branch import BzrBranchFormat5
 
1995
        self.bzrdir = to_convert
 
1996
        self.pb = pb
 
1997
        self.count = 0
 
1998
        self.total = 20 # the steps we know about
 
1999
        self.garbage_inventories = []
 
2000
 
 
2001
        self.pb.note('starting upgrade from format 6 to metadir')
 
2002
        self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
 
2003
        # its faster to move specific files around than to open and use the apis...
 
2004
        # first off, nuke ancestry.weave, it was never used.
 
2005
        try:
 
2006
            self.step('Removing ancestry.weave')
 
2007
            self.bzrdir.transport.delete('ancestry.weave')
 
2008
        except errors.NoSuchFile:
 
2009
            pass
 
2010
        # find out whats there
 
2011
        self.step('Finding branch files')
 
2012
        last_revision = self.bzrdir.open_branch().last_revision()
 
2013
        bzrcontents = self.bzrdir.transport.list_dir('.')
 
2014
        for name in bzrcontents:
 
2015
            if name.startswith('basis-inventory.'):
 
2016
                self.garbage_inventories.append(name)
 
2017
        # create new directories for repository, working tree and branch
 
2018
        self.dir_mode = self.bzrdir._control_files._dir_mode
 
2019
        self.file_mode = self.bzrdir._control_files._file_mode
 
2020
        repository_names = [('inventory.weave', True),
 
2021
                            ('revision-store', True),
 
2022
                            ('weaves', True)]
 
2023
        self.step('Upgrading repository  ')
 
2024
        self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
 
2025
        self.make_lock('repository')
 
2026
        # we hard code the formats here because we are converting into
 
2027
        # the meta format. The meta format upgrader can take this to a 
 
2028
        # future format within each component.
 
2029
        self.put_format('repository', RepositoryFormat7())
 
2030
        for entry in repository_names:
 
2031
            self.move_entry('repository', entry)
 
2032
 
 
2033
        self.step('Upgrading branch      ')
 
2034
        self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
 
2035
        self.make_lock('branch')
 
2036
        self.put_format('branch', BzrBranchFormat5())
 
2037
        branch_files = [('revision-history', True),
 
2038
                        ('branch-name', True),
 
2039
                        ('parent', False)]
 
2040
        for entry in branch_files:
 
2041
            self.move_entry('branch', entry)
 
2042
 
 
2043
        checkout_files = [('pending-merges', True),
 
2044
                          ('inventory', True),
 
2045
                          ('stat-cache', False)]
 
2046
        # If a mandatory checkout file is not present, the branch does not have
 
2047
        # a functional checkout. Do not create a checkout in the converted
 
2048
        # branch.
 
2049
        for name, mandatory in checkout_files:
 
2050
            if mandatory and name not in bzrcontents:
 
2051
                has_checkout = False
 
2052
                break
 
2053
        else:
 
2054
            has_checkout = True
 
2055
        if not has_checkout:
 
2056
            self.pb.note('No working tree.')
 
2057
            # If some checkout files are there, we may as well get rid of them.
 
2058
            for name, mandatory in checkout_files:
 
2059
                if name in bzrcontents:
 
2060
                    self.bzrdir.transport.delete(name)
 
2061
        else:
 
2062
            from bzrlib.workingtree import WorkingTreeFormat3
 
2063
            self.step('Upgrading working tree')
 
2064
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
 
2065
            self.make_lock('checkout')
 
2066
            self.put_format(
 
2067
                'checkout', WorkingTreeFormat3())
 
2068
            self.bzrdir.transport.delete_multi(
 
2069
                self.garbage_inventories, self.pb)
 
2070
            for entry in checkout_files:
 
2071
                self.move_entry('checkout', entry)
 
2072
            if last_revision is not None:
 
2073
                self.bzrdir._control_files.put_utf8(
 
2074
                    'checkout/last-revision', last_revision)
 
2075
        self.bzrdir._control_files.put_utf8(
 
2076
            'branch-format', BzrDirMetaFormat1().get_format_string())
 
2077
        return BzrDir.open(self.bzrdir.root_transport.base)
 
2078
 
 
2079
    def make_lock(self, name):
 
2080
        """Make a lock for the new control dir name."""
 
2081
        self.step('Make %s lock' % name)
 
2082
        ld = lockdir.LockDir(self.bzrdir.transport,
 
2083
                             '%s/lock' % name,
 
2084
                             file_modebits=self.file_mode,
 
2085
                             dir_modebits=self.dir_mode)
 
2086
        ld.create()
 
2087
 
 
2088
    def move_entry(self, new_dir, entry):
 
2089
        """Move then entry name into new_dir."""
 
2090
        name = entry[0]
 
2091
        mandatory = entry[1]
 
2092
        self.step('Moving %s' % name)
 
2093
        try:
 
2094
            self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
 
2095
        except errors.NoSuchFile:
 
2096
            if mandatory:
 
2097
                raise
 
2098
 
 
2099
    def put_format(self, dirname, format):
 
2100
        self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
 
2101
 
 
2102
 
 
2103
class ConvertMetaToMeta(Converter):
 
2104
    """Converts the components of metadirs."""
 
2105
 
 
2106
    def __init__(self, target_format):
 
2107
        """Create a metadir to metadir converter.
 
2108
 
 
2109
        :param target_format: The final metadir format that is desired.
 
2110
        """
 
2111
        self.target_format = target_format
 
2112
 
 
2113
    def convert(self, to_convert, pb):
 
2114
        """See Converter.convert()."""
 
2115
        self.bzrdir = to_convert
 
2116
        self.pb = pb
 
2117
        self.count = 0
 
2118
        self.total = 1
 
2119
        self.step('checking repository format')
 
2120
        try:
 
2121
            repo = self.bzrdir.open_repository()
 
2122
        except errors.NoRepositoryPresent:
 
2123
            pass
 
2124
        else:
 
2125
            if not isinstance(repo._format, self.target_format.repository_format.__class__):
 
2126
                from bzrlib.repository import CopyConverter
 
2127
                self.pb.note('starting repository conversion')
 
2128
                converter = CopyConverter(self.target_format.repository_format)
 
2129
                converter.convert(repo, pb)
 
2130
        try:
 
2131
            branch = self.bzrdir.open_branch()
 
2132
        except errors.NotBranchError:
 
2133
            pass
 
2134
        else:
 
2135
            # TODO: conversions of Branch and Tree should be done by
 
2136
            # InterXFormat lookups
 
2137
            # Avoid circular imports
 
2138
            from bzrlib import branch as _mod_branch
 
2139
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
 
2140
                self.target_format.get_branch_format().__class__ is
 
2141
                _mod_branch.BzrBranchFormat6):
 
2142
                branch_converter = _mod_branch.Converter5to6()
 
2143
                branch_converter.convert(branch)
 
2144
        try:
 
2145
            tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
 
2146
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2147
            pass
 
2148
        else:
 
2149
            # TODO: conversions of Branch and Tree should be done by
 
2150
            # InterXFormat lookups
 
2151
            if (isinstance(tree, workingtree.WorkingTree3) and
 
2152
                not isinstance(tree, workingtree_4.WorkingTree4) and
 
2153
                isinstance(self.target_format.workingtree_format,
 
2154
                    workingtree_4.WorkingTreeFormat4)):
 
2155
                workingtree_4.Converter3to4().convert(tree)
 
2156
        return to_convert
 
2157
 
 
2158
 
 
2159
class BzrDirFormatInfo(object):
 
2160
 
 
2161
    def __init__(self, native, deprecated, hidden):
 
2162
        self.deprecated = deprecated
 
2163
        self.native = native
 
2164
        self.hidden = hidden
 
2165
 
 
2166
 
 
2167
class BzrDirFormatRegistry(registry.Registry):
 
2168
    """Registry of user-selectable BzrDir subformats.
 
2169
    
 
2170
    Differs from BzrDirFormat._control_formats in that it provides sub-formats,
 
2171
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
2172
    """
 
2173
 
 
2174
    def register_metadir(self, key,
 
2175
             repository_format, help, native=True, deprecated=False,
 
2176
             branch_format=None,
 
2177
             tree_format=None,
 
2178
             hidden=False):
 
2179
        """Register a metadir subformat.
 
2180
 
 
2181
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
 
2182
        by the Repository format.
 
2183
 
 
2184
        :param repository_format: The fully-qualified repository format class
 
2185
            name as a string.
 
2186
        :param branch_format: Fully-qualified branch format class name as
 
2187
            a string.
 
2188
        :param tree_format: Fully-qualified tree format class name as
 
2189
            a string.
 
2190
        """
 
2191
        # This should be expanded to support setting WorkingTree and Branch
 
2192
        # formats, once BzrDirMetaFormat1 supports that.
 
2193
        def _load(full_name):
 
2194
            mod_name, factory_name = full_name.rsplit('.', 1)
 
2195
            try:
 
2196
                mod = __import__(mod_name, globals(), locals(),
 
2197
                        [factory_name])
 
2198
            except ImportError, e:
 
2199
                raise ImportError('failed to load %s: %s' % (full_name, e))
 
2200
            try:
 
2201
                factory = getattr(mod, factory_name)
 
2202
            except AttributeError:
 
2203
                raise AttributeError('no factory %s in module %r'
 
2204
                    % (full_name, mod))
 
2205
            return factory()
 
2206
 
 
2207
        def helper():
 
2208
            bd = BzrDirMetaFormat1()
 
2209
            if branch_format is not None:
 
2210
                bd.set_branch_format(_load(branch_format))
 
2211
            if tree_format is not None:
 
2212
                bd.workingtree_format = _load(tree_format)
 
2213
            if repository_format is not None:
 
2214
                bd.repository_format = _load(repository_format)
 
2215
            return bd
 
2216
        self.register(key, helper, help, native, deprecated, hidden)
 
2217
 
 
2218
    def register(self, key, factory, help, native=True, deprecated=False,
 
2219
                 hidden=False):
 
2220
        """Register a BzrDirFormat factory.
 
2221
        
 
2222
        The factory must be a callable that takes one parameter: the key.
 
2223
        It must produce an instance of the BzrDirFormat when called.
 
2224
 
 
2225
        This function mainly exists to prevent the info object from being
 
2226
        supplied directly.
 
2227
        """
 
2228
        registry.Registry.register(self, key, factory, help, 
 
2229
            BzrDirFormatInfo(native, deprecated, hidden))
 
2230
 
 
2231
    def register_lazy(self, key, module_name, member_name, help, native=True,
 
2232
                      deprecated=False, hidden=False):
 
2233
        registry.Registry.register_lazy(self, key, module_name, member_name, 
 
2234
            help, BzrDirFormatInfo(native, deprecated, hidden))
 
2235
 
 
2236
    def set_default(self, key):
 
2237
        """Set the 'default' key to be a clone of the supplied key.
 
2238
        
 
2239
        This method must be called once and only once.
 
2240
        """
 
2241
        registry.Registry.register(self, 'default', self.get(key), 
 
2242
            self.get_help(key), info=self.get_info(key))
 
2243
 
 
2244
    def set_default_repository(self, key):
 
2245
        """Set the FormatRegistry default and Repository default.
 
2246
        
 
2247
        This is a transitional method while Repository.set_default_format
 
2248
        is deprecated.
 
2249
        """
 
2250
        if 'default' in self:
 
2251
            self.remove('default')
 
2252
        self.set_default(key)
 
2253
        format = self.get('default')()
 
2254
        assert isinstance(format, BzrDirMetaFormat1)
 
2255
 
 
2256
    def make_bzrdir(self, key):
 
2257
        return self.get(key)()
 
2258
 
 
2259
    def help_topic(self, topic):
 
2260
        output = textwrap.dedent("""\
 
2261
            Bazaar directory formats
 
2262
            ------------------------
 
2263
 
 
2264
            These formats can be used for creating branches, working trees, and
 
2265
            repositories.
 
2266
 
 
2267
            """)
 
2268
        default_help = self.get_help('default')
 
2269
        help_pairs = []
 
2270
        for key in self.keys():
 
2271
            if key == 'default':
 
2272
                continue
 
2273
            help = self.get_help(key)
 
2274
            if help == default_help:
 
2275
                default_realkey = key
 
2276
            else:
 
2277
                help_pairs.append((key, help))
 
2278
 
 
2279
        def wrapped(key, help, info):
 
2280
            if info.native:
 
2281
                help = '(native) ' + help
 
2282
            return '  %s:\n%s\n\n' % (key, 
 
2283
                    textwrap.fill(help, initial_indent='    ', 
 
2284
                    subsequent_indent='    '))
 
2285
        output += wrapped('%s/default' % default_realkey, default_help,
 
2286
                          self.get_info('default'))
 
2287
        deprecated_pairs = []
 
2288
        for key, help in help_pairs:
 
2289
            info = self.get_info(key)
 
2290
            if info.hidden:
 
2291
                continue
 
2292
            elif info.deprecated:
 
2293
                deprecated_pairs.append((key, help))
 
2294
            else:
 
2295
                output += wrapped(key, help, info)
 
2296
        if len(deprecated_pairs) > 0:
 
2297
            output += "Deprecated formats\n------------------\n\n"
 
2298
            for key, help in deprecated_pairs:
 
2299
                info = self.get_info(key)
 
2300
                output += wrapped(key, help, info)
 
2301
 
 
2302
        return output
 
2303
 
 
2304
 
 
2305
format_registry = BzrDirFormatRegistry()
 
2306
format_registry.register('weave', BzrDirFormat6,
 
2307
    'Pre-0.8 format.  Slower than knit and does not'
 
2308
    ' support checkouts or shared repositories.',
 
2309
    deprecated=True)
 
2310
format_registry.register_metadir('knit',
 
2311
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2312
    'Format using knits.  Recommended for interoperation with bzr <= 0.14.',
 
2313
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2314
    tree_format='bzrlib.workingtree.WorkingTreeFormat3')
 
2315
format_registry.register_metadir('metaweave',
 
2316
    'bzrlib.repofmt.weaverepo.RepositoryFormat7',
 
2317
    'Transitional format in 0.8.  Slower than knit.',
 
2318
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2319
    tree_format='bzrlib.workingtree.WorkingTreeFormat3',
 
2320
    deprecated=True)
 
2321
format_registry.register_metadir('dirstate',
 
2322
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2323
    help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
 
2324
        'above when accessed over the network.',
 
2325
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2326
    # this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
 
2327
    # directly from workingtree_4 triggers a circular import.
 
2328
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2329
    )
 
2330
format_registry.register_metadir('dirstate-tags',
 
2331
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2332
    help='New in 0.15: Fast local operations and improved scaling for '
 
2333
        'network operations. Additionally adds support for tags.'
 
2334
        ' Incompatible with bzr < 0.15.',
 
2335
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2336
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2337
    )
 
2338
format_registry.register_metadir('dirstate-with-subtree',
 
2339
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
 
2340
    help='New in 0.15: Fast local operations and improved scaling for '
 
2341
        'network operations. Additionally adds support for versioning nested '
 
2342
        'bzr branches. Incompatible with bzr < 0.15.',
 
2343
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2344
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2345
    hidden=True,
 
2346
    )
 
2347
format_registry.set_default('dirstate')