~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

Optimize common case where unique_lcs returns a set of lines all in a row

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 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
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
 
 
 
23
from copy import deepcopy
 
24
import os
31
25
from cStringIO import StringIO
32
 
import os
33
 
 
34
 
from bzrlib.lazy_import import lazy_import
35
 
lazy_import(globals(), """
36
 
from copy import deepcopy
37
 
from stat import S_ISDIR
38
 
import unittest
 
26
from unittest import TestSuite
39
27
 
40
28
import bzrlib
41
 
from bzrlib import (
42
 
    errors,
43
 
    lockable_files,
44
 
    lockdir,
45
 
    revision as _mod_revision,
46
 
    urlutils,
47
 
    xml4,
48
 
    xml5,
49
 
    )
 
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
50
33
from bzrlib.osutils import (
51
 
    safe_unicode,
52
 
    sha_strings,
53
 
    sha_string,
54
 
    )
 
34
                            abspath,
 
35
                            pathjoin,
 
36
                            safe_unicode,
 
37
                            sha_strings,
 
38
                            sha_string,
 
39
                            )
55
40
from bzrlib.store.revision.text import TextRevisionStore
56
41
from bzrlib.store.text import TextStore
57
42
from bzrlib.store.versioned import WeaveStore
 
43
from bzrlib.symbol_versioning import *
 
44
from bzrlib.trace import mutter
58
45
from bzrlib.transactions import WriteTransaction
59
 
from bzrlib.transport import get_transport
 
46
from bzrlib.transport import get_transport, urlunescape
 
47
from bzrlib.transport.local import LocalTransport
60
48
from bzrlib.weave import Weave
61
 
""")
62
 
 
63
 
from bzrlib.trace import mutter
64
 
from bzrlib.transport.local import LocalTransport
 
49
from bzrlib.xml4 import serializer_v4
 
50
from bzrlib.xml5 import serializer_v5
65
51
 
66
52
 
67
53
class BzrDir(object):
98
84
        """Return true if this bzrdir is one whose format we can convert from."""
99
85
        return True
100
86
 
101
 
    def check_conversion_target(self, target_format):
102
 
        target_repo_format = target_format.repository_format
103
 
        source_repo_format = self._format.repository_format
104
 
        source_repo_format.check_conversion_target(target_repo_format)
105
 
 
106
87
    @staticmethod
107
88
    def _check_supported(format, allow_unsupported):
108
89
        """Check whether format is a supported format.
111
92
        """
112
93
        if not allow_unsupported and not format.is_supported():
113
94
            # see open_downlevel to open legacy branches.
114
 
            raise errors.UnsupportedFormatError(format=format)
 
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'])
115
100
 
116
101
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
117
102
        """Clone this bzrdir and its contents to url verbatim.
188
173
                    basis_repo = None
189
174
        return basis_repo, basis_branch, basis_tree
190
175
 
191
 
    # TODO: This should be given a Transport, and should chdir up; otherwise
192
 
    # this will open a new connection.
193
176
    def _make_tail(self, url):
194
 
        head, tail = urlutils.split(url)
195
 
        if tail and tail != '.':
196
 
            t = get_transport(head)
 
177
        segments = url.split('/')
 
178
        if segments and segments[-1] not in ('', '.'):
 
179
            parent = '/'.join(segments[:-1])
 
180
            t = bzrlib.transport.get_transport(parent)
197
181
            try:
198
 
                t.mkdir(tail)
 
182
                t.mkdir(segments[-1])
199
183
            except errors.FileExists:
200
184
                pass
201
185
 
202
 
    # TODO: Should take a Transport
203
186
    @classmethod
204
187
    def create(cls, base):
205
188
        """Create a new BzrDir at the url 'base'.
213
196
        if cls is not BzrDir:
214
197
            raise AssertionError("BzrDir.create always creates the default format, "
215
198
                    "not one of %r" % cls)
216
 
        head, tail = urlutils.split(base)
217
 
        if tail and tail != '.':
218
 
            t = get_transport(head)
 
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
203
            try:
220
 
                t.mkdir(tail)
 
204
                t.mkdir(segments[-1])
221
205
            except errors.FileExists:
222
206
                pass
223
207
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
309
293
        This will use the current default BzrDirFormat, and use whatever 
310
294
        repository format that that uses for bzrdirformat.create_repository.
311
295
 
312
 
        :param shared: Create a shared repository rather than a standalone
 
296
        ;param shared: Create a shared repository rather than a standalone
313
297
                       repository.
314
298
        The Repository object is returned.
315
299
 
318
302
        that child class desires.
319
303
        """
320
304
        bzrdir = BzrDir.create(base)
321
 
        return bzrdir.create_repository(shared)
 
305
        return bzrdir.create_repository()
322
306
 
323
307
    @staticmethod
324
308
    def create_standalone_workingtree(base):
330
314
        repository format that that uses for bzrdirformat.create_workingtree,
331
315
        create_branch and create_repository.
332
316
 
333
 
        :return: The WorkingTree object.
 
317
        The WorkingTree object is returned.
334
318
        """
335
319
        t = get_transport(safe_unicode(base))
336
320
        if not isinstance(t, LocalTransport):
346
330
        """
347
331
        raise NotImplementedError(self.create_workingtree)
348
332
 
349
 
    def destroy_workingtree(self):
350
 
        """Destroy the working tree at this BzrDir.
351
 
 
352
 
        Formats that do not support this may raise UnsupportedOperation.
353
 
        """
354
 
        raise NotImplementedError(self.destroy_workingtree)
355
 
 
356
 
    def destroy_workingtree_metadata(self):
357
 
        """Destroy the control files for the working tree at this BzrDir.
358
 
 
359
 
        The contents of working tree files are not affected.
360
 
        Formats that do not support this may raise UnsupportedOperation.
361
 
        """
362
 
        raise NotImplementedError(self.destroy_workingtree_metadata)
363
 
 
364
333
    def find_repository(self):
365
334
        """Find the repository that should be used for a_bzrdir.
366
335
 
374
343
            pass
375
344
        next_transport = self.root_transport.clone('..')
376
345
        while True:
377
 
            # find the next containing bzrdir
378
346
            try:
379
347
                found_bzrdir = BzrDir.open_containing_from_transport(
380
348
                    next_transport)[0]
381
349
            except errors.NotBranchError:
382
 
                # none found
383
350
                raise errors.NoRepositoryPresent(self)
384
 
            # does it have a repository ?
385
351
            try:
386
352
                repository = found_bzrdir.open_repository()
387
353
            except errors.NoRepositoryPresent:
388
354
                next_transport = found_bzrdir.root_transport.clone('..')
389
 
                if (found_bzrdir.root_transport.base == next_transport.base):
390
 
                    # top of the file system
391
 
                    break
392
 
                else:
393
 
                    continue
 
355
                continue
394
356
            if ((found_bzrdir.root_transport.base == 
395
357
                 self.root_transport.base) or repository.is_shared()):
396
358
                return repository
403
365
 
404
366
        Note that bzr dirs that do not support format strings will raise
405
367
        IncompatibleFormat if the branch format they are given has
406
 
        a format string, and vice versa.
 
368
        a format string, and vice verca.
407
369
 
408
370
        If branch_format is None, the transport is returned with no 
409
371
        checking. if it is not None, then the returned transport is
416
378
 
417
379
        Note that bzr dirs that do not support format strings will raise
418
380
        IncompatibleFormat if the repository format they are given has
419
 
        a format string, and vice versa.
 
381
        a format string, and vice verca.
420
382
 
421
383
        If repository_format is None, the transport is returned with no 
422
384
        checking. if it is not None, then the returned transport is
429
391
 
430
392
        Note that bzr dirs that do not support format strings will raise
431
393
        IncompatibleFormat if the workingtree format they are given has
432
 
        a format string, and vice versa.
 
394
        a format string, and vice verca.
433
395
 
434
396
        If workingtree_format is None, the transport is returned with no 
435
397
        checking. if it is not None, then the returned transport is
464
426
        # this might be better on the BzrDirFormat class because it refers to 
465
427
        # all the possible bzrdir disk formats. 
466
428
        # This method is tested via the workingtree is_control_filename tests- 
467
 
        # it was extracted from WorkingTree.is_control_filename. If the methods
 
429
        # it was extractd from WorkingTree.is_control_filename. If the methods
468
430
        # contract is extended beyond the current trivial  implementation please
469
431
        # add new tests for it to the appropriate place.
470
432
        return filename == '.bzr' or filename.startswith('.bzr/')
492
454
        _unsupported is a private parameter to the BzrDir class.
493
455
        """
494
456
        t = get_transport(base)
495
 
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
496
 
 
497
 
    @staticmethod
498
 
    def open_from_transport(transport, _unsupported=False):
499
 
        """Open a bzrdir within a particular directory.
500
 
 
501
 
        :param transport: Transport containing the bzrdir.
502
 
        :param _unsupported: private.
503
 
        """
504
 
        format = BzrDirFormat.find_format(transport)
 
457
        mutter("trying to open %r with transport %r", base, t)
 
458
        format = BzrDirFormat.find_format(t)
505
459
        BzrDir._check_supported(format, _unsupported)
506
 
        return format.open(transport, _found=True)
 
460
        return format.open(t, _found=True)
507
461
 
508
462
    def open_branch(self, unsupported=False):
509
463
        """Open the branch object at this BzrDir if one is present.
535
489
        If there is one and it is either an unrecognised format or an unsupported 
536
490
        format, UnknownFormatError or UnsupportedFormatError are raised.
537
491
        If there is one, it is returned, along with the unused portion of url.
538
 
 
539
 
        :return: The BzrDir that contains the path, and a Unicode path 
540
 
                for the rest of the URL.
541
492
        """
542
493
        # this gets the normalised url back. I.e. '.' -> the full path.
543
494
        url = a_transport.base
544
495
        while True:
545
496
            try:
546
 
                result = BzrDir.open_from_transport(a_transport)
547
 
                return result, urlutils.unescape(a_transport.relpath(url))
 
497
                format = BzrDirFormat.find_format(a_transport)
 
498
                BzrDir._check_supported(format, False)
 
499
                return format.open(a_transport), a_transport.relpath(url)
548
500
            except errors.NotBranchError, e:
549
 
                pass
 
501
                mutter('not a branch in: %r %s', a_transport.base, e)
550
502
            new_t = a_transport.clone('..')
551
503
            if new_t.base == a_transport.base:
552
504
                # reached the root, whatever that may be
601
553
        except errors.NoWorkingTree:
602
554
            return False
603
555
 
604
 
    def cloning_metadir(self, basis=None):
605
 
        """Produce a metadir suitable for cloning with"""
606
 
        def related_repository(bzrdir):
607
 
            try:
608
 
                branch = bzrdir.open_branch()
609
 
                return branch.repository
610
 
            except errors.NotBranchError:
611
 
                source_branch = None
612
 
                return bzrdir.open_repository()
613
 
        result_format = self._format.__class__()
614
 
        try:
615
 
            try:
616
 
                source_repository = related_repository(self)
617
 
            except errors.NoRepositoryPresent:
618
 
                if basis is None:
619
 
                    raise
620
 
                source_repository = related_repository(self)
621
 
            result_format.repository_format = source_repository._format
622
 
        except errors.NoRepositoryPresent:
623
 
            pass
624
 
        return result_format
625
 
 
626
556
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
627
557
        """Create a copy of this bzrdir prepared for use as a new line of
628
558
        development.
638
568
            itself to download less data.
639
569
        """
640
570
        self._make_tail(url)
641
 
        cloning_format = self.cloning_metadir(basis)
642
 
        result = cloning_format.initialize(url)
 
571
        result = self._format.initialize(url)
643
572
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
644
573
        try:
645
574
            source_branch = self.open_branch()
666
595
            result.create_repository()
667
596
        elif source_repository is not None and result_repo is None:
668
597
            # have source, and want to make a new target repo
669
 
            # we don't clone the repo because that preserves attributes
 
598
            # we dont clone the repo because that preserves attributes
670
599
            # like is_shared(), and we have not yet implemented a 
671
600
            # repository sprout().
672
601
            result_repo = result.create_repository()
676
605
                # XXX FIXME RBC 20060214 need tests for this when the basis
677
606
                # is incomplete
678
607
                result_repo.fetch(basis_repo, revision_id=revision_id)
679
 
            if source_repository is not None:
680
 
                result_repo.fetch(source_repository, revision_id=revision_id)
 
608
            result_repo.fetch(source_repository, revision_id=revision_id)
681
609
        if source_branch is not None:
682
610
            source_branch.sprout(result, revision_id=revision_id)
683
611
        else:
684
612
            result.create_branch()
685
 
        # TODO: jam 20060426 we probably need a test in here in the
686
 
        #       case that the newly sprouted branch is a remote one
687
613
        if result_repo is None or result_repo.make_working_trees():
688
 
            wt = result.create_workingtree()
689
 
            if wt.inventory.root is None:
690
 
                try:
691
 
                    wt.set_root_id(self.open_workingtree.get_root_id())
692
 
                except errors.NoWorkingTree:
693
 
                    pass
 
614
            result.create_workingtree()
694
615
        return result
695
616
 
696
617
 
700
621
    def __init__(self, _transport, _format):
701
622
        """See BzrDir.__init__."""
702
623
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
703
 
        assert self._format._lock_class == lockable_files.TransportLock
 
624
        assert self._format._lock_class == TransportLock
704
625
        assert self._format._lock_file_name == 'branch-lock'
705
 
        self._control_files = lockable_files.LockableFiles(
706
 
                                            self.get_branch_transport(None),
 
626
        self._control_files = LockableFiles(self.get_branch_transport(None),
707
627
                                            self._format._lock_file_name,
708
628
                                            self._format._lock_class)
709
629
 
753
673
        # done on this format anyway. So - acceptable wart.
754
674
        result = self.open_workingtree()
755
675
        if revision_id is not None:
756
 
            if revision_id == _mod_revision.NULL_REVISION:
757
 
                result.set_parent_ids([])
758
 
            else:
759
 
                result.set_parent_ids([revision_id])
 
676
            result.set_last_revision(revision_id)
760
677
        return result
761
678
 
762
 
    def destroy_workingtree(self):
763
 
        """See BzrDir.destroy_workingtree."""
764
 
        raise errors.UnsupportedOperation(self.destroy_workingtree, self)
765
 
 
766
 
    def destroy_workingtree_metadata(self):
767
 
        """See BzrDir.destroy_workingtree_metadata."""
768
 
        raise errors.UnsupportedOperation(self.destroy_workingtree_metadata, 
769
 
                                          self)
770
 
 
771
679
    def get_branch_transport(self, branch_format):
772
680
        """See BzrDir.get_branch_transport()."""
773
681
        if branch_format is None:
813
721
        self._check_supported(format, unsupported)
814
722
        return format.open(self, _found=True)
815
723
 
816
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
724
    def sprout(self, url, revision_id=None, basis=None):
817
725
        """See BzrDir.sprout()."""
818
726
        from bzrlib.workingtree import WorkingTreeFormat2
819
727
        self._make_tail(url)
913
821
        from bzrlib.workingtree import WorkingTreeFormat
914
822
        return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
915
823
 
916
 
    def destroy_workingtree(self):
917
 
        """See BzrDir.destroy_workingtree."""
918
 
        wt = self.open_workingtree()
919
 
        repository = wt.branch.repository
920
 
        empty = repository.revision_tree(bzrlib.revision.NULL_REVISION)
921
 
        wt.revert([], old_tree=empty)
922
 
        self.destroy_workingtree_metadata()
923
 
 
924
 
    def destroy_workingtree_metadata(self):
925
 
        self.transport.delete_tree('checkout')
926
 
 
927
824
    def _get_mkdir_mode(self):
928
825
        """Figure out the mode to use when creating a bzrdir subdir."""
929
 
        temp_control = lockable_files.LockableFiles(self.transport, '',
930
 
                                     lockable_files.TransportLock)
 
826
        temp_control = LockableFiles(self.transport, '', TransportLock)
931
827
        return temp_control._dir_mode
932
828
 
933
829
    def get_branch_transport(self, branch_format):
1035
931
    _formats = {}
1036
932
    """The known formats."""
1037
933
 
1038
 
    _control_formats = []
1039
 
    """The registered control formats - .bzr, ....
1040
 
    
1041
 
    This is a list of BzrDirFormat objects.
1042
 
    """
1043
 
 
1044
934
    _lock_file_name = 'branch-lock'
1045
935
 
1046
936
    # _lock_class must be set in subclasses to the lock type, typ.
1048
938
 
1049
939
    @classmethod
1050
940
    def find_format(klass, transport):
1051
 
        """Return the format present at transport."""
1052
 
        for format in klass._control_formats:
1053
 
            try:
1054
 
                return format.probe_transport(transport)
1055
 
            except errors.NotBranchError:
1056
 
                # this format does not find a control dir here.
1057
 
                pass
1058
 
        raise errors.NotBranchError(path=transport.base)
1059
 
 
1060
 
    @classmethod
1061
 
    def probe_transport(klass, transport):
1062
 
        """Return the .bzrdir style transport present at URL."""
 
941
        """Return the format registered for URL."""
1063
942
        try:
1064
943
            format_string = transport.get(".bzr/branch-format").read()
 
944
            return klass._formats[format_string]
1065
945
        except errors.NoSuchFile:
1066
946
            raise errors.NotBranchError(path=transport.base)
1067
 
 
1068
 
        try:
1069
 
            return klass._formats[format_string]
1070
947
        except KeyError:
1071
 
            raise errors.UnknownFormatError(format=format_string)
 
948
            raise errors.UnknownFormatError(format_string)
1072
949
 
1073
950
    @classmethod
1074
951
    def get_default_format(klass):
1089
966
        This returns a bzrlib.bzrdir.Converter object.
1090
967
 
1091
968
        This should return the best upgrader to step this format towards the
1092
 
        current default format. In the case of plugins we can/should provide
 
969
        current default format. In the case of plugins we can/shouold provide
1093
970
        some means for them to extend the range of returnable converters.
1094
971
 
1095
 
        :param format: Optional format to override the default format of the 
 
972
        :param format: Optional format to override the default foramt of the 
1096
973
                       library.
1097
974
        """
1098
975
        raise NotImplementedError(self.get_converter)
1107
984
 
1108
985
    def initialize_on_transport(self, transport):
1109
986
        """Initialize a new bzrdir in the base directory of a Transport."""
1110
 
        # Since we don't have a .bzr directory, inherit the
 
987
        # Since we don'transport have a .bzr directory, inherit the
1111
988
        # mode from the root directory
1112
 
        temp_control = lockable_files.LockableFiles(transport,
1113
 
                            '', lockable_files.TransportLock)
 
989
        temp_control = LockableFiles(transport, '', TransportLock)
1114
990
        temp_control._transport.mkdir('.bzr',
1115
 
                                      # FIXME: RBC 20060121 don't peek under
 
991
                                      # FIXME: RBC 20060121 dont peek under
1116
992
                                      # the covers
1117
993
                                      mode=temp_control._dir_mode)
1118
994
        file_mode = temp_control._file_mode
1125
1001
                      ('branch-format', self.get_format_string()),
1126
1002
                      ]
1127
1003
        # NB: no need to escape relative paths that are url safe.
1128
 
        control_files = lockable_files.LockableFiles(control,
1129
 
                            self._lock_file_name, self._lock_class)
 
1004
        control_files = LockableFiles(control, self._lock_file_name, 
 
1005
                                      self._lock_class)
1130
1006
        control_files.create_lock()
1131
1007
        control_files.lock_write()
1132
1008
        try:
1145
1021
        """
1146
1022
        return True
1147
1023
 
1148
 
    def same_model(self, target_format):
1149
 
        return (self.repository_format.rich_root_data == 
1150
 
            target_format.rich_root_data)
1151
 
 
1152
 
    @classmethod
1153
 
    def known_formats(klass):
1154
 
        """Return all the known formats.
1155
 
        
1156
 
        Concrete formats should override _known_formats.
1157
 
        """
1158
 
        # There is double indirection here to make sure that control 
1159
 
        # formats used by more than one dir format will only be probed 
1160
 
        # once. This can otherwise be quite expensive for remote connections.
1161
 
        result = set()
1162
 
        for format in klass._control_formats:
1163
 
            result.update(format._known_formats())
1164
 
        return result
1165
 
    
1166
 
    @classmethod
1167
 
    def _known_formats(klass):
1168
 
        """Return the known format instances for this control format."""
1169
 
        return set(klass._formats.values())
1170
 
 
1171
1024
    def open(self, transport, _found=False):
1172
1025
        """Return an instance of this format for the dir transport points at.
1173
1026
        
1174
1027
        _found is a private parameter, do not use it.
1175
1028
        """
1176
1029
        if not _found:
1177
 
            found_format = BzrDirFormat.find_format(transport)
1178
 
            if not isinstance(found_format, self.__class__):
1179
 
                raise AssertionError("%s was asked to open %s, but it seems to need "
1180
 
                        "format %s" 
1181
 
                        % (self, transport, found_format))
 
1030
            assert isinstance(BzrDirFormat.find_format(transport),
 
1031
                              self.__class__)
1182
1032
        return self._open(transport)
1183
1033
 
1184
1034
    def _open(self, transport):
1194
1044
        klass._formats[format.get_format_string()] = format
1195
1045
 
1196
1046
    @classmethod
1197
 
    def register_control_format(klass, format):
1198
 
        """Register a format that does not use '.bzrdir' for its control dir.
1199
 
 
1200
 
        TODO: This should be pulled up into a 'ControlDirFormat' base class
1201
 
        which BzrDirFormat can inherit from, and renamed to register_format 
1202
 
        there. It has been done without that for now for simplicity of
1203
 
        implementation.
1204
 
        """
1205
 
        klass._control_formats.append(format)
1206
 
 
1207
 
    @classmethod
1208
1047
    def set_default_format(klass, format):
1209
1048
        klass._default_format = format
1210
1049
 
1216
1055
        assert klass._formats[format.get_format_string()] is format
1217
1056
        del klass._formats[format.get_format_string()]
1218
1057
 
1219
 
    @classmethod
1220
 
    def unregister_control_format(klass, format):
1221
 
        klass._control_formats.remove(format)
1222
 
 
1223
 
 
1224
 
# register BzrDirFormat as a control format
1225
 
BzrDirFormat.register_control_format(BzrDirFormat)
1226
 
 
1227
1058
 
1228
1059
class BzrDirFormat4(BzrDirFormat):
1229
1060
    """Bzr dir format 4.
1238
1069
    removed in format 5; write support for this format has been removed.
1239
1070
    """
1240
1071
 
1241
 
    _lock_class = lockable_files.TransportLock
 
1072
    _lock_class = TransportLock
1242
1073
 
1243
1074
    def get_format_string(self):
1244
1075
        """See BzrDirFormat.get_format_string()."""
1273
1104
    def __return_repository_format(self):
1274
1105
        """Circular import protection."""
1275
1106
        from bzrlib.repository import RepositoryFormat4
1276
 
        return RepositoryFormat4()
 
1107
        return RepositoryFormat4(self)
1277
1108
    repository_format = property(__return_repository_format)
1278
1109
 
1279
1110
 
1288
1119
       Unhashed stores in the repository.
1289
1120
    """
1290
1121
 
1291
 
    _lock_class = lockable_files.TransportLock
 
1122
    _lock_class = TransportLock
1292
1123
 
1293
1124
    def get_format_string(self):
1294
1125
        """See BzrDirFormat.get_format_string()."""
1317
1148
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1318
1149
        RepositoryFormat5().initialize(result, _internal=True)
1319
1150
        if not _cloning:
1320
 
            branch = BzrBranchFormat4().initialize(result)
1321
 
            try:
1322
 
                WorkingTreeFormat2().initialize(result)
1323
 
            except errors.NotLocalUrl:
1324
 
                # Even though we can't access the working tree, we need to
1325
 
                # create its control files.
1326
 
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
 
1151
            BzrBranchFormat4().initialize(result)
 
1152
            WorkingTreeFormat2().initialize(result)
1327
1153
        return result
1328
1154
 
1329
1155
    def _open(self, transport):
1333
1159
    def __return_repository_format(self):
1334
1160
        """Circular import protection."""
1335
1161
        from bzrlib.repository import RepositoryFormat5
1336
 
        return RepositoryFormat5()
 
1162
        return RepositoryFormat5(self)
1337
1163
    repository_format = property(__return_repository_format)
1338
1164
 
1339
1165
 
1347
1173
     - Format 6 repositories [always]
1348
1174
    """
1349
1175
 
1350
 
    _lock_class = lockable_files.TransportLock
 
1176
    _lock_class = TransportLock
1351
1177
 
1352
1178
    def get_format_string(self):
1353
1179
        """See BzrDirFormat.get_format_string()."""
1376
1202
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1377
1203
        RepositoryFormat6().initialize(result, _internal=True)
1378
1204
        if not _cloning:
1379
 
            branch = BzrBranchFormat4().initialize(result)
 
1205
            BzrBranchFormat4().initialize(result)
1380
1206
            try:
1381
1207
                WorkingTreeFormat2().initialize(result)
1382
1208
            except errors.NotLocalUrl:
1383
 
                # Even though we can't access the working tree, we need to
1384
 
                # create its control files.
1385
 
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
 
1209
                # emulate pre-check behaviour for working tree and silently 
 
1210
                # fail.
 
1211
                pass
1386
1212
        return result
1387
1213
 
1388
1214
    def _open(self, transport):
1392
1218
    def __return_repository_format(self):
1393
1219
        """Circular import protection."""
1394
1220
        from bzrlib.repository import RepositoryFormat6
1395
 
        return RepositoryFormat6()
 
1221
        return RepositoryFormat6(self)
1396
1222
    repository_format = property(__return_repository_format)
1397
1223
 
1398
1224
 
1407
1233
     - Format 7 repositories [optional]
1408
1234
    """
1409
1235
 
1410
 
    _lock_class = lockdir.LockDir
 
1236
    _lock_class = LockDir
1411
1237
 
1412
1238
    def get_converter(self, format=None):
1413
1239
        """See BzrDirFormat.get_converter()."""
1467
1293
        self._formats = formats
1468
1294
    
1469
1295
    def adapt(self, test):
1470
 
        result = unittest.TestSuite()
 
1296
        result = TestSuite()
1471
1297
        for format in self._formats:
1472
1298
            new_test = deepcopy(test)
1473
1299
            new_test.transport_server = self._transport_server
1481
1307
        return result
1482
1308
 
1483
1309
 
 
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
 
1484
1376
class Converter(object):
1485
1377
    """Converts a disk format object from one format to another."""
1486
1378
 
1571
1463
        self.bzrdir.transport.delete_tree('text-store')
1572
1464
 
1573
1465
    def _convert_working_inv(self):
1574
 
        inv = xml4.serializer_v4.read_inventory(
1575
 
                    self.branch.control_files.get('inventory'))
1576
 
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
 
1466
        inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
 
1467
        new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1577
1468
        # FIXME inventory is a working tree change.
1578
 
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
 
1469
        self.branch.control_files.put('inventory', new_inv_xml)
1579
1470
 
1580
1471
    def _write_all_weaves(self):
1581
1472
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1605
1496
                                                      prefixed=False,
1606
1497
                                                      compressed=True))
1607
1498
        try:
1608
 
            transaction = WriteTransaction()
 
1499
            transaction = bzrlib.transactions.WriteTransaction()
1609
1500
            for i, rev_id in enumerate(self.converted_revs):
1610
1501
                self.pb.update('write revision', i, len(self.converted_revs))
1611
1502
                _revision_store.add_revision(self.revisions[rev_id], transaction)
1637
1528
    def _load_old_inventory(self, rev_id):
1638
1529
        assert rev_id not in self.converted_revs
1639
1530
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1640
 
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1641
 
        inv.revision_id = rev_id
 
1531
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1642
1532
        rev = self.revisions[rev_id]
1643
1533
        if rev.inventory_sha1:
1644
1534
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1648
1538
    def _load_updated_inventory(self, rev_id):
1649
1539
        assert rev_id in self.converted_revs
1650
1540
        inv_xml = self.inv_weave.get_text(rev_id)
1651
 
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
1541
        inv = serializer_v5.read_inventory_from_string(inv_xml)
1652
1542
        return inv
1653
1543
 
1654
1544
    def _convert_one_rev(self, rev_id):
1664
1554
    def _store_new_weave(self, rev, inv, present_parents):
1665
1555
        # the XML is now updated with text versions
1666
1556
        if __debug__:
1667
 
            entries = inv.iter_entries()
1668
 
            entries.next()
1669
 
            for path, ie in entries:
1670
 
                assert getattr(ie, 'revision', None) is not None, \
 
1557
            for file_id in inv:
 
1558
                ie = inv[file_id]
 
1559
                if ie.kind == 'root_directory':
 
1560
                    continue
 
1561
                assert hasattr(ie, 'revision'), \
1671
1562
                    'no revision on {%s} in {%s}' % \
1672
1563
                    (file_id, rev.revision_id)
1673
 
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
 
1564
        new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1674
1565
        new_inv_sha1 = sha_string(new_inv_xml)
1675
1566
        self.inv_weave.add_lines(rev.revision_id, 
1676
1567
                                 present_parents,
1685
1576
        mutter('converting texts of revision {%s}',
1686
1577
               rev_id)
1687
1578
        parent_invs = map(self._load_updated_inventory, present_parents)
1688
 
        entries = inv.iter_entries()
1689
 
        entries.next()
1690
 
        for path, ie in entries:
 
1579
        for file_id in inv:
 
1580
            ie = inv[file_id]
1691
1581
            self._convert_file_version(rev, ie, parent_invs)
1692
1582
 
1693
1583
    def _convert_file_version(self, rev, ie, parent_invs):
1696
1586
        The file needs to be added into the weave if it is a merge
1697
1587
        of >=2 parents or if it's changed from its parent.
1698
1588
        """
 
1589
        if ie.kind == 'root_directory':
 
1590
            return
1699
1591
        file_id = ie.file_id
1700
1592
        rev_id = rev.revision_id
1701
1593
        w = self.text_weaves.get(file_id)
1709
1601
                                                  entry_vf=w)
1710
1602
        for old_revision in previous_entries:
1711
1603
                # if this fails, its a ghost ?
1712
 
                assert old_revision in self.converted_revs, \
1713
 
                    "Revision {%s} not in converted_revs" % old_revision
 
1604
                assert old_revision in self.converted_revs 
1714
1605
        self.snapshot_ie(previous_entries, ie, w, rev_id)
1715
1606
        del ie.text_id
1716
1607
        assert getattr(ie, 'revision', None) is not None
1781
1672
            store_transport = self.bzrdir.transport.clone(store_name)
1782
1673
            store = TransportStore(store_transport, prefixed=True)
1783
1674
            for urlfilename in store_transport.list_dir('.'):
1784
 
                filename = urlutils.unescape(urlfilename)
 
1675
                filename = urlunescape(urlfilename)
1785
1676
                if (filename.endswith(".weave") or
1786
1677
                    filename.endswith(".gz") or
1787
1678
                    filename.endswith(".sig")):
1851
1742
        for entry in branch_files:
1852
1743
            self.move_entry('branch', entry)
1853
1744
 
 
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)
1854
1750
        checkout_files = [('pending-merges', True),
1855
1751
                          ('inventory', True),
1856
1752
                          ('stat-cache', False)]
1857
 
        # If a mandatory checkout file is not present, the branch does not have
1858
 
        # a functional checkout. Do not create a checkout in the converted
1859
 
        # branch.
1860
 
        for name, mandatory in checkout_files:
1861
 
            if mandatory and name not in bzrcontents:
1862
 
                has_checkout = False
1863
 
                break
1864
 
        else:
1865
 
            has_checkout = True
1866
 
        if not has_checkout:
1867
 
            self.pb.note('No working tree.')
1868
 
            # If some checkout files are there, we may as well get rid of them.
1869
 
            for name, mandatory in checkout_files:
1870
 
                if name in bzrcontents:
1871
 
                    self.bzrdir.transport.delete(name)
1872
 
        else:
1873
 
            self.step('Upgrading working tree')
1874
 
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1875
 
            self.make_lock('checkout')
1876
 
            self.put_format(
1877
 
                'checkout', bzrlib.workingtree.WorkingTreeFormat3())
1878
 
            self.bzrdir.transport.delete_multi(
1879
 
                self.garbage_inventories, self.pb)
1880
 
            for entry in checkout_files:
1881
 
                self.move_entry('checkout', entry)
1882
 
            if last_revision is not None:
1883
 
                self.bzrdir._control_files.put_utf8(
1884
 
                    'checkout/last-revision', last_revision)
1885
 
        self.bzrdir._control_files.put_utf8(
1886
 
            'branch-format', BzrDirMetaFormat1().get_format_string())
 
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())
1887
1759
        return BzrDir.open(self.bzrdir.root_transport.base)
1888
1760
 
1889
1761
    def make_lock(self, name):
1890
1762
        """Make a lock for the new control dir name."""
1891
1763
        self.step('Make %s lock' % name)
1892
 
        ld = lockdir.LockDir(self.bzrdir.transport,
1893
 
                             '%s/lock' % name,
1894
 
                             file_modebits=self.file_mode,
1895
 
                             dir_modebits=self.dir_mode)
 
1764
        ld = LockDir(self.bzrdir.transport, 
 
1765
                     '%s/lock' % name,
 
1766
                     file_modebits=self.file_mode,
 
1767
                     dir_modebits=self.dir_mode)
1896
1768
        ld.create()
1897
1769
 
1898
1770
    def move_entry(self, new_dir, entry):