~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Ian Clatworthy
  • Date: 2008-04-17 08:21:14 UTC
  • mto: (4171.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 4173.
  • Revision ID: ian.clatworthy@canonical.com-20080417082114-76imbiarcbj16ov3
first cut at working tree content filtering

Show diffs side-by-side

added added

removed removed

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