~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: John Arbash Meinel
  • Date: 2008-05-06 14:34:57 UTC
  • mto: This revision was merged to the branch mainline in revision 3438.
  • Revision ID: john@arbash-meinel.com-20080506143457-mytwmwshmz1bu1uf
minor doc clarification for decentralized with shared-mainline

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
 
19
19
At format 7 this was split out into Branch, Repository and Checkout control
20
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.
21
26
"""
22
27
 
23
 
from copy import deepcopy
 
28
# TODO: Move old formats into a plugin to make this file smaller.
 
29
 
 
30
from cStringIO import StringIO
24
31
import os
25
 
from cStringIO import StringIO
26
 
from unittest import TestSuite
 
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
27
39
 
28
40
import bzrlib
29
 
import bzrlib.errors as errors
30
 
from bzrlib.lockable_files import LockableFiles, TransportLock
31
 
from bzrlib.lockdir import LockDir
 
41
from bzrlib import (
 
42
    errors,
 
43
    graph,
 
44
    lockable_files,
 
45
    lockdir,
 
46
    registry,
 
47
    remote,
 
48
    revision as _mod_revision,
 
49
    symbol_versioning,
 
50
    ui,
 
51
    urlutils,
 
52
    win32utils,
 
53
    workingtree,
 
54
    workingtree_4,
 
55
    xml4,
 
56
    xml5,
 
57
    )
32
58
from bzrlib.osutils import (
33
 
                            abspath,
34
 
                            pathjoin,
35
 
                            safe_unicode,
36
 
                            sha_strings,
37
 
                            sha_string,
38
 
                            )
 
59
    sha_strings,
 
60
    sha_string,
 
61
    )
 
62
from bzrlib.smart.client import _SmartClient
 
63
from bzrlib.smart import protocol
39
64
from bzrlib.store.revision.text import TextRevisionStore
40
65
from bzrlib.store.text import TextStore
41
66
from bzrlib.store.versioned import WeaveStore
42
 
from bzrlib.symbol_versioning import *
43
 
from bzrlib.trace import mutter
44
67
from bzrlib.transactions import WriteTransaction
45
 
from bzrlib.transport import get_transport
 
68
from bzrlib.transport import (
 
69
    do_catching_redirections,
 
70
    get_transport,
 
71
    )
 
72
from bzrlib.weave import Weave
 
73
""")
 
74
 
 
75
from bzrlib.trace import (
 
76
    mutter,
 
77
    note,
 
78
    )
46
79
from bzrlib.transport.local import LocalTransport
47
 
import bzrlib.urlutils as urlutils
48
 
from bzrlib.weave import Weave
49
 
from bzrlib.xml4 import serializer_v4
50
 
import bzrlib.xml5
 
80
from bzrlib.symbol_versioning import (
 
81
    deprecated_function,
 
82
    deprecated_method,
 
83
    zero_ninetyone,
 
84
    )
51
85
 
52
86
 
53
87
class BzrDir(object):
59
93
    transport
60
94
        the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
61
95
    root_transport
62
 
        a transport connected to the directory this bzr was opened from.
 
96
        a transport connected to the directory this bzr was opened from
 
97
        (i.e. the parent directory holding the .bzr directory).
63
98
    """
64
99
 
65
100
    def break_lock(self):
68
103
        If there is a tree, the tree is opened and break_lock() called.
69
104
        Otherwise, branch is tried, and finally repository.
70
105
        """
 
106
        # XXX: This seems more like a UI function than something that really
 
107
        # belongs in this class.
71
108
        try:
72
109
            thing_to_unlock = self.open_workingtree()
73
110
        except (errors.NotLocalUrl, errors.NoWorkingTree):
84
121
        """Return true if this bzrdir is one whose format we can convert from."""
85
122
        return True
86
123
 
 
124
    def check_conversion_target(self, target_format):
 
125
        target_repo_format = target_format.repository_format
 
126
        source_repo_format = self._format.repository_format
 
127
        source_repo_format.check_conversion_target(target_repo_format)
 
128
 
87
129
    @staticmethod
88
 
    def _check_supported(format, allow_unsupported):
89
 
        """Check whether format is a supported format.
90
 
 
91
 
        If allow_unsupported is True, this is a no-op.
 
130
    def _check_supported(format, allow_unsupported,
 
131
        recommend_upgrade=True,
 
132
        basedir=None):
 
133
        """Give an error or warning on old formats.
 
134
 
 
135
        :param format: may be any kind of format - workingtree, branch, 
 
136
        or repository.
 
137
 
 
138
        :param allow_unsupported: If true, allow opening 
 
139
        formats that are strongly deprecated, and which may 
 
140
        have limited functionality.
 
141
 
 
142
        :param recommend_upgrade: If true (default), warn
 
143
        the user through the ui object that they may wish
 
144
        to upgrade the object.
92
145
        """
 
146
        # TODO: perhaps move this into a base Format class; it's not BzrDir
 
147
        # specific. mbp 20070323
93
148
        if not allow_unsupported and not format.is_supported():
94
149
            # see open_downlevel to open legacy branches.
95
 
            raise errors.UnsupportedFormatError(
96
 
                    'sorry, format %s not supported' % format,
97
 
                    ['use a different bzr version',
98
 
                     'or remove the .bzr directory'
99
 
                     ' and "bzr init" again'])
 
150
            raise errors.UnsupportedFormatError(format=format)
 
151
        if recommend_upgrade \
 
152
            and getattr(format, 'upgrade_recommended', False):
 
153
            ui.ui_factory.recommend_upgrade(
 
154
                format.get_format_description(),
 
155
                basedir)
100
156
 
101
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
157
    def clone(self, url, revision_id=None, force_new_repo=False):
102
158
        """Clone this bzrdir and its contents to url verbatim.
103
159
 
104
 
        If urls last component does not exist, it will be created.
105
 
 
106
 
        if revision_id is not None, then the clone operation may tune
107
 
            itself to download less data.
108
 
        :param force_new_repo: Do not use a shared repository for the target 
109
 
                               even if one is available.
110
 
        """
111
 
        self._make_tail(url)
112
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
113
 
        result = self._format.initialize(url)
 
160
        If url's last component does not exist, it will be created.
 
161
 
 
162
        if revision_id is not None, then the clone operation may tune
 
163
            itself to download less data.
 
164
        :param force_new_repo: Do not use a shared repository for the target 
 
165
                               even if one is available.
 
166
        """
 
167
        return self.clone_on_transport(get_transport(url),
 
168
                                       revision_id=revision_id,
 
169
                                       force_new_repo=force_new_repo)
 
170
 
 
171
    def clone_on_transport(self, transport, revision_id=None,
 
172
                           force_new_repo=False):
 
173
        """Clone this bzrdir and its contents to transport verbatim.
 
174
 
 
175
        If the target directory does not exist, it will be created.
 
176
 
 
177
        if revision_id is not None, then the clone operation may tune
 
178
            itself to download less data.
 
179
        :param force_new_repo: Do not use a shared repository for the target 
 
180
                               even if one is available.
 
181
        """
 
182
        transport.ensure_base()
 
183
        result = self.cloning_metadir().initialize_on_transport(transport)
 
184
        repository_policy = None
114
185
        try:
115
186
            local_repo = self.find_repository()
116
187
        except errors.NoRepositoryPresent:
117
188
            local_repo = None
118
189
        if local_repo:
119
190
            # may need to copy content in
120
 
            if force_new_repo:
121
 
                result_repo = local_repo.clone(
122
 
                    result,
123
 
                    revision_id=revision_id,
124
 
                    basis=basis_repo)
125
 
                result_repo.set_make_working_trees(local_repo.make_working_trees())
126
 
            else:
127
 
                try:
128
 
                    result_repo = result.find_repository()
129
 
                    # fetch content this dir needs.
130
 
                    if basis_repo:
131
 
                        # XXX FIXME RBC 20060214 need tests for this when the basis
132
 
                        # is incomplete
133
 
                        result_repo.fetch(basis_repo, revision_id=revision_id)
134
 
                    result_repo.fetch(local_repo, revision_id=revision_id)
135
 
                except errors.NoRepositoryPresent:
136
 
                    # needed to make one anyway.
137
 
                    result_repo = local_repo.clone(
138
 
                        result,
139
 
                        revision_id=revision_id,
140
 
                        basis=basis_repo)
141
 
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
 
191
            repository_policy = result.determine_repository_policy(
 
192
                force_new_repo)
 
193
            make_working_trees = local_repo.make_working_trees()
 
194
            result_repo = repository_policy.acquire_repository(
 
195
                make_working_trees, local_repo.is_shared())
 
196
            result_repo.fetch(local_repo, revision_id=revision_id)
142
197
        # 1 if there is a branch present
143
198
        #   make sure its content is available in the target repository
144
199
        #   clone it.
145
200
        try:
146
 
            self.open_branch().clone(result, revision_id=revision_id)
 
201
            local_branch = self.open_branch()
147
202
        except errors.NotBranchError:
148
203
            pass
 
204
        else:
 
205
            result_branch = local_branch.clone(result, revision_id=revision_id)
 
206
            if repository_policy is not None:
 
207
                repository_policy.configure_branch(result_branch)
149
208
        try:
150
 
            self.open_workingtree().clone(result, basis=basis_tree)
151
 
        except (errors.NoWorkingTree, errors.NotLocalUrl):
152
 
            pass
 
209
            result_repo = result.find_repository()
 
210
        except errors.NoRepositoryPresent:
 
211
            result_repo = None
 
212
        if result_repo is None or result_repo.make_working_trees():
 
213
            try:
 
214
                self.open_workingtree().clone(result)
 
215
            except (errors.NoWorkingTree, errors.NotLocalUrl):
 
216
                pass
153
217
        return result
154
218
 
155
 
    def _get_basis_components(self, basis):
156
 
        """Retrieve the basis components that are available at basis."""
157
 
        if basis is None:
158
 
            return None, None, None
159
 
        try:
160
 
            basis_tree = basis.open_workingtree()
161
 
            basis_branch = basis_tree.branch
162
 
            basis_repo = basis_branch.repository
163
 
        except (errors.NoWorkingTree, errors.NotLocalUrl):
164
 
            basis_tree = None
165
 
            try:
166
 
                basis_branch = basis.open_branch()
167
 
                basis_repo = basis_branch.repository
168
 
            except errors.NotBranchError:
169
 
                basis_branch = None
170
 
                try:
171
 
                    basis_repo = basis.open_repository()
172
 
                except errors.NoRepositoryPresent:
173
 
                    basis_repo = None
174
 
        return basis_repo, basis_branch, basis_tree
175
 
 
176
219
    # TODO: This should be given a Transport, and should chdir up; otherwise
177
220
    # this will open a new connection.
178
221
    def _make_tail(self, url):
179
 
        head, tail = urlutils.split(url)
180
 
        if tail and tail != '.':
181
 
            t = bzrlib.transport.get_transport(head)
182
 
            try:
183
 
                t.mkdir(tail)
184
 
            except errors.FileExists:
185
 
                pass
 
222
        t = get_transport(url)
 
223
        t.ensure_base()
186
224
 
187
 
    # TODO: Should take a Transport
188
225
    @classmethod
189
 
    def create(cls, base):
 
226
    def create(cls, base, format=None, possible_transports=None):
190
227
        """Create a new BzrDir at the url 'base'.
191
228
        
192
 
        This will call the current default formats initialize with base
193
 
        as the only parameter.
194
 
 
195
 
        If you need a specific format, consider creating an instance
196
 
        of that and calling initialize().
 
229
        :param format: If supplied, the format of branch to create.  If not
 
230
            supplied, the default is used.
 
231
        :param possible_transports: If supplied, a list of transports that 
 
232
            can be reused to share a remote connection.
197
233
        """
198
234
        if cls is not BzrDir:
199
 
            raise AssertionError("BzrDir.create always creates the default format, "
200
 
                    "not one of %r" % cls)
201
 
        head, tail = urlutils.split(base)
202
 
        if tail and tail != '.':
203
 
            t = bzrlib.transport.get_transport(head)
204
 
            try:
205
 
                t.mkdir(tail)
206
 
            except errors.FileExists:
207
 
                pass
208
 
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
 
235
            raise AssertionError("BzrDir.create always creates the default"
 
236
                " format, not one of %r" % cls)
 
237
        t = get_transport(base, possible_transports)
 
238
        t.ensure_base()
 
239
        if format is None:
 
240
            format = BzrDirFormat.get_default_format()
 
241
        return format.initialize_on_transport(t)
 
242
 
 
243
    @staticmethod
 
244
    def find_bzrdirs(transport, evaluate=None, list_current=None):
 
245
        """Find bzrdirs recursively from current location.
 
246
 
 
247
        This is intended primarily as a building block for more sophisticated
 
248
        functionality, like finding trees under a directory, or finding
 
249
        branches that use a given repository.
 
250
        :param evaluate: An optional callable that yields recurse, value,
 
251
            where recurse controls whether this bzrdir is recursed into
 
252
            and value is the value to yield.  By default, all bzrdirs
 
253
            are recursed into, and the return value is the bzrdir.
 
254
        :param list_current: if supplied, use this function to list the current
 
255
            directory, instead of Transport.list_dir
 
256
        :return: a generator of found bzrdirs, or whatever evaluate returns.
 
257
        """
 
258
        if list_current is None:
 
259
            def list_current(transport):
 
260
                return transport.list_dir('')
 
261
        if evaluate is None:
 
262
            def evaluate(bzrdir):
 
263
                return True, bzrdir
 
264
 
 
265
        pending = [transport]
 
266
        while len(pending) > 0:
 
267
            current_transport = pending.pop()
 
268
            recurse = True
 
269
            try:
 
270
                bzrdir = BzrDir.open_from_transport(current_transport)
 
271
            except errors.NotBranchError:
 
272
                pass
 
273
            else:
 
274
                recurse, value = evaluate(bzrdir)
 
275
                yield value
 
276
            try:
 
277
                subdirs = list_current(current_transport)
 
278
            except errors.NoSuchFile:
 
279
                continue
 
280
            if recurse:
 
281
                for subdir in sorted(subdirs, reverse=True):
 
282
                    pending.append(current_transport.clone(subdir))
 
283
 
 
284
    @staticmethod
 
285
    def find_branches(transport):
 
286
        """Find all branches under a transport.
 
287
 
 
288
        This will find all branches below the transport, including branches
 
289
        inside other branches.  Where possible, it will use
 
290
        Repository.find_branches.
 
291
 
 
292
        To list all the branches that use a particular Repository, see
 
293
        Repository.find_branches
 
294
        """
 
295
        def evaluate(bzrdir):
 
296
            try:
 
297
                repository = bzrdir.open_repository()
 
298
            except errors.NoRepositoryPresent:
 
299
                pass
 
300
            else:
 
301
                return False, (None, repository)
 
302
            try:
 
303
                branch = bzrdir.open_branch()
 
304
            except errors.NotBranchError:
 
305
                return True, (None, None)
 
306
            else:
 
307
                return True, (branch, None)
 
308
        branches = []
 
309
        for branch, repo in BzrDir.find_bzrdirs(transport, evaluate=evaluate):
 
310
            if repo is not None:
 
311
                branches.extend(repo.find_branches())
 
312
            if branch is not None:
 
313
                branches.append(branch)
 
314
        return branches
 
315
 
 
316
 
 
317
    def destroy_repository(self):
 
318
        """Destroy the repository in this BzrDir"""
 
319
        raise NotImplementedError(self.destroy_repository)
209
320
 
210
321
    def create_branch(self):
211
322
        """Create a branch in this BzrDir.
212
323
 
213
 
        The bzrdirs format will control what branch format is created.
 
324
        The bzrdir's format will control what branch format is created.
214
325
        For more control see BranchFormatXX.create(a_bzrdir).
215
326
        """
216
327
        raise NotImplementedError(self.create_branch)
217
328
 
 
329
    def destroy_branch(self):
 
330
        """Destroy the branch in this BzrDir"""
 
331
        raise NotImplementedError(self.destroy_branch)
 
332
 
218
333
    @staticmethod
219
 
    def create_branch_and_repo(base, force_new_repo=False):
 
334
    def create_branch_and_repo(base, force_new_repo=False, format=None):
220
335
        """Create a new BzrDir, Branch and Repository at the url 'base'.
221
336
 
222
 
        This will use the current default BzrDirFormat, and use whatever 
 
337
        This will use the current default BzrDirFormat unless one is
 
338
        specified, and use whatever 
223
339
        repository format that that uses via bzrdir.create_branch and
224
340
        create_repository. If a shared repository is available that is used
225
341
        preferentially.
228
344
 
229
345
        :param base: The URL to create the branch at.
230
346
        :param force_new_repo: If True a new repository is always created.
 
347
        :param format: If supplied, the format of branch to create.  If not
 
348
            supplied, the default is used.
231
349
        """
232
 
        bzrdir = BzrDir.create(base)
 
350
        bzrdir = BzrDir.create(base, format)
233
351
        bzrdir._find_or_create_repository(force_new_repo)
234
352
        return bzrdir.create_branch()
235
353
 
 
354
    def determine_repository_policy(self, force_new_repo=False):
 
355
        """Return an object representing a policy to use.
 
356
 
 
357
        This controls whether a new repository is created, or a shared
 
358
        repository used instead.
 
359
        """
 
360
        def repository_policy(found_bzrdir):
 
361
            stop = False
 
362
            # does it have a repository ?
 
363
            try:
 
364
                repository = found_bzrdir.open_repository()
 
365
            except errors.NoRepositoryPresent:
 
366
                repository = None
 
367
            else:
 
368
                if ((found_bzrdir.root_transport.base !=
 
369
                     self.root_transport.base) and not repository.is_shared()):
 
370
                    repository = None
 
371
                else:
 
372
                    stop = True
 
373
            if not stop:
 
374
                return None, False
 
375
            if repository:
 
376
                return UseExistingRepository(repository), True
 
377
            else:
 
378
                return CreateRepository(self), True
 
379
 
 
380
        if not force_new_repo:
 
381
            policy = self._find_containing(repository_policy)
 
382
            if policy is not None:
 
383
                return policy
 
384
        return CreateRepository(self)
 
385
 
236
386
    def _find_or_create_repository(self, force_new_repo):
237
387
        """Create a new repository if needed, returning the repository."""
238
 
        if force_new_repo:
239
 
            return self.create_repository()
240
 
        try:
241
 
            return self.find_repository()
242
 
        except errors.NoRepositoryPresent:
243
 
            return self.create_repository()
244
 
        
 
388
        policy = self.determine_repository_policy(force_new_repo)
 
389
        return policy.acquire_repository()
 
390
 
245
391
    @staticmethod
246
392
    def create_branch_convenience(base, force_new_repo=False,
247
 
                                  force_new_tree=None, format=None):
 
393
                                  force_new_tree=None, format=None,
 
394
                                  possible_transports=None):
248
395
        """Create a new BzrDir, Branch and Repository at the url 'base'.
249
396
 
250
397
        This is a convenience function - it will use an existing repository
251
398
        if possible, can be told explicitly whether to create a working tree or
252
399
        not.
253
400
 
254
 
        This will use the current default BzrDirFormat, and use whatever 
 
401
        This will use the current default BzrDirFormat unless one is
 
402
        specified, and use whatever 
255
403
        repository format that that uses via bzrdir.create_branch and
256
404
        create_repository. If a shared repository is available that is used
257
405
        preferentially. Whatever repository is used, its tree creation policy
266
414
        :param force_new_repo: If True a new repository is always created.
267
415
        :param force_new_tree: If True or False force creation of a tree or 
268
416
                               prevent such creation respectively.
269
 
        :param format: Override for the for the bzrdir format to create
 
417
        :param format: Override for the bzrdir format to create.
 
418
        :param possible_transports: An optional reusable transports list.
270
419
        """
271
420
        if force_new_tree:
272
421
            # check for non local urls
273
 
            t = get_transport(safe_unicode(base))
 
422
            t = get_transport(base, possible_transports)
274
423
            if not isinstance(t, LocalTransport):
275
424
                raise errors.NotLocalUrl(base)
276
 
        if format is None:
277
 
            bzrdir = BzrDir.create(base)
278
 
        else:
279
 
            bzrdir = format.initialize(base)
 
425
        bzrdir = BzrDir.create(base, format, possible_transports)
280
426
        repo = bzrdir._find_or_create_repository(force_new_repo)
281
427
        result = bzrdir.create_branch()
282
 
        if force_new_tree or (repo.make_working_trees() and 
 
428
        if force_new_tree or (repo.make_working_trees() and
283
429
                              force_new_tree is None):
284
430
            try:
285
431
                bzrdir.create_workingtree()
286
432
            except errors.NotLocalUrl:
287
433
                pass
288
434
        return result
289
 
        
 
435
 
290
436
    @staticmethod
291
 
    def create_repository(base, shared=False):
 
437
    @deprecated_function(zero_ninetyone)
 
438
    def create_repository(base, shared=False, format=None):
292
439
        """Create a new BzrDir and Repository at the url 'base'.
293
440
 
294
 
        This will use the current default BzrDirFormat, and use whatever 
295
 
        repository format that that uses for bzrdirformat.create_repository.
 
441
        If no format is supplied, this will default to the current default
 
442
        BzrDirFormat by default, and use whatever repository format that that
 
443
        uses for bzrdirformat.create_repository.
296
444
 
297
 
        ;param shared: Create a shared repository rather than a standalone
 
445
        :param shared: Create a shared repository rather than a standalone
298
446
                       repository.
299
447
        The Repository object is returned.
300
448
 
301
449
        This must be overridden as an instance method in child classes, where
302
450
        it should take no parameters and construct whatever repository format
303
451
        that child class desires.
 
452
 
 
453
        This method is deprecated, please call create_repository on a bzrdir
 
454
        instance instead.
304
455
        """
305
 
        bzrdir = BzrDir.create(base)
306
 
        return bzrdir.create_repository()
 
456
        bzrdir = BzrDir.create(base, format)
 
457
        return bzrdir.create_repository(shared)
307
458
 
308
459
    @staticmethod
309
 
    def create_standalone_workingtree(base):
 
460
    def create_standalone_workingtree(base, format=None):
310
461
        """Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
311
462
 
312
463
        'base' must be a local path or a file:// url.
313
464
 
314
 
        This will use the current default BzrDirFormat, and use whatever 
 
465
        This will use the current default BzrDirFormat unless one is
 
466
        specified, and use whatever 
315
467
        repository format that that uses for bzrdirformat.create_workingtree,
316
468
        create_branch and create_repository.
317
469
 
318
 
        The WorkingTree object is returned.
 
470
        :param format: Override for the bzrdir format to create.
 
471
        :return: The WorkingTree object.
319
472
        """
320
 
        t = get_transport(safe_unicode(base))
 
473
        t = get_transport(base)
321
474
        if not isinstance(t, LocalTransport):
322
475
            raise errors.NotLocalUrl(base)
323
 
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
324
 
                                               force_new_repo=True).bzrdir
 
476
        bzrdir = BzrDir.create_branch_and_repo(base,
 
477
                                               force_new_repo=True,
 
478
                                               format=format).bzrdir
325
479
        return bzrdir.create_workingtree()
326
480
 
327
 
    def create_workingtree(self, revision_id=None):
 
481
    def create_workingtree(self, revision_id=None, from_branch=None,
 
482
        accelerator_tree=None, hardlink=False):
328
483
        """Create a working tree at this BzrDir.
329
484
        
330
 
        revision_id: create it as of this revision id.
 
485
        :param revision_id: create it as of this revision id.
 
486
        :param from_branch: override bzrdir branch (for lightweight checkouts)
 
487
        :param accelerator_tree: A tree which can be used for retrieving file
 
488
            contents more quickly than the revision tree, i.e. a workingtree.
 
489
            The revision tree will be used for cases where accelerator_tree's
 
490
            content is different.
331
491
        """
332
492
        raise NotImplementedError(self.create_workingtree)
333
493
 
 
494
    def retire_bzrdir(self, limit=10000):
 
495
        """Permanently disable the bzrdir.
 
496
 
 
497
        This is done by renaming it to give the user some ability to recover
 
498
        if there was a problem.
 
499
 
 
500
        This will have horrible consequences if anyone has anything locked or
 
501
        in use.
 
502
        :param limit: number of times to retry
 
503
        """
 
504
        i  = 0
 
505
        while True:
 
506
            try:
 
507
                to_path = '.bzr.retired.%d' % i
 
508
                self.root_transport.rename('.bzr', to_path)
 
509
                note("renamed %s to %s"
 
510
                    % (self.root_transport.abspath('.bzr'), to_path))
 
511
                return
 
512
            except (errors.TransportError, IOError, errors.PathError):
 
513
                i += 1
 
514
                if i > limit:
 
515
                    raise
 
516
                else:
 
517
                    pass
 
518
 
 
519
    def destroy_workingtree(self):
 
520
        """Destroy the working tree at this BzrDir.
 
521
 
 
522
        Formats that do not support this may raise UnsupportedOperation.
 
523
        """
 
524
        raise NotImplementedError(self.destroy_workingtree)
 
525
 
 
526
    def destroy_workingtree_metadata(self):
 
527
        """Destroy the control files for the working tree at this BzrDir.
 
528
 
 
529
        The contents of working tree files are not affected.
 
530
        Formats that do not support this may raise UnsupportedOperation.
 
531
        """
 
532
        raise NotImplementedError(self.destroy_workingtree_metadata)
 
533
 
 
534
    def _find_containing(self, evaluate):
 
535
        """Find something in a containing control directory.
 
536
 
 
537
        This method will scan containing control dirs, until it finds what
 
538
        it is looking for, decides that it will never find it, or runs out
 
539
        of containing control directories to check.
 
540
 
 
541
        It is used to implement find_repository and
 
542
        determine_repository_policy.
 
543
 
 
544
        :param evaluate: A function returning (value, stop).  If stop is True,
 
545
            the value will be returned.
 
546
        """
 
547
        found_bzrdir = self
 
548
        while True:
 
549
            result, stop = evaluate(found_bzrdir)
 
550
            if stop:
 
551
                return result
 
552
            next_transport = found_bzrdir.root_transport.clone('..')
 
553
            if (found_bzrdir.root_transport.base == next_transport.base):
 
554
                # top of the file system
 
555
                return None
 
556
            # find the next containing bzrdir
 
557
            try:
 
558
                found_bzrdir = BzrDir.open_containing_from_transport(
 
559
                    next_transport)[0]
 
560
            except errors.NotBranchError:
 
561
                return None
 
562
 
334
563
    def find_repository(self):
335
 
        """Find the repository that should be used for a_bzrdir.
 
564
        """Find the repository that should be used.
336
565
 
337
566
        This does not require a branch as we use it to find the repo for
338
567
        new branches as well as to hook existing branches up to their
339
568
        repository.
340
569
        """
341
 
        try:
342
 
            return self.open_repository()
343
 
        except errors.NoRepositoryPresent:
344
 
            pass
345
 
        next_transport = self.root_transport.clone('..')
346
 
        while True:
347
 
            # find the next containing bzrdir
348
 
            try:
349
 
                found_bzrdir = BzrDir.open_containing_from_transport(
350
 
                    next_transport)[0]
351
 
            except errors.NotBranchError:
352
 
                # none found
353
 
                raise errors.NoRepositoryPresent(self)
 
570
        def usable_repository(found_bzrdir):
354
571
            # does it have a repository ?
355
572
            try:
356
573
                repository = found_bzrdir.open_repository()
357
574
            except errors.NoRepositoryPresent:
358
 
                next_transport = found_bzrdir.root_transport.clone('..')
359
 
                if (found_bzrdir.root_transport.base == next_transport.base):
360
 
                    # top of the file system
361
 
                    break
362
 
                else:
363
 
                    continue
364
 
            if ((found_bzrdir.root_transport.base == 
365
 
                 self.root_transport.base) or repository.is_shared()):
366
 
                return repository
 
575
                return None, False
 
576
            if found_bzrdir.root_transport.base == self.root_transport.base:
 
577
                return repository, True
 
578
            elif repository.is_shared():
 
579
                return repository, True
367
580
            else:
368
 
                raise errors.NoRepositoryPresent(self)
369
 
        raise errors.NoRepositoryPresent(self)
 
581
                return None, True
 
582
 
 
583
        found_repo = self._find_containing(usable_repository)
 
584
        if found_repo is None:
 
585
            raise errors.NoRepositoryPresent(self)
 
586
        return found_repo
 
587
 
 
588
    def get_branch_reference(self):
 
589
        """Return the referenced URL for the branch in this bzrdir.
 
590
 
 
591
        :raises NotBranchError: If there is no Branch.
 
592
        :return: The URL the branch in this bzrdir references if it is a
 
593
            reference branch, or None for regular branches.
 
594
        """
 
595
        return None
370
596
 
371
597
    def get_branch_transport(self, branch_format):
372
598
        """Get the transport for use by branch format in this BzrDir.
376
602
        a format string, and vice versa.
377
603
 
378
604
        If branch_format is None, the transport is returned with no 
379
 
        checking. if it is not None, then the returned transport is
 
605
        checking. If it is not None, then the returned transport is
380
606
        guaranteed to point to an existing directory ready for use.
381
607
        """
382
608
        raise NotImplementedError(self.get_branch_transport)
389
615
        a format string, and vice versa.
390
616
 
391
617
        If repository_format is None, the transport is returned with no 
392
 
        checking. if it is not None, then the returned transport is
 
618
        checking. If it is not None, then the returned transport is
393
619
        guaranteed to point to an existing directory ready for use.
394
620
        """
395
621
        raise NotImplementedError(self.get_repository_transport)
398
624
        """Get the transport for use by workingtree format in this BzrDir.
399
625
 
400
626
        Note that bzr dirs that do not support format strings will raise
401
 
        IncompatibleFormat if the workingtree format they are given has
402
 
        a format string, and vice versa.
 
627
        IncompatibleFormat if the workingtree format they are given has a
 
628
        format string, and vice versa.
403
629
 
404
630
        If workingtree_format is None, the transport is returned with no 
405
 
        checking. if it is not None, then the returned transport is
 
631
        checking. If it is not None, then the returned transport is
406
632
        guaranteed to point to an existing directory ready for use.
407
633
        """
408
634
        raise NotImplementedError(self.get_workingtree_transport)
434
660
        # this might be better on the BzrDirFormat class because it refers to 
435
661
        # all the possible bzrdir disk formats. 
436
662
        # This method is tested via the workingtree is_control_filename tests- 
437
 
        # it was extracted from WorkingTree.is_control_filename. If the methods
438
 
        # contract is extended beyond the current trivial  implementation please
 
663
        # it was extracted from WorkingTree.is_control_filename. If the method's
 
664
        # contract is extended beyond the current trivial implementation, please
439
665
        # add new tests for it to the appropriate place.
440
666
        return filename == '.bzr' or filename.startswith('.bzr/')
441
667
 
456
682
        return BzrDir.open(base, _unsupported=True)
457
683
        
458
684
    @staticmethod
459
 
    def open(base, _unsupported=False):
460
 
        """Open an existing bzrdir, rooted at 'base' (url)
 
685
    def open(base, _unsupported=False, possible_transports=None):
 
686
        """Open an existing bzrdir, rooted at 'base' (url).
461
687
        
462
 
        _unsupported is a private parameter to the BzrDir class.
463
 
        """
464
 
        t = get_transport(base)
465
 
        mutter("trying to open %r with transport %r", base, t)
466
 
        format = BzrDirFormat.find_format(t)
 
688
        :param _unsupported: a private parameter to the BzrDir class.
 
689
        """
 
690
        t = get_transport(base, possible_transports=possible_transports)
 
691
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
 
692
 
 
693
    @staticmethod
 
694
    def open_from_transport(transport, _unsupported=False,
 
695
                            _server_formats=True):
 
696
        """Open a bzrdir within a particular directory.
 
697
 
 
698
        :param transport: Transport containing the bzrdir.
 
699
        :param _unsupported: private.
 
700
        """
 
701
        base = transport.base
 
702
 
 
703
        def find_format(transport):
 
704
            return transport, BzrDirFormat.find_format(
 
705
                transport, _server_formats=_server_formats)
 
706
 
 
707
        def redirected(transport, e, redirection_notice):
 
708
            qualified_source = e.get_source_url()
 
709
            relpath = transport.relpath(qualified_source)
 
710
            if not e.target.endswith(relpath):
 
711
                # Not redirected to a branch-format, not a branch
 
712
                raise errors.NotBranchError(path=e.target)
 
713
            target = e.target[:-len(relpath)]
 
714
            note('%s is%s redirected to %s',
 
715
                 transport.base, e.permanently, target)
 
716
            # Let's try with a new transport
 
717
            # FIXME: If 'transport' has a qualifier, this should
 
718
            # be applied again to the new transport *iff* the
 
719
            # schemes used are the same. Uncomment this code
 
720
            # once the function (and tests) exist.
 
721
            # -- vila20070212
 
722
            #target = urlutils.copy_url_qualifiers(original, target)
 
723
            return get_transport(target)
 
724
 
 
725
        try:
 
726
            transport, format = do_catching_redirections(find_format,
 
727
                                                         transport,
 
728
                                                         redirected)
 
729
        except errors.TooManyRedirections:
 
730
            raise errors.NotBranchError(base)
 
731
 
467
732
        BzrDir._check_supported(format, _unsupported)
468
 
        return format.open(t, _found=True)
 
733
        return format.open(transport, _found=True)
469
734
 
470
735
    def open_branch(self, unsupported=False):
471
736
        """Open the branch object at this BzrDir if one is present.
478
743
        raise NotImplementedError(self.open_branch)
479
744
 
480
745
    @staticmethod
481
 
    def open_containing(url):
 
746
    def open_containing(url, possible_transports=None):
482
747
        """Open an existing branch which contains url.
483
748
        
484
749
        :param url: url to search from.
485
750
        See open_containing_from_transport for more detail.
486
751
        """
487
 
        return BzrDir.open_containing_from_transport(get_transport(url))
 
752
        transport = get_transport(url, possible_transports)
 
753
        return BzrDir.open_containing_from_transport(transport)
488
754
    
489
755
    @staticmethod
490
756
    def open_containing_from_transport(a_transport):
491
 
        """Open an existing branch which contains a_transport.base
 
757
        """Open an existing branch which contains a_transport.base.
492
758
 
493
759
        This probes for a branch at a_transport, and searches upwards from there.
494
760
 
505
771
        url = a_transport.base
506
772
        while True:
507
773
            try:
508
 
                format = BzrDirFormat.find_format(a_transport)
509
 
                BzrDir._check_supported(format, False)
510
 
                return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
 
774
                result = BzrDir.open_from_transport(a_transport)
 
775
                return result, urlutils.unescape(a_transport.relpath(url))
511
776
            except errors.NotBranchError, e:
512
 
                ## mutter('not a branch in: %r %s', a_transport.base, e)
513
777
                pass
514
 
            new_t = a_transport.clone('..')
 
778
            try:
 
779
                new_t = a_transport.clone('..')
 
780
            except errors.InvalidURLJoin:
 
781
                # reached the root, whatever that may be
 
782
                raise errors.NotBranchError(path=url)
515
783
            if new_t.base == a_transport.base:
516
784
                # reached the root, whatever that may be
517
785
                raise errors.NotBranchError(path=url)
518
786
            a_transport = new_t
519
787
 
 
788
    def _get_tree_branch(self):
 
789
        """Return the branch and tree, if any, for this bzrdir.
 
790
 
 
791
        Return None for tree if not present or inaccessible.
 
792
        Raise NotBranchError if no branch is present.
 
793
        :return: (tree, branch)
 
794
        """
 
795
        try:
 
796
            tree = self.open_workingtree()
 
797
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
798
            tree = None
 
799
            branch = self.open_branch()
 
800
        else:
 
801
            branch = tree.branch
 
802
        return tree, branch
 
803
 
 
804
    @classmethod
 
805
    def open_tree_or_branch(klass, location):
 
806
        """Return the branch and working tree at a location.
 
807
 
 
808
        If there is no tree at the location, tree will be None.
 
809
        If there is no branch at the location, an exception will be
 
810
        raised
 
811
        :return: (tree, branch)
 
812
        """
 
813
        bzrdir = klass.open(location)
 
814
        return bzrdir._get_tree_branch()
 
815
 
 
816
    @classmethod
 
817
    def open_containing_tree_or_branch(klass, location):
 
818
        """Return the branch and working tree contained by a location.
 
819
 
 
820
        Returns (tree, branch, relpath).
 
821
        If there is no tree at containing the location, tree will be None.
 
822
        If there is no branch containing the location, an exception will be
 
823
        raised
 
824
        relpath is the portion of the path that is contained by the branch.
 
825
        """
 
826
        bzrdir, relpath = klass.open_containing(location)
 
827
        tree, branch = bzrdir._get_tree_branch()
 
828
        return tree, branch, relpath
 
829
 
520
830
    def open_repository(self, _unsupported=False):
521
831
        """Open the repository object at this BzrDir if one is present.
522
832
 
523
 
        This will not follow the Branch object pointer - its strictly a direct
 
833
        This will not follow the Branch object pointer - it's strictly a direct
524
834
        open facility. Most client code should use open_branch().repository to
525
835
        get at a repository.
526
836
 
527
 
        _unsupported is a private parameter, not part of the api.
 
837
        :param _unsupported: a private parameter, not part of the api.
528
838
        TODO: static convenience version of this?
529
839
        """
530
840
        raise NotImplementedError(self.open_repository)
531
841
 
532
 
    def open_workingtree(self, _unsupported=False):
 
842
    def open_workingtree(self, _unsupported=False,
 
843
                         recommend_upgrade=True, from_branch=None):
533
844
        """Open the workingtree object at this BzrDir if one is present.
534
 
        
535
 
        TODO: static convenience version of this?
 
845
 
 
846
        :param recommend_upgrade: Optional keyword parameter, when True (the
 
847
            default), emit through the ui module a recommendation that the user
 
848
            upgrade the working tree when the workingtree being opened is old
 
849
            (but still fully supported).
 
850
        :param from_branch: override bzrdir branch (for lightweight checkouts)
536
851
        """
537
852
        raise NotImplementedError(self.open_workingtree)
538
853
 
560
875
        workingtree and discards it, and that's somewhat expensive.) 
561
876
        """
562
877
        try:
563
 
            self.open_workingtree()
 
878
            self.open_workingtree(recommend_upgrade=False)
564
879
            return True
565
880
        except errors.NoWorkingTree:
566
881
            return False
567
882
 
568
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
883
    def _cloning_metadir(self):
 
884
        """Produce a metadir suitable for cloning with."""
 
885
        result_format = self._format.__class__()
 
886
        try:
 
887
            try:
 
888
                branch = self.open_branch()
 
889
                source_repository = branch.repository
 
890
            except errors.NotBranchError:
 
891
                source_branch = None
 
892
                source_repository = self.open_repository()
 
893
        except errors.NoRepositoryPresent:
 
894
            source_repository = None
 
895
        else:
 
896
            # XXX TODO: This isinstance is here because we have not implemented
 
897
            # the fix recommended in bug # 103195 - to delegate this choice the
 
898
            # repository itself.
 
899
            repo_format = source_repository._format
 
900
            if not isinstance(repo_format, remote.RemoteRepositoryFormat):
 
901
                result_format.repository_format = repo_format
 
902
        try:
 
903
            # TODO: Couldn't we just probe for the format in these cases,
 
904
            # rather than opening the whole tree?  It would be a little
 
905
            # faster. mbp 20070401
 
906
            tree = self.open_workingtree(recommend_upgrade=False)
 
907
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
908
            result_format.workingtree_format = None
 
909
        else:
 
910
            result_format.workingtree_format = tree._format.__class__()
 
911
        return result_format, source_repository
 
912
 
 
913
    def cloning_metadir(self):
 
914
        """Produce a metadir suitable for cloning or sprouting with.
 
915
 
 
916
        These operations may produce workingtrees (yes, even though they're
 
917
        "cloning" something that doesn't have a tree), so a viable workingtree
 
918
        format must be selected.
 
919
        """
 
920
        format, repository = self._cloning_metadir()
 
921
        if format._workingtree_format is None:
 
922
            if repository is None:
 
923
                return format
 
924
            tree_format = repository._format._matchingbzrdir.workingtree_format
 
925
            format.workingtree_format = tree_format.__class__()
 
926
        return format
 
927
 
 
928
    def checkout_metadir(self):
 
929
        return self.cloning_metadir()
 
930
 
 
931
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
932
               recurse='down', possible_transports=None,
 
933
               accelerator_tree=None, hardlink=False):
569
934
        """Create a copy of this bzrdir prepared for use as a new line of
570
935
        development.
571
936
 
572
 
        If urls last component does not exist, it will be created.
 
937
        If url's last component does not exist, it will be created.
573
938
 
574
939
        Attributes related to the identity of the source branch like
575
940
        branch nickname will be cleaned, a working tree is created
578
943
 
579
944
        if revision_id is not None, then the clone operation may tune
580
945
            itself to download less data.
 
946
        :param accelerator_tree: A tree which can be used for retrieving file
 
947
            contents more quickly than the revision tree, i.e. a workingtree.
 
948
            The revision tree will be used for cases where accelerator_tree's
 
949
            content is different.
 
950
        :param hardlink: If true, hard-link files from accelerator_tree,
 
951
            where possible.
581
952
        """
582
 
        self._make_tail(url)
583
 
        result = self._format.initialize(url)
584
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
953
        target_transport = get_transport(url, possible_transports)
 
954
        target_transport.ensure_base()
 
955
        cloning_format = self.cloning_metadir()
 
956
        result = cloning_format.initialize_on_transport(target_transport)
585
957
        try:
586
958
            source_branch = self.open_branch()
587
959
            source_repository = source_branch.repository
590
962
            try:
591
963
                source_repository = self.open_repository()
592
964
            except errors.NoRepositoryPresent:
593
 
                # copy the entire basis one if there is one
594
 
                # but there is no repository.
595
 
                source_repository = basis_repo
 
965
                source_repository = None
596
966
        if force_new_repo:
597
967
            result_repo = None
598
968
        else:
607
977
            result.create_repository()
608
978
        elif source_repository is not None and result_repo is None:
609
979
            # have source, and want to make a new target repo
610
 
            # we don't clone the repo because that preserves attributes
611
 
            # like is_shared(), and we have not yet implemented a 
612
 
            # repository sprout().
613
 
            result_repo = result.create_repository()
614
 
        if result_repo is not None:
 
980
            result_repo = source_repository.sprout(result,
 
981
                                                   revision_id=revision_id)
 
982
        else:
615
983
            # fetch needed content into target.
616
 
            if basis_repo:
617
 
                # XXX FIXME RBC 20060214 need tests for this when the basis
618
 
                # is incomplete
619
 
                result_repo.fetch(basis_repo, revision_id=revision_id)
620
 
            result_repo.fetch(source_repository, revision_id=revision_id)
 
984
            if source_repository is not None:
 
985
                # would rather do 
 
986
                # source_repository.copy_content_into(result_repo,
 
987
                #                                     revision_id=revision_id)
 
988
                # so we can override the copy method
 
989
                result_repo.fetch(source_repository, revision_id=revision_id)
621
990
        if source_branch is not None:
622
991
            source_branch.sprout(result, revision_id=revision_id)
623
992
        else:
624
993
            result.create_branch()
625
 
        # TODO: jam 20060426 we probably need a test in here in the
626
 
        #       case that the newly sprouted branch is a remote one
627
 
        if result_repo is None or result_repo.make_working_trees():
628
 
            result.create_workingtree()
 
994
        if isinstance(target_transport, LocalTransport) and (
 
995
            result_repo is None or result_repo.make_working_trees()):
 
996
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
 
997
                hardlink=hardlink)
 
998
            wt.lock_write()
 
999
            try:
 
1000
                if wt.path2id('') is None:
 
1001
                    try:
 
1002
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
1003
                    except errors.NoWorkingTree:
 
1004
                        pass
 
1005
            finally:
 
1006
                wt.unlock()
 
1007
        else:
 
1008
            wt = None
 
1009
        if recurse == 'down':
 
1010
            if wt is not None:
 
1011
                basis = wt.basis_tree()
 
1012
                basis.lock_read()
 
1013
                subtrees = basis.iter_references()
 
1014
                recurse_branch = wt.branch
 
1015
            elif source_branch is not None:
 
1016
                basis = source_branch.basis_tree()
 
1017
                basis.lock_read()
 
1018
                subtrees = basis.iter_references()
 
1019
                recurse_branch = source_branch
 
1020
            else:
 
1021
                subtrees = []
 
1022
                basis = None
 
1023
            try:
 
1024
                for path, file_id in subtrees:
 
1025
                    target = urlutils.join(url, urlutils.escape(path))
 
1026
                    sublocation = source_branch.reference_parent(file_id, path)
 
1027
                    sublocation.bzrdir.sprout(target,
 
1028
                        basis.get_reference_revision(file_id, path),
 
1029
                        force_new_repo=force_new_repo, recurse=recurse)
 
1030
            finally:
 
1031
                if basis is not None:
 
1032
                    basis.unlock()
629
1033
        return result
630
1034
 
631
1035
 
635
1039
    def __init__(self, _transport, _format):
636
1040
        """See BzrDir.__init__."""
637
1041
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
638
 
        assert self._format._lock_class == TransportLock
 
1042
        assert self._format._lock_class == lockable_files.TransportLock
639
1043
        assert self._format._lock_file_name == 'branch-lock'
640
 
        self._control_files = LockableFiles(self.get_branch_transport(None),
 
1044
        self._control_files = lockable_files.LockableFiles(
 
1045
                                            self.get_branch_transport(None),
641
1046
                                            self._format._lock_file_name,
642
1047
                                            self._format._lock_class)
643
1048
 
645
1050
        """Pre-splitout bzrdirs do not suffer from stale locks."""
646
1051
        raise NotImplementedError(self.break_lock)
647
1052
 
648
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
1053
    def cloning_metadir(self):
 
1054
        """Produce a metadir suitable for cloning with."""
 
1055
        return self._format.__class__()
 
1056
 
 
1057
    def clone(self, url, revision_id=None, force_new_repo=False):
649
1058
        """See BzrDir.clone()."""
650
1059
        from bzrlib.workingtree import WorkingTreeFormat2
651
1060
        self._make_tail(url)
652
1061
        result = self._format._initialize_for_clone(url)
653
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
654
 
        self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
1062
        self.open_repository().clone(result, revision_id=revision_id)
655
1063
        from_branch = self.open_branch()
656
1064
        from_branch.clone(result, revision_id=revision_id)
657
1065
        try:
658
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
1066
            self.open_workingtree().clone(result)
659
1067
        except errors.NotLocalUrl:
660
1068
            # make a new one, this format always has to have one.
661
1069
            try:
670
1078
        """See BzrDir.create_branch."""
671
1079
        return self.open_branch()
672
1080
 
 
1081
    def destroy_branch(self):
 
1082
        """See BzrDir.destroy_branch."""
 
1083
        raise errors.UnsupportedOperation(self.destroy_branch, self)
 
1084
 
673
1085
    def create_repository(self, shared=False):
674
1086
        """See BzrDir.create_repository."""
675
1087
        if shared:
676
1088
            raise errors.IncompatibleFormat('shared repository', self._format)
677
1089
        return self.open_repository()
678
1090
 
679
 
    def create_workingtree(self, revision_id=None):
 
1091
    def destroy_repository(self):
 
1092
        """See BzrDir.destroy_repository."""
 
1093
        raise errors.UnsupportedOperation(self.destroy_repository, self)
 
1094
 
 
1095
    def create_workingtree(self, revision_id=None, from_branch=None,
 
1096
                           accelerator_tree=None, hardlink=False):
680
1097
        """See BzrDir.create_workingtree."""
681
1098
        # this looks buggy but is not -really-
 
1099
        # because this format creates the workingtree when the bzrdir is
 
1100
        # created
682
1101
        # clone and sprout will have set the revision_id
683
1102
        # and that will have set it for us, its only
684
1103
        # specific uses of create_workingtree in isolation
685
1104
        # that can do wonky stuff here, and that only
686
1105
        # happens for creating checkouts, which cannot be 
687
1106
        # done on this format anyway. So - acceptable wart.
688
 
        result = self.open_workingtree()
 
1107
        result = self.open_workingtree(recommend_upgrade=False)
689
1108
        if revision_id is not None:
690
 
            result.set_last_revision(revision_id)
 
1109
            if revision_id == _mod_revision.NULL_REVISION:
 
1110
                result.set_parent_ids([])
 
1111
            else:
 
1112
                result.set_parent_ids([revision_id])
691
1113
        return result
692
1114
 
 
1115
    def destroy_workingtree(self):
 
1116
        """See BzrDir.destroy_workingtree."""
 
1117
        raise errors.UnsupportedOperation(self.destroy_workingtree, self)
 
1118
 
 
1119
    def destroy_workingtree_metadata(self):
 
1120
        """See BzrDir.destroy_workingtree_metadata."""
 
1121
        raise errors.UnsupportedOperation(self.destroy_workingtree_metadata, 
 
1122
                                          self)
 
1123
 
693
1124
    def get_branch_transport(self, branch_format):
694
1125
        """See BzrDir.get_branch_transport()."""
695
1126
        if branch_format is None:
735
1166
        self._check_supported(format, unsupported)
736
1167
        return format.open(self, _found=True)
737
1168
 
738
 
    def sprout(self, url, revision_id=None, basis=None):
 
1169
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
1170
               possible_transports=None, accelerator_tree=None,
 
1171
               hardlink=False):
739
1172
        """See BzrDir.sprout()."""
740
1173
        from bzrlib.workingtree import WorkingTreeFormat2
741
1174
        self._make_tail(url)
742
1175
        result = self._format._initialize_for_clone(url)
743
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
744
1176
        try:
745
 
            self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
1177
            self.open_repository().clone(result, revision_id=revision_id)
746
1178
        except errors.NoRepositoryPresent:
747
1179
            pass
748
1180
        try:
750
1182
        except errors.NotBranchError:
751
1183
            pass
752
1184
        # we always want a working tree
753
 
        WorkingTreeFormat2().initialize(result)
 
1185
        WorkingTreeFormat2().initialize(result,
 
1186
                                        accelerator_tree=accelerator_tree,
 
1187
                                        hardlink=hardlink)
754
1188
        return result
755
1189
 
756
1190
 
770
1204
 
771
1205
    def open_repository(self):
772
1206
        """See BzrDir.open_repository."""
773
 
        from bzrlib.repository import RepositoryFormat4
 
1207
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
774
1208
        return RepositoryFormat4().open(self, _found=True)
775
1209
 
776
1210
 
782
1216
 
783
1217
    def open_repository(self):
784
1218
        """See BzrDir.open_repository."""
785
 
        from bzrlib.repository import RepositoryFormat5
 
1219
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
786
1220
        return RepositoryFormat5().open(self, _found=True)
787
1221
 
788
 
    def open_workingtree(self, _unsupported=False):
 
1222
    def open_workingtree(self, _unsupported=False,
 
1223
            recommend_upgrade=True):
789
1224
        """See BzrDir.create_workingtree."""
790
1225
        from bzrlib.workingtree import WorkingTreeFormat2
791
 
        return WorkingTreeFormat2().open(self, _found=True)
 
1226
        wt_format = WorkingTreeFormat2()
 
1227
        # we don't warn here about upgrades; that ought to be handled for the
 
1228
        # bzrdir as a whole
 
1229
        return wt_format.open(self, _found=True)
792
1230
 
793
1231
 
794
1232
class BzrDir6(BzrDirPreSplitOut):
799
1237
 
800
1238
    def open_repository(self):
801
1239
        """See BzrDir.open_repository."""
802
 
        from bzrlib.repository import RepositoryFormat6
 
1240
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
803
1241
        return RepositoryFormat6().open(self, _found=True)
804
1242
 
805
 
    def open_workingtree(self, _unsupported=False):
 
1243
    def open_workingtree(self, _unsupported=False,
 
1244
        recommend_upgrade=True):
806
1245
        """See BzrDir.create_workingtree."""
 
1246
        # we don't warn here about upgrades; that ought to be handled for the
 
1247
        # bzrdir as a whole
807
1248
        from bzrlib.workingtree import WorkingTreeFormat2
808
1249
        return WorkingTreeFormat2().open(self, _found=True)
809
1250
 
823
1264
 
824
1265
    def create_branch(self):
825
1266
        """See BzrDir.create_branch."""
826
 
        from bzrlib.branch import BranchFormat
827
 
        return BranchFormat.get_default_format().initialize(self)
 
1267
        return self._format.get_branch_format().initialize(self)
 
1268
 
 
1269
    def destroy_branch(self):
 
1270
        """See BzrDir.create_branch."""
 
1271
        self.transport.delete_tree('branch')
828
1272
 
829
1273
    def create_repository(self, shared=False):
830
1274
        """See BzrDir.create_repository."""
831
1275
        return self._format.repository_format.initialize(self, shared)
832
1276
 
833
 
    def create_workingtree(self, revision_id=None):
 
1277
    def destroy_repository(self):
 
1278
        """See BzrDir.destroy_repository."""
 
1279
        self.transport.delete_tree('repository')
 
1280
 
 
1281
    def create_workingtree(self, revision_id=None, from_branch=None,
 
1282
                           accelerator_tree=None, hardlink=False):
834
1283
        """See BzrDir.create_workingtree."""
835
 
        from bzrlib.workingtree import WorkingTreeFormat
836
 
        return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
 
1284
        return self._format.workingtree_format.initialize(
 
1285
            self, revision_id, from_branch=from_branch,
 
1286
            accelerator_tree=accelerator_tree, hardlink=hardlink)
 
1287
 
 
1288
    def destroy_workingtree(self):
 
1289
        """See BzrDir.destroy_workingtree."""
 
1290
        wt = self.open_workingtree(recommend_upgrade=False)
 
1291
        repository = wt.branch.repository
 
1292
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1293
        wt.revert(old_tree=empty)
 
1294
        self.destroy_workingtree_metadata()
 
1295
 
 
1296
    def destroy_workingtree_metadata(self):
 
1297
        self.transport.delete_tree('checkout')
 
1298
 
 
1299
    def find_branch_format(self):
 
1300
        """Find the branch 'format' for this bzrdir.
 
1301
 
 
1302
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
1303
        """
 
1304
        from bzrlib.branch import BranchFormat
 
1305
        return BranchFormat.find_format(self)
837
1306
 
838
1307
    def _get_mkdir_mode(self):
839
1308
        """Figure out the mode to use when creating a bzrdir subdir."""
840
 
        temp_control = LockableFiles(self.transport, '', TransportLock)
 
1309
        temp_control = lockable_files.LockableFiles(self.transport, '',
 
1310
                                     lockable_files.TransportLock)
841
1311
        return temp_control._dir_mode
842
1312
 
 
1313
    def get_branch_reference(self):
 
1314
        """See BzrDir.get_branch_reference()."""
 
1315
        from bzrlib.branch import BranchFormat
 
1316
        format = BranchFormat.find_format(self)
 
1317
        return format.get_reference(self)
 
1318
 
843
1319
    def get_branch_transport(self, branch_format):
844
1320
        """See BzrDir.get_branch_transport()."""
845
1321
        if branch_format is None:
897
1373
                return True
898
1374
        except errors.NoRepositoryPresent:
899
1375
            pass
900
 
        # currently there are no other possible conversions for meta1 formats.
 
1376
        try:
 
1377
            if not isinstance(self.open_branch()._format,
 
1378
                              format.get_branch_format().__class__):
 
1379
                # the branch needs an upgrade.
 
1380
                return True
 
1381
        except errors.NotBranchError:
 
1382
            pass
 
1383
        try:
 
1384
            my_wt = self.open_workingtree(recommend_upgrade=False)
 
1385
            if not isinstance(my_wt._format,
 
1386
                              format.workingtree_format.__class__):
 
1387
                # the workingtree needs an upgrade.
 
1388
                return True
 
1389
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1390
            pass
901
1391
        return False
902
1392
 
903
1393
    def open_branch(self, unsupported=False):
904
1394
        """See BzrDir.open_branch."""
905
 
        from bzrlib.branch import BranchFormat
906
 
        format = BranchFormat.find_format(self)
 
1395
        format = self.find_branch_format()
907
1396
        self._check_supported(format, unsupported)
908
1397
        return format.open(self, _found=True)
909
1398
 
914
1403
        self._check_supported(format, unsupported)
915
1404
        return format.open(self, _found=True)
916
1405
 
917
 
    def open_workingtree(self, unsupported=False):
 
1406
    def open_workingtree(self, unsupported=False,
 
1407
            recommend_upgrade=True):
918
1408
        """See BzrDir.open_workingtree."""
919
1409
        from bzrlib.workingtree import WorkingTreeFormat
920
1410
        format = WorkingTreeFormat.find_format(self)
921
 
        self._check_supported(format, unsupported)
 
1411
        self._check_supported(format, unsupported,
 
1412
            recommend_upgrade,
 
1413
            basedir=self.root_transport.base)
922
1414
        return format.open(self, _found=True)
923
1415
 
924
1416
 
930
1422
     * a format string,
931
1423
     * an open routine.
932
1424
 
933
 
    Formats are placed in an dict by their format string for reference 
 
1425
    Formats are placed in a dict by their format string for reference 
934
1426
    during bzrdir opening. These should be subclasses of BzrDirFormat
935
1427
    for consistency.
936
1428
 
951
1443
    This is a list of BzrDirFormat objects.
952
1444
    """
953
1445
 
 
1446
    _control_server_formats = []
 
1447
    """The registered control server formats, e.g. RemoteBzrDirs.
 
1448
 
 
1449
    This is a list of BzrDirFormat objects.
 
1450
    """
 
1451
 
954
1452
    _lock_file_name = 'branch-lock'
955
1453
 
956
1454
    # _lock_class must be set in subclasses to the lock type, typ.
957
1455
    # TransportLock or LockDir
958
1456
 
959
1457
    @classmethod
960
 
    def find_format(klass, transport):
 
1458
    def find_format(klass, transport, _server_formats=True):
961
1459
        """Return the format present at transport."""
962
 
        for format in klass._control_formats:
 
1460
        if _server_formats:
 
1461
            formats = klass._control_server_formats + klass._control_formats
 
1462
        else:
 
1463
            formats = klass._control_formats
 
1464
        for format in formats:
963
1465
            try:
964
1466
                return format.probe_transport(transport)
965
1467
            except errors.NotBranchError:
969
1471
 
970
1472
    @classmethod
971
1473
    def probe_transport(klass, transport):
972
 
        """Return the .bzrdir style transport present at URL."""
 
1474
        """Return the .bzrdir style format present in a directory."""
973
1475
        try:
974
1476
            format_string = transport.get(".bzr/branch-format").read()
 
1477
        except errors.NoSuchFile:
 
1478
            raise errors.NotBranchError(path=transport.base)
 
1479
 
 
1480
        try:
975
1481
            return klass._formats[format_string]
976
 
        except errors.NoSuchFile:
977
 
            raise errors.NotBranchError(path=transport.base)
978
1482
        except KeyError:
979
 
            raise errors.UnknownFormatError(format_string)
 
1483
            raise errors.UnknownFormatError(format=format_string, kind='bzrdir')
980
1484
 
981
1485
    @classmethod
982
1486
    def get_default_format(klass):
1005
1509
        """
1006
1510
        raise NotImplementedError(self.get_converter)
1007
1511
 
1008
 
    def initialize(self, url):
 
1512
    def initialize(self, url, possible_transports=None):
1009
1513
        """Create a bzr control dir at this url and return an opened copy.
1010
1514
        
1011
1515
        Subclasses should typically override initialize_on_transport
1012
1516
        instead of this method.
1013
1517
        """
1014
 
        return self.initialize_on_transport(get_transport(url))
 
1518
        return self.initialize_on_transport(get_transport(url,
 
1519
                                                          possible_transports))
1015
1520
 
1016
1521
    def initialize_on_transport(self, transport):
1017
1522
        """Initialize a new bzrdir in the base directory of a Transport."""
1018
1523
        # Since we don't have a .bzr directory, inherit the
1019
1524
        # mode from the root directory
1020
 
        temp_control = LockableFiles(transport, '', TransportLock)
 
1525
        temp_control = lockable_files.LockableFiles(transport,
 
1526
                            '', lockable_files.TransportLock)
1021
1527
        temp_control._transport.mkdir('.bzr',
1022
1528
                                      # FIXME: RBC 20060121 don't peek under
1023
1529
                                      # the covers
1024
1530
                                      mode=temp_control._dir_mode)
 
1531
        if sys.platform == 'win32' and isinstance(transport, LocalTransport):
 
1532
            win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1025
1533
        file_mode = temp_control._file_mode
1026
1534
        del temp_control
1027
1535
        mutter('created control directory in ' + transport.base)
1028
1536
        control = transport.clone('.bzr')
1029
1537
        utf8_files = [('README', 
1030
 
                       "This is a Bazaar-NG control directory.\n"
1031
 
                       "Do not change any files in this directory.\n"),
 
1538
                       "This is a Bazaar control directory.\n"
 
1539
                       "Do not change any files in this directory.\n"
 
1540
                       "See http://bazaar-vcs.org/ for more information about Bazaar.\n"),
1032
1541
                      ('branch-format', self.get_format_string()),
1033
1542
                      ]
1034
1543
        # NB: no need to escape relative paths that are url safe.
1035
 
        control_files = LockableFiles(control, self._lock_file_name, 
1036
 
                                      self._lock_class)
 
1544
        control_files = lockable_files.LockableFiles(control,
 
1545
                            self._lock_file_name, self._lock_class)
1037
1546
        control_files.create_lock()
1038
1547
        control_files.lock_write()
1039
1548
        try:
1052
1561
        """
1053
1562
        return True
1054
1563
 
 
1564
    def same_model(self, target_format):
 
1565
        return (self.repository_format.rich_root_data == 
 
1566
            target_format.rich_root_data)
 
1567
 
1055
1568
    @classmethod
1056
1569
    def known_formats(klass):
1057
1570
        """Return all the known formats.
1077
1590
        _found is a private parameter, do not use it.
1078
1591
        """
1079
1592
        if not _found:
1080
 
            assert isinstance(BzrDirFormat.find_format(transport),
1081
 
                              self.__class__)
 
1593
            found_format = BzrDirFormat.find_format(transport)
 
1594
            if not isinstance(found_format, self.__class__):
 
1595
                raise AssertionError("%s was asked to open %s, but it seems to need "
 
1596
                        "format %s" 
 
1597
                        % (self, transport, found_format))
1082
1598
        return self._open(transport)
1083
1599
 
1084
1600
    def _open(self, transport):
1095
1611
 
1096
1612
    @classmethod
1097
1613
    def register_control_format(klass, format):
1098
 
        """Register a format that does not use '.bzrdir' for its control dir.
 
1614
        """Register a format that does not use '.bzr' for its control dir.
1099
1615
 
1100
1616
        TODO: This should be pulled up into a 'ControlDirFormat' base class
1101
1617
        which BzrDirFormat can inherit from, and renamed to register_format 
1105
1621
        klass._control_formats.append(format)
1106
1622
 
1107
1623
    @classmethod
 
1624
    def register_control_server_format(klass, format):
 
1625
        """Register a control format for client-server environments.
 
1626
 
 
1627
        These formats will be tried before ones registered with
 
1628
        register_control_format.  This gives implementations that decide to the
 
1629
        chance to grab it before anything looks at the contents of the format
 
1630
        file.
 
1631
        """
 
1632
        klass._control_server_formats.append(format)
 
1633
 
 
1634
    @classmethod
 
1635
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1108
1636
    def set_default_format(klass, format):
 
1637
        klass._set_default_format(format)
 
1638
 
 
1639
    @classmethod
 
1640
    def _set_default_format(klass, format):
 
1641
        """Set default format (for testing behavior of defaults only)"""
1109
1642
        klass._default_format = format
1110
1643
 
1111
1644
    def __str__(self):
1112
 
        return self.get_format_string()[:-1]
 
1645
        # Trim the newline
 
1646
        return self.get_format_string().rstrip()
1113
1647
 
1114
1648
    @classmethod
1115
1649
    def unregister_format(klass, format):
1121
1655
        klass._control_formats.remove(format)
1122
1656
 
1123
1657
 
1124
 
# register BzrDirFormat as a control format
1125
 
BzrDirFormat.register_control_format(BzrDirFormat)
1126
 
 
1127
 
 
1128
1658
class BzrDirFormat4(BzrDirFormat):
1129
1659
    """Bzr dir format 4.
1130
1660
 
1138
1668
    removed in format 5; write support for this format has been removed.
1139
1669
    """
1140
1670
 
1141
 
    _lock_class = TransportLock
 
1671
    _lock_class = lockable_files.TransportLock
1142
1672
 
1143
1673
    def get_format_string(self):
1144
1674
        """See BzrDirFormat.get_format_string()."""
1172
1702
 
1173
1703
    def __return_repository_format(self):
1174
1704
        """Circular import protection."""
1175
 
        from bzrlib.repository import RepositoryFormat4
1176
 
        return RepositoryFormat4(self)
 
1705
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
 
1706
        return RepositoryFormat4()
1177
1707
    repository_format = property(__return_repository_format)
1178
1708
 
1179
1709
 
1188
1718
       Unhashed stores in the repository.
1189
1719
    """
1190
1720
 
1191
 
    _lock_class = TransportLock
 
1721
    _lock_class = lockable_files.TransportLock
1192
1722
 
1193
1723
    def get_format_string(self):
1194
1724
        """See BzrDirFormat.get_format_string()."""
1212
1742
        Except when they are being cloned.
1213
1743
        """
1214
1744
        from bzrlib.branch import BzrBranchFormat4
1215
 
        from bzrlib.repository import RepositoryFormat5
 
1745
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
1216
1746
        from bzrlib.workingtree import WorkingTreeFormat2
1217
1747
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1218
1748
        RepositoryFormat5().initialize(result, _internal=True)
1219
1749
        if not _cloning:
1220
 
            BzrBranchFormat4().initialize(result)
1221
 
            WorkingTreeFormat2().initialize(result)
 
1750
            branch = BzrBranchFormat4().initialize(result)
 
1751
            try:
 
1752
                WorkingTreeFormat2().initialize(result)
 
1753
            except errors.NotLocalUrl:
 
1754
                # Even though we can't access the working tree, we need to
 
1755
                # create its control files.
 
1756
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1222
1757
        return result
1223
1758
 
1224
1759
    def _open(self, transport):
1227
1762
 
1228
1763
    def __return_repository_format(self):
1229
1764
        """Circular import protection."""
1230
 
        from bzrlib.repository import RepositoryFormat5
1231
 
        return RepositoryFormat5(self)
 
1765
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
 
1766
        return RepositoryFormat5()
1232
1767
    repository_format = property(__return_repository_format)
1233
1768
 
1234
1769
 
1242
1777
     - Format 6 repositories [always]
1243
1778
    """
1244
1779
 
1245
 
    _lock_class = TransportLock
 
1780
    _lock_class = lockable_files.TransportLock
1246
1781
 
1247
1782
    def get_format_string(self):
1248
1783
        """See BzrDirFormat.get_format_string()."""
1266
1801
        Except when they are being cloned.
1267
1802
        """
1268
1803
        from bzrlib.branch import BzrBranchFormat4
1269
 
        from bzrlib.repository import RepositoryFormat6
 
1804
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
1270
1805
        from bzrlib.workingtree import WorkingTreeFormat2
1271
1806
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1272
1807
        RepositoryFormat6().initialize(result, _internal=True)
1273
1808
        if not _cloning:
1274
 
            BzrBranchFormat4().initialize(result)
 
1809
            branch = BzrBranchFormat4().initialize(result)
1275
1810
            try:
1276
1811
                WorkingTreeFormat2().initialize(result)
1277
1812
            except errors.NotLocalUrl:
1278
 
                # emulate pre-check behaviour for working tree and silently 
1279
 
                # fail.
1280
 
                pass
 
1813
                # Even though we can't access the working tree, we need to
 
1814
                # create its control files.
 
1815
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1281
1816
        return result
1282
1817
 
1283
1818
    def _open(self, transport):
1286
1821
 
1287
1822
    def __return_repository_format(self):
1288
1823
        """Circular import protection."""
1289
 
        from bzrlib.repository import RepositoryFormat6
1290
 
        return RepositoryFormat6(self)
 
1824
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
 
1825
        return RepositoryFormat6()
1291
1826
    repository_format = property(__return_repository_format)
1292
1827
 
1293
1828
 
1302
1837
     - Format 7 repositories [optional]
1303
1838
    """
1304
1839
 
1305
 
    _lock_class = LockDir
 
1840
    _lock_class = lockdir.LockDir
 
1841
 
 
1842
    def __init__(self):
 
1843
        self._workingtree_format = None
 
1844
        self._branch_format = None
 
1845
 
 
1846
    def __eq__(self, other):
 
1847
        if other.__class__ is not self.__class__:
 
1848
            return False
 
1849
        if other.repository_format != self.repository_format:
 
1850
            return False
 
1851
        if other.workingtree_format != self.workingtree_format:
 
1852
            return False
 
1853
        return True
 
1854
 
 
1855
    def __ne__(self, other):
 
1856
        return not self == other
 
1857
 
 
1858
    def get_branch_format(self):
 
1859
        if self._branch_format is None:
 
1860
            from bzrlib.branch import BranchFormat
 
1861
            self._branch_format = BranchFormat.get_default_format()
 
1862
        return self._branch_format
 
1863
 
 
1864
    def set_branch_format(self, format):
 
1865
        self._branch_format = format
1306
1866
 
1307
1867
    def get_converter(self, format=None):
1308
1868
        """See BzrDirFormat.get_converter()."""
1333
1893
        return RepositoryFormat.get_default_format()
1334
1894
 
1335
1895
    def __set_repository_format(self, value):
1336
 
        """Allow changint the repository format for metadir formats."""
 
1896
        """Allow changing the repository format for metadir formats."""
1337
1897
        self._repository_format = value
1338
1898
 
1339
1899
    repository_format = property(__return_repository_format, __set_repository_format)
1340
1900
 
1341
 
 
 
1901
    def __get_workingtree_format(self):
 
1902
        if self._workingtree_format is None:
 
1903
            from bzrlib.workingtree import WorkingTreeFormat
 
1904
            self._workingtree_format = WorkingTreeFormat.get_default_format()
 
1905
        return self._workingtree_format
 
1906
 
 
1907
    def __set_workingtree_format(self, wt_format):
 
1908
        self._workingtree_format = wt_format
 
1909
 
 
1910
    workingtree_format = property(__get_workingtree_format,
 
1911
                                  __set_workingtree_format)
 
1912
 
 
1913
 
 
1914
# Register bzr control format
 
1915
BzrDirFormat.register_control_format(BzrDirFormat)
 
1916
 
 
1917
# Register bzr formats
1342
1918
BzrDirFormat.register_format(BzrDirFormat4())
1343
1919
BzrDirFormat.register_format(BzrDirFormat5())
1344
1920
BzrDirFormat.register_format(BzrDirFormat6())
1345
1921
__default_format = BzrDirMetaFormat1()
1346
1922
BzrDirFormat.register_format(__default_format)
1347
 
BzrDirFormat.set_default_format(__default_format)
1348
 
 
1349
 
 
1350
 
class BzrDirTestProviderAdapter(object):
1351
 
    """A tool to generate a suite testing multiple bzrdir formats at once.
1352
 
 
1353
 
    This is done by copying the test once for each transport and injecting
1354
 
    the transport_server, transport_readonly_server, and bzrdir_format
1355
 
    classes into each copy. Each copy is also given a new id() to make it
1356
 
    easy to identify.
1357
 
    """
1358
 
 
1359
 
    def __init__(self, transport_server, transport_readonly_server, formats):
1360
 
        self._transport_server = transport_server
1361
 
        self._transport_readonly_server = transport_readonly_server
1362
 
        self._formats = formats
1363
 
    
1364
 
    def adapt(self, test):
1365
 
        result = TestSuite()
1366
 
        for format in self._formats:
1367
 
            new_test = deepcopy(test)
1368
 
            new_test.transport_server = self._transport_server
1369
 
            new_test.transport_readonly_server = self._transport_readonly_server
1370
 
            new_test.bzrdir_format = format
1371
 
            def make_new_test_id():
1372
 
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1373
 
                return lambda: new_id
1374
 
            new_test.id = make_new_test_id()
1375
 
            result.addTest(new_test)
1376
 
        return result
1377
 
 
1378
 
 
1379
 
class ScratchDir(BzrDir6):
1380
 
    """Special test class: a bzrdir that cleans up itself..
1381
 
 
1382
 
    >>> d = ScratchDir()
1383
 
    >>> base = d.transport.base
1384
 
    >>> isdir(base)
1385
 
    True
1386
 
    >>> b.transport.__del__()
1387
 
    >>> isdir(base)
1388
 
    False
1389
 
    """
1390
 
 
1391
 
    def __init__(self, files=[], dirs=[], transport=None):
1392
 
        """Make a test branch.
1393
 
 
1394
 
        This creates a temporary directory and runs init-tree in it.
1395
 
 
1396
 
        If any files are listed, they are created in the working copy.
1397
 
        """
1398
 
        if transport is None:
1399
 
            transport = bzrlib.transport.local.ScratchTransport()
1400
 
            # local import for scope restriction
1401
 
            BzrDirFormat6().initialize(transport.base)
1402
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1403
 
            self.create_repository()
1404
 
            self.create_branch()
1405
 
            self.create_workingtree()
1406
 
        else:
1407
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1408
 
 
1409
 
        # BzrBranch creates a clone to .bzr and then forgets about the
1410
 
        # original transport. A ScratchTransport() deletes itself and
1411
 
        # everything underneath it when it goes away, so we need to
1412
 
        # grab a local copy to prevent that from happening
1413
 
        self._transport = transport
1414
 
 
1415
 
        for d in dirs:
1416
 
            self._transport.mkdir(d)
1417
 
            
1418
 
        for f in files:
1419
 
            self._transport.put(f, 'content of %s' % f)
1420
 
 
1421
 
    def clone(self):
1422
 
        """
1423
 
        >>> orig = ScratchDir(files=["file1", "file2"])
1424
 
        >>> os.listdir(orig.base)
1425
 
        [u'.bzr', u'file1', u'file2']
1426
 
        >>> clone = orig.clone()
1427
 
        >>> if os.name != 'nt':
1428
 
        ...   os.path.samefile(orig.base, clone.base)
1429
 
        ... else:
1430
 
        ...   orig.base == clone.base
1431
 
        ...
1432
 
        False
1433
 
        >>> os.listdir(clone.base)
1434
 
        [u'.bzr', u'file1', u'file2']
1435
 
        """
1436
 
        from shutil import copytree
1437
 
        from bzrlib.osutils import mkdtemp
1438
 
        base = mkdtemp()
1439
 
        os.rmdir(base)
1440
 
        copytree(self.base, base, symlinks=True)
1441
 
        return ScratchDir(
1442
 
            transport=bzrlib.transport.local.ScratchTransport(base))
 
1923
BzrDirFormat._default_format = __default_format
1443
1924
 
1444
1925
 
1445
1926
class Converter(object):
1532
2013
        self.bzrdir.transport.delete_tree('text-store')
1533
2014
 
1534
2015
    def _convert_working_inv(self):
1535
 
        inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1536
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
2016
        inv = xml4.serializer_v4.read_inventory(
 
2017
                    self.branch.control_files.get('inventory'))
 
2018
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1537
2019
        # FIXME inventory is a working tree change.
1538
 
        self.branch.control_files.put('inventory', new_inv_xml)
 
2020
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1539
2021
 
1540
2022
    def _write_all_weaves(self):
1541
2023
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1565
2047
                                                      prefixed=False,
1566
2048
                                                      compressed=True))
1567
2049
        try:
1568
 
            transaction = bzrlib.transactions.WriteTransaction()
 
2050
            transaction = WriteTransaction()
1569
2051
            for i, rev_id in enumerate(self.converted_revs):
1570
2052
                self.pb.update('write revision', i, len(self.converted_revs))
1571
2053
                _revision_store.add_revision(self.revisions[rev_id], transaction)
1597
2079
    def _load_old_inventory(self, rev_id):
1598
2080
        assert rev_id not in self.converted_revs
1599
2081
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1600
 
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
 
2082
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
 
2083
        inv.revision_id = rev_id
1601
2084
        rev = self.revisions[rev_id]
1602
2085
        if rev.inventory_sha1:
1603
2086
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1607
2090
    def _load_updated_inventory(self, rev_id):
1608
2091
        assert rev_id in self.converted_revs
1609
2092
        inv_xml = self.inv_weave.get_text(rev_id)
1610
 
        inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
2093
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml, rev_id)
1611
2094
        return inv
1612
2095
 
1613
2096
    def _convert_one_rev(self, rev_id):
1617
2100
        present_parents = [p for p in rev.parent_ids
1618
2101
                           if p not in self.absent_revisions]
1619
2102
        self._convert_revision_contents(rev, inv, present_parents)
1620
 
        self._store_new_weave(rev, inv, present_parents)
 
2103
        self._store_new_inv(rev, inv, present_parents)
1621
2104
        self.converted_revs.add(rev_id)
1622
2105
 
1623
 
    def _store_new_weave(self, rev, inv, present_parents):
 
2106
    def _store_new_inv(self, rev, inv, present_parents):
1624
2107
        # the XML is now updated with text versions
1625
2108
        if __debug__:
1626
 
            for file_id in inv:
1627
 
                ie = inv[file_id]
1628
 
                if ie.kind == 'root_directory':
1629
 
                    continue
1630
 
                assert hasattr(ie, 'revision'), \
 
2109
            entries = inv.iter_entries()
 
2110
            entries.next()
 
2111
            for path, ie in entries:
 
2112
                assert getattr(ie, 'revision', None) is not None, \
1631
2113
                    'no revision on {%s} in {%s}' % \
1632
2114
                    (file_id, rev.revision_id)
1633
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
2115
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1634
2116
        new_inv_sha1 = sha_string(new_inv_xml)
1635
 
        self.inv_weave.add_lines(rev.revision_id, 
 
2117
        self.inv_weave.add_lines(rev.revision_id,
1636
2118
                                 present_parents,
1637
2119
                                 new_inv_xml.splitlines(True))
1638
2120
        rev.inventory_sha1 = new_inv_sha1
1645
2127
        mutter('converting texts of revision {%s}',
1646
2128
               rev_id)
1647
2129
        parent_invs = map(self._load_updated_inventory, present_parents)
1648
 
        for file_id in inv:
1649
 
            ie = inv[file_id]
 
2130
        entries = inv.iter_entries()
 
2131
        entries.next()
 
2132
        for path, ie in entries:
1650
2133
            self._convert_file_version(rev, ie, parent_invs)
1651
2134
 
1652
2135
    def _convert_file_version(self, rev, ie, parent_invs):
1655
2138
        The file needs to be added into the weave if it is a merge
1656
2139
        of >=2 parents or if it's changed from its parent.
1657
2140
        """
1658
 
        if ie.kind == 'root_directory':
1659
 
            return
1660
2141
        file_id = ie.file_id
1661
2142
        rev_id = rev.revision_id
1662
2143
        w = self.text_weaves.get(file_id)
1664
2145
            w = Weave(file_id)
1665
2146
            self.text_weaves[file_id] = w
1666
2147
        text_changed = False
1667
 
        previous_entries = ie.find_previous_heads(parent_invs,
1668
 
                                                  None,
1669
 
                                                  None,
1670
 
                                                  entry_vf=w)
1671
 
        for old_revision in previous_entries:
1672
 
                # if this fails, its a ghost ?
1673
 
                assert old_revision in self.converted_revs 
 
2148
        parent_candiate_entries = ie.parent_candidates(parent_invs)
 
2149
        for old_revision in parent_candiate_entries.keys():
 
2150
            # if this fails, its a ghost ?
 
2151
            assert old_revision in self.converted_revs, \
 
2152
                "Revision {%s} not in converted_revs" % old_revision
 
2153
        heads = graph.Graph(self).heads(parent_candiate_entries.keys())
 
2154
        # XXX: Note that this is unordered - and this is tolerable because 
 
2155
        # the previous code was also unordered.
 
2156
        previous_entries = dict((head, parent_candiate_entries[head]) for head
 
2157
            in heads)
1674
2158
        self.snapshot_ie(previous_entries, ie, w, rev_id)
1675
2159
        del ie.text_id
1676
2160
        assert getattr(ie, 'revision', None) is not None
1677
2161
 
 
2162
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
 
2163
    def get_parents(self, revision_ids):
 
2164
        for revision_id in revision_ids:
 
2165
            yield self.revisions[revision_id].parent_ids
 
2166
 
 
2167
    def get_parent_map(self, revision_ids):
 
2168
        """See graph._StackedParentsProvider.get_parent_map"""
 
2169
        return dict((revision_id, self.revisions[revision_id])
 
2170
                    for revision_id in revision_ids
 
2171
                     if revision_id in self.revisions)
 
2172
 
1678
2173
    def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1679
2174
        # TODO: convert this logic, which is ~= snapshot to
1680
2175
        # a call to:. This needs the path figured out. rather than a work_tree
1689
2184
                ie.revision = previous_ie.revision
1690
2185
                return
1691
2186
        if ie.has_text():
1692
 
            text = self.branch.repository.text_store.get(ie.text_id)
 
2187
            text = self.branch.repository.weave_store.get(ie.text_id)
1693
2188
            file_lines = text.readlines()
1694
2189
            assert sha_strings(file_lines) == ie.text_sha1
1695
2190
            assert sum(map(len, file_lines)) == ie.text_size
1763
2258
 
1764
2259
    def convert(self, to_convert, pb):
1765
2260
        """See Converter.convert()."""
 
2261
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
 
2262
        from bzrlib.branch import BzrBranchFormat5
1766
2263
        self.bzrdir = to_convert
1767
2264
        self.pb = pb
1768
2265
        self.count = 0
1797
2294
        # we hard code the formats here because we are converting into
1798
2295
        # the meta format. The meta format upgrader can take this to a 
1799
2296
        # future format within each component.
1800
 
        self.put_format('repository', bzrlib.repository.RepositoryFormat7())
 
2297
        self.put_format('repository', RepositoryFormat7())
1801
2298
        for entry in repository_names:
1802
2299
            self.move_entry('repository', entry)
1803
2300
 
1804
2301
        self.step('Upgrading branch      ')
1805
2302
        self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1806
2303
        self.make_lock('branch')
1807
 
        self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
 
2304
        self.put_format('branch', BzrBranchFormat5())
1808
2305
        branch_files = [('revision-history', True),
1809
2306
                        ('branch-name', True),
1810
2307
                        ('parent', False)]
1811
2308
        for entry in branch_files:
1812
2309
            self.move_entry('branch', entry)
1813
2310
 
1814
 
        self.step('Upgrading working tree')
1815
 
        self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1816
 
        self.make_lock('checkout')
1817
 
        self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1818
 
        self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1819
2311
        checkout_files = [('pending-merges', True),
1820
2312
                          ('inventory', True),
1821
2313
                          ('stat-cache', False)]
1822
 
        for entry in checkout_files:
1823
 
            self.move_entry('checkout', entry)
1824
 
        if last_revision is not None:
1825
 
            self.bzrdir._control_files.put_utf8('checkout/last-revision',
1826
 
                                                last_revision)
1827
 
        self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
 
2314
        # If a mandatory checkout file is not present, the branch does not have
 
2315
        # a functional checkout. Do not create a checkout in the converted
 
2316
        # branch.
 
2317
        for name, mandatory in checkout_files:
 
2318
            if mandatory and name not in bzrcontents:
 
2319
                has_checkout = False
 
2320
                break
 
2321
        else:
 
2322
            has_checkout = True
 
2323
        if not has_checkout:
 
2324
            self.pb.note('No working tree.')
 
2325
            # If some checkout files are there, we may as well get rid of them.
 
2326
            for name, mandatory in checkout_files:
 
2327
                if name in bzrcontents:
 
2328
                    self.bzrdir.transport.delete(name)
 
2329
        else:
 
2330
            from bzrlib.workingtree import WorkingTreeFormat3
 
2331
            self.step('Upgrading working tree')
 
2332
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
 
2333
            self.make_lock('checkout')
 
2334
            self.put_format(
 
2335
                'checkout', WorkingTreeFormat3())
 
2336
            self.bzrdir.transport.delete_multi(
 
2337
                self.garbage_inventories, self.pb)
 
2338
            for entry in checkout_files:
 
2339
                self.move_entry('checkout', entry)
 
2340
            if last_revision is not None:
 
2341
                self.bzrdir._control_files.put_utf8(
 
2342
                    'checkout/last-revision', last_revision)
 
2343
        self.bzrdir._control_files.put_utf8(
 
2344
            'branch-format', BzrDirMetaFormat1().get_format_string())
1828
2345
        return BzrDir.open(self.bzrdir.root_transport.base)
1829
2346
 
1830
2347
    def make_lock(self, name):
1831
2348
        """Make a lock for the new control dir name."""
1832
2349
        self.step('Make %s lock' % name)
1833
 
        ld = LockDir(self.bzrdir.transport, 
1834
 
                     '%s/lock' % name,
1835
 
                     file_modebits=self.file_mode,
1836
 
                     dir_modebits=self.dir_mode)
 
2350
        ld = lockdir.LockDir(self.bzrdir.transport,
 
2351
                             '%s/lock' % name,
 
2352
                             file_modebits=self.file_mode,
 
2353
                             dir_modebits=self.dir_mode)
1837
2354
        ld.create()
1838
2355
 
1839
2356
    def move_entry(self, new_dir, entry):
1878
2395
                self.pb.note('starting repository conversion')
1879
2396
                converter = CopyConverter(self.target_format.repository_format)
1880
2397
                converter.convert(repo, pb)
 
2398
        try:
 
2399
            branch = self.bzrdir.open_branch()
 
2400
        except errors.NotBranchError:
 
2401
            pass
 
2402
        else:
 
2403
            # TODO: conversions of Branch and Tree should be done by
 
2404
            # InterXFormat lookups
 
2405
            # Avoid circular imports
 
2406
            from bzrlib import branch as _mod_branch
 
2407
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
 
2408
                self.target_format.get_branch_format().__class__ is
 
2409
                _mod_branch.BzrBranchFormat6):
 
2410
                branch_converter = _mod_branch.Converter5to6()
 
2411
                branch_converter.convert(branch)
 
2412
        try:
 
2413
            tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
 
2414
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2415
            pass
 
2416
        else:
 
2417
            # TODO: conversions of Branch and Tree should be done by
 
2418
            # InterXFormat lookups
 
2419
            if (isinstance(tree, workingtree.WorkingTree3) and
 
2420
                not isinstance(tree, workingtree_4.WorkingTree4) and
 
2421
                isinstance(self.target_format.workingtree_format,
 
2422
                    workingtree_4.WorkingTreeFormat4)):
 
2423
                workingtree_4.Converter3to4().convert(tree)
1881
2424
        return to_convert
 
2425
 
 
2426
 
 
2427
# This is not in remote.py because it's small, and needs to be registered.
 
2428
# Putting it in remote.py creates a circular import problem.
 
2429
# we can make it a lazy object if the control formats is turned into something
 
2430
# like a registry.
 
2431
class RemoteBzrDirFormat(BzrDirMetaFormat1):
 
2432
    """Format representing bzrdirs accessed via a smart server"""
 
2433
 
 
2434
    def get_format_description(self):
 
2435
        return 'bzr remote bzrdir'
 
2436
    
 
2437
    @classmethod
 
2438
    def probe_transport(klass, transport):
 
2439
        """Return a RemoteBzrDirFormat object if it looks possible."""
 
2440
        try:
 
2441
            medium = transport.get_smart_medium()
 
2442
        except (NotImplementedError, AttributeError,
 
2443
                errors.TransportNotPossible, errors.NoSmartMedium):
 
2444
            # no smart server, so not a branch for this format type.
 
2445
            raise errors.NotBranchError(path=transport.base)
 
2446
        else:
 
2447
            # Decline to open it if the server doesn't support our required
 
2448
            # version (2) so that the VFS-based transport will do it.
 
2449
            try:
 
2450
                server_version = medium.protocol_version()
 
2451
            except errors.SmartProtocolError:
 
2452
                # Apparently there's no usable smart server there, even though
 
2453
                # the medium supports the smart protocol.
 
2454
                raise errors.NotBranchError(path=transport.base)
 
2455
            if server_version != 2:
 
2456
                raise errors.NotBranchError(path=transport.base)
 
2457
            return klass()
 
2458
 
 
2459
    def initialize_on_transport(self, transport):
 
2460
        try:
 
2461
            # hand off the request to the smart server
 
2462
            client_medium = transport.get_smart_medium()
 
2463
        except errors.NoSmartMedium:
 
2464
            # TODO: lookup the local format from a server hint.
 
2465
            local_dir_format = BzrDirMetaFormat1()
 
2466
            return local_dir_format.initialize_on_transport(transport)
 
2467
        client = _SmartClient(client_medium, transport.base)
 
2468
        path = client.remote_path_from_transport(transport)
 
2469
        response = client.call('BzrDirFormat.initialize', path)
 
2470
        assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
 
2471
        return remote.RemoteBzrDir(transport)
 
2472
 
 
2473
    def _open(self, transport):
 
2474
        return remote.RemoteBzrDir(transport)
 
2475
 
 
2476
    def __eq__(self, other):
 
2477
        if not isinstance(other, RemoteBzrDirFormat):
 
2478
            return False
 
2479
        return self.get_format_description() == other.get_format_description()
 
2480
 
 
2481
 
 
2482
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
 
2483
 
 
2484
 
 
2485
class BzrDirFormatInfo(object):
 
2486
 
 
2487
    def __init__(self, native, deprecated, hidden, experimental):
 
2488
        self.deprecated = deprecated
 
2489
        self.native = native
 
2490
        self.hidden = hidden
 
2491
        self.experimental = experimental
 
2492
 
 
2493
 
 
2494
class BzrDirFormatRegistry(registry.Registry):
 
2495
    """Registry of user-selectable BzrDir subformats.
 
2496
    
 
2497
    Differs from BzrDirFormat._control_formats in that it provides sub-formats,
 
2498
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
2499
    """
 
2500
 
 
2501
    def __init__(self):
 
2502
        """Create a BzrDirFormatRegistry."""
 
2503
        self._aliases = set()
 
2504
        super(BzrDirFormatRegistry, self).__init__()
 
2505
 
 
2506
    def aliases(self):
 
2507
        """Return a set of the format names which are aliases."""
 
2508
        return frozenset(self._aliases)
 
2509
 
 
2510
    def register_metadir(self, key,
 
2511
             repository_format, help, native=True, deprecated=False,
 
2512
             branch_format=None,
 
2513
             tree_format=None,
 
2514
             hidden=False,
 
2515
             experimental=False,
 
2516
             alias=False):
 
2517
        """Register a metadir subformat.
 
2518
 
 
2519
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
 
2520
        by the Repository format.
 
2521
 
 
2522
        :param repository_format: The fully-qualified repository format class
 
2523
            name as a string.
 
2524
        :param branch_format: Fully-qualified branch format class name as
 
2525
            a string.
 
2526
        :param tree_format: Fully-qualified tree format class name as
 
2527
            a string.
 
2528
        """
 
2529
        # This should be expanded to support setting WorkingTree and Branch
 
2530
        # formats, once BzrDirMetaFormat1 supports that.
 
2531
        def _load(full_name):
 
2532
            mod_name, factory_name = full_name.rsplit('.', 1)
 
2533
            try:
 
2534
                mod = __import__(mod_name, globals(), locals(),
 
2535
                        [factory_name])
 
2536
            except ImportError, e:
 
2537
                raise ImportError('failed to load %s: %s' % (full_name, e))
 
2538
            try:
 
2539
                factory = getattr(mod, factory_name)
 
2540
            except AttributeError:
 
2541
                raise AttributeError('no factory %s in module %r'
 
2542
                    % (full_name, mod))
 
2543
            return factory()
 
2544
 
 
2545
        def helper():
 
2546
            bd = BzrDirMetaFormat1()
 
2547
            if branch_format is not None:
 
2548
                bd.set_branch_format(_load(branch_format))
 
2549
            if tree_format is not None:
 
2550
                bd.workingtree_format = _load(tree_format)
 
2551
            if repository_format is not None:
 
2552
                bd.repository_format = _load(repository_format)
 
2553
            return bd
 
2554
        self.register(key, helper, help, native, deprecated, hidden,
 
2555
            experimental, alias)
 
2556
 
 
2557
    def register(self, key, factory, help, native=True, deprecated=False,
 
2558
                 hidden=False, experimental=False, alias=False):
 
2559
        """Register a BzrDirFormat factory.
 
2560
        
 
2561
        The factory must be a callable that takes one parameter: the key.
 
2562
        It must produce an instance of the BzrDirFormat when called.
 
2563
 
 
2564
        This function mainly exists to prevent the info object from being
 
2565
        supplied directly.
 
2566
        """
 
2567
        registry.Registry.register(self, key, factory, help,
 
2568
            BzrDirFormatInfo(native, deprecated, hidden, experimental))
 
2569
        if alias:
 
2570
            self._aliases.add(key)
 
2571
 
 
2572
    def register_lazy(self, key, module_name, member_name, help, native=True,
 
2573
        deprecated=False, hidden=False, experimental=False, alias=False):
 
2574
        registry.Registry.register_lazy(self, key, module_name, member_name,
 
2575
            help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
 
2576
        if alias:
 
2577
            self._aliases.add(key)
 
2578
 
 
2579
    def set_default(self, key):
 
2580
        """Set the 'default' key to be a clone of the supplied key.
 
2581
        
 
2582
        This method must be called once and only once.
 
2583
        """
 
2584
        registry.Registry.register(self, 'default', self.get(key),
 
2585
            self.get_help(key), info=self.get_info(key))
 
2586
        self._aliases.add('default')
 
2587
 
 
2588
    def set_default_repository(self, key):
 
2589
        """Set the FormatRegistry default and Repository default.
 
2590
        
 
2591
        This is a transitional method while Repository.set_default_format
 
2592
        is deprecated.
 
2593
        """
 
2594
        if 'default' in self:
 
2595
            self.remove('default')
 
2596
        self.set_default(key)
 
2597
        format = self.get('default')()
 
2598
        assert isinstance(format, BzrDirMetaFormat1)
 
2599
 
 
2600
    def make_bzrdir(self, key):
 
2601
        return self.get(key)()
 
2602
 
 
2603
    def help_topic(self, topic):
 
2604
        output = textwrap.dedent("""\
 
2605
            These formats can be used for creating branches, working trees, and
 
2606
            repositories.
 
2607
 
 
2608
            """)
 
2609
        default_realkey = None
 
2610
        default_help = self.get_help('default')
 
2611
        help_pairs = []
 
2612
        for key in self.keys():
 
2613
            if key == 'default':
 
2614
                continue
 
2615
            help = self.get_help(key)
 
2616
            if help == default_help:
 
2617
                default_realkey = key
 
2618
            else:
 
2619
                help_pairs.append((key, help))
 
2620
 
 
2621
        def wrapped(key, help, info):
 
2622
            if info.native:
 
2623
                help = '(native) ' + help
 
2624
            return ':%s:\n%s\n\n' % (key, 
 
2625
                    textwrap.fill(help, initial_indent='    ', 
 
2626
                    subsequent_indent='    '))
 
2627
        if default_realkey is not None:
 
2628
            output += wrapped(default_realkey, '(default) %s' % default_help,
 
2629
                              self.get_info('default'))
 
2630
        deprecated_pairs = []
 
2631
        experimental_pairs = []
 
2632
        for key, help in help_pairs:
 
2633
            info = self.get_info(key)
 
2634
            if info.hidden:
 
2635
                continue
 
2636
            elif info.deprecated:
 
2637
                deprecated_pairs.append((key, help))
 
2638
            elif info.experimental:
 
2639
                experimental_pairs.append((key, help))
 
2640
            else:
 
2641
                output += wrapped(key, help, info)
 
2642
        if len(experimental_pairs) > 0:
 
2643
            output += "Experimental formats are shown below.\n\n"
 
2644
            for key, help in experimental_pairs:
 
2645
                info = self.get_info(key)
 
2646
                output += wrapped(key, help, info)
 
2647
        if len(deprecated_pairs) > 0:
 
2648
            output += "Deprecated formats are shown below.\n\n"
 
2649
            for key, help in deprecated_pairs:
 
2650
                info = self.get_info(key)
 
2651
                output += wrapped(key, help, info)
 
2652
 
 
2653
        return output
 
2654
 
 
2655
 
 
2656
class RepositoryAcquisitionPolicy(object):
 
2657
    """Abstract base class for repository acquisition policies.
 
2658
 
 
2659
    A repository acquisition policy decides how a BzrDir acquires a repository
 
2660
    for a branch that is being created.  The most basic policy decision is
 
2661
    whether to create a new repository or use an existing one.
 
2662
    """
 
2663
 
 
2664
    def configure_branch(self, branch):
 
2665
        """Apply any configuration data from this policy to the branch.
 
2666
 
 
2667
        Default implementation does nothing.
 
2668
        """
 
2669
        pass
 
2670
 
 
2671
    def acquire_repository(self, make_working_trees=None, shared=False):
 
2672
        """Acquire a repository for this bzrdir.
 
2673
 
 
2674
        Implementations may create a new repository or use a pre-exising
 
2675
        repository.
 
2676
        :param make_working_trees: If creating a repository, set
 
2677
            make_working_trees to this value (if non-None)
 
2678
        :param shared: If creating a repository, make it shared if True
 
2679
        :return: A repository
 
2680
        """
 
2681
        raise NotImplemented(RepositoryAcquisitionPolicy.acquire_repository)
 
2682
 
 
2683
 
 
2684
class CreateRepository(RepositoryAcquisitionPolicy):
 
2685
    """A policy of creating a new repository"""
 
2686
 
 
2687
    def __init__(self, bzrdir):
 
2688
        RepositoryAcquisitionPolicy.__init__(self)
 
2689
        self._bzrdir = bzrdir
 
2690
 
 
2691
    def acquire_repository(self, make_working_trees=None, shared=False):
 
2692
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
 
2693
 
 
2694
        Creates the desired repository in the bzrdir we already have.
 
2695
        """
 
2696
        repository = self._bzrdir.create_repository(shared=shared)
 
2697
        if make_working_trees is not None:
 
2698
            repository.set_make_working_trees(make_working_trees)
 
2699
        return repository
 
2700
 
 
2701
 
 
2702
class UseExistingRepository(RepositoryAcquisitionPolicy):
 
2703
    """A policy of reusing an existing repository"""
 
2704
 
 
2705
    def __init__(self, repository):
 
2706
        RepositoryAcquisitionPolicy.__init__(self)
 
2707
        self._repository = repository
 
2708
 
 
2709
    def acquire_repository(self, make_working_trees=None, shared=False):
 
2710
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
 
2711
 
 
2712
        Returns an existing repository to use
 
2713
        """
 
2714
        return self._repository
 
2715
 
 
2716
 
 
2717
format_registry = BzrDirFormatRegistry()
 
2718
format_registry.register('weave', BzrDirFormat6,
 
2719
    'Pre-0.8 format.  Slower than knit and does not'
 
2720
    ' support checkouts or shared repositories.',
 
2721
    deprecated=True)
 
2722
format_registry.register_metadir('knit',
 
2723
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2724
    'Format using knits.  Recommended for interoperation with bzr <= 0.14.',
 
2725
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2726
    tree_format='bzrlib.workingtree.WorkingTreeFormat3')
 
2727
format_registry.register_metadir('metaweave',
 
2728
    'bzrlib.repofmt.weaverepo.RepositoryFormat7',
 
2729
    'Transitional format in 0.8.  Slower than knit.',
 
2730
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2731
    tree_format='bzrlib.workingtree.WorkingTreeFormat3',
 
2732
    deprecated=True)
 
2733
format_registry.register_metadir('dirstate',
 
2734
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2735
    help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
 
2736
        'above when accessed over the network.',
 
2737
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2738
    # this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
 
2739
    # directly from workingtree_4 triggers a circular import.
 
2740
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2741
    )
 
2742
format_registry.register_metadir('dirstate-tags',
 
2743
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2744
    help='New in 0.15: Fast local operations and improved scaling for '
 
2745
        'network operations. Additionally adds support for tags.'
 
2746
        ' Incompatible with bzr < 0.15.',
 
2747
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2748
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2749
    )
 
2750
format_registry.register_metadir('rich-root',
 
2751
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit4',
 
2752
    help='New in 1.0.  Better handling of tree roots.  Incompatible with'
 
2753
        ' bzr < 1.0',
 
2754
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2755
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2756
    )
 
2757
format_registry.register_metadir('dirstate-with-subtree',
 
2758
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
 
2759
    help='New in 0.15: Fast local operations and improved scaling for '
 
2760
        'network operations. Additionally adds support for versioning nested '
 
2761
        'bzr branches. Incompatible with bzr < 0.15.',
 
2762
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2763
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2764
    experimental=True,
 
2765
    hidden=True,
 
2766
    )
 
2767
format_registry.register_metadir('pack-0.92',
 
2768
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
 
2769
    help='New in 0.92: Pack-based format with data compatible with '
 
2770
        'dirstate-tags format repositories. Interoperates with '
 
2771
        'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
 
2772
        'Previously called knitpack-experimental.  '
 
2773
        'For more information, see '
 
2774
        'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
 
2775
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2776
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2777
    )
 
2778
format_registry.register_metadir('pack-0.92-subtree',
 
2779
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
 
2780
    help='New in 0.92: Pack-based format with data compatible with '
 
2781
        'dirstate-with-subtree format repositories. Interoperates with '
 
2782
        'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
 
2783
        'Previously called knitpack-experimental.  '
 
2784
        'For more information, see '
 
2785
        'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
 
2786
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2787
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2788
    hidden=True,
 
2789
    experimental=True,
 
2790
    )
 
2791
format_registry.register_metadir('rich-root-pack',
 
2792
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
 
2793
    help='New in 1.0: Pack-based format with data compatible with '
 
2794
        'rich-root format repositories. Incompatible with'
 
2795
        ' bzr < 1.0',
 
2796
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2797
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2798
    )
 
2799
# The following two formats should always just be aliases.
 
2800
format_registry.register_metadir('development',
 
2801
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
 
2802
    help='Current development format. Can convert data to and from pack-0.92 '
 
2803
        '(and anything compatible with pack-0.92) format repositories. '
 
2804
        'Repositories in this format can only be read by bzr.dev. '
 
2805
        'Please read '
 
2806
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
2807
        'before use.',
 
2808
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2809
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2810
    experimental=True,
 
2811
    alias=True,
 
2812
    )
 
2813
format_registry.register_metadir('development-subtree',
 
2814
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
 
2815
    help='Current development format, subtree variant. Can convert data to and '
 
2816
        'from pack-0.92 (and anything compatible with pack-0.92) format '
 
2817
        'repositories. Repositories in this format can only be read by '
 
2818
        'bzr.dev. Please read '
 
2819
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
2820
        'before use.',
 
2821
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2822
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2823
    experimental=True,
 
2824
    alias=True,
 
2825
    )
 
2826
# And the development formats which the will have aliased one of follow:
 
2827
format_registry.register_metadir('development0',
 
2828
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
 
2829
    help='Trivial rename of pack-0.92 to provide a development format. '
 
2830
        'Please read '
 
2831
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
2832
        'before use.',
 
2833
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2834
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2835
    hidden=True,
 
2836
    experimental=True,
 
2837
    )
 
2838
format_registry.register_metadir('development0-subtree',
 
2839
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
 
2840
    help='Trivial rename of pack-0.92-subtree to provide a development format. '
 
2841
        'Please read '
 
2842
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
2843
        'before use.',
 
2844
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2845
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2846
    hidden=True,
 
2847
    experimental=True,
 
2848
    )
 
2849
format_registry.set_default('pack-0.92')