~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Robert Collins
  • Date: 2007-03-08 04:06:06 UTC
  • mfrom: (2323.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 2442.
  • Revision ID: robertc@robertcollins.net-20070308040606-84gsniv56huiyjt4
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
20
directories.
21
21
"""
22
22
 
 
23
# TODO: remove unittest dependency; put that stuff inside the test suite
 
24
 
 
25
# TODO: The Format probe_transport seems a bit redundant with just trying to
 
26
# open the bzrdir. -- mbp
 
27
#
 
28
# TODO: Can we move specific formats into separate modules to make this file
 
29
# smaller?
 
30
 
 
31
from cStringIO import StringIO
 
32
import os
 
33
import textwrap
 
34
 
 
35
from bzrlib.lazy_import import lazy_import
 
36
lazy_import(globals(), """
23
37
from copy import deepcopy
24
 
import os
25
 
from cStringIO import StringIO
26
 
from unittest import TestSuite
 
38
from stat import S_ISDIR
 
39
import unittest
27
40
 
28
41
import bzrlib
29
 
import bzrlib.errors as errors
30
 
from bzrlib.lockable_files import LockableFiles, TransportLock
31
 
from bzrlib.lockdir import LockDir
32
 
from bzrlib.osutils import safe_unicode
 
42
from bzrlib import (
 
43
    errors,
 
44
    lockable_files,
 
45
    lockdir,
 
46
    registry,
 
47
    revision as _mod_revision,
 
48
    symbol_versioning,
 
49
    urlutils,
 
50
    xml4,
 
51
    xml5,
 
52
    workingtree,
 
53
    workingtree_4,
 
54
    )
33
55
from bzrlib.osutils import (
34
 
                            abspath,
35
 
                            pathjoin,
36
 
                            safe_unicode,
37
 
                            sha_strings,
38
 
                            sha_string,
39
 
                            )
 
56
    safe_unicode,
 
57
    sha_strings,
 
58
    sha_string,
 
59
    )
40
60
from bzrlib.store.revision.text import TextRevisionStore
41
61
from bzrlib.store.text import TextStore
42
62
from bzrlib.store.versioned import WeaveStore
43
 
from bzrlib.symbol_versioning import *
44
 
from bzrlib.trace import mutter
45
63
from bzrlib.transactions import WriteTransaction
46
 
from bzrlib.transport import get_transport, urlunescape
 
64
from bzrlib.transport import get_transport
 
65
from bzrlib.weave import Weave
 
66
""")
 
67
 
 
68
from bzrlib.trace import mutter, note
47
69
from bzrlib.transport.local import LocalTransport
48
 
from bzrlib.weave import Weave
49
 
from bzrlib.xml4 import serializer_v4
50
 
import bzrlib.xml5
51
70
 
52
71
 
53
72
class BzrDir(object):
84
103
        """Return true if this bzrdir is one whose format we can convert from."""
85
104
        return True
86
105
 
 
106
    def check_conversion_target(self, target_format):
 
107
        target_repo_format = target_format.repository_format
 
108
        source_repo_format = self._format.repository_format
 
109
        source_repo_format.check_conversion_target(target_repo_format)
 
110
 
87
111
    @staticmethod
88
112
    def _check_supported(format, allow_unsupported):
89
113
        """Check whether format is a supported format.
92
116
        """
93
117
        if not allow_unsupported and not format.is_supported():
94
118
            # see open_downlevel to open legacy branches.
95
 
            raise errors.UnsupportedFormatError(
96
 
                    'sorry, format %s not supported' % format,
97
 
                    ['use a different bzr version',
98
 
                     'or remove the .bzr directory'
99
 
                     ' and "bzr init" again'])
 
119
            raise errors.UnsupportedFormatError(format=format)
100
120
 
101
121
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
102
122
        """Clone this bzrdir and its contents to url verbatim.
173
193
                    basis_repo = None
174
194
        return basis_repo, basis_branch, basis_tree
175
195
 
 
196
    # TODO: This should be given a Transport, and should chdir up; otherwise
 
197
    # this will open a new connection.
176
198
    def _make_tail(self, url):
177
 
        segments = url.split('/')
178
 
        if segments and segments[-1] not in ('', '.'):
179
 
            parent = '/'.join(segments[:-1])
180
 
            t = bzrlib.transport.get_transport(parent)
 
199
        head, tail = urlutils.split(url)
 
200
        if tail and tail != '.':
 
201
            t = get_transport(head)
181
202
            try:
182
 
                t.mkdir(segments[-1])
 
203
                t.mkdir(tail)
183
204
            except errors.FileExists:
184
205
                pass
185
206
 
 
207
    # TODO: Should take a Transport
186
208
    @classmethod
187
 
    def create(cls, base):
 
209
    def create(cls, base, format=None):
188
210
        """Create a new BzrDir at the url 'base'.
189
211
        
190
212
        This will call the current default formats initialize with base
191
213
        as the only parameter.
192
214
 
193
 
        If you need a specific format, consider creating an instance
194
 
        of that and calling initialize().
 
215
        :param format: If supplied, the format of branch to create.  If not
 
216
            supplied, the default is used.
195
217
        """
196
218
        if cls is not BzrDir:
197
 
            raise AssertionError("BzrDir.create always creates the default format, "
198
 
                    "not one of %r" % cls)
199
 
        segments = base.split('/')
200
 
        if segments and segments[-1] not in ('', '.'):
201
 
            parent = '/'.join(segments[:-1])
202
 
            t = bzrlib.transport.get_transport(parent)
 
219
            raise AssertionError("BzrDir.create always creates the default"
 
220
                " format, not one of %r" % cls)
 
221
        head, tail = urlutils.split(base)
 
222
        if tail and tail != '.':
 
223
            t = get_transport(head)
203
224
            try:
204
 
                t.mkdir(segments[-1])
 
225
                t.mkdir(tail)
205
226
            except errors.FileExists:
206
227
                pass
207
 
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
 
228
        if format is None:
 
229
            format = BzrDirFormat.get_default_format()
 
230
        return format.initialize(safe_unicode(base))
208
231
 
209
232
    def create_branch(self):
210
233
        """Create a branch in this BzrDir.
215
238
        raise NotImplementedError(self.create_branch)
216
239
 
217
240
    @staticmethod
218
 
    def create_branch_and_repo(base, force_new_repo=False):
 
241
    def create_branch_and_repo(base, force_new_repo=False, format=None):
219
242
        """Create a new BzrDir, Branch and Repository at the url 'base'.
220
243
 
221
244
        This will use the current default BzrDirFormat, and use whatever 
228
251
        :param base: The URL to create the branch at.
229
252
        :param force_new_repo: If True a new repository is always created.
230
253
        """
231
 
        bzrdir = BzrDir.create(base)
 
254
        bzrdir = BzrDir.create(base, format)
232
255
        bzrdir._find_or_create_repository(force_new_repo)
233
256
        return bzrdir.create_branch()
234
257
 
272
295
            t = get_transport(safe_unicode(base))
273
296
            if not isinstance(t, LocalTransport):
274
297
                raise errors.NotLocalUrl(base)
275
 
        if format is None:
276
 
            bzrdir = BzrDir.create(base)
277
 
        else:
278
 
            bzrdir = format.initialize(base)
 
298
        bzrdir = BzrDir.create(base, format)
279
299
        repo = bzrdir._find_or_create_repository(force_new_repo)
280
300
        result = bzrdir.create_branch()
281
301
        if force_new_tree or (repo.make_working_trees() and 
287
307
        return result
288
308
        
289
309
    @staticmethod
290
 
    def create_repository(base, shared=False):
 
310
    def create_repository(base, shared=False, format=None):
291
311
        """Create a new BzrDir and Repository at the url 'base'.
292
312
 
293
 
        This will use the current default BzrDirFormat, and use whatever 
294
 
        repository format that that uses for bzrdirformat.create_repository.
 
313
        If no format is supplied, this will default to the current default
 
314
        BzrDirFormat by default, and use whatever repository format that that
 
315
        uses for bzrdirformat.create_repository.
295
316
 
296
 
        ;param shared: Create a shared repository rather than a standalone
 
317
        :param shared: Create a shared repository rather than a standalone
297
318
                       repository.
298
319
        The Repository object is returned.
299
320
 
301
322
        it should take no parameters and construct whatever repository format
302
323
        that child class desires.
303
324
        """
304
 
        bzrdir = BzrDir.create(base)
305
 
        return bzrdir.create_repository()
 
325
        bzrdir = BzrDir.create(base, format)
 
326
        return bzrdir.create_repository(shared)
306
327
 
307
328
    @staticmethod
308
 
    def create_standalone_workingtree(base):
 
329
    def create_standalone_workingtree(base, format=None):
309
330
        """Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
310
331
 
311
332
        'base' must be a local path or a file:// url.
314
335
        repository format that that uses for bzrdirformat.create_workingtree,
315
336
        create_branch and create_repository.
316
337
 
317
 
        The WorkingTree object is returned.
 
338
        :return: The WorkingTree object.
318
339
        """
319
340
        t = get_transport(safe_unicode(base))
320
341
        if not isinstance(t, LocalTransport):
321
342
            raise errors.NotLocalUrl(base)
322
343
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
323
 
                                               force_new_repo=True).bzrdir
 
344
                                               force_new_repo=True,
 
345
                                               format=format).bzrdir
324
346
        return bzrdir.create_workingtree()
325
347
 
326
348
    def create_workingtree(self, revision_id=None):
330
352
        """
331
353
        raise NotImplementedError(self.create_workingtree)
332
354
 
 
355
    def retire_bzrdir(self):
 
356
        """Permanently disable the bzrdir.
 
357
 
 
358
        This is done by renaming it to give the user some ability to recover
 
359
        if there was a problem.
 
360
 
 
361
        This will have horrible consequences if anyone has anything locked or
 
362
        in use.
 
363
        """
 
364
        for i in xrange(10000):
 
365
            try:
 
366
                to_path = '.bzr.retired.%d' % i
 
367
                self.root_transport.rename('.bzr', to_path)
 
368
                note("renamed %s to %s"
 
369
                    % (self.root_transport.abspath('.bzr'), to_path))
 
370
                break
 
371
            except (errors.TransportError, IOError, errors.PathError):
 
372
                pass
 
373
 
 
374
    def destroy_workingtree(self):
 
375
        """Destroy the working tree at this BzrDir.
 
376
 
 
377
        Formats that do not support this may raise UnsupportedOperation.
 
378
        """
 
379
        raise NotImplementedError(self.destroy_workingtree)
 
380
 
 
381
    def destroy_workingtree_metadata(self):
 
382
        """Destroy the control files for the working tree at this BzrDir.
 
383
 
 
384
        The contents of working tree files are not affected.
 
385
        Formats that do not support this may raise UnsupportedOperation.
 
386
        """
 
387
        raise NotImplementedError(self.destroy_workingtree_metadata)
 
388
 
333
389
    def find_repository(self):
334
390
        """Find the repository that should be used for a_bzrdir.
335
391
 
343
399
            pass
344
400
        next_transport = self.root_transport.clone('..')
345
401
        while True:
 
402
            # find the next containing bzrdir
346
403
            try:
347
404
                found_bzrdir = BzrDir.open_containing_from_transport(
348
405
                    next_transport)[0]
349
406
            except errors.NotBranchError:
 
407
                # none found
350
408
                raise errors.NoRepositoryPresent(self)
 
409
            # does it have a repository ?
351
410
            try:
352
411
                repository = found_bzrdir.open_repository()
353
412
            except errors.NoRepositoryPresent:
354
413
                next_transport = found_bzrdir.root_transport.clone('..')
355
 
                continue
 
414
                if (found_bzrdir.root_transport.base == next_transport.base):
 
415
                    # top of the file system
 
416
                    break
 
417
                else:
 
418
                    continue
356
419
            if ((found_bzrdir.root_transport.base == 
357
420
                 self.root_transport.base) or repository.is_shared()):
358
421
                return repository
365
428
 
366
429
        Note that bzr dirs that do not support format strings will raise
367
430
        IncompatibleFormat if the branch format they are given has
368
 
        a format string, and vice verca.
 
431
        a format string, and vice versa.
369
432
 
370
433
        If branch_format is None, the transport is returned with no 
371
434
        checking. if it is not None, then the returned transport is
378
441
 
379
442
        Note that bzr dirs that do not support format strings will raise
380
443
        IncompatibleFormat if the repository format they are given has
381
 
        a format string, and vice verca.
 
444
        a format string, and vice versa.
382
445
 
383
446
        If repository_format is None, the transport is returned with no 
384
447
        checking. if it is not None, then the returned transport is
390
453
        """Get the transport for use by workingtree format in this BzrDir.
391
454
 
392
455
        Note that bzr dirs that do not support format strings will raise
393
 
        IncompatibleFormat if the workingtree format they are given has
394
 
        a format string, and vice verca.
 
456
        IncompatibleFormat if the workingtree format they are given has a
 
457
        format string, and vice versa.
395
458
 
396
459
        If workingtree_format is None, the transport is returned with no 
397
460
        checking. if it is not None, then the returned transport is
426
489
        # this might be better on the BzrDirFormat class because it refers to 
427
490
        # all the possible bzrdir disk formats. 
428
491
        # This method is tested via the workingtree is_control_filename tests- 
429
 
        # it was extractd from WorkingTree.is_control_filename. If the methods
 
492
        # it was extracted from WorkingTree.is_control_filename. If the methods
430
493
        # contract is extended beyond the current trivial  implementation please
431
494
        # add new tests for it to the appropriate place.
432
495
        return filename == '.bzr' or filename.startswith('.bzr/')
454
517
        _unsupported is a private parameter to the BzrDir class.
455
518
        """
456
519
        t = get_transport(base)
457
 
        mutter("trying to open %r with transport %r", base, t)
458
 
        format = BzrDirFormat.find_format(t)
 
520
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
 
521
 
 
522
    @staticmethod
 
523
    def open_from_transport(transport, _unsupported=False):
 
524
        """Open a bzrdir within a particular directory.
 
525
 
 
526
        :param transport: Transport containing the bzrdir.
 
527
        :param _unsupported: private.
 
528
        """
 
529
        format = BzrDirFormat.find_format(transport)
459
530
        BzrDir._check_supported(format, _unsupported)
460
 
        return format.open(t, _found=True)
 
531
        return format.open(transport, _found=True)
461
532
 
462
533
    def open_branch(self, unsupported=False):
463
534
        """Open the branch object at this BzrDir if one is present.
489
560
        If there is one and it is either an unrecognised format or an unsupported 
490
561
        format, UnknownFormatError or UnsupportedFormatError are raised.
491
562
        If there is one, it is returned, along with the unused portion of url.
 
563
 
 
564
        :return: The BzrDir that contains the path, and a Unicode path 
 
565
                for the rest of the URL.
492
566
        """
493
567
        # this gets the normalised url back. I.e. '.' -> the full path.
494
568
        url = a_transport.base
495
569
        while True:
496
570
            try:
497
 
                format = BzrDirFormat.find_format(a_transport)
498
 
                BzrDir._check_supported(format, False)
499
 
                return format.open(a_transport), a_transport.relpath(url)
 
571
                result = BzrDir.open_from_transport(a_transport)
 
572
                return result, urlutils.unescape(a_transport.relpath(url))
500
573
            except errors.NotBranchError, e:
501
 
                mutter('not a branch in: %r %s', a_transport.base, e)
 
574
                pass
502
575
            new_t = a_transport.clone('..')
503
576
            if new_t.base == a_transport.base:
504
577
                # reached the root, whatever that may be
505
578
                raise errors.NotBranchError(path=url)
506
579
            a_transport = new_t
507
580
 
 
581
    @classmethod
 
582
    def open_containing_tree_or_branch(klass, location):
 
583
        """Return the branch and working tree contained by a location.
 
584
 
 
585
        Returns (tree, branch, relpath).
 
586
        If there is no tree at containing the location, tree will be None.
 
587
        If there is no branch containing the location, an exception will be
 
588
        raised
 
589
        relpath is the portion of the path that is contained by the branch.
 
590
        """
 
591
        bzrdir, relpath = klass.open_containing(location)
 
592
        try:
 
593
            tree = bzrdir.open_workingtree()
 
594
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
595
            tree = None
 
596
            branch = bzrdir.open_branch()
 
597
        else:
 
598
            branch = tree.branch
 
599
        return tree, branch, relpath
 
600
 
508
601
    def open_repository(self, _unsupported=False):
509
602
        """Open the repository object at this BzrDir if one is present.
510
603
 
553
646
        except errors.NoWorkingTree:
554
647
            return False
555
648
 
556
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
649
    def _cloning_metadir(self, basis=None):
 
650
        def related_repository(bzrdir):
 
651
            try:
 
652
                branch = bzrdir.open_branch()
 
653
                return branch.repository
 
654
            except errors.NotBranchError:
 
655
                source_branch = None
 
656
                return bzrdir.open_repository()
 
657
        result_format = self._format.__class__()
 
658
        try:
 
659
            try:
 
660
                source_repository = related_repository(self)
 
661
            except errors.NoRepositoryPresent:
 
662
                if basis is None:
 
663
                    raise
 
664
                source_repository = related_repository(self)
 
665
            result_format.repository_format = source_repository._format
 
666
        except errors.NoRepositoryPresent:
 
667
            source_repository = None
 
668
        try:
 
669
            tree = self.open_workingtree()
 
670
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
671
            result_format.workingtree_format = None
 
672
        else:
 
673
            result_format.workingtree_format = tree._format.__class__()
 
674
        return result_format, source_repository
 
675
 
 
676
    def cloning_metadir(self, basis=None):
 
677
        """Produce a metadir suitable for cloning or sprouting with.
 
678
 
 
679
        These operations may produce workingtrees (yes, even though they're
 
680
        "cloning" something that doesn't have a tree, so a viable workingtree
 
681
        format must be selected.
 
682
        """
 
683
        format, repository = self._cloning_metadir()
 
684
        if format._workingtree_format is None:
 
685
            if repository is None:
 
686
                return format
 
687
            tree_format = repository._format._matchingbzrdir.workingtree_format
 
688
            format.workingtree_format = tree_format.__class__()
 
689
        return format
 
690
 
 
691
    def checkout_metadir(self):
 
692
        return self.cloning_metadir()
 
693
 
 
694
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False,
 
695
               recurse='down'):
557
696
        """Create a copy of this bzrdir prepared for use as a new line of
558
697
        development.
559
698
 
568
707
            itself to download less data.
569
708
        """
570
709
        self._make_tail(url)
571
 
        result = self._format.initialize(url)
 
710
        cloning_format = self.cloning_metadir(basis)
 
711
        result = cloning_format.initialize(url)
572
712
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
573
713
        try:
574
714
            source_branch = self.open_branch()
595
735
            result.create_repository()
596
736
        elif source_repository is not None and result_repo is None:
597
737
            # have source, and want to make a new target repo
598
 
            # we dont clone the repo because that preserves attributes
 
738
            # we don't clone the repo because that preserves attributes
599
739
            # like is_shared(), and we have not yet implemented a 
600
740
            # repository sprout().
601
741
            result_repo = result.create_repository()
605
745
                # XXX FIXME RBC 20060214 need tests for this when the basis
606
746
                # is incomplete
607
747
                result_repo.fetch(basis_repo, revision_id=revision_id)
608
 
            result_repo.fetch(source_repository, revision_id=revision_id)
 
748
            if source_repository is not None:
 
749
                result_repo.fetch(source_repository, revision_id=revision_id)
609
750
        if source_branch is not None:
610
751
            source_branch.sprout(result, revision_id=revision_id)
611
752
        else:
612
753
            result.create_branch()
 
754
        # TODO: jam 20060426 we probably need a test in here in the
 
755
        #       case that the newly sprouted branch is a remote one
613
756
        if result_repo is None or result_repo.make_working_trees():
614
 
            result.create_workingtree()
 
757
            wt = result.create_workingtree()
 
758
            wt.lock_write()
 
759
            try:
 
760
                if wt.path2id('') is None:
 
761
                    try:
 
762
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
763
                    except errors.NoWorkingTree:
 
764
                        pass
 
765
            finally:
 
766
                wt.unlock()
 
767
        else:
 
768
            wt = None
 
769
        if recurse == 'down':
 
770
            if wt is not None:
 
771
                basis = wt.basis_tree()
 
772
                basis.lock_read()
 
773
                subtrees = basis.iter_references()
 
774
                recurse_branch = wt.branch
 
775
            elif source_branch is not None:
 
776
                basis = source_branch.basis_tree()
 
777
                basis.lock_read()
 
778
                subtrees = basis.iter_references()
 
779
                recurse_branch = source_branch
 
780
            else:
 
781
                subtrees = []
 
782
                basis = None
 
783
            try:
 
784
                for path, file_id in subtrees:
 
785
                    target = urlutils.join(url, urlutils.escape(path))
 
786
                    sublocation = source_branch.reference_parent(file_id, path)
 
787
                    sublocation.bzrdir.sprout(target,
 
788
                        basis.get_reference_revision(file_id, path),
 
789
                        force_new_repo=force_new_repo, recurse=recurse)
 
790
            finally:
 
791
                if basis is not None:
 
792
                    basis.unlock()
615
793
        return result
616
794
 
617
795
 
621
799
    def __init__(self, _transport, _format):
622
800
        """See BzrDir.__init__."""
623
801
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
624
 
        assert self._format._lock_class == TransportLock
 
802
        assert self._format._lock_class == lockable_files.TransportLock
625
803
        assert self._format._lock_file_name == 'branch-lock'
626
 
        self._control_files = LockableFiles(self.get_branch_transport(None),
 
804
        self._control_files = lockable_files.LockableFiles(
 
805
                                            self.get_branch_transport(None),
627
806
                                            self._format._lock_file_name,
628
807
                                            self._format._lock_class)
629
808
 
673
852
        # done on this format anyway. So - acceptable wart.
674
853
        result = self.open_workingtree()
675
854
        if revision_id is not None:
676
 
            result.set_last_revision(revision_id)
 
855
            if revision_id == _mod_revision.NULL_REVISION:
 
856
                result.set_parent_ids([])
 
857
            else:
 
858
                result.set_parent_ids([revision_id])
677
859
        return result
678
860
 
 
861
    def destroy_workingtree(self):
 
862
        """See BzrDir.destroy_workingtree."""
 
863
        raise errors.UnsupportedOperation(self.destroy_workingtree, self)
 
864
 
 
865
    def destroy_workingtree_metadata(self):
 
866
        """See BzrDir.destroy_workingtree_metadata."""
 
867
        raise errors.UnsupportedOperation(self.destroy_workingtree_metadata, 
 
868
                                          self)
 
869
 
679
870
    def get_branch_transport(self, branch_format):
680
871
        """See BzrDir.get_branch_transport()."""
681
872
        if branch_format is None:
721
912
        self._check_supported(format, unsupported)
722
913
        return format.open(self, _found=True)
723
914
 
724
 
    def sprout(self, url, revision_id=None, basis=None):
 
915
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
725
916
        """See BzrDir.sprout()."""
726
917
        from bzrlib.workingtree import WorkingTreeFormat2
727
918
        self._make_tail(url)
756
947
 
757
948
    def open_repository(self):
758
949
        """See BzrDir.open_repository."""
759
 
        from bzrlib.repository import RepositoryFormat4
 
950
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
760
951
        return RepositoryFormat4().open(self, _found=True)
761
952
 
762
953
 
768
959
 
769
960
    def open_repository(self):
770
961
        """See BzrDir.open_repository."""
771
 
        from bzrlib.repository import RepositoryFormat5
 
962
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
772
963
        return RepositoryFormat5().open(self, _found=True)
773
964
 
774
965
    def open_workingtree(self, _unsupported=False):
785
976
 
786
977
    def open_repository(self):
787
978
        """See BzrDir.open_repository."""
788
 
        from bzrlib.repository import RepositoryFormat6
 
979
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
789
980
        return RepositoryFormat6().open(self, _found=True)
790
981
 
791
982
    def open_workingtree(self, _unsupported=False):
809
1000
 
810
1001
    def create_branch(self):
811
1002
        """See BzrDir.create_branch."""
812
 
        from bzrlib.branch import BranchFormat
813
 
        return BranchFormat.get_default_format().initialize(self)
 
1003
        return self._format.get_branch_format().initialize(self)
814
1004
 
815
1005
    def create_repository(self, shared=False):
816
1006
        """See BzrDir.create_repository."""
819
1009
    def create_workingtree(self, revision_id=None):
820
1010
        """See BzrDir.create_workingtree."""
821
1011
        from bzrlib.workingtree import WorkingTreeFormat
822
 
        return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
 
1012
        return self._format.workingtree_format.initialize(self, revision_id)
 
1013
 
 
1014
    def destroy_workingtree(self):
 
1015
        """See BzrDir.destroy_workingtree."""
 
1016
        wt = self.open_workingtree()
 
1017
        repository = wt.branch.repository
 
1018
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1019
        wt.revert([], old_tree=empty)
 
1020
        self.destroy_workingtree_metadata()
 
1021
 
 
1022
    def destroy_workingtree_metadata(self):
 
1023
        self.transport.delete_tree('checkout')
823
1024
 
824
1025
    def _get_mkdir_mode(self):
825
1026
        """Figure out the mode to use when creating a bzrdir subdir."""
826
 
        temp_control = LockableFiles(self.transport, '', TransportLock)
 
1027
        temp_control = lockable_files.LockableFiles(self.transport, '',
 
1028
                                     lockable_files.TransportLock)
827
1029
        return temp_control._dir_mode
828
1030
 
829
1031
    def get_branch_transport(self, branch_format):
883
1085
                return True
884
1086
        except errors.NoRepositoryPresent:
885
1087
            pass
886
 
        # currently there are no other possible conversions for meta1 formats.
 
1088
        try:
 
1089
            if not isinstance(self.open_branch()._format,
 
1090
                              format.get_branch_format().__class__):
 
1091
                # the branch needs an upgrade.
 
1092
                return True
 
1093
        except errors.NotBranchError:
 
1094
            pass
 
1095
        try:
 
1096
            if not isinstance(self.open_workingtree()._format,
 
1097
                              format.workingtree_format.__class__):
 
1098
                # the workingtree needs an upgrade.
 
1099
                return True
 
1100
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1101
            pass
887
1102
        return False
888
1103
 
889
1104
    def open_branch(self, unsupported=False):
931
1146
    _formats = {}
932
1147
    """The known formats."""
933
1148
 
 
1149
    _control_formats = []
 
1150
    """The registered control formats - .bzr, ....
 
1151
    
 
1152
    This is a list of BzrDirFormat objects.
 
1153
    """
 
1154
 
934
1155
    _lock_file_name = 'branch-lock'
935
1156
 
936
1157
    # _lock_class must be set in subclasses to the lock type, typ.
938
1159
 
939
1160
    @classmethod
940
1161
    def find_format(klass, transport):
941
 
        """Return the format registered for URL."""
 
1162
        """Return the format present at transport."""
 
1163
        for format in klass._control_formats:
 
1164
            try:
 
1165
                return format.probe_transport(transport)
 
1166
            except errors.NotBranchError:
 
1167
                # this format does not find a control dir here.
 
1168
                pass
 
1169
        raise errors.NotBranchError(path=transport.base)
 
1170
 
 
1171
    @classmethod
 
1172
    def probe_transport(klass, transport):
 
1173
        """Return the .bzrdir style transport present at URL."""
942
1174
        try:
943
1175
            format_string = transport.get(".bzr/branch-format").read()
 
1176
        except errors.NoSuchFile:
 
1177
            raise errors.NotBranchError(path=transport.base)
 
1178
 
 
1179
        try:
944
1180
            return klass._formats[format_string]
945
 
        except errors.NoSuchFile:
946
 
            raise errors.NotBranchError(path=transport.base)
947
1181
        except KeyError:
948
 
            raise errors.UnknownFormatError(format_string)
 
1182
            raise errors.UnknownFormatError(format=format_string)
949
1183
 
950
1184
    @classmethod
951
1185
    def get_default_format(klass):
966
1200
        This returns a bzrlib.bzrdir.Converter object.
967
1201
 
968
1202
        This should return the best upgrader to step this format towards the
969
 
        current default format. In the case of plugins we can/shouold provide
 
1203
        current default format. In the case of plugins we can/should provide
970
1204
        some means for them to extend the range of returnable converters.
971
1205
 
972
 
        :param format: Optional format to override the default foramt of the 
 
1206
        :param format: Optional format to override the default format of the 
973
1207
                       library.
974
1208
        """
975
1209
        raise NotImplementedError(self.get_converter)
984
1218
 
985
1219
    def initialize_on_transport(self, transport):
986
1220
        """Initialize a new bzrdir in the base directory of a Transport."""
987
 
        # Since we don'transport have a .bzr directory, inherit the
 
1221
        # Since we don't have a .bzr directory, inherit the
988
1222
        # mode from the root directory
989
 
        temp_control = LockableFiles(transport, '', TransportLock)
 
1223
        temp_control = lockable_files.LockableFiles(transport,
 
1224
                            '', lockable_files.TransportLock)
990
1225
        temp_control._transport.mkdir('.bzr',
991
 
                                      # FIXME: RBC 20060121 dont peek under
 
1226
                                      # FIXME: RBC 20060121 don't peek under
992
1227
                                      # the covers
993
1228
                                      mode=temp_control._dir_mode)
994
1229
        file_mode = temp_control._file_mode
1001
1236
                      ('branch-format', self.get_format_string()),
1002
1237
                      ]
1003
1238
        # NB: no need to escape relative paths that are url safe.
1004
 
        control_files = LockableFiles(control, self._lock_file_name, 
1005
 
                                      self._lock_class)
 
1239
        control_files = lockable_files.LockableFiles(control,
 
1240
                            self._lock_file_name, self._lock_class)
1006
1241
        control_files.create_lock()
1007
1242
        control_files.lock_write()
1008
1243
        try:
1021
1256
        """
1022
1257
        return True
1023
1258
 
 
1259
    def same_model(self, target_format):
 
1260
        return (self.repository_format.rich_root_data == 
 
1261
            target_format.rich_root_data)
 
1262
 
 
1263
    @classmethod
 
1264
    def known_formats(klass):
 
1265
        """Return all the known formats.
 
1266
        
 
1267
        Concrete formats should override _known_formats.
 
1268
        """
 
1269
        # There is double indirection here to make sure that control 
 
1270
        # formats used by more than one dir format will only be probed 
 
1271
        # once. This can otherwise be quite expensive for remote connections.
 
1272
        result = set()
 
1273
        for format in klass._control_formats:
 
1274
            result.update(format._known_formats())
 
1275
        return result
 
1276
    
 
1277
    @classmethod
 
1278
    def _known_formats(klass):
 
1279
        """Return the known format instances for this control format."""
 
1280
        return set(klass._formats.values())
 
1281
 
1024
1282
    def open(self, transport, _found=False):
1025
1283
        """Return an instance of this format for the dir transport points at.
1026
1284
        
1027
1285
        _found is a private parameter, do not use it.
1028
1286
        """
1029
1287
        if not _found:
1030
 
            assert isinstance(BzrDirFormat.find_format(transport),
1031
 
                              self.__class__)
 
1288
            found_format = BzrDirFormat.find_format(transport)
 
1289
            if not isinstance(found_format, self.__class__):
 
1290
                raise AssertionError("%s was asked to open %s, but it seems to need "
 
1291
                        "format %s" 
 
1292
                        % (self, transport, found_format))
1032
1293
        return self._open(transport)
1033
1294
 
1034
1295
    def _open(self, transport):
1044
1305
        klass._formats[format.get_format_string()] = format
1045
1306
 
1046
1307
    @classmethod
 
1308
    def register_control_format(klass, format):
 
1309
        """Register a format that does not use '.bzrdir' for its control dir.
 
1310
 
 
1311
        TODO: This should be pulled up into a 'ControlDirFormat' base class
 
1312
        which BzrDirFormat can inherit from, and renamed to register_format 
 
1313
        there. It has been done without that for now for simplicity of
 
1314
        implementation.
 
1315
        """
 
1316
        klass._control_formats.append(format)
 
1317
 
 
1318
    @classmethod
 
1319
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1047
1320
    def set_default_format(klass, format):
 
1321
        klass._set_default_format(format)
 
1322
 
 
1323
    @classmethod
 
1324
    def _set_default_format(klass, format):
 
1325
        """Set default format (for testing behavior of defaults only)"""
1048
1326
        klass._default_format = format
1049
1327
 
1050
1328
    def __str__(self):
1055
1333
        assert klass._formats[format.get_format_string()] is format
1056
1334
        del klass._formats[format.get_format_string()]
1057
1335
 
 
1336
    @classmethod
 
1337
    def unregister_control_format(klass, format):
 
1338
        klass._control_formats.remove(format)
 
1339
 
 
1340
 
 
1341
# register BzrDirFormat as a control format
 
1342
BzrDirFormat.register_control_format(BzrDirFormat)
 
1343
 
1058
1344
 
1059
1345
class BzrDirFormat4(BzrDirFormat):
1060
1346
    """Bzr dir format 4.
1069
1355
    removed in format 5; write support for this format has been removed.
1070
1356
    """
1071
1357
 
1072
 
    _lock_class = TransportLock
 
1358
    _lock_class = lockable_files.TransportLock
1073
1359
 
1074
1360
    def get_format_string(self):
1075
1361
        """See BzrDirFormat.get_format_string()."""
1103
1389
 
1104
1390
    def __return_repository_format(self):
1105
1391
        """Circular import protection."""
1106
 
        from bzrlib.repository import RepositoryFormat4
1107
 
        return RepositoryFormat4(self)
 
1392
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
 
1393
        return RepositoryFormat4()
1108
1394
    repository_format = property(__return_repository_format)
1109
1395
 
1110
1396
 
1119
1405
       Unhashed stores in the repository.
1120
1406
    """
1121
1407
 
1122
 
    _lock_class = TransportLock
 
1408
    _lock_class = lockable_files.TransportLock
1123
1409
 
1124
1410
    def get_format_string(self):
1125
1411
        """See BzrDirFormat.get_format_string()."""
1143
1429
        Except when they are being cloned.
1144
1430
        """
1145
1431
        from bzrlib.branch import BzrBranchFormat4
1146
 
        from bzrlib.repository import RepositoryFormat5
 
1432
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
1147
1433
        from bzrlib.workingtree import WorkingTreeFormat2
1148
1434
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1149
1435
        RepositoryFormat5().initialize(result, _internal=True)
1150
1436
        if not _cloning:
1151
 
            BzrBranchFormat4().initialize(result)
1152
 
            WorkingTreeFormat2().initialize(result)
 
1437
            branch = BzrBranchFormat4().initialize(result)
 
1438
            try:
 
1439
                WorkingTreeFormat2().initialize(result)
 
1440
            except errors.NotLocalUrl:
 
1441
                # Even though we can't access the working tree, we need to
 
1442
                # create its control files.
 
1443
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1153
1444
        return result
1154
1445
 
1155
1446
    def _open(self, transport):
1158
1449
 
1159
1450
    def __return_repository_format(self):
1160
1451
        """Circular import protection."""
1161
 
        from bzrlib.repository import RepositoryFormat5
1162
 
        return RepositoryFormat5(self)
 
1452
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
 
1453
        return RepositoryFormat5()
1163
1454
    repository_format = property(__return_repository_format)
1164
1455
 
1165
1456
 
1173
1464
     - Format 6 repositories [always]
1174
1465
    """
1175
1466
 
1176
 
    _lock_class = TransportLock
 
1467
    _lock_class = lockable_files.TransportLock
1177
1468
 
1178
1469
    def get_format_string(self):
1179
1470
        """See BzrDirFormat.get_format_string()."""
1197
1488
        Except when they are being cloned.
1198
1489
        """
1199
1490
        from bzrlib.branch import BzrBranchFormat4
1200
 
        from bzrlib.repository import RepositoryFormat6
 
1491
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
1201
1492
        from bzrlib.workingtree import WorkingTreeFormat2
1202
1493
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1203
1494
        RepositoryFormat6().initialize(result, _internal=True)
1204
1495
        if not _cloning:
1205
 
            BzrBranchFormat4().initialize(result)
 
1496
            branch = BzrBranchFormat4().initialize(result)
1206
1497
            try:
1207
1498
                WorkingTreeFormat2().initialize(result)
1208
1499
            except errors.NotLocalUrl:
1209
 
                # emulate pre-check behaviour for working tree and silently 
1210
 
                # fail.
1211
 
                pass
 
1500
                # Even though we can't access the working tree, we need to
 
1501
                # create its control files.
 
1502
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1212
1503
        return result
1213
1504
 
1214
1505
    def _open(self, transport):
1217
1508
 
1218
1509
    def __return_repository_format(self):
1219
1510
        """Circular import protection."""
1220
 
        from bzrlib.repository import RepositoryFormat6
1221
 
        return RepositoryFormat6(self)
 
1511
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
 
1512
        return RepositoryFormat6()
1222
1513
    repository_format = property(__return_repository_format)
1223
1514
 
1224
1515
 
1233
1524
     - Format 7 repositories [optional]
1234
1525
    """
1235
1526
 
1236
 
    _lock_class = LockDir
 
1527
    _lock_class = lockdir.LockDir
 
1528
 
 
1529
    def __init__(self):
 
1530
        self._workingtree_format = None
 
1531
        self._branch_format = None
 
1532
 
 
1533
    def __eq__(self, other):
 
1534
        if other.__class__ is not self.__class__:
 
1535
            return False
 
1536
        if other.repository_format != self.repository_format:
 
1537
            return False
 
1538
        if other.workingtree_format != self.workingtree_format:
 
1539
            return False
 
1540
        return True
 
1541
 
 
1542
    def __ne__(self, other):
 
1543
        return not self == other
 
1544
 
 
1545
    def get_branch_format(self):
 
1546
        if self._branch_format is None:
 
1547
            from bzrlib.branch import BranchFormat
 
1548
            self._branch_format = BranchFormat.get_default_format()
 
1549
        return self._branch_format
 
1550
 
 
1551
    def set_branch_format(self, format):
 
1552
        self._branch_format = format
1237
1553
 
1238
1554
    def get_converter(self, format=None):
1239
1555
        """See BzrDirFormat.get_converter()."""
1269
1585
 
1270
1586
    repository_format = property(__return_repository_format, __set_repository_format)
1271
1587
 
 
1588
    def __get_workingtree_format(self):
 
1589
        if self._workingtree_format is None:
 
1590
            from bzrlib.workingtree import WorkingTreeFormat
 
1591
            self._workingtree_format = WorkingTreeFormat.get_default_format()
 
1592
        return self._workingtree_format
 
1593
 
 
1594
    def __set_workingtree_format(self, wt_format):
 
1595
        self._workingtree_format = wt_format
 
1596
 
 
1597
    workingtree_format = property(__get_workingtree_format,
 
1598
                                  __set_workingtree_format)
 
1599
 
1272
1600
 
1273
1601
BzrDirFormat.register_format(BzrDirFormat4())
1274
1602
BzrDirFormat.register_format(BzrDirFormat5())
1275
1603
BzrDirFormat.register_format(BzrDirFormat6())
1276
1604
__default_format = BzrDirMetaFormat1()
1277
1605
BzrDirFormat.register_format(__default_format)
1278
 
BzrDirFormat.set_default_format(__default_format)
 
1606
BzrDirFormat._default_format = __default_format
1279
1607
 
1280
1608
 
1281
1609
class BzrDirTestProviderAdapter(object):
1293
1621
        self._formats = formats
1294
1622
    
1295
1623
    def adapt(self, test):
1296
 
        result = TestSuite()
 
1624
        result = unittest.TestSuite()
1297
1625
        for format in self._formats:
1298
1626
            new_test = deepcopy(test)
1299
1627
            new_test.transport_server = self._transport_server
1307
1635
        return result
1308
1636
 
1309
1637
 
1310
 
class ScratchDir(BzrDir6):
1311
 
    """Special test class: a bzrdir that cleans up itself..
1312
 
 
1313
 
    >>> d = ScratchDir()
1314
 
    >>> base = d.transport.base
1315
 
    >>> isdir(base)
1316
 
    True
1317
 
    >>> b.transport.__del__()
1318
 
    >>> isdir(base)
1319
 
    False
1320
 
    """
1321
 
 
1322
 
    def __init__(self, files=[], dirs=[], transport=None):
1323
 
        """Make a test branch.
1324
 
 
1325
 
        This creates a temporary directory and runs init-tree in it.
1326
 
 
1327
 
        If any files are listed, they are created in the working copy.
1328
 
        """
1329
 
        if transport is None:
1330
 
            transport = bzrlib.transport.local.ScratchTransport()
1331
 
            # local import for scope restriction
1332
 
            BzrDirFormat6().initialize(transport.base)
1333
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1334
 
            self.create_repository()
1335
 
            self.create_branch()
1336
 
            self.create_workingtree()
1337
 
        else:
1338
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1339
 
 
1340
 
        # BzrBranch creates a clone to .bzr and then forgets about the
1341
 
        # original transport. A ScratchTransport() deletes itself and
1342
 
        # everything underneath it when it goes away, so we need to
1343
 
        # grab a local copy to prevent that from happening
1344
 
        self._transport = transport
1345
 
 
1346
 
        for d in dirs:
1347
 
            self._transport.mkdir(d)
1348
 
            
1349
 
        for f in files:
1350
 
            self._transport.put(f, 'content of %s' % f)
1351
 
 
1352
 
    def clone(self):
1353
 
        """
1354
 
        >>> orig = ScratchDir(files=["file1", "file2"])
1355
 
        >>> os.listdir(orig.base)
1356
 
        [u'.bzr', u'file1', u'file2']
1357
 
        >>> clone = orig.clone()
1358
 
        >>> if os.name != 'nt':
1359
 
        ...   os.path.samefile(orig.base, clone.base)
1360
 
        ... else:
1361
 
        ...   orig.base == clone.base
1362
 
        ...
1363
 
        False
1364
 
        >>> os.listdir(clone.base)
1365
 
        [u'.bzr', u'file1', u'file2']
1366
 
        """
1367
 
        from shutil import copytree
1368
 
        from bzrlib.osutils import mkdtemp
1369
 
        base = mkdtemp()
1370
 
        os.rmdir(base)
1371
 
        copytree(self.base, base, symlinks=True)
1372
 
        return ScratchDir(
1373
 
            transport=bzrlib.transport.local.ScratchTransport(base))
1374
 
 
1375
 
 
1376
1638
class Converter(object):
1377
1639
    """Converts a disk format object from one format to another."""
1378
1640
 
1463
1725
        self.bzrdir.transport.delete_tree('text-store')
1464
1726
 
1465
1727
    def _convert_working_inv(self):
1466
 
        inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1467
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1728
        inv = xml4.serializer_v4.read_inventory(
 
1729
                    self.branch.control_files.get('inventory'))
 
1730
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1468
1731
        # FIXME inventory is a working tree change.
1469
 
        self.branch.control_files.put('inventory', new_inv_xml)
 
1732
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1470
1733
 
1471
1734
    def _write_all_weaves(self):
1472
1735
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1496
1759
                                                      prefixed=False,
1497
1760
                                                      compressed=True))
1498
1761
        try:
1499
 
            transaction = bzrlib.transactions.WriteTransaction()
 
1762
            transaction = WriteTransaction()
1500
1763
            for i, rev_id in enumerate(self.converted_revs):
1501
1764
                self.pb.update('write revision', i, len(self.converted_revs))
1502
1765
                _revision_store.add_revision(self.revisions[rev_id], transaction)
1528
1791
    def _load_old_inventory(self, rev_id):
1529
1792
        assert rev_id not in self.converted_revs
1530
1793
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1531
 
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
 
1794
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
 
1795
        inv.revision_id = rev_id
1532
1796
        rev = self.revisions[rev_id]
1533
1797
        if rev.inventory_sha1:
1534
1798
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1538
1802
    def _load_updated_inventory(self, rev_id):
1539
1803
        assert rev_id in self.converted_revs
1540
1804
        inv_xml = self.inv_weave.get_text(rev_id)
1541
 
        inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
1805
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1542
1806
        return inv
1543
1807
 
1544
1808
    def _convert_one_rev(self, rev_id):
1554
1818
    def _store_new_weave(self, rev, inv, present_parents):
1555
1819
        # the XML is now updated with text versions
1556
1820
        if __debug__:
1557
 
            for file_id in inv:
1558
 
                ie = inv[file_id]
1559
 
                if ie.kind == 'root_directory':
1560
 
                    continue
1561
 
                assert hasattr(ie, 'revision'), \
 
1821
            entries = inv.iter_entries()
 
1822
            entries.next()
 
1823
            for path, ie in entries:
 
1824
                assert getattr(ie, 'revision', None) is not None, \
1562
1825
                    'no revision on {%s} in {%s}' % \
1563
1826
                    (file_id, rev.revision_id)
1564
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1827
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1565
1828
        new_inv_sha1 = sha_string(new_inv_xml)
1566
1829
        self.inv_weave.add_lines(rev.revision_id, 
1567
1830
                                 present_parents,
1576
1839
        mutter('converting texts of revision {%s}',
1577
1840
               rev_id)
1578
1841
        parent_invs = map(self._load_updated_inventory, present_parents)
1579
 
        for file_id in inv:
1580
 
            ie = inv[file_id]
 
1842
        entries = inv.iter_entries()
 
1843
        entries.next()
 
1844
        for path, ie in entries:
1581
1845
            self._convert_file_version(rev, ie, parent_invs)
1582
1846
 
1583
1847
    def _convert_file_version(self, rev, ie, parent_invs):
1586
1850
        The file needs to be added into the weave if it is a merge
1587
1851
        of >=2 parents or if it's changed from its parent.
1588
1852
        """
1589
 
        if ie.kind == 'root_directory':
1590
 
            return
1591
1853
        file_id = ie.file_id
1592
1854
        rev_id = rev.revision_id
1593
1855
        w = self.text_weaves.get(file_id)
1601
1863
                                                  entry_vf=w)
1602
1864
        for old_revision in previous_entries:
1603
1865
                # if this fails, its a ghost ?
1604
 
                assert old_revision in self.converted_revs 
 
1866
                assert old_revision in self.converted_revs, \
 
1867
                    "Revision {%s} not in converted_revs" % old_revision
1605
1868
        self.snapshot_ie(previous_entries, ie, w, rev_id)
1606
1869
        del ie.text_id
1607
1870
        assert getattr(ie, 'revision', None) is not None
1672
1935
            store_transport = self.bzrdir.transport.clone(store_name)
1673
1936
            store = TransportStore(store_transport, prefixed=True)
1674
1937
            for urlfilename in store_transport.list_dir('.'):
1675
 
                filename = urlunescape(urlfilename)
 
1938
                filename = urlutils.unescape(urlfilename)
1676
1939
                if (filename.endswith(".weave") or
1677
1940
                    filename.endswith(".gz") or
1678
1941
                    filename.endswith(".sig")):
1694
1957
 
1695
1958
    def convert(self, to_convert, pb):
1696
1959
        """See Converter.convert()."""
 
1960
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
 
1961
        from bzrlib.branch import BzrBranchFormat5
1697
1962
        self.bzrdir = to_convert
1698
1963
        self.pb = pb
1699
1964
        self.count = 0
1728
1993
        # we hard code the formats here because we are converting into
1729
1994
        # the meta format. The meta format upgrader can take this to a 
1730
1995
        # future format within each component.
1731
 
        self.put_format('repository', bzrlib.repository.RepositoryFormat7())
 
1996
        self.put_format('repository', RepositoryFormat7())
1732
1997
        for entry in repository_names:
1733
1998
            self.move_entry('repository', entry)
1734
1999
 
1735
2000
        self.step('Upgrading branch      ')
1736
2001
        self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1737
2002
        self.make_lock('branch')
1738
 
        self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
 
2003
        self.put_format('branch', BzrBranchFormat5())
1739
2004
        branch_files = [('revision-history', True),
1740
2005
                        ('branch-name', True),
1741
2006
                        ('parent', False)]
1742
2007
        for entry in branch_files:
1743
2008
            self.move_entry('branch', entry)
1744
2009
 
1745
 
        self.step('Upgrading working tree')
1746
 
        self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1747
 
        self.make_lock('checkout')
1748
 
        self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1749
 
        self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1750
2010
        checkout_files = [('pending-merges', True),
1751
2011
                          ('inventory', True),
1752
2012
                          ('stat-cache', False)]
1753
 
        for entry in checkout_files:
1754
 
            self.move_entry('checkout', entry)
1755
 
        if last_revision is not None:
1756
 
            self.bzrdir._control_files.put_utf8('checkout/last-revision',
1757
 
                                                last_revision)
1758
 
        self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
 
2013
        # If a mandatory checkout file is not present, the branch does not have
 
2014
        # a functional checkout. Do not create a checkout in the converted
 
2015
        # branch.
 
2016
        for name, mandatory in checkout_files:
 
2017
            if mandatory and name not in bzrcontents:
 
2018
                has_checkout = False
 
2019
                break
 
2020
        else:
 
2021
            has_checkout = True
 
2022
        if not has_checkout:
 
2023
            self.pb.note('No working tree.')
 
2024
            # If some checkout files are there, we may as well get rid of them.
 
2025
            for name, mandatory in checkout_files:
 
2026
                if name in bzrcontents:
 
2027
                    self.bzrdir.transport.delete(name)
 
2028
        else:
 
2029
            from bzrlib.workingtree import WorkingTreeFormat3
 
2030
            self.step('Upgrading working tree')
 
2031
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
 
2032
            self.make_lock('checkout')
 
2033
            self.put_format(
 
2034
                'checkout', WorkingTreeFormat3())
 
2035
            self.bzrdir.transport.delete_multi(
 
2036
                self.garbage_inventories, self.pb)
 
2037
            for entry in checkout_files:
 
2038
                self.move_entry('checkout', entry)
 
2039
            if last_revision is not None:
 
2040
                self.bzrdir._control_files.put_utf8(
 
2041
                    'checkout/last-revision', last_revision)
 
2042
        self.bzrdir._control_files.put_utf8(
 
2043
            'branch-format', BzrDirMetaFormat1().get_format_string())
1759
2044
        return BzrDir.open(self.bzrdir.root_transport.base)
1760
2045
 
1761
2046
    def make_lock(self, name):
1762
2047
        """Make a lock for the new control dir name."""
1763
2048
        self.step('Make %s lock' % name)
1764
 
        ld = LockDir(self.bzrdir.transport, 
1765
 
                     '%s/lock' % name,
1766
 
                     file_modebits=self.file_mode,
1767
 
                     dir_modebits=self.dir_mode)
 
2049
        ld = lockdir.LockDir(self.bzrdir.transport,
 
2050
                             '%s/lock' % name,
 
2051
                             file_modebits=self.file_mode,
 
2052
                             dir_modebits=self.dir_mode)
1768
2053
        ld.create()
1769
2054
 
1770
2055
    def move_entry(self, new_dir, entry):
1809
2094
                self.pb.note('starting repository conversion')
1810
2095
                converter = CopyConverter(self.target_format.repository_format)
1811
2096
                converter.convert(repo, pb)
 
2097
        try:
 
2098
            branch = self.bzrdir.open_branch()
 
2099
        except errors.NotBranchError:
 
2100
            pass
 
2101
        else:
 
2102
            # TODO: conversions of Branch and Tree should be done by
 
2103
            # InterXFormat lookups
 
2104
            # Avoid circular imports
 
2105
            from bzrlib import branch as _mod_branch
 
2106
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
 
2107
                self.target_format.get_branch_format().__class__ is
 
2108
                _mod_branch.BzrBranchFormat6):
 
2109
                branch_converter = _mod_branch.Converter5to6()
 
2110
                branch_converter.convert(branch)
 
2111
        try:
 
2112
            tree = self.bzrdir.open_workingtree()
 
2113
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2114
            pass
 
2115
        else:
 
2116
            # TODO: conversions of Branch and Tree should be done by
 
2117
            # InterXFormat lookups
 
2118
            if (isinstance(tree, workingtree.WorkingTree3) and
 
2119
                not isinstance(tree, workingtree_4.WorkingTree4) and
 
2120
                isinstance(self.target_format.workingtree_format,
 
2121
                    workingtree_4.WorkingTreeFormat4)):
 
2122
                workingtree_4.Converter3to4().convert(tree)
1812
2123
        return to_convert
 
2124
 
 
2125
 
 
2126
class BzrDirFormatInfo(object):
 
2127
 
 
2128
    def __init__(self, native, deprecated):
 
2129
        self.deprecated = deprecated
 
2130
        self.native = native
 
2131
 
 
2132
 
 
2133
class BzrDirFormatRegistry(registry.Registry):
 
2134
    """Registry of user-selectable BzrDir subformats.
 
2135
    
 
2136
    Differs from BzrDirFormat._control_formats in that it provides sub-formats,
 
2137
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
2138
    """
 
2139
 
 
2140
    def register_metadir(self, key,
 
2141
             repository_format, help, native=True, deprecated=False,
 
2142
             branch_format=None,
 
2143
             tree_format=None):
 
2144
        """Register a metadir subformat.
 
2145
 
 
2146
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
 
2147
        by the Repository format.
 
2148
 
 
2149
        :param repository_format: The fully-qualified repository format class
 
2150
            name as a string.
 
2151
        :param branch_format: Fully-qualified branch format class name as
 
2152
            a string.
 
2153
        :param tree_format: Fully-qualified tree format class name as
 
2154
            a string.
 
2155
        """
 
2156
        # This should be expanded to support setting WorkingTree and Branch
 
2157
        # formats, once BzrDirMetaFormat1 supports that.
 
2158
        def _load(full_name):
 
2159
            mod_name, factory_name = full_name.rsplit('.', 1)
 
2160
            try:
 
2161
                mod = __import__(mod_name, globals(), locals(),
 
2162
                        [factory_name])
 
2163
            except ImportError, e:
 
2164
                raise ImportError('failed to load %s: %s' % (full_name, e))
 
2165
            try:
 
2166
                factory = getattr(mod, factory_name)
 
2167
            except AttributeError:
 
2168
                raise AttributeError('no factory %s in module %r'
 
2169
                    % (full_name, mod))
 
2170
            return factory()
 
2171
 
 
2172
        def helper():
 
2173
            bd = BzrDirMetaFormat1()
 
2174
            if branch_format is not None:
 
2175
                bd.set_branch_format(_load(branch_format))
 
2176
            if tree_format is not None:
 
2177
                bd.workingtree_format = _load(tree_format)
 
2178
            if repository_format is not None:
 
2179
                bd.repository_format = _load(repository_format)
 
2180
            return bd
 
2181
        self.register(key, helper, help, native, deprecated)
 
2182
 
 
2183
    def register(self, key, factory, help, native=True, deprecated=False):
 
2184
        """Register a BzrDirFormat factory.
 
2185
        
 
2186
        The factory must be a callable that takes one parameter: the key.
 
2187
        It must produce an instance of the BzrDirFormat when called.
 
2188
 
 
2189
        This function mainly exists to prevent the info object from being
 
2190
        supplied directly.
 
2191
        """
 
2192
        registry.Registry.register(self, key, factory, help, 
 
2193
            BzrDirFormatInfo(native, deprecated))
 
2194
 
 
2195
    def register_lazy(self, key, module_name, member_name, help, native=True,
 
2196
                      deprecated=False):
 
2197
        registry.Registry.register_lazy(self, key, module_name, member_name, 
 
2198
            help, BzrDirFormatInfo(native, deprecated))
 
2199
 
 
2200
    def set_default(self, key):
 
2201
        """Set the 'default' key to be a clone of the supplied key.
 
2202
        
 
2203
        This method must be called once and only once.
 
2204
        """
 
2205
        registry.Registry.register(self, 'default', self.get(key), 
 
2206
            self.get_help(key), info=self.get_info(key))
 
2207
 
 
2208
    def set_default_repository(self, key):
 
2209
        """Set the FormatRegistry default and Repository default.
 
2210
        
 
2211
        This is a transitional method while Repository.set_default_format
 
2212
        is deprecated.
 
2213
        """
 
2214
        if 'default' in self:
 
2215
            self.remove('default')
 
2216
        self.set_default(key)
 
2217
        format = self.get('default')()
 
2218
        assert isinstance(format, BzrDirMetaFormat1)
 
2219
 
 
2220
    def make_bzrdir(self, key):
 
2221
        return self.get(key)()
 
2222
 
 
2223
    def help_topic(self, topic):
 
2224
        output = textwrap.dedent("""\
 
2225
            Bazaar directory formats
 
2226
            ------------------------
 
2227
 
 
2228
            These formats can be used for creating branches, working trees, and
 
2229
            repositories.
 
2230
 
 
2231
            """)
 
2232
        default_help = self.get_help('default')
 
2233
        help_pairs = []
 
2234
        for key in self.keys():
 
2235
            if key == 'default':
 
2236
                continue
 
2237
            help = self.get_help(key)
 
2238
            if help == default_help:
 
2239
                default_realkey = key
 
2240
            else:
 
2241
                help_pairs.append((key, help))
 
2242
 
 
2243
        def wrapped(key, help, info):
 
2244
            if info.native:
 
2245
                help = '(native) ' + help
 
2246
            return '  %s:\n%s\n\n' % (key, 
 
2247
                    textwrap.fill(help, initial_indent='    ', 
 
2248
                    subsequent_indent='    '))
 
2249
        output += wrapped('%s/default' % default_realkey, default_help,
 
2250
                          self.get_info('default'))
 
2251
        deprecated_pairs = []
 
2252
        for key, help in help_pairs:
 
2253
            info = self.get_info(key)
 
2254
            if info.deprecated:
 
2255
                deprecated_pairs.append((key, help))
 
2256
            else:
 
2257
                output += wrapped(key, help, info)
 
2258
        if len(deprecated_pairs) > 0:
 
2259
            output += "Deprecated formats\n------------------\n\n"
 
2260
            for key, help in deprecated_pairs:
 
2261
                info = self.get_info(key)
 
2262
                output += wrapped(key, help, info)
 
2263
 
 
2264
        return output
 
2265
 
 
2266
 
 
2267
format_registry = BzrDirFormatRegistry()
 
2268
format_registry.register('weave', BzrDirFormat6,
 
2269
    'Pre-0.8 format.  Slower than knit and does not'
 
2270
    ' support checkouts or shared repositories.',
 
2271
    deprecated=True)
 
2272
format_registry.register_metadir('knit',
 
2273
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2274
    'Format using knits.  Recommended for interoperation with bzr <= 0.14.',
 
2275
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2276
    tree_format='bzrlib.workingtree.WorkingTreeFormat3')
 
2277
format_registry.register_metadir('metaweave',
 
2278
    'bzrlib.repofmt.weaverepo.RepositoryFormat7',
 
2279
    'Transitional format in 0.8.  Slower than knit.',
 
2280
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2281
    tree_format='bzrlib.workingtree.WorkingTreeFormat3',
 
2282
    deprecated=True)
 
2283
format_registry.register_metadir('dirstate',
 
2284
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2285
    help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
 
2286
        'above when accessed over the network.',
 
2287
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2288
    # this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
 
2289
    # directly from workingtree_4 triggers a circular import.
 
2290
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2291
    )
 
2292
format_registry.register_metadir('dirstate-with-subtree',
 
2293
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
 
2294
    help='New in 0.15: Fast local operations and improved scaling for '
 
2295
        'network operations. Additionally adds support for versioning nested '
 
2296
        'bzr branches. Incompatible with bzr < 0.15.',
 
2297
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2298
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2299
    )
 
2300
format_registry.set_default('dirstate')