/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Aaron Bentley
  • Date: 2007-11-27 19:37:36 UTC
  • mto: This revision was merged to the branch mainline in revision 3052.
  • Revision ID: abentley@panoramicfeedback.com-20071127193736-fjqqz482w0a6400o
SupportĀ reconfigureĀ --lightweight-checkout

Show diffs side-by-side

added added

removed removed

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