~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Andrew Bennetts
  • Date: 2007-03-26 06:24:01 UTC
  • mto: This revision was merged to the branch mainline in revision 2376.
  • Revision ID: andrew.bennetts@canonical.com-20070326062401-k3nbefzje5332jaf
Deal with review comments from Robert:

  * Add my name to the NEWS file
  * Move the test case to a new module in branch_implementations
  * Remove revision_history cruft from identitymap and test_identitymap
  * Improve some docstrings

Also, this fixes a bug where revision_history was not returning a copy of the
cached data, allowing the cache to be corrupted.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
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
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.
26
21
"""
27
22
 
28
 
# TODO: Move old formats into a plugin to make this file smaller.
 
23
# TODO: remove unittest dependency; put that stuff inside the test suite
 
24
 
 
25
# TODO: The Format probe_transport seems a bit redundant with just trying to
 
26
# open the bzrdir. -- mbp
 
27
#
 
28
# TODO: Can we move specific formats into separate modules to make this file
 
29
# smaller?
29
30
 
30
31
from cStringIO import StringIO
31
32
import os
32
 
import sys
 
33
import textwrap
33
34
 
34
35
from bzrlib.lazy_import import lazy_import
35
36
lazy_import(globals(), """
 
37
from copy import deepcopy
36
38
from stat import S_ISDIR
37
 
import textwrap
38
 
from warnings import warn
 
39
import unittest
39
40
 
40
41
import bzrlib
41
42
from bzrlib import (
42
 
    config,
43
43
    errors,
44
 
    graph,
45
44
    lockable_files,
46
45
    lockdir,
47
 
    osutils,
48
46
    registry,
49
 
    remote,
50
47
    revision as _mod_revision,
51
48
    symbol_versioning,
52
 
    ui,
53
49
    urlutils,
54
 
    versionedfile,
55
 
    win32utils,
 
50
    xml4,
 
51
    xml5,
56
52
    workingtree,
57
53
    workingtree_4,
58
 
    xml4,
59
 
    xml5,
60
54
    )
61
55
from bzrlib.osutils import (
 
56
    safe_unicode,
62
57
    sha_strings,
63
58
    sha_string,
64
59
    )
65
 
from bzrlib.repository import Repository
66
 
from bzrlib.smart.client import _SmartClient
67
 
from bzrlib.smart import protocol
 
60
from bzrlib.store.revision.text import TextRevisionStore
 
61
from bzrlib.store.text import TextStore
68
62
from bzrlib.store.versioned import WeaveStore
69
63
from bzrlib.transactions import WriteTransaction
70
 
from bzrlib.transport import (
71
 
    do_catching_redirections,
72
 
    get_transport,
73
 
    )
 
64
from bzrlib.transport import get_transport
74
65
from bzrlib.weave import Weave
75
66
""")
76
67
 
77
 
from bzrlib.trace import (
78
 
    mutter,
79
 
    note,
80
 
    )
 
68
from bzrlib.trace import mutter, note
81
69
from bzrlib.transport.local import LocalTransport
82
 
from bzrlib.symbol_versioning import (
83
 
    deprecated_function,
84
 
    deprecated_method,
85
 
    )
86
70
 
87
71
 
88
72
class BzrDir(object):
91
75
    BzrDir instances let you create or open any of the things that can be
92
76
    found within .bzr - checkouts, branches and repositories.
93
77
    
94
 
    :ivar transport:
 
78
    transport
95
79
        the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
96
 
    :ivar root_transport:
97
 
        a transport connected to the directory this bzr was opened from
98
 
        (i.e. the parent directory holding the .bzr directory).
99
 
 
100
 
    Everything in the bzrdir should have the same file permissions.
 
80
    root_transport
 
81
        a transport connected to the directory this bzr was opened from.
101
82
    """
102
83
 
103
84
    def break_lock(self):
106
87
        If there is a tree, the tree is opened and break_lock() called.
107
88
        Otherwise, branch is tried, and finally repository.
108
89
        """
109
 
        # XXX: This seems more like a UI function than something that really
110
 
        # belongs in this class.
111
90
        try:
112
91
            thing_to_unlock = self.open_workingtree()
113
92
        except (errors.NotLocalUrl, errors.NoWorkingTree):
130
109
        source_repo_format.check_conversion_target(target_repo_format)
131
110
 
132
111
    @staticmethod
133
 
    def _check_supported(format, allow_unsupported,
134
 
        recommend_upgrade=True,
135
 
        basedir=None):
136
 
        """Give an error or warning on old formats.
137
 
 
138
 
        :param format: may be any kind of format - workingtree, branch, 
139
 
        or repository.
140
 
 
141
 
        :param allow_unsupported: If true, allow opening 
142
 
        formats that are strongly deprecated, and which may 
143
 
        have limited functionality.
144
 
 
145
 
        :param recommend_upgrade: If true (default), warn
146
 
        the user through the ui object that they may wish
147
 
        to upgrade the object.
 
112
    def _check_supported(format, allow_unsupported):
 
113
        """Check whether format is a supported format.
 
114
 
 
115
        If allow_unsupported is True, this is a no-op.
148
116
        """
149
 
        # TODO: perhaps move this into a base Format class; it's not BzrDir
150
 
        # specific. mbp 20070323
151
117
        if not allow_unsupported and not format.is_supported():
152
118
            # see open_downlevel to open legacy branches.
153
119
            raise errors.UnsupportedFormatError(format=format)
154
 
        if recommend_upgrade \
155
 
            and getattr(format, 'upgrade_recommended', False):
156
 
            ui.ui_factory.recommend_upgrade(
157
 
                format.get_format_description(),
158
 
                basedir)
159
120
 
160
 
    def clone(self, url, revision_id=None, force_new_repo=False,
161
 
              preserve_stacking=False):
 
121
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
162
122
        """Clone this bzrdir and its contents to url verbatim.
163
123
 
164
 
        :param url: The url create the clone at.  If url's last component does
165
 
            not exist, it will be created.
166
 
        :param revision_id: The tip revision-id to use for any branch or
167
 
            working tree.  If not None, then the clone operation may tune
168
 
            itself to download less data.
169
 
        :param force_new_repo: Do not use a shared repository for the target
170
 
                               even if one is available.
171
 
        :param preserve_stacking: When cloning a stacked branch, stack the
172
 
            new branch on top of the other branch's stacked-on branch.
173
 
        """
174
 
        return self.clone_on_transport(get_transport(url),
175
 
                                       revision_id=revision_id,
176
 
                                       force_new_repo=force_new_repo,
177
 
                                       preserve_stacking=preserve_stacking)
178
 
 
179
 
    def clone_on_transport(self, transport, revision_id=None,
180
 
                           force_new_repo=False, preserve_stacking=False):
181
 
        """Clone this bzrdir and its contents to transport verbatim.
182
 
 
183
 
        :param transport: The transport for the location to produce the clone
184
 
            at.  If the target directory does not exist, it will be created.
185
 
        :param revision_id: The tip revision-id to use for any branch or
186
 
            working tree.  If not None, then the clone operation may tune
187
 
            itself to download less data.
188
 
        :param force_new_repo: Do not use a shared repository for the target,
189
 
                               even if one is available.
190
 
        :param preserve_stacking: When cloning a stacked branch, stack the
191
 
            new branch on top of the other branch's stacked-on branch.
192
 
        """
193
 
        transport.ensure_base()
194
 
        result = self.cloning_metadir().initialize_on_transport(transport)
195
 
        repository_policy = None
196
 
        stack_on = None
 
124
        If urls last component does not exist, it will be created.
 
125
 
 
126
        if revision_id is not None, then the clone operation may tune
 
127
            itself to download less data.
 
128
        :param force_new_repo: Do not use a shared repository for the target 
 
129
                               even if one is available.
 
130
        """
 
131
        self._make_tail(url)
 
132
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
133
        result = self._format.initialize(url)
197
134
        try:
198
135
            local_repo = self.find_repository()
199
136
        except errors.NoRepositoryPresent:
200
137
            local_repo = None
201
 
        try:
202
 
            local_branch = self.open_branch()
203
 
        except errors.NotBranchError:
204
 
            local_branch = None
205
 
        else:
206
 
            # enable fallbacks when branch is not a branch reference
207
 
            if local_branch.repository.has_same_location(local_repo):
208
 
                local_repo = local_branch.repository
209
 
            if preserve_stacking:
210
 
                try:
211
 
                    stack_on = local_branch.get_stacked_on_url()
212
 
                except (errors.UnstackableBranchFormat,
213
 
                        errors.UnstackableRepositoryFormat,
214
 
                        errors.NotStacked):
215
 
                    pass
216
 
 
217
138
        if local_repo:
218
139
            # may need to copy content in
219
 
            repository_policy = result.determine_repository_policy(
220
 
                force_new_repo, stack_on, self.root_transport.base)
221
 
            make_working_trees = local_repo.make_working_trees()
222
 
            result_repo = repository_policy.acquire_repository(
223
 
                make_working_trees, local_repo.is_shared())
224
 
            result_repo.fetch(local_repo, revision_id=revision_id)
225
 
        else:
226
 
            result_repo = None
 
140
            if force_new_repo:
 
141
                result_repo = local_repo.clone(
 
142
                    result,
 
143
                    revision_id=revision_id,
 
144
                    basis=basis_repo)
 
145
                result_repo.set_make_working_trees(local_repo.make_working_trees())
 
146
            else:
 
147
                try:
 
148
                    result_repo = result.find_repository()
 
149
                    # fetch content this dir needs.
 
150
                    if basis_repo:
 
151
                        # XXX FIXME RBC 20060214 need tests for this when the basis
 
152
                        # is incomplete
 
153
                        result_repo.fetch(basis_repo, revision_id=revision_id)
 
154
                    result_repo.fetch(local_repo, revision_id=revision_id)
 
155
                except errors.NoRepositoryPresent:
 
156
                    # needed to make one anyway.
 
157
                    result_repo = local_repo.clone(
 
158
                        result,
 
159
                        revision_id=revision_id,
 
160
                        basis=basis_repo)
 
161
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
227
162
        # 1 if there is a branch present
228
163
        #   make sure its content is available in the target repository
229
164
        #   clone it.
230
 
        if local_branch is not None:
231
 
            result_branch = local_branch.clone(result, revision_id=revision_id)
232
 
            if repository_policy is not None:
233
 
                repository_policy.configure_branch(result_branch)
234
 
        if result_repo is None or result_repo.make_working_trees():
 
165
        try:
 
166
            self.open_branch().clone(result, revision_id=revision_id)
 
167
        except errors.NotBranchError:
 
168
            pass
 
169
        try:
 
170
            self.open_workingtree().clone(result, basis=basis_tree)
 
171
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
172
            pass
 
173
        return result
 
174
 
 
175
    def _get_basis_components(self, basis):
 
176
        """Retrieve the basis components that are available at basis."""
 
177
        if basis is None:
 
178
            return None, None, None
 
179
        try:
 
180
            basis_tree = basis.open_workingtree()
 
181
            basis_branch = basis_tree.branch
 
182
            basis_repo = basis_branch.repository
 
183
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
184
            basis_tree = None
235
185
            try:
236
 
                self.open_workingtree().clone(result)
237
 
            except (errors.NoWorkingTree, errors.NotLocalUrl):
238
 
                pass
239
 
        return result
 
186
                basis_branch = basis.open_branch()
 
187
                basis_repo = basis_branch.repository
 
188
            except errors.NotBranchError:
 
189
                basis_branch = None
 
190
                try:
 
191
                    basis_repo = basis.open_repository()
 
192
                except errors.NoRepositoryPresent:
 
193
                    basis_repo = None
 
194
        return basis_repo, basis_branch, basis_tree
240
195
 
241
196
    # TODO: This should be given a Transport, and should chdir up; otherwise
242
197
    # this will open a new connection.
243
198
    def _make_tail(self, url):
244
 
        t = get_transport(url)
245
 
        t.ensure_base()
 
199
        head, tail = urlutils.split(url)
 
200
        if tail and tail != '.':
 
201
            t = get_transport(head)
 
202
            try:
 
203
                t.mkdir(tail)
 
204
            except errors.FileExists:
 
205
                pass
246
206
 
 
207
    # TODO: Should take a Transport
247
208
    @classmethod
248
 
    def create(cls, base, format=None, possible_transports=None):
 
209
    def create(cls, base, format=None):
249
210
        """Create a new BzrDir at the url 'base'.
250
211
        
 
212
        This will call the current default formats initialize with base
 
213
        as the only parameter.
 
214
 
251
215
        :param format: If supplied, the format of branch to create.  If not
252
216
            supplied, the default is used.
253
 
        :param possible_transports: If supplied, a list of transports that 
254
 
            can be reused to share a remote connection.
255
217
        """
256
218
        if cls is not BzrDir:
257
219
            raise AssertionError("BzrDir.create always creates the default"
258
220
                " format, not one of %r" % cls)
259
 
        t = get_transport(base, possible_transports)
260
 
        t.ensure_base()
 
221
        head, tail = urlutils.split(base)
 
222
        if tail and tail != '.':
 
223
            t = get_transport(head)
 
224
            try:
 
225
                t.mkdir(tail)
 
226
            except errors.FileExists:
 
227
                pass
261
228
        if format is None:
262
229
            format = BzrDirFormat.get_default_format()
263
 
        return format.initialize_on_transport(t)
264
 
 
265
 
    @staticmethod
266
 
    def find_bzrdirs(transport, evaluate=None, list_current=None):
267
 
        """Find bzrdirs recursively from current location.
268
 
 
269
 
        This is intended primarily as a building block for more sophisticated
270
 
        functionality, like finding trees under a directory, or finding
271
 
        branches that use a given repository.
272
 
        :param evaluate: An optional callable that yields recurse, value,
273
 
            where recurse controls whether this bzrdir is recursed into
274
 
            and value is the value to yield.  By default, all bzrdirs
275
 
            are recursed into, and the return value is the bzrdir.
276
 
        :param list_current: if supplied, use this function to list the current
277
 
            directory, instead of Transport.list_dir
278
 
        :return: a generator of found bzrdirs, or whatever evaluate returns.
279
 
        """
280
 
        if list_current is None:
281
 
            def list_current(transport):
282
 
                return transport.list_dir('')
283
 
        if evaluate is None:
284
 
            def evaluate(bzrdir):
285
 
                return True, bzrdir
286
 
 
287
 
        pending = [transport]
288
 
        while len(pending) > 0:
289
 
            current_transport = pending.pop()
290
 
            recurse = True
291
 
            try:
292
 
                bzrdir = BzrDir.open_from_transport(current_transport)
293
 
            except errors.NotBranchError:
294
 
                pass
295
 
            else:
296
 
                recurse, value = evaluate(bzrdir)
297
 
                yield value
298
 
            try:
299
 
                subdirs = list_current(current_transport)
300
 
            except errors.NoSuchFile:
301
 
                continue
302
 
            if recurse:
303
 
                for subdir in sorted(subdirs, reverse=True):
304
 
                    pending.append(current_transport.clone(subdir))
305
 
 
306
 
    @staticmethod
307
 
    def find_branches(transport):
308
 
        """Find all branches under a transport.
309
 
 
310
 
        This will find all branches below the transport, including branches
311
 
        inside other branches.  Where possible, it will use
312
 
        Repository.find_branches.
313
 
 
314
 
        To list all the branches that use a particular Repository, see
315
 
        Repository.find_branches
316
 
        """
317
 
        def evaluate(bzrdir):
318
 
            try:
319
 
                repository = bzrdir.open_repository()
320
 
            except errors.NoRepositoryPresent:
321
 
                pass
322
 
            else:
323
 
                return False, (None, repository)
324
 
            try:
325
 
                branch = bzrdir.open_branch()
326
 
            except errors.NotBranchError:
327
 
                return True, (None, None)
328
 
            else:
329
 
                return True, (branch, None)
330
 
        branches = []
331
 
        for branch, repo in BzrDir.find_bzrdirs(transport, evaluate=evaluate):
332
 
            if repo is not None:
333
 
                branches.extend(repo.find_branches())
334
 
            if branch is not None:
335
 
                branches.append(branch)
336
 
        return branches
337
 
 
338
 
    def destroy_repository(self):
339
 
        """Destroy the repository in this BzrDir"""
340
 
        raise NotImplementedError(self.destroy_repository)
 
230
        return format.initialize(safe_unicode(base))
341
231
 
342
232
    def create_branch(self):
343
233
        """Create a branch in this BzrDir.
344
234
 
345
 
        The bzrdir's format will control what branch format is created.
 
235
        The bzrdirs format will control what branch format is created.
346
236
        For more control see BranchFormatXX.create(a_bzrdir).
347
237
        """
348
238
        raise NotImplementedError(self.create_branch)
349
239
 
350
 
    def destroy_branch(self):
351
 
        """Destroy the branch in this BzrDir"""
352
 
        raise NotImplementedError(self.destroy_branch)
353
 
 
354
240
    @staticmethod
355
241
    def create_branch_and_repo(base, force_new_repo=False, format=None):
356
242
        """Create a new BzrDir, Branch and Repository at the url 'base'.
357
243
 
358
 
        This will use the current default BzrDirFormat unless one is
359
 
        specified, and use whatever 
 
244
        This will use the current default BzrDirFormat, and use whatever 
360
245
        repository format that that uses via bzrdir.create_branch and
361
246
        create_repository. If a shared repository is available that is used
362
247
        preferentially.
365
250
 
366
251
        :param base: The URL to create the branch at.
367
252
        :param force_new_repo: If True a new repository is always created.
368
 
        :param format: If supplied, the format of branch to create.  If not
369
 
            supplied, the default is used.
370
253
        """
371
254
        bzrdir = BzrDir.create(base, format)
372
255
        bzrdir._find_or_create_repository(force_new_repo)
373
256
        return bzrdir.create_branch()
374
257
 
375
 
    def determine_repository_policy(self, force_new_repo=False, stack_on=None,
376
 
                                    stack_on_pwd=None, require_stacking=False):
377
 
        """Return an object representing a policy to use.
378
 
 
379
 
        This controls whether a new repository is created, or a shared
380
 
        repository used instead.
381
 
 
382
 
        If stack_on is supplied, will not seek a containing shared repo.
383
 
 
384
 
        :param force_new_repo: If True, require a new repository to be created.
385
 
        :param stack_on: If supplied, the location to stack on.  If not
386
 
            supplied, a default_stack_on location may be used.
387
 
        :param stack_on_pwd: If stack_on is relative, the location it is
388
 
            relative to.
389
 
        """
390
 
        def repository_policy(found_bzrdir):
391
 
            stack_on = None
392
 
            stack_on_pwd = None
393
 
            config = found_bzrdir.get_config()
394
 
            stop = False
395
 
            if config is not None:
396
 
                stack_on = config.get_default_stack_on()
397
 
                if stack_on is not None:
398
 
                    stack_on_pwd = found_bzrdir.root_transport.base
399
 
                    stop = True
400
 
                    note('Using default stacking branch %s at %s', stack_on,
401
 
                         stack_on_pwd)
402
 
            # does it have a repository ?
403
 
            try:
404
 
                repository = found_bzrdir.open_repository()
405
 
            except errors.NoRepositoryPresent:
406
 
                repository = None
407
 
            else:
408
 
                if ((found_bzrdir.root_transport.base !=
409
 
                     self.root_transport.base) and not repository.is_shared()):
410
 
                    repository = None
411
 
                else:
412
 
                    stop = True
413
 
            if not stop:
414
 
                return None, False
415
 
            if repository:
416
 
                return UseExistingRepository(repository, stack_on,
417
 
                    stack_on_pwd, require_stacking=require_stacking), True
418
 
            else:
419
 
                return CreateRepository(self, stack_on, stack_on_pwd,
420
 
                    require_stacking=require_stacking), True
421
 
 
422
 
        if not force_new_repo:
423
 
            if stack_on is None:
424
 
                policy = self._find_containing(repository_policy)
425
 
                if policy is not None:
426
 
                    return policy
427
 
            else:
428
 
                try:
429
 
                    return UseExistingRepository(self.open_repository(),
430
 
                        stack_on, stack_on_pwd,
431
 
                        require_stacking=require_stacking)
432
 
                except errors.NoRepositoryPresent:
433
 
                    pass
434
 
        return CreateRepository(self, stack_on, stack_on_pwd,
435
 
                                require_stacking=require_stacking)
436
 
 
437
258
    def _find_or_create_repository(self, force_new_repo):
438
259
        """Create a new repository if needed, returning the repository."""
439
 
        policy = self.determine_repository_policy(force_new_repo)
440
 
        return policy.acquire_repository()
441
 
 
 
260
        if force_new_repo:
 
261
            return self.create_repository()
 
262
        try:
 
263
            return self.find_repository()
 
264
        except errors.NoRepositoryPresent:
 
265
            return self.create_repository()
 
266
        
442
267
    @staticmethod
443
268
    def create_branch_convenience(base, force_new_repo=False,
444
 
                                  force_new_tree=None, format=None,
445
 
                                  possible_transports=None):
 
269
                                  force_new_tree=None, format=None):
446
270
        """Create a new BzrDir, Branch and Repository at the url 'base'.
447
271
 
448
272
        This is a convenience function - it will use an existing repository
449
273
        if possible, can be told explicitly whether to create a working tree or
450
274
        not.
451
275
 
452
 
        This will use the current default BzrDirFormat unless one is
453
 
        specified, and use whatever 
 
276
        This will use the current default BzrDirFormat, and use whatever 
454
277
        repository format that that uses via bzrdir.create_branch and
455
278
        create_repository. If a shared repository is available that is used
456
279
        preferentially. Whatever repository is used, its tree creation policy
465
288
        :param force_new_repo: If True a new repository is always created.
466
289
        :param force_new_tree: If True or False force creation of a tree or 
467
290
                               prevent such creation respectively.
468
 
        :param format: Override for the bzrdir format to create.
469
 
        :param possible_transports: An optional reusable transports list.
 
291
        :param format: Override for the for the bzrdir format to create
470
292
        """
471
293
        if force_new_tree:
472
294
            # check for non local urls
473
 
            t = get_transport(base, possible_transports)
 
295
            t = get_transport(safe_unicode(base))
474
296
            if not isinstance(t, LocalTransport):
475
297
                raise errors.NotLocalUrl(base)
476
 
        bzrdir = BzrDir.create(base, format, possible_transports)
 
298
        bzrdir = BzrDir.create(base, format)
477
299
        repo = bzrdir._find_or_create_repository(force_new_repo)
478
300
        result = bzrdir.create_branch()
479
 
        if force_new_tree or (repo.make_working_trees() and
 
301
        if force_new_tree or (repo.make_working_trees() and 
480
302
                              force_new_tree is None):
481
303
            try:
482
304
                bzrdir.create_workingtree()
483
305
            except errors.NotLocalUrl:
484
306
                pass
485
307
        return result
 
308
        
 
309
    @staticmethod
 
310
    def create_repository(base, shared=False, format=None):
 
311
        """Create a new BzrDir and Repository at the url 'base'.
 
312
 
 
313
        If no format is supplied, this will default to the current default
 
314
        BzrDirFormat by default, and use whatever repository format that that
 
315
        uses for bzrdirformat.create_repository.
 
316
 
 
317
        :param shared: Create a shared repository rather than a standalone
 
318
                       repository.
 
319
        The Repository object is returned.
 
320
 
 
321
        This must be overridden as an instance method in child classes, where
 
322
        it should take no parameters and construct whatever repository format
 
323
        that child class desires.
 
324
        """
 
325
        bzrdir = BzrDir.create(base, format)
 
326
        return bzrdir.create_repository(shared)
486
327
 
487
328
    @staticmethod
488
329
    def create_standalone_workingtree(base, format=None):
490
331
 
491
332
        'base' must be a local path or a file:// url.
492
333
 
493
 
        This will use the current default BzrDirFormat unless one is
494
 
        specified, and use whatever 
 
334
        This will use the current default BzrDirFormat, and use whatever 
495
335
        repository format that that uses for bzrdirformat.create_workingtree,
496
336
        create_branch and create_repository.
497
337
 
498
 
        :param format: Override for the bzrdir format to create.
499
338
        :return: The WorkingTree object.
500
339
        """
501
 
        t = get_transport(base)
 
340
        t = get_transport(safe_unicode(base))
502
341
        if not isinstance(t, LocalTransport):
503
342
            raise errors.NotLocalUrl(base)
504
 
        bzrdir = BzrDir.create_branch_and_repo(base,
 
343
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
505
344
                                               force_new_repo=True,
506
345
                                               format=format).bzrdir
507
346
        return bzrdir.create_workingtree()
508
347
 
509
 
    def create_workingtree(self, revision_id=None, from_branch=None,
510
 
        accelerator_tree=None, hardlink=False):
 
348
    def create_workingtree(self, revision_id=None):
511
349
        """Create a working tree at this BzrDir.
512
350
        
513
 
        :param revision_id: create it as of this revision id.
514
 
        :param from_branch: override bzrdir branch (for lightweight checkouts)
515
 
        :param accelerator_tree: A tree which can be used for retrieving file
516
 
            contents more quickly than the revision tree, i.e. a workingtree.
517
 
            The revision tree will be used for cases where accelerator_tree's
518
 
            content is different.
 
351
        revision_id: create it as of this revision id.
519
352
        """
520
353
        raise NotImplementedError(self.create_workingtree)
521
354
 
522
 
    def retire_bzrdir(self, limit=10000):
 
355
    def retire_bzrdir(self):
523
356
        """Permanently disable the bzrdir.
524
357
 
525
358
        This is done by renaming it to give the user some ability to recover
527
360
 
528
361
        This will have horrible consequences if anyone has anything locked or
529
362
        in use.
530
 
        :param limit: number of times to retry
531
363
        """
532
 
        i  = 0
533
 
        while True:
 
364
        for i in xrange(10000):
534
365
            try:
535
366
                to_path = '.bzr.retired.%d' % i
536
367
                self.root_transport.rename('.bzr', to_path)
537
368
                note("renamed %s to %s"
538
369
                    % (self.root_transport.abspath('.bzr'), to_path))
539
 
                return
 
370
                break
540
371
            except (errors.TransportError, IOError, errors.PathError):
541
 
                i += 1
542
 
                if i > limit:
543
 
                    raise
544
 
                else:
545
 
                    pass
 
372
                pass
546
373
 
547
374
    def destroy_workingtree(self):
548
375
        """Destroy the working tree at this BzrDir.
559
386
        """
560
387
        raise NotImplementedError(self.destroy_workingtree_metadata)
561
388
 
562
 
    def _find_containing(self, evaluate):
563
 
        """Find something in a containing control directory.
564
 
 
565
 
        This method will scan containing control dirs, until it finds what
566
 
        it is looking for, decides that it will never find it, or runs out
567
 
        of containing control directories to check.
568
 
 
569
 
        It is used to implement find_repository and
570
 
        determine_repository_policy.
571
 
 
572
 
        :param evaluate: A function returning (value, stop).  If stop is True,
573
 
            the value will be returned.
 
389
    def find_repository(self):
 
390
        """Find the repository that should be used for a_bzrdir.
 
391
 
 
392
        This does not require a branch as we use it to find the repo for
 
393
        new branches as well as to hook existing branches up to their
 
394
        repository.
574
395
        """
575
 
        found_bzrdir = self
 
396
        try:
 
397
            return self.open_repository()
 
398
        except errors.NoRepositoryPresent:
 
399
            pass
 
400
        next_transport = self.root_transport.clone('..')
576
401
        while True:
577
 
            result, stop = evaluate(found_bzrdir)
578
 
            if stop:
579
 
                return result
580
 
            next_transport = found_bzrdir.root_transport.clone('..')
581
 
            if (found_bzrdir.root_transport.base == next_transport.base):
582
 
                # top of the file system
583
 
                return None
584
402
            # find the next containing bzrdir
585
403
            try:
586
404
                found_bzrdir = BzrDir.open_containing_from_transport(
587
405
                    next_transport)[0]
588
406
            except errors.NotBranchError:
589
 
                return None
590
 
 
591
 
    def find_repository(self):
592
 
        """Find the repository that should be used.
593
 
 
594
 
        This does not require a branch as we use it to find the repo for
595
 
        new branches as well as to hook existing branches up to their
596
 
        repository.
597
 
        """
598
 
        def usable_repository(found_bzrdir):
 
407
                # none found
 
408
                raise errors.NoRepositoryPresent(self)
599
409
            # does it have a repository ?
600
410
            try:
601
411
                repository = found_bzrdir.open_repository()
602
412
            except errors.NoRepositoryPresent:
603
 
                return None, False
604
 
            if found_bzrdir.root_transport.base == self.root_transport.base:
605
 
                return repository, True
606
 
            elif repository.is_shared():
607
 
                return repository, True
 
413
                next_transport = found_bzrdir.root_transport.clone('..')
 
414
                if (found_bzrdir.root_transport.base == next_transport.base):
 
415
                    # top of the file system
 
416
                    break
 
417
                else:
 
418
                    continue
 
419
            if ((found_bzrdir.root_transport.base == 
 
420
                 self.root_transport.base) or repository.is_shared()):
 
421
                return repository
608
422
            else:
609
 
                return None, True
610
 
 
611
 
        found_repo = self._find_containing(usable_repository)
612
 
        if found_repo is None:
613
 
            raise errors.NoRepositoryPresent(self)
614
 
        return found_repo
615
 
 
616
 
    def get_branch_reference(self):
617
 
        """Return the referenced URL for the branch in this bzrdir.
618
 
 
619
 
        :raises NotBranchError: If there is no Branch.
620
 
        :return: The URL the branch in this bzrdir references if it is a
621
 
            reference branch, or None for regular branches.
622
 
        """
623
 
        return None
 
423
                raise errors.NoRepositoryPresent(self)
 
424
        raise errors.NoRepositoryPresent(self)
624
425
 
625
426
    def get_branch_transport(self, branch_format):
626
427
        """Get the transport for use by branch format in this BzrDir.
630
431
        a format string, and vice versa.
631
432
 
632
433
        If branch_format is None, the transport is returned with no 
633
 
        checking. If it is not None, then the returned transport is
 
434
        checking. if it is not None, then the returned transport is
634
435
        guaranteed to point to an existing directory ready for use.
635
436
        """
636
437
        raise NotImplementedError(self.get_branch_transport)
637
 
 
638
 
    def _find_creation_modes(self):
639
 
        """Determine the appropriate modes for files and directories.
640
 
        
641
 
        They're always set to be consistent with the base directory,
642
 
        assuming that this transport allows setting modes.
643
 
        """
644
 
        # TODO: Do we need or want an option (maybe a config setting) to turn
645
 
        # this off or override it for particular locations? -- mbp 20080512
646
 
        if self._mode_check_done:
647
 
            return
648
 
        self._mode_check_done = True
649
 
        try:
650
 
            st = self.transport.stat('.')
651
 
        except errors.TransportNotPossible:
652
 
            self._dir_mode = None
653
 
            self._file_mode = None
654
 
        else:
655
 
            # Check the directory mode, but also make sure the created
656
 
            # directories and files are read-write for this user. This is
657
 
            # mostly a workaround for filesystems which lie about being able to
658
 
            # write to a directory (cygwin & win32)
659
 
            self._dir_mode = (st.st_mode & 07777) | 00700
660
 
            # Remove the sticky and execute bits for files
661
 
            self._file_mode = self._dir_mode & ~07111
662
 
 
663
 
    def _get_file_mode(self):
664
 
        """Return Unix mode for newly created files, or None.
665
 
        """
666
 
        if not self._mode_check_done:
667
 
            self._find_creation_modes()
668
 
        return self._file_mode
669
 
 
670
 
    def _get_dir_mode(self):
671
 
        """Return Unix mode for newly created directories, or None.
672
 
        """
673
 
        if not self._mode_check_done:
674
 
            self._find_creation_modes()
675
 
        return self._dir_mode
676
438
        
677
439
    def get_repository_transport(self, repository_format):
678
440
        """Get the transport for use by repository format in this BzrDir.
682
444
        a format string, and vice versa.
683
445
 
684
446
        If repository_format is None, the transport is returned with no 
685
 
        checking. If it is not None, then the returned transport is
 
447
        checking. if it is not None, then the returned transport is
686
448
        guaranteed to point to an existing directory ready for use.
687
449
        """
688
450
        raise NotImplementedError(self.get_repository_transport)
695
457
        format string, and vice versa.
696
458
 
697
459
        If workingtree_format is None, the transport is returned with no 
698
 
        checking. If it is not None, then the returned transport is
 
460
        checking. if it is not None, then the returned transport is
699
461
        guaranteed to point to an existing directory ready for use.
700
462
        """
701
463
        raise NotImplementedError(self.get_workingtree_transport)
702
 
 
703
 
    def get_config(self):
704
 
        if getattr(self, '_get_config', None) is None:
705
 
            return None
706
 
        return self._get_config()
707
 
 
 
464
        
708
465
    def __init__(self, _transport, _format):
709
466
        """Initialize a Bzr control dir object.
710
467
        
717
474
        self._format = _format
718
475
        self.transport = _transport.clone('.bzr')
719
476
        self.root_transport = _transport
720
 
        self._mode_check_done = False
721
477
 
722
478
    def is_control_filename(self, filename):
723
479
        """True if filename is the name of a path which is reserved for bzrdir's.
733
489
        # this might be better on the BzrDirFormat class because it refers to 
734
490
        # all the possible bzrdir disk formats. 
735
491
        # This method is tested via the workingtree is_control_filename tests- 
736
 
        # it was extracted from WorkingTree.is_control_filename. If the method's
737
 
        # contract is extended beyond the current trivial implementation, please
 
492
        # it was extracted from WorkingTree.is_control_filename. If the methods
 
493
        # contract is extended beyond the current trivial  implementation please
738
494
        # add new tests for it to the appropriate place.
739
495
        return filename == '.bzr' or filename.startswith('.bzr/')
740
496
 
755
511
        return BzrDir.open(base, _unsupported=True)
756
512
        
757
513
    @staticmethod
758
 
    def open(base, _unsupported=False, possible_transports=None):
759
 
        """Open an existing bzrdir, rooted at 'base' (url).
 
514
    def open(base, _unsupported=False):
 
515
        """Open an existing bzrdir, rooted at 'base' (url)
760
516
        
761
 
        :param _unsupported: a private parameter to the BzrDir class.
 
517
        _unsupported is a private parameter to the BzrDir class.
762
518
        """
763
 
        t = get_transport(base, possible_transports=possible_transports)
 
519
        t = get_transport(base)
764
520
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
765
521
 
766
522
    @staticmethod
767
 
    def open_from_transport(transport, _unsupported=False,
768
 
                            _server_formats=True):
 
523
    def open_from_transport(transport, _unsupported=False):
769
524
        """Open a bzrdir within a particular directory.
770
525
 
771
526
        :param transport: Transport containing the bzrdir.
772
527
        :param _unsupported: private.
773
528
        """
774
 
        base = transport.base
775
 
 
776
 
        def find_format(transport):
777
 
            return transport, BzrDirFormat.find_format(
778
 
                transport, _server_formats=_server_formats)
779
 
 
780
 
        def redirected(transport, e, redirection_notice):
781
 
            qualified_source = e.get_source_url()
782
 
            relpath = transport.relpath(qualified_source)
783
 
            if not e.target.endswith(relpath):
784
 
                # Not redirected to a branch-format, not a branch
785
 
                raise errors.NotBranchError(path=e.target)
786
 
            target = e.target[:-len(relpath)]
787
 
            note('%s is%s redirected to %s',
788
 
                 transport.base, e.permanently, target)
789
 
            # Let's try with a new transport
790
 
            # FIXME: If 'transport' has a qualifier, this should
791
 
            # be applied again to the new transport *iff* the
792
 
            # schemes used are the same. Uncomment this code
793
 
            # once the function (and tests) exist.
794
 
            # -- vila20070212
795
 
            #target = urlutils.copy_url_qualifiers(original, target)
796
 
            return get_transport(target)
797
 
 
798
 
        try:
799
 
            transport, format = do_catching_redirections(find_format,
800
 
                                                         transport,
801
 
                                                         redirected)
802
 
        except errors.TooManyRedirections:
803
 
            raise errors.NotBranchError(base)
804
 
 
 
529
        format = BzrDirFormat.find_format(transport)
805
530
        BzrDir._check_supported(format, _unsupported)
806
531
        return format.open(transport, _found=True)
807
532
 
816
541
        raise NotImplementedError(self.open_branch)
817
542
 
818
543
    @staticmethod
819
 
    def open_containing(url, possible_transports=None):
 
544
    def open_containing(url):
820
545
        """Open an existing branch which contains url.
821
546
        
822
547
        :param url: url to search from.
823
548
        See open_containing_from_transport for more detail.
824
549
        """
825
 
        transport = get_transport(url, possible_transports)
826
 
        return BzrDir.open_containing_from_transport(transport)
 
550
        return BzrDir.open_containing_from_transport(get_transport(url))
827
551
    
828
552
    @staticmethod
829
553
    def open_containing_from_transport(a_transport):
830
 
        """Open an existing branch which contains a_transport.base.
 
554
        """Open an existing branch which contains a_transport.base
831
555
 
832
556
        This probes for a branch at a_transport, and searches upwards from there.
833
557
 
848
572
                return result, urlutils.unescape(a_transport.relpath(url))
849
573
            except errors.NotBranchError, e:
850
574
                pass
851
 
            try:
852
 
                new_t = a_transport.clone('..')
853
 
            except errors.InvalidURLJoin:
854
 
                # reached the root, whatever that may be
855
 
                raise errors.NotBranchError(path=url)
 
575
            new_t = a_transport.clone('..')
856
576
            if new_t.base == a_transport.base:
857
577
                # reached the root, whatever that may be
858
578
                raise errors.NotBranchError(path=url)
859
579
            a_transport = new_t
860
580
 
861
 
    def _get_tree_branch(self):
862
 
        """Return the branch and tree, if any, for this bzrdir.
863
 
 
864
 
        Return None for tree if not present or inaccessible.
865
 
        Raise NotBranchError if no branch is present.
866
 
        :return: (tree, branch)
867
 
        """
868
 
        try:
869
 
            tree = self.open_workingtree()
870
 
        except (errors.NoWorkingTree, errors.NotLocalUrl):
871
 
            tree = None
872
 
            branch = self.open_branch()
873
 
        else:
874
 
            branch = tree.branch
875
 
        return tree, branch
876
 
 
877
 
    @classmethod
878
 
    def open_tree_or_branch(klass, location):
879
 
        """Return the branch and working tree at a location.
880
 
 
881
 
        If there is no tree at the location, tree will be None.
882
 
        If there is no branch at the location, an exception will be
883
 
        raised
884
 
        :return: (tree, branch)
885
 
        """
886
 
        bzrdir = klass.open(location)
887
 
        return bzrdir._get_tree_branch()
888
 
 
889
581
    @classmethod
890
582
    def open_containing_tree_or_branch(klass, location):
891
583
        """Return the branch and working tree contained by a location.
897
589
        relpath is the portion of the path that is contained by the branch.
898
590
        """
899
591
        bzrdir, relpath = klass.open_containing(location)
900
 
        tree, branch = bzrdir._get_tree_branch()
 
592
        try:
 
593
            tree = bzrdir.open_workingtree()
 
594
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
595
            tree = None
 
596
            branch = bzrdir.open_branch()
 
597
        else:
 
598
            branch = tree.branch
901
599
        return tree, branch, relpath
902
600
 
903
 
    @classmethod
904
 
    def open_containing_tree_branch_or_repository(klass, location):
905
 
        """Return the working tree, branch and repo contained by a location.
906
 
 
907
 
        Returns (tree, branch, repository, relpath).
908
 
        If there is no tree containing the location, tree will be None.
909
 
        If there is no branch containing the location, branch will be None.
910
 
        If there is no repository containing the location, repository will be
911
 
        None.
912
 
        relpath is the portion of the path that is contained by the innermost
913
 
        BzrDir.
914
 
 
915
 
        If no tree, branch or repository is found, a NotBranchError is raised.
916
 
        """
917
 
        bzrdir, relpath = klass.open_containing(location)
918
 
        try:
919
 
            tree, branch = bzrdir._get_tree_branch()
920
 
        except errors.NotBranchError:
921
 
            try:
922
 
                repo = bzrdir.find_repository()
923
 
                return None, None, repo, relpath
924
 
            except (errors.NoRepositoryPresent):
925
 
                raise errors.NotBranchError(location)
926
 
        return tree, branch, branch.repository, relpath
927
 
 
928
601
    def open_repository(self, _unsupported=False):
929
602
        """Open the repository object at this BzrDir if one is present.
930
603
 
931
 
        This will not follow the Branch object pointer - it's strictly a direct
 
604
        This will not follow the Branch object pointer - its strictly a direct
932
605
        open facility. Most client code should use open_branch().repository to
933
606
        get at a repository.
934
607
 
935
 
        :param _unsupported: a private parameter, not part of the api.
 
608
        _unsupported is a private parameter, not part of the api.
936
609
        TODO: static convenience version of this?
937
610
        """
938
611
        raise NotImplementedError(self.open_repository)
939
612
 
940
 
    def open_workingtree(self, _unsupported=False,
941
 
                         recommend_upgrade=True, from_branch=None):
 
613
    def open_workingtree(self, _unsupported=False):
942
614
        """Open the workingtree object at this BzrDir if one is present.
943
 
 
944
 
        :param recommend_upgrade: Optional keyword parameter, when True (the
945
 
            default), emit through the ui module a recommendation that the user
946
 
            upgrade the working tree when the workingtree being opened is old
947
 
            (but still fully supported).
948
 
        :param from_branch: override bzrdir branch (for lightweight checkouts)
 
615
        
 
616
        TODO: static convenience version of this?
949
617
        """
950
618
        raise NotImplementedError(self.open_workingtree)
951
619
 
973
641
        workingtree and discards it, and that's somewhat expensive.) 
974
642
        """
975
643
        try:
976
 
            self.open_workingtree(recommend_upgrade=False)
 
644
            self.open_workingtree()
977
645
            return True
978
646
        except errors.NoWorkingTree:
979
647
            return False
980
648
 
981
 
    def _cloning_metadir(self):
982
 
        """Produce a metadir suitable for cloning with.
983
 
        
984
 
        :returns: (destination_bzrdir_format, source_repository)
985
 
        """
 
649
    def _cloning_metadir(self, basis=None):
 
650
        def related_repository(bzrdir):
 
651
            try:
 
652
                branch = bzrdir.open_branch()
 
653
                return branch.repository
 
654
            except errors.NotBranchError:
 
655
                source_branch = None
 
656
                return bzrdir.open_repository()
986
657
        result_format = self._format.__class__()
987
658
        try:
988
659
            try:
989
 
                branch = self.open_branch()
990
 
                source_repository = branch.repository
991
 
            except errors.NotBranchError:
992
 
                source_branch = None
993
 
                source_repository = self.open_repository()
 
660
                source_repository = related_repository(self)
 
661
            except errors.NoRepositoryPresent:
 
662
                if basis is None:
 
663
                    raise
 
664
                source_repository = related_repository(self)
 
665
            result_format.repository_format = source_repository._format
994
666
        except errors.NoRepositoryPresent:
995
667
            source_repository = None
996
 
        else:
997
 
            # XXX TODO: This isinstance is here because we have not implemented
998
 
            # the fix recommended in bug # 103195 - to delegate this choice the
999
 
            # repository itself.
1000
 
            repo_format = source_repository._format
1001
 
            if not isinstance(repo_format, remote.RemoteRepositoryFormat):
1002
 
                result_format.repository_format = repo_format
1003
668
        try:
1004
 
            # TODO: Couldn't we just probe for the format in these cases,
1005
 
            # rather than opening the whole tree?  It would be a little
1006
 
            # faster. mbp 20070401
1007
 
            tree = self.open_workingtree(recommend_upgrade=False)
 
669
            tree = self.open_workingtree()
1008
670
        except (errors.NoWorkingTree, errors.NotLocalUrl):
1009
671
            result_format.workingtree_format = None
1010
672
        else:
1011
673
            result_format.workingtree_format = tree._format.__class__()
1012
674
        return result_format, source_repository
1013
675
 
1014
 
    def cloning_metadir(self):
 
676
    def cloning_metadir(self, basis=None):
1015
677
        """Produce a metadir suitable for cloning or sprouting with.
1016
678
 
1017
679
        These operations may produce workingtrees (yes, even though they're
1018
 
        "cloning" something that doesn't have a tree), so a viable workingtree
 
680
        "cloning" something that doesn't have a tree, so a viable workingtree
1019
681
        format must be selected.
1020
 
 
1021
 
        :returns: a BzrDirFormat with all component formats either set
1022
 
            appropriately or set to None if that component should not be 
1023
 
            created.
1024
682
        """
1025
683
        format, repository = self._cloning_metadir()
1026
684
        if format._workingtree_format is None:
1033
691
    def checkout_metadir(self):
1034
692
        return self.cloning_metadir()
1035
693
 
1036
 
    def sprout(self, url, revision_id=None, force_new_repo=False,
1037
 
               recurse='down', possible_transports=None,
1038
 
               accelerator_tree=None, hardlink=False, stacked=False):
 
694
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False,
 
695
               recurse='down'):
1039
696
        """Create a copy of this bzrdir prepared for use as a new line of
1040
697
        development.
1041
698
 
1042
 
        If url's last component does not exist, it will be created.
 
699
        If urls last component does not exist, it will be created.
1043
700
 
1044
701
        Attributes related to the identity of the source branch like
1045
702
        branch nickname will be cleaned, a working tree is created
1048
705
 
1049
706
        if revision_id is not None, then the clone operation may tune
1050
707
            itself to download less data.
1051
 
        :param accelerator_tree: A tree which can be used for retrieving file
1052
 
            contents more quickly than the revision tree, i.e. a workingtree.
1053
 
            The revision tree will be used for cases where accelerator_tree's
1054
 
            content is different.
1055
 
        :param hardlink: If true, hard-link files from accelerator_tree,
1056
 
            where possible.
1057
 
        :param stacked: If true, create a stacked branch referring to the
1058
 
            location of this control directory.
1059
708
        """
1060
 
        target_transport = get_transport(url, possible_transports)
1061
 
        target_transport.ensure_base()
1062
 
        cloning_format = self.cloning_metadir()
1063
 
        result = cloning_format.initialize_on_transport(target_transport)
 
709
        self._make_tail(url)
 
710
        cloning_format = self.cloning_metadir(basis)
 
711
        result = cloning_format.initialize(url)
 
712
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
1064
713
        try:
1065
714
            source_branch = self.open_branch()
1066
715
            source_repository = source_branch.repository
1067
 
            if stacked:
1068
 
                stacked_branch_url = self.root_transport.base
1069
 
            else:
1070
 
                # if a stacked branch wasn't requested, we don't create one
1071
 
                # even if the origin was stacked
1072
 
                stacked_branch_url = None
1073
716
        except errors.NotBranchError:
1074
717
            source_branch = None
1075
718
            try:
1076
719
                source_repository = self.open_repository()
1077
720
            except errors.NoRepositoryPresent:
1078
 
                source_repository = None
1079
 
            stacked_branch_url = None
1080
 
        repository_policy = result.determine_repository_policy(
1081
 
            force_new_repo, stacked_branch_url, require_stacking=stacked)
1082
 
        result_repo = repository_policy.acquire_repository()
1083
 
        if source_repository is not None:
1084
 
            # XXX: Isn't this redundant with the copy_content_into used below
1085
 
            # after creating the branch? -- mbp 20080724
1086
 
            result_repo.fetch(source_repository, revision_id=revision_id)
1087
 
 
1088
 
        # Create/update the result branch
1089
 
        if ((stacked 
1090
 
             or repository_policy._require_stacking 
1091
 
             or repository_policy._stack_on)
1092
 
            and not result._format.get_branch_format().supports_stacking()):
1093
 
            # force a branch that can support stacking 
1094
 
            from bzrlib.branch import BzrBranchFormat7
1095
 
            format = BzrBranchFormat7()
1096
 
            result_branch = format.initialize(result)
1097
 
            mutter("using %r for stacking" % (format,))
1098
 
        elif source_branch is None:
1099
 
            # this is for sprouting a bzrdir without a branch; is that
1100
 
            # actually useful?
1101
 
            result_branch = result.create_branch()
 
721
                # copy the entire basis one if there is one
 
722
                # but there is no repository.
 
723
                source_repository = basis_repo
 
724
        if force_new_repo:
 
725
            result_repo = None
1102
726
        else:
1103
 
            result_branch = source_branch._format.initialize(result)
1104
 
        mutter("created new branch %r" % (result_branch,))
1105
 
        repository_policy.configure_branch(result_branch)
 
727
            try:
 
728
                result_repo = result.find_repository()
 
729
            except errors.NoRepositoryPresent:
 
730
                result_repo = None
 
731
        if source_repository is None and result_repo is not None:
 
732
            pass
 
733
        elif source_repository is None and result_repo is None:
 
734
            # no repo available, make a new one
 
735
            result.create_repository()
 
736
        elif source_repository is not None and result_repo is None:
 
737
            # have source, and want to make a new target repo
 
738
            # we don't clone the repo because that preserves attributes
 
739
            # like is_shared(), and we have not yet implemented a 
 
740
            # repository sprout().
 
741
            result_repo = result.create_repository()
 
742
        if result_repo is not None:
 
743
            # fetch needed content into target.
 
744
            if basis_repo:
 
745
                # XXX FIXME RBC 20060214 need tests for this when the basis
 
746
                # is incomplete
 
747
                result_repo.fetch(basis_repo, revision_id=revision_id)
 
748
            if source_repository is not None:
 
749
                result_repo.fetch(source_repository, revision_id=revision_id)
1106
750
        if source_branch is not None:
1107
 
            # XXX: this duplicates Branch.sprout(); it probably belongs on an
1108
 
            # InterBranch method? -- mbp 20080724
1109
 
            source_branch.copy_content_into(result_branch,
1110
 
                 revision_id=revision_id)
1111
 
            result_branch.set_parent(self.root_transport.base)
1112
 
 
1113
 
        # Create/update the result working tree
1114
 
        if isinstance(target_transport, LocalTransport) and (
1115
 
            result_repo is None or result_repo.make_working_trees()):
1116
 
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
1117
 
                hardlink=hardlink)
 
751
            source_branch.sprout(result, revision_id=revision_id)
 
752
        else:
 
753
            result.create_branch()
 
754
        # TODO: jam 20060426 we probably need a test in here in the
 
755
        #       case that the newly sprouted branch is a remote one
 
756
        if result_repo is None or result_repo.make_working_trees():
 
757
            wt = result.create_workingtree()
1118
758
            wt.lock_write()
1119
759
            try:
1120
760
                if wt.path2id('') is None:
1131
771
                basis = wt.basis_tree()
1132
772
                basis.lock_read()
1133
773
                subtrees = basis.iter_references()
 
774
                recurse_branch = wt.branch
1134
775
            elif source_branch is not None:
1135
776
                basis = source_branch.basis_tree()
1136
777
                basis.lock_read()
1137
778
                subtrees = basis.iter_references()
 
779
                recurse_branch = source_branch
1138
780
            else:
1139
781
                subtrees = []
1140
782
                basis = None
1144
786
                    sublocation = source_branch.reference_parent(file_id, path)
1145
787
                    sublocation.bzrdir.sprout(target,
1146
788
                        basis.get_reference_revision(file_id, path),
1147
 
                        force_new_repo=force_new_repo, recurse=recurse,
1148
 
                        stacked=stacked)
 
789
                        force_new_repo=force_new_repo, recurse=recurse)
1149
790
            finally:
1150
791
                if basis is not None:
1151
792
                    basis.unlock()
1158
799
    def __init__(self, _transport, _format):
1159
800
        """See BzrDir.__init__."""
1160
801
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
 
802
        assert self._format._lock_class == lockable_files.TransportLock
 
803
        assert self._format._lock_file_name == 'branch-lock'
1161
804
        self._control_files = lockable_files.LockableFiles(
1162
805
                                            self.get_branch_transport(None),
1163
806
                                            self._format._lock_file_name,
1167
810
        """Pre-splitout bzrdirs do not suffer from stale locks."""
1168
811
        raise NotImplementedError(self.break_lock)
1169
812
 
1170
 
    def cloning_metadir(self):
1171
 
        """Produce a metadir suitable for cloning with."""
1172
 
        return self._format.__class__()
1173
 
 
1174
 
    def clone(self, url, revision_id=None, force_new_repo=False,
1175
 
              preserve_stacking=False):
1176
 
        """See BzrDir.clone().
1177
 
 
1178
 
        force_new_repo has no effect, since this family of formats always
1179
 
        require a new repository.
1180
 
        preserve_stacking has no effect, since no source branch using this
1181
 
        family of formats can be stacked, so there is no stacking to preserve.
1182
 
        """
 
813
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
814
        """See BzrDir.clone()."""
1183
815
        from bzrlib.workingtree import WorkingTreeFormat2
1184
816
        self._make_tail(url)
1185
817
        result = self._format._initialize_for_clone(url)
1186
 
        self.open_repository().clone(result, revision_id=revision_id)
 
818
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
819
        self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
1187
820
        from_branch = self.open_branch()
1188
821
        from_branch.clone(result, revision_id=revision_id)
1189
822
        try:
1190
 
            self.open_workingtree().clone(result)
 
823
            self.open_workingtree().clone(result, basis=basis_tree)
1191
824
        except errors.NotLocalUrl:
1192
825
            # make a new one, this format always has to have one.
1193
826
            try:
1195
828
            except errors.NotLocalUrl:
1196
829
                # but we cannot do it for remote trees.
1197
830
                to_branch = result.open_branch()
1198
 
                WorkingTreeFormat2()._stub_initialize_remote(to_branch)
 
831
                WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
1199
832
        return result
1200
833
 
1201
834
    def create_branch(self):
1202
835
        """See BzrDir.create_branch."""
1203
836
        return self.open_branch()
1204
837
 
1205
 
    def destroy_branch(self):
1206
 
        """See BzrDir.destroy_branch."""
1207
 
        raise errors.UnsupportedOperation(self.destroy_branch, self)
1208
 
 
1209
838
    def create_repository(self, shared=False):
1210
839
        """See BzrDir.create_repository."""
1211
840
        if shared:
1212
841
            raise errors.IncompatibleFormat('shared repository', self._format)
1213
842
        return self.open_repository()
1214
843
 
1215
 
    def destroy_repository(self):
1216
 
        """See BzrDir.destroy_repository."""
1217
 
        raise errors.UnsupportedOperation(self.destroy_repository, self)
1218
 
 
1219
 
    def create_workingtree(self, revision_id=None, from_branch=None,
1220
 
                           accelerator_tree=None, hardlink=False):
 
844
    def create_workingtree(self, revision_id=None):
1221
845
        """See BzrDir.create_workingtree."""
1222
846
        # this looks buggy but is not -really-
1223
 
        # because this format creates the workingtree when the bzrdir is
1224
 
        # created
1225
847
        # clone and sprout will have set the revision_id
1226
848
        # and that will have set it for us, its only
1227
849
        # specific uses of create_workingtree in isolation
1228
850
        # that can do wonky stuff here, and that only
1229
851
        # happens for creating checkouts, which cannot be 
1230
852
        # done on this format anyway. So - acceptable wart.
1231
 
        result = self.open_workingtree(recommend_upgrade=False)
 
853
        result = self.open_workingtree()
1232
854
        if revision_id is not None:
1233
855
            if revision_id == _mod_revision.NULL_REVISION:
1234
856
                result.set_parent_ids([])
1290
912
        self._check_supported(format, unsupported)
1291
913
        return format.open(self, _found=True)
1292
914
 
1293
 
    def sprout(self, url, revision_id=None, force_new_repo=False,
1294
 
               possible_transports=None, accelerator_tree=None,
1295
 
               hardlink=False, stacked=False):
 
915
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
1296
916
        """See BzrDir.sprout()."""
1297
 
        if stacked:
1298
 
            raise errors.UnstackableBranchFormat(
1299
 
                self._format, self.root_transport.base)
1300
917
        from bzrlib.workingtree import WorkingTreeFormat2
1301
918
        self._make_tail(url)
1302
919
        result = self._format._initialize_for_clone(url)
 
920
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
1303
921
        try:
1304
 
            self.open_repository().clone(result, revision_id=revision_id)
 
922
            self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
1305
923
        except errors.NoRepositoryPresent:
1306
924
            pass
1307
925
        try:
1309
927
        except errors.NotBranchError:
1310
928
            pass
1311
929
        # we always want a working tree
1312
 
        WorkingTreeFormat2().initialize(result,
1313
 
                                        accelerator_tree=accelerator_tree,
1314
 
                                        hardlink=hardlink)
 
930
        WorkingTreeFormat2().initialize(result)
1315
931
        return result
1316
932
 
1317
933
 
1346
962
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
1347
963
        return RepositoryFormat5().open(self, _found=True)
1348
964
 
1349
 
    def open_workingtree(self, _unsupported=False,
1350
 
            recommend_upgrade=True):
 
965
    def open_workingtree(self, _unsupported=False):
1351
966
        """See BzrDir.create_workingtree."""
1352
967
        from bzrlib.workingtree import WorkingTreeFormat2
1353
 
        wt_format = WorkingTreeFormat2()
1354
 
        # we don't warn here about upgrades; that ought to be handled for the
1355
 
        # bzrdir as a whole
1356
 
        return wt_format.open(self, _found=True)
 
968
        return WorkingTreeFormat2().open(self, _found=True)
1357
969
 
1358
970
 
1359
971
class BzrDir6(BzrDirPreSplitOut):
1367
979
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
1368
980
        return RepositoryFormat6().open(self, _found=True)
1369
981
 
1370
 
    def open_workingtree(self, _unsupported=False,
1371
 
        recommend_upgrade=True):
 
982
    def open_workingtree(self, _unsupported=False):
1372
983
        """See BzrDir.create_workingtree."""
1373
 
        # we don't warn here about upgrades; that ought to be handled for the
1374
 
        # bzrdir as a whole
1375
984
        from bzrlib.workingtree import WorkingTreeFormat2
1376
985
        return WorkingTreeFormat2().open(self, _found=True)
1377
986
 
1393
1002
        """See BzrDir.create_branch."""
1394
1003
        return self._format.get_branch_format().initialize(self)
1395
1004
 
1396
 
    def destroy_branch(self):
1397
 
        """See BzrDir.create_branch."""
1398
 
        self.transport.delete_tree('branch')
1399
 
 
1400
1005
    def create_repository(self, shared=False):
1401
1006
        """See BzrDir.create_repository."""
1402
1007
        return self._format.repository_format.initialize(self, shared)
1403
1008
 
1404
 
    def destroy_repository(self):
1405
 
        """See BzrDir.destroy_repository."""
1406
 
        self.transport.delete_tree('repository')
1407
 
 
1408
 
    def create_workingtree(self, revision_id=None, from_branch=None,
1409
 
                           accelerator_tree=None, hardlink=False):
 
1009
    def create_workingtree(self, revision_id=None):
1410
1010
        """See BzrDir.create_workingtree."""
1411
 
        return self._format.workingtree_format.initialize(
1412
 
            self, revision_id, from_branch=from_branch,
1413
 
            accelerator_tree=accelerator_tree, hardlink=hardlink)
 
1011
        from bzrlib.workingtree import WorkingTreeFormat
 
1012
        return self._format.workingtree_format.initialize(self, revision_id)
1414
1013
 
1415
1014
    def destroy_workingtree(self):
1416
1015
        """See BzrDir.destroy_workingtree."""
1417
 
        wt = self.open_workingtree(recommend_upgrade=False)
 
1016
        wt = self.open_workingtree()
1418
1017
        repository = wt.branch.repository
1419
1018
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1420
 
        wt.revert(old_tree=empty)
 
1019
        wt.revert([], old_tree=empty)
1421
1020
        self.destroy_workingtree_metadata()
1422
1021
 
1423
1022
    def destroy_workingtree_metadata(self):
1424
1023
        self.transport.delete_tree('checkout')
1425
1024
 
1426
 
    def find_branch_format(self):
1427
 
        """Find the branch 'format' for this bzrdir.
1428
 
 
1429
 
        This might be a synthetic object for e.g. RemoteBranch and SVN.
1430
 
        """
1431
 
        from bzrlib.branch import BranchFormat
1432
 
        return BranchFormat.find_format(self)
1433
 
 
1434
1025
    def _get_mkdir_mode(self):
1435
1026
        """Figure out the mode to use when creating a bzrdir subdir."""
1436
1027
        temp_control = lockable_files.LockableFiles(self.transport, '',
1437
1028
                                     lockable_files.TransportLock)
1438
1029
        return temp_control._dir_mode
1439
1030
 
1440
 
    def get_branch_reference(self):
1441
 
        """See BzrDir.get_branch_reference()."""
1442
 
        from bzrlib.branch import BranchFormat
1443
 
        format = BranchFormat.find_format(self)
1444
 
        return format.get_reference(self)
1445
 
 
1446
1031
    def get_branch_transport(self, branch_format):
1447
1032
        """See BzrDir.get_branch_transport()."""
1448
1033
        if branch_format is None:
1508
1093
        except errors.NotBranchError:
1509
1094
            pass
1510
1095
        try:
1511
 
            my_wt = self.open_workingtree(recommend_upgrade=False)
1512
 
            if not isinstance(my_wt._format,
 
1096
            if not isinstance(self.open_workingtree()._format,
1513
1097
                              format.workingtree_format.__class__):
1514
1098
                # the workingtree needs an upgrade.
1515
1099
                return True
1519
1103
 
1520
1104
    def open_branch(self, unsupported=False):
1521
1105
        """See BzrDir.open_branch."""
1522
 
        format = self.find_branch_format()
 
1106
        from bzrlib.branch import BranchFormat
 
1107
        format = BranchFormat.find_format(self)
1523
1108
        self._check_supported(format, unsupported)
1524
1109
        return format.open(self, _found=True)
1525
1110
 
1530
1115
        self._check_supported(format, unsupported)
1531
1116
        return format.open(self, _found=True)
1532
1117
 
1533
 
    def open_workingtree(self, unsupported=False,
1534
 
            recommend_upgrade=True):
 
1118
    def open_workingtree(self, unsupported=False):
1535
1119
        """See BzrDir.open_workingtree."""
1536
1120
        from bzrlib.workingtree import WorkingTreeFormat
1537
1121
        format = WorkingTreeFormat.find_format(self)
1538
 
        self._check_supported(format, unsupported,
1539
 
            recommend_upgrade,
1540
 
            basedir=self.root_transport.base)
 
1122
        self._check_supported(format, unsupported)
1541
1123
        return format.open(self, _found=True)
1542
1124
 
1543
 
    def _get_config(self):
1544
 
        return config.BzrDirConfig(self.transport)
1545
 
 
1546
1125
 
1547
1126
class BzrDirFormat(object):
1548
1127
    """An encapsulation of the initialization and open routines for a format.
1552
1131
     * a format string,
1553
1132
     * an open routine.
1554
1133
 
1555
 
    Formats are placed in a dict by their format string for reference 
 
1134
    Formats are placed in an dict by their format string for reference 
1556
1135
    during bzrdir opening. These should be subclasses of BzrDirFormat
1557
1136
    for consistency.
1558
1137
 
1573
1152
    This is a list of BzrDirFormat objects.
1574
1153
    """
1575
1154
 
1576
 
    _control_server_formats = []
1577
 
    """The registered control server formats, e.g. RemoteBzrDirs.
1578
 
 
1579
 
    This is a list of BzrDirFormat objects.
1580
 
    """
1581
 
 
1582
1155
    _lock_file_name = 'branch-lock'
1583
1156
 
1584
1157
    # _lock_class must be set in subclasses to the lock type, typ.
1585
1158
    # TransportLock or LockDir
1586
1159
 
1587
1160
    @classmethod
1588
 
    def find_format(klass, transport, _server_formats=True):
 
1161
    def find_format(klass, transport):
1589
1162
        """Return the format present at transport."""
1590
 
        if _server_formats:
1591
 
            formats = klass._control_server_formats + klass._control_formats
1592
 
        else:
1593
 
            formats = klass._control_formats
1594
 
        for format in formats:
 
1163
        for format in klass._control_formats:
1595
1164
            try:
1596
1165
                return format.probe_transport(transport)
1597
1166
            except errors.NotBranchError:
1601
1170
 
1602
1171
    @classmethod
1603
1172
    def probe_transport(klass, transport):
1604
 
        """Return the .bzrdir style format present in a directory."""
 
1173
        """Return the .bzrdir style transport present at URL."""
1605
1174
        try:
1606
1175
            format_string = transport.get(".bzr/branch-format").read()
1607
1176
        except errors.NoSuchFile:
1610
1179
        try:
1611
1180
            return klass._formats[format_string]
1612
1181
        except KeyError:
1613
 
            raise errors.UnknownFormatError(format=format_string, kind='bzrdir')
 
1182
            raise errors.UnknownFormatError(format=format_string)
1614
1183
 
1615
1184
    @classmethod
1616
1185
    def get_default_format(klass):
1639
1208
        """
1640
1209
        raise NotImplementedError(self.get_converter)
1641
1210
 
1642
 
    def initialize(self, url, possible_transports=None):
 
1211
    def initialize(self, url):
1643
1212
        """Create a bzr control dir at this url and return an opened copy.
1644
1213
        
1645
1214
        Subclasses should typically override initialize_on_transport
1646
1215
        instead of this method.
1647
1216
        """
1648
 
        return self.initialize_on_transport(get_transport(url,
1649
 
                                                          possible_transports))
 
1217
        return self.initialize_on_transport(get_transport(url))
1650
1218
 
1651
1219
    def initialize_on_transport(self, transport):
1652
1220
        """Initialize a new bzrdir in the base directory of a Transport."""
1658
1226
                                      # FIXME: RBC 20060121 don't peek under
1659
1227
                                      # the covers
1660
1228
                                      mode=temp_control._dir_mode)
1661
 
        if sys.platform == 'win32' and isinstance(transport, LocalTransport):
1662
 
            win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1663
1229
        file_mode = temp_control._file_mode
1664
1230
        del temp_control
1665
 
        bzrdir_transport = transport.clone('.bzr')
1666
 
        utf8_files = [('README',
1667
 
                       "This is a Bazaar control directory.\n"
1668
 
                       "Do not change any files in this directory.\n"
1669
 
                       "See http://bazaar-vcs.org/ for more information about Bazaar.\n"),
 
1231
        mutter('created control directory in ' + transport.base)
 
1232
        control = transport.clone('.bzr')
 
1233
        utf8_files = [('README', 
 
1234
                       "This is a Bazaar-NG control directory.\n"
 
1235
                       "Do not change any files in this directory.\n"),
1670
1236
                      ('branch-format', self.get_format_string()),
1671
1237
                      ]
1672
1238
        # NB: no need to escape relative paths that are url safe.
1673
 
        control_files = lockable_files.LockableFiles(bzrdir_transport,
1674
 
            self._lock_file_name, self._lock_class)
 
1239
        control_files = lockable_files.LockableFiles(control,
 
1240
                            self._lock_file_name, self._lock_class)
1675
1241
        control_files.create_lock()
1676
1242
        control_files.lock_write()
1677
1243
        try:
1678
 
            for (filename, content) in utf8_files:
1679
 
                bzrdir_transport.put_bytes(filename, content,
1680
 
                    mode=file_mode)
 
1244
            for file, content in utf8_files:
 
1245
                control_files.put_utf8(file, content)
1681
1246
        finally:
1682
1247
            control_files.unlock()
1683
1248
        return self.open(transport, _found=True)
1741
1306
 
1742
1307
    @classmethod
1743
1308
    def register_control_format(klass, format):
1744
 
        """Register a format that does not use '.bzr' for its control dir.
 
1309
        """Register a format that does not use '.bzrdir' for its control dir.
1745
1310
 
1746
1311
        TODO: This should be pulled up into a 'ControlDirFormat' base class
1747
1312
        which BzrDirFormat can inherit from, and renamed to register_format 
1751
1316
        klass._control_formats.append(format)
1752
1317
 
1753
1318
    @classmethod
1754
 
    def register_control_server_format(klass, format):
1755
 
        """Register a control format for client-server environments.
1756
 
 
1757
 
        These formats will be tried before ones registered with
1758
 
        register_control_format.  This gives implementations that decide to the
1759
 
        chance to grab it before anything looks at the contents of the format
1760
 
        file.
1761
 
        """
1762
 
        klass._control_server_formats.append(format)
 
1319
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
 
1320
    def set_default_format(klass, format):
 
1321
        klass._set_default_format(format)
1763
1322
 
1764
1323
    @classmethod
1765
1324
    def _set_default_format(klass, format):
1767
1326
        klass._default_format = format
1768
1327
 
1769
1328
    def __str__(self):
1770
 
        # Trim the newline
1771
 
        return self.get_format_string().rstrip()
 
1329
        return self.get_format_string()[:-1]
1772
1330
 
1773
1331
    @classmethod
1774
1332
    def unregister_format(klass, format):
 
1333
        assert klass._formats[format.get_format_string()] is format
1775
1334
        del klass._formats[format.get_format_string()]
1776
1335
 
1777
1336
    @classmethod
1779
1338
        klass._control_formats.remove(format)
1780
1339
 
1781
1340
 
 
1341
# register BzrDirFormat as a control format
 
1342
BzrDirFormat.register_control_format(BzrDirFormat)
 
1343
 
 
1344
 
1782
1345
class BzrDirFormat4(BzrDirFormat):
1783
1346
    """Bzr dir format 4.
1784
1347
 
1877
1440
            except errors.NotLocalUrl:
1878
1441
                # Even though we can't access the working tree, we need to
1879
1442
                # create its control files.
1880
 
                WorkingTreeFormat2()._stub_initialize_remote(branch)
 
1443
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1881
1444
        return result
1882
1445
 
1883
1446
    def _open(self, transport):
1936
1499
            except errors.NotLocalUrl:
1937
1500
                # Even though we can't access the working tree, we need to
1938
1501
                # create its control files.
1939
 
                WorkingTreeFormat2()._stub_initialize_remote(branch)
 
1502
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1940
1503
        return result
1941
1504
 
1942
1505
    def _open(self, transport):
2017
1580
        return RepositoryFormat.get_default_format()
2018
1581
 
2019
1582
    def __set_repository_format(self, value):
2020
 
        """Allow changing the repository format for metadir formats."""
 
1583
        """Allow changint the repository format for metadir formats."""
2021
1584
        self._repository_format = value
2022
1585
 
2023
1586
    repository_format = property(__return_repository_format, __set_repository_format)
2035
1598
                                  __set_workingtree_format)
2036
1599
 
2037
1600
 
2038
 
# Register bzr control format
2039
 
BzrDirFormat.register_control_format(BzrDirFormat)
2040
 
 
2041
 
# Register bzr formats
2042
1601
BzrDirFormat.register_format(BzrDirFormat4())
2043
1602
BzrDirFormat.register_format(BzrDirFormat5())
2044
1603
BzrDirFormat.register_format(BzrDirFormat6())
2047
1606
BzrDirFormat._default_format = __default_format
2048
1607
 
2049
1608
 
 
1609
class BzrDirTestProviderAdapter(object):
 
1610
    """A tool to generate a suite testing multiple bzrdir formats at once.
 
1611
 
 
1612
    This is done by copying the test once for each transport and injecting
 
1613
    the transport_server, transport_readonly_server, and bzrdir_format
 
1614
    classes into each copy. Each copy is also given a new id() to make it
 
1615
    easy to identify.
 
1616
    """
 
1617
 
 
1618
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1619
        self._transport_server = transport_server
 
1620
        self._transport_readonly_server = transport_readonly_server
 
1621
        self._formats = formats
 
1622
    
 
1623
    def adapt(self, test):
 
1624
        result = unittest.TestSuite()
 
1625
        for format in self._formats:
 
1626
            new_test = deepcopy(test)
 
1627
            new_test.transport_server = self._transport_server
 
1628
            new_test.transport_readonly_server = self._transport_readonly_server
 
1629
            new_test.bzrdir_format = format
 
1630
            def make_new_test_id():
 
1631
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
 
1632
                return lambda: new_id
 
1633
            new_test.id = make_new_test_id()
 
1634
            result.addTest(new_test)
 
1635
        return result
 
1636
 
 
1637
 
2050
1638
class Converter(object):
2051
1639
    """Converts a disk format object from one format to another."""
2052
1640
 
2123
1711
        self.pb.note('  %6d revisions not present', len(self.absent_revisions))
2124
1712
        self.pb.note('  %6d texts', self.text_count)
2125
1713
        self._cleanup_spare_files_after_format4()
2126
 
        self.branch._transport.put_bytes(
2127
 
            'branch-format',
2128
 
            BzrDirFormat5().get_format_string(),
2129
 
            mode=self.bzrdir._get_file_mode())
 
1714
        self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
2130
1715
 
2131
1716
    def _cleanup_spare_files_after_format4(self):
2132
1717
        # FIXME working tree upgrade foo.
2141
1726
 
2142
1727
    def _convert_working_inv(self):
2143
1728
        inv = xml4.serializer_v4.read_inventory(
2144
 
                self.branch._transport.get('inventory'))
2145
 
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
2146
 
        self.branch._transport.put_bytes('inventory', new_inv_xml,
2147
 
            mode=self.bzrdir._get_file_mode())
 
1729
                    self.branch.control_files.get('inventory'))
 
1730
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
 
1731
        # FIXME inventory is a working tree change.
 
1732
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
2148
1733
 
2149
1734
    def _write_all_weaves(self):
2150
1735
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
2170
1755
        self.bzrdir.transport.mkdir('revision-store')
2171
1756
        revision_transport = self.bzrdir.transport.clone('revision-store')
2172
1757
        # TODO permissions
2173
 
        from bzrlib.xml5 import serializer_v5
2174
 
        from bzrlib.repofmt.weaverepo import RevisionTextStore
2175
 
        revision_store = RevisionTextStore(revision_transport,
2176
 
            serializer_v5, False, versionedfile.PrefixMapper(),
2177
 
            lambda:True, lambda:True)
 
1758
        _revision_store = TextRevisionStore(TextStore(revision_transport,
 
1759
                                                      prefixed=False,
 
1760
                                                      compressed=True))
2178
1761
        try:
 
1762
            transaction = WriteTransaction()
2179
1763
            for i, rev_id in enumerate(self.converted_revs):
2180
1764
                self.pb.update('write revision', i, len(self.converted_revs))
2181
 
                text = serializer_v5.write_revision_to_string(
2182
 
                    self.revisions[rev_id])
2183
 
                key = (rev_id,)
2184
 
                revision_store.add_lines(key, None, osutils.split_lines(text))
 
1765
                _revision_store.add_revision(self.revisions[rev_id], transaction)
2185
1766
        finally:
2186
1767
            self.pb.clear()
2187
1768
            
2200
1781
                         rev_id)
2201
1782
            self.absent_revisions.add(rev_id)
2202
1783
        else:
2203
 
            rev = self.branch.repository.get_revision(rev_id)
 
1784
            rev = self.branch.repository._revision_store.get_revision(rev_id,
 
1785
                self.branch.repository.get_transaction())
2204
1786
            for parent_id in rev.parent_ids:
2205
1787
                self.known_revisions.add(parent_id)
2206
1788
                self.to_read.append(parent_id)
2207
1789
            self.revisions[rev_id] = rev
2208
1790
 
2209
1791
    def _load_old_inventory(self, rev_id):
 
1792
        assert rev_id not in self.converted_revs
2210
1793
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
2211
1794
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
2212
1795
        inv.revision_id = rev_id
2213
1796
        rev = self.revisions[rev_id]
 
1797
        if rev.inventory_sha1:
 
1798
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
 
1799
                'inventory sha mismatch for {%s}' % rev_id
2214
1800
        return inv
2215
1801
 
2216
1802
    def _load_updated_inventory(self, rev_id):
 
1803
        assert rev_id in self.converted_revs
2217
1804
        inv_xml = self.inv_weave.get_text(rev_id)
2218
 
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml, rev_id)
 
1805
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
2219
1806
        return inv
2220
1807
 
2221
1808
    def _convert_one_rev(self, rev_id):
2225
1812
        present_parents = [p for p in rev.parent_ids
2226
1813
                           if p not in self.absent_revisions]
2227
1814
        self._convert_revision_contents(rev, inv, present_parents)
2228
 
        self._store_new_inv(rev, inv, present_parents)
 
1815
        self._store_new_weave(rev, inv, present_parents)
2229
1816
        self.converted_revs.add(rev_id)
2230
1817
 
2231
 
    def _store_new_inv(self, rev, inv, present_parents):
 
1818
    def _store_new_weave(self, rev, inv, present_parents):
 
1819
        # the XML is now updated with text versions
 
1820
        if __debug__:
 
1821
            entries = inv.iter_entries()
 
1822
            entries.next()
 
1823
            for path, ie in entries:
 
1824
                assert getattr(ie, 'revision', None) is not None, \
 
1825
                    'no revision on {%s} in {%s}' % \
 
1826
                    (file_id, rev.revision_id)
2232
1827
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
2233
1828
        new_inv_sha1 = sha_string(new_inv_xml)
2234
 
        self.inv_weave.add_lines(rev.revision_id,
 
1829
        self.inv_weave.add_lines(rev.revision_id, 
2235
1830
                                 present_parents,
2236
1831
                                 new_inv_xml.splitlines(True))
2237
1832
        rev.inventory_sha1 = new_inv_sha1
2262
1857
            w = Weave(file_id)
2263
1858
            self.text_weaves[file_id] = w
2264
1859
        text_changed = False
2265
 
        parent_candiate_entries = ie.parent_candidates(parent_invs)
2266
 
        heads = graph.Graph(self).heads(parent_candiate_entries.keys())
2267
 
        # XXX: Note that this is unordered - and this is tolerable because 
2268
 
        # the previous code was also unordered.
2269
 
        previous_entries = dict((head, parent_candiate_entries[head]) for head
2270
 
            in heads)
 
1860
        previous_entries = ie.find_previous_heads(parent_invs,
 
1861
                                                  None,
 
1862
                                                  None,
 
1863
                                                  entry_vf=w)
 
1864
        for old_revision in previous_entries:
 
1865
                # if this fails, its a ghost ?
 
1866
                assert old_revision in self.converted_revs, \
 
1867
                    "Revision {%s} not in converted_revs" % old_revision
2271
1868
        self.snapshot_ie(previous_entries, ie, w, rev_id)
2272
1869
        del ie.text_id
2273
 
 
2274
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
2275
 
    def get_parents(self, revision_ids):
2276
 
        for revision_id in revision_ids:
2277
 
            yield self.revisions[revision_id].parent_ids
2278
 
 
2279
 
    def get_parent_map(self, revision_ids):
2280
 
        """See graph._StackedParentsProvider.get_parent_map"""
2281
 
        return dict((revision_id, self.revisions[revision_id])
2282
 
                    for revision_id in revision_ids
2283
 
                     if revision_id in self.revisions)
 
1870
        assert getattr(ie, 'revision', None) is not None
2284
1871
 
2285
1872
    def snapshot_ie(self, previous_revisions, ie, w, rev_id):
2286
1873
        # TODO: convert this logic, which is ~= snapshot to
2296
1883
                ie.revision = previous_ie.revision
2297
1884
                return
2298
1885
        if ie.has_text():
2299
 
            text = self.branch.repository._text_store.get(ie.text_id)
 
1886
            text = self.branch.repository.text_store.get(ie.text_id)
2300
1887
            file_lines = text.readlines()
 
1888
            assert sha_strings(file_lines) == ie.text_sha1
 
1889
            assert sum(map(len, file_lines)) == ie.text_size
2301
1890
            w.add_lines(rev_id, previous_revisions, file_lines)
2302
1891
            self.text_count += 1
2303
1892
        else:
2350
1939
                if (filename.endswith(".weave") or
2351
1940
                    filename.endswith(".gz") or
2352
1941
                    filename.endswith(".sig")):
2353
 
                    file_id, suffix = os.path.splitext(filename)
 
1942
                    file_id = os.path.splitext(filename)[0]
2354
1943
                else:
2355
1944
                    file_id = filename
2356
 
                    suffix = ''
2357
 
                new_name = store._mapper.map((file_id,)) + suffix
 
1945
                prefix_dir = store.hash_prefix(file_id)
2358
1946
                # FIXME keep track of the dirs made RBC 20060121
2359
1947
                try:
2360
 
                    store_transport.move(filename, new_name)
 
1948
                    store_transport.move(filename, prefix_dir + '/' + filename)
2361
1949
                except errors.NoSuchFile: # catches missing dirs strangely enough
2362
 
                    store_transport.mkdir(osutils.dirname(new_name))
2363
 
                    store_transport.move(filename, new_name)
2364
 
        self.bzrdir.transport.put_bytes(
2365
 
            'branch-format',
2366
 
            BzrDirFormat6().get_format_string(),
2367
 
            mode=self.bzrdir._get_file_mode())
 
1950
                    store_transport.mkdir(prefix_dir)
 
1951
                    store_transport.move(filename, prefix_dir + '/' + filename)
 
1952
        self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2368
1953
 
2369
1954
 
2370
1955
class ConvertBzrDir6ToMeta(Converter):
2379
1964
        self.count = 0
2380
1965
        self.total = 20 # the steps we know about
2381
1966
        self.garbage_inventories = []
2382
 
        self.dir_mode = self.bzrdir._get_dir_mode()
2383
 
        self.file_mode = self.bzrdir._get_file_mode()
2384
1967
 
2385
1968
        self.pb.note('starting upgrade from format 6 to metadir')
2386
 
        self.bzrdir.transport.put_bytes(
2387
 
                'branch-format',
2388
 
                "Converting to format 6",
2389
 
                mode=self.file_mode)
 
1969
        self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2390
1970
        # its faster to move specific files around than to open and use the apis...
2391
1971
        # first off, nuke ancestry.weave, it was never used.
2392
1972
        try:
2402
1982
            if name.startswith('basis-inventory.'):
2403
1983
                self.garbage_inventories.append(name)
2404
1984
        # create new directories for repository, working tree and branch
 
1985
        self.dir_mode = self.bzrdir._control_files._dir_mode
 
1986
        self.file_mode = self.bzrdir._control_files._file_mode
2405
1987
        repository_names = [('inventory.weave', True),
2406
1988
                            ('revision-store', True),
2407
1989
                            ('weaves', True)]
2455
2037
            for entry in checkout_files:
2456
2038
                self.move_entry('checkout', entry)
2457
2039
            if last_revision is not None:
2458
 
                self.bzrdir.transport.put_bytes(
 
2040
                self.bzrdir._control_files.put_utf8(
2459
2041
                    'checkout/last-revision', last_revision)
2460
 
        self.bzrdir.transport.put_bytes(
2461
 
            'branch-format',
2462
 
            BzrDirMetaFormat1().get_format_string(),
2463
 
            mode=self.file_mode)
 
2042
        self.bzrdir._control_files.put_utf8(
 
2043
            'branch-format', BzrDirMetaFormat1().get_format_string())
2464
2044
        return BzrDir.open(self.bzrdir.root_transport.base)
2465
2045
 
2466
2046
    def make_lock(self, name):
2484
2064
                raise
2485
2065
 
2486
2066
    def put_format(self, dirname, format):
2487
 
        self.bzrdir.transport.put_bytes('%s/format' % dirname,
2488
 
            format.get_format_string(),
2489
 
            self.file_mode)
 
2067
        self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2490
2068
 
2491
2069
 
2492
2070
class ConvertMetaToMeta(Converter):
2522
2100
            pass
2523
2101
        else:
2524
2102
            # TODO: conversions of Branch and Tree should be done by
2525
 
            # InterXFormat lookups/some sort of registry.
 
2103
            # InterXFormat lookups
2526
2104
            # Avoid circular imports
2527
2105
            from bzrlib import branch as _mod_branch
2528
 
            old = branch._format.__class__
2529
 
            new = self.target_format.get_branch_format().__class__
2530
 
            while old != new:
2531
 
                if (old == _mod_branch.BzrBranchFormat5 and
2532
 
                    new in (_mod_branch.BzrBranchFormat6,
2533
 
                        _mod_branch.BzrBranchFormat7)):
2534
 
                    branch_converter = _mod_branch.Converter5to6()
2535
 
                elif (old == _mod_branch.BzrBranchFormat6 and
2536
 
                    new == _mod_branch.BzrBranchFormat7):
2537
 
                    branch_converter = _mod_branch.Converter6to7()
2538
 
                else:
2539
 
                    raise errors.BadConversionTarget("No converter", new)
 
2106
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
 
2107
                self.target_format.get_branch_format().__class__ is
 
2108
                _mod_branch.BzrBranchFormat6):
 
2109
                branch_converter = _mod_branch.Converter5to6()
2540
2110
                branch_converter.convert(branch)
2541
 
                branch = self.bzrdir.open_branch()
2542
 
                old = branch._format.__class__
2543
2111
        try:
2544
 
            tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
 
2112
            tree = self.bzrdir.open_workingtree()
2545
2113
        except (errors.NoWorkingTree, errors.NotLocalUrl):
2546
2114
            pass
2547
2115
        else:
2555
2123
        return to_convert
2556
2124
 
2557
2125
 
2558
 
# This is not in remote.py because it's small, and needs to be registered.
2559
 
# Putting it in remote.py creates a circular import problem.
2560
 
# we can make it a lazy object if the control formats is turned into something
2561
 
# like a registry.
2562
 
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2563
 
    """Format representing bzrdirs accessed via a smart server"""
2564
 
 
2565
 
    def get_format_description(self):
2566
 
        return 'bzr remote bzrdir'
2567
 
    
2568
 
    @classmethod
2569
 
    def probe_transport(klass, transport):
2570
 
        """Return a RemoteBzrDirFormat object if it looks possible."""
2571
 
        try:
2572
 
            medium = transport.get_smart_medium()
2573
 
        except (NotImplementedError, AttributeError,
2574
 
                errors.TransportNotPossible, errors.NoSmartMedium,
2575
 
                errors.SmartProtocolError):
2576
 
            # no smart server, so not a branch for this format type.
2577
 
            raise errors.NotBranchError(path=transport.base)
2578
 
        else:
2579
 
            # Decline to open it if the server doesn't support our required
2580
 
            # version (3) so that the VFS-based transport will do it.
2581
 
            if medium.should_probe():
2582
 
                try:
2583
 
                    server_version = medium.protocol_version()
2584
 
                except errors.SmartProtocolError:
2585
 
                    # Apparently there's no usable smart server there, even though
2586
 
                    # the medium supports the smart protocol.
2587
 
                    raise errors.NotBranchError(path=transport.base)
2588
 
                if server_version != '2':
2589
 
                    raise errors.NotBranchError(path=transport.base)
2590
 
            return klass()
2591
 
 
2592
 
    def initialize_on_transport(self, transport):
2593
 
        try:
2594
 
            # hand off the request to the smart server
2595
 
            client_medium = transport.get_smart_medium()
2596
 
        except errors.NoSmartMedium:
2597
 
            # TODO: lookup the local format from a server hint.
2598
 
            local_dir_format = BzrDirMetaFormat1()
2599
 
            return local_dir_format.initialize_on_transport(transport)
2600
 
        client = _SmartClient(client_medium)
2601
 
        path = client.remote_path_from_transport(transport)
2602
 
        response = client.call('BzrDirFormat.initialize', path)
2603
 
        if response[0] != 'ok':
2604
 
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
2605
 
        return remote.RemoteBzrDir(transport)
2606
 
 
2607
 
    def _open(self, transport):
2608
 
        return remote.RemoteBzrDir(transport)
2609
 
 
2610
 
    def __eq__(self, other):
2611
 
        if not isinstance(other, RemoteBzrDirFormat):
2612
 
            return False
2613
 
        return self.get_format_description() == other.get_format_description()
2614
 
 
2615
 
 
2616
 
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2617
 
 
2618
 
 
2619
2126
class BzrDirFormatInfo(object):
2620
2127
 
2621
 
    def __init__(self, native, deprecated, hidden, experimental):
 
2128
    def __init__(self, native, deprecated, hidden):
2622
2129
        self.deprecated = deprecated
2623
2130
        self.native = native
2624
2131
        self.hidden = hidden
2625
 
        self.experimental = experimental
2626
2132
 
2627
2133
 
2628
2134
class BzrDirFormatRegistry(registry.Registry):
2632
2138
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
2633
2139
    """
2634
2140
 
2635
 
    def __init__(self):
2636
 
        """Create a BzrDirFormatRegistry."""
2637
 
        self._aliases = set()
2638
 
        super(BzrDirFormatRegistry, self).__init__()
2639
 
 
2640
 
    def aliases(self):
2641
 
        """Return a set of the format names which are aliases."""
2642
 
        return frozenset(self._aliases)
2643
 
 
2644
2141
    def register_metadir(self, key,
2645
2142
             repository_format, help, native=True, deprecated=False,
2646
2143
             branch_format=None,
2647
2144
             tree_format=None,
2648
 
             hidden=False,
2649
 
             experimental=False,
2650
 
             alias=False):
 
2145
             hidden=False):
2651
2146
        """Register a metadir subformat.
2652
2147
 
2653
2148
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2685
2180
            if repository_format is not None:
2686
2181
                bd.repository_format = _load(repository_format)
2687
2182
            return bd
2688
 
        self.register(key, helper, help, native, deprecated, hidden,
2689
 
            experimental, alias)
 
2183
        self.register(key, helper, help, native, deprecated, hidden)
2690
2184
 
2691
2185
    def register(self, key, factory, help, native=True, deprecated=False,
2692
 
                 hidden=False, experimental=False, alias=False):
 
2186
                 hidden=False):
2693
2187
        """Register a BzrDirFormat factory.
2694
2188
        
2695
2189
        The factory must be a callable that takes one parameter: the key.
2698
2192
        This function mainly exists to prevent the info object from being
2699
2193
        supplied directly.
2700
2194
        """
2701
 
        registry.Registry.register(self, key, factory, help,
2702
 
            BzrDirFormatInfo(native, deprecated, hidden, experimental))
2703
 
        if alias:
2704
 
            self._aliases.add(key)
 
2195
        registry.Registry.register(self, key, factory, help, 
 
2196
            BzrDirFormatInfo(native, deprecated, hidden))
2705
2197
 
2706
2198
    def register_lazy(self, key, module_name, member_name, help, native=True,
2707
 
        deprecated=False, hidden=False, experimental=False, alias=False):
2708
 
        registry.Registry.register_lazy(self, key, module_name, member_name,
2709
 
            help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2710
 
        if alias:
2711
 
            self._aliases.add(key)
 
2199
                      deprecated=False, hidden=False):
 
2200
        registry.Registry.register_lazy(self, key, module_name, member_name, 
 
2201
            help, BzrDirFormatInfo(native, deprecated, hidden))
2712
2202
 
2713
2203
    def set_default(self, key):
2714
2204
        """Set the 'default' key to be a clone of the supplied key.
2715
2205
        
2716
2206
        This method must be called once and only once.
2717
2207
        """
2718
 
        registry.Registry.register(self, 'default', self.get(key),
 
2208
        registry.Registry.register(self, 'default', self.get(key), 
2719
2209
            self.get_help(key), info=self.get_info(key))
2720
 
        self._aliases.add('default')
2721
2210
 
2722
2211
    def set_default_repository(self, key):
2723
2212
        """Set the FormatRegistry default and Repository default.
2729
2218
            self.remove('default')
2730
2219
        self.set_default(key)
2731
2220
        format = self.get('default')()
 
2221
        assert isinstance(format, BzrDirMetaFormat1)
2732
2222
 
2733
2223
    def make_bzrdir(self, key):
2734
2224
        return self.get(key)()
2735
2225
 
2736
2226
    def help_topic(self, topic):
2737
2227
        output = textwrap.dedent("""\
 
2228
            Bazaar directory formats
 
2229
            ------------------------
 
2230
 
2738
2231
            These formats can be used for creating branches, working trees, and
2739
2232
            repositories.
2740
2233
 
2741
2234
            """)
2742
 
        default_realkey = None
2743
2235
        default_help = self.get_help('default')
2744
2236
        help_pairs = []
2745
2237
        for key in self.keys():
2754
2246
        def wrapped(key, help, info):
2755
2247
            if info.native:
2756
2248
                help = '(native) ' + help
2757
 
            return ':%s:\n%s\n\n' % (key, 
 
2249
            return '  %s:\n%s\n\n' % (key, 
2758
2250
                    textwrap.fill(help, initial_indent='    ', 
2759
2251
                    subsequent_indent='    '))
2760
 
        if default_realkey is not None:
2761
 
            output += wrapped(default_realkey, '(default) %s' % default_help,
2762
 
                              self.get_info('default'))
 
2252
        output += wrapped('%s/default' % default_realkey, default_help,
 
2253
                          self.get_info('default'))
2763
2254
        deprecated_pairs = []
2764
 
        experimental_pairs = []
2765
2255
        for key, help in help_pairs:
2766
2256
            info = self.get_info(key)
2767
2257
            if info.hidden:
2768
2258
                continue
2769
2259
            elif info.deprecated:
2770
2260
                deprecated_pairs.append((key, help))
2771
 
            elif info.experimental:
2772
 
                experimental_pairs.append((key, help))
2773
2261
            else:
2774
2262
                output += wrapped(key, help, info)
2775
 
        if len(experimental_pairs) > 0:
2776
 
            output += "Experimental formats are shown below.\n\n"
2777
 
            for key, help in experimental_pairs:
2778
 
                info = self.get_info(key)
2779
 
                output += wrapped(key, help, info)
2780
2263
        if len(deprecated_pairs) > 0:
2781
 
            output += "Deprecated formats are shown below.\n\n"
 
2264
            output += "Deprecated formats\n------------------\n\n"
2782
2265
            for key, help in deprecated_pairs:
2783
2266
                info = self.get_info(key)
2784
2267
                output += wrapped(key, help, info)
2786
2269
        return output
2787
2270
 
2788
2271
 
2789
 
class RepositoryAcquisitionPolicy(object):
2790
 
    """Abstract base class for repository acquisition policies.
2791
 
 
2792
 
    A repository acquisition policy decides how a BzrDir acquires a repository
2793
 
    for a branch that is being created.  The most basic policy decision is
2794
 
    whether to create a new repository or use an existing one.
2795
 
    """
2796
 
    def __init__(self, stack_on, stack_on_pwd, require_stacking):
2797
 
        """Constructor.
2798
 
 
2799
 
        :param stack_on: A location to stack on
2800
 
        :param stack_on_pwd: If stack_on is relative, the location it is
2801
 
            relative to.
2802
 
        :param require_stacking: If True, it is a failure to not stack.
2803
 
        """
2804
 
        self._stack_on = stack_on
2805
 
        self._stack_on_pwd = stack_on_pwd
2806
 
        self._require_stacking = require_stacking
2807
 
 
2808
 
    def configure_branch(self, branch):
2809
 
        """Apply any configuration data from this policy to the branch.
2810
 
 
2811
 
        Default implementation sets repository stacking.
2812
 
        """
2813
 
        if self._stack_on is None:
2814
 
            return
2815
 
        if self._stack_on_pwd is None:
2816
 
            stack_on = self._stack_on
2817
 
        else:
2818
 
            try:
2819
 
                stack_on = urlutils.rebase_url(self._stack_on,
2820
 
                    self._stack_on_pwd,
2821
 
                    branch.bzrdir.root_transport.base)
2822
 
            except errors.InvalidRebaseURLs:
2823
 
                stack_on = self._get_full_stack_on()
2824
 
        try:
2825
 
            branch.set_stacked_on_url(stack_on)
2826
 
        except errors.UnstackableBranchFormat:
2827
 
            if self._require_stacking:
2828
 
                raise
2829
 
 
2830
 
    def _get_full_stack_on(self):
2831
 
        """Get a fully-qualified URL for the stack_on location."""
2832
 
        if self._stack_on is None:
2833
 
            return None
2834
 
        if self._stack_on_pwd is None:
2835
 
            return self._stack_on
2836
 
        else:
2837
 
            return urlutils.join(self._stack_on_pwd, self._stack_on)
2838
 
 
2839
 
    def _add_fallback(self, repository):
2840
 
        """Add a fallback to the supplied repository, if stacking is set."""
2841
 
        stack_on = self._get_full_stack_on()
2842
 
        if stack_on is None:
2843
 
            return
2844
 
        stacked_dir = BzrDir.open(stack_on)
2845
 
        try:
2846
 
            stacked_repo = stacked_dir.open_branch().repository
2847
 
        except errors.NotBranchError:
2848
 
            stacked_repo = stacked_dir.open_repository()
2849
 
        try:
2850
 
            repository.add_fallback_repository(stacked_repo)
2851
 
        except errors.UnstackableRepositoryFormat:
2852
 
            if self._require_stacking:
2853
 
                raise
2854
 
 
2855
 
    def acquire_repository(self, make_working_trees=None, shared=False):
2856
 
        """Acquire a repository for this bzrdir.
2857
 
 
2858
 
        Implementations may create a new repository or use a pre-exising
2859
 
        repository.
2860
 
        :param make_working_trees: If creating a repository, set
2861
 
            make_working_trees to this value (if non-None)
2862
 
        :param shared: If creating a repository, make it shared if True
2863
 
        :return: A repository
2864
 
        """
2865
 
        raise NotImplemented(RepositoryAcquisitionPolicy.acquire_repository)
2866
 
 
2867
 
 
2868
 
class CreateRepository(RepositoryAcquisitionPolicy):
2869
 
    """A policy of creating a new repository"""
2870
 
 
2871
 
    def __init__(self, bzrdir, stack_on=None, stack_on_pwd=None,
2872
 
                 require_stacking=False):
2873
 
        """
2874
 
        Constructor.
2875
 
        :param bzrdir: The bzrdir to create the repository on.
2876
 
        :param stack_on: A location to stack on
2877
 
        :param stack_on_pwd: If stack_on is relative, the location it is
2878
 
            relative to.
2879
 
        """
2880
 
        RepositoryAcquisitionPolicy.__init__(self, stack_on, stack_on_pwd,
2881
 
                                             require_stacking)
2882
 
        self._bzrdir = bzrdir
2883
 
 
2884
 
    def acquire_repository(self, make_working_trees=None, shared=False):
2885
 
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
2886
 
 
2887
 
        Creates the desired repository in the bzrdir we already have.
2888
 
        """
2889
 
        if self._stack_on or self._require_stacking:
2890
 
            # we may be coming from a format that doesn't support stacking,
2891
 
            # but we require it in the destination, so force creation of a new
2892
 
            # one here.
2893
 
            #
2894
 
            # TODO: perhaps this should be treated as a distinct repository
2895
 
            # acquisition policy?
2896
 
            repository_format = self._bzrdir._format.repository_format
2897
 
            if not repository_format.supports_external_lookups:
2898
 
                # should possibly be controlled by the registry rather than
2899
 
                # hardcoded here.
2900
 
                from bzrlib.repofmt import pack_repo
2901
 
                if repository_format.rich_root_data:
2902
 
                    repository_format = \
2903
 
                        pack_repo.RepositoryFormatKnitPack5RichRoot()
2904
 
                else:
2905
 
                    repository_format = pack_repo.RepositoryFormatKnitPack5()
2906
 
                note("using %r for stacking" % (repository_format,))
2907
 
            repository = repository_format.initialize(self._bzrdir,
2908
 
                shared=shared)
2909
 
        else:
2910
 
            # let bzrdir choose
2911
 
            repository = self._bzrdir.create_repository(shared=shared)
2912
 
        self._add_fallback(repository)
2913
 
        if make_working_trees is not None:
2914
 
            repository.set_make_working_trees(make_working_trees)
2915
 
        return repository
2916
 
 
2917
 
 
2918
 
class UseExistingRepository(RepositoryAcquisitionPolicy):
2919
 
    """A policy of reusing an existing repository"""
2920
 
 
2921
 
    def __init__(self, repository, stack_on=None, stack_on_pwd=None,
2922
 
                 require_stacking=False):
2923
 
        """Constructor.
2924
 
 
2925
 
        :param repository: The repository to use.
2926
 
        :param stack_on: A location to stack on
2927
 
        :param stack_on_pwd: If stack_on is relative, the location it is
2928
 
            relative to.
2929
 
        """
2930
 
        RepositoryAcquisitionPolicy.__init__(self, stack_on, stack_on_pwd,
2931
 
                                             require_stacking)
2932
 
        self._repository = repository
2933
 
 
2934
 
    def acquire_repository(self, make_working_trees=None, shared=False):
2935
 
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
2936
 
 
2937
 
        Returns an existing repository to use
2938
 
        """
2939
 
        self._add_fallback(self._repository)
2940
 
        return self._repository
2941
 
 
2942
 
 
2943
2272
format_registry = BzrDirFormatRegistry()
2944
2273
format_registry.register('weave', BzrDirFormat6,
2945
2274
    'Pre-0.8 format.  Slower than knit and does not'
2973
2302
    branch_format='bzrlib.branch.BzrBranchFormat6',
2974
2303
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2975
2304
    )
2976
 
format_registry.register_metadir('rich-root',
2977
 
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit4',
2978
 
    help='New in 1.0.  Better handling of tree roots.  Incompatible with'
2979
 
        ' bzr < 1.0',
2980
 
    branch_format='bzrlib.branch.BzrBranchFormat6',
2981
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2982
 
    )
2983
2305
format_registry.register_metadir('dirstate-with-subtree',
2984
2306
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2985
2307
    help='New in 0.15: Fast local operations and improved scaling for '
2987
2309
        'bzr branches. Incompatible with bzr < 0.15.',
2988
2310
    branch_format='bzrlib.branch.BzrBranchFormat6',
2989
2311
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2990
 
    experimental=True,
2991
 
    hidden=True,
2992
 
    )
2993
 
format_registry.register_metadir('pack-0.92',
2994
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2995
 
    help='New in 0.92: Pack-based format with data compatible with '
2996
 
        'dirstate-tags format repositories. Interoperates with '
2997
 
        'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2998
 
        'Previously called knitpack-experimental.  '
2999
 
        'For more information, see '
3000
 
        'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
3001
 
    branch_format='bzrlib.branch.BzrBranchFormat6',
3002
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3003
 
    )
3004
 
format_registry.register_metadir('pack-0.92-subtree',
3005
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
3006
 
    help='New in 0.92: Pack-based format with data compatible with '
3007
 
        'dirstate-with-subtree format repositories. Interoperates with '
3008
 
        'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
3009
 
        'Previously called knitpack-experimental.  '
3010
 
        'For more information, see '
3011
 
        'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
3012
 
    branch_format='bzrlib.branch.BzrBranchFormat6',
3013
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3014
 
    hidden=True,
3015
 
    experimental=True,
3016
 
    )
3017
 
format_registry.register_metadir('rich-root-pack',
3018
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
3019
 
    help='New in 1.0: Pack-based format with data compatible with '
3020
 
        'rich-root format repositories. Incompatible with'
3021
 
        ' bzr < 1.0',
3022
 
    branch_format='bzrlib.branch.BzrBranchFormat6',
3023
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3024
 
    )
3025
 
format_registry.register_metadir('1.6',
3026
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack5',
3027
 
    help='A branch and pack based repository that supports stacking. ',
3028
 
    branch_format='bzrlib.branch.BzrBranchFormat7',
3029
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3030
 
    )
3031
 
format_registry.register_metadir('1.6-rich-root',
3032
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack5RichRoot',
3033
 
    help='A branch and pack based repository that supports stacking '
3034
 
         'and rich root data (needed for bzr-svn). ',
3035
 
    branch_format='bzrlib.branch.BzrBranchFormat7',
3036
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3037
 
    )
3038
 
# The following two formats should always just be aliases.
3039
 
format_registry.register_metadir('development',
3040
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1',
3041
 
    help='Current development format. Can convert data to and from pack-0.92 '
3042
 
        '(and anything compatible with pack-0.92) format repositories. '
3043
 
        'Repositories and branches in this format can only be read by bzr.dev. '
3044
 
        'Please read '
3045
 
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
3046
 
        'before use.',
3047
 
    branch_format='bzrlib.branch.BzrBranchFormat7',
3048
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3049
 
    experimental=True,
3050
 
    alias=True,
3051
 
    )
3052
 
format_registry.register_metadir('development-subtree',
3053
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1Subtree',
3054
 
    help='Current development format, subtree variant. Can convert data to and '
3055
 
        'from pack-0.92-subtree (and anything compatible with '
3056
 
        'pack-0.92-subtree) format repositories. Repositories and branches in '
3057
 
        'this format can only be read by bzr.dev. Please read '
3058
 
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
3059
 
        'before use.',
3060
 
    branch_format='bzrlib.branch.BzrBranchFormat7',
3061
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3062
 
    experimental=True,
3063
 
    alias=True,
3064
 
    )
3065
 
# And the development formats which the will have aliased one of follow:
3066
 
format_registry.register_metadir('development0',
3067
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
3068
 
    help='Trivial rename of pack-0.92 to provide a development format. '
3069
 
        'Please read '
3070
 
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
3071
 
        'before use.',
3072
 
    branch_format='bzrlib.branch.BzrBranchFormat6',
3073
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3074
 
    hidden=True,
3075
 
    experimental=True,
3076
 
    )
3077
 
format_registry.register_metadir('development0-subtree',
3078
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
3079
 
    help='Trivial rename of pack-0.92-subtree to provide a development format. '
3080
 
        'Please read '
3081
 
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
3082
 
        'before use.',
3083
 
    branch_format='bzrlib.branch.BzrBranchFormat6',
3084
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3085
 
    hidden=True,
3086
 
    experimental=True,
3087
 
    )
3088
 
format_registry.register_metadir('development1',
3089
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1',
3090
 
    help='A branch and pack based repository that supports stacking. '
3091
 
        'Please read '
3092
 
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
3093
 
        'before use.',
3094
 
    branch_format='bzrlib.branch.BzrBranchFormat7',
3095
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3096
 
    hidden=True,
3097
 
    experimental=True,
3098
 
    )
3099
 
format_registry.register_metadir('development1-subtree',
3100
 
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1Subtree',
3101
 
    help='A branch and pack based repository that supports stacking. '
3102
 
        'Please read '
3103
 
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
3104
 
        'before use.',
3105
 
    branch_format='bzrlib.branch.BzrBranchFormat7',
3106
 
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
3107
 
    hidden=True,
3108
 
    experimental=True,
3109
 
    )
3110
 
# The current format that is made on 'bzr init'.
3111
 
format_registry.set_default('pack-0.92')
 
2312
    hidden=True,
 
2313
    )
 
2314
format_registry.set_default('dirstate')