/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

 * New ``versionedfile.KeyMapper`` interface to abstract out the access to
   underyling .knit/.kndx etc files in repositories with partitioned
   storage. (Robert Collins)

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