~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-07-20 05:45:55 UTC
  • mfrom: (2635.1.1 ianc-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070720054555-bffj18ybejmgiv50
(Ian Clatworthy) Clarify use of underscores in HACKING

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
 
from copy import deepcopy
 
23
# TODO: Can we move specific formats into separate modules to make this file
 
24
# smaller?
 
25
 
 
26
from cStringIO import StringIO
24
27
import os
25
 
from cStringIO import StringIO
26
 
from unittest import TestSuite
 
28
import textwrap
 
29
 
 
30
from bzrlib.lazy_import import lazy_import
 
31
lazy_import(globals(), """
 
32
from stat import S_ISDIR
27
33
 
28
34
import bzrlib
29
 
import bzrlib.errors as errors
30
 
from bzrlib.lockable_files import LockableFiles, TransportLock
31
 
from bzrlib.lockdir import LockDir
 
35
from bzrlib import (
 
36
    errors,
 
37
    lockable_files,
 
38
    lockdir,
 
39
    registry,
 
40
    remote,
 
41
    revision as _mod_revision,
 
42
    symbol_versioning,
 
43
    ui,
 
44
    urlutils,
 
45
    xml4,
 
46
    xml5,
 
47
    workingtree,
 
48
    workingtree_4,
 
49
    )
32
50
from bzrlib.osutils import (
33
 
                            abspath,
34
 
                            pathjoin,
35
 
                            safe_unicode,
36
 
                            sha_strings,
37
 
                            sha_string,
38
 
                            )
 
51
    safe_unicode,
 
52
    sha_strings,
 
53
    sha_string,
 
54
    )
 
55
from bzrlib.smart.client import _SmartClient
 
56
from bzrlib.smart import protocol
39
57
from bzrlib.store.revision.text import TextRevisionStore
40
58
from bzrlib.store.text import TextStore
41
59
from bzrlib.store.versioned import WeaveStore
42
 
from bzrlib.symbol_versioning import *
43
 
from bzrlib.trace import mutter
44
60
from bzrlib.transactions import WriteTransaction
45
 
from bzrlib.transport import get_transport
 
61
from bzrlib.transport import (
 
62
    do_catching_redirections,
 
63
    get_transport,
 
64
    )
 
65
from bzrlib.weave import Weave
 
66
""")
 
67
 
 
68
from bzrlib.trace import (
 
69
    mutter,
 
70
    note,
 
71
    )
46
72
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
73
 
52
74
 
53
75
class BzrDir(object):
68
90
        If there is a tree, the tree is opened and break_lock() called.
69
91
        Otherwise, branch is tried, and finally repository.
70
92
        """
 
93
        # XXX: This seems more like a UI function than something that really
 
94
        # belongs in this class.
71
95
        try:
72
96
            thing_to_unlock = self.open_workingtree()
73
97
        except (errors.NotLocalUrl, errors.NoWorkingTree):
84
108
        """Return true if this bzrdir is one whose format we can convert from."""
85
109
        return True
86
110
 
 
111
    def check_conversion_target(self, target_format):
 
112
        target_repo_format = target_format.repository_format
 
113
        source_repo_format = self._format.repository_format
 
114
        source_repo_format.check_conversion_target(target_repo_format)
 
115
 
87
116
    @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.
 
117
    def _check_supported(format, allow_unsupported,
 
118
        recommend_upgrade=True,
 
119
        basedir=None):
 
120
        """Give an error or warning on old formats.
 
121
 
 
122
        :param format: may be any kind of format - workingtree, branch, 
 
123
        or repository.
 
124
 
 
125
        :param allow_unsupported: If true, allow opening 
 
126
        formats that are strongly deprecated, and which may 
 
127
        have limited functionality.
 
128
 
 
129
        :param recommend_upgrade: If true (default), warn
 
130
        the user through the ui object that they may wish
 
131
        to upgrade the object.
92
132
        """
 
133
        # TODO: perhaps move this into a base Format class; it's not BzrDir
 
134
        # specific. mbp 20070323
93
135
        if not allow_unsupported and not format.is_supported():
94
136
            # 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'])
 
137
            raise errors.UnsupportedFormatError(format=format)
 
138
        if recommend_upgrade \
 
139
            and getattr(format, 'upgrade_recommended', False):
 
140
            ui.ui_factory.recommend_upgrade(
 
141
                format.get_format_description(),
 
142
                basedir)
100
143
 
101
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
144
    def clone(self, url, revision_id=None, force_new_repo=False):
102
145
        """Clone this bzrdir and its contents to url verbatim.
103
146
 
104
147
        If urls last component does not exist, it will be created.
108
151
        :param force_new_repo: Do not use a shared repository for the target 
109
152
                               even if one is available.
110
153
        """
111
 
        self._make_tail(url)
112
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
113
 
        result = self._format.initialize(url)
 
154
        return self.clone_on_transport(get_transport(url),
 
155
                                       revision_id=revision_id,
 
156
                                       force_new_repo=force_new_repo)
 
157
 
 
158
    def clone_on_transport(self, transport, revision_id=None,
 
159
                           force_new_repo=False):
 
160
        """Clone this bzrdir and its contents to transport verbatim.
 
161
 
 
162
        If the target directory does not exist, it will be created.
 
163
 
 
164
        if revision_id is not None, then the clone operation may tune
 
165
            itself to download less data.
 
166
        :param force_new_repo: Do not use a shared repository for the target 
 
167
                               even if one is available.
 
168
        """
 
169
        transport.ensure_base()
 
170
        result = self._format.initialize_on_transport(transport)
114
171
        try:
115
172
            local_repo = self.find_repository()
116
173
        except errors.NoRepositoryPresent:
120
177
            if force_new_repo:
121
178
                result_repo = local_repo.clone(
122
179
                    result,
123
 
                    revision_id=revision_id,
124
 
                    basis=basis_repo)
 
180
                    revision_id=revision_id)
125
181
                result_repo.set_make_working_trees(local_repo.make_working_trees())
126
182
            else:
127
183
                try:
128
184
                    result_repo = result.find_repository()
129
185
                    # 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
186
                    result_repo.fetch(local_repo, revision_id=revision_id)
135
187
                except errors.NoRepositoryPresent:
136
188
                    # needed to make one anyway.
137
189
                    result_repo = local_repo.clone(
138
190
                        result,
139
 
                        revision_id=revision_id,
140
 
                        basis=basis_repo)
 
191
                        revision_id=revision_id)
141
192
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
142
193
        # 1 if there is a branch present
143
194
        #   make sure its content is available in the target repository
147
198
        except errors.NotBranchError:
148
199
            pass
149
200
        try:
150
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
201
            self.open_workingtree().clone(result)
151
202
        except (errors.NoWorkingTree, errors.NotLocalUrl):
152
203
            pass
153
204
        return result
154
205
 
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
206
    # TODO: This should be given a Transport, and should chdir up; otherwise
177
207
    # this will open a new connection.
178
208
    def _make_tail(self, url):
179
 
        head, tail = urlutils.split(url)
180
 
        if tail and tail != '.':
181
 
            t = bzrlib.transport.get_transport(head)
182
 
            try:
183
 
                t.mkdir(tail)
184
 
            except errors.FileExists:
185
 
                pass
 
209
        t = get_transport(url)
 
210
        t.ensure_base()
186
211
 
187
212
    # TODO: Should take a Transport
188
213
    @classmethod
189
 
    def create(cls, base):
 
214
    def create(cls, base, format=None):
190
215
        """Create a new BzrDir at the url 'base'.
191
216
        
192
217
        This will call the current default formats initialize with base
193
218
        as the only parameter.
194
219
 
195
 
        If you need a specific format, consider creating an instance
196
 
        of that and calling initialize().
 
220
        :param format: If supplied, the format of branch to create.  If not
 
221
            supplied, the default is used.
197
222
        """
198
223
        if cls is not BzrDir:
199
 
            raise AssertionError("BzrDir.create always creates the default format, "
200
 
                    "not one of %r" % cls)
201
 
        head, tail = urlutils.split(base)
202
 
        if tail and tail != '.':
203
 
            t = bzrlib.transport.get_transport(head)
204
 
            try:
205
 
                t.mkdir(tail)
206
 
            except errors.FileExists:
207
 
                pass
208
 
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
 
224
            raise AssertionError("BzrDir.create always creates the default"
 
225
                " format, not one of %r" % cls)
 
226
        t = get_transport(base)
 
227
        t.ensure_base()
 
228
        if format is None:
 
229
            format = BzrDirFormat.get_default_format()
 
230
        return format.initialize(safe_unicode(base))
209
231
 
210
232
    def create_branch(self):
211
233
        """Create a branch in this BzrDir.
216
238
        raise NotImplementedError(self.create_branch)
217
239
 
218
240
    @staticmethod
219
 
    def create_branch_and_repo(base, force_new_repo=False):
 
241
    def create_branch_and_repo(base, force_new_repo=False, format=None):
220
242
        """Create a new BzrDir, Branch and Repository at the url 'base'.
221
243
 
222
244
        This will use the current default BzrDirFormat, and use whatever 
229
251
        :param base: The URL to create the branch at.
230
252
        :param force_new_repo: If True a new repository is always created.
231
253
        """
232
 
        bzrdir = BzrDir.create(base)
 
254
        bzrdir = BzrDir.create(base, format)
233
255
        bzrdir._find_or_create_repository(force_new_repo)
234
256
        return bzrdir.create_branch()
235
257
 
273
295
            t = get_transport(safe_unicode(base))
274
296
            if not isinstance(t, LocalTransport):
275
297
                raise errors.NotLocalUrl(base)
276
 
        if format is None:
277
 
            bzrdir = BzrDir.create(base)
278
 
        else:
279
 
            bzrdir = format.initialize(base)
 
298
        bzrdir = BzrDir.create(base, format)
280
299
        repo = bzrdir._find_or_create_repository(force_new_repo)
281
300
        result = bzrdir.create_branch()
282
301
        if force_new_tree or (repo.make_working_trees() and 
288
307
        return result
289
308
        
290
309
    @staticmethod
291
 
    def create_repository(base, shared=False):
 
310
    def create_repository(base, shared=False, format=None):
292
311
        """Create a new BzrDir and Repository at the url 'base'.
293
312
 
294
 
        This will use the current default BzrDirFormat, and use whatever 
295
 
        repository format that that uses for bzrdirformat.create_repository.
 
313
        If no format is supplied, this will default to the current default
 
314
        BzrDirFormat by default, and use whatever repository format that that
 
315
        uses for bzrdirformat.create_repository.
296
316
 
297
 
        ;param shared: Create a shared repository rather than a standalone
 
317
        :param shared: Create a shared repository rather than a standalone
298
318
                       repository.
299
319
        The Repository object is returned.
300
320
 
302
322
        it should take no parameters and construct whatever repository format
303
323
        that child class desires.
304
324
        """
305
 
        bzrdir = BzrDir.create(base)
306
 
        return bzrdir.create_repository()
 
325
        bzrdir = BzrDir.create(base, format)
 
326
        return bzrdir.create_repository(shared)
307
327
 
308
328
    @staticmethod
309
 
    def create_standalone_workingtree(base):
 
329
    def create_standalone_workingtree(base, format=None):
310
330
        """Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
311
331
 
312
332
        'base' must be a local path or a file:// url.
315
335
        repository format that that uses for bzrdirformat.create_workingtree,
316
336
        create_branch and create_repository.
317
337
 
318
 
        The WorkingTree object is returned.
 
338
        :return: The WorkingTree object.
319
339
        """
320
340
        t = get_transport(safe_unicode(base))
321
341
        if not isinstance(t, LocalTransport):
322
342
            raise errors.NotLocalUrl(base)
323
343
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
324
 
                                               force_new_repo=True).bzrdir
 
344
                                               force_new_repo=True,
 
345
                                               format=format).bzrdir
325
346
        return bzrdir.create_workingtree()
326
347
 
327
348
    def create_workingtree(self, revision_id=None):
331
352
        """
332
353
        raise NotImplementedError(self.create_workingtree)
333
354
 
 
355
    def retire_bzrdir(self):
 
356
        """Permanently disable the bzrdir.
 
357
 
 
358
        This is done by renaming it to give the user some ability to recover
 
359
        if there was a problem.
 
360
 
 
361
        This will have horrible consequences if anyone has anything locked or
 
362
        in use.
 
363
        """
 
364
        for i in xrange(10000):
 
365
            try:
 
366
                to_path = '.bzr.retired.%d' % i
 
367
                self.root_transport.rename('.bzr', to_path)
 
368
                note("renamed %s to %s"
 
369
                    % (self.root_transport.abspath('.bzr'), to_path))
 
370
                break
 
371
            except (errors.TransportError, IOError, errors.PathError):
 
372
                pass
 
373
 
 
374
    def destroy_workingtree(self):
 
375
        """Destroy the working tree at this BzrDir.
 
376
 
 
377
        Formats that do not support this may raise UnsupportedOperation.
 
378
        """
 
379
        raise NotImplementedError(self.destroy_workingtree)
 
380
 
 
381
    def destroy_workingtree_metadata(self):
 
382
        """Destroy the control files for the working tree at this BzrDir.
 
383
 
 
384
        The contents of working tree files are not affected.
 
385
        Formats that do not support this may raise UnsupportedOperation.
 
386
        """
 
387
        raise NotImplementedError(self.destroy_workingtree_metadata)
 
388
 
334
389
    def find_repository(self):
335
390
        """Find the repository that should be used for a_bzrdir.
336
391
 
344
399
            pass
345
400
        next_transport = self.root_transport.clone('..')
346
401
        while True:
 
402
            # find the next containing bzrdir
347
403
            try:
348
404
                found_bzrdir = BzrDir.open_containing_from_transport(
349
405
                    next_transport)[0]
350
406
            except errors.NotBranchError:
 
407
                # none found
351
408
                raise errors.NoRepositoryPresent(self)
 
409
            # does it have a repository ?
352
410
            try:
353
411
                repository = found_bzrdir.open_repository()
354
412
            except errors.NoRepositoryPresent:
355
413
                next_transport = found_bzrdir.root_transport.clone('..')
356
 
                continue
357
 
            if ((found_bzrdir.root_transport.base == 
 
414
                if (found_bzrdir.root_transport.base == next_transport.base):
 
415
                    # top of the file system
 
416
                    break
 
417
                else:
 
418
                    continue
 
419
            if ((found_bzrdir.root_transport.base ==
358
420
                 self.root_transport.base) or repository.is_shared()):
359
421
                return repository
360
422
            else:
361
423
                raise errors.NoRepositoryPresent(self)
362
424
        raise errors.NoRepositoryPresent(self)
363
425
 
 
426
    def get_branch_reference(self):
 
427
        """Return the referenced URL for the branch in this bzrdir.
 
428
 
 
429
        :raises NotBranchError: If there is no Branch.
 
430
        :return: The URL the branch in this bzrdir references if it is a
 
431
            reference branch, or None for regular branches.
 
432
        """
 
433
        return None
 
434
 
364
435
    def get_branch_transport(self, branch_format):
365
436
        """Get the transport for use by branch format in this BzrDir.
366
437
 
367
438
        Note that bzr dirs that do not support format strings will raise
368
439
        IncompatibleFormat if the branch format they are given has
369
 
        a format string, and vice verca.
 
440
        a format string, and vice versa.
370
441
 
371
442
        If branch_format is None, the transport is returned with no 
372
443
        checking. if it is not None, then the returned transport is
379
450
 
380
451
        Note that bzr dirs that do not support format strings will raise
381
452
        IncompatibleFormat if the repository format they are given has
382
 
        a format string, and vice verca.
 
453
        a format string, and vice versa.
383
454
 
384
455
        If repository_format is None, the transport is returned with no 
385
456
        checking. if it is not None, then the returned transport is
391
462
        """Get the transport for use by workingtree format in this BzrDir.
392
463
 
393
464
        Note that bzr dirs that do not support format strings will raise
394
 
        IncompatibleFormat if the workingtree format they are given has
395
 
        a format string, and vice verca.
 
465
        IncompatibleFormat if the workingtree format they are given has a
 
466
        format string, and vice versa.
396
467
 
397
468
        If workingtree_format is None, the transport is returned with no 
398
469
        checking. if it is not None, then the returned transport is
427
498
        # this might be better on the BzrDirFormat class because it refers to 
428
499
        # all the possible bzrdir disk formats. 
429
500
        # This method is tested via the workingtree is_control_filename tests- 
430
 
        # it was extractd from WorkingTree.is_control_filename. If the methods
 
501
        # it was extracted from WorkingTree.is_control_filename. If the methods
431
502
        # contract is extended beyond the current trivial  implementation please
432
503
        # add new tests for it to the appropriate place.
433
504
        return filename == '.bzr' or filename.startswith('.bzr/')
455
526
        _unsupported is a private parameter to the BzrDir class.
456
527
        """
457
528
        t = get_transport(base)
458
 
        mutter("trying to open %r with transport %r", base, t)
459
 
        format = BzrDirFormat.find_format(t)
 
529
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
 
530
 
 
531
    @staticmethod
 
532
    def open_from_transport(transport, _unsupported=False,
 
533
                            _server_formats=True):
 
534
        """Open a bzrdir within a particular directory.
 
535
 
 
536
        :param transport: Transport containing the bzrdir.
 
537
        :param _unsupported: private.
 
538
        """
 
539
        base = transport.base
 
540
 
 
541
        def find_format(transport):
 
542
            return transport, BzrDirFormat.find_format(
 
543
                transport, _server_formats=_server_formats)
 
544
 
 
545
        def redirected(transport, e, redirection_notice):
 
546
            qualified_source = e.get_source_url()
 
547
            relpath = transport.relpath(qualified_source)
 
548
            if not e.target.endswith(relpath):
 
549
                # Not redirected to a branch-format, not a branch
 
550
                raise errors.NotBranchError(path=e.target)
 
551
            target = e.target[:-len(relpath)]
 
552
            note('%s is%s redirected to %s',
 
553
                 transport.base, e.permanently, target)
 
554
            # Let's try with a new transport
 
555
            qualified_target = e.get_target_url()[:-len(relpath)]
 
556
            # FIXME: If 'transport' has a qualifier, this should
 
557
            # be applied again to the new transport *iff* the
 
558
            # schemes used are the same. It's a bit tricky to
 
559
            # verify, so I'll punt for now
 
560
            # -- vila20070212
 
561
            return get_transport(target)
 
562
 
 
563
        try:
 
564
            transport, format = do_catching_redirections(find_format,
 
565
                                                         transport,
 
566
                                                         redirected)
 
567
        except errors.TooManyRedirections:
 
568
            raise errors.NotBranchError(base)
 
569
 
460
570
        BzrDir._check_supported(format, _unsupported)
461
 
        return format.open(t, _found=True)
 
571
        return format.open(transport, _found=True)
462
572
 
463
573
    def open_branch(self, unsupported=False):
464
574
        """Open the branch object at this BzrDir if one is present.
498
608
        url = a_transport.base
499
609
        while True:
500
610
            try:
501
 
                format = BzrDirFormat.find_format(a_transport)
502
 
                BzrDir._check_supported(format, False)
503
 
                return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
 
611
                result = BzrDir.open_from_transport(a_transport)
 
612
                return result, urlutils.unescape(a_transport.relpath(url))
504
613
            except errors.NotBranchError, e:
505
 
                ## mutter('not a branch in: %r %s', a_transport.base, e)
506
614
                pass
507
 
            new_t = a_transport.clone('..')
 
615
            try:
 
616
                new_t = a_transport.clone('..')
 
617
            except errors.InvalidURLJoin:
 
618
                # reached the root, whatever that may be
 
619
                raise errors.NotBranchError(path=url)
508
620
            if new_t.base == a_transport.base:
509
621
                # reached the root, whatever that may be
510
622
                raise errors.NotBranchError(path=url)
511
623
            a_transport = new_t
512
624
 
 
625
    @classmethod
 
626
    def open_containing_tree_or_branch(klass, location):
 
627
        """Return the branch and working tree contained by a location.
 
628
 
 
629
        Returns (tree, branch, relpath).
 
630
        If there is no tree at containing the location, tree will be None.
 
631
        If there is no branch containing the location, an exception will be
 
632
        raised
 
633
        relpath is the portion of the path that is contained by the branch.
 
634
        """
 
635
        bzrdir, relpath = klass.open_containing(location)
 
636
        try:
 
637
            tree = bzrdir.open_workingtree()
 
638
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
639
            tree = None
 
640
            branch = bzrdir.open_branch()
 
641
        else:
 
642
            branch = tree.branch
 
643
        return tree, branch, relpath
 
644
 
513
645
    def open_repository(self, _unsupported=False):
514
646
        """Open the repository object at this BzrDir if one is present.
515
647
 
522
654
        """
523
655
        raise NotImplementedError(self.open_repository)
524
656
 
525
 
    def open_workingtree(self, _unsupported=False):
 
657
    def open_workingtree(self, _unsupported=False,
 
658
            recommend_upgrade=True):
526
659
        """Open the workingtree object at this BzrDir if one is present.
527
 
        
528
 
        TODO: static convenience version of this?
 
660
 
 
661
        :param recommend_upgrade: Optional keyword parameter, when True (the
 
662
            default), emit through the ui module a recommendation that the user
 
663
            upgrade the working tree when the workingtree being opened is old
 
664
            (but still fully supported).
529
665
        """
530
666
        raise NotImplementedError(self.open_workingtree)
531
667
 
553
689
        workingtree and discards it, and that's somewhat expensive.) 
554
690
        """
555
691
        try:
556
 
            self.open_workingtree()
 
692
            self.open_workingtree(recommend_upgrade=False)
557
693
            return True
558
694
        except errors.NoWorkingTree:
559
695
            return False
560
696
 
561
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
697
    def _cloning_metadir(self):
 
698
        """Produce a metadir suitable for cloning with"""
 
699
        result_format = self._format.__class__()
 
700
        try:
 
701
            try:
 
702
                branch = self.open_branch()
 
703
                source_repository = branch.repository
 
704
            except errors.NotBranchError:
 
705
                source_branch = None
 
706
                source_repository = self.open_repository()
 
707
        except errors.NoRepositoryPresent:
 
708
            source_repository = None
 
709
        else:
 
710
            # XXX TODO: This isinstance is here because we have not implemented
 
711
            # the fix recommended in bug # 103195 - to delegate this choice the
 
712
            # repository itself.
 
713
            repo_format = source_repository._format
 
714
            if not isinstance(repo_format, remote.RemoteRepositoryFormat):
 
715
                result_format.repository_format = repo_format
 
716
        try:
 
717
            # TODO: Couldn't we just probe for the format in these cases,
 
718
            # rather than opening the whole tree?  It would be a little
 
719
            # faster. mbp 20070401
 
720
            tree = self.open_workingtree(recommend_upgrade=False)
 
721
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
722
            result_format.workingtree_format = None
 
723
        else:
 
724
            result_format.workingtree_format = tree._format.__class__()
 
725
        return result_format, source_repository
 
726
 
 
727
    def cloning_metadir(self):
 
728
        """Produce a metadir suitable for cloning or sprouting with.
 
729
 
 
730
        These operations may produce workingtrees (yes, even though they're
 
731
        "cloning" something that doesn't have a tree, so a viable workingtree
 
732
        format must be selected.
 
733
        """
 
734
        format, repository = self._cloning_metadir()
 
735
        if format._workingtree_format is None:
 
736
            if repository is None:
 
737
                return format
 
738
            tree_format = repository._format._matchingbzrdir.workingtree_format
 
739
            format.workingtree_format = tree_format.__class__()
 
740
        return format
 
741
 
 
742
    def checkout_metadir(self):
 
743
        return self.cloning_metadir()
 
744
 
 
745
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
746
               recurse='down'):
562
747
        """Create a copy of this bzrdir prepared for use as a new line of
563
748
        development.
564
749
 
572
757
        if revision_id is not None, then the clone operation may tune
573
758
            itself to download less data.
574
759
        """
575
 
        self._make_tail(url)
576
 
        result = self._format.initialize(url)
577
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
760
        target_transport = get_transport(url)
 
761
        target_transport.ensure_base()
 
762
        cloning_format = self.cloning_metadir()
 
763
        result = cloning_format.initialize_on_transport(target_transport)
578
764
        try:
579
765
            source_branch = self.open_branch()
580
766
            source_repository = source_branch.repository
583
769
            try:
584
770
                source_repository = self.open_repository()
585
771
            except errors.NoRepositoryPresent:
586
 
                # copy the entire basis one if there is one
587
 
                # but there is no repository.
588
 
                source_repository = basis_repo
 
772
                source_repository = None
589
773
        if force_new_repo:
590
774
            result_repo = None
591
775
        else:
600
784
            result.create_repository()
601
785
        elif source_repository is not None and result_repo is None:
602
786
            # have source, and want to make a new target repo
603
 
            # we dont clone the repo because that preserves attributes
604
 
            # like is_shared(), and we have not yet implemented a 
605
 
            # repository sprout().
606
 
            result_repo = result.create_repository()
607
 
        if result_repo is not None:
 
787
            result_repo = source_repository.sprout(result, revision_id=revision_id)
 
788
        else:
608
789
            # fetch needed content into target.
609
 
            if basis_repo:
610
 
                # XXX FIXME RBC 20060214 need tests for this when the basis
611
 
                # is incomplete
612
 
                result_repo.fetch(basis_repo, revision_id=revision_id)
613
 
            result_repo.fetch(source_repository, revision_id=revision_id)
 
790
            if source_repository is not None:
 
791
                # would rather do 
 
792
                # source_repository.copy_content_into(result_repo, revision_id=revision_id)
 
793
                # so we can override the copy method
 
794
                result_repo.fetch(source_repository, revision_id=revision_id)
614
795
        if source_branch is not None:
615
796
            source_branch.sprout(result, revision_id=revision_id)
616
797
        else:
618
799
        # TODO: jam 20060426 we probably need a test in here in the
619
800
        #       case that the newly sprouted branch is a remote one
620
801
        if result_repo is None or result_repo.make_working_trees():
621
 
            result.create_workingtree()
 
802
            wt = result.create_workingtree()
 
803
            wt.lock_write()
 
804
            try:
 
805
                if wt.path2id('') is None:
 
806
                    try:
 
807
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
808
                    except errors.NoWorkingTree:
 
809
                        pass
 
810
            finally:
 
811
                wt.unlock()
 
812
        else:
 
813
            wt = None
 
814
        if recurse == 'down':
 
815
            if wt is not None:
 
816
                basis = wt.basis_tree()
 
817
                basis.lock_read()
 
818
                subtrees = basis.iter_references()
 
819
                recurse_branch = wt.branch
 
820
            elif source_branch is not None:
 
821
                basis = source_branch.basis_tree()
 
822
                basis.lock_read()
 
823
                subtrees = basis.iter_references()
 
824
                recurse_branch = source_branch
 
825
            else:
 
826
                subtrees = []
 
827
                basis = None
 
828
            try:
 
829
                for path, file_id in subtrees:
 
830
                    target = urlutils.join(url, urlutils.escape(path))
 
831
                    sublocation = source_branch.reference_parent(file_id, path)
 
832
                    sublocation.bzrdir.sprout(target,
 
833
                        basis.get_reference_revision(file_id, path),
 
834
                        force_new_repo=force_new_repo, recurse=recurse)
 
835
            finally:
 
836
                if basis is not None:
 
837
                    basis.unlock()
622
838
        return result
623
839
 
624
840
 
628
844
    def __init__(self, _transport, _format):
629
845
        """See BzrDir.__init__."""
630
846
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
631
 
        assert self._format._lock_class == TransportLock
 
847
        assert self._format._lock_class == lockable_files.TransportLock
632
848
        assert self._format._lock_file_name == 'branch-lock'
633
 
        self._control_files = LockableFiles(self.get_branch_transport(None),
 
849
        self._control_files = lockable_files.LockableFiles(
 
850
                                            self.get_branch_transport(None),
634
851
                                            self._format._lock_file_name,
635
852
                                            self._format._lock_class)
636
853
 
638
855
        """Pre-splitout bzrdirs do not suffer from stale locks."""
639
856
        raise NotImplementedError(self.break_lock)
640
857
 
641
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
858
    def clone(self, url, revision_id=None, force_new_repo=False):
642
859
        """See BzrDir.clone()."""
643
860
        from bzrlib.workingtree import WorkingTreeFormat2
644
861
        self._make_tail(url)
645
862
        result = self._format._initialize_for_clone(url)
646
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
647
 
        self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
863
        self.open_repository().clone(result, revision_id=revision_id)
648
864
        from_branch = self.open_branch()
649
865
        from_branch.clone(result, revision_id=revision_id)
650
866
        try:
651
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
867
            self.open_workingtree().clone(result)
652
868
        except errors.NotLocalUrl:
653
869
            # make a new one, this format always has to have one.
654
870
            try:
672
888
    def create_workingtree(self, revision_id=None):
673
889
        """See BzrDir.create_workingtree."""
674
890
        # this looks buggy but is not -really-
 
891
        # because this format creates the workingtree when the bzrdir is
 
892
        # created
675
893
        # clone and sprout will have set the revision_id
676
894
        # and that will have set it for us, its only
677
895
        # specific uses of create_workingtree in isolation
678
896
        # that can do wonky stuff here, and that only
679
897
        # happens for creating checkouts, which cannot be 
680
898
        # done on this format anyway. So - acceptable wart.
681
 
        result = self.open_workingtree()
 
899
        result = self.open_workingtree(recommend_upgrade=False)
682
900
        if revision_id is not None:
683
 
            result.set_last_revision(revision_id)
 
901
            if revision_id == _mod_revision.NULL_REVISION:
 
902
                result.set_parent_ids([])
 
903
            else:
 
904
                result.set_parent_ids([revision_id])
684
905
        return result
685
906
 
 
907
    def destroy_workingtree(self):
 
908
        """See BzrDir.destroy_workingtree."""
 
909
        raise errors.UnsupportedOperation(self.destroy_workingtree, self)
 
910
 
 
911
    def destroy_workingtree_metadata(self):
 
912
        """See BzrDir.destroy_workingtree_metadata."""
 
913
        raise errors.UnsupportedOperation(self.destroy_workingtree_metadata, 
 
914
                                          self)
 
915
 
686
916
    def get_branch_transport(self, branch_format):
687
917
        """See BzrDir.get_branch_transport()."""
688
918
        if branch_format is None:
728
958
        self._check_supported(format, unsupported)
729
959
        return format.open(self, _found=True)
730
960
 
731
 
    def sprout(self, url, revision_id=None, basis=None):
 
961
    def sprout(self, url, revision_id=None, force_new_repo=False):
732
962
        """See BzrDir.sprout()."""
733
963
        from bzrlib.workingtree import WorkingTreeFormat2
734
964
        self._make_tail(url)
735
965
        result = self._format._initialize_for_clone(url)
736
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
737
966
        try:
738
 
            self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
967
            self.open_repository().clone(result, revision_id=revision_id)
739
968
        except errors.NoRepositoryPresent:
740
969
            pass
741
970
        try:
763
992
 
764
993
    def open_repository(self):
765
994
        """See BzrDir.open_repository."""
766
 
        from bzrlib.repository import RepositoryFormat4
 
995
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
767
996
        return RepositoryFormat4().open(self, _found=True)
768
997
 
769
998
 
775
1004
 
776
1005
    def open_repository(self):
777
1006
        """See BzrDir.open_repository."""
778
 
        from bzrlib.repository import RepositoryFormat5
 
1007
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
779
1008
        return RepositoryFormat5().open(self, _found=True)
780
1009
 
781
 
    def open_workingtree(self, _unsupported=False):
 
1010
    def open_workingtree(self, _unsupported=False,
 
1011
            recommend_upgrade=True):
782
1012
        """See BzrDir.create_workingtree."""
783
1013
        from bzrlib.workingtree import WorkingTreeFormat2
784
 
        return WorkingTreeFormat2().open(self, _found=True)
 
1014
        wt_format = WorkingTreeFormat2()
 
1015
        # we don't warn here about upgrades; that ought to be handled for the
 
1016
        # bzrdir as a whole
 
1017
        return wt_format.open(self, _found=True)
785
1018
 
786
1019
 
787
1020
class BzrDir6(BzrDirPreSplitOut):
792
1025
 
793
1026
    def open_repository(self):
794
1027
        """See BzrDir.open_repository."""
795
 
        from bzrlib.repository import RepositoryFormat6
 
1028
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
796
1029
        return RepositoryFormat6().open(self, _found=True)
797
1030
 
798
 
    def open_workingtree(self, _unsupported=False):
 
1031
    def open_workingtree(self, _unsupported=False,
 
1032
        recommend_upgrade=True):
799
1033
        """See BzrDir.create_workingtree."""
 
1034
        # we don't warn here about upgrades; that ought to be handled for the
 
1035
        # bzrdir as a whole
800
1036
        from bzrlib.workingtree import WorkingTreeFormat2
801
1037
        return WorkingTreeFormat2().open(self, _found=True)
802
1038
 
816
1052
 
817
1053
    def create_branch(self):
818
1054
        """See BzrDir.create_branch."""
819
 
        from bzrlib.branch import BranchFormat
820
 
        return BranchFormat.get_default_format().initialize(self)
 
1055
        return self._format.get_branch_format().initialize(self)
821
1056
 
822
1057
    def create_repository(self, shared=False):
823
1058
        """See BzrDir.create_repository."""
826
1061
    def create_workingtree(self, revision_id=None):
827
1062
        """See BzrDir.create_workingtree."""
828
1063
        from bzrlib.workingtree import WorkingTreeFormat
829
 
        return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
 
1064
        return self._format.workingtree_format.initialize(self, revision_id)
 
1065
 
 
1066
    def destroy_workingtree(self):
 
1067
        """See BzrDir.destroy_workingtree."""
 
1068
        wt = self.open_workingtree(recommend_upgrade=False)
 
1069
        repository = wt.branch.repository
 
1070
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1071
        wt.revert([], old_tree=empty)
 
1072
        self.destroy_workingtree_metadata()
 
1073
 
 
1074
    def destroy_workingtree_metadata(self):
 
1075
        self.transport.delete_tree('checkout')
 
1076
 
 
1077
    def find_branch_format(self):
 
1078
        """Find the branch 'format' for this bzrdir.
 
1079
 
 
1080
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
1081
        """
 
1082
        from bzrlib.branch import BranchFormat
 
1083
        return BranchFormat.find_format(self)
830
1084
 
831
1085
    def _get_mkdir_mode(self):
832
1086
        """Figure out the mode to use when creating a bzrdir subdir."""
833
 
        temp_control = LockableFiles(self.transport, '', TransportLock)
 
1087
        temp_control = lockable_files.LockableFiles(self.transport, '',
 
1088
                                     lockable_files.TransportLock)
834
1089
        return temp_control._dir_mode
835
1090
 
 
1091
    def get_branch_reference(self):
 
1092
        """See BzrDir.get_branch_reference()."""
 
1093
        from bzrlib.branch import BranchFormat
 
1094
        format = BranchFormat.find_format(self)
 
1095
        return format.get_reference(self)
 
1096
 
836
1097
    def get_branch_transport(self, branch_format):
837
1098
        """See BzrDir.get_branch_transport()."""
838
1099
        if branch_format is None:
890
1151
                return True
891
1152
        except errors.NoRepositoryPresent:
892
1153
            pass
893
 
        # currently there are no other possible conversions for meta1 formats.
 
1154
        try:
 
1155
            if not isinstance(self.open_branch()._format,
 
1156
                              format.get_branch_format().__class__):
 
1157
                # the branch needs an upgrade.
 
1158
                return True
 
1159
        except errors.NotBranchError:
 
1160
            pass
 
1161
        try:
 
1162
            my_wt = self.open_workingtree(recommend_upgrade=False)
 
1163
            if not isinstance(my_wt._format,
 
1164
                              format.workingtree_format.__class__):
 
1165
                # the workingtree needs an upgrade.
 
1166
                return True
 
1167
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1168
            pass
894
1169
        return False
895
1170
 
896
1171
    def open_branch(self, unsupported=False):
897
1172
        """See BzrDir.open_branch."""
898
 
        from bzrlib.branch import BranchFormat
899
 
        format = BranchFormat.find_format(self)
 
1173
        format = self.find_branch_format()
900
1174
        self._check_supported(format, unsupported)
901
1175
        return format.open(self, _found=True)
902
1176
 
907
1181
        self._check_supported(format, unsupported)
908
1182
        return format.open(self, _found=True)
909
1183
 
910
 
    def open_workingtree(self, unsupported=False):
 
1184
    def open_workingtree(self, unsupported=False,
 
1185
            recommend_upgrade=True):
911
1186
        """See BzrDir.open_workingtree."""
912
1187
        from bzrlib.workingtree import WorkingTreeFormat
913
1188
        format = WorkingTreeFormat.find_format(self)
914
 
        self._check_supported(format, unsupported)
 
1189
        self._check_supported(format, unsupported,
 
1190
            recommend_upgrade,
 
1191
            basedir=self.root_transport.base)
915
1192
        return format.open(self, _found=True)
916
1193
 
917
1194
 
938
1215
    _formats = {}
939
1216
    """The known formats."""
940
1217
 
 
1218
    _control_formats = []
 
1219
    """The registered control formats - .bzr, ....
 
1220
    
 
1221
    This is a list of BzrDirFormat objects.
 
1222
    """
 
1223
 
 
1224
    _control_server_formats = []
 
1225
    """The registered control server formats, e.g. RemoteBzrDirs.
 
1226
 
 
1227
    This is a list of BzrDirFormat objects.
 
1228
    """
 
1229
 
941
1230
    _lock_file_name = 'branch-lock'
942
1231
 
943
1232
    # _lock_class must be set in subclasses to the lock type, typ.
944
1233
    # TransportLock or LockDir
945
1234
 
946
1235
    @classmethod
947
 
    def find_format(klass, transport):
948
 
        """Return the format registered for URL."""
 
1236
    def find_format(klass, transport, _server_formats=True):
 
1237
        """Return the format present at transport."""
 
1238
        if _server_formats:
 
1239
            formats = klass._control_server_formats + klass._control_formats
 
1240
        else:
 
1241
            formats = klass._control_formats
 
1242
        for format in formats:
 
1243
            try:
 
1244
                return format.probe_transport(transport)
 
1245
            except errors.NotBranchError:
 
1246
                # this format does not find a control dir here.
 
1247
                pass
 
1248
        raise errors.NotBranchError(path=transport.base)
 
1249
 
 
1250
    @classmethod
 
1251
    def probe_transport(klass, transport):
 
1252
        """Return the .bzrdir style format present in a directory."""
949
1253
        try:
950
1254
            format_string = transport.get(".bzr/branch-format").read()
 
1255
        except errors.NoSuchFile:
 
1256
            raise errors.NotBranchError(path=transport.base)
 
1257
 
 
1258
        try:
951
1259
            return klass._formats[format_string]
952
 
        except errors.NoSuchFile:
953
 
            raise errors.NotBranchError(path=transport.base)
954
1260
        except KeyError:
955
 
            raise errors.UnknownFormatError(format_string)
 
1261
            raise errors.UnknownFormatError(format=format_string)
956
1262
 
957
1263
    @classmethod
958
1264
    def get_default_format(klass):
973
1279
        This returns a bzrlib.bzrdir.Converter object.
974
1280
 
975
1281
        This should return the best upgrader to step this format towards the
976
 
        current default format. In the case of plugins we can/shouold provide
 
1282
        current default format. In the case of plugins we can/should provide
977
1283
        some means for them to extend the range of returnable converters.
978
1284
 
979
 
        :param format: Optional format to override the default foramt of the 
 
1285
        :param format: Optional format to override the default format of the 
980
1286
                       library.
981
1287
        """
982
1288
        raise NotImplementedError(self.get_converter)
991
1297
 
992
1298
    def initialize_on_transport(self, transport):
993
1299
        """Initialize a new bzrdir in the base directory of a Transport."""
994
 
        # Since we don'transport have a .bzr directory, inherit the
 
1300
        # Since we don't have a .bzr directory, inherit the
995
1301
        # mode from the root directory
996
 
        temp_control = LockableFiles(transport, '', TransportLock)
 
1302
        temp_control = lockable_files.LockableFiles(transport,
 
1303
                            '', lockable_files.TransportLock)
997
1304
        temp_control._transport.mkdir('.bzr',
998
 
                                      # FIXME: RBC 20060121 dont peek under
 
1305
                                      # FIXME: RBC 20060121 don't peek under
999
1306
                                      # the covers
1000
1307
                                      mode=temp_control._dir_mode)
1001
1308
        file_mode = temp_control._file_mode
1008
1315
                      ('branch-format', self.get_format_string()),
1009
1316
                      ]
1010
1317
        # NB: no need to escape relative paths that are url safe.
1011
 
        control_files = LockableFiles(control, self._lock_file_name, 
1012
 
                                      self._lock_class)
 
1318
        control_files = lockable_files.LockableFiles(control,
 
1319
                            self._lock_file_name, self._lock_class)
1013
1320
        control_files.create_lock()
1014
1321
        control_files.lock_write()
1015
1322
        try:
1028
1335
        """
1029
1336
        return True
1030
1337
 
 
1338
    def same_model(self, target_format):
 
1339
        return (self.repository_format.rich_root_data == 
 
1340
            target_format.rich_root_data)
 
1341
 
 
1342
    @classmethod
 
1343
    def known_formats(klass):
 
1344
        """Return all the known formats.
 
1345
        
 
1346
        Concrete formats should override _known_formats.
 
1347
        """
 
1348
        # There is double indirection here to make sure that control 
 
1349
        # formats used by more than one dir format will only be probed 
 
1350
        # once. This can otherwise be quite expensive for remote connections.
 
1351
        result = set()
 
1352
        for format in klass._control_formats:
 
1353
            result.update(format._known_formats())
 
1354
        return result
 
1355
    
 
1356
    @classmethod
 
1357
    def _known_formats(klass):
 
1358
        """Return the known format instances for this control format."""
 
1359
        return set(klass._formats.values())
 
1360
 
1031
1361
    def open(self, transport, _found=False):
1032
1362
        """Return an instance of this format for the dir transport points at.
1033
1363
        
1034
1364
        _found is a private parameter, do not use it.
1035
1365
        """
1036
1366
        if not _found:
1037
 
            assert isinstance(BzrDirFormat.find_format(transport),
1038
 
                              self.__class__)
 
1367
            found_format = BzrDirFormat.find_format(transport)
 
1368
            if not isinstance(found_format, self.__class__):
 
1369
                raise AssertionError("%s was asked to open %s, but it seems to need "
 
1370
                        "format %s" 
 
1371
                        % (self, transport, found_format))
1039
1372
        return self._open(transport)
1040
1373
 
1041
1374
    def _open(self, transport):
1051
1384
        klass._formats[format.get_format_string()] = format
1052
1385
 
1053
1386
    @classmethod
 
1387
    def register_control_format(klass, format):
 
1388
        """Register a format that does not use '.bzr' for its control dir.
 
1389
 
 
1390
        TODO: This should be pulled up into a 'ControlDirFormat' base class
 
1391
        which BzrDirFormat can inherit from, and renamed to register_format 
 
1392
        there. It has been done without that for now for simplicity of
 
1393
        implementation.
 
1394
        """
 
1395
        klass._control_formats.append(format)
 
1396
 
 
1397
    @classmethod
 
1398
    def register_control_server_format(klass, format):
 
1399
        """Register a control format for client-server environments.
 
1400
 
 
1401
        These formats will be tried before ones registered with
 
1402
        register_control_format.  This gives implementations that decide to the
 
1403
        chance to grab it before anything looks at the contents of the format
 
1404
        file.
 
1405
        """
 
1406
        klass._control_server_formats.append(format)
 
1407
 
 
1408
    @classmethod
 
1409
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1054
1410
    def set_default_format(klass, format):
 
1411
        klass._set_default_format(format)
 
1412
 
 
1413
    @classmethod
 
1414
    def _set_default_format(klass, format):
 
1415
        """Set default format (for testing behavior of defaults only)"""
1055
1416
        klass._default_format = format
1056
1417
 
1057
1418
    def __str__(self):
1062
1423
        assert klass._formats[format.get_format_string()] is format
1063
1424
        del klass._formats[format.get_format_string()]
1064
1425
 
 
1426
    @classmethod
 
1427
    def unregister_control_format(klass, format):
 
1428
        klass._control_formats.remove(format)
 
1429
 
1065
1430
 
1066
1431
class BzrDirFormat4(BzrDirFormat):
1067
1432
    """Bzr dir format 4.
1076
1441
    removed in format 5; write support for this format has been removed.
1077
1442
    """
1078
1443
 
1079
 
    _lock_class = TransportLock
 
1444
    _lock_class = lockable_files.TransportLock
1080
1445
 
1081
1446
    def get_format_string(self):
1082
1447
        """See BzrDirFormat.get_format_string()."""
1110
1475
 
1111
1476
    def __return_repository_format(self):
1112
1477
        """Circular import protection."""
1113
 
        from bzrlib.repository import RepositoryFormat4
1114
 
        return RepositoryFormat4(self)
 
1478
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
 
1479
        return RepositoryFormat4()
1115
1480
    repository_format = property(__return_repository_format)
1116
1481
 
1117
1482
 
1126
1491
       Unhashed stores in the repository.
1127
1492
    """
1128
1493
 
1129
 
    _lock_class = TransportLock
 
1494
    _lock_class = lockable_files.TransportLock
1130
1495
 
1131
1496
    def get_format_string(self):
1132
1497
        """See BzrDirFormat.get_format_string()."""
1150
1515
        Except when they are being cloned.
1151
1516
        """
1152
1517
        from bzrlib.branch import BzrBranchFormat4
1153
 
        from bzrlib.repository import RepositoryFormat5
 
1518
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
1154
1519
        from bzrlib.workingtree import WorkingTreeFormat2
1155
1520
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1156
1521
        RepositoryFormat5().initialize(result, _internal=True)
1157
1522
        if not _cloning:
1158
 
            BzrBranchFormat4().initialize(result)
1159
 
            WorkingTreeFormat2().initialize(result)
 
1523
            branch = BzrBranchFormat4().initialize(result)
 
1524
            try:
 
1525
                WorkingTreeFormat2().initialize(result)
 
1526
            except errors.NotLocalUrl:
 
1527
                # Even though we can't access the working tree, we need to
 
1528
                # create its control files.
 
1529
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1160
1530
        return result
1161
1531
 
1162
1532
    def _open(self, transport):
1165
1535
 
1166
1536
    def __return_repository_format(self):
1167
1537
        """Circular import protection."""
1168
 
        from bzrlib.repository import RepositoryFormat5
1169
 
        return RepositoryFormat5(self)
 
1538
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
 
1539
        return RepositoryFormat5()
1170
1540
    repository_format = property(__return_repository_format)
1171
1541
 
1172
1542
 
1180
1550
     - Format 6 repositories [always]
1181
1551
    """
1182
1552
 
1183
 
    _lock_class = TransportLock
 
1553
    _lock_class = lockable_files.TransportLock
1184
1554
 
1185
1555
    def get_format_string(self):
1186
1556
        """See BzrDirFormat.get_format_string()."""
1204
1574
        Except when they are being cloned.
1205
1575
        """
1206
1576
        from bzrlib.branch import BzrBranchFormat4
1207
 
        from bzrlib.repository import RepositoryFormat6
 
1577
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
1208
1578
        from bzrlib.workingtree import WorkingTreeFormat2
1209
1579
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1210
1580
        RepositoryFormat6().initialize(result, _internal=True)
1211
1581
        if not _cloning:
1212
 
            BzrBranchFormat4().initialize(result)
 
1582
            branch = BzrBranchFormat4().initialize(result)
1213
1583
            try:
1214
1584
                WorkingTreeFormat2().initialize(result)
1215
1585
            except errors.NotLocalUrl:
1216
 
                # emulate pre-check behaviour for working tree and silently 
1217
 
                # fail.
1218
 
                pass
 
1586
                # Even though we can't access the working tree, we need to
 
1587
                # create its control files.
 
1588
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1219
1589
        return result
1220
1590
 
1221
1591
    def _open(self, transport):
1224
1594
 
1225
1595
    def __return_repository_format(self):
1226
1596
        """Circular import protection."""
1227
 
        from bzrlib.repository import RepositoryFormat6
1228
 
        return RepositoryFormat6(self)
 
1597
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
 
1598
        return RepositoryFormat6()
1229
1599
    repository_format = property(__return_repository_format)
1230
1600
 
1231
1601
 
1240
1610
     - Format 7 repositories [optional]
1241
1611
    """
1242
1612
 
1243
 
    _lock_class = LockDir
 
1613
    _lock_class = lockdir.LockDir
 
1614
 
 
1615
    def __init__(self):
 
1616
        self._workingtree_format = None
 
1617
        self._branch_format = None
 
1618
 
 
1619
    def __eq__(self, other):
 
1620
        if other.__class__ is not self.__class__:
 
1621
            return False
 
1622
        if other.repository_format != self.repository_format:
 
1623
            return False
 
1624
        if other.workingtree_format != self.workingtree_format:
 
1625
            return False
 
1626
        return True
 
1627
 
 
1628
    def __ne__(self, other):
 
1629
        return not self == other
 
1630
 
 
1631
    def get_branch_format(self):
 
1632
        if self._branch_format is None:
 
1633
            from bzrlib.branch import BranchFormat
 
1634
            self._branch_format = BranchFormat.get_default_format()
 
1635
        return self._branch_format
 
1636
 
 
1637
    def set_branch_format(self, format):
 
1638
        self._branch_format = format
1244
1639
 
1245
1640
    def get_converter(self, format=None):
1246
1641
        """See BzrDirFormat.get_converter()."""
1276
1671
 
1277
1672
    repository_format = property(__return_repository_format, __set_repository_format)
1278
1673
 
1279
 
 
 
1674
    def __get_workingtree_format(self):
 
1675
        if self._workingtree_format is None:
 
1676
            from bzrlib.workingtree import WorkingTreeFormat
 
1677
            self._workingtree_format = WorkingTreeFormat.get_default_format()
 
1678
        return self._workingtree_format
 
1679
 
 
1680
    def __set_workingtree_format(self, wt_format):
 
1681
        self._workingtree_format = wt_format
 
1682
 
 
1683
    workingtree_format = property(__get_workingtree_format,
 
1684
                                  __set_workingtree_format)
 
1685
 
 
1686
 
 
1687
# Register bzr control format
 
1688
BzrDirFormat.register_control_format(BzrDirFormat)
 
1689
 
 
1690
# Register bzr formats
1280
1691
BzrDirFormat.register_format(BzrDirFormat4())
1281
1692
BzrDirFormat.register_format(BzrDirFormat5())
1282
1693
BzrDirFormat.register_format(BzrDirFormat6())
1283
1694
__default_format = BzrDirMetaFormat1()
1284
1695
BzrDirFormat.register_format(__default_format)
1285
 
BzrDirFormat.set_default_format(__default_format)
1286
 
 
1287
 
 
1288
 
class BzrDirTestProviderAdapter(object):
1289
 
    """A tool to generate a suite testing multiple bzrdir formats at once.
1290
 
 
1291
 
    This is done by copying the test once for each transport and injecting
1292
 
    the transport_server, transport_readonly_server, and bzrdir_format
1293
 
    classes into each copy. Each copy is also given a new id() to make it
1294
 
    easy to identify.
1295
 
    """
1296
 
 
1297
 
    def __init__(self, transport_server, transport_readonly_server, formats):
1298
 
        self._transport_server = transport_server
1299
 
        self._transport_readonly_server = transport_readonly_server
1300
 
        self._formats = formats
1301
 
    
1302
 
    def adapt(self, test):
1303
 
        result = TestSuite()
1304
 
        for format in self._formats:
1305
 
            new_test = deepcopy(test)
1306
 
            new_test.transport_server = self._transport_server
1307
 
            new_test.transport_readonly_server = self._transport_readonly_server
1308
 
            new_test.bzrdir_format = format
1309
 
            def make_new_test_id():
1310
 
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1311
 
                return lambda: new_id
1312
 
            new_test.id = make_new_test_id()
1313
 
            result.addTest(new_test)
1314
 
        return result
1315
 
 
1316
 
 
1317
 
class ScratchDir(BzrDir6):
1318
 
    """Special test class: a bzrdir that cleans up itself..
1319
 
 
1320
 
    >>> d = ScratchDir()
1321
 
    >>> base = d.transport.base
1322
 
    >>> isdir(base)
1323
 
    True
1324
 
    >>> b.transport.__del__()
1325
 
    >>> isdir(base)
1326
 
    False
1327
 
    """
1328
 
 
1329
 
    def __init__(self, files=[], dirs=[], transport=None):
1330
 
        """Make a test branch.
1331
 
 
1332
 
        This creates a temporary directory and runs init-tree in it.
1333
 
 
1334
 
        If any files are listed, they are created in the working copy.
1335
 
        """
1336
 
        if transport is None:
1337
 
            transport = bzrlib.transport.local.ScratchTransport()
1338
 
            # local import for scope restriction
1339
 
            BzrDirFormat6().initialize(transport.base)
1340
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1341
 
            self.create_repository()
1342
 
            self.create_branch()
1343
 
            self.create_workingtree()
1344
 
        else:
1345
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1346
 
 
1347
 
        # BzrBranch creates a clone to .bzr and then forgets about the
1348
 
        # original transport. A ScratchTransport() deletes itself and
1349
 
        # everything underneath it when it goes away, so we need to
1350
 
        # grab a local copy to prevent that from happening
1351
 
        self._transport = transport
1352
 
 
1353
 
        for d in dirs:
1354
 
            self._transport.mkdir(d)
1355
 
            
1356
 
        for f in files:
1357
 
            self._transport.put(f, 'content of %s' % f)
1358
 
 
1359
 
    def clone(self):
1360
 
        """
1361
 
        >>> orig = ScratchDir(files=["file1", "file2"])
1362
 
        >>> os.listdir(orig.base)
1363
 
        [u'.bzr', u'file1', u'file2']
1364
 
        >>> clone = orig.clone()
1365
 
        >>> if os.name != 'nt':
1366
 
        ...   os.path.samefile(orig.base, clone.base)
1367
 
        ... else:
1368
 
        ...   orig.base == clone.base
1369
 
        ...
1370
 
        False
1371
 
        >>> os.listdir(clone.base)
1372
 
        [u'.bzr', u'file1', u'file2']
1373
 
        """
1374
 
        from shutil import copytree
1375
 
        from bzrlib.osutils import mkdtemp
1376
 
        base = mkdtemp()
1377
 
        os.rmdir(base)
1378
 
        copytree(self.base, base, symlinks=True)
1379
 
        return ScratchDir(
1380
 
            transport=bzrlib.transport.local.ScratchTransport(base))
 
1696
BzrDirFormat._default_format = __default_format
1381
1697
 
1382
1698
 
1383
1699
class Converter(object):
1470
1786
        self.bzrdir.transport.delete_tree('text-store')
1471
1787
 
1472
1788
    def _convert_working_inv(self):
1473
 
        inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1474
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1789
        inv = xml4.serializer_v4.read_inventory(
 
1790
                    self.branch.control_files.get('inventory'))
 
1791
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1475
1792
        # FIXME inventory is a working tree change.
1476
 
        self.branch.control_files.put('inventory', new_inv_xml)
 
1793
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1477
1794
 
1478
1795
    def _write_all_weaves(self):
1479
1796
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1503
1820
                                                      prefixed=False,
1504
1821
                                                      compressed=True))
1505
1822
        try:
1506
 
            transaction = bzrlib.transactions.WriteTransaction()
 
1823
            transaction = WriteTransaction()
1507
1824
            for i, rev_id in enumerate(self.converted_revs):
1508
1825
                self.pb.update('write revision', i, len(self.converted_revs))
1509
1826
                _revision_store.add_revision(self.revisions[rev_id], transaction)
1535
1852
    def _load_old_inventory(self, rev_id):
1536
1853
        assert rev_id not in self.converted_revs
1537
1854
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1538
 
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
 
1855
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
 
1856
        inv.revision_id = rev_id
1539
1857
        rev = self.revisions[rev_id]
1540
1858
        if rev.inventory_sha1:
1541
1859
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1545
1863
    def _load_updated_inventory(self, rev_id):
1546
1864
        assert rev_id in self.converted_revs
1547
1865
        inv_xml = self.inv_weave.get_text(rev_id)
1548
 
        inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
1866
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1549
1867
        return inv
1550
1868
 
1551
1869
    def _convert_one_rev(self, rev_id):
1561
1879
    def _store_new_weave(self, rev, inv, present_parents):
1562
1880
        # the XML is now updated with text versions
1563
1881
        if __debug__:
1564
 
            for file_id in inv:
1565
 
                ie = inv[file_id]
1566
 
                if ie.kind == 'root_directory':
1567
 
                    continue
1568
 
                assert hasattr(ie, 'revision'), \
 
1882
            entries = inv.iter_entries()
 
1883
            entries.next()
 
1884
            for path, ie in entries:
 
1885
                assert getattr(ie, 'revision', None) is not None, \
1569
1886
                    'no revision on {%s} in {%s}' % \
1570
1887
                    (file_id, rev.revision_id)
1571
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1888
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1572
1889
        new_inv_sha1 = sha_string(new_inv_xml)
1573
1890
        self.inv_weave.add_lines(rev.revision_id, 
1574
1891
                                 present_parents,
1583
1900
        mutter('converting texts of revision {%s}',
1584
1901
               rev_id)
1585
1902
        parent_invs = map(self._load_updated_inventory, present_parents)
1586
 
        for file_id in inv:
1587
 
            ie = inv[file_id]
 
1903
        entries = inv.iter_entries()
 
1904
        entries.next()
 
1905
        for path, ie in entries:
1588
1906
            self._convert_file_version(rev, ie, parent_invs)
1589
1907
 
1590
1908
    def _convert_file_version(self, rev, ie, parent_invs):
1593
1911
        The file needs to be added into the weave if it is a merge
1594
1912
        of >=2 parents or if it's changed from its parent.
1595
1913
        """
1596
 
        if ie.kind == 'root_directory':
1597
 
            return
1598
1914
        file_id = ie.file_id
1599
1915
        rev_id = rev.revision_id
1600
1916
        w = self.text_weaves.get(file_id)
1608
1924
                                                  entry_vf=w)
1609
1925
        for old_revision in previous_entries:
1610
1926
                # if this fails, its a ghost ?
1611
 
                assert old_revision in self.converted_revs 
 
1927
                assert old_revision in self.converted_revs, \
 
1928
                    "Revision {%s} not in converted_revs" % old_revision
1612
1929
        self.snapshot_ie(previous_entries, ie, w, rev_id)
1613
1930
        del ie.text_id
1614
1931
        assert getattr(ie, 'revision', None) is not None
1701
2018
 
1702
2019
    def convert(self, to_convert, pb):
1703
2020
        """See Converter.convert()."""
 
2021
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
 
2022
        from bzrlib.branch import BzrBranchFormat5
1704
2023
        self.bzrdir = to_convert
1705
2024
        self.pb = pb
1706
2025
        self.count = 0
1735
2054
        # we hard code the formats here because we are converting into
1736
2055
        # the meta format. The meta format upgrader can take this to a 
1737
2056
        # future format within each component.
1738
 
        self.put_format('repository', bzrlib.repository.RepositoryFormat7())
 
2057
        self.put_format('repository', RepositoryFormat7())
1739
2058
        for entry in repository_names:
1740
2059
            self.move_entry('repository', entry)
1741
2060
 
1742
2061
        self.step('Upgrading branch      ')
1743
2062
        self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1744
2063
        self.make_lock('branch')
1745
 
        self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
 
2064
        self.put_format('branch', BzrBranchFormat5())
1746
2065
        branch_files = [('revision-history', True),
1747
2066
                        ('branch-name', True),
1748
2067
                        ('parent', False)]
1749
2068
        for entry in branch_files:
1750
2069
            self.move_entry('branch', entry)
1751
2070
 
1752
 
        self.step('Upgrading working tree')
1753
 
        self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1754
 
        self.make_lock('checkout')
1755
 
        self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1756
 
        self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1757
2071
        checkout_files = [('pending-merges', True),
1758
2072
                          ('inventory', True),
1759
2073
                          ('stat-cache', False)]
1760
 
        for entry in checkout_files:
1761
 
            self.move_entry('checkout', entry)
1762
 
        if last_revision is not None:
1763
 
            self.bzrdir._control_files.put_utf8('checkout/last-revision',
1764
 
                                                last_revision)
1765
 
        self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
 
2074
        # If a mandatory checkout file is not present, the branch does not have
 
2075
        # a functional checkout. Do not create a checkout in the converted
 
2076
        # branch.
 
2077
        for name, mandatory in checkout_files:
 
2078
            if mandatory and name not in bzrcontents:
 
2079
                has_checkout = False
 
2080
                break
 
2081
        else:
 
2082
            has_checkout = True
 
2083
        if not has_checkout:
 
2084
            self.pb.note('No working tree.')
 
2085
            # If some checkout files are there, we may as well get rid of them.
 
2086
            for name, mandatory in checkout_files:
 
2087
                if name in bzrcontents:
 
2088
                    self.bzrdir.transport.delete(name)
 
2089
        else:
 
2090
            from bzrlib.workingtree import WorkingTreeFormat3
 
2091
            self.step('Upgrading working tree')
 
2092
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
 
2093
            self.make_lock('checkout')
 
2094
            self.put_format(
 
2095
                'checkout', WorkingTreeFormat3())
 
2096
            self.bzrdir.transport.delete_multi(
 
2097
                self.garbage_inventories, self.pb)
 
2098
            for entry in checkout_files:
 
2099
                self.move_entry('checkout', entry)
 
2100
            if last_revision is not None:
 
2101
                self.bzrdir._control_files.put_utf8(
 
2102
                    'checkout/last-revision', last_revision)
 
2103
        self.bzrdir._control_files.put_utf8(
 
2104
            'branch-format', BzrDirMetaFormat1().get_format_string())
1766
2105
        return BzrDir.open(self.bzrdir.root_transport.base)
1767
2106
 
1768
2107
    def make_lock(self, name):
1769
2108
        """Make a lock for the new control dir name."""
1770
2109
        self.step('Make %s lock' % name)
1771
 
        ld = LockDir(self.bzrdir.transport, 
1772
 
                     '%s/lock' % name,
1773
 
                     file_modebits=self.file_mode,
1774
 
                     dir_modebits=self.dir_mode)
 
2110
        ld = lockdir.LockDir(self.bzrdir.transport,
 
2111
                             '%s/lock' % name,
 
2112
                             file_modebits=self.file_mode,
 
2113
                             dir_modebits=self.dir_mode)
1775
2114
        ld.create()
1776
2115
 
1777
2116
    def move_entry(self, new_dir, entry):
1816
2155
                self.pb.note('starting repository conversion')
1817
2156
                converter = CopyConverter(self.target_format.repository_format)
1818
2157
                converter.convert(repo, pb)
 
2158
        try:
 
2159
            branch = self.bzrdir.open_branch()
 
2160
        except errors.NotBranchError:
 
2161
            pass
 
2162
        else:
 
2163
            # TODO: conversions of Branch and Tree should be done by
 
2164
            # InterXFormat lookups
 
2165
            # Avoid circular imports
 
2166
            from bzrlib import branch as _mod_branch
 
2167
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
 
2168
                self.target_format.get_branch_format().__class__ is
 
2169
                _mod_branch.BzrBranchFormat6):
 
2170
                branch_converter = _mod_branch.Converter5to6()
 
2171
                branch_converter.convert(branch)
 
2172
        try:
 
2173
            tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
 
2174
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2175
            pass
 
2176
        else:
 
2177
            # TODO: conversions of Branch and Tree should be done by
 
2178
            # InterXFormat lookups
 
2179
            if (isinstance(tree, workingtree.WorkingTree3) and
 
2180
                not isinstance(tree, workingtree_4.WorkingTree4) and
 
2181
                isinstance(self.target_format.workingtree_format,
 
2182
                    workingtree_4.WorkingTreeFormat4)):
 
2183
                workingtree_4.Converter3to4().convert(tree)
1819
2184
        return to_convert
 
2185
 
 
2186
 
 
2187
# This is not in remote.py because it's small, and needs to be registered.
 
2188
# Putting it in remote.py creates a circular import problem.
 
2189
# we can make it a lazy object if the control formats is turned into something
 
2190
# like a registry.
 
2191
class RemoteBzrDirFormat(BzrDirMetaFormat1):
 
2192
    """Format representing bzrdirs accessed via a smart server"""
 
2193
 
 
2194
    def get_format_description(self):
 
2195
        return 'bzr remote bzrdir'
 
2196
    
 
2197
    @classmethod
 
2198
    def probe_transport(klass, transport):
 
2199
        """Return a RemoteBzrDirFormat object if it looks possible."""
 
2200
        try:
 
2201
            client = transport.get_smart_client()
 
2202
        except (NotImplementedError, AttributeError,
 
2203
                errors.TransportNotPossible):
 
2204
            # no smart server, so not a branch for this format type.
 
2205
            raise errors.NotBranchError(path=transport.base)
 
2206
        else:
 
2207
            # Send a 'hello' request in protocol version one, and decline to
 
2208
            # open it if the server doesn't support our required version (2) so
 
2209
            # that the VFS-based transport will do it.
 
2210
            request = client.get_request()
 
2211
            smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
2212
            server_version = smart_protocol.query_version()
 
2213
            if server_version != 2:
 
2214
                raise errors.NotBranchError(path=transport.base)
 
2215
            return klass()
 
2216
 
 
2217
    def initialize_on_transport(self, transport):
 
2218
        try:
 
2219
            # hand off the request to the smart server
 
2220
            medium = transport.get_smart_medium()
 
2221
        except errors.NoSmartMedium:
 
2222
            # TODO: lookup the local format from a server hint.
 
2223
            local_dir_format = BzrDirMetaFormat1()
 
2224
            return local_dir_format.initialize_on_transport(transport)
 
2225
        client = _SmartClient(medium)
 
2226
        path = client.remote_path_from_transport(transport)
 
2227
        response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
 
2228
        assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
 
2229
        return remote.RemoteBzrDir(transport)
 
2230
 
 
2231
    def _open(self, transport):
 
2232
        return remote.RemoteBzrDir(transport)
 
2233
 
 
2234
    def __eq__(self, other):
 
2235
        if not isinstance(other, RemoteBzrDirFormat):
 
2236
            return False
 
2237
        return self.get_format_description() == other.get_format_description()
 
2238
 
 
2239
 
 
2240
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
 
2241
 
 
2242
 
 
2243
class BzrDirFormatInfo(object):
 
2244
 
 
2245
    def __init__(self, native, deprecated, hidden):
 
2246
        self.deprecated = deprecated
 
2247
        self.native = native
 
2248
        self.hidden = hidden
 
2249
 
 
2250
 
 
2251
class BzrDirFormatRegistry(registry.Registry):
 
2252
    """Registry of user-selectable BzrDir subformats.
 
2253
    
 
2254
    Differs from BzrDirFormat._control_formats in that it provides sub-formats,
 
2255
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
2256
    """
 
2257
 
 
2258
    def register_metadir(self, key,
 
2259
             repository_format, help, native=True, deprecated=False,
 
2260
             branch_format=None,
 
2261
             tree_format=None,
 
2262
             hidden=False):
 
2263
        """Register a metadir subformat.
 
2264
 
 
2265
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
 
2266
        by the Repository format.
 
2267
 
 
2268
        :param repository_format: The fully-qualified repository format class
 
2269
            name as a string.
 
2270
        :param branch_format: Fully-qualified branch format class name as
 
2271
            a string.
 
2272
        :param tree_format: Fully-qualified tree format class name as
 
2273
            a string.
 
2274
        """
 
2275
        # This should be expanded to support setting WorkingTree and Branch
 
2276
        # formats, once BzrDirMetaFormat1 supports that.
 
2277
        def _load(full_name):
 
2278
            mod_name, factory_name = full_name.rsplit('.', 1)
 
2279
            try:
 
2280
                mod = __import__(mod_name, globals(), locals(),
 
2281
                        [factory_name])
 
2282
            except ImportError, e:
 
2283
                raise ImportError('failed to load %s: %s' % (full_name, e))
 
2284
            try:
 
2285
                factory = getattr(mod, factory_name)
 
2286
            except AttributeError:
 
2287
                raise AttributeError('no factory %s in module %r'
 
2288
                    % (full_name, mod))
 
2289
            return factory()
 
2290
 
 
2291
        def helper():
 
2292
            bd = BzrDirMetaFormat1()
 
2293
            if branch_format is not None:
 
2294
                bd.set_branch_format(_load(branch_format))
 
2295
            if tree_format is not None:
 
2296
                bd.workingtree_format = _load(tree_format)
 
2297
            if repository_format is not None:
 
2298
                bd.repository_format = _load(repository_format)
 
2299
            return bd
 
2300
        self.register(key, helper, help, native, deprecated, hidden)
 
2301
 
 
2302
    def register(self, key, factory, help, native=True, deprecated=False,
 
2303
                 hidden=False):
 
2304
        """Register a BzrDirFormat factory.
 
2305
        
 
2306
        The factory must be a callable that takes one parameter: the key.
 
2307
        It must produce an instance of the BzrDirFormat when called.
 
2308
 
 
2309
        This function mainly exists to prevent the info object from being
 
2310
        supplied directly.
 
2311
        """
 
2312
        registry.Registry.register(self, key, factory, help, 
 
2313
            BzrDirFormatInfo(native, deprecated, hidden))
 
2314
 
 
2315
    def register_lazy(self, key, module_name, member_name, help, native=True,
 
2316
                      deprecated=False, hidden=False):
 
2317
        registry.Registry.register_lazy(self, key, module_name, member_name, 
 
2318
            help, BzrDirFormatInfo(native, deprecated, hidden))
 
2319
 
 
2320
    def set_default(self, key):
 
2321
        """Set the 'default' key to be a clone of the supplied key.
 
2322
        
 
2323
        This method must be called once and only once.
 
2324
        """
 
2325
        registry.Registry.register(self, 'default', self.get(key), 
 
2326
            self.get_help(key), info=self.get_info(key))
 
2327
 
 
2328
    def set_default_repository(self, key):
 
2329
        """Set the FormatRegistry default and Repository default.
 
2330
        
 
2331
        This is a transitional method while Repository.set_default_format
 
2332
        is deprecated.
 
2333
        """
 
2334
        if 'default' in self:
 
2335
            self.remove('default')
 
2336
        self.set_default(key)
 
2337
        format = self.get('default')()
 
2338
        assert isinstance(format, BzrDirMetaFormat1)
 
2339
 
 
2340
    def make_bzrdir(self, key):
 
2341
        return self.get(key)()
 
2342
 
 
2343
    def help_topic(self, topic):
 
2344
        output = textwrap.dedent("""\
 
2345
            Bazaar directory formats
 
2346
            ------------------------
 
2347
 
 
2348
            These formats can be used for creating branches, working trees, and
 
2349
            repositories.
 
2350
 
 
2351
            """)
 
2352
        default_help = self.get_help('default')
 
2353
        help_pairs = []
 
2354
        for key in self.keys():
 
2355
            if key == 'default':
 
2356
                continue
 
2357
            help = self.get_help(key)
 
2358
            if help == default_help:
 
2359
                default_realkey = key
 
2360
            else:
 
2361
                help_pairs.append((key, help))
 
2362
 
 
2363
        def wrapped(key, help, info):
 
2364
            if info.native:
 
2365
                help = '(native) ' + help
 
2366
            return '  %s:\n%s\n\n' % (key, 
 
2367
                    textwrap.fill(help, initial_indent='    ', 
 
2368
                    subsequent_indent='    '))
 
2369
        output += wrapped('%s/default' % default_realkey, default_help,
 
2370
                          self.get_info('default'))
 
2371
        deprecated_pairs = []
 
2372
        for key, help in help_pairs:
 
2373
            info = self.get_info(key)
 
2374
            if info.hidden:
 
2375
                continue
 
2376
            elif info.deprecated:
 
2377
                deprecated_pairs.append((key, help))
 
2378
            else:
 
2379
                output += wrapped(key, help, info)
 
2380
        if len(deprecated_pairs) > 0:
 
2381
            output += "Deprecated formats\n------------------\n\n"
 
2382
            for key, help in deprecated_pairs:
 
2383
                info = self.get_info(key)
 
2384
                output += wrapped(key, help, info)
 
2385
 
 
2386
        return output
 
2387
 
 
2388
 
 
2389
format_registry = BzrDirFormatRegistry()
 
2390
format_registry.register('weave', BzrDirFormat6,
 
2391
    'Pre-0.8 format.  Slower than knit and does not'
 
2392
    ' support checkouts or shared repositories.',
 
2393
    deprecated=True)
 
2394
format_registry.register_metadir('knit',
 
2395
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2396
    'Format using knits.  Recommended for interoperation with bzr <= 0.14.',
 
2397
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2398
    tree_format='bzrlib.workingtree.WorkingTreeFormat3')
 
2399
format_registry.register_metadir('metaweave',
 
2400
    'bzrlib.repofmt.weaverepo.RepositoryFormat7',
 
2401
    'Transitional format in 0.8.  Slower than knit.',
 
2402
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2403
    tree_format='bzrlib.workingtree.WorkingTreeFormat3',
 
2404
    deprecated=True)
 
2405
format_registry.register_metadir('dirstate',
 
2406
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2407
    help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
 
2408
        'above when accessed over the network.',
 
2409
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2410
    # this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
 
2411
    # directly from workingtree_4 triggers a circular import.
 
2412
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2413
    )
 
2414
format_registry.register_metadir('dirstate-tags',
 
2415
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2416
    help='New in 0.15: Fast local operations and improved scaling for '
 
2417
        'network operations. Additionally adds support for tags.'
 
2418
        ' Incompatible with bzr < 0.15.',
 
2419
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2420
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2421
    )
 
2422
format_registry.register_metadir('dirstate-with-subtree',
 
2423
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
 
2424
    help='New in 0.15: Fast local operations and improved scaling for '
 
2425
        'network operations. Additionally adds support for versioning nested '
 
2426
        'bzr branches. Incompatible with bzr < 0.15.',
 
2427
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2428
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2429
    hidden=True,
 
2430
    )
 
2431
format_registry.set_default('dirstate')