/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

Deprecate LockableFiles.put_utf8 and put_bytes.

Show diffs side-by-side

added added

removed removed

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