/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Aaron Bentley
  • Date: 2008-04-09 20:24:11 UTC
  • mto: This revision was merged to the branch mainline in revision 3359.
  • Revision ID: aaron@aaronbentley.com-20080409202411-7sygk52ahrxvq3pn
Update from review comments

Show diffs side-by-side

added added

removed removed

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