~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: John Arbash Meinel
  • Date: 2008-07-22 21:47:13 UTC
  • mto: (3697.7.4 1.7)
  • mto: This revision was merged to the branch mainline in revision 3748.
  • Revision ID: john@arbash-meinel.com-20080722214713-xpdvsv1aixxq05tn
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
Also include both the case where the file is modified, and where it isn't.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 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.
21
26
"""
22
27
 
23
 
# TODO: remove unittest dependency; put that stuff inside the test suite
24
 
 
25
 
# TODO: Can we move specific formats into separate modules to make this file
26
 
# smaller?
 
28
# TODO: Move old formats into a plugin to make this file smaller.
27
29
 
28
30
from cStringIO import StringIO
29
31
import os
30
 
import textwrap
 
32
import sys
31
33
 
32
34
from bzrlib.lazy_import import lazy_import
33
35
lazy_import(globals(), """
34
 
from copy import deepcopy
35
36
from stat import S_ISDIR
36
 
import unittest
 
37
import textwrap
 
38
from warnings import warn
37
39
 
38
40
import bzrlib
39
41
from bzrlib import (
 
42
    config,
40
43
    errors,
 
44
    graph,
41
45
    lockable_files,
42
46
    lockdir,
 
47
    osutils,
43
48
    registry,
44
49
    remote,
45
50
    revision as _mod_revision,
46
51
    symbol_versioning,
47
52
    ui,
48
53
    urlutils,
 
54
    versionedfile,
 
55
    win32utils,
 
56
    workingtree,
 
57
    workingtree_4,
49
58
    xml4,
50
59
    xml5,
51
 
    workingtree,
52
 
    workingtree_4,
53
60
    )
54
61
from bzrlib.osutils import (
55
 
    safe_unicode,
56
62
    sha_strings,
57
63
    sha_string,
58
64
    )
 
65
from bzrlib.repository import Repository
59
66
from bzrlib.smart.client import _SmartClient
60
67
from bzrlib.smart import protocol
61
 
from bzrlib.store.revision.text import TextRevisionStore
62
 
from bzrlib.store.text import TextStore
63
68
from bzrlib.store.versioned import WeaveStore
64
69
from bzrlib.transactions import WriteTransaction
65
70
from bzrlib.transport import (
74
79
    note,
75
80
    )
76
81
from bzrlib.transport.local import LocalTransport
 
82
from bzrlib.symbol_versioning import (
 
83
    deprecated_function,
 
84
    deprecated_method,
 
85
    )
77
86
 
78
87
 
79
88
class BzrDir(object):
82
91
    BzrDir instances let you create or open any of the things that can be
83
92
    found within .bzr - checkouts, branches and repositories.
84
93
    
85
 
    transport
 
94
    :ivar transport:
86
95
        the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
87
 
    root_transport
88
 
        a transport connected to the directory this bzr was opened from.
 
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.
89
101
    """
90
102
 
91
103
    def break_lock(self):
145
157
                format.get_format_description(),
146
158
                basedir)
147
159
 
148
 
    def clone(self, url, revision_id=None, force_new_repo=False):
 
160
    def clone(self, url, revision_id=None, force_new_repo=False,
 
161
              preserve_stacking=False):
149
162
        """Clone this bzrdir and its contents to url verbatim.
150
163
 
151
 
        If urls last component does not exist, it will be created.
152
 
 
153
 
        if revision_id is not None, then the clone operation may tune
 
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
154
168
            itself to download less data.
155
 
        :param force_new_repo: Do not use a shared repository for the target 
 
169
        :param force_new_repo: Do not use a shared repository for the target
156
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.
157
173
        """
158
174
        return self.clone_on_transport(get_transport(url),
159
175
                                       revision_id=revision_id,
160
 
                                       force_new_repo=force_new_repo)
 
176
                                       force_new_repo=force_new_repo,
 
177
                                       preserve_stacking=preserve_stacking)
161
178
 
162
179
    def clone_on_transport(self, transport, revision_id=None,
163
 
                           force_new_repo=False):
 
180
                           force_new_repo=False, preserve_stacking=False):
164
181
        """Clone this bzrdir and its contents to transport verbatim.
165
182
 
166
 
        If the target directory does not exist, it will be created.
167
 
 
168
 
        if revision_id is not None, then the clone operation may tune
 
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
169
187
            itself to download less data.
170
 
        :param force_new_repo: Do not use a shared repository for the target 
 
188
        :param force_new_repo: Do not use a shared repository for the target,
171
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.
172
192
        """
173
193
        transport.ensure_base()
174
 
        result = self._format.initialize_on_transport(transport)
 
194
        result = self.cloning_metadir().initialize_on_transport(transport)
 
195
        repository_policy = None
 
196
        stack_on = None
175
197
        try:
176
198
            local_repo = self.find_repository()
177
199
        except errors.NoRepositoryPresent:
178
200
            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
 
179
217
        if local_repo:
180
218
            # may need to copy content in
181
 
            if force_new_repo:
182
 
                result_repo = local_repo.clone(
183
 
                    result,
184
 
                    revision_id=revision_id)
185
 
                result_repo.set_make_working_trees(local_repo.make_working_trees())
186
 
            else:
187
 
                try:
188
 
                    result_repo = result.find_repository()
189
 
                    # fetch content this dir needs.
190
 
                    result_repo.fetch(local_repo, revision_id=revision_id)
191
 
                except errors.NoRepositoryPresent:
192
 
                    # needed to make one anyway.
193
 
                    result_repo = local_repo.clone(
194
 
                        result,
195
 
                        revision_id=revision_id)
196
 
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
 
219
            repository_policy = result.determine_repository_policy(
 
220
                force_new_repo, stack_on)
 
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
197
227
        # 1 if there is a branch present
198
228
        #   make sure its content is available in the target repository
199
229
        #   clone it.
200
 
        try:
201
 
            self.open_branch().clone(result, revision_id=revision_id)
202
 
        except errors.NotBranchError:
203
 
            pass
204
 
        try:
205
 
            self.open_workingtree().clone(result)
206
 
        except (errors.NoWorkingTree, errors.NotLocalUrl):
207
 
            pass
 
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():
 
235
            try:
 
236
                self.open_workingtree().clone(result)
 
237
            except (errors.NoWorkingTree, errors.NotLocalUrl):
 
238
                pass
208
239
        return result
209
240
 
210
241
    # TODO: This should be given a Transport, and should chdir up; otherwise
213
244
        t = get_transport(url)
214
245
        t.ensure_base()
215
246
 
216
 
    # TODO: Should take a Transport
217
247
    @classmethod
218
 
    def create(cls, base, format=None):
 
248
    def create(cls, base, format=None, possible_transports=None):
219
249
        """Create a new BzrDir at the url 'base'.
220
250
        
221
 
        This will call the current default formats initialize with base
222
 
        as the only parameter.
223
 
 
224
251
        :param format: If supplied, the format of branch to create.  If not
225
252
            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.
226
255
        """
227
256
        if cls is not BzrDir:
228
257
            raise AssertionError("BzrDir.create always creates the default"
229
258
                " format, not one of %r" % cls)
230
 
        t = get_transport(base)
 
259
        t = get_transport(base, possible_transports)
231
260
        t.ensure_base()
232
261
        if format is None:
233
262
            format = BzrDirFormat.get_default_format()
234
 
        return format.initialize(safe_unicode(base))
 
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)
235
341
 
236
342
    def create_branch(self):
237
343
        """Create a branch in this BzrDir.
238
344
 
239
 
        The bzrdirs format will control what branch format is created.
 
345
        The bzrdir's format will control what branch format is created.
240
346
        For more control see BranchFormatXX.create(a_bzrdir).
241
347
        """
242
348
        raise NotImplementedError(self.create_branch)
243
349
 
 
350
    def destroy_branch(self):
 
351
        """Destroy the branch in this BzrDir"""
 
352
        raise NotImplementedError(self.destroy_branch)
 
353
 
244
354
    @staticmethod
245
355
    def create_branch_and_repo(base, force_new_repo=False, format=None):
246
356
        """Create a new BzrDir, Branch and Repository at the url 'base'.
247
357
 
248
 
        This will use the current default BzrDirFormat, and use whatever 
 
358
        This will use the current default BzrDirFormat unless one is
 
359
        specified, and use whatever 
249
360
        repository format that that uses via bzrdir.create_branch and
250
361
        create_repository. If a shared repository is available that is used
251
362
        preferentially.
254
365
 
255
366
        :param base: The URL to create the branch at.
256
367
        :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.
257
370
        """
258
371
        bzrdir = BzrDir.create(base, format)
259
372
        bzrdir._find_or_create_repository(force_new_repo)
260
373
        return bzrdir.create_branch()
261
374
 
 
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
        :param force_new_repo: If True, require a new repository to be created.
 
384
        :param stack_on: If supplied, the location to stack on.  If not
 
385
            supplied, a default_stack_on location may be used.
 
386
        :param stack_on_pwd: If stack_on is relative, the location it is
 
387
            relative to.
 
388
        """
 
389
        def repository_policy(found_bzrdir):
 
390
            stack_on = None
 
391
            stack_on_pwd = None
 
392
            config = found_bzrdir.get_config()
 
393
            stop = False
 
394
            if config is not None:
 
395
                stack_on = config.get_default_stack_on()
 
396
                if stack_on is not None:
 
397
                    stack_on_pwd = found_bzrdir.root_transport.base
 
398
                    stop = True
 
399
                    note('Using default stacking branch %s at %s', stack_on,
 
400
                         stack_on_pwd)
 
401
            # does it have a repository ?
 
402
            try:
 
403
                repository = found_bzrdir.open_repository()
 
404
            except errors.NoRepositoryPresent:
 
405
                repository = None
 
406
            else:
 
407
                if ((found_bzrdir.root_transport.base !=
 
408
                     self.root_transport.base) and not repository.is_shared()):
 
409
                    repository = None
 
410
                else:
 
411
                    stop = True
 
412
            if not stop:
 
413
                return None, False
 
414
            if repository:
 
415
                return UseExistingRepository(repository, stack_on,
 
416
                    stack_on_pwd, require_stacking=require_stacking), True
 
417
            else:
 
418
                return CreateRepository(self, stack_on, stack_on_pwd,
 
419
                    require_stacking=require_stacking), True
 
420
 
 
421
        if not force_new_repo:
 
422
            if stack_on is None:
 
423
                policy = self._find_containing(repository_policy)
 
424
                if policy is not None:
 
425
                    return policy
 
426
            else:
 
427
                try:
 
428
                    return UseExistingRepository(self.open_repository(),
 
429
                        stack_on, stack_on_pwd,
 
430
                        require_stacking=require_stacking)
 
431
                except errors.NoRepositoryPresent:
 
432
                    pass
 
433
        return CreateRepository(self, stack_on, stack_on_pwd,
 
434
                                require_stacking=require_stacking)
 
435
 
262
436
    def _find_or_create_repository(self, force_new_repo):
263
437
        """Create a new repository if needed, returning the repository."""
264
 
        if force_new_repo:
265
 
            return self.create_repository()
266
 
        try:
267
 
            return self.find_repository()
268
 
        except errors.NoRepositoryPresent:
269
 
            return self.create_repository()
270
 
        
 
438
        policy = self.determine_repository_policy(force_new_repo)
 
439
        return policy.acquire_repository()
 
440
 
271
441
    @staticmethod
272
442
    def create_branch_convenience(base, force_new_repo=False,
273
 
                                  force_new_tree=None, format=None):
 
443
                                  force_new_tree=None, format=None,
 
444
                                  possible_transports=None):
274
445
        """Create a new BzrDir, Branch and Repository at the url 'base'.
275
446
 
276
447
        This is a convenience function - it will use an existing repository
277
448
        if possible, can be told explicitly whether to create a working tree or
278
449
        not.
279
450
 
280
 
        This will use the current default BzrDirFormat, and use whatever 
 
451
        This will use the current default BzrDirFormat unless one is
 
452
        specified, and use whatever 
281
453
        repository format that that uses via bzrdir.create_branch and
282
454
        create_repository. If a shared repository is available that is used
283
455
        preferentially. Whatever repository is used, its tree creation policy
292
464
        :param force_new_repo: If True a new repository is always created.
293
465
        :param force_new_tree: If True or False force creation of a tree or 
294
466
                               prevent such creation respectively.
295
 
        :param format: Override for the for the bzrdir format to create
 
467
        :param format: Override for the bzrdir format to create.
 
468
        :param possible_transports: An optional reusable transports list.
296
469
        """
297
470
        if force_new_tree:
298
471
            # check for non local urls
299
 
            t = get_transport(safe_unicode(base))
 
472
            t = get_transport(base, possible_transports)
300
473
            if not isinstance(t, LocalTransport):
301
474
                raise errors.NotLocalUrl(base)
302
 
        bzrdir = BzrDir.create(base, format)
 
475
        bzrdir = BzrDir.create(base, format, possible_transports)
303
476
        repo = bzrdir._find_or_create_repository(force_new_repo)
304
477
        result = bzrdir.create_branch()
305
 
        if force_new_tree or (repo.make_working_trees() and 
 
478
        if force_new_tree or (repo.make_working_trees() and
306
479
                              force_new_tree is None):
307
480
            try:
308
481
                bzrdir.create_workingtree()
309
482
            except errors.NotLocalUrl:
310
483
                pass
311
484
        return result
312
 
        
313
 
    @staticmethod
314
 
    def create_repository(base, shared=False, format=None):
315
 
        """Create a new BzrDir and Repository at the url 'base'.
316
 
 
317
 
        If no format is supplied, this will default to the current default
318
 
        BzrDirFormat by default, and use whatever repository format that that
319
 
        uses for bzrdirformat.create_repository.
320
 
 
321
 
        :param shared: Create a shared repository rather than a standalone
322
 
                       repository.
323
 
        The Repository object is returned.
324
 
 
325
 
        This must be overridden as an instance method in child classes, where
326
 
        it should take no parameters and construct whatever repository format
327
 
        that child class desires.
328
 
        """
329
 
        bzrdir = BzrDir.create(base, format)
330
 
        return bzrdir.create_repository(shared)
331
485
 
332
486
    @staticmethod
333
487
    def create_standalone_workingtree(base, format=None):
335
489
 
336
490
        'base' must be a local path or a file:// url.
337
491
 
338
 
        This will use the current default BzrDirFormat, and use whatever 
 
492
        This will use the current default BzrDirFormat unless one is
 
493
        specified, and use whatever 
339
494
        repository format that that uses for bzrdirformat.create_workingtree,
340
495
        create_branch and create_repository.
341
496
 
 
497
        :param format: Override for the bzrdir format to create.
342
498
        :return: The WorkingTree object.
343
499
        """
344
 
        t = get_transport(safe_unicode(base))
 
500
        t = get_transport(base)
345
501
        if not isinstance(t, LocalTransport):
346
502
            raise errors.NotLocalUrl(base)
347
 
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
 
503
        bzrdir = BzrDir.create_branch_and_repo(base,
348
504
                                               force_new_repo=True,
349
505
                                               format=format).bzrdir
350
506
        return bzrdir.create_workingtree()
351
507
 
352
 
    def create_workingtree(self, revision_id=None):
 
508
    def create_workingtree(self, revision_id=None, from_branch=None,
 
509
        accelerator_tree=None, hardlink=False):
353
510
        """Create a working tree at this BzrDir.
354
511
        
355
 
        revision_id: create it as of this revision id.
 
512
        :param revision_id: create it as of this revision id.
 
513
        :param from_branch: override bzrdir branch (for lightweight checkouts)
 
514
        :param accelerator_tree: A tree which can be used for retrieving file
 
515
            contents more quickly than the revision tree, i.e. a workingtree.
 
516
            The revision tree will be used for cases where accelerator_tree's
 
517
            content is different.
356
518
        """
357
519
        raise NotImplementedError(self.create_workingtree)
358
520
 
359
 
    def retire_bzrdir(self):
 
521
    def retire_bzrdir(self, limit=10000):
360
522
        """Permanently disable the bzrdir.
361
523
 
362
524
        This is done by renaming it to give the user some ability to recover
364
526
 
365
527
        This will have horrible consequences if anyone has anything locked or
366
528
        in use.
 
529
        :param limit: number of times to retry
367
530
        """
368
 
        for i in xrange(10000):
 
531
        i  = 0
 
532
        while True:
369
533
            try:
370
534
                to_path = '.bzr.retired.%d' % i
371
535
                self.root_transport.rename('.bzr', to_path)
372
536
                note("renamed %s to %s"
373
537
                    % (self.root_transport.abspath('.bzr'), to_path))
374
 
                break
 
538
                return
375
539
            except (errors.TransportError, IOError, errors.PathError):
376
 
                pass
 
540
                i += 1
 
541
                if i > limit:
 
542
                    raise
 
543
                else:
 
544
                    pass
377
545
 
378
546
    def destroy_workingtree(self):
379
547
        """Destroy the working tree at this BzrDir.
390
558
        """
391
559
        raise NotImplementedError(self.destroy_workingtree_metadata)
392
560
 
 
561
    def _find_containing(self, evaluate):
 
562
        """Find something in a containing control directory.
 
563
 
 
564
        This method will scan containing control dirs, until it finds what
 
565
        it is looking for, decides that it will never find it, or runs out
 
566
        of containing control directories to check.
 
567
 
 
568
        It is used to implement find_repository and
 
569
        determine_repository_policy.
 
570
 
 
571
        :param evaluate: A function returning (value, stop).  If stop is True,
 
572
            the value will be returned.
 
573
        """
 
574
        found_bzrdir = self
 
575
        while True:
 
576
            result, stop = evaluate(found_bzrdir)
 
577
            if stop:
 
578
                return result
 
579
            next_transport = found_bzrdir.root_transport.clone('..')
 
580
            if (found_bzrdir.root_transport.base == next_transport.base):
 
581
                # top of the file system
 
582
                return None
 
583
            # find the next containing bzrdir
 
584
            try:
 
585
                found_bzrdir = BzrDir.open_containing_from_transport(
 
586
                    next_transport)[0]
 
587
            except errors.NotBranchError:
 
588
                return None
 
589
 
393
590
    def find_repository(self):
394
 
        """Find the repository that should be used for a_bzrdir.
 
591
        """Find the repository that should be used.
395
592
 
396
593
        This does not require a branch as we use it to find the repo for
397
594
        new branches as well as to hook existing branches up to their
398
595
        repository.
399
596
        """
400
 
        try:
401
 
            return self.open_repository()
402
 
        except errors.NoRepositoryPresent:
403
 
            pass
404
 
        next_transport = self.root_transport.clone('..')
405
 
        while True:
406
 
            # find the next containing bzrdir
407
 
            try:
408
 
                found_bzrdir = BzrDir.open_containing_from_transport(
409
 
                    next_transport)[0]
410
 
            except errors.NotBranchError:
411
 
                # none found
412
 
                raise errors.NoRepositoryPresent(self)
 
597
        def usable_repository(found_bzrdir):
413
598
            # does it have a repository ?
414
599
            try:
415
600
                repository = found_bzrdir.open_repository()
416
601
            except errors.NoRepositoryPresent:
417
 
                next_transport = found_bzrdir.root_transport.clone('..')
418
 
                if (found_bzrdir.root_transport.base == next_transport.base):
419
 
                    # top of the file system
420
 
                    break
421
 
                else:
422
 
                    continue
423
 
            if ((found_bzrdir.root_transport.base ==
424
 
                 self.root_transport.base) or repository.is_shared()):
425
 
                return repository
 
602
                return None, False
 
603
            if found_bzrdir.root_transport.base == self.root_transport.base:
 
604
                return repository, True
 
605
            elif repository.is_shared():
 
606
                return repository, True
426
607
            else:
427
 
                raise errors.NoRepositoryPresent(self)
428
 
        raise errors.NoRepositoryPresent(self)
 
608
                return None, True
 
609
 
 
610
        found_repo = self._find_containing(usable_repository)
 
611
        if found_repo is None:
 
612
            raise errors.NoRepositoryPresent(self)
 
613
        return found_repo
429
614
 
430
615
    def get_branch_reference(self):
431
616
        """Return the referenced URL for the branch in this bzrdir.
444
629
        a format string, and vice versa.
445
630
 
446
631
        If branch_format is None, the transport is returned with no 
447
 
        checking. if it is not None, then the returned transport is
 
632
        checking. If it is not None, then the returned transport is
448
633
        guaranteed to point to an existing directory ready for use.
449
634
        """
450
635
        raise NotImplementedError(self.get_branch_transport)
 
636
 
 
637
    def _find_creation_modes(self):
 
638
        """Determine the appropriate modes for files and directories.
 
639
        
 
640
        They're always set to be consistent with the base directory,
 
641
        assuming that this transport allows setting modes.
 
642
        """
 
643
        # TODO: Do we need or want an option (maybe a config setting) to turn
 
644
        # this off or override it for particular locations? -- mbp 20080512
 
645
        if self._mode_check_done:
 
646
            return
 
647
        self._mode_check_done = True
 
648
        try:
 
649
            st = self.transport.stat('.')
 
650
        except errors.TransportNotPossible:
 
651
            self._dir_mode = None
 
652
            self._file_mode = None
 
653
        else:
 
654
            # Check the directory mode, but also make sure the created
 
655
            # directories and files are read-write for this user. This is
 
656
            # mostly a workaround for filesystems which lie about being able to
 
657
            # write to a directory (cygwin & win32)
 
658
            self._dir_mode = (st.st_mode & 07777) | 00700
 
659
            # Remove the sticky and execute bits for files
 
660
            self._file_mode = self._dir_mode & ~07111
 
661
 
 
662
    def _get_file_mode(self):
 
663
        """Return Unix mode for newly created files, or None.
 
664
        """
 
665
        if not self._mode_check_done:
 
666
            self._find_creation_modes()
 
667
        return self._file_mode
 
668
 
 
669
    def _get_dir_mode(self):
 
670
        """Return Unix mode for newly created directories, or None.
 
671
        """
 
672
        if not self._mode_check_done:
 
673
            self._find_creation_modes()
 
674
        return self._dir_mode
451
675
        
452
676
    def get_repository_transport(self, repository_format):
453
677
        """Get the transport for use by repository format in this BzrDir.
457
681
        a format string, and vice versa.
458
682
 
459
683
        If repository_format is None, the transport is returned with no 
460
 
        checking. if it is not None, then the returned transport is
 
684
        checking. If it is not None, then the returned transport is
461
685
        guaranteed to point to an existing directory ready for use.
462
686
        """
463
687
        raise NotImplementedError(self.get_repository_transport)
470
694
        format string, and vice versa.
471
695
 
472
696
        If workingtree_format is None, the transport is returned with no 
473
 
        checking. if it is not None, then the returned transport is
 
697
        checking. If it is not None, then the returned transport is
474
698
        guaranteed to point to an existing directory ready for use.
475
699
        """
476
700
        raise NotImplementedError(self.get_workingtree_transport)
477
 
        
 
701
 
 
702
    def get_config(self):
 
703
        if getattr(self, '_get_config', None) is None:
 
704
            return None
 
705
        return self._get_config()
 
706
 
478
707
    def __init__(self, _transport, _format):
479
708
        """Initialize a Bzr control dir object.
480
709
        
487
716
        self._format = _format
488
717
        self.transport = _transport.clone('.bzr')
489
718
        self.root_transport = _transport
 
719
        self._mode_check_done = False
490
720
 
491
721
    def is_control_filename(self, filename):
492
722
        """True if filename is the name of a path which is reserved for bzrdir's.
502
732
        # this might be better on the BzrDirFormat class because it refers to 
503
733
        # all the possible bzrdir disk formats. 
504
734
        # This method is tested via the workingtree is_control_filename tests- 
505
 
        # it was extracted from WorkingTree.is_control_filename. If the methods
506
 
        # contract is extended beyond the current trivial  implementation please
 
735
        # it was extracted from WorkingTree.is_control_filename. If the method's
 
736
        # contract is extended beyond the current trivial implementation, please
507
737
        # add new tests for it to the appropriate place.
508
738
        return filename == '.bzr' or filename.startswith('.bzr/')
509
739
 
524
754
        return BzrDir.open(base, _unsupported=True)
525
755
        
526
756
    @staticmethod
527
 
    def open(base, _unsupported=False):
528
 
        """Open an existing bzrdir, rooted at 'base' (url)
 
757
    def open(base, _unsupported=False, possible_transports=None):
 
758
        """Open an existing bzrdir, rooted at 'base' (url).
529
759
        
530
 
        _unsupported is a private parameter to the BzrDir class.
 
760
        :param _unsupported: a private parameter to the BzrDir class.
531
761
        """
532
 
        t = get_transport(base)
 
762
        t = get_transport(base, possible_transports=possible_transports)
533
763
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
534
764
 
535
765
    @staticmethod
556
786
            note('%s is%s redirected to %s',
557
787
                 transport.base, e.permanently, target)
558
788
            # Let's try with a new transport
559
 
            qualified_target = e.get_target_url()[:-len(relpath)]
560
789
            # FIXME: If 'transport' has a qualifier, this should
561
790
            # be applied again to the new transport *iff* the
562
 
            # schemes used are the same. It's a bit tricky to
563
 
            # verify, so I'll punt for now
 
791
            # schemes used are the same. Uncomment this code
 
792
            # once the function (and tests) exist.
564
793
            # -- vila20070212
 
794
            #target = urlutils.copy_url_qualifiers(original, target)
565
795
            return get_transport(target)
566
796
 
567
797
        try:
585
815
        raise NotImplementedError(self.open_branch)
586
816
 
587
817
    @staticmethod
588
 
    def open_containing(url):
 
818
    def open_containing(url, possible_transports=None):
589
819
        """Open an existing branch which contains url.
590
820
        
591
821
        :param url: url to search from.
592
822
        See open_containing_from_transport for more detail.
593
823
        """
594
 
        return BzrDir.open_containing_from_transport(get_transport(url))
 
824
        transport = get_transport(url, possible_transports)
 
825
        return BzrDir.open_containing_from_transport(transport)
595
826
    
596
827
    @staticmethod
597
828
    def open_containing_from_transport(a_transport):
598
 
        """Open an existing branch which contains a_transport.base
 
829
        """Open an existing branch which contains a_transport.base.
599
830
 
600
831
        This probes for a branch at a_transport, and searches upwards from there.
601
832
 
626
857
                raise errors.NotBranchError(path=url)
627
858
            a_transport = new_t
628
859
 
 
860
    def _get_tree_branch(self):
 
861
        """Return the branch and tree, if any, for this bzrdir.
 
862
 
 
863
        Return None for tree if not present or inaccessible.
 
864
        Raise NotBranchError if no branch is present.
 
865
        :return: (tree, branch)
 
866
        """
 
867
        try:
 
868
            tree = self.open_workingtree()
 
869
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
870
            tree = None
 
871
            branch = self.open_branch()
 
872
        else:
 
873
            branch = tree.branch
 
874
        return tree, branch
 
875
 
 
876
    @classmethod
 
877
    def open_tree_or_branch(klass, location):
 
878
        """Return the branch and working tree at a location.
 
879
 
 
880
        If there is no tree at the location, tree will be None.
 
881
        If there is no branch at the location, an exception will be
 
882
        raised
 
883
        :return: (tree, branch)
 
884
        """
 
885
        bzrdir = klass.open(location)
 
886
        return bzrdir._get_tree_branch()
 
887
 
629
888
    @classmethod
630
889
    def open_containing_tree_or_branch(klass, location):
631
890
        """Return the branch and working tree contained by a location.
637
896
        relpath is the portion of the path that is contained by the branch.
638
897
        """
639
898
        bzrdir, relpath = klass.open_containing(location)
640
 
        try:
641
 
            tree = bzrdir.open_workingtree()
642
 
        except (errors.NoWorkingTree, errors.NotLocalUrl):
643
 
            tree = None
644
 
            branch = bzrdir.open_branch()
645
 
        else:
646
 
            branch = tree.branch
 
899
        tree, branch = bzrdir._get_tree_branch()
647
900
        return tree, branch, relpath
648
901
 
 
902
    @classmethod
 
903
    def open_containing_tree_branch_or_repository(klass, location):
 
904
        """Return the working tree, branch and repo contained by a location.
 
905
 
 
906
        Returns (tree, branch, repository, relpath).
 
907
        If there is no tree containing the location, tree will be None.
 
908
        If there is no branch containing the location, branch will be None.
 
909
        If there is no repository containing the location, repository will be
 
910
        None.
 
911
        relpath is the portion of the path that is contained by the innermost
 
912
        BzrDir.
 
913
 
 
914
        If no tree, branch or repository is found, a NotBranchError is raised.
 
915
        """
 
916
        bzrdir, relpath = klass.open_containing(location)
 
917
        try:
 
918
            tree, branch = bzrdir._get_tree_branch()
 
919
        except errors.NotBranchError:
 
920
            try:
 
921
                repo = bzrdir.find_repository()
 
922
                return None, None, repo, relpath
 
923
            except (errors.NoRepositoryPresent):
 
924
                raise errors.NotBranchError(location)
 
925
        return tree, branch, branch.repository, relpath
 
926
 
649
927
    def open_repository(self, _unsupported=False):
650
928
        """Open the repository object at this BzrDir if one is present.
651
929
 
652
 
        This will not follow the Branch object pointer - its strictly a direct
 
930
        This will not follow the Branch object pointer - it's strictly a direct
653
931
        open facility. Most client code should use open_branch().repository to
654
932
        get at a repository.
655
933
 
656
 
        _unsupported is a private parameter, not part of the api.
 
934
        :param _unsupported: a private parameter, not part of the api.
657
935
        TODO: static convenience version of this?
658
936
        """
659
937
        raise NotImplementedError(self.open_repository)
660
938
 
661
939
    def open_workingtree(self, _unsupported=False,
662
 
            recommend_upgrade=True):
 
940
                         recommend_upgrade=True, from_branch=None):
663
941
        """Open the workingtree object at this BzrDir if one is present.
664
942
 
665
943
        :param recommend_upgrade: Optional keyword parameter, when True (the
666
944
            default), emit through the ui module a recommendation that the user
667
945
            upgrade the working tree when the workingtree being opened is old
668
946
            (but still fully supported).
 
947
        :param from_branch: override bzrdir branch (for lightweight checkouts)
669
948
        """
670
949
        raise NotImplementedError(self.open_workingtree)
671
950
 
699
978
            return False
700
979
 
701
980
    def _cloning_metadir(self):
702
 
        """Produce a metadir suitable for cloning with"""
 
981
        """Produce a metadir suitable for cloning with."""
703
982
        result_format = self._format.__class__()
704
983
        try:
705
984
            try:
732
1011
        """Produce a metadir suitable for cloning or sprouting with.
733
1012
 
734
1013
        These operations may produce workingtrees (yes, even though they're
735
 
        "cloning" something that doesn't have a tree, so a viable workingtree
 
1014
        "cloning" something that doesn't have a tree), so a viable workingtree
736
1015
        format must be selected.
737
1016
        """
738
1017
        format, repository = self._cloning_metadir()
747
1026
        return self.cloning_metadir()
748
1027
 
749
1028
    def sprout(self, url, revision_id=None, force_new_repo=False,
750
 
               recurse='down'):
 
1029
               recurse='down', possible_transports=None,
 
1030
               accelerator_tree=None, hardlink=False, stacked=False):
751
1031
        """Create a copy of this bzrdir prepared for use as a new line of
752
1032
        development.
753
1033
 
754
 
        If urls last component does not exist, it will be created.
 
1034
        If url's last component does not exist, it will be created.
755
1035
 
756
1036
        Attributes related to the identity of the source branch like
757
1037
        branch nickname will be cleaned, a working tree is created
760
1040
 
761
1041
        if revision_id is not None, then the clone operation may tune
762
1042
            itself to download less data.
 
1043
        :param accelerator_tree: A tree which can be used for retrieving file
 
1044
            contents more quickly than the revision tree, i.e. a workingtree.
 
1045
            The revision tree will be used for cases where accelerator_tree's
 
1046
            content is different.
 
1047
        :param hardlink: If true, hard-link files from accelerator_tree,
 
1048
            where possible.
 
1049
        :param stacked: If true, create a stacked branch referring to the
 
1050
            location of this control directory.
763
1051
        """
764
 
        target_transport = get_transport(url)
 
1052
        target_transport = get_transport(url, possible_transports)
765
1053
        target_transport.ensure_base()
766
1054
        cloning_format = self.cloning_metadir()
767
1055
        result = cloning_format.initialize_on_transport(target_transport)
768
1056
        try:
769
1057
            source_branch = self.open_branch()
770
1058
            source_repository = source_branch.repository
 
1059
            if stacked:
 
1060
                stacked_branch_url = self.root_transport.base
 
1061
            else:
 
1062
                # if a stacked branch wasn't requested, we don't create one
 
1063
                # even if the origin was stacked
 
1064
                stacked_branch_url = None
771
1065
        except errors.NotBranchError:
772
1066
            source_branch = None
773
1067
            try:
774
1068
                source_repository = self.open_repository()
775
1069
            except errors.NoRepositoryPresent:
776
1070
                source_repository = None
777
 
        if force_new_repo:
778
 
            result_repo = None
779
 
        else:
780
 
            try:
781
 
                result_repo = result.find_repository()
782
 
            except errors.NoRepositoryPresent:
783
 
                result_repo = None
784
 
        if source_repository is None and result_repo is not None:
785
 
            pass
786
 
        elif source_repository is None and result_repo is None:
787
 
            # no repo available, make a new one
788
 
            result.create_repository()
789
 
        elif source_repository is not None and result_repo is None:
790
 
            # have source, and want to make a new target repo
791
 
            result_repo = source_repository.sprout(result, revision_id=revision_id)
792
 
        else:
793
 
            # fetch needed content into target.
794
 
            if source_repository is not None:
795
 
                # would rather do 
796
 
                # source_repository.copy_content_into(result_repo, revision_id=revision_id)
797
 
                # so we can override the copy method
798
 
                result_repo.fetch(source_repository, revision_id=revision_id)
 
1071
            stacked_branch_url = None
 
1072
        repository_policy = result.determine_repository_policy(
 
1073
            force_new_repo, stacked_branch_url, require_stacking=stacked)
 
1074
        result_repo = repository_policy.acquire_repository()
 
1075
        if source_repository is not None:
 
1076
            result_repo.fetch(source_repository, revision_id=revision_id)
 
1077
 
 
1078
        # Create/update the result branch
799
1079
        if source_branch is not None:
800
 
            source_branch.sprout(result, revision_id=revision_id)
 
1080
            result_branch = source_branch.sprout(result,
 
1081
                revision_id=revision_id)
801
1082
        else:
802
 
            result.create_branch()
803
 
        # TODO: jam 20060426 we probably need a test in here in the
804
 
        #       case that the newly sprouted branch is a remote one
805
 
        if result_repo is None or result_repo.make_working_trees():
806
 
            wt = result.create_workingtree()
 
1083
            result_branch = result.create_branch()
 
1084
        repository_policy.configure_branch(result_branch)
 
1085
 
 
1086
        # Create/update the result working tree
 
1087
        if isinstance(target_transport, LocalTransport) and (
 
1088
            result_repo is None or result_repo.make_working_trees()):
 
1089
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
 
1090
                hardlink=hardlink)
807
1091
            wt.lock_write()
808
1092
            try:
809
1093
                if wt.path2id('') is None:
820
1104
                basis = wt.basis_tree()
821
1105
                basis.lock_read()
822
1106
                subtrees = basis.iter_references()
823
 
                recurse_branch = wt.branch
824
1107
            elif source_branch is not None:
825
1108
                basis = source_branch.basis_tree()
826
1109
                basis.lock_read()
827
1110
                subtrees = basis.iter_references()
828
 
                recurse_branch = source_branch
829
1111
            else:
830
1112
                subtrees = []
831
1113
                basis = None
835
1117
                    sublocation = source_branch.reference_parent(file_id, path)
836
1118
                    sublocation.bzrdir.sprout(target,
837
1119
                        basis.get_reference_revision(file_id, path),
838
 
                        force_new_repo=force_new_repo, recurse=recurse)
 
1120
                        force_new_repo=force_new_repo, recurse=recurse,
 
1121
                        stacked=stacked)
839
1122
            finally:
840
1123
                if basis is not None:
841
1124
                    basis.unlock()
848
1131
    def __init__(self, _transport, _format):
849
1132
        """See BzrDir.__init__."""
850
1133
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
851
 
        assert self._format._lock_class == lockable_files.TransportLock
852
 
        assert self._format._lock_file_name == 'branch-lock'
853
1134
        self._control_files = lockable_files.LockableFiles(
854
1135
                                            self.get_branch_transport(None),
855
1136
                                            self._format._lock_file_name,
859
1140
        """Pre-splitout bzrdirs do not suffer from stale locks."""
860
1141
        raise NotImplementedError(self.break_lock)
861
1142
 
862
 
    def clone(self, url, revision_id=None, force_new_repo=False):
863
 
        """See BzrDir.clone()."""
 
1143
    def cloning_metadir(self):
 
1144
        """Produce a metadir suitable for cloning with."""
 
1145
        return self._format.__class__()
 
1146
 
 
1147
    def clone(self, url, revision_id=None, force_new_repo=False,
 
1148
              preserve_stacking=False):
 
1149
        """See BzrDir.clone().
 
1150
 
 
1151
        force_new_repo has no effect, since this family of formats always
 
1152
        require a new repository.
 
1153
        preserve_stacking has no effect, since no source branch using this
 
1154
        family of formats can be stacked, so there is no stacking to preserve.
 
1155
        """
864
1156
        from bzrlib.workingtree import WorkingTreeFormat2
865
1157
        self._make_tail(url)
866
1158
        result = self._format._initialize_for_clone(url)
876
1168
            except errors.NotLocalUrl:
877
1169
                # but we cannot do it for remote trees.
878
1170
                to_branch = result.open_branch()
879
 
                WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
 
1171
                WorkingTreeFormat2()._stub_initialize_remote(to_branch)
880
1172
        return result
881
1173
 
882
1174
    def create_branch(self):
883
1175
        """See BzrDir.create_branch."""
884
1176
        return self.open_branch()
885
1177
 
 
1178
    def destroy_branch(self):
 
1179
        """See BzrDir.destroy_branch."""
 
1180
        raise errors.UnsupportedOperation(self.destroy_branch, self)
 
1181
 
886
1182
    def create_repository(self, shared=False):
887
1183
        """See BzrDir.create_repository."""
888
1184
        if shared:
889
1185
            raise errors.IncompatibleFormat('shared repository', self._format)
890
1186
        return self.open_repository()
891
1187
 
892
 
    def create_workingtree(self, revision_id=None):
 
1188
    def destroy_repository(self):
 
1189
        """See BzrDir.destroy_repository."""
 
1190
        raise errors.UnsupportedOperation(self.destroy_repository, self)
 
1191
 
 
1192
    def create_workingtree(self, revision_id=None, from_branch=None,
 
1193
                           accelerator_tree=None, hardlink=False):
893
1194
        """See BzrDir.create_workingtree."""
894
1195
        # this looks buggy but is not -really-
895
1196
        # because this format creates the workingtree when the bzrdir is
962
1263
        self._check_supported(format, unsupported)
963
1264
        return format.open(self, _found=True)
964
1265
 
965
 
    def sprout(self, url, revision_id=None, force_new_repo=False):
 
1266
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
1267
               possible_transports=None, accelerator_tree=None,
 
1268
               hardlink=False, stacked=False):
966
1269
        """See BzrDir.sprout()."""
 
1270
        if stacked:
 
1271
            raise errors.UnstackableBranchFormat(
 
1272
                self._format, self.root_transport.base)
967
1273
        from bzrlib.workingtree import WorkingTreeFormat2
968
1274
        self._make_tail(url)
969
1275
        result = self._format._initialize_for_clone(url)
976
1282
        except errors.NotBranchError:
977
1283
            pass
978
1284
        # we always want a working tree
979
 
        WorkingTreeFormat2().initialize(result)
 
1285
        WorkingTreeFormat2().initialize(result,
 
1286
                                        accelerator_tree=accelerator_tree,
 
1287
                                        hardlink=hardlink)
980
1288
        return result
981
1289
 
982
1290
 
1058
1366
        """See BzrDir.create_branch."""
1059
1367
        return self._format.get_branch_format().initialize(self)
1060
1368
 
 
1369
    def destroy_branch(self):
 
1370
        """See BzrDir.create_branch."""
 
1371
        self.transport.delete_tree('branch')
 
1372
 
1061
1373
    def create_repository(self, shared=False):
1062
1374
        """See BzrDir.create_repository."""
1063
1375
        return self._format.repository_format.initialize(self, shared)
1064
1376
 
1065
 
    def create_workingtree(self, revision_id=None):
 
1377
    def destroy_repository(self):
 
1378
        """See BzrDir.destroy_repository."""
 
1379
        self.transport.delete_tree('repository')
 
1380
 
 
1381
    def create_workingtree(self, revision_id=None, from_branch=None,
 
1382
                           accelerator_tree=None, hardlink=False):
1066
1383
        """See BzrDir.create_workingtree."""
1067
 
        from bzrlib.workingtree import WorkingTreeFormat
1068
 
        return self._format.workingtree_format.initialize(self, revision_id)
 
1384
        return self._format.workingtree_format.initialize(
 
1385
            self, revision_id, from_branch=from_branch,
 
1386
            accelerator_tree=accelerator_tree, hardlink=hardlink)
1069
1387
 
1070
1388
    def destroy_workingtree(self):
1071
1389
        """See BzrDir.destroy_workingtree."""
1072
1390
        wt = self.open_workingtree(recommend_upgrade=False)
1073
1391
        repository = wt.branch.repository
1074
1392
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1075
 
        wt.revert([], old_tree=empty)
 
1393
        wt.revert(old_tree=empty)
1076
1394
        self.destroy_workingtree_metadata()
1077
1395
 
1078
1396
    def destroy_workingtree_metadata(self):
1195
1513
            basedir=self.root_transport.base)
1196
1514
        return format.open(self, _found=True)
1197
1515
 
 
1516
    def _get_config(self):
 
1517
        return config.BzrDirConfig(self.transport)
 
1518
 
1198
1519
 
1199
1520
class BzrDirFormat(object):
1200
1521
    """An encapsulation of the initialization and open routines for a format.
1204
1525
     * a format string,
1205
1526
     * an open routine.
1206
1527
 
1207
 
    Formats are placed in an dict by their format string for reference 
 
1528
    Formats are placed in a dict by their format string for reference 
1208
1529
    during bzrdir opening. These should be subclasses of BzrDirFormat
1209
1530
    for consistency.
1210
1531
 
1262
1583
        try:
1263
1584
            return klass._formats[format_string]
1264
1585
        except KeyError:
1265
 
            raise errors.UnknownFormatError(format=format_string)
 
1586
            raise errors.UnknownFormatError(format=format_string, kind='bzrdir')
1266
1587
 
1267
1588
    @classmethod
1268
1589
    def get_default_format(klass):
1291
1612
        """
1292
1613
        raise NotImplementedError(self.get_converter)
1293
1614
 
1294
 
    def initialize(self, url):
 
1615
    def initialize(self, url, possible_transports=None):
1295
1616
        """Create a bzr control dir at this url and return an opened copy.
1296
1617
        
1297
1618
        Subclasses should typically override initialize_on_transport
1298
1619
        instead of this method.
1299
1620
        """
1300
 
        return self.initialize_on_transport(get_transport(url))
 
1621
        return self.initialize_on_transport(get_transport(url,
 
1622
                                                          possible_transports))
1301
1623
 
1302
1624
    def initialize_on_transport(self, transport):
1303
1625
        """Initialize a new bzrdir in the base directory of a Transport."""
1309
1631
                                      # FIXME: RBC 20060121 don't peek under
1310
1632
                                      # the covers
1311
1633
                                      mode=temp_control._dir_mode)
 
1634
        if sys.platform == 'win32' and isinstance(transport, LocalTransport):
 
1635
            win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1312
1636
        file_mode = temp_control._file_mode
1313
1637
        del temp_control
1314
 
        mutter('created control directory in ' + transport.base)
1315
 
        control = transport.clone('.bzr')
1316
 
        utf8_files = [('README', 
1317
 
                       "This is a Bazaar-NG control directory.\n"
1318
 
                       "Do not change any files in this directory.\n"),
 
1638
        bzrdir_transport = transport.clone('.bzr')
 
1639
        utf8_files = [('README',
 
1640
                       "This is a Bazaar control directory.\n"
 
1641
                       "Do not change any files in this directory.\n"
 
1642
                       "See http://bazaar-vcs.org/ for more information about Bazaar.\n"),
1319
1643
                      ('branch-format', self.get_format_string()),
1320
1644
                      ]
1321
1645
        # NB: no need to escape relative paths that are url safe.
1322
 
        control_files = lockable_files.LockableFiles(control,
1323
 
                            self._lock_file_name, self._lock_class)
 
1646
        control_files = lockable_files.LockableFiles(bzrdir_transport,
 
1647
            self._lock_file_name, self._lock_class)
1324
1648
        control_files.create_lock()
1325
1649
        control_files.lock_write()
1326
1650
        try:
1327
 
            for file, content in utf8_files:
1328
 
                control_files.put_utf8(file, content)
 
1651
            for (filename, content) in utf8_files:
 
1652
                bzrdir_transport.put_bytes(filename, content,
 
1653
                    mode=file_mode)
1329
1654
        finally:
1330
1655
            control_files.unlock()
1331
1656
        return self.open(transport, _found=True)
1410
1735
        klass._control_server_formats.append(format)
1411
1736
 
1412
1737
    @classmethod
1413
 
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1414
 
    def set_default_format(klass, format):
1415
 
        klass._set_default_format(format)
1416
 
 
1417
 
    @classmethod
1418
1738
    def _set_default_format(klass, format):
1419
1739
        """Set default format (for testing behavior of defaults only)"""
1420
1740
        klass._default_format = format
1421
1741
 
1422
1742
    def __str__(self):
1423
 
        return self.get_format_string()[:-1]
 
1743
        # Trim the newline
 
1744
        return self.get_format_string().rstrip()
1424
1745
 
1425
1746
    @classmethod
1426
1747
    def unregister_format(klass, format):
1427
 
        assert klass._formats[format.get_format_string()] is format
1428
1748
        del klass._formats[format.get_format_string()]
1429
1749
 
1430
1750
    @classmethod
1530
1850
            except errors.NotLocalUrl:
1531
1851
                # Even though we can't access the working tree, we need to
1532
1852
                # create its control files.
1533
 
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
 
1853
                WorkingTreeFormat2()._stub_initialize_remote(branch)
1534
1854
        return result
1535
1855
 
1536
1856
    def _open(self, transport):
1589
1909
            except errors.NotLocalUrl:
1590
1910
                # Even though we can't access the working tree, we need to
1591
1911
                # create its control files.
1592
 
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
 
1912
                WorkingTreeFormat2()._stub_initialize_remote(branch)
1593
1913
        return result
1594
1914
 
1595
1915
    def _open(self, transport):
1670
1990
        return RepositoryFormat.get_default_format()
1671
1991
 
1672
1992
    def __set_repository_format(self, value):
1673
 
        """Allow changint the repository format for metadir formats."""
 
1993
        """Allow changing the repository format for metadir formats."""
1674
1994
        self._repository_format = value
1675
1995
 
1676
1996
    repository_format = property(__return_repository_format, __set_repository_format)
1700
2020
BzrDirFormat._default_format = __default_format
1701
2021
 
1702
2022
 
1703
 
class BzrDirTestProviderAdapter(object):
1704
 
    """A tool to generate a suite testing multiple bzrdir formats at once.
1705
 
 
1706
 
    This is done by copying the test once for each transport and injecting
1707
 
    the transport_server, transport_readonly_server, and bzrdir_format
1708
 
    classes into each copy. Each copy is also given a new id() to make it
1709
 
    easy to identify.
1710
 
    """
1711
 
 
1712
 
    def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1713
 
        formats):
1714
 
        """Create an object to adapt tests.
1715
 
 
1716
 
        :param vfs_server: A factory to create a Transport Server which has
1717
 
            all the VFS methods working, and is writable.
1718
 
        """
1719
 
        self._vfs_factory = vfs_factory
1720
 
        self._transport_server = transport_server
1721
 
        self._transport_readonly_server = transport_readonly_server
1722
 
        self._formats = formats
1723
 
    
1724
 
    def adapt(self, test):
1725
 
        result = unittest.TestSuite()
1726
 
        for format in self._formats:
1727
 
            new_test = deepcopy(test)
1728
 
            new_test.vfs_transport_factory = self._vfs_factory
1729
 
            new_test.transport_server = self._transport_server
1730
 
            new_test.transport_readonly_server = self._transport_readonly_server
1731
 
            new_test.bzrdir_format = format
1732
 
            def make_new_test_id():
1733
 
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1734
 
                return lambda: new_id
1735
 
            new_test.id = make_new_test_id()
1736
 
            result.addTest(new_test)
1737
 
        return result
1738
 
 
1739
 
 
1740
2023
class Converter(object):
1741
2024
    """Converts a disk format object from one format to another."""
1742
2025
 
1813
2096
        self.pb.note('  %6d revisions not present', len(self.absent_revisions))
1814
2097
        self.pb.note('  %6d texts', self.text_count)
1815
2098
        self._cleanup_spare_files_after_format4()
1816
 
        self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
 
2099
        self.branch._transport.put_bytes(
 
2100
            'branch-format',
 
2101
            BzrDirFormat5().get_format_string(),
 
2102
            mode=self.bzrdir._get_file_mode())
1817
2103
 
1818
2104
    def _cleanup_spare_files_after_format4(self):
1819
2105
        # FIXME working tree upgrade foo.
1828
2114
 
1829
2115
    def _convert_working_inv(self):
1830
2116
        inv = xml4.serializer_v4.read_inventory(
1831
 
                    self.branch.control_files.get('inventory'))
1832
 
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1833
 
        # FIXME inventory is a working tree change.
1834
 
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
 
2117
                self.branch._transport.get('inventory'))
 
2118
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
 
2119
        self.branch._transport.put_bytes('inventory', new_inv_xml,
 
2120
            mode=self.bzrdir._get_file_mode())
1835
2121
 
1836
2122
    def _write_all_weaves(self):
1837
2123
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1857
2143
        self.bzrdir.transport.mkdir('revision-store')
1858
2144
        revision_transport = self.bzrdir.transport.clone('revision-store')
1859
2145
        # TODO permissions
1860
 
        _revision_store = TextRevisionStore(TextStore(revision_transport,
1861
 
                                                      prefixed=False,
1862
 
                                                      compressed=True))
 
2146
        from bzrlib.xml5 import serializer_v5
 
2147
        from bzrlib.repofmt.weaverepo import RevisionTextStore
 
2148
        revision_store = RevisionTextStore(revision_transport,
 
2149
            serializer_v5, False, versionedfile.PrefixMapper(),
 
2150
            lambda:True, lambda:True)
1863
2151
        try:
1864
 
            transaction = WriteTransaction()
1865
2152
            for i, rev_id in enumerate(self.converted_revs):
1866
2153
                self.pb.update('write revision', i, len(self.converted_revs))
1867
 
                _revision_store.add_revision(self.revisions[rev_id], transaction)
 
2154
                text = serializer_v5.write_revision_to_string(
 
2155
                    self.revisions[rev_id])
 
2156
                key = (rev_id,)
 
2157
                revision_store.add_lines(key, None, osutils.split_lines(text))
1868
2158
        finally:
1869
2159
            self.pb.clear()
1870
2160
            
1883
2173
                         rev_id)
1884
2174
            self.absent_revisions.add(rev_id)
1885
2175
        else:
1886
 
            rev = self.branch.repository._revision_store.get_revision(rev_id,
1887
 
                self.branch.repository.get_transaction())
 
2176
            rev = self.branch.repository.get_revision(rev_id)
1888
2177
            for parent_id in rev.parent_ids:
1889
2178
                self.known_revisions.add(parent_id)
1890
2179
                self.to_read.append(parent_id)
1891
2180
            self.revisions[rev_id] = rev
1892
2181
 
1893
2182
    def _load_old_inventory(self, rev_id):
1894
 
        assert rev_id not in self.converted_revs
1895
2183
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1896
2184
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1897
2185
        inv.revision_id = rev_id
1898
2186
        rev = self.revisions[rev_id]
1899
 
        if rev.inventory_sha1:
1900
 
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1901
 
                'inventory sha mismatch for {%s}' % rev_id
1902
2187
        return inv
1903
2188
 
1904
2189
    def _load_updated_inventory(self, rev_id):
1905
 
        assert rev_id in self.converted_revs
1906
2190
        inv_xml = self.inv_weave.get_text(rev_id)
1907
 
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
2191
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml, rev_id)
1908
2192
        return inv
1909
2193
 
1910
2194
    def _convert_one_rev(self, rev_id):
1914
2198
        present_parents = [p for p in rev.parent_ids
1915
2199
                           if p not in self.absent_revisions]
1916
2200
        self._convert_revision_contents(rev, inv, present_parents)
1917
 
        self._store_new_weave(rev, inv, present_parents)
 
2201
        self._store_new_inv(rev, inv, present_parents)
1918
2202
        self.converted_revs.add(rev_id)
1919
2203
 
1920
 
    def _store_new_weave(self, rev, inv, present_parents):
1921
 
        # the XML is now updated with text versions
1922
 
        if __debug__:
1923
 
            entries = inv.iter_entries()
1924
 
            entries.next()
1925
 
            for path, ie in entries:
1926
 
                assert getattr(ie, 'revision', None) is not None, \
1927
 
                    'no revision on {%s} in {%s}' % \
1928
 
                    (file_id, rev.revision_id)
 
2204
    def _store_new_inv(self, rev, inv, present_parents):
1929
2205
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1930
2206
        new_inv_sha1 = sha_string(new_inv_xml)
1931
 
        self.inv_weave.add_lines(rev.revision_id, 
 
2207
        self.inv_weave.add_lines(rev.revision_id,
1932
2208
                                 present_parents,
1933
2209
                                 new_inv_xml.splitlines(True))
1934
2210
        rev.inventory_sha1 = new_inv_sha1
1959
2235
            w = Weave(file_id)
1960
2236
            self.text_weaves[file_id] = w
1961
2237
        text_changed = False
1962
 
        previous_entries = ie.find_previous_heads(parent_invs,
1963
 
                                                  None,
1964
 
                                                  None,
1965
 
                                                  entry_vf=w)
1966
 
        for old_revision in previous_entries:
1967
 
                # if this fails, its a ghost ?
1968
 
                assert old_revision in self.converted_revs, \
1969
 
                    "Revision {%s} not in converted_revs" % old_revision
 
2238
        parent_candiate_entries = ie.parent_candidates(parent_invs)
 
2239
        heads = graph.Graph(self).heads(parent_candiate_entries.keys())
 
2240
        # XXX: Note that this is unordered - and this is tolerable because 
 
2241
        # the previous code was also unordered.
 
2242
        previous_entries = dict((head, parent_candiate_entries[head]) for head
 
2243
            in heads)
1970
2244
        self.snapshot_ie(previous_entries, ie, w, rev_id)
1971
2245
        del ie.text_id
1972
 
        assert getattr(ie, 'revision', None) is not None
 
2246
 
 
2247
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
 
2248
    def get_parents(self, revision_ids):
 
2249
        for revision_id in revision_ids:
 
2250
            yield self.revisions[revision_id].parent_ids
 
2251
 
 
2252
    def get_parent_map(self, revision_ids):
 
2253
        """See graph._StackedParentsProvider.get_parent_map"""
 
2254
        return dict((revision_id, self.revisions[revision_id])
 
2255
                    for revision_id in revision_ids
 
2256
                     if revision_id in self.revisions)
1973
2257
 
1974
2258
    def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1975
2259
        # TODO: convert this logic, which is ~= snapshot to
1985
2269
                ie.revision = previous_ie.revision
1986
2270
                return
1987
2271
        if ie.has_text():
1988
 
            text = self.branch.repository.text_store.get(ie.text_id)
 
2272
            text = self.branch.repository._text_store.get(ie.text_id)
1989
2273
            file_lines = text.readlines()
1990
 
            assert sha_strings(file_lines) == ie.text_sha1
1991
 
            assert sum(map(len, file_lines)) == ie.text_size
1992
2274
            w.add_lines(rev_id, previous_revisions, file_lines)
1993
2275
            self.text_count += 1
1994
2276
        else:
2041
2323
                if (filename.endswith(".weave") or
2042
2324
                    filename.endswith(".gz") or
2043
2325
                    filename.endswith(".sig")):
2044
 
                    file_id = os.path.splitext(filename)[0]
 
2326
                    file_id, suffix = os.path.splitext(filename)
2045
2327
                else:
2046
2328
                    file_id = filename
2047
 
                prefix_dir = store.hash_prefix(file_id)
 
2329
                    suffix = ''
 
2330
                new_name = store._mapper.map((file_id,)) + suffix
2048
2331
                # FIXME keep track of the dirs made RBC 20060121
2049
2332
                try:
2050
 
                    store_transport.move(filename, prefix_dir + '/' + filename)
 
2333
                    store_transport.move(filename, new_name)
2051
2334
                except errors.NoSuchFile: # catches missing dirs strangely enough
2052
 
                    store_transport.mkdir(prefix_dir)
2053
 
                    store_transport.move(filename, prefix_dir + '/' + filename)
2054
 
        self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
 
2335
                    store_transport.mkdir(osutils.dirname(new_name))
 
2336
                    store_transport.move(filename, new_name)
 
2337
        self.bzrdir.transport.put_bytes(
 
2338
            'branch-format',
 
2339
            BzrDirFormat6().get_format_string(),
 
2340
            mode=self.bzrdir._get_file_mode())
2055
2341
 
2056
2342
 
2057
2343
class ConvertBzrDir6ToMeta(Converter):
2066
2352
        self.count = 0
2067
2353
        self.total = 20 # the steps we know about
2068
2354
        self.garbage_inventories = []
 
2355
        self.dir_mode = self.bzrdir._get_dir_mode()
 
2356
        self.file_mode = self.bzrdir._get_file_mode()
2069
2357
 
2070
2358
        self.pb.note('starting upgrade from format 6 to metadir')
2071
 
        self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
 
2359
        self.bzrdir.transport.put_bytes(
 
2360
                'branch-format',
 
2361
                "Converting to format 6",
 
2362
                mode=self.file_mode)
2072
2363
        # its faster to move specific files around than to open and use the apis...
2073
2364
        # first off, nuke ancestry.weave, it was never used.
2074
2365
        try:
2084
2375
            if name.startswith('basis-inventory.'):
2085
2376
                self.garbage_inventories.append(name)
2086
2377
        # create new directories for repository, working tree and branch
2087
 
        self.dir_mode = self.bzrdir._control_files._dir_mode
2088
 
        self.file_mode = self.bzrdir._control_files._file_mode
2089
2378
        repository_names = [('inventory.weave', True),
2090
2379
                            ('revision-store', True),
2091
2380
                            ('weaves', True)]
2139
2428
            for entry in checkout_files:
2140
2429
                self.move_entry('checkout', entry)
2141
2430
            if last_revision is not None:
2142
 
                self.bzrdir._control_files.put_utf8(
 
2431
                self.bzrdir.transport.put_bytes(
2143
2432
                    'checkout/last-revision', last_revision)
2144
 
        self.bzrdir._control_files.put_utf8(
2145
 
            'branch-format', BzrDirMetaFormat1().get_format_string())
 
2433
        self.bzrdir.transport.put_bytes(
 
2434
            'branch-format',
 
2435
            BzrDirMetaFormat1().get_format_string(),
 
2436
            mode=self.file_mode)
2146
2437
        return BzrDir.open(self.bzrdir.root_transport.base)
2147
2438
 
2148
2439
    def make_lock(self, name):
2166
2457
                raise
2167
2458
 
2168
2459
    def put_format(self, dirname, format):
2169
 
        self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
 
2460
        self.bzrdir.transport.put_bytes('%s/format' % dirname,
 
2461
            format.get_format_string(),
 
2462
            self.file_mode)
2170
2463
 
2171
2464
 
2172
2465
class ConvertMetaToMeta(Converter):
2202
2495
            pass
2203
2496
        else:
2204
2497
            # TODO: conversions of Branch and Tree should be done by
2205
 
            # InterXFormat lookups
 
2498
            # InterXFormat lookups/some sort of registry.
2206
2499
            # Avoid circular imports
2207
2500
            from bzrlib import branch as _mod_branch
2208
 
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2209
 
                self.target_format.get_branch_format().__class__ is
2210
 
                _mod_branch.BzrBranchFormat6):
2211
 
                branch_converter = _mod_branch.Converter5to6()
 
2501
            old = branch._format.__class__
 
2502
            new = self.target_format.get_branch_format().__class__
 
2503
            while old != new:
 
2504
                if (old == _mod_branch.BzrBranchFormat5 and
 
2505
                    new in (_mod_branch.BzrBranchFormat6,
 
2506
                        _mod_branch.BzrBranchFormat7)):
 
2507
                    branch_converter = _mod_branch.Converter5to6()
 
2508
                elif (old == _mod_branch.BzrBranchFormat6 and
 
2509
                    new == _mod_branch.BzrBranchFormat7):
 
2510
                    branch_converter = _mod_branch.Converter6to7()
 
2511
                else:
 
2512
                    raise errors.BadConversionTarget("No converter", new)
2212
2513
                branch_converter.convert(branch)
 
2514
                branch = self.bzrdir.open_branch()
 
2515
                old = branch._format.__class__
2213
2516
        try:
2214
2517
            tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2215
2518
        except (errors.NoWorkingTree, errors.NotLocalUrl):
2239
2542
    def probe_transport(klass, transport):
2240
2543
        """Return a RemoteBzrDirFormat object if it looks possible."""
2241
2544
        try:
2242
 
            client = transport.get_smart_client()
 
2545
            medium = transport.get_smart_medium()
2243
2546
        except (NotImplementedError, AttributeError,
2244
 
                errors.TransportNotPossible):
 
2547
                errors.TransportNotPossible, errors.NoSmartMedium,
 
2548
                errors.SmartProtocolError):
2245
2549
            # no smart server, so not a branch for this format type.
2246
2550
            raise errors.NotBranchError(path=transport.base)
2247
2551
        else:
2248
 
            # Send a 'hello' request in protocol version one, and decline to
2249
 
            # open it if the server doesn't support our required version (2) so
2250
 
            # that the VFS-based transport will do it.
2251
 
            request = client.get_request()
2252
 
            smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2253
 
            server_version = smart_protocol.query_version()
2254
 
            if server_version != 2:
2255
 
                raise errors.NotBranchError(path=transport.base)
 
2552
            # Decline to open it if the server doesn't support our required
 
2553
            # version (3) so that the VFS-based transport will do it.
 
2554
            if medium.should_probe():
 
2555
                try:
 
2556
                    server_version = medium.protocol_version()
 
2557
                except errors.SmartProtocolError:
 
2558
                    # Apparently there's no usable smart server there, even though
 
2559
                    # the medium supports the smart protocol.
 
2560
                    raise errors.NotBranchError(path=transport.base)
 
2561
                if server_version != '2':
 
2562
                    raise errors.NotBranchError(path=transport.base)
2256
2563
            return klass()
2257
2564
 
2258
2565
    def initialize_on_transport(self, transport):
2259
2566
        try:
2260
2567
            # hand off the request to the smart server
2261
 
            medium = transport.get_smart_medium()
 
2568
            client_medium = transport.get_smart_medium()
2262
2569
        except errors.NoSmartMedium:
2263
2570
            # TODO: lookup the local format from a server hint.
2264
2571
            local_dir_format = BzrDirMetaFormat1()
2265
2572
            return local_dir_format.initialize_on_transport(transport)
2266
 
        client = _SmartClient(medium)
 
2573
        client = _SmartClient(client_medium)
2267
2574
        path = client.remote_path_from_transport(transport)
2268
 
        response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
2269
 
        assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
 
2575
        response = client.call('BzrDirFormat.initialize', path)
 
2576
        if response[0] != 'ok':
 
2577
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
2270
2578
        return remote.RemoteBzrDir(transport)
2271
2579
 
2272
2580
    def _open(self, transport):
2283
2591
 
2284
2592
class BzrDirFormatInfo(object):
2285
2593
 
2286
 
    def __init__(self, native, deprecated, hidden):
 
2594
    def __init__(self, native, deprecated, hidden, experimental):
2287
2595
        self.deprecated = deprecated
2288
2596
        self.native = native
2289
2597
        self.hidden = hidden
 
2598
        self.experimental = experimental
2290
2599
 
2291
2600
 
2292
2601
class BzrDirFormatRegistry(registry.Registry):
2296
2605
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
2297
2606
    """
2298
2607
 
 
2608
    def __init__(self):
 
2609
        """Create a BzrDirFormatRegistry."""
 
2610
        self._aliases = set()
 
2611
        super(BzrDirFormatRegistry, self).__init__()
 
2612
 
 
2613
    def aliases(self):
 
2614
        """Return a set of the format names which are aliases."""
 
2615
        return frozenset(self._aliases)
 
2616
 
2299
2617
    def register_metadir(self, key,
2300
2618
             repository_format, help, native=True, deprecated=False,
2301
2619
             branch_format=None,
2302
2620
             tree_format=None,
2303
 
             hidden=False):
 
2621
             hidden=False,
 
2622
             experimental=False,
 
2623
             alias=False):
2304
2624
        """Register a metadir subformat.
2305
2625
 
2306
2626
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2338
2658
            if repository_format is not None:
2339
2659
                bd.repository_format = _load(repository_format)
2340
2660
            return bd
2341
 
        self.register(key, helper, help, native, deprecated, hidden)
 
2661
        self.register(key, helper, help, native, deprecated, hidden,
 
2662
            experimental, alias)
2342
2663
 
2343
2664
    def register(self, key, factory, help, native=True, deprecated=False,
2344
 
                 hidden=False):
 
2665
                 hidden=False, experimental=False, alias=False):
2345
2666
        """Register a BzrDirFormat factory.
2346
2667
        
2347
2668
        The factory must be a callable that takes one parameter: the key.
2350
2671
        This function mainly exists to prevent the info object from being
2351
2672
        supplied directly.
2352
2673
        """
2353
 
        registry.Registry.register(self, key, factory, help, 
2354
 
            BzrDirFormatInfo(native, deprecated, hidden))
 
2674
        registry.Registry.register(self, key, factory, help,
 
2675
            BzrDirFormatInfo(native, deprecated, hidden, experimental))
 
2676
        if alias:
 
2677
            self._aliases.add(key)
2355
2678
 
2356
2679
    def register_lazy(self, key, module_name, member_name, help, native=True,
2357
 
                      deprecated=False, hidden=False):
2358
 
        registry.Registry.register_lazy(self, key, module_name, member_name, 
2359
 
            help, BzrDirFormatInfo(native, deprecated, hidden))
 
2680
        deprecated=False, hidden=False, experimental=False, alias=False):
 
2681
        registry.Registry.register_lazy(self, key, module_name, member_name,
 
2682
            help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
 
2683
        if alias:
 
2684
            self._aliases.add(key)
2360
2685
 
2361
2686
    def set_default(self, key):
2362
2687
        """Set the 'default' key to be a clone of the supplied key.
2363
2688
        
2364
2689
        This method must be called once and only once.
2365
2690
        """
2366
 
        registry.Registry.register(self, 'default', self.get(key), 
 
2691
        registry.Registry.register(self, 'default', self.get(key),
2367
2692
            self.get_help(key), info=self.get_info(key))
 
2693
        self._aliases.add('default')
2368
2694
 
2369
2695
    def set_default_repository(self, key):
2370
2696
        """Set the FormatRegistry default and Repository default.
2376
2702
            self.remove('default')
2377
2703
        self.set_default(key)
2378
2704
        format = self.get('default')()
2379
 
        assert isinstance(format, BzrDirMetaFormat1)
2380
2705
 
2381
2706
    def make_bzrdir(self, key):
2382
2707
        return self.get(key)()
2383
2708
 
2384
2709
    def help_topic(self, topic):
2385
2710
        output = textwrap.dedent("""\
2386
 
            Bazaar directory formats
2387
 
            ------------------------
2388
 
 
2389
2711
            These formats can be used for creating branches, working trees, and
2390
2712
            repositories.
2391
2713
 
2392
2714
            """)
 
2715
        default_realkey = None
2393
2716
        default_help = self.get_help('default')
2394
2717
        help_pairs = []
2395
2718
        for key in self.keys():
2404
2727
        def wrapped(key, help, info):
2405
2728
            if info.native:
2406
2729
                help = '(native) ' + help
2407
 
            return '  %s:\n%s\n\n' % (key, 
 
2730
            return ':%s:\n%s\n\n' % (key, 
2408
2731
                    textwrap.fill(help, initial_indent='    ', 
2409
2732
                    subsequent_indent='    '))
2410
 
        output += wrapped('%s/default' % default_realkey, default_help,
2411
 
                          self.get_info('default'))
 
2733
        if default_realkey is not None:
 
2734
            output += wrapped(default_realkey, '(default) %s' % default_help,
 
2735
                              self.get_info('default'))
2412
2736
        deprecated_pairs = []
 
2737
        experimental_pairs = []
2413
2738
        for key, help in help_pairs:
2414
2739
            info = self.get_info(key)
2415
2740
            if info.hidden:
2416
2741
                continue
2417
2742
            elif info.deprecated:
2418
2743
                deprecated_pairs.append((key, help))
 
2744
            elif info.experimental:
 
2745
                experimental_pairs.append((key, help))
2419
2746
            else:
2420
2747
                output += wrapped(key, help, info)
 
2748
        if len(experimental_pairs) > 0:
 
2749
            output += "Experimental formats are shown below.\n\n"
 
2750
            for key, help in experimental_pairs:
 
2751
                info = self.get_info(key)
 
2752
                output += wrapped(key, help, info)
2421
2753
        if len(deprecated_pairs) > 0:
2422
 
            output += "Deprecated formats\n------------------\n\n"
 
2754
            output += "Deprecated formats are shown below.\n\n"
2423
2755
            for key, help in deprecated_pairs:
2424
2756
                info = self.get_info(key)
2425
2757
                output += wrapped(key, help, info)
2427
2759
        return output
2428
2760
 
2429
2761
 
 
2762
class RepositoryAcquisitionPolicy(object):
 
2763
    """Abstract base class for repository acquisition policies.
 
2764
 
 
2765
    A repository acquisition policy decides how a BzrDir acquires a repository
 
2766
    for a branch that is being created.  The most basic policy decision is
 
2767
    whether to create a new repository or use an existing one.
 
2768
    """
 
2769
    def __init__(self, stack_on, stack_on_pwd, require_stacking):
 
2770
        """Constructor.
 
2771
 
 
2772
        :param stack_on: A location to stack on
 
2773
        :param stack_on_pwd: If stack_on is relative, the location it is
 
2774
            relative to.
 
2775
        :param require_stacking: If True, it is a failure to not stack.
 
2776
        """
 
2777
        self._stack_on = stack_on
 
2778
        self._stack_on_pwd = stack_on_pwd
 
2779
        self._require_stacking = require_stacking
 
2780
 
 
2781
    def configure_branch(self, branch):
 
2782
        """Apply any configuration data from this policy to the branch.
 
2783
 
 
2784
        Default implementation sets repository stacking.
 
2785
        """
 
2786
        if self._stack_on is None:
 
2787
            return
 
2788
        if self._stack_on_pwd is None:
 
2789
            stack_on = self._stack_on
 
2790
        else:
 
2791
            try:
 
2792
                stack_on = urlutils.rebase_url(self._stack_on,
 
2793
                    self._stack_on_pwd,
 
2794
                    branch.bzrdir.root_transport.base)
 
2795
            except errors.InvalidRebaseURLs:
 
2796
                stack_on = self._get_full_stack_on()
 
2797
        try:
 
2798
            branch.set_stacked_on_url(stack_on)
 
2799
        except errors.UnstackableBranchFormat:
 
2800
            if self._require_stacking:
 
2801
                raise
 
2802
 
 
2803
    def _get_full_stack_on(self):
 
2804
        """Get a fully-qualified URL for the stack_on location."""
 
2805
        if self._stack_on is None:
 
2806
            return None
 
2807
        if self._stack_on_pwd is None:
 
2808
            return self._stack_on
 
2809
        else:
 
2810
            return urlutils.join(self._stack_on_pwd, self._stack_on)
 
2811
 
 
2812
    def _add_fallback(self, repository):
 
2813
        """Add a fallback to the supplied repository, if stacking is set."""
 
2814
        stack_on = self._get_full_stack_on()
 
2815
        if stack_on is None:
 
2816
            return
 
2817
        stacked_dir = BzrDir.open(stack_on)
 
2818
        try:
 
2819
            stacked_repo = stacked_dir.open_branch().repository
 
2820
        except errors.NotBranchError:
 
2821
            stacked_repo = stacked_dir.open_repository()
 
2822
        try:
 
2823
            repository.add_fallback_repository(stacked_repo)
 
2824
        except errors.UnstackableRepositoryFormat:
 
2825
            if self._require_stacking:
 
2826
                raise
 
2827
 
 
2828
    def acquire_repository(self, make_working_trees=None, shared=False):
 
2829
        """Acquire a repository for this bzrdir.
 
2830
 
 
2831
        Implementations may create a new repository or use a pre-exising
 
2832
        repository.
 
2833
        :param make_working_trees: If creating a repository, set
 
2834
            make_working_trees to this value (if non-None)
 
2835
        :param shared: If creating a repository, make it shared if True
 
2836
        :return: A repository
 
2837
        """
 
2838
        raise NotImplemented(RepositoryAcquisitionPolicy.acquire_repository)
 
2839
 
 
2840
 
 
2841
class CreateRepository(RepositoryAcquisitionPolicy):
 
2842
    """A policy of creating a new repository"""
 
2843
 
 
2844
    def __init__(self, bzrdir, stack_on=None, stack_on_pwd=None,
 
2845
                 require_stacking=False):
 
2846
        """
 
2847
        Constructor.
 
2848
        :param bzrdir: The bzrdir to create the repository on.
 
2849
        :param stack_on: A location to stack on
 
2850
        :param stack_on_pwd: If stack_on is relative, the location it is
 
2851
            relative to.
 
2852
        """
 
2853
        RepositoryAcquisitionPolicy.__init__(self, stack_on, stack_on_pwd,
 
2854
                                             require_stacking)
 
2855
        self._bzrdir = bzrdir
 
2856
 
 
2857
    def acquire_repository(self, make_working_trees=None, shared=False):
 
2858
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
 
2859
 
 
2860
        Creates the desired repository in the bzrdir we already have.
 
2861
        """
 
2862
        repository = self._bzrdir.create_repository(shared=shared)
 
2863
        self._add_fallback(repository)
 
2864
        if make_working_trees is not None:
 
2865
            repository.set_make_working_trees(make_working_trees)
 
2866
        return repository
 
2867
 
 
2868
 
 
2869
class UseExistingRepository(RepositoryAcquisitionPolicy):
 
2870
    """A policy of reusing an existing repository"""
 
2871
 
 
2872
    def __init__(self, repository, stack_on=None, stack_on_pwd=None,
 
2873
                 require_stacking=False):
 
2874
        """Constructor.
 
2875
 
 
2876
        :param repository: The repository to use.
 
2877
        :param stack_on: A location to stack on
 
2878
        :param stack_on_pwd: If stack_on is relative, the location it is
 
2879
            relative to.
 
2880
        """
 
2881
        RepositoryAcquisitionPolicy.__init__(self, stack_on, stack_on_pwd,
 
2882
                                             require_stacking)
 
2883
        self._repository = repository
 
2884
 
 
2885
    def acquire_repository(self, make_working_trees=None, shared=False):
 
2886
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
 
2887
 
 
2888
        Returns an existing repository to use
 
2889
        """
 
2890
        self._add_fallback(self._repository)
 
2891
        return self._repository
 
2892
 
 
2893
 
2430
2894
format_registry = BzrDirFormatRegistry()
2431
2895
format_registry.register('weave', BzrDirFormat6,
2432
2896
    'Pre-0.8 format.  Slower than knit and does not'
2460
2924
    branch_format='bzrlib.branch.BzrBranchFormat6',
2461
2925
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2462
2926
    )
 
2927
format_registry.register_metadir('rich-root',
 
2928
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit4',
 
2929
    help='New in 1.0.  Better handling of tree roots.  Incompatible with'
 
2930
        ' bzr < 1.0',
 
2931
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2932
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2933
    )
2463
2934
format_registry.register_metadir('dirstate-with-subtree',
2464
2935
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2465
2936
    help='New in 0.15: Fast local operations and improved scaling for '
2467
2938
        'bzr branches. Incompatible with bzr < 0.15.',
2468
2939
    branch_format='bzrlib.branch.BzrBranchFormat6',
2469
2940
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2470
 
    hidden=True,
2471
 
    )
2472
 
format_registry.set_default('dirstate')
 
2941
    experimental=True,
 
2942
    hidden=True,
 
2943
    )
 
2944
format_registry.register_metadir('pack-0.92',
 
2945
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
 
2946
    help='New in 0.92: Pack-based format with data compatible with '
 
2947
        'dirstate-tags format repositories. Interoperates with '
 
2948
        'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
 
2949
        'Previously called knitpack-experimental.  '
 
2950
        'For more information, see '
 
2951
        'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
 
2952
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2953
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2954
    )
 
2955
format_registry.register_metadir('pack-0.92-subtree',
 
2956
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
 
2957
    help='New in 0.92: Pack-based format with data compatible with '
 
2958
        'dirstate-with-subtree format repositories. Interoperates with '
 
2959
        'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
 
2960
        'Previously called knitpack-experimental.  '
 
2961
        'For more information, see '
 
2962
        'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
 
2963
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2964
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2965
    hidden=True,
 
2966
    experimental=True,
 
2967
    )
 
2968
format_registry.register_metadir('rich-root-pack',
 
2969
    'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
 
2970
    help='New in 1.0: Pack-based format with data compatible with '
 
2971
        'rich-root format repositories. Incompatible with'
 
2972
        ' bzr < 1.0',
 
2973
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2974
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2975
    )
 
2976
# The following two formats should always just be aliases.
 
2977
format_registry.register_metadir('development',
 
2978
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1',
 
2979
    help='Current development format. Can convert data to and from pack-0.92 '
 
2980
        '(and anything compatible with pack-0.92) format repositories. '
 
2981
        'Repositories and branches in this format can only be read by bzr.dev. '
 
2982
        'Please read '
 
2983
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
2984
        'before use.',
 
2985
    branch_format='bzrlib.branch.BzrBranchFormat7',
 
2986
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2987
    experimental=True,
 
2988
    alias=True,
 
2989
    )
 
2990
format_registry.register_metadir('development-subtree',
 
2991
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1Subtree',
 
2992
    help='Current development format, subtree variant. Can convert data to and '
 
2993
        'from pack-0.92-subtree (and anything compatible with '
 
2994
        'pack-0.92-subtree) format repositories. Repositories and branches in '
 
2995
        'this format can only be read by bzr.dev. Please read '
 
2996
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
2997
        'before use.',
 
2998
    branch_format='bzrlib.branch.BzrBranchFormat7',
 
2999
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
3000
    experimental=True,
 
3001
    alias=True,
 
3002
    )
 
3003
# And the development formats which the will have aliased one of follow:
 
3004
format_registry.register_metadir('development0',
 
3005
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
 
3006
    help='Trivial rename of pack-0.92 to provide a development format. '
 
3007
        'Please read '
 
3008
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
3009
        'before use.',
 
3010
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
3011
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
3012
    hidden=True,
 
3013
    experimental=True,
 
3014
    )
 
3015
format_registry.register_metadir('development0-subtree',
 
3016
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
 
3017
    help='Trivial rename of pack-0.92-subtree to provide a development format. '
 
3018
        'Please read '
 
3019
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
3020
        'before use.',
 
3021
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
3022
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
3023
    hidden=True,
 
3024
    experimental=True,
 
3025
    )
 
3026
format_registry.register_metadir('development1',
 
3027
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1',
 
3028
    help='A branch and pack based repository that supports stacking. '
 
3029
        'Please read '
 
3030
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
3031
        'before use.',
 
3032
    branch_format='bzrlib.branch.BzrBranchFormat7',
 
3033
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
3034
    hidden=True,
 
3035
    experimental=True,
 
3036
    )
 
3037
format_registry.register_metadir('development1-subtree',
 
3038
    'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1Subtree',
 
3039
    help='A branch and pack based repository that supports stacking. '
 
3040
        'Please read '
 
3041
        'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
 
3042
        'before use.',
 
3043
    branch_format='bzrlib.branch.BzrBranchFormat7',
 
3044
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
3045
    hidden=True,
 
3046
    experimental=True,
 
3047
    )
 
3048
# The current format that is made on 'bzr init'.
 
3049
format_registry.set_default('pack-0.92')