~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Martin Packman
  • Date: 2012-01-05 10:37:58 UTC
  • mto: This revision was merged to the branch mainline in revision 6427.
  • Revision ID: martin.packman@canonical.com-20120105103758-wzftnmsip5iv9n2g
Revert addition of get_message_encoding function

Show diffs side-by-side

added added

removed removed

Lines of Context:
54
54
    generate_ids,
55
55
    globbing,
56
56
    graph as _mod_graph,
57
 
    hashcache,
58
57
    ignores,
59
58
    inventory,
60
59
    merge,
72
71
 
73
72
from bzrlib import symbol_versioning
74
73
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
74
from bzrlib.i18n import gettext
75
75
from bzrlib.lock import LogicalLockResult
76
76
import bzrlib.mutabletree
77
77
from bzrlib.mutabletree import needs_tree_write_lock
172
172
 
173
173
    def __init__(self, basedir='.',
174
174
                 branch=DEPRECATED_PARAMETER,
175
 
                 _control_files=None,
176
175
                 _internal=False,
 
176
                 _transport=None,
177
177
                 _format=None,
178
178
                 _bzrdir=None):
179
179
        """Construct a WorkingTree instance. This is not a public API.
192
192
        else:
193
193
            self._branch = self.bzrdir.open_branch()
194
194
        self.basedir = realpath(basedir)
195
 
        self._control_files = _control_files
196
 
        self._transport = self._control_files._transport
197
 
        # update the whole cache up front and write to disk if anything changed;
198
 
        # in the future we might want to do this more selectively
199
 
        # two possible ways offer themselves : in self._unlock, write the cache
200
 
        # if needed, or, when the cache sees a change, append it to the hash
201
 
        # cache file, and have the parser take the most recent entry for a
202
 
        # given path only.
203
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
204
 
        cache_filename = wt_trans.local_abspath('stat-cache')
205
 
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
206
 
            self.bzrdir._get_file_mode(),
207
 
            self._content_filter_stack_provider())
208
 
        hc = self._hashcache
209
 
        hc.read()
210
 
        # is this scan needed ? it makes things kinda slow.
211
 
        #hc.scan()
212
 
 
213
 
        if hc.needs_write:
214
 
            mutter("write hc")
215
 
            hc.write()
216
 
 
217
 
        self._detect_case_handling()
 
195
        self._transport = _transport
218
196
        self._rules_searcher = None
219
197
        self.views = self._make_views()
220
198
 
238
216
        """
239
217
        return self.bzrdir.is_control_filename(filename)
240
218
 
241
 
    def _detect_case_handling(self):
242
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
243
 
        try:
244
 
            wt_trans.stat(self._format.case_sensitive_filename)
245
 
        except errors.NoSuchFile:
246
 
            self.case_sensitive = True
247
 
        else:
248
 
            self.case_sensitive = False
249
 
 
250
 
        self._setup_directory_is_tree_reference()
251
 
 
252
219
    branch = property(
253
220
        fget=lambda self: self._branch,
254
221
        doc="""The branch this WorkingTree is connected to.
257
224
            the working tree has been constructed from.
258
225
            """)
259
226
 
 
227
    def has_versioned_directories(self):
 
228
        """See `Tree.has_versioned_directories`."""
 
229
        return self._format.supports_versioned_directories
 
230
 
260
231
    def break_lock(self):
261
232
        """Break a lock if one is present from another instance.
262
233
 
265
236
 
266
237
        This will probe the repository for its lock as well.
267
238
        """
268
 
        self._control_files.break_lock()
269
 
        self.branch.break_lock()
 
239
        raise NotImplementedError(self.break_lock)
270
240
 
271
241
    def requires_rich_root(self):
272
242
        return self._format.requires_rich_root
287
257
        """
288
258
        if path is None:
289
259
            path = osutils.getcwd()
290
 
        control = bzrdir.BzrDir.open(path, _unsupported)
 
260
        control = controldir.ControlDir.open(path, _unsupported)
291
261
        return control.open_workingtree(_unsupported)
292
262
 
293
263
    @staticmethod
305
275
        """
306
276
        if path is None:
307
277
            path = osutils.getcwd()
308
 
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
278
        control, relpath = controldir.ControlDir.open_containing(path)
309
279
        return control.open_workingtree(), relpath
310
280
 
311
281
    @staticmethod
331
301
                if view_files:
332
302
                    file_list = view_files
333
303
                    view_str = views.view_display_str(view_files)
334
 
                    note("Ignoring files outside view. View is %s" % view_str)
 
304
                    note(gettext("Ignoring files outside view. View is %s") % view_str)
335
305
            return tree, file_list
336
306
        if default_directory == u'.':
337
307
            seed = file_list[0]
394
364
            else:
395
365
                return True, tree
396
366
        t = transport.get_transport(location)
397
 
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
 
367
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
398
368
                                              list_current=list_current)
399
369
        return [tr for tr in iterator if tr is not None]
400
370
 
522
492
        raise NotImplementedError(self.get_root_id)
523
493
 
524
494
    @needs_read_lock
525
 
    def clone(self, to_bzrdir, revision_id=None):
 
495
    def clone(self, to_controldir, revision_id=None):
526
496
        """Duplicate this working tree into to_bzr, including all state.
527
497
 
528
498
        Specifically modified files are kept as modified, but
529
499
        ignored and unknown files are discarded.
530
500
 
531
 
        If you want to make a new line of development, see bzrdir.sprout()
 
501
        If you want to make a new line of development, see ControlDir.sprout()
532
502
 
533
503
        revision
534
504
            If not None, the cloned tree will have its last revision set to
536
506
            and this one merged in.
537
507
        """
538
508
        # assumes the target bzr dir format is compatible.
539
 
        result = to_bzrdir.create_workingtree()
 
509
        result = to_controldir.create_workingtree()
540
510
        self.copy_content_into(result, revision_id)
541
511
        return result
542
512
 
550
520
            # TODO now merge from tree.last_revision to revision (to preserve
551
521
            # user local changes)
552
522
            merge.transform_tree(tree, self)
553
 
            tree.set_parent_ids([revision_id])
 
523
            if revision_id == _mod_revision.NULL_REVISION:
 
524
                new_parents = []
 
525
            else:
 
526
                new_parents = [revision_id]
 
527
            tree.set_parent_ids(new_parents)
554
528
 
555
529
    def id2abspath(self, file_id):
556
530
        return self.abspath(self.id2path(file_id))
595
569
            else:
596
570
                return None
597
571
 
598
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
599
 
        # FIXME: Shouldn't this be in Tree?
600
 
        raise NotImplementedError(self.get_file_sha1)
601
 
 
602
572
    @needs_tree_write_lock
603
573
    def _gather_kinds(self, files, kinds):
604
574
        """See MutableTree._gather_kinds."""
769
739
 
770
740
    @needs_tree_write_lock
771
741
    def set_merge_modified(self, modified_hashes):
772
 
        def iter_stanzas():
773
 
            for file_id, hash in modified_hashes.iteritems():
774
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
775
 
                    hash=hash)
776
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
742
        """Set the merge modified hashes."""
 
743
        raise NotImplementedError(self.set_merge_modified)
777
744
 
778
745
    def _sha_from_stat(self, path, stat_result):
779
746
        """Get a sha digest from the tree's stat cache.
785
752
        """
786
753
        return None
787
754
 
788
 
    def _put_rio(self, filename, stanzas, header):
789
 
        self._must_be_locked()
790
 
        my_file = _mod_rio.rio_file(stanzas, header)
791
 
        self._transport.put_file(filename, my_file,
792
 
            mode=self.bzrdir._get_file_mode())
793
 
 
794
755
    @needs_write_lock # because merge pulls data into the branch.
795
756
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
796
757
                          merge_type=None, force=False):
1036
997
                                show_base=show_base)
1037
998
                    basis_root_id = basis_tree.get_root_id()
1038
999
                    new_root_id = new_basis_tree.get_root_id()
1039
 
                    if basis_root_id != new_root_id:
 
1000
                    if new_root_id is not None and basis_root_id != new_root_id:
1040
1001
                        self.set_root_id(new_root_id)
1041
1002
                finally:
1042
1003
                    basis_tree.unlock()
1043
1004
                # TODO - dedup parents list with things merged by pull ?
1044
1005
                # reuse the revisiontree we merged against to set the new
1045
1006
                # tree data.
1046
 
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
 
1007
                parent_trees = []
 
1008
                if self.branch.last_revision() != _mod_revision.NULL_REVISION:
 
1009
                    parent_trees.append(
 
1010
                        (self.branch.last_revision(), new_basis_tree))
1047
1011
                # we have to pull the merge trees out again, because
1048
1012
                # merge_inner has set the ids. - this corner is not yet
1049
1013
                # layered well enough to prevent double handling.
1066
1030
            stream.write(bytes)
1067
1031
        finally:
1068
1032
            stream.close()
1069
 
        # TODO: update the hashcache here ?
1070
1033
 
1071
1034
    def extras(self):
1072
1035
        """Yield all unversioned files in this WorkingTree.
1174
1137
        return _mod_revision.ensure_null(self.branch.last_revision())
1175
1138
 
1176
1139
    def is_locked(self):
1177
 
        return self._control_files.is_locked()
1178
 
 
1179
 
    def _must_be_locked(self):
1180
 
        if not self.is_locked():
1181
 
            raise errors.ObjectNotLocked(self)
 
1140
        """Check if this tree is locked."""
 
1141
        raise NotImplementedError(self.is_locked)
1182
1142
 
1183
1143
    def lock_read(self):
1184
1144
        """Lock the tree for reading.
1187
1147
 
1188
1148
        :return: A bzrlib.lock.LogicalLockResult.
1189
1149
        """
1190
 
        if not self.is_locked():
1191
 
            self._reset_data()
1192
 
        self.branch.lock_read()
1193
 
        try:
1194
 
            self._control_files.lock_read()
1195
 
            return LogicalLockResult(self.unlock)
1196
 
        except:
1197
 
            self.branch.unlock()
1198
 
            raise
 
1150
        raise NotImplementedError(self.lock_read)
1199
1151
 
1200
1152
    def lock_tree_write(self):
1201
1153
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1202
1154
 
1203
1155
        :return: A bzrlib.lock.LogicalLockResult.
1204
1156
        """
1205
 
        if not self.is_locked():
1206
 
            self._reset_data()
1207
 
        self.branch.lock_read()
1208
 
        try:
1209
 
            self._control_files.lock_write()
1210
 
            return LogicalLockResult(self.unlock)
1211
 
        except:
1212
 
            self.branch.unlock()
1213
 
            raise
 
1157
        raise NotImplementedError(self.lock_tree_write)
1214
1158
 
1215
1159
    def lock_write(self):
1216
1160
        """See MutableTree.lock_write, and WorkingTree.unlock.
1217
1161
 
1218
1162
        :return: A bzrlib.lock.LogicalLockResult.
1219
1163
        """
1220
 
        if not self.is_locked():
1221
 
            self._reset_data()
1222
 
        self.branch.lock_write()
1223
 
        try:
1224
 
            self._control_files.lock_write()
1225
 
            return LogicalLockResult(self.unlock)
1226
 
        except:
1227
 
            self.branch.unlock()
1228
 
            raise
 
1164
        raise NotImplementedError(self.lock_write)
1229
1165
 
1230
1166
    def get_physical_lock_status(self):
1231
 
        return self._control_files.get_physical_lock_status()
1232
 
 
1233
 
    def _reset_data(self):
1234
 
        """Reset transient data that cannot be revalidated."""
1235
 
        raise NotImplementedError(self._reset_data)
 
1167
        raise NotImplementedError(self.get_physical_lock_status)
1236
1168
 
1237
1169
    def set_last_revision(self, new_revision):
1238
1170
        """Change the last revision in the working tree."""
1532
1464
                                             show_base=show_base)
1533
1465
            if nb_conflicts:
1534
1466
                self.add_parent_tree((old_tip, other_tree))
1535
 
                note('Rerun update after fixing the conflicts.')
 
1467
                note(gettext('Rerun update after fixing the conflicts.'))
1536
1468
                return nb_conflicts
1537
1469
 
1538
1470
        if last_rev != _mod_revision.ensure_null(revision):
1580
1512
            last_rev = parent_trees[0][0]
1581
1513
        return nb_conflicts
1582
1514
 
1583
 
    def _write_hashcache_if_dirty(self):
1584
 
        """Write out the hashcache if it is dirty."""
1585
 
        if self._hashcache.needs_write:
1586
 
            try:
1587
 
                self._hashcache.write()
1588
 
            except OSError, e:
1589
 
                if e.errno not in (errno.EPERM, errno.EACCES):
1590
 
                    raise
1591
 
                # TODO: jam 20061219 Should this be a warning? A single line
1592
 
                #       warning might be sufficient to let the user know what
1593
 
                #       is going on.
1594
 
                mutter('Could not write hashcache for %s\nError: %s',
1595
 
                              self._hashcache.cache_file_name(), e)
1596
 
 
1597
1515
    def set_conflicts(self, arg):
1598
1516
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1599
1517
 
1828
1746
        :param branch: A branch to override probing for the branch.
1829
1747
        """
1830
1748
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1831
 
            branch=branch, _control_files=_control_files, _internal=_internal,
1832
 
            _format=_format, _bzrdir=_bzrdir)
 
1749
            branch=branch, _transport=_control_files._transport,
 
1750
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
 
1751
 
 
1752
        self._control_files = _control_files
 
1753
        self._detect_case_handling()
1833
1754
 
1834
1755
        if _inventory is None:
1835
1756
            # This will be acquired on lock_read() or lock_write()
1855
1776
        self._inventory = inv
1856
1777
        self._inventory_is_modified = dirty
1857
1778
 
 
1779
    def _detect_case_handling(self):
 
1780
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
1781
        try:
 
1782
            wt_trans.stat(self._format.case_sensitive_filename)
 
1783
        except errors.NoSuchFile:
 
1784
            self.case_sensitive = True
 
1785
        else:
 
1786
            self.case_sensitive = False
 
1787
 
 
1788
        self._setup_directory_is_tree_reference()
 
1789
 
1858
1790
    def _serialize(self, inventory, out_file):
1859
1791
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1860
1792
            working=True)
1862
1794
    def _deserialize(selt, in_file):
1863
1795
        return xml5.serializer_v5.read_inventory(in_file)
1864
1796
 
 
1797
    def break_lock(self):
 
1798
        """Break a lock if one is present from another instance.
 
1799
 
 
1800
        Uses the ui factory to ask for confirmation if the lock may be from
 
1801
        an active process.
 
1802
 
 
1803
        This will probe the repository for its lock as well.
 
1804
        """
 
1805
        self._control_files.break_lock()
 
1806
        self.branch.break_lock()
 
1807
 
 
1808
    def is_locked(self):
 
1809
        return self._control_files.is_locked()
 
1810
 
 
1811
    def _must_be_locked(self):
 
1812
        if not self.is_locked():
 
1813
            raise errors.ObjectNotLocked(self)
 
1814
 
 
1815
    def lock_read(self):
 
1816
        """Lock the tree for reading.
 
1817
 
 
1818
        This also locks the branch, and can be unlocked via self.unlock().
 
1819
 
 
1820
        :return: A bzrlib.lock.LogicalLockResult.
 
1821
        """
 
1822
        if not self.is_locked():
 
1823
            self._reset_data()
 
1824
        self.branch.lock_read()
 
1825
        try:
 
1826
            self._control_files.lock_read()
 
1827
            return LogicalLockResult(self.unlock)
 
1828
        except:
 
1829
            self.branch.unlock()
 
1830
            raise
 
1831
 
 
1832
    def lock_tree_write(self):
 
1833
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
1834
 
 
1835
        :return: A bzrlib.lock.LogicalLockResult.
 
1836
        """
 
1837
        if not self.is_locked():
 
1838
            self._reset_data()
 
1839
        self.branch.lock_read()
 
1840
        try:
 
1841
            self._control_files.lock_write()
 
1842
            return LogicalLockResult(self.unlock)
 
1843
        except:
 
1844
            self.branch.unlock()
 
1845
            raise
 
1846
 
 
1847
    def lock_write(self):
 
1848
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
1849
 
 
1850
        :return: A bzrlib.lock.LogicalLockResult.
 
1851
        """
 
1852
        if not self.is_locked():
 
1853
            self._reset_data()
 
1854
        self.branch.lock_write()
 
1855
        try:
 
1856
            self._control_files.lock_write()
 
1857
            return LogicalLockResult(self.unlock)
 
1858
        except:
 
1859
            self.branch.unlock()
 
1860
            raise
 
1861
 
 
1862
    def get_physical_lock_status(self):
 
1863
        return self._control_files.get_physical_lock_status()
 
1864
 
1865
1865
    @needs_tree_write_lock
1866
1866
    def _write_inventory(self, inv):
1867
1867
        """Write inventory as the current inventory."""
2164
2164
            mode=self.bzrdir._get_file_mode())
2165
2165
        self._inventory_is_modified = False
2166
2166
 
2167
 
    @needs_read_lock
2168
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2169
 
        if not path:
2170
 
            path = self._inventory.id2path(file_id)
2171
 
        return self._hashcache.get_sha1(path, stat_value)
2172
 
 
2173
2167
    def get_file_mtime(self, file_id, path=None):
2174
2168
        """See Tree.get_file_mtime."""
2175
2169
        if not path:
2176
2170
            path = self.inventory.id2path(file_id)
2177
 
        return os.lstat(self.abspath(path)).st_mtime
 
2171
        try:
 
2172
            return os.lstat(self.abspath(path)).st_mtime
 
2173
        except OSError, e:
 
2174
            if e.errno == errno.ENOENT:
 
2175
                raise errors.FileTimestampUnavailable(path)
 
2176
            raise
2178
2177
 
2179
2178
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2180
2179
        file_id = self.path2id(path)
2287
2286
                       for key, line in annotator.annotate_flat(this_key)]
2288
2287
        return annotations
2289
2288
 
 
2289
    def _put_rio(self, filename, stanzas, header):
 
2290
        self._must_be_locked()
 
2291
        my_file = _mod_rio.rio_file(stanzas, header)
 
2292
        self._transport.put_file(filename, my_file,
 
2293
            mode=self.bzrdir._get_file_mode())
 
2294
 
 
2295
    @needs_tree_write_lock
 
2296
    def set_merge_modified(self, modified_hashes):
 
2297
        def iter_stanzas():
 
2298
            for file_id, hash in modified_hashes.iteritems():
 
2299
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
 
2300
                    hash=hash)
 
2301
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
2302
 
2290
2303
    @needs_read_lock
2291
2304
    def merge_modified(self):
2292
2305
        """Return a dictionary of files modified by a merge.
2782
2795
                # something is wrong, so lets determine what exactly
2783
2796
                if not self.has_filename(from_rel) and \
2784
2797
                   not self.has_filename(to_rel):
2785
 
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
2786
 
                        errors.PathsDoNotExist(paths=(str(from_rel),
2787
 
                        str(to_rel))))
 
2798
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
 
2799
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2788
2800
                else:
2789
2801
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2790
2802
            rename_entry.only_change_inv = only_change_inv
3016
3028
 
3017
3029
    supports_versioned_directories = None
3018
3030
 
3019
 
    @classmethod
3020
 
    def find_format_string(klass, a_bzrdir):
3021
 
        """Return format name for the working tree object in a_bzrdir."""
3022
 
        try:
3023
 
            transport = a_bzrdir.get_workingtree_transport(None)
3024
 
            return transport.get_bytes("format")
3025
 
        except errors.NoSuchFile:
3026
 
            raise errors.NoWorkingTree(base=transport.base)
3027
 
 
3028
 
    @classmethod
3029
 
    def find_format(klass, a_bzrdir):
3030
 
        """Return the format for the working tree object in a_bzrdir."""
3031
 
        try:
3032
 
            format_string = klass.find_format_string(a_bzrdir)
3033
 
            return format_registry.get(format_string)
3034
 
        except KeyError:
3035
 
            raise errors.UnknownFormatError(format=format_string,
3036
 
                                            kind="working tree")
3037
 
 
3038
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
3031
    def initialize(self, controldir, revision_id=None, from_branch=None,
3039
3032
                   accelerator_tree=None, hardlink=False):
3040
 
        """Initialize a new working tree in a_bzrdir.
 
3033
        """Initialize a new working tree in controldir.
3041
3034
 
3042
 
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
3035
        :param controldir: ControlDir to initialize the working tree in.
3043
3036
        :param revision_id: allows creating a working tree at a different
3044
3037
            revision than the branch is at.
3045
3038
        :param from_branch: Branch to checkout
3065
3058
        """Return the current default format."""
3066
3059
        return format_registry.get_default()
3067
3060
 
3068
 
    def get_format_string(self):
3069
 
        """Return the ASCII format string that identifies this format."""
3070
 
        raise NotImplementedError(self.get_format_string)
3071
 
 
3072
3061
    def get_format_description(self):
3073
3062
        """Return the short description for this format."""
3074
3063
        raise NotImplementedError(self.get_format_description)
3126
3115
    def unregister_format(klass, format):
3127
3116
        format_registry.remove(format)
3128
3117
 
 
3118
    def get_controldir_for_branch(self):
 
3119
        """Get the control directory format for creating branches.
 
3120
 
 
3121
        This is to support testing of working tree formats that can not exist
 
3122
        in the same control directory as a branch.
 
3123
        """
 
3124
        return self._matchingbzrdir
 
3125
 
 
3126
 
 
3127
class WorkingTreeFormatMetaDir(bzrdir.BzrDirMetaComponentFormat, WorkingTreeFormat):
 
3128
    """Base class for working trees that live in bzr meta directories."""
 
3129
 
 
3130
    def __init__(self):
 
3131
        WorkingTreeFormat.__init__(self)
 
3132
        bzrdir.BzrDirMetaComponentFormat.__init__(self)
 
3133
 
 
3134
    @classmethod
 
3135
    def find_format_string(klass, controldir):
 
3136
        """Return format name for the working tree object in controldir."""
 
3137
        try:
 
3138
            transport = controldir.get_workingtree_transport(None)
 
3139
            return transport.get_bytes("format")
 
3140
        except errors.NoSuchFile:
 
3141
            raise errors.NoWorkingTree(base=transport.base)
 
3142
 
 
3143
    @classmethod
 
3144
    def find_format(klass, controldir):
 
3145
        """Return the format for the working tree object in controldir."""
 
3146
        format_string = klass.find_format_string(controldir)
 
3147
        return klass._find_format(format_registry, 'working tree',
 
3148
                format_string)
 
3149
 
3129
3150
 
3130
3151
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3131
3152
    "bzrlib.workingtree_4", "WorkingTreeFormat4")