/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

(John Arbash Meinel)  Fix bug #158333,
        make sure that Repository.fetch(self) is properly a no-op for all
        Repository implementations.

Show diffs side-by-side

added added

removed removed

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