/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Martin Pool
  • Date: 2008-07-15 05:18:31 UTC
  • mto: This revision was merged to the branch mainline in revision 3542.
  • Revision ID: mbp@sourcefrog.net-20080715051831-t18m22ffrmbn9lj2
Rename Branch.set_stacked_on to set_stacked_on_url

Show diffs side-by-side

added added

removed removed

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