~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Jelmer Vernooij
  • Date: 2012-01-06 22:44:57 UTC
  • mfrom: (6436 +trunk)
  • mto: (6437.3.11 2.5)
  • mto: This revision was merged to the branch mainline in revision 6444.
  • Revision ID: jelmer@samba.org-20120106224457-re0pcy0fz31xob77
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
objects returned.
26
26
"""
27
27
 
 
28
from __future__ import absolute_import
 
29
 
28
30
import sys
29
31
 
30
32
from bzrlib.lazy_import import lazy_import
784
786
    def __repr__(self):
785
787
        return "<%s at %r>" % (self.__class__.__name__, self.user_url)
786
788
 
 
789
    def update_feature_flags(self, updated_flags):
 
790
        """Update the features required by this bzrdir.
 
791
 
 
792
        :param updated_flags: Dictionary mapping feature names to necessities
 
793
            A necessity can be None to indicate the feature should be removed
 
794
        """
 
795
        self.control_files.lock_write()
 
796
        try:
 
797
            self._format._update_feature_flags(updated_flags)
 
798
            self.transport.put_bytes('branch-format', self._format.as_string())
 
799
        finally:
 
800
            self.control_files.unlock()
 
801
 
787
802
 
788
803
class BzrDirMeta1(BzrDir):
789
804
    """A .bzr meta version 1 control object.
794
809
    present within a BzrDir.
795
810
    """
796
811
 
 
812
    def __init__(self, _transport, _format):
 
813
        super(BzrDirMeta1, self).__init__(_transport, _format)
 
814
        self.control_files = lockable_files.LockableFiles(self.control_transport,
 
815
            self._format._lock_file_name, self._format._lock_class)
 
816
 
797
817
    def can_convert_format(self):
798
818
        """See BzrDir.can_convert_format()."""
799
819
        return True
991
1011
    BzrDirMeta1.
992
1012
    """
993
1013
 
994
 
    def __init__(self, _transport, _format):
995
 
        super(BzrDirMeta1Colo, self).__init__(_transport, _format)
996
 
        self.control_files = lockable_files.LockableFiles(self.control_transport,
997
 
            self._format._lock_file_name, self._format._lock_class)
998
 
 
999
1014
    def _get_branch_path(self, name):
1000
1015
        """Obtain the branch path to use.
1001
1016
 
1100
1115
        return self.transport.clone(path)
1101
1116
 
1102
1117
 
1103
 
class BzrDirMetaComponentFormat(controldir.ControlComponentFormat):
 
1118
class BzrFormat(object):
1104
1119
    """Base class for all formats of things living in metadirs.
1105
1120
 
1106
1121
    This class manages the format string that is stored in the 'format'
1111
1126
    (i.e. different from .bzr/branch-format) derive from this class,
1112
1127
    as well as the relevant base class for their kind
1113
1128
    (BranchFormat, WorkingTreeFormat, RepositoryFormat).
 
1129
 
 
1130
    Each format is identified by a "format" or "branch-format" file with a
 
1131
    single line containing the base format name and then an optional list of
 
1132
    feature flags.
 
1133
 
 
1134
    Feature flags are supported as of bzr 2.5. Setting feature flags on formats
 
1135
    will render them inaccessible to older versions of bzr.
 
1136
 
 
1137
    :ivar features: Dictionary mapping feature names to their necessity
1114
1138
    """
1115
1139
 
 
1140
    _present_features = set()
 
1141
 
 
1142
    def __init__(self):
 
1143
        self.features = {}
 
1144
 
 
1145
    @classmethod
 
1146
    def register_feature(cls, name):
 
1147
        """Register a feature as being present.
 
1148
 
 
1149
        :param name: Name of the feature
 
1150
        """
 
1151
        if " " in name:
 
1152
            raise ValueError("spaces are not allowed in feature names")
 
1153
        if name in cls._present_features:
 
1154
            raise errors.FeatureAlreadyRegistered(name)
 
1155
        cls._present_features.add(name)
 
1156
 
 
1157
    @classmethod
 
1158
    def unregister_feature(cls, name):
 
1159
        """Unregister a feature."""
 
1160
        cls._present_features.remove(name)
 
1161
 
 
1162
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
1163
            basedir=None):
 
1164
        for name, necessity in self.features.iteritems():
 
1165
            if name in self._present_features:
 
1166
                continue
 
1167
            if necessity == "optional":
 
1168
                mutter("ignoring optional missing feature %s", name)
 
1169
                continue
 
1170
            elif necessity == "required":
 
1171
                raise errors.MissingFeature(name)
 
1172
            else:
 
1173
                mutter("treating unknown necessity as require for %s",
 
1174
                       name)
 
1175
                raise errors.MissingFeature(name)
 
1176
 
1116
1177
    @classmethod
1117
1178
    def get_format_string(cls):
1118
1179
        """Return the ASCII format string that identifies this format."""
1119
1180
        raise NotImplementedError(cls.get_format_string)
1120
1181
 
1121
1182
    @classmethod
1122
 
    def from_string(cls, format_string):
1123
 
        if format_string != cls.get_format_string():
1124
 
            raise ValueError("Invalid format header %r" % format_string)
1125
 
        return cls()
 
1183
    def from_string(cls, text):
 
1184
        format_string = cls.get_format_string()
 
1185
        if not text.startswith(format_string):
 
1186
            raise AssertionError("Invalid format header %r for %r" % (text, cls))
 
1187
        lines = text[len(format_string):].splitlines()
 
1188
        ret = cls()
 
1189
        for lineno, line in enumerate(lines):
 
1190
            try:
 
1191
                (necessity, feature) = line.split(" ", 1)
 
1192
            except ValueError:
 
1193
                raise errors.ParseFormatError(format=cls, lineno=lineno+2,
 
1194
                    line=line, text=text)
 
1195
            ret.features[feature] = necessity
 
1196
        return ret
 
1197
 
 
1198
    def as_string(self):
 
1199
        """Return the string representation of this format.
 
1200
        """
 
1201
        lines = [self.get_format_string()]
 
1202
        lines.extend([("%s %s\n" % (item[1], item[0])) for item in
 
1203
            self.features.iteritems()])
 
1204
        return "".join(lines)
1126
1205
 
1127
1206
    @classmethod
1128
1207
    def _find_format(klass, registry, kind, format_string):
1129
1208
        try:
1130
 
            cls = registry.get(format_string)
 
1209
            first_line = format_string[:format_string.index("\n")+1]
 
1210
        except ValueError:
 
1211
            first_line = format_string
 
1212
        try:
 
1213
            cls = registry.get(first_line)
1131
1214
        except KeyError:
1132
 
            raise errors.UnknownFormatError(format=format_string, kind=kind)
1133
 
        return cls
 
1215
            raise errors.UnknownFormatError(format=first_line, kind=kind)
 
1216
        return cls.from_string(format_string)
1134
1217
 
1135
1218
    def network_name(self):
1136
1219
        """A simple byte string uniquely identifying this format for RPC calls.
1137
1220
 
1138
1221
        Metadir branch formats use their format string.
1139
1222
        """
1140
 
        return self.get_format_string()
 
1223
        return self.as_string()
1141
1224
 
1142
1225
    def __eq__(self, other):
1143
 
        return (self.__class__ is other.__class__)
 
1226
        return (self.__class__ is other.__class__ and
 
1227
                self.features == other.features)
 
1228
 
 
1229
    def _update_feature_flags(self, updated_flags):
 
1230
        """Update the feature flags in this format.
 
1231
 
 
1232
        :param updated_flags: Updated feature flags
 
1233
        """
 
1234
        for name, necessity in updated_flags.iteritems():
 
1235
            if necessity is None:
 
1236
                try:
 
1237
                    del self.features[name]
 
1238
                except KeyError:
 
1239
                    pass
 
1240
            else:
 
1241
                self.features[name] = necessity
1144
1242
 
1145
1243
 
1146
1244
class BzrProber(controldir.Prober):
1167
1265
        except errors.NoSuchFile:
1168
1266
            raise errors.NotBranchError(path=transport.base)
1169
1267
        try:
1170
 
            cls = klass.formats.get(format_string)
 
1268
            first_line = format_string[:format_string.index("\n")+1]
 
1269
        except ValueError:
 
1270
            first_line = format_string
 
1271
        try:
 
1272
            cls = klass.formats.get(first_line)
1171
1273
        except KeyError:
1172
 
            raise errors.UnknownFormatError(format=format_string, kind='bzrdir')
 
1274
            raise errors.UnknownFormatError(format=first_line, kind='bzrdir')
1173
1275
        return cls.from_string(format_string)
1174
1276
 
1175
1277
    @classmethod
1219
1321
        return set([RemoteBzrDirFormat()])
1220
1322
 
1221
1323
 
1222
 
class BzrDirFormat(controldir.ControlDirFormat):
 
1324
class BzrDirFormat(BzrFormat, controldir.ControlDirFormat):
1223
1325
    """ControlDirFormat base class for .bzr/ directories.
1224
1326
 
1225
1327
    Formats are placed in a dict by their format string for reference
1236
1338
    # _lock_class must be set in subclasses to the lock type, typ.
1237
1339
    # TransportLock or LockDir
1238
1340
 
1239
 
    @classmethod
1240
 
    def get_format_string(cls):
1241
 
        """Return the ASCII format string that identifies this format."""
1242
 
        raise NotImplementedError(cls.get_format_string)
1243
 
 
1244
1341
    def initialize_on_transport(self, transport):
1245
1342
        """Initialize a new bzrdir in the base directory of a Transport."""
1246
1343
        try:
1381
1478
                       "This is a Bazaar control directory.\n"
1382
1479
                       "Do not change any files in this directory.\n"
1383
1480
                       "See http://bazaar.canonical.com/ for more information about Bazaar.\n"),
1384
 
                      ('branch-format', self.get_format_string()),
 
1481
                      ('branch-format', self.as_string()),
1385
1482
                      ]
1386
1483
        # NB: no need to escape relative paths that are url safe.
1387
1484
        control_files = lockable_files.LockableFiles(bzrdir_transport,
1431
1528
            compatible with whatever sub formats are supported by self.
1432
1529
        :return: None.
1433
1530
        """
 
1531
        other_format.features = dict(self.features)
1434
1532
 
1435
1533
    def supports_transport(self, transport):
1436
1534
        # bzr formats can be opened over all known transports
1437
1535
        return True
1438
1536
 
 
1537
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
1538
            basedir=None):
 
1539
        controldir.ControlDirFormat.check_support_status(self,
 
1540
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
1541
            basedir=basedir)
 
1542
        BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
1543
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
1544
 
1439
1545
 
1440
1546
class BzrDirMetaFormat1(BzrDirFormat):
1441
1547
    """Bzr meta control format 1
1457
1563
    colocated_branches = False
1458
1564
 
1459
1565
    def __init__(self):
 
1566
        BzrDirFormat.__init__(self)
1460
1567
        self._workingtree_format = None
1461
1568
        self._branch_format = None
1462
1569
        self._repository_format = None
1468
1575
            return False
1469
1576
        if other.workingtree_format != self.workingtree_format:
1470
1577
            return False
 
1578
        if other.features != self.features:
 
1579
            return False
1471
1580
        return True
1472
1581
 
1473
1582
    def __ne__(self, other):
1599
1708
        """See BzrDirFormat.get_format_description()."""
1600
1709
        return "Meta directory format 1"
1601
1710
 
1602
 
    @classmethod
1603
 
    def from_string(cls, format_string):
1604
 
        if format_string != cls.get_format_string():
1605
 
            raise ValueError("Invalid format string %r" % format_string)
1606
 
        return cls()
1607
 
 
1608
 
    def network_name(self):
1609
 
        return self.get_format_string()
1610
 
 
1611
1711
    def _open(self, transport):
1612
1712
        """See BzrDirFormat._open."""
1613
1713
        # Create a new format instance because otherwise initialisation of new
1642
1742
            compatible with whatever sub formats are supported by self.
1643
1743
        :return: None.
1644
1744
        """
 
1745
        super(BzrDirMetaFormat1, self)._supply_sub_formats_to(other_format)
1645
1746
        if getattr(self, '_repository_format', None) is not None:
1646
1747
            other_format.repository_format = self.repository_format
1647
1748
        if self._branch_format is not None:
1792
1893
    def convert(self, to_convert, pb):
1793
1894
        """See Converter.convert()."""
1794
1895
        to_convert.transport.put_bytes('branch-format',
1795
 
            self.target_format.get_format_string())
 
1896
            self.target_format.as_string())
1796
1897
        return BzrDir.open_from_transport(to_convert.root_transport)
1797
1898
 
1798
1899
 
1818
1919
        finally:
1819
1920
            to_convert.control_files.unlock()
1820
1921
        to_convert.transport.put_bytes('branch-format',
1821
 
            self.target_format.get_format_string())
 
1922
            self.target_format.as_string())
1822
1923
        return BzrDir.open_from_transport(to_convert.root_transport)
1823
1924
 
1824
1925
 
1917
2018
        :return: A repository, is_new_flag (True if the repository was
1918
2019
            created).
1919
2020
        """
1920
 
        raise NotImplemented(RepositoryAcquisitionPolicy.acquire_repository)
 
2021
        raise NotImplementedError(RepositoryAcquisitionPolicy.acquire_repository)
1921
2022
 
1922
2023
 
1923
2024
class CreateRepository(RepositoryAcquisitionPolicy):