~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Martin Pool
  • Date: 2006-06-20 03:30:14 UTC
  • mfrom: (1793 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1797.
  • Revision ID: mbp@sourcefrog.net-20060620033014-e19ce470e2ce6561
[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
import bzrlib.errors as errors
30
30
from bzrlib.lockable_files import LockableFiles, TransportLock
31
31
from bzrlib.lockdir import LockDir
32
 
from bzrlib.osutils import safe_unicode
33
32
from bzrlib.osutils import (
34
33
                            abspath,
35
34
                            pathjoin,
43
42
from bzrlib.symbol_versioning import *
44
43
from bzrlib.trace import mutter
45
44
from bzrlib.transactions import WriteTransaction
46
 
from bzrlib.transport import get_transport, urlunescape
 
45
from bzrlib.transport import get_transport
47
46
from bzrlib.transport.local import LocalTransport
 
47
import bzrlib.urlutils as urlutils
48
48
from bzrlib.weave import Weave
49
49
from bzrlib.xml4 import serializer_v4
50
50
import bzrlib.xml5
169
169
                    basis_repo = None
170
170
        return basis_repo, basis_branch, basis_tree
171
171
 
 
172
    # TODO: This should be given a Transport, and should chdir up; otherwise
 
173
    # this will open a new connection.
172
174
    def _make_tail(self, url):
173
 
        segments = url.split('/')
174
 
        if segments and segments[-1] not in ('', '.'):
175
 
            parent = '/'.join(segments[:-1])
176
 
            t = bzrlib.transport.get_transport(parent)
 
175
        head, tail = urlutils.split(url)
 
176
        if tail and tail != '.':
 
177
            t = bzrlib.transport.get_transport(head)
177
178
            try:
178
 
                t.mkdir(segments[-1])
 
179
                t.mkdir(tail)
179
180
            except errors.FileExists:
180
181
                pass
181
182
 
 
183
    # TODO: Should take a Transport
182
184
    @classmethod
183
185
    def create(cls, base):
184
186
        """Create a new BzrDir at the url 'base'.
192
194
        if cls is not BzrDir:
193
195
            raise AssertionError("BzrDir.create always creates the default format, "
194
196
                    "not one of %r" % cls)
195
 
        segments = base.split('/')
196
 
        if segments and segments[-1] not in ('', '.'):
197
 
            parent = '/'.join(segments[:-1])
198
 
            t = bzrlib.transport.get_transport(parent)
 
197
        head, tail = urlutils.split(base)
 
198
        if tail and tail != '.':
 
199
            t = bzrlib.transport.get_transport(head)
199
200
            try:
200
 
                t.mkdir(segments[-1])
 
201
                t.mkdir(tail)
201
202
            except errors.FileExists:
202
203
                pass
203
204
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
339
340
            pass
340
341
        next_transport = self.root_transport.clone('..')
341
342
        while True:
 
343
            # find the next containing bzrdir
342
344
            try:
343
345
                found_bzrdir = BzrDir.open_containing_from_transport(
344
346
                    next_transport)[0]
345
347
            except errors.NotBranchError:
 
348
                # none found
346
349
                raise errors.NoRepositoryPresent(self)
 
350
            # does it have a repository ?
347
351
            try:
348
352
                repository = found_bzrdir.open_repository()
349
353
            except errors.NoRepositoryPresent:
350
354
                next_transport = found_bzrdir.root_transport.clone('..')
351
 
                continue
 
355
                if (found_bzrdir.root_transport.base == next_transport.base):
 
356
                    # top of the file system
 
357
                    break
 
358
                else:
 
359
                    continue
352
360
            if ((found_bzrdir.root_transport.base == 
353
361
                 self.root_transport.base) or repository.is_shared()):
354
362
                return repository
361
369
 
362
370
        Note that bzr dirs that do not support format strings will raise
363
371
        IncompatibleFormat if the branch format they are given has
364
 
        a format string, and vice verca.
 
372
        a format string, and vice versa.
365
373
 
366
374
        If branch_format is None, the transport is returned with no 
367
375
        checking. if it is not None, then the returned transport is
374
382
 
375
383
        Note that bzr dirs that do not support format strings will raise
376
384
        IncompatibleFormat if the repository format they are given has
377
 
        a format string, and vice verca.
 
385
        a format string, and vice versa.
378
386
 
379
387
        If repository_format is None, the transport is returned with no 
380
388
        checking. if it is not None, then the returned transport is
387
395
 
388
396
        Note that bzr dirs that do not support format strings will raise
389
397
        IncompatibleFormat if the workingtree format they are given has
390
 
        a format string, and vice verca.
 
398
        a format string, and vice versa.
391
399
 
392
400
        If workingtree_format is None, the transport is returned with no 
393
401
        checking. if it is not None, then the returned transport is
422
430
        # this might be better on the BzrDirFormat class because it refers to 
423
431
        # all the possible bzrdir disk formats. 
424
432
        # This method is tested via the workingtree is_control_filename tests- 
425
 
        # it was extractd from WorkingTree.is_control_filename. If the methods
 
433
        # it was extracted from WorkingTree.is_control_filename. If the methods
426
434
        # contract is extended beyond the current trivial  implementation please
427
435
        # add new tests for it to the appropriate place.
428
436
        return filename == '.bzr' or filename.startswith('.bzr/')
485
493
        If there is one and it is either an unrecognised format or an unsupported 
486
494
        format, UnknownFormatError or UnsupportedFormatError are raised.
487
495
        If there is one, it is returned, along with the unused portion of url.
 
496
 
 
497
        :return: The BzrDir that contains the path, and a Unicode path 
 
498
                for the rest of the URL.
488
499
        """
489
500
        # this gets the normalised url back. I.e. '.' -> the full path.
490
501
        url = a_transport.base
492
503
            try:
493
504
                format = BzrDirFormat.find_format(a_transport)
494
505
                BzrDir._check_supported(format, False)
495
 
                return format.open(a_transport), a_transport.relpath(url)
 
506
                return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
496
507
            except errors.NotBranchError, e:
497
 
                mutter('not a branch in: %r %s', a_transport.base, e)
 
508
                ## mutter('not a branch in: %r %s', a_transport.base, e)
 
509
                pass
498
510
            new_t = a_transport.clone('..')
499
511
            if new_t.base == a_transport.base:
500
512
                # reached the root, whatever that may be
591
603
            result.create_repository()
592
604
        elif source_repository is not None and result_repo is None:
593
605
            # have source, and want to make a new target repo
594
 
            # we dont clone the repo because that preserves attributes
 
606
            # we don't clone the repo because that preserves attributes
595
607
            # like is_shared(), and we have not yet implemented a 
596
608
            # repository sprout().
597
609
            result_repo = result.create_repository()
606
618
            source_branch.sprout(result, revision_id=revision_id)
607
619
        else:
608
620
            result.create_branch()
 
621
        # TODO: jam 20060426 we probably need a test in here in the
 
622
        #       case that the newly sprouted branch is a remote one
609
623
        if result_repo is None or result_repo.make_working_trees():
610
624
            result.create_workingtree()
611
625
        return result
927
941
    _formats = {}
928
942
    """The known formats."""
929
943
 
 
944
    _control_formats = []
 
945
    """The registered control formats - .bzr, ....
 
946
    
 
947
    This is a list of BzrDirFormat objects.
 
948
    """
 
949
 
930
950
    _lock_file_name = 'branch-lock'
931
951
 
932
952
    # _lock_class must be set in subclasses to the lock type, typ.
934
954
 
935
955
    @classmethod
936
956
    def find_format(klass, transport):
937
 
        """Return the format registered for URL."""
 
957
        """Return the format present at transport."""
 
958
        for format in klass._control_formats:
 
959
            try:
 
960
                return format.probe_transport(transport)
 
961
            except errors.NotBranchError:
 
962
                # this format does not find a control dir here.
 
963
                pass
 
964
        raise errors.NotBranchError(path=transport.base)
 
965
 
 
966
    @classmethod
 
967
    def probe_transport(klass, transport):
 
968
        """Return the .bzrdir style transport present at URL."""
938
969
        try:
939
970
            format_string = transport.get(".bzr/branch-format").read()
 
971
        except errors.NoSuchFile:
 
972
            raise errors.NotBranchError(path=transport.base)
 
973
 
 
974
        try:
940
975
            return klass._formats[format_string]
941
 
        except errors.NoSuchFile:
942
 
            raise errors.NotBranchError(path=transport.base)
943
976
        except KeyError:
944
977
            raise errors.UnknownFormatError(format=format_string)
945
978
 
962
995
        This returns a bzrlib.bzrdir.Converter object.
963
996
 
964
997
        This should return the best upgrader to step this format towards the
965
 
        current default format. In the case of plugins we can/shouold provide
 
998
        current default format. In the case of plugins we can/should provide
966
999
        some means for them to extend the range of returnable converters.
967
1000
 
968
 
        :param format: Optional format to override the default foramt of the 
 
1001
        :param format: Optional format to override the default format of the 
969
1002
                       library.
970
1003
        """
971
1004
        raise NotImplementedError(self.get_converter)
980
1013
 
981
1014
    def initialize_on_transport(self, transport):
982
1015
        """Initialize a new bzrdir in the base directory of a Transport."""
983
 
        # Since we don'transport have a .bzr directory, inherit the
 
1016
        # Since we don't have a .bzr directory, inherit the
984
1017
        # mode from the root directory
985
1018
        temp_control = LockableFiles(transport, '', TransportLock)
986
1019
        temp_control._transport.mkdir('.bzr',
987
 
                                      # FIXME: RBC 20060121 dont peek under
 
1020
                                      # FIXME: RBC 20060121 don't peek under
988
1021
                                      # the covers
989
1022
                                      mode=temp_control._dir_mode)
990
1023
        file_mode = temp_control._file_mode
1017
1050
        """
1018
1051
        return True
1019
1052
 
 
1053
    @classmethod
 
1054
    def known_formats(klass):
 
1055
        """Return all the known formats.
 
1056
        
 
1057
        Concrete formats should override _known_formats.
 
1058
        """
 
1059
        # There is double indirection here to make sure that control 
 
1060
        # formats used by more than one dir format will only be probed 
 
1061
        # once. This can otherwise be quite expensive for remote connections.
 
1062
        result = set()
 
1063
        for format in klass._control_formats:
 
1064
            result.update(format._known_formats())
 
1065
        return result
 
1066
    
 
1067
    @classmethod
 
1068
    def _known_formats(klass):
 
1069
        """Return the known format instances for this control format."""
 
1070
        return set(klass._formats.values())
 
1071
 
1020
1072
    def open(self, transport, _found=False):
1021
1073
        """Return an instance of this format for the dir transport points at.
1022
1074
        
1040
1092
        klass._formats[format.get_format_string()] = format
1041
1093
 
1042
1094
    @classmethod
 
1095
    def register_control_format(klass, format):
 
1096
        """Register a format that does not use '.bzrdir' for its control dir.
 
1097
 
 
1098
        TODO: This should be pulled up into a 'ControlDirFormat' base class
 
1099
        which BzrDirFormat can inherit from, and renamed to register_format 
 
1100
        there. It has been done without that for now for simplicity of
 
1101
        implementation.
 
1102
        """
 
1103
        klass._control_formats.append(format)
 
1104
 
 
1105
    @classmethod
1043
1106
    def set_default_format(klass, format):
1044
1107
        klass._default_format = format
1045
1108
 
1051
1114
        assert klass._formats[format.get_format_string()] is format
1052
1115
        del klass._formats[format.get_format_string()]
1053
1116
 
 
1117
    @classmethod
 
1118
    def unregister_control_format(klass, format):
 
1119
        klass._control_formats.remove(format)
 
1120
 
 
1121
 
 
1122
# register BzrDirFormat as a control format
 
1123
BzrDirFormat.register_control_format(BzrDirFormat)
 
1124
 
1054
1125
 
1055
1126
class BzrDirFormat4(BzrDirFormat):
1056
1127
    """Bzr dir format 4.
1668
1739
            store_transport = self.bzrdir.transport.clone(store_name)
1669
1740
            store = TransportStore(store_transport, prefixed=True)
1670
1741
            for urlfilename in store_transport.list_dir('.'):
1671
 
                filename = urlunescape(urlfilename)
 
1742
                filename = urlutils.unescape(urlfilename)
1672
1743
                if (filename.endswith(".weave") or
1673
1744
                    filename.endswith(".gz") or
1674
1745
                    filename.endswith(".sig")):