~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Jonathan Lange
  • Date: 2007-04-23 01:30:35 UTC
  • mto: This revision was merged to the branch mainline in revision 2446.
  • Revision ID: jml@canonical.com-20070423013035-zuqiamuro8h1hba9
Can also set the bug config options in branch.conf

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
20
directories.
21
21
"""
22
22
 
 
23
# TODO: remove unittest dependency; put that stuff inside the test suite
 
24
 
 
25
# TODO: Can we move specific formats into separate modules to make this file
 
26
# smaller?
 
27
 
 
28
from cStringIO import StringIO
 
29
import os
 
30
import textwrap
 
31
 
 
32
from bzrlib.lazy_import import lazy_import
 
33
lazy_import(globals(), """
23
34
from copy import deepcopy
24
 
import os
25
 
from cStringIO import StringIO
26
 
from unittest import TestSuite
 
35
from stat import S_ISDIR
 
36
import unittest
27
37
 
28
38
import bzrlib
29
 
import bzrlib.errors as errors
30
 
from bzrlib.lockable_files import LockableFiles, TransportLock
31
 
from bzrlib.lockdir import LockDir
 
39
from bzrlib import (
 
40
    errors,
 
41
    lockable_files,
 
42
    lockdir,
 
43
    registry,
 
44
    remote,
 
45
    revision as _mod_revision,
 
46
    symbol_versioning,
 
47
    ui,
 
48
    urlutils,
 
49
    xml4,
 
50
    xml5,
 
51
    workingtree,
 
52
    workingtree_4,
 
53
    )
32
54
from bzrlib.osutils import (
33
 
                            abspath,
34
 
                            pathjoin,
35
 
                            safe_unicode,
36
 
                            sha_strings,
37
 
                            sha_string,
38
 
                            )
 
55
    safe_unicode,
 
56
    sha_strings,
 
57
    sha_string,
 
58
    )
 
59
from bzrlib.smart.client import _SmartClient
39
60
from bzrlib.store.revision.text import TextRevisionStore
40
61
from bzrlib.store.text import TextStore
41
62
from bzrlib.store.versioned import WeaveStore
42
 
from bzrlib.symbol_versioning import *
43
 
from bzrlib.trace import mutter
44
63
from bzrlib.transactions import WriteTransaction
45
 
from bzrlib.transport import get_transport
 
64
from bzrlib.transport import (
 
65
    do_catching_redirections,
 
66
    get_transport,
 
67
    )
 
68
from bzrlib.weave import Weave
 
69
""")
 
70
 
 
71
from bzrlib.trace import (
 
72
    mutter,
 
73
    note,
 
74
    )
46
75
from bzrlib.transport.local import LocalTransport
47
 
import bzrlib.urlutils as urlutils
48
 
from bzrlib.weave import Weave
49
 
from bzrlib.xml4 import serializer_v4
50
 
import bzrlib.xml5
51
76
 
52
77
 
53
78
class BzrDir(object):
68
93
        If there is a tree, the tree is opened and break_lock() called.
69
94
        Otherwise, branch is tried, and finally repository.
70
95
        """
 
96
        # XXX: This seems more like a UI function than something that really
 
97
        # belongs in this class.
71
98
        try:
72
99
            thing_to_unlock = self.open_workingtree()
73
100
        except (errors.NotLocalUrl, errors.NoWorkingTree):
84
111
        """Return true if this bzrdir is one whose format we can convert from."""
85
112
        return True
86
113
 
 
114
    def check_conversion_target(self, target_format):
 
115
        target_repo_format = target_format.repository_format
 
116
        source_repo_format = self._format.repository_format
 
117
        source_repo_format.check_conversion_target(target_repo_format)
 
118
 
87
119
    @staticmethod
88
 
    def _check_supported(format, allow_unsupported):
89
 
        """Check whether format is a supported format.
90
 
 
91
 
        If allow_unsupported is True, this is a no-op.
 
120
    def _check_supported(format, allow_unsupported,
 
121
        recommend_upgrade=True,
 
122
        basedir=None):
 
123
        """Give an error or warning on old formats.
 
124
 
 
125
        :param format: may be any kind of format - workingtree, branch, 
 
126
        or repository.
 
127
 
 
128
        :param allow_unsupported: If true, allow opening 
 
129
        formats that are strongly deprecated, and which may 
 
130
        have limited functionality.
 
131
 
 
132
        :param recommend_upgrade: If true (default), warn
 
133
        the user through the ui object that they may wish
 
134
        to upgrade the object.
92
135
        """
 
136
        # TODO: perhaps move this into a base Format class; it's not BzrDir
 
137
        # specific. mbp 20070323
93
138
        if not allow_unsupported and not format.is_supported():
94
139
            # see open_downlevel to open legacy branches.
95
 
            raise errors.UnsupportedFormatError(
96
 
                    'sorry, format %s not supported' % format,
97
 
                    ['use a different bzr version',
98
 
                     'or remove the .bzr directory'
99
 
                     ' and "bzr init" again'])
 
140
            raise errors.UnsupportedFormatError(format=format)
 
141
        if recommend_upgrade \
 
142
            and getattr(format, 'upgrade_recommended', False):
 
143
            ui.ui_factory.recommend_upgrade(
 
144
                format.get_format_description(),
 
145
                basedir)
100
146
 
101
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
147
    def clone(self, url, revision_id=None, force_new_repo=False):
102
148
        """Clone this bzrdir and its contents to url verbatim.
103
149
 
104
150
        If urls last component does not exist, it will be created.
109
155
                               even if one is available.
110
156
        """
111
157
        self._make_tail(url)
112
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
113
158
        result = self._format.initialize(url)
114
159
        try:
115
160
            local_repo = self.find_repository()
120
165
            if force_new_repo:
121
166
                result_repo = local_repo.clone(
122
167
                    result,
123
 
                    revision_id=revision_id,
124
 
                    basis=basis_repo)
 
168
                    revision_id=revision_id)
125
169
                result_repo.set_make_working_trees(local_repo.make_working_trees())
126
170
            else:
127
171
                try:
128
172
                    result_repo = result.find_repository()
129
173
                    # fetch content this dir needs.
130
 
                    if basis_repo:
131
 
                        # XXX FIXME RBC 20060214 need tests for this when the basis
132
 
                        # is incomplete
133
 
                        result_repo.fetch(basis_repo, revision_id=revision_id)
134
174
                    result_repo.fetch(local_repo, revision_id=revision_id)
135
175
                except errors.NoRepositoryPresent:
136
176
                    # needed to make one anyway.
137
177
                    result_repo = local_repo.clone(
138
178
                        result,
139
 
                        revision_id=revision_id,
140
 
                        basis=basis_repo)
 
179
                        revision_id=revision_id)
141
180
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
142
181
        # 1 if there is a branch present
143
182
        #   make sure its content is available in the target repository
147
186
        except errors.NotBranchError:
148
187
            pass
149
188
        try:
150
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
189
            self.open_workingtree().clone(result)
151
190
        except (errors.NoWorkingTree, errors.NotLocalUrl):
152
191
            pass
153
192
        return result
154
193
 
155
 
    def _get_basis_components(self, basis):
156
 
        """Retrieve the basis components that are available at basis."""
157
 
        if basis is None:
158
 
            return None, None, None
159
 
        try:
160
 
            basis_tree = basis.open_workingtree()
161
 
            basis_branch = basis_tree.branch
162
 
            basis_repo = basis_branch.repository
163
 
        except (errors.NoWorkingTree, errors.NotLocalUrl):
164
 
            basis_tree = None
165
 
            try:
166
 
                basis_branch = basis.open_branch()
167
 
                basis_repo = basis_branch.repository
168
 
            except errors.NotBranchError:
169
 
                basis_branch = None
170
 
                try:
171
 
                    basis_repo = basis.open_repository()
172
 
                except errors.NoRepositoryPresent:
173
 
                    basis_repo = None
174
 
        return basis_repo, basis_branch, basis_tree
175
 
 
176
194
    # TODO: This should be given a Transport, and should chdir up; otherwise
177
195
    # this will open a new connection.
178
196
    def _make_tail(self, url):
179
197
        head, tail = urlutils.split(url)
180
198
        if tail and tail != '.':
181
 
            t = bzrlib.transport.get_transport(head)
 
199
            t = get_transport(head)
182
200
            try:
183
201
                t.mkdir(tail)
184
202
            except errors.FileExists:
186
204
 
187
205
    # TODO: Should take a Transport
188
206
    @classmethod
189
 
    def create(cls, base):
 
207
    def create(cls, base, format=None):
190
208
        """Create a new BzrDir at the url 'base'.
191
209
        
192
210
        This will call the current default formats initialize with base
193
211
        as the only parameter.
194
212
 
195
 
        If you need a specific format, consider creating an instance
196
 
        of that and calling initialize().
 
213
        :param format: If supplied, the format of branch to create.  If not
 
214
            supplied, the default is used.
197
215
        """
198
216
        if cls is not BzrDir:
199
 
            raise AssertionError("BzrDir.create always creates the default format, "
200
 
                    "not one of %r" % cls)
 
217
            raise AssertionError("BzrDir.create always creates the default"
 
218
                " format, not one of %r" % cls)
201
219
        head, tail = urlutils.split(base)
202
220
        if tail and tail != '.':
203
 
            t = bzrlib.transport.get_transport(head)
 
221
            t = get_transport(head)
204
222
            try:
205
223
                t.mkdir(tail)
206
224
            except errors.FileExists:
207
225
                pass
208
 
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
 
226
        if format is None:
 
227
            format = BzrDirFormat.get_default_format()
 
228
        return format.initialize(safe_unicode(base))
209
229
 
210
230
    def create_branch(self):
211
231
        """Create a branch in this BzrDir.
216
236
        raise NotImplementedError(self.create_branch)
217
237
 
218
238
    @staticmethod
219
 
    def create_branch_and_repo(base, force_new_repo=False):
 
239
    def create_branch_and_repo(base, force_new_repo=False, format=None):
220
240
        """Create a new BzrDir, Branch and Repository at the url 'base'.
221
241
 
222
242
        This will use the current default BzrDirFormat, and use whatever 
229
249
        :param base: The URL to create the branch at.
230
250
        :param force_new_repo: If True a new repository is always created.
231
251
        """
232
 
        bzrdir = BzrDir.create(base)
 
252
        bzrdir = BzrDir.create(base, format)
233
253
        bzrdir._find_or_create_repository(force_new_repo)
234
254
        return bzrdir.create_branch()
235
255
 
273
293
            t = get_transport(safe_unicode(base))
274
294
            if not isinstance(t, LocalTransport):
275
295
                raise errors.NotLocalUrl(base)
276
 
        if format is None:
277
 
            bzrdir = BzrDir.create(base)
278
 
        else:
279
 
            bzrdir = format.initialize(base)
 
296
        bzrdir = BzrDir.create(base, format)
280
297
        repo = bzrdir._find_or_create_repository(force_new_repo)
281
298
        result = bzrdir.create_branch()
282
299
        if force_new_tree or (repo.make_working_trees() and 
288
305
        return result
289
306
        
290
307
    @staticmethod
291
 
    def create_repository(base, shared=False):
 
308
    def create_repository(base, shared=False, format=None):
292
309
        """Create a new BzrDir and Repository at the url 'base'.
293
310
 
294
 
        This will use the current default BzrDirFormat, and use whatever 
295
 
        repository format that that uses for bzrdirformat.create_repository.
 
311
        If no format is supplied, this will default to the current default
 
312
        BzrDirFormat by default, and use whatever repository format that that
 
313
        uses for bzrdirformat.create_repository.
296
314
 
297
 
        ;param shared: Create a shared repository rather than a standalone
 
315
        :param shared: Create a shared repository rather than a standalone
298
316
                       repository.
299
317
        The Repository object is returned.
300
318
 
302
320
        it should take no parameters and construct whatever repository format
303
321
        that child class desires.
304
322
        """
305
 
        bzrdir = BzrDir.create(base)
306
 
        return bzrdir.create_repository()
 
323
        bzrdir = BzrDir.create(base, format)
 
324
        return bzrdir.create_repository(shared)
307
325
 
308
326
    @staticmethod
309
 
    def create_standalone_workingtree(base):
 
327
    def create_standalone_workingtree(base, format=None):
310
328
        """Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
311
329
 
312
330
        'base' must be a local path or a file:// url.
315
333
        repository format that that uses for bzrdirformat.create_workingtree,
316
334
        create_branch and create_repository.
317
335
 
318
 
        The WorkingTree object is returned.
 
336
        :return: The WorkingTree object.
319
337
        """
320
338
        t = get_transport(safe_unicode(base))
321
339
        if not isinstance(t, LocalTransport):
322
340
            raise errors.NotLocalUrl(base)
323
341
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
324
 
                                               force_new_repo=True).bzrdir
 
342
                                               force_new_repo=True,
 
343
                                               format=format).bzrdir
325
344
        return bzrdir.create_workingtree()
326
345
 
327
346
    def create_workingtree(self, revision_id=None):
331
350
        """
332
351
        raise NotImplementedError(self.create_workingtree)
333
352
 
 
353
    def retire_bzrdir(self):
 
354
        """Permanently disable the bzrdir.
 
355
 
 
356
        This is done by renaming it to give the user some ability to recover
 
357
        if there was a problem.
 
358
 
 
359
        This will have horrible consequences if anyone has anything locked or
 
360
        in use.
 
361
        """
 
362
        for i in xrange(10000):
 
363
            try:
 
364
                to_path = '.bzr.retired.%d' % i
 
365
                self.root_transport.rename('.bzr', to_path)
 
366
                note("renamed %s to %s"
 
367
                    % (self.root_transport.abspath('.bzr'), to_path))
 
368
                break
 
369
            except (errors.TransportError, IOError, errors.PathError):
 
370
                pass
 
371
 
 
372
    def destroy_workingtree(self):
 
373
        """Destroy the working tree at this BzrDir.
 
374
 
 
375
        Formats that do not support this may raise UnsupportedOperation.
 
376
        """
 
377
        raise NotImplementedError(self.destroy_workingtree)
 
378
 
 
379
    def destroy_workingtree_metadata(self):
 
380
        """Destroy the control files for the working tree at this BzrDir.
 
381
 
 
382
        The contents of working tree files are not affected.
 
383
        Formats that do not support this may raise UnsupportedOperation.
 
384
        """
 
385
        raise NotImplementedError(self.destroy_workingtree_metadata)
 
386
 
334
387
    def find_repository(self):
335
388
        """Find the repository that should be used for a_bzrdir.
336
389
 
361
414
                    break
362
415
                else:
363
416
                    continue
364
 
            if ((found_bzrdir.root_transport.base == 
 
417
            if ((found_bzrdir.root_transport.base ==
365
418
                 self.root_transport.base) or repository.is_shared()):
366
419
                return repository
367
420
            else:
368
421
                raise errors.NoRepositoryPresent(self)
369
422
        raise errors.NoRepositoryPresent(self)
370
423
 
 
424
    def get_branch_reference(self):
 
425
        """Return the referenced URL for the branch in this bzrdir.
 
426
 
 
427
        :raises NotBranchError: If there is no Branch.
 
428
        :return: The URL the branch in this bzrdir references if it is a
 
429
            reference branch, or None for regular branches.
 
430
        """
 
431
        return None
 
432
 
371
433
    def get_branch_transport(self, branch_format):
372
434
        """Get the transport for use by branch format in this BzrDir.
373
435
 
398
460
        """Get the transport for use by workingtree format in this BzrDir.
399
461
 
400
462
        Note that bzr dirs that do not support format strings will raise
401
 
        IncompatibleFormat if the workingtree format they are given has
402
 
        a format string, and vice versa.
 
463
        IncompatibleFormat if the workingtree format they are given has a
 
464
        format string, and vice versa.
403
465
 
404
466
        If workingtree_format is None, the transport is returned with no 
405
467
        checking. if it is not None, then the returned transport is
462
524
        _unsupported is a private parameter to the BzrDir class.
463
525
        """
464
526
        t = get_transport(base)
465
 
        mutter("trying to open %r with transport %r", base, t)
466
 
        format = BzrDirFormat.find_format(t)
 
527
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
 
528
 
 
529
    @staticmethod
 
530
    def open_from_transport(transport, _unsupported=False,
 
531
                            _server_formats=True):
 
532
        """Open a bzrdir within a particular directory.
 
533
 
 
534
        :param transport: Transport containing the bzrdir.
 
535
        :param _unsupported: private.
 
536
        """
 
537
        base = transport.base
 
538
 
 
539
        def find_format(transport):
 
540
            return transport, BzrDirFormat.find_format(
 
541
                transport, _server_formats=_server_formats)
 
542
 
 
543
        def redirected(transport, e, redirection_notice):
 
544
            qualified_source = e.get_source_url()
 
545
            relpath = transport.relpath(qualified_source)
 
546
            if not e.target.endswith(relpath):
 
547
                # Not redirected to a branch-format, not a branch
 
548
                raise errors.NotBranchError(path=e.target)
 
549
            target = e.target[:-len(relpath)]
 
550
            note('%s is%s redirected to %s',
 
551
                 transport.base, e.permanently, target)
 
552
            # Let's try with a new transport
 
553
            qualified_target = e.get_target_url()[:-len(relpath)]
 
554
            # FIXME: If 'transport' has a qualifier, this should
 
555
            # be applied again to the new transport *iff* the
 
556
            # schemes used are the same. It's a bit tricky to
 
557
            # verify, so I'll punt for now
 
558
            # -- vila20070212
 
559
            return get_transport(target)
 
560
 
 
561
        try:
 
562
            transport, format = do_catching_redirections(find_format,
 
563
                                                         transport,
 
564
                                                         redirected)
 
565
        except errors.TooManyRedirections:
 
566
            raise errors.NotBranchError(base)
 
567
 
467
568
        BzrDir._check_supported(format, _unsupported)
468
 
        return format.open(t, _found=True)
 
569
        return format.open(transport, _found=True)
469
570
 
470
571
    def open_branch(self, unsupported=False):
471
572
        """Open the branch object at this BzrDir if one is present.
505
606
        url = a_transport.base
506
607
        while True:
507
608
            try:
508
 
                format = BzrDirFormat.find_format(a_transport)
509
 
                BzrDir._check_supported(format, False)
510
 
                return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
 
609
                result = BzrDir.open_from_transport(a_transport)
 
610
                return result, urlutils.unescape(a_transport.relpath(url))
511
611
            except errors.NotBranchError, e:
512
 
                ## mutter('not a branch in: %r %s', a_transport.base, e)
513
612
                pass
514
 
            new_t = a_transport.clone('..')
 
613
            try:
 
614
                new_t = a_transport.clone('..')
 
615
            except errors.InvalidURLJoin:
 
616
                # reached the root, whatever that may be
 
617
                raise errors.NotBranchError(path=url)
515
618
            if new_t.base == a_transport.base:
516
619
                # reached the root, whatever that may be
517
620
                raise errors.NotBranchError(path=url)
518
621
            a_transport = new_t
519
622
 
 
623
    @classmethod
 
624
    def open_containing_tree_or_branch(klass, location):
 
625
        """Return the branch and working tree contained by a location.
 
626
 
 
627
        Returns (tree, branch, relpath).
 
628
        If there is no tree at containing the location, tree will be None.
 
629
        If there is no branch containing the location, an exception will be
 
630
        raised
 
631
        relpath is the portion of the path that is contained by the branch.
 
632
        """
 
633
        bzrdir, relpath = klass.open_containing(location)
 
634
        try:
 
635
            tree = bzrdir.open_workingtree()
 
636
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
637
            tree = None
 
638
            branch = bzrdir.open_branch()
 
639
        else:
 
640
            branch = tree.branch
 
641
        return tree, branch, relpath
 
642
 
520
643
    def open_repository(self, _unsupported=False):
521
644
        """Open the repository object at this BzrDir if one is present.
522
645
 
529
652
        """
530
653
        raise NotImplementedError(self.open_repository)
531
654
 
532
 
    def open_workingtree(self, _unsupported=False):
 
655
    def open_workingtree(self, _unsupported=False,
 
656
            recommend_upgrade=True):
533
657
        """Open the workingtree object at this BzrDir if one is present.
534
 
        
535
 
        TODO: static convenience version of this?
 
658
 
 
659
        :param recommend_upgrade: Optional keyword parameter, when True (the
 
660
            default), emit through the ui module a recommendation that the user
 
661
            upgrade the working tree when the workingtree being opened is old
 
662
            (but still fully supported).
536
663
        """
537
664
        raise NotImplementedError(self.open_workingtree)
538
665
 
560
687
        workingtree and discards it, and that's somewhat expensive.) 
561
688
        """
562
689
        try:
563
 
            self.open_workingtree()
 
690
            self.open_workingtree(recommend_upgrade=False)
564
691
            return True
565
692
        except errors.NoWorkingTree:
566
693
            return False
567
694
 
568
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
695
    def _cloning_metadir(self):
 
696
        """Produce a metadir suitable for cloning with"""
 
697
        result_format = self._format.__class__()
 
698
        try:
 
699
            try:
 
700
                branch = self.open_branch()
 
701
                source_repository = branch.repository
 
702
            except errors.NotBranchError:
 
703
                source_branch = None
 
704
                source_repository = self.open_repository()
 
705
        except errors.NoRepositoryPresent:
 
706
            source_repository = None
 
707
        else:
 
708
            # XXX TODO: This isinstance is here because we have not implemented
 
709
            # the fix recommended in bug # 103195 - to delegate this choice the
 
710
            # repository itself.
 
711
            repo_format = source_repository._format
 
712
            if not isinstance(repo_format, remote.RemoteRepositoryFormat):
 
713
                result_format.repository_format = repo_format
 
714
        try:
 
715
            # TODO: Couldn't we just probe for the format in these cases,
 
716
            # rather than opening the whole tree?  It would be a little
 
717
            # faster. mbp 20070401
 
718
            tree = self.open_workingtree(recommend_upgrade=False)
 
719
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
720
            result_format.workingtree_format = None
 
721
        else:
 
722
            result_format.workingtree_format = tree._format.__class__()
 
723
        return result_format, source_repository
 
724
 
 
725
    def cloning_metadir(self):
 
726
        """Produce a metadir suitable for cloning or sprouting with.
 
727
 
 
728
        These operations may produce workingtrees (yes, even though they're
 
729
        "cloning" something that doesn't have a tree, so a viable workingtree
 
730
        format must be selected.
 
731
        """
 
732
        format, repository = self._cloning_metadir()
 
733
        if format._workingtree_format is None:
 
734
            if repository is None:
 
735
                return format
 
736
            tree_format = repository._format._matchingbzrdir.workingtree_format
 
737
            format.workingtree_format = tree_format.__class__()
 
738
        return format
 
739
 
 
740
    def checkout_metadir(self):
 
741
        return self.cloning_metadir()
 
742
 
 
743
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
744
               recurse='down'):
569
745
        """Create a copy of this bzrdir prepared for use as a new line of
570
746
        development.
571
747
 
580
756
            itself to download less data.
581
757
        """
582
758
        self._make_tail(url)
583
 
        result = self._format.initialize(url)
584
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
759
        cloning_format = self.cloning_metadir()
 
760
        result = cloning_format.initialize(url)
585
761
        try:
586
762
            source_branch = self.open_branch()
587
763
            source_repository = source_branch.repository
590
766
            try:
591
767
                source_repository = self.open_repository()
592
768
            except errors.NoRepositoryPresent:
593
 
                # copy the entire basis one if there is one
594
 
                # but there is no repository.
595
 
                source_repository = basis_repo
 
769
                source_repository = None
596
770
        if force_new_repo:
597
771
            result_repo = None
598
772
        else:
613
787
            result_repo = result.create_repository()
614
788
        if result_repo is not None:
615
789
            # fetch needed content into target.
616
 
            if basis_repo:
617
 
                # XXX FIXME RBC 20060214 need tests for this when the basis
618
 
                # is incomplete
619
 
                result_repo.fetch(basis_repo, revision_id=revision_id)
620
 
            result_repo.fetch(source_repository, revision_id=revision_id)
 
790
            if source_repository is not None:
 
791
                result_repo.fetch(source_repository, revision_id=revision_id)
621
792
        if source_branch is not None:
622
793
            source_branch.sprout(result, revision_id=revision_id)
623
794
        else:
625
796
        # TODO: jam 20060426 we probably need a test in here in the
626
797
        #       case that the newly sprouted branch is a remote one
627
798
        if result_repo is None or result_repo.make_working_trees():
628
 
            result.create_workingtree()
 
799
            wt = result.create_workingtree()
 
800
            wt.lock_write()
 
801
            try:
 
802
                if wt.path2id('') is None:
 
803
                    try:
 
804
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
805
                    except errors.NoWorkingTree:
 
806
                        pass
 
807
            finally:
 
808
                wt.unlock()
 
809
        else:
 
810
            wt = None
 
811
        if recurse == 'down':
 
812
            if wt is not None:
 
813
                basis = wt.basis_tree()
 
814
                basis.lock_read()
 
815
                subtrees = basis.iter_references()
 
816
                recurse_branch = wt.branch
 
817
            elif source_branch is not None:
 
818
                basis = source_branch.basis_tree()
 
819
                basis.lock_read()
 
820
                subtrees = basis.iter_references()
 
821
                recurse_branch = source_branch
 
822
            else:
 
823
                subtrees = []
 
824
                basis = None
 
825
            try:
 
826
                for path, file_id in subtrees:
 
827
                    target = urlutils.join(url, urlutils.escape(path))
 
828
                    sublocation = source_branch.reference_parent(file_id, path)
 
829
                    sublocation.bzrdir.sprout(target,
 
830
                        basis.get_reference_revision(file_id, path),
 
831
                        force_new_repo=force_new_repo, recurse=recurse)
 
832
            finally:
 
833
                if basis is not None:
 
834
                    basis.unlock()
629
835
        return result
630
836
 
631
837
 
635
841
    def __init__(self, _transport, _format):
636
842
        """See BzrDir.__init__."""
637
843
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
638
 
        assert self._format._lock_class == TransportLock
 
844
        assert self._format._lock_class == lockable_files.TransportLock
639
845
        assert self._format._lock_file_name == 'branch-lock'
640
 
        self._control_files = LockableFiles(self.get_branch_transport(None),
 
846
        self._control_files = lockable_files.LockableFiles(
 
847
                                            self.get_branch_transport(None),
641
848
                                            self._format._lock_file_name,
642
849
                                            self._format._lock_class)
643
850
 
645
852
        """Pre-splitout bzrdirs do not suffer from stale locks."""
646
853
        raise NotImplementedError(self.break_lock)
647
854
 
648
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
855
    def clone(self, url, revision_id=None, force_new_repo=False):
649
856
        """See BzrDir.clone()."""
650
857
        from bzrlib.workingtree import WorkingTreeFormat2
651
858
        self._make_tail(url)
652
859
        result = self._format._initialize_for_clone(url)
653
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
654
 
        self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
860
        self.open_repository().clone(result, revision_id=revision_id)
655
861
        from_branch = self.open_branch()
656
862
        from_branch.clone(result, revision_id=revision_id)
657
863
        try:
658
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
864
            self.open_workingtree().clone(result)
659
865
        except errors.NotLocalUrl:
660
866
            # make a new one, this format always has to have one.
661
867
            try:
679
885
    def create_workingtree(self, revision_id=None):
680
886
        """See BzrDir.create_workingtree."""
681
887
        # this looks buggy but is not -really-
 
888
        # because this format creates the workingtree when the bzrdir is
 
889
        # created
682
890
        # clone and sprout will have set the revision_id
683
891
        # and that will have set it for us, its only
684
892
        # specific uses of create_workingtree in isolation
685
893
        # that can do wonky stuff here, and that only
686
894
        # happens for creating checkouts, which cannot be 
687
895
        # done on this format anyway. So - acceptable wart.
688
 
        result = self.open_workingtree()
 
896
        result = self.open_workingtree(recommend_upgrade=False)
689
897
        if revision_id is not None:
690
 
            result.set_last_revision(revision_id)
 
898
            if revision_id == _mod_revision.NULL_REVISION:
 
899
                result.set_parent_ids([])
 
900
            else:
 
901
                result.set_parent_ids([revision_id])
691
902
        return result
692
903
 
 
904
    def destroy_workingtree(self):
 
905
        """See BzrDir.destroy_workingtree."""
 
906
        raise errors.UnsupportedOperation(self.destroy_workingtree, self)
 
907
 
 
908
    def destroy_workingtree_metadata(self):
 
909
        """See BzrDir.destroy_workingtree_metadata."""
 
910
        raise errors.UnsupportedOperation(self.destroy_workingtree_metadata, 
 
911
                                          self)
 
912
 
693
913
    def get_branch_transport(self, branch_format):
694
914
        """See BzrDir.get_branch_transport()."""
695
915
        if branch_format is None:
735
955
        self._check_supported(format, unsupported)
736
956
        return format.open(self, _found=True)
737
957
 
738
 
    def sprout(self, url, revision_id=None, basis=None):
 
958
    def sprout(self, url, revision_id=None, force_new_repo=False):
739
959
        """See BzrDir.sprout()."""
740
960
        from bzrlib.workingtree import WorkingTreeFormat2
741
961
        self._make_tail(url)
742
962
        result = self._format._initialize_for_clone(url)
743
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
744
963
        try:
745
 
            self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
964
            self.open_repository().clone(result, revision_id=revision_id)
746
965
        except errors.NoRepositoryPresent:
747
966
            pass
748
967
        try:
770
989
 
771
990
    def open_repository(self):
772
991
        """See BzrDir.open_repository."""
773
 
        from bzrlib.repository import RepositoryFormat4
 
992
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
774
993
        return RepositoryFormat4().open(self, _found=True)
775
994
 
776
995
 
782
1001
 
783
1002
    def open_repository(self):
784
1003
        """See BzrDir.open_repository."""
785
 
        from bzrlib.repository import RepositoryFormat5
 
1004
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
786
1005
        return RepositoryFormat5().open(self, _found=True)
787
1006
 
788
 
    def open_workingtree(self, _unsupported=False):
 
1007
    def open_workingtree(self, _unsupported=False,
 
1008
            recommend_upgrade=True):
789
1009
        """See BzrDir.create_workingtree."""
790
1010
        from bzrlib.workingtree import WorkingTreeFormat2
791
 
        return WorkingTreeFormat2().open(self, _found=True)
 
1011
        wt_format = WorkingTreeFormat2()
 
1012
        # we don't warn here about upgrades; that ought to be handled for the
 
1013
        # bzrdir as a whole
 
1014
        return wt_format.open(self, _found=True)
792
1015
 
793
1016
 
794
1017
class BzrDir6(BzrDirPreSplitOut):
799
1022
 
800
1023
    def open_repository(self):
801
1024
        """See BzrDir.open_repository."""
802
 
        from bzrlib.repository import RepositoryFormat6
 
1025
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
803
1026
        return RepositoryFormat6().open(self, _found=True)
804
1027
 
805
 
    def open_workingtree(self, _unsupported=False):
 
1028
    def open_workingtree(self, _unsupported=False,
 
1029
        recommend_upgrade=True):
806
1030
        """See BzrDir.create_workingtree."""
 
1031
        # we don't warn here about upgrades; that ought to be handled for the
 
1032
        # bzrdir as a whole
807
1033
        from bzrlib.workingtree import WorkingTreeFormat2
808
1034
        return WorkingTreeFormat2().open(self, _found=True)
809
1035
 
823
1049
 
824
1050
    def create_branch(self):
825
1051
        """See BzrDir.create_branch."""
826
 
        from bzrlib.branch import BranchFormat
827
 
        return BranchFormat.get_default_format().initialize(self)
 
1052
        return self._format.get_branch_format().initialize(self)
828
1053
 
829
1054
    def create_repository(self, shared=False):
830
1055
        """See BzrDir.create_repository."""
833
1058
    def create_workingtree(self, revision_id=None):
834
1059
        """See BzrDir.create_workingtree."""
835
1060
        from bzrlib.workingtree import WorkingTreeFormat
836
 
        return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
 
1061
        return self._format.workingtree_format.initialize(self, revision_id)
 
1062
 
 
1063
    def destroy_workingtree(self):
 
1064
        """See BzrDir.destroy_workingtree."""
 
1065
        wt = self.open_workingtree(recommend_upgrade=False)
 
1066
        repository = wt.branch.repository
 
1067
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1068
        wt.revert([], old_tree=empty)
 
1069
        self.destroy_workingtree_metadata()
 
1070
 
 
1071
    def destroy_workingtree_metadata(self):
 
1072
        self.transport.delete_tree('checkout')
 
1073
 
 
1074
    def find_branch_format(self):
 
1075
        """Find the branch 'format' for this bzrdir.
 
1076
 
 
1077
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
1078
        """
 
1079
        from bzrlib.branch import BranchFormat
 
1080
        return BranchFormat.find_format(self)
837
1081
 
838
1082
    def _get_mkdir_mode(self):
839
1083
        """Figure out the mode to use when creating a bzrdir subdir."""
840
 
        temp_control = LockableFiles(self.transport, '', TransportLock)
 
1084
        temp_control = lockable_files.LockableFiles(self.transport, '',
 
1085
                                     lockable_files.TransportLock)
841
1086
        return temp_control._dir_mode
842
1087
 
 
1088
    def get_branch_reference(self):
 
1089
        """See BzrDir.get_branch_reference()."""
 
1090
        from bzrlib.branch import BranchFormat
 
1091
        format = BranchFormat.find_format(self)
 
1092
        return format.get_reference(self)
 
1093
 
843
1094
    def get_branch_transport(self, branch_format):
844
1095
        """See BzrDir.get_branch_transport()."""
845
1096
        if branch_format is None:
897
1148
                return True
898
1149
        except errors.NoRepositoryPresent:
899
1150
            pass
900
 
        # currently there are no other possible conversions for meta1 formats.
 
1151
        try:
 
1152
            if not isinstance(self.open_branch()._format,
 
1153
                              format.get_branch_format().__class__):
 
1154
                # the branch needs an upgrade.
 
1155
                return True
 
1156
        except errors.NotBranchError:
 
1157
            pass
 
1158
        try:
 
1159
            my_wt = self.open_workingtree(recommend_upgrade=False)
 
1160
            if not isinstance(my_wt._format,
 
1161
                              format.workingtree_format.__class__):
 
1162
                # the workingtree needs an upgrade.
 
1163
                return True
 
1164
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1165
            pass
901
1166
        return False
902
1167
 
903
1168
    def open_branch(self, unsupported=False):
904
1169
        """See BzrDir.open_branch."""
905
 
        from bzrlib.branch import BranchFormat
906
 
        format = BranchFormat.find_format(self)
 
1170
        format = self.find_branch_format()
907
1171
        self._check_supported(format, unsupported)
908
1172
        return format.open(self, _found=True)
909
1173
 
914
1178
        self._check_supported(format, unsupported)
915
1179
        return format.open(self, _found=True)
916
1180
 
917
 
    def open_workingtree(self, unsupported=False):
 
1181
    def open_workingtree(self, unsupported=False,
 
1182
            recommend_upgrade=True):
918
1183
        """See BzrDir.open_workingtree."""
919
1184
        from bzrlib.workingtree import WorkingTreeFormat
920
1185
        format = WorkingTreeFormat.find_format(self)
921
 
        self._check_supported(format, unsupported)
 
1186
        self._check_supported(format, unsupported,
 
1187
            recommend_upgrade,
 
1188
            basedir=self.root_transport.base)
922
1189
        return format.open(self, _found=True)
923
1190
 
924
1191
 
951
1218
    This is a list of BzrDirFormat objects.
952
1219
    """
953
1220
 
 
1221
    _control_server_formats = []
 
1222
    """The registered control server formats, e.g. RemoteBzrDirs.
 
1223
 
 
1224
    This is a list of BzrDirFormat objects.
 
1225
    """
 
1226
 
954
1227
    _lock_file_name = 'branch-lock'
955
1228
 
956
1229
    # _lock_class must be set in subclasses to the lock type, typ.
957
1230
    # TransportLock or LockDir
958
1231
 
959
1232
    @classmethod
960
 
    def find_format(klass, transport):
 
1233
    def find_format(klass, transport, _server_formats=True):
961
1234
        """Return the format present at transport."""
962
 
        for format in klass._control_formats:
 
1235
        if _server_formats:
 
1236
            formats = klass._control_server_formats + klass._control_formats
 
1237
        else:
 
1238
            formats = klass._control_formats
 
1239
        for format in formats:
963
1240
            try:
964
1241
                return format.probe_transport(transport)
965
1242
            except errors.NotBranchError:
969
1246
 
970
1247
    @classmethod
971
1248
    def probe_transport(klass, transport):
972
 
        """Return the .bzrdir style transport present at URL."""
 
1249
        """Return the .bzrdir style format present in a directory."""
973
1250
        try:
974
1251
            format_string = transport.get(".bzr/branch-format").read()
 
1252
        except errors.NoSuchFile:
 
1253
            raise errors.NotBranchError(path=transport.base)
 
1254
 
 
1255
        try:
975
1256
            return klass._formats[format_string]
976
 
        except errors.NoSuchFile:
977
 
            raise errors.NotBranchError(path=transport.base)
978
1257
        except KeyError:
979
 
            raise errors.UnknownFormatError(format_string)
 
1258
            raise errors.UnknownFormatError(format=format_string)
980
1259
 
981
1260
    @classmethod
982
1261
    def get_default_format(klass):
1017
1296
        """Initialize a new bzrdir in the base directory of a Transport."""
1018
1297
        # Since we don't have a .bzr directory, inherit the
1019
1298
        # mode from the root directory
1020
 
        temp_control = LockableFiles(transport, '', TransportLock)
 
1299
        temp_control = lockable_files.LockableFiles(transport,
 
1300
                            '', lockable_files.TransportLock)
1021
1301
        temp_control._transport.mkdir('.bzr',
1022
1302
                                      # FIXME: RBC 20060121 don't peek under
1023
1303
                                      # the covers
1032
1312
                      ('branch-format', self.get_format_string()),
1033
1313
                      ]
1034
1314
        # NB: no need to escape relative paths that are url safe.
1035
 
        control_files = LockableFiles(control, self._lock_file_name, 
1036
 
                                      self._lock_class)
 
1315
        control_files = lockable_files.LockableFiles(control,
 
1316
                            self._lock_file_name, self._lock_class)
1037
1317
        control_files.create_lock()
1038
1318
        control_files.lock_write()
1039
1319
        try:
1052
1332
        """
1053
1333
        return True
1054
1334
 
 
1335
    def same_model(self, target_format):
 
1336
        return (self.repository_format.rich_root_data == 
 
1337
            target_format.rich_root_data)
 
1338
 
1055
1339
    @classmethod
1056
1340
    def known_formats(klass):
1057
1341
        """Return all the known formats.
1077
1361
        _found is a private parameter, do not use it.
1078
1362
        """
1079
1363
        if not _found:
1080
 
            assert isinstance(BzrDirFormat.find_format(transport),
1081
 
                              self.__class__)
 
1364
            found_format = BzrDirFormat.find_format(transport)
 
1365
            if not isinstance(found_format, self.__class__):
 
1366
                raise AssertionError("%s was asked to open %s, but it seems to need "
 
1367
                        "format %s" 
 
1368
                        % (self, transport, found_format))
1082
1369
        return self._open(transport)
1083
1370
 
1084
1371
    def _open(self, transport):
1095
1382
 
1096
1383
    @classmethod
1097
1384
    def register_control_format(klass, format):
1098
 
        """Register a format that does not use '.bzrdir' for its control dir.
 
1385
        """Register a format that does not use '.bzr' for its control dir.
1099
1386
 
1100
1387
        TODO: This should be pulled up into a 'ControlDirFormat' base class
1101
1388
        which BzrDirFormat can inherit from, and renamed to register_format 
1105
1392
        klass._control_formats.append(format)
1106
1393
 
1107
1394
    @classmethod
 
1395
    def register_control_server_format(klass, format):
 
1396
        """Register a control format for client-server environments.
 
1397
 
 
1398
        These formats will be tried before ones registered with
 
1399
        register_control_format.  This gives implementations that decide to the
 
1400
        chance to grab it before anything looks at the contents of the format
 
1401
        file.
 
1402
        """
 
1403
        klass._control_server_formats.append(format)
 
1404
 
 
1405
    @classmethod
 
1406
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1108
1407
    def set_default_format(klass, format):
 
1408
        klass._set_default_format(format)
 
1409
 
 
1410
    @classmethod
 
1411
    def _set_default_format(klass, format):
 
1412
        """Set default format (for testing behavior of defaults only)"""
1109
1413
        klass._default_format = format
1110
1414
 
1111
1415
    def __str__(self):
1121
1425
        klass._control_formats.remove(format)
1122
1426
 
1123
1427
 
1124
 
# register BzrDirFormat as a control format
1125
 
BzrDirFormat.register_control_format(BzrDirFormat)
1126
 
 
1127
 
 
1128
1428
class BzrDirFormat4(BzrDirFormat):
1129
1429
    """Bzr dir format 4.
1130
1430
 
1138
1438
    removed in format 5; write support for this format has been removed.
1139
1439
    """
1140
1440
 
1141
 
    _lock_class = TransportLock
 
1441
    _lock_class = lockable_files.TransportLock
1142
1442
 
1143
1443
    def get_format_string(self):
1144
1444
        """See BzrDirFormat.get_format_string()."""
1172
1472
 
1173
1473
    def __return_repository_format(self):
1174
1474
        """Circular import protection."""
1175
 
        from bzrlib.repository import RepositoryFormat4
1176
 
        return RepositoryFormat4(self)
 
1475
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
 
1476
        return RepositoryFormat4()
1177
1477
    repository_format = property(__return_repository_format)
1178
1478
 
1179
1479
 
1188
1488
       Unhashed stores in the repository.
1189
1489
    """
1190
1490
 
1191
 
    _lock_class = TransportLock
 
1491
    _lock_class = lockable_files.TransportLock
1192
1492
 
1193
1493
    def get_format_string(self):
1194
1494
        """See BzrDirFormat.get_format_string()."""
1212
1512
        Except when they are being cloned.
1213
1513
        """
1214
1514
        from bzrlib.branch import BzrBranchFormat4
1215
 
        from bzrlib.repository import RepositoryFormat5
 
1515
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
1216
1516
        from bzrlib.workingtree import WorkingTreeFormat2
1217
1517
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1218
1518
        RepositoryFormat5().initialize(result, _internal=True)
1219
1519
        if not _cloning:
1220
 
            BzrBranchFormat4().initialize(result)
1221
 
            WorkingTreeFormat2().initialize(result)
 
1520
            branch = BzrBranchFormat4().initialize(result)
 
1521
            try:
 
1522
                WorkingTreeFormat2().initialize(result)
 
1523
            except errors.NotLocalUrl:
 
1524
                # Even though we can't access the working tree, we need to
 
1525
                # create its control files.
 
1526
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1222
1527
        return result
1223
1528
 
1224
1529
    def _open(self, transport):
1227
1532
 
1228
1533
    def __return_repository_format(self):
1229
1534
        """Circular import protection."""
1230
 
        from bzrlib.repository import RepositoryFormat5
1231
 
        return RepositoryFormat5(self)
 
1535
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
 
1536
        return RepositoryFormat5()
1232
1537
    repository_format = property(__return_repository_format)
1233
1538
 
1234
1539
 
1242
1547
     - Format 6 repositories [always]
1243
1548
    """
1244
1549
 
1245
 
    _lock_class = TransportLock
 
1550
    _lock_class = lockable_files.TransportLock
1246
1551
 
1247
1552
    def get_format_string(self):
1248
1553
        """See BzrDirFormat.get_format_string()."""
1266
1571
        Except when they are being cloned.
1267
1572
        """
1268
1573
        from bzrlib.branch import BzrBranchFormat4
1269
 
        from bzrlib.repository import RepositoryFormat6
 
1574
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
1270
1575
        from bzrlib.workingtree import WorkingTreeFormat2
1271
1576
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1272
1577
        RepositoryFormat6().initialize(result, _internal=True)
1273
1578
        if not _cloning:
1274
 
            BzrBranchFormat4().initialize(result)
 
1579
            branch = BzrBranchFormat4().initialize(result)
1275
1580
            try:
1276
1581
                WorkingTreeFormat2().initialize(result)
1277
1582
            except errors.NotLocalUrl:
1278
 
                # emulate pre-check behaviour for working tree and silently 
1279
 
                # fail.
1280
 
                pass
 
1583
                # Even though we can't access the working tree, we need to
 
1584
                # create its control files.
 
1585
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1281
1586
        return result
1282
1587
 
1283
1588
    def _open(self, transport):
1286
1591
 
1287
1592
    def __return_repository_format(self):
1288
1593
        """Circular import protection."""
1289
 
        from bzrlib.repository import RepositoryFormat6
1290
 
        return RepositoryFormat6(self)
 
1594
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
 
1595
        return RepositoryFormat6()
1291
1596
    repository_format = property(__return_repository_format)
1292
1597
 
1293
1598
 
1302
1607
     - Format 7 repositories [optional]
1303
1608
    """
1304
1609
 
1305
 
    _lock_class = LockDir
 
1610
    _lock_class = lockdir.LockDir
 
1611
 
 
1612
    def __init__(self):
 
1613
        self._workingtree_format = None
 
1614
        self._branch_format = None
 
1615
 
 
1616
    def __eq__(self, other):
 
1617
        if other.__class__ is not self.__class__:
 
1618
            return False
 
1619
        if other.repository_format != self.repository_format:
 
1620
            return False
 
1621
        if other.workingtree_format != self.workingtree_format:
 
1622
            return False
 
1623
        return True
 
1624
 
 
1625
    def __ne__(self, other):
 
1626
        return not self == other
 
1627
 
 
1628
    def get_branch_format(self):
 
1629
        if self._branch_format is None:
 
1630
            from bzrlib.branch import BranchFormat
 
1631
            self._branch_format = BranchFormat.get_default_format()
 
1632
        return self._branch_format
 
1633
 
 
1634
    def set_branch_format(self, format):
 
1635
        self._branch_format = format
1306
1636
 
1307
1637
    def get_converter(self, format=None):
1308
1638
        """See BzrDirFormat.get_converter()."""
1338
1668
 
1339
1669
    repository_format = property(__return_repository_format, __set_repository_format)
1340
1670
 
1341
 
 
 
1671
    def __get_workingtree_format(self):
 
1672
        if self._workingtree_format is None:
 
1673
            from bzrlib.workingtree import WorkingTreeFormat
 
1674
            self._workingtree_format = WorkingTreeFormat.get_default_format()
 
1675
        return self._workingtree_format
 
1676
 
 
1677
    def __set_workingtree_format(self, wt_format):
 
1678
        self._workingtree_format = wt_format
 
1679
 
 
1680
    workingtree_format = property(__get_workingtree_format,
 
1681
                                  __set_workingtree_format)
 
1682
 
 
1683
 
 
1684
# Register bzr control format
 
1685
BzrDirFormat.register_control_format(BzrDirFormat)
 
1686
 
 
1687
# Register bzr formats
1342
1688
BzrDirFormat.register_format(BzrDirFormat4())
1343
1689
BzrDirFormat.register_format(BzrDirFormat5())
1344
1690
BzrDirFormat.register_format(BzrDirFormat6())
1345
1691
__default_format = BzrDirMetaFormat1()
1346
1692
BzrDirFormat.register_format(__default_format)
1347
 
BzrDirFormat.set_default_format(__default_format)
 
1693
BzrDirFormat._default_format = __default_format
1348
1694
 
1349
1695
 
1350
1696
class BzrDirTestProviderAdapter(object):
1356
1702
    easy to identify.
1357
1703
    """
1358
1704
 
1359
 
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1705
    def __init__(self, vfs_factory, transport_server, transport_readonly_server,
 
1706
        formats):
 
1707
        """Create an object to adapt tests.
 
1708
 
 
1709
        :param vfs_server: A factory to create a Transport Server which has
 
1710
            all the VFS methods working, and is writable.
 
1711
        """
 
1712
        self._vfs_factory = vfs_factory
1360
1713
        self._transport_server = transport_server
1361
1714
        self._transport_readonly_server = transport_readonly_server
1362
1715
        self._formats = formats
1363
1716
    
1364
1717
    def adapt(self, test):
1365
 
        result = TestSuite()
 
1718
        result = unittest.TestSuite()
1366
1719
        for format in self._formats:
1367
1720
            new_test = deepcopy(test)
 
1721
            new_test.vfs_transport_factory = self._vfs_factory
1368
1722
            new_test.transport_server = self._transport_server
1369
1723
            new_test.transport_readonly_server = self._transport_readonly_server
1370
1724
            new_test.bzrdir_format = format
1376
1730
        return result
1377
1731
 
1378
1732
 
1379
 
class ScratchDir(BzrDir6):
1380
 
    """Special test class: a bzrdir that cleans up itself..
1381
 
 
1382
 
    >>> d = ScratchDir()
1383
 
    >>> base = d.transport.base
1384
 
    >>> isdir(base)
1385
 
    True
1386
 
    >>> b.transport.__del__()
1387
 
    >>> isdir(base)
1388
 
    False
1389
 
    """
1390
 
 
1391
 
    def __init__(self, files=[], dirs=[], transport=None):
1392
 
        """Make a test branch.
1393
 
 
1394
 
        This creates a temporary directory and runs init-tree in it.
1395
 
 
1396
 
        If any files are listed, they are created in the working copy.
1397
 
        """
1398
 
        if transport is None:
1399
 
            transport = bzrlib.transport.local.ScratchTransport()
1400
 
            # local import for scope restriction
1401
 
            BzrDirFormat6().initialize(transport.base)
1402
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1403
 
            self.create_repository()
1404
 
            self.create_branch()
1405
 
            self.create_workingtree()
1406
 
        else:
1407
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1408
 
 
1409
 
        # BzrBranch creates a clone to .bzr and then forgets about the
1410
 
        # original transport. A ScratchTransport() deletes itself and
1411
 
        # everything underneath it when it goes away, so we need to
1412
 
        # grab a local copy to prevent that from happening
1413
 
        self._transport = transport
1414
 
 
1415
 
        for d in dirs:
1416
 
            self._transport.mkdir(d)
1417
 
            
1418
 
        for f in files:
1419
 
            self._transport.put(f, 'content of %s' % f)
1420
 
 
1421
 
    def clone(self):
1422
 
        """
1423
 
        >>> orig = ScratchDir(files=["file1", "file2"])
1424
 
        >>> os.listdir(orig.base)
1425
 
        [u'.bzr', u'file1', u'file2']
1426
 
        >>> clone = orig.clone()
1427
 
        >>> if os.name != 'nt':
1428
 
        ...   os.path.samefile(orig.base, clone.base)
1429
 
        ... else:
1430
 
        ...   orig.base == clone.base
1431
 
        ...
1432
 
        False
1433
 
        >>> os.listdir(clone.base)
1434
 
        [u'.bzr', u'file1', u'file2']
1435
 
        """
1436
 
        from shutil import copytree
1437
 
        from bzrlib.osutils import mkdtemp
1438
 
        base = mkdtemp()
1439
 
        os.rmdir(base)
1440
 
        copytree(self.base, base, symlinks=True)
1441
 
        return ScratchDir(
1442
 
            transport=bzrlib.transport.local.ScratchTransport(base))
1443
 
 
1444
 
 
1445
1733
class Converter(object):
1446
1734
    """Converts a disk format object from one format to another."""
1447
1735
 
1532
1820
        self.bzrdir.transport.delete_tree('text-store')
1533
1821
 
1534
1822
    def _convert_working_inv(self):
1535
 
        inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1536
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1823
        inv = xml4.serializer_v4.read_inventory(
 
1824
                    self.branch.control_files.get('inventory'))
 
1825
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1537
1826
        # FIXME inventory is a working tree change.
1538
 
        self.branch.control_files.put('inventory', new_inv_xml)
 
1827
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1539
1828
 
1540
1829
    def _write_all_weaves(self):
1541
1830
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1565
1854
                                                      prefixed=False,
1566
1855
                                                      compressed=True))
1567
1856
        try:
1568
 
            transaction = bzrlib.transactions.WriteTransaction()
 
1857
            transaction = WriteTransaction()
1569
1858
            for i, rev_id in enumerate(self.converted_revs):
1570
1859
                self.pb.update('write revision', i, len(self.converted_revs))
1571
1860
                _revision_store.add_revision(self.revisions[rev_id], transaction)
1597
1886
    def _load_old_inventory(self, rev_id):
1598
1887
        assert rev_id not in self.converted_revs
1599
1888
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1600
 
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
 
1889
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
 
1890
        inv.revision_id = rev_id
1601
1891
        rev = self.revisions[rev_id]
1602
1892
        if rev.inventory_sha1:
1603
1893
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1607
1897
    def _load_updated_inventory(self, rev_id):
1608
1898
        assert rev_id in self.converted_revs
1609
1899
        inv_xml = self.inv_weave.get_text(rev_id)
1610
 
        inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
1900
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1611
1901
        return inv
1612
1902
 
1613
1903
    def _convert_one_rev(self, rev_id):
1623
1913
    def _store_new_weave(self, rev, inv, present_parents):
1624
1914
        # the XML is now updated with text versions
1625
1915
        if __debug__:
1626
 
            for file_id in inv:
1627
 
                ie = inv[file_id]
1628
 
                if ie.kind == 'root_directory':
1629
 
                    continue
1630
 
                assert hasattr(ie, 'revision'), \
 
1916
            entries = inv.iter_entries()
 
1917
            entries.next()
 
1918
            for path, ie in entries:
 
1919
                assert getattr(ie, 'revision', None) is not None, \
1631
1920
                    'no revision on {%s} in {%s}' % \
1632
1921
                    (file_id, rev.revision_id)
1633
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1922
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1634
1923
        new_inv_sha1 = sha_string(new_inv_xml)
1635
1924
        self.inv_weave.add_lines(rev.revision_id, 
1636
1925
                                 present_parents,
1645
1934
        mutter('converting texts of revision {%s}',
1646
1935
               rev_id)
1647
1936
        parent_invs = map(self._load_updated_inventory, present_parents)
1648
 
        for file_id in inv:
1649
 
            ie = inv[file_id]
 
1937
        entries = inv.iter_entries()
 
1938
        entries.next()
 
1939
        for path, ie in entries:
1650
1940
            self._convert_file_version(rev, ie, parent_invs)
1651
1941
 
1652
1942
    def _convert_file_version(self, rev, ie, parent_invs):
1655
1945
        The file needs to be added into the weave if it is a merge
1656
1946
        of >=2 parents or if it's changed from its parent.
1657
1947
        """
1658
 
        if ie.kind == 'root_directory':
1659
 
            return
1660
1948
        file_id = ie.file_id
1661
1949
        rev_id = rev.revision_id
1662
1950
        w = self.text_weaves.get(file_id)
1670
1958
                                                  entry_vf=w)
1671
1959
        for old_revision in previous_entries:
1672
1960
                # if this fails, its a ghost ?
1673
 
                assert old_revision in self.converted_revs 
 
1961
                assert old_revision in self.converted_revs, \
 
1962
                    "Revision {%s} not in converted_revs" % old_revision
1674
1963
        self.snapshot_ie(previous_entries, ie, w, rev_id)
1675
1964
        del ie.text_id
1676
1965
        assert getattr(ie, 'revision', None) is not None
1763
2052
 
1764
2053
    def convert(self, to_convert, pb):
1765
2054
        """See Converter.convert()."""
 
2055
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
 
2056
        from bzrlib.branch import BzrBranchFormat5
1766
2057
        self.bzrdir = to_convert
1767
2058
        self.pb = pb
1768
2059
        self.count = 0
1797
2088
        # we hard code the formats here because we are converting into
1798
2089
        # the meta format. The meta format upgrader can take this to a 
1799
2090
        # future format within each component.
1800
 
        self.put_format('repository', bzrlib.repository.RepositoryFormat7())
 
2091
        self.put_format('repository', RepositoryFormat7())
1801
2092
        for entry in repository_names:
1802
2093
            self.move_entry('repository', entry)
1803
2094
 
1804
2095
        self.step('Upgrading branch      ')
1805
2096
        self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1806
2097
        self.make_lock('branch')
1807
 
        self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
 
2098
        self.put_format('branch', BzrBranchFormat5())
1808
2099
        branch_files = [('revision-history', True),
1809
2100
                        ('branch-name', True),
1810
2101
                        ('parent', False)]
1811
2102
        for entry in branch_files:
1812
2103
            self.move_entry('branch', entry)
1813
2104
 
1814
 
        self.step('Upgrading working tree')
1815
 
        self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1816
 
        self.make_lock('checkout')
1817
 
        self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1818
 
        self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1819
2105
        checkout_files = [('pending-merges', True),
1820
2106
                          ('inventory', True),
1821
2107
                          ('stat-cache', False)]
1822
 
        for entry in checkout_files:
1823
 
            self.move_entry('checkout', entry)
1824
 
        if last_revision is not None:
1825
 
            self.bzrdir._control_files.put_utf8('checkout/last-revision',
1826
 
                                                last_revision)
1827
 
        self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
 
2108
        # If a mandatory checkout file is not present, the branch does not have
 
2109
        # a functional checkout. Do not create a checkout in the converted
 
2110
        # branch.
 
2111
        for name, mandatory in checkout_files:
 
2112
            if mandatory and name not in bzrcontents:
 
2113
                has_checkout = False
 
2114
                break
 
2115
        else:
 
2116
            has_checkout = True
 
2117
        if not has_checkout:
 
2118
            self.pb.note('No working tree.')
 
2119
            # If some checkout files are there, we may as well get rid of them.
 
2120
            for name, mandatory in checkout_files:
 
2121
                if name in bzrcontents:
 
2122
                    self.bzrdir.transport.delete(name)
 
2123
        else:
 
2124
            from bzrlib.workingtree import WorkingTreeFormat3
 
2125
            self.step('Upgrading working tree')
 
2126
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
 
2127
            self.make_lock('checkout')
 
2128
            self.put_format(
 
2129
                'checkout', WorkingTreeFormat3())
 
2130
            self.bzrdir.transport.delete_multi(
 
2131
                self.garbage_inventories, self.pb)
 
2132
            for entry in checkout_files:
 
2133
                self.move_entry('checkout', entry)
 
2134
            if last_revision is not None:
 
2135
                self.bzrdir._control_files.put_utf8(
 
2136
                    'checkout/last-revision', last_revision)
 
2137
        self.bzrdir._control_files.put_utf8(
 
2138
            'branch-format', BzrDirMetaFormat1().get_format_string())
1828
2139
        return BzrDir.open(self.bzrdir.root_transport.base)
1829
2140
 
1830
2141
    def make_lock(self, name):
1831
2142
        """Make a lock for the new control dir name."""
1832
2143
        self.step('Make %s lock' % name)
1833
 
        ld = LockDir(self.bzrdir.transport, 
1834
 
                     '%s/lock' % name,
1835
 
                     file_modebits=self.file_mode,
1836
 
                     dir_modebits=self.dir_mode)
 
2144
        ld = lockdir.LockDir(self.bzrdir.transport,
 
2145
                             '%s/lock' % name,
 
2146
                             file_modebits=self.file_mode,
 
2147
                             dir_modebits=self.dir_mode)
1837
2148
        ld.create()
1838
2149
 
1839
2150
    def move_entry(self, new_dir, entry):
1878
2189
                self.pb.note('starting repository conversion')
1879
2190
                converter = CopyConverter(self.target_format.repository_format)
1880
2191
                converter.convert(repo, pb)
 
2192
        try:
 
2193
            branch = self.bzrdir.open_branch()
 
2194
        except errors.NotBranchError:
 
2195
            pass
 
2196
        else:
 
2197
            # TODO: conversions of Branch and Tree should be done by
 
2198
            # InterXFormat lookups
 
2199
            # Avoid circular imports
 
2200
            from bzrlib import branch as _mod_branch
 
2201
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
 
2202
                self.target_format.get_branch_format().__class__ is
 
2203
                _mod_branch.BzrBranchFormat6):
 
2204
                branch_converter = _mod_branch.Converter5to6()
 
2205
                branch_converter.convert(branch)
 
2206
        try:
 
2207
            tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
 
2208
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2209
            pass
 
2210
        else:
 
2211
            # TODO: conversions of Branch and Tree should be done by
 
2212
            # InterXFormat lookups
 
2213
            if (isinstance(tree, workingtree.WorkingTree3) and
 
2214
                not isinstance(tree, workingtree_4.WorkingTree4) and
 
2215
                isinstance(self.target_format.workingtree_format,
 
2216
                    workingtree_4.WorkingTreeFormat4)):
 
2217
                workingtree_4.Converter3to4().convert(tree)
1881
2218
        return to_convert
 
2219
 
 
2220
 
 
2221
# This is not in remote.py because it's small, and needs to be registered.
 
2222
# Putting it in remote.py creates a circular import problem.
 
2223
# we can make it a lazy object if the control formats is turned into something
 
2224
# like a registry.
 
2225
class RemoteBzrDirFormat(BzrDirMetaFormat1):
 
2226
    """Format representing bzrdirs accessed via a smart server"""
 
2227
 
 
2228
    def get_format_description(self):
 
2229
        return 'bzr remote bzrdir'
 
2230
    
 
2231
    @classmethod
 
2232
    def probe_transport(klass, transport):
 
2233
        """Return a RemoteBzrDirFormat object if it looks possible."""
 
2234
        try:
 
2235
            transport.get_smart_client()
 
2236
        except (NotImplementedError, AttributeError,
 
2237
                errors.TransportNotPossible):
 
2238
            # no smart server, so not a branch for this format type.
 
2239
            raise errors.NotBranchError(path=transport.base)
 
2240
        else:
 
2241
            return klass()
 
2242
 
 
2243
    def initialize_on_transport(self, transport):
 
2244
        try:
 
2245
            # hand off the request to the smart server
 
2246
            medium = transport.get_smart_medium()
 
2247
        except errors.NoSmartMedium:
 
2248
            # TODO: lookup the local format from a server hint.
 
2249
            local_dir_format = BzrDirMetaFormat1()
 
2250
            return local_dir_format.initialize_on_transport(transport)
 
2251
        client = _SmartClient(medium)
 
2252
        path = client.remote_path_from_transport(transport)
 
2253
        response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
 
2254
        assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
 
2255
        return remote.RemoteBzrDir(transport)
 
2256
 
 
2257
    def _open(self, transport):
 
2258
        return remote.RemoteBzrDir(transport)
 
2259
 
 
2260
    def __eq__(self, other):
 
2261
        if not isinstance(other, RemoteBzrDirFormat):
 
2262
            return False
 
2263
        return self.get_format_description() == other.get_format_description()
 
2264
 
 
2265
 
 
2266
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
 
2267
 
 
2268
 
 
2269
class BzrDirFormatInfo(object):
 
2270
 
 
2271
    def __init__(self, native, deprecated, hidden):
 
2272
        self.deprecated = deprecated
 
2273
        self.native = native
 
2274
        self.hidden = hidden
 
2275
 
 
2276
 
 
2277
class BzrDirFormatRegistry(registry.Registry):
 
2278
    """Registry of user-selectable BzrDir subformats.
 
2279
    
 
2280
    Differs from BzrDirFormat._control_formats in that it provides sub-formats,
 
2281
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
2282
    """
 
2283
 
 
2284
    def register_metadir(self, key,
 
2285
             repository_format, help, native=True, deprecated=False,
 
2286
             branch_format=None,
 
2287
             tree_format=None,
 
2288
             hidden=False):
 
2289
        """Register a metadir subformat.
 
2290
 
 
2291
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
 
2292
        by the Repository format.
 
2293
 
 
2294
        :param repository_format: The fully-qualified repository format class
 
2295
            name as a string.
 
2296
        :param branch_format: Fully-qualified branch format class name as
 
2297
            a string.
 
2298
        :param tree_format: Fully-qualified tree format class name as
 
2299
            a string.
 
2300
        """
 
2301
        # This should be expanded to support setting WorkingTree and Branch
 
2302
        # formats, once BzrDirMetaFormat1 supports that.
 
2303
        def _load(full_name):
 
2304
            mod_name, factory_name = full_name.rsplit('.', 1)
 
2305
            try:
 
2306
                mod = __import__(mod_name, globals(), locals(),
 
2307
                        [factory_name])
 
2308
            except ImportError, e:
 
2309
                raise ImportError('failed to load %s: %s' % (full_name, e))
 
2310
            try:
 
2311
                factory = getattr(mod, factory_name)
 
2312
            except AttributeError:
 
2313
                raise AttributeError('no factory %s in module %r'
 
2314
                    % (full_name, mod))
 
2315
            return factory()
 
2316
 
 
2317
        def helper():
 
2318
            bd = BzrDirMetaFormat1()
 
2319
            if branch_format is not None:
 
2320
                bd.set_branch_format(_load(branch_format))
 
2321
            if tree_format is not None:
 
2322
                bd.workingtree_format = _load(tree_format)
 
2323
            if repository_format is not None:
 
2324
                bd.repository_format = _load(repository_format)
 
2325
            return bd
 
2326
        self.register(key, helper, help, native, deprecated, hidden)
 
2327
 
 
2328
    def register(self, key, factory, help, native=True, deprecated=False,
 
2329
                 hidden=False):
 
2330
        """Register a BzrDirFormat factory.
 
2331
        
 
2332
        The factory must be a callable that takes one parameter: the key.
 
2333
        It must produce an instance of the BzrDirFormat when called.
 
2334
 
 
2335
        This function mainly exists to prevent the info object from being
 
2336
        supplied directly.
 
2337
        """
 
2338
        registry.Registry.register(self, key, factory, help, 
 
2339
            BzrDirFormatInfo(native, deprecated, hidden))
 
2340
 
 
2341
    def register_lazy(self, key, module_name, member_name, help, native=True,
 
2342
                      deprecated=False, hidden=False):
 
2343
        registry.Registry.register_lazy(self, key, module_name, member_name, 
 
2344
            help, BzrDirFormatInfo(native, deprecated, hidden))
 
2345
 
 
2346
    def set_default(self, key):
 
2347
        """Set the 'default' key to be a clone of the supplied key.
 
2348
        
 
2349
        This method must be called once and only once.
 
2350
        """
 
2351
        registry.Registry.register(self, 'default', self.get(key), 
 
2352
            self.get_help(key), info=self.get_info(key))
 
2353
 
 
2354
    def set_default_repository(self, key):
 
2355
        """Set the FormatRegistry default and Repository default.
 
2356
        
 
2357
        This is a transitional method while Repository.set_default_format
 
2358
        is deprecated.
 
2359
        """
 
2360
        if 'default' in self:
 
2361
            self.remove('default')
 
2362
        self.set_default(key)
 
2363
        format = self.get('default')()
 
2364
        assert isinstance(format, BzrDirMetaFormat1)
 
2365
 
 
2366
    def make_bzrdir(self, key):
 
2367
        return self.get(key)()
 
2368
 
 
2369
    def help_topic(self, topic):
 
2370
        output = textwrap.dedent("""\
 
2371
            Bazaar directory formats
 
2372
            ------------------------
 
2373
 
 
2374
            These formats can be used for creating branches, working trees, and
 
2375
            repositories.
 
2376
 
 
2377
            """)
 
2378
        default_help = self.get_help('default')
 
2379
        help_pairs = []
 
2380
        for key in self.keys():
 
2381
            if key == 'default':
 
2382
                continue
 
2383
            help = self.get_help(key)
 
2384
            if help == default_help:
 
2385
                default_realkey = key
 
2386
            else:
 
2387
                help_pairs.append((key, help))
 
2388
 
 
2389
        def wrapped(key, help, info):
 
2390
            if info.native:
 
2391
                help = '(native) ' + help
 
2392
            return '  %s:\n%s\n\n' % (key, 
 
2393
                    textwrap.fill(help, initial_indent='    ', 
 
2394
                    subsequent_indent='    '))
 
2395
        output += wrapped('%s/default' % default_realkey, default_help,
 
2396
                          self.get_info('default'))
 
2397
        deprecated_pairs = []
 
2398
        for key, help in help_pairs:
 
2399
            info = self.get_info(key)
 
2400
            if info.hidden:
 
2401
                continue
 
2402
            elif info.deprecated:
 
2403
                deprecated_pairs.append((key, help))
 
2404
            else:
 
2405
                output += wrapped(key, help, info)
 
2406
        if len(deprecated_pairs) > 0:
 
2407
            output += "Deprecated formats\n------------------\n\n"
 
2408
            for key, help in deprecated_pairs:
 
2409
                info = self.get_info(key)
 
2410
                output += wrapped(key, help, info)
 
2411
 
 
2412
        return output
 
2413
 
 
2414
 
 
2415
format_registry = BzrDirFormatRegistry()
 
2416
format_registry.register('weave', BzrDirFormat6,
 
2417
    'Pre-0.8 format.  Slower than knit and does not'
 
2418
    ' support checkouts or shared repositories.',
 
2419
    deprecated=True)
 
2420
format_registry.register_metadir('knit',
 
2421
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2422
    'Format using knits.  Recommended for interoperation with bzr <= 0.14.',
 
2423
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2424
    tree_format='bzrlib.workingtree.WorkingTreeFormat3')
 
2425
format_registry.register_metadir('metaweave',
 
2426
    'bzrlib.repofmt.weaverepo.RepositoryFormat7',
 
2427
    'Transitional format in 0.8.  Slower than knit.',
 
2428
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2429
    tree_format='bzrlib.workingtree.WorkingTreeFormat3',
 
2430
    deprecated=True)
 
2431
format_registry.register_metadir('dirstate',
 
2432
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2433
    help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
 
2434
        'above when accessed over the network.',
 
2435
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2436
    # this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
 
2437
    # directly from workingtree_4 triggers a circular import.
 
2438
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2439
    )
 
2440
format_registry.register_metadir('dirstate-tags',
 
2441
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2442
    help='New in 0.15: Fast local operations and improved scaling for '
 
2443
        'network operations. Additionally adds support for tags.'
 
2444
        ' Incompatible with bzr < 0.15.',
 
2445
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2446
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2447
    )
 
2448
format_registry.register_metadir('dirstate-with-subtree',
 
2449
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
 
2450
    help='New in 0.15: Fast local operations and improved scaling for '
 
2451
        'network operations. Additionally adds support for versioning nested '
 
2452
        'bzr branches. Incompatible with bzr < 0.15.',
 
2453
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2454
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2455
    hidden=True,
 
2456
    )
 
2457
format_registry.set_default('dirstate')