~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-08-17 18:13:57 UTC
  • mfrom: (5268.7.29 transport-segments)
  • Revision ID: pqm@pqm.ubuntu.com-20110817181357-y5q5eth1hk8bl3om
(jelmer) Allow specifying the colocated branch to use in the branch URL,
 and retrieving the branch name using ControlDir._get_selected_branch.
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
WorkingTree.open(dir).
30
30
"""
31
31
 
32
 
from __future__ import absolute_import
33
32
 
34
33
from cStringIO import StringIO
35
34
import os
47
46
 
48
47
from bzrlib import (
49
48
    branch,
 
49
    bzrdir,
50
50
    conflicts as _mod_conflicts,
51
51
    controldir,
52
52
    errors,
54
54
    generate_ids,
55
55
    globbing,
56
56
    graph as _mod_graph,
 
57
    hashcache,
57
58
    ignores,
58
59
    inventory,
59
60
    merge,
60
61
    revision as _mod_revision,
61
62
    revisiontree,
62
63
    rio as _mod_rio,
63
 
    shelf,
64
64
    transform,
65
65
    transport,
66
66
    ui,
70
70
    )
71
71
""")
72
72
 
73
 
# Explicitly import bzrlib.bzrdir so that the BzrProber
74
 
# is guaranteed to be registered.
75
 
from bzrlib import (
76
 
    bzrdir,
77
 
    symbol_versioning,
78
 
    )
79
 
 
 
73
from bzrlib import symbol_versioning
80
74
from bzrlib.decorators import needs_read_lock, needs_write_lock
81
 
from bzrlib.i18n import gettext
82
75
from bzrlib.lock import LogicalLockResult
83
76
import bzrlib.mutabletree
84
77
from bzrlib.mutabletree import needs_tree_write_lock
91
84
    realpath,
92
85
    safe_unicode,
93
86
    splitpath,
 
87
    supports_executable,
94
88
    )
95
89
from bzrlib.trace import mutter, note
96
90
from bzrlib.revision import CURRENT_REVISION
178
172
 
179
173
    def __init__(self, basedir='.',
180
174
                 branch=DEPRECATED_PARAMETER,
 
175
                 _control_files=None,
181
176
                 _internal=False,
182
 
                 _transport=None,
183
177
                 _format=None,
184
178
                 _bzrdir=None):
185
179
        """Construct a WorkingTree instance. This is not a public API.
198
192
        else:
199
193
            self._branch = self.bzrdir.open_branch()
200
194
        self.basedir = realpath(basedir)
201
 
        self._transport = _transport
 
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()
202
218
        self._rules_searcher = None
203
219
        self.views = self._make_views()
204
220
 
222
238
        """
223
239
        return self.bzrdir.is_control_filename(filename)
224
240
 
 
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
 
225
252
    branch = property(
226
253
        fget=lambda self: self._branch,
227
254
        doc="""The branch this WorkingTree is connected to.
230
257
            the working tree has been constructed from.
231
258
            """)
232
259
 
233
 
    def has_versioned_directories(self):
234
 
        """See `Tree.has_versioned_directories`."""
235
 
        return self._format.supports_versioned_directories
236
 
 
237
 
    def _supports_executable(self):
238
 
        if sys.platform == 'win32':
239
 
            return False
240
 
        # FIXME: Ideally this should check the file system
241
 
        return True
242
 
 
243
260
    def break_lock(self):
244
261
        """Break a lock if one is present from another instance.
245
262
 
248
265
 
249
266
        This will probe the repository for its lock as well.
250
267
        """
251
 
        raise NotImplementedError(self.break_lock)
 
268
        self._control_files.break_lock()
 
269
        self.branch.break_lock()
252
270
 
253
271
    def requires_rich_root(self):
254
272
        return self._format.requires_rich_root
262
280
    def supports_views(self):
263
281
        return self.views.supports_views()
264
282
 
265
 
    def get_config_stack(self):
266
 
        """Retrieve the config stack for this tree.
267
 
 
268
 
        :return: A ``bzrlib.config.Stack``
269
 
        """
270
 
        # For the moment, just provide the branch config stack.
271
 
        return self.branch.get_config_stack()
272
 
 
273
283
    @staticmethod
274
284
    def open(path=None, _unsupported=False):
275
285
        """Open an existing working tree at path.
277
287
        """
278
288
        if path is None:
279
289
            path = osutils.getcwd()
280
 
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
281
 
        return control.open_workingtree(unsupported=_unsupported)
 
290
        control = bzrdir.BzrDir.open(path, _unsupported)
 
291
        return control.open_workingtree(_unsupported)
282
292
 
283
293
    @staticmethod
284
294
    def open_containing(path=None):
295
305
        """
296
306
        if path is None:
297
307
            path = osutils.getcwd()
298
 
        control, relpath = controldir.ControlDir.open_containing(path)
 
308
        control, relpath = bzrdir.BzrDir.open_containing(path)
299
309
        return control.open_workingtree(), relpath
300
310
 
301
311
    @staticmethod
321
331
                if view_files:
322
332
                    file_list = view_files
323
333
                    view_str = views.view_display_str(view_files)
324
 
                    note(gettext("Ignoring files outside view. View is %s") % view_str)
 
334
                    note("Ignoring files outside view. View is %s" % view_str)
325
335
            return tree, file_list
326
336
        if default_directory == u'.':
327
337
            seed = file_list[0]
384
394
            else:
385
395
                return True, tree
386
396
        t = transport.get_transport(location)
387
 
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
 
397
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
388
398
                                              list_current=list_current)
389
399
        return [tr for tr in iterator if tr is not None]
390
400
 
 
401
    def all_file_ids(self):
 
402
        """See Tree.iter_all_file_ids"""
 
403
        raise NotImplementedError(self.all_file_ids)
 
404
 
391
405
    def __repr__(self):
392
406
        return "<%s of %s>" % (self.__class__.__name__,
393
407
                               getattr(self, 'basedir', None))
508
522
        raise NotImplementedError(self.get_root_id)
509
523
 
510
524
    @needs_read_lock
511
 
    def clone(self, to_controldir, revision_id=None):
 
525
    def clone(self, to_bzrdir, revision_id=None):
512
526
        """Duplicate this working tree into to_bzr, including all state.
513
527
 
514
528
        Specifically modified files are kept as modified, but
515
529
        ignored and unknown files are discarded.
516
530
 
517
 
        If you want to make a new line of development, see ControlDir.sprout()
 
531
        If you want to make a new line of development, see bzrdir.sprout()
518
532
 
519
533
        revision
520
534
            If not None, the cloned tree will have its last revision set to
522
536
            and this one merged in.
523
537
        """
524
538
        # assumes the target bzr dir format is compatible.
525
 
        result = to_controldir.create_workingtree()
 
539
        result = to_bzrdir.create_workingtree()
526
540
        self.copy_content_into(result, revision_id)
527
541
        return result
528
542
 
535
549
        else:
536
550
            # TODO now merge from tree.last_revision to revision (to preserve
537
551
            # user local changes)
538
 
            try:
539
 
                other_tree = self.revision_tree(revision_id)
540
 
            except errors.NoSuchRevision:
541
 
                other_tree = self.branch.repository.revision_tree(revision_id)
542
 
 
543
 
            merge.transform_tree(tree, other_tree)
544
 
            if revision_id == _mod_revision.NULL_REVISION:
545
 
                new_parents = []
546
 
            else:
547
 
                new_parents = [revision_id]
548
 
            tree.set_parent_ids(new_parents)
 
552
            merge.transform_tree(tree, self)
 
553
            tree.set_parent_ids([revision_id])
549
554
 
550
555
    def id2abspath(self, file_id):
551
556
        return self.abspath(self.id2path(file_id))
760
765
 
761
766
    @needs_tree_write_lock
762
767
    def set_merge_modified(self, modified_hashes):
763
 
        """Set the merge modified hashes."""
764
 
        raise NotImplementedError(self.set_merge_modified)
 
768
        def iter_stanzas():
 
769
            for file_id, hash in modified_hashes.iteritems():
 
770
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
 
771
                    hash=hash)
 
772
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
765
773
 
766
774
    def _sha_from_stat(self, path, stat_result):
767
775
        """Get a sha digest from the tree's stat cache.
773
781
        """
774
782
        return None
775
783
 
 
784
    def _put_rio(self, filename, stanzas, header):
 
785
        self._must_be_locked()
 
786
        my_file = _mod_rio.rio_file(stanzas, header)
 
787
        self._transport.put_file(filename, my_file,
 
788
            mode=self.bzrdir._get_file_mode())
 
789
 
776
790
    @needs_write_lock # because merge pulls data into the branch.
777
791
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
778
792
                          merge_type=None, force=False):
1018
1032
                                show_base=show_base)
1019
1033
                    basis_root_id = basis_tree.get_root_id()
1020
1034
                    new_root_id = new_basis_tree.get_root_id()
1021
 
                    if new_root_id is not None and basis_root_id != new_root_id:
 
1035
                    if basis_root_id != new_root_id:
1022
1036
                        self.set_root_id(new_root_id)
1023
1037
                finally:
1024
1038
                    basis_tree.unlock()
1025
1039
                # TODO - dedup parents list with things merged by pull ?
1026
1040
                # reuse the revisiontree we merged against to set the new
1027
1041
                # tree data.
1028
 
                parent_trees = []
1029
 
                if self.branch.last_revision() != _mod_revision.NULL_REVISION:
1030
 
                    parent_trees.append(
1031
 
                        (self.branch.last_revision(), new_basis_tree))
 
1042
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1032
1043
                # we have to pull the merge trees out again, because
1033
1044
                # merge_inner has set the ids. - this corner is not yet
1034
1045
                # layered well enough to prevent double handling.
1051
1062
            stream.write(bytes)
1052
1063
        finally:
1053
1064
            stream.close()
 
1065
        # TODO: update the hashcache here ?
1054
1066
 
1055
1067
    def extras(self):
1056
1068
        """Yield all unversioned files in this WorkingTree.
1133
1145
        else:
1134
1146
            mode = stat_value.st_mode
1135
1147
            kind = osutils.file_kind_from_stat_mode(mode)
1136
 
            if not self._supports_executable():
 
1148
            if not supports_executable():
1137
1149
                executable = entry is not None and entry.executable
1138
1150
            else:
1139
1151
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1158
1170
        return _mod_revision.ensure_null(self.branch.last_revision())
1159
1171
 
1160
1172
    def is_locked(self):
1161
 
        """Check if this tree is locked."""
1162
 
        raise NotImplementedError(self.is_locked)
 
1173
        return self._control_files.is_locked()
 
1174
 
 
1175
    def _must_be_locked(self):
 
1176
        if not self.is_locked():
 
1177
            raise errors.ObjectNotLocked(self)
1163
1178
 
1164
1179
    def lock_read(self):
1165
1180
        """Lock the tree for reading.
1168
1183
 
1169
1184
        :return: A bzrlib.lock.LogicalLockResult.
1170
1185
        """
1171
 
        raise NotImplementedError(self.lock_read)
 
1186
        if not self.is_locked():
 
1187
            self._reset_data()
 
1188
        self.branch.lock_read()
 
1189
        try:
 
1190
            self._control_files.lock_read()
 
1191
            return LogicalLockResult(self.unlock)
 
1192
        except:
 
1193
            self.branch.unlock()
 
1194
            raise
1172
1195
 
1173
1196
    def lock_tree_write(self):
1174
1197
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1175
1198
 
1176
1199
        :return: A bzrlib.lock.LogicalLockResult.
1177
1200
        """
1178
 
        raise NotImplementedError(self.lock_tree_write)
 
1201
        if not self.is_locked():
 
1202
            self._reset_data()
 
1203
        self.branch.lock_read()
 
1204
        try:
 
1205
            self._control_files.lock_write()
 
1206
            return LogicalLockResult(self.unlock)
 
1207
        except:
 
1208
            self.branch.unlock()
 
1209
            raise
1179
1210
 
1180
1211
    def lock_write(self):
1181
1212
        """See MutableTree.lock_write, and WorkingTree.unlock.
1182
1213
 
1183
1214
        :return: A bzrlib.lock.LogicalLockResult.
1184
1215
        """
1185
 
        raise NotImplementedError(self.lock_write)
 
1216
        if not self.is_locked():
 
1217
            self._reset_data()
 
1218
        self.branch.lock_write()
 
1219
        try:
 
1220
            self._control_files.lock_write()
 
1221
            return LogicalLockResult(self.unlock)
 
1222
        except:
 
1223
            self.branch.unlock()
 
1224
            raise
1186
1225
 
1187
1226
    def get_physical_lock_status(self):
1188
 
        raise NotImplementedError(self.get_physical_lock_status)
 
1227
        return self._control_files.get_physical_lock_status()
 
1228
 
 
1229
    def _reset_data(self):
 
1230
        """Reset transient data that cannot be revalidated."""
 
1231
        raise NotImplementedError(self._reset_data)
1189
1232
 
1190
1233
    def set_last_revision(self, new_revision):
1191
1234
        """Change the last revision in the working tree."""
1357
1400
                basis_tree.unlock()
1358
1401
        return conflicts
1359
1402
 
1360
 
    @needs_write_lock
1361
 
    def store_uncommitted(self):
1362
 
        """Store uncommitted changes from the tree in the branch."""
1363
 
        target_tree = self.basis_tree()
1364
 
        shelf_creator = shelf.ShelfCreator(self, target_tree)
1365
 
        try:
1366
 
            if not shelf_creator.shelve_all():
1367
 
                return
1368
 
            self.branch.store_uncommitted(shelf_creator)
1369
 
            shelf_creator.transform()
1370
 
        finally:
1371
 
            shelf_creator.finalize()
1372
 
        note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1373
 
 
1374
 
    @needs_write_lock
1375
 
    def restore_uncommitted(self):
1376
 
        """Restore uncommitted changes from the branch into the tree."""
1377
 
        unshelver = self.branch.get_unshelver(self)
1378
 
        if unshelver is None:
1379
 
            return
1380
 
        try:
1381
 
            merger = unshelver.make_merger()
1382
 
            merger.ignore_zero = True
1383
 
            merger.do_merge()
1384
 
            self.branch.store_uncommitted(None)
1385
 
        finally:
1386
 
            unshelver.finalize()
1387
 
 
1388
1403
    def revision_tree(self, revision_id):
1389
1404
        """See Tree.revision_tree.
1390
1405
 
1513
1528
                                             show_base=show_base)
1514
1529
            if nb_conflicts:
1515
1530
                self.add_parent_tree((old_tip, other_tree))
1516
 
                note(gettext('Rerun update after fixing the conflicts.'))
 
1531
                note('Rerun update after fixing the conflicts.')
1517
1532
                return nb_conflicts
1518
1533
 
1519
1534
        if last_rev != _mod_revision.ensure_null(revision):
1561
1576
            last_rev = parent_trees[0][0]
1562
1577
        return nb_conflicts
1563
1578
 
 
1579
    def _write_hashcache_if_dirty(self):
 
1580
        """Write out the hashcache if it is dirty."""
 
1581
        if self._hashcache.needs_write:
 
1582
            try:
 
1583
                self._hashcache.write()
 
1584
            except OSError, e:
 
1585
                if e.errno not in (errno.EPERM, errno.EACCES):
 
1586
                    raise
 
1587
                # TODO: jam 20061219 Should this be a warning? A single line
 
1588
                #       warning might be sufficient to let the user know what
 
1589
                #       is going on.
 
1590
                mutter('Could not write hashcache for %s\nError: %s',
 
1591
                              self._hashcache.cache_file_name(), e)
 
1592
 
1564
1593
    def set_conflicts(self, arg):
1565
1594
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1566
1595
 
1795
1824
        :param branch: A branch to override probing for the branch.
1796
1825
        """
1797
1826
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1798
 
            branch=branch, _transport=_control_files._transport,
1799
 
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
1800
 
 
1801
 
        self._control_files = _control_files
1802
 
        self._detect_case_handling()
 
1827
            branch=branch, _control_files=_control_files, _internal=_internal,
 
1828
            _format=_format, _bzrdir=_bzrdir)
1803
1829
 
1804
1830
        if _inventory is None:
1805
1831
            # This will be acquired on lock_read() or lock_write()
1825
1851
        self._inventory = inv
1826
1852
        self._inventory_is_modified = dirty
1827
1853
 
1828
 
    def _detect_case_handling(self):
1829
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
1830
 
        try:
1831
 
            wt_trans.stat(self._format.case_sensitive_filename)
1832
 
        except errors.NoSuchFile:
1833
 
            self.case_sensitive = True
1834
 
        else:
1835
 
            self.case_sensitive = False
1836
 
 
1837
 
        self._setup_directory_is_tree_reference()
1838
 
 
1839
1854
    def _serialize(self, inventory, out_file):
1840
1855
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1841
1856
            working=True)
1843
1858
    def _deserialize(selt, in_file):
1844
1859
        return xml5.serializer_v5.read_inventory(in_file)
1845
1860
 
1846
 
    def break_lock(self):
1847
 
        """Break a lock if one is present from another instance.
1848
 
 
1849
 
        Uses the ui factory to ask for confirmation if the lock may be from
1850
 
        an active process.
1851
 
 
1852
 
        This will probe the repository for its lock as well.
1853
 
        """
1854
 
        self._control_files.break_lock()
1855
 
        self.branch.break_lock()
1856
 
 
1857
 
    def is_locked(self):
1858
 
        return self._control_files.is_locked()
1859
 
 
1860
 
    def _must_be_locked(self):
1861
 
        if not self.is_locked():
1862
 
            raise errors.ObjectNotLocked(self)
1863
 
 
1864
 
    def lock_read(self):
1865
 
        """Lock the tree for reading.
1866
 
 
1867
 
        This also locks the branch, and can be unlocked via self.unlock().
1868
 
 
1869
 
        :return: A bzrlib.lock.LogicalLockResult.
1870
 
        """
1871
 
        if not self.is_locked():
1872
 
            self._reset_data()
1873
 
        self.branch.lock_read()
1874
 
        try:
1875
 
            self._control_files.lock_read()
1876
 
            return LogicalLockResult(self.unlock)
1877
 
        except:
1878
 
            self.branch.unlock()
1879
 
            raise
1880
 
 
1881
 
    def lock_tree_write(self):
1882
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1883
 
 
1884
 
        :return: A bzrlib.lock.LogicalLockResult.
1885
 
        """
1886
 
        if not self.is_locked():
1887
 
            self._reset_data()
1888
 
        self.branch.lock_read()
1889
 
        try:
1890
 
            self._control_files.lock_write()
1891
 
            return LogicalLockResult(self.unlock)
1892
 
        except:
1893
 
            self.branch.unlock()
1894
 
            raise
1895
 
 
1896
 
    def lock_write(self):
1897
 
        """See MutableTree.lock_write, and WorkingTree.unlock.
1898
 
 
1899
 
        :return: A bzrlib.lock.LogicalLockResult.
1900
 
        """
1901
 
        if not self.is_locked():
1902
 
            self._reset_data()
1903
 
        self.branch.lock_write()
1904
 
        try:
1905
 
            self._control_files.lock_write()
1906
 
            return LogicalLockResult(self.unlock)
1907
 
        except:
1908
 
            self.branch.unlock()
1909
 
            raise
1910
 
 
1911
 
    def get_physical_lock_status(self):
1912
 
        return self._control_files.get_physical_lock_status()
1913
 
 
1914
1861
    @needs_tree_write_lock
1915
1862
    def _write_inventory(self, inv):
1916
1863
        """Write inventory as the current inventory."""
1984
1931
            if entry.parent_id == orig_root_id:
1985
1932
                entry.parent_id = inv.root.file_id
1986
1933
 
 
1934
    def all_file_ids(self):
 
1935
        """See Tree.iter_all_file_ids"""
 
1936
        return set(self.inventory)
 
1937
 
1987
1938
    @needs_tree_write_lock
1988
1939
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1989
1940
        """See MutableTree.set_parent_trees."""
2008
1959
                # parent tree from the repository.
2009
1960
                self._cache_basis_inventory(leftmost_parent_id)
2010
1961
            else:
2011
 
                inv = leftmost_parent_tree.root_inventory
 
1962
                inv = leftmost_parent_tree.inventory
2012
1963
                xml = self._create_basis_xml_from_inventory(
2013
1964
                                        leftmost_parent_id, inv)
2014
1965
                self._write_basis_inventory(xml)
2111
2062
 
2112
2063
    def has_id(self, file_id):
2113
2064
        # files that have been deleted are excluded
2114
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2115
 
        if not inv.has_id(inv_file_id):
 
2065
        inv = self.inventory
 
2066
        if not inv.has_id(file_id):
2116
2067
            return False
2117
 
        path = inv.id2path(inv_file_id)
 
2068
        path = inv.id2path(file_id)
2118
2069
        return osutils.lexists(self.abspath(path))
2119
2070
 
2120
2071
    def has_or_had_id(self, file_id):
2121
 
        if file_id == self.get_root_id():
 
2072
        if file_id == self.inventory.root.file_id:
2122
2073
            return True
2123
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2124
 
        return inv.has_id(inv_file_id)
 
2074
        return self.inventory.has_id(file_id)
2125
2075
 
2126
 
    def all_file_ids(self):
 
2076
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
2077
    def __iter__(self):
2127
2078
        """Iterate through file_ids for this tree.
2128
2079
 
2129
2080
        file_ids are in a WorkingTree if they are in the working inventory
2130
2081
        and the working file exists.
2131
2082
        """
2132
 
        ret = set()
2133
 
        for path, ie in self.iter_entries_by_dir():
2134
 
            ret.add(ie.file_id)
2135
 
        return ret
 
2083
        inv = self._inventory
 
2084
        for path, ie in inv.iter_entries():
 
2085
            if osutils.lexists(self.abspath(path)):
 
2086
                yield ie.file_id
2136
2087
 
2137
2088
    @needs_tree_write_lock
2138
2089
    def set_last_revision(self, new_revision):
2194
2145
                _mod_revision.NULL_REVISION)
2195
2146
        else:
2196
2147
            rt = self.branch.repository.revision_tree(revision_ids[0])
2197
 
        self._write_inventory(rt.root_inventory)
 
2148
        self._write_inventory(rt.inventory)
2198
2149
        self.set_parent_ids(revision_ids)
2199
2150
 
2200
2151
    def flush(self):
2209
2160
            mode=self.bzrdir._get_file_mode())
2210
2161
        self._inventory_is_modified = False
2211
2162
 
 
2163
    @needs_read_lock
 
2164
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
2165
        if not path:
 
2166
            path = self._inventory.id2path(file_id)
 
2167
        return self._hashcache.get_sha1(path, stat_value)
 
2168
 
2212
2169
    def get_file_mtime(self, file_id, path=None):
2213
2170
        """See Tree.get_file_mtime."""
2214
2171
        if not path:
2215
 
            path = self.id2path(file_id)
2216
 
        try:
2217
 
            return os.lstat(self.abspath(path)).st_mtime
2218
 
        except OSError, e:
2219
 
            if e.errno == errno.ENOENT:
2220
 
                raise errors.FileTimestampUnavailable(path)
2221
 
            raise
 
2172
            path = self.inventory.id2path(file_id)
 
2173
        return os.lstat(self.abspath(path)).st_mtime
2222
2174
 
2223
2175
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2224
 
        inv, file_id = self._path2inv_file_id(path)
 
2176
        file_id = self.path2id(path)
2225
2177
        if file_id is None:
2226
2178
            # For unversioned files on win32, we just assume they are not
2227
2179
            # executable
2228
2180
            return False
2229
 
        return inv[file_id].executable
 
2181
        return self._inventory[file_id].executable
2230
2182
 
2231
2183
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2232
2184
        mode = stat_result.st_mode
2233
2185
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2234
2186
 
2235
 
    def is_executable(self, file_id, path=None):
2236
 
        if not self._supports_executable():
2237
 
            inv, inv_file_id = self._unpack_file_id(file_id)
2238
 
            return inv[inv_file_id].executable
2239
 
        else:
 
2187
    if not supports_executable():
 
2188
        def is_executable(self, file_id, path=None):
 
2189
            return self._inventory[file_id].executable
 
2190
 
 
2191
        _is_executable_from_path_and_stat = \
 
2192
            _is_executable_from_path_and_stat_from_basis
 
2193
    else:
 
2194
        def is_executable(self, file_id, path=None):
2240
2195
            if not path:
2241
2196
                path = self.id2path(file_id)
2242
2197
            mode = os.lstat(self.abspath(path)).st_mode
2243
2198
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2244
2199
 
2245
 
    def _is_executable_from_path_and_stat(self, path, stat_result):
2246
 
        if not self._supports_executable():
2247
 
            return self._is_executable_from_path_and_stat_from_basis(path, stat_result)
2248
 
        else:
2249
 
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
 
2200
        _is_executable_from_path_and_stat = \
 
2201
            _is_executable_from_path_and_stat_from_stat
2250
2202
 
2251
2203
    @needs_tree_write_lock
2252
2204
    def _add(self, files, ids, kinds):
2255
2207
        # should probably put it back with the previous ID.
2256
2208
        # the read and write working inventory should not occur in this
2257
2209
        # function - they should be part of lock_write and unlock.
2258
 
        # FIXME: nested trees
2259
 
        inv = self.root_inventory
 
2210
        inv = self.inventory
2260
2211
        for f, file_id, kind in zip(files, ids, kinds):
2261
2212
            if file_id is None:
2262
2213
                inv.add_path(f, kind=kind)
2303
2254
                parent_tree = self.branch.repository.revision_tree(parent_id)
2304
2255
            parent_tree.lock_read()
2305
2256
            try:
2306
 
                try:
2307
 
                    kind = parent_tree.kind(file_id)
2308
 
                except errors.NoSuchId:
 
2257
                if not parent_tree.has_id(file_id):
2309
2258
                    continue
2310
 
                if kind != 'file':
 
2259
                ie = parent_tree.inventory[file_id]
 
2260
                if ie.kind != 'file':
2311
2261
                    # Note: this is slightly unnecessary, because symlinks and
2312
2262
                    # directories have a "text" which is the empty text, and we
2313
2263
                    # know that won't mess up annotations. But it seems cleaner
2314
2264
                    continue
2315
 
                parent_text_key = (
2316
 
                    file_id, parent_tree.get_file_revision(file_id))
 
2265
                parent_text_key = (file_id, ie.revision)
2317
2266
                if parent_text_key not in maybe_file_parent_keys:
2318
2267
                    maybe_file_parent_keys.append(parent_text_key)
2319
2268
            finally:
2334
2283
                       for key, line in annotator.annotate_flat(this_key)]
2335
2284
        return annotations
2336
2285
 
2337
 
    def _put_rio(self, filename, stanzas, header):
2338
 
        self._must_be_locked()
2339
 
        my_file = _mod_rio.rio_file(stanzas, header)
2340
 
        self._transport.put_file(filename, my_file,
2341
 
            mode=self.bzrdir._get_file_mode())
2342
 
 
2343
 
    @needs_tree_write_lock
2344
 
    def set_merge_modified(self, modified_hashes):
2345
 
        def iter_stanzas():
2346
 
            for file_id, hash in modified_hashes.iteritems():
2347
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
2348
 
                    hash=hash)
2349
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
2350
 
 
2351
2286
    @needs_read_lock
2352
2287
    def merge_modified(self):
2353
2288
        """Return a dictionary of files modified by a merge.
2373
2308
            for s in _mod_rio.RioReader(hashfile):
2374
2309
                # RioReader reads in Unicode, so convert file_ids back to utf8
2375
2310
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2376
 
                if not self.has_id(file_id):
 
2311
                if not self.inventory.has_id(file_id):
2377
2312
                    continue
2378
2313
                text_hash = s.get("hash")
2379
2314
                if text_hash == self.get_file_sha1(file_id):
2409
2344
        other_tree.lock_tree_write()
2410
2345
        try:
2411
2346
            new_parents = other_tree.get_parent_ids()
2412
 
            other_root = other_tree.root_inventory.root
 
2347
            other_root = other_tree.inventory.root
2413
2348
            other_root.parent_id = new_root_parent
2414
2349
            other_root.name = osutils.basename(other_tree_path)
2415
 
            self.root_inventory.add(other_root)
2416
 
            add_children(self.root_inventory, other_root)
2417
 
            self._write_inventory(self.root_inventory)
 
2350
            self.inventory.add(other_root)
 
2351
            add_children(self.inventory, other_root)
 
2352
            self._write_inventory(self.inventory)
2418
2353
            # normally we don't want to fetch whole repositories, but i think
2419
2354
            # here we really do want to consolidate the whole thing.
2420
2355
            for parent_id in other_tree.get_parent_ids():
2458
2393
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2459
2394
        if tree_transport.base != branch_transport.base:
2460
2395
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2461
 
            tree_bzrdir.set_branch_reference(new_branch)
 
2396
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
 
2397
                target_branch=new_branch)
2462
2398
        else:
2463
2399
            tree_bzrdir = branch_bzrdir
2464
2400
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2465
2401
        wt.set_parent_ids(self.get_parent_ids())
2466
 
        # FIXME: Support nested trees
2467
 
        my_inv = self.root_inventory
 
2402
        my_inv = self.inventory
2468
2403
        child_inv = inventory.Inventory(root_id=None)
2469
2404
        new_root = my_inv[file_id]
2470
2405
        my_inv.remove_recursive_id(file_id)
2490
2425
        if not self.is_locked():
2491
2426
            raise errors.ObjectNotLocked(self)
2492
2427
 
 
2428
        inv = self.inventory
2493
2429
        if from_dir is None and include_root is True:
2494
 
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
 
2430
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2495
2431
        # Convert these into local objects to save lookup times
2496
2432
        pathjoin = osutils.pathjoin
2497
2433
        file_kind = self._kind
2504
2440
 
2505
2441
        # directory file_id, relative path, absolute path, reverse sorted children
2506
2442
        if from_dir is not None:
2507
 
            inv, from_dir_id = self._path2inv_file_id(from_dir)
 
2443
            from_dir_id = inv.path2id(from_dir)
2508
2444
            if from_dir_id is None:
2509
2445
                # Directory not versioned
2510
2446
                return
2511
2447
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2512
2448
        else:
2513
 
            inv = self.root_inventory
2514
2449
            from_dir_id = inv.root.file_id
2515
2450
            from_dir_abspath = self.basedir
2516
2451
        children = os.listdir(from_dir_abspath)
2639
2574
        rename_entries = []
2640
2575
        rename_tuples = []
2641
2576
 
2642
 
        invs_to_write = set()
2643
 
 
2644
2577
        # check for deprecated use of signature
2645
2578
        if to_dir is None:
2646
2579
            raise TypeError('You must supply a target directory')
2647
2580
        # check destination directory
2648
2581
        if isinstance(from_paths, basestring):
2649
2582
            raise ValueError()
 
2583
        inv = self.inventory
2650
2584
        to_abs = self.abspath(to_dir)
2651
2585
        if not isdir(to_abs):
2652
2586
            raise errors.BzrMoveFailedError('',to_dir,
2654
2588
        if not self.has_filename(to_dir):
2655
2589
            raise errors.BzrMoveFailedError('',to_dir,
2656
2590
                errors.NotInWorkingDirectory(to_dir))
2657
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2591
        to_dir_id = inv.path2id(to_dir)
2658
2592
        if to_dir_id is None:
2659
2593
            raise errors.BzrMoveFailedError('',to_dir,
2660
2594
                errors.NotVersionedError(path=to_dir))
2661
2595
 
2662
 
        to_dir_ie = to_inv[to_dir_id]
 
2596
        to_dir_ie = inv[to_dir_id]
2663
2597
        if to_dir_ie.kind != 'directory':
2664
2598
            raise errors.BzrMoveFailedError('',to_dir,
2665
2599
                errors.NotADirectory(to_abs))
2667
2601
        # create rename entries and tuples
2668
2602
        for from_rel in from_paths:
2669
2603
            from_tail = splitpath(from_rel)[-1]
2670
 
            from_inv, from_id = self._path2inv_file_id(from_rel)
 
2604
            from_id = inv.path2id(from_rel)
2671
2605
            if from_id is None:
2672
2606
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2673
2607
                    errors.NotVersionedError(path=from_rel))
2674
2608
 
2675
 
            from_entry = from_inv[from_id]
 
2609
            from_entry = inv[from_id]
2676
2610
            from_parent_id = from_entry.parent_id
2677
2611
            to_rel = pathjoin(to_dir, from_tail)
2678
2612
            rename_entry = InventoryWorkingTree._RenameEntry(
2697
2631
            # restore the inventory on error
2698
2632
            self._inventory_is_modified = original_modified
2699
2633
            raise
2700
 
        #FIXME: Should potentially also write the from_invs
2701
 
        self._write_inventory(to_inv)
 
2634
        self._write_inventory(inv)
2702
2635
        return rename_tuples
2703
2636
 
2704
2637
    @needs_tree_write_lock
2724
2657
 
2725
2658
        Everything else results in an error.
2726
2659
        """
 
2660
        inv = self.inventory
2727
2661
        rename_entries = []
2728
2662
 
2729
2663
        # create rename entries and tuples
2730
2664
        from_tail = splitpath(from_rel)[-1]
2731
 
        from_inv, from_id = self._path2inv_file_id(from_rel)
 
2665
        from_id = inv.path2id(from_rel)
2732
2666
        if from_id is None:
2733
2667
            # if file is missing in the inventory maybe it's in the basis_tree
2734
2668
            basis_tree = self.branch.basis_tree()
2737
2671
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2738
2672
                    errors.NotVersionedError(path=from_rel))
2739
2673
            # put entry back in the inventory so we can rename it
2740
 
            from_entry = basis_tree.root_inventory[from_id].copy()
2741
 
            from_inv.add(from_entry)
 
2674
            from_entry = basis_tree.inventory[from_id].copy()
 
2675
            inv.add(from_entry)
2742
2676
        else:
2743
 
            from_inv, from_inv_id = self._unpack_file_id(from_id)
2744
 
            from_entry = from_inv[from_inv_id]
 
2677
            from_entry = inv[from_id]
2745
2678
        from_parent_id = from_entry.parent_id
2746
2679
        to_dir, to_tail = os.path.split(to_rel)
2747
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2680
        to_dir_id = inv.path2id(to_dir)
2748
2681
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2749
2682
                                     from_id=from_id,
2750
2683
                                     from_tail=from_tail,
2772
2705
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2773
2706
 
2774
2707
        self._move(rename_entries)
2775
 
        self._write_inventory(to_inv)
 
2708
        self._write_inventory(inv)
2776
2709
 
2777
2710
    class _RenameEntry(object):
2778
2711
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2794
2727
 
2795
2728
        Also does basic plausability tests.
2796
2729
        """
2797
 
        # FIXME: Handling of nested trees
2798
 
        inv = self.root_inventory
 
2730
        inv = self.inventory
2799
2731
 
2800
2732
        for rename_entry in rename_entries:
2801
2733
            # store to local variables for easier reference
2846
2778
                # something is wrong, so lets determine what exactly
2847
2779
                if not self.has_filename(from_rel) and \
2848
2780
                   not self.has_filename(to_rel):
2849
 
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
2850
 
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
 
2781
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
 
2782
                        errors.PathsDoNotExist(paths=(str(from_rel),
 
2783
                        str(to_rel))))
2851
2784
                else:
2852
2785
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2853
2786
            rename_entry.only_change_inv = only_change_inv
2859
2792
        Depending on the value of the flag 'only_change_inv', the
2860
2793
        file will be moved on the file system or not.
2861
2794
        """
 
2795
        inv = self.inventory
2862
2796
        moved = []
2863
2797
 
2864
2798
        for entry in rename_entries:
2871
2805
 
2872
2806
    def _rollback_move(self, moved):
2873
2807
        """Try to rollback a previous move in case of an filesystem error."""
 
2808
        inv = self.inventory
2874
2809
        for entry in moved:
2875
2810
            try:
2876
2811
                self._move_entry(WorkingTree._RenameEntry(
2885
2820
                        " Error message is: %s" % e)
2886
2821
 
2887
2822
    def _move_entry(self, entry):
2888
 
        inv = self.root_inventory
 
2823
        inv = self.inventory
2889
2824
        from_rel_abs = self.abspath(entry.from_rel)
2890
2825
        to_rel_abs = self.abspath(entry.to_rel)
2891
2826
        if from_rel_abs == to_rel_abs:
2932
2867
 
2933
2868
    def stored_kind(self, file_id):
2934
2869
        """See Tree.stored_kind"""
2935
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2936
 
        return inv[inv_file_id].kind
 
2870
        return self.inventory[file_id].kind
2937
2871
 
2938
2872
    def extras(self):
2939
2873
        """Yield all unversioned files in this WorkingTree.
2946
2880
        This is the same order used by 'osutils.walkdirs'.
2947
2881
        """
2948
2882
        ## TODO: Work from given directory downwards
2949
 
        for path, dir_entry in self.iter_entries_by_dir():
2950
 
            if dir_entry.kind != 'directory':
2951
 
                continue
 
2883
        for path, dir_entry in self.inventory.directories():
2952
2884
            # mutter("search for unknowns in %r", path)
2953
2885
            dirabs = self.abspath(path)
2954
2886
            if not isdir(dirabs):
2991
2923
        """
2992
2924
        _directory = 'directory'
2993
2925
        # get the root in the inventory
2994
 
        inv, top_id = self._path2inv_file_id(prefix)
 
2926
        inv = self.inventory
 
2927
        top_id = inv.path2id(prefix)
2995
2928
        if top_id is None:
2996
2929
            pending = []
2997
2930
        else:
3018
2951
                if dir[2] == _directory:
3019
2952
                    pending.append(dir)
3020
2953
 
3021
 
    @needs_write_lock
3022
 
    def update_feature_flags(self, updated_flags):
3023
 
        """Update the feature flags for this branch.
3024
 
 
3025
 
        :param updated_flags: Dictionary mapping feature names to necessities
3026
 
            A necessity can be None to indicate the feature should be removed
3027
 
        """
3028
 
        self._format._update_feature_flags(updated_flags)
3029
 
        self.control_transport.put_bytes('format', self._format.as_string())
3030
 
 
3031
2954
 
3032
2955
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
3033
2956
    """Registry for working tree formats."""
3089
3012
 
3090
3013
    supports_versioned_directories = None
3091
3014
 
3092
 
    def initialize(self, controldir, revision_id=None, from_branch=None,
 
3015
    @classmethod
 
3016
    def find_format_string(klass, a_bzrdir):
 
3017
        """Return format name for the working tree object in a_bzrdir."""
 
3018
        try:
 
3019
            transport = a_bzrdir.get_workingtree_transport(None)
 
3020
            return transport.get_bytes("format")
 
3021
        except errors.NoSuchFile:
 
3022
            raise errors.NoWorkingTree(base=transport.base)
 
3023
 
 
3024
    @classmethod
 
3025
    def find_format(klass, a_bzrdir):
 
3026
        """Return the format for the working tree object in a_bzrdir."""
 
3027
        try:
 
3028
            format_string = klass.find_format_string(a_bzrdir)
 
3029
            return format_registry.get(format_string)
 
3030
        except KeyError:
 
3031
            raise errors.UnknownFormatError(format=format_string,
 
3032
                                            kind="working tree")
 
3033
 
 
3034
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
3093
3035
                   accelerator_tree=None, hardlink=False):
3094
 
        """Initialize a new working tree in controldir.
 
3036
        """Initialize a new working tree in a_bzrdir.
3095
3037
 
3096
 
        :param controldir: ControlDir to initialize the working tree in.
 
3038
        :param a_bzrdir: BzrDir to initialize the working tree in.
3097
3039
        :param revision_id: allows creating a working tree at a different
3098
3040
            revision than the branch is at.
3099
3041
        :param from_branch: Branch to checkout
3112
3054
    def __ne__(self, other):
3113
3055
        return not (self == other)
3114
3056
 
 
3057
    @classmethod
 
3058
    @symbol_versioning.deprecated_method(
 
3059
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3060
    def get_default_format(klass):
 
3061
        """Return the current default format."""
 
3062
        return format_registry.get_default()
 
3063
 
 
3064
    def get_format_string(self):
 
3065
        """Return the ASCII format string that identifies this format."""
 
3066
        raise NotImplementedError(self.get_format_string)
 
3067
 
3115
3068
    def get_format_description(self):
3116
3069
        """Return the short description for this format."""
3117
3070
        raise NotImplementedError(self.get_format_description)
3133
3086
        """True if this format supports stored views."""
3134
3087
        return False
3135
3088
 
3136
 
    def get_controldir_for_branch(self):
3137
 
        """Get the control directory format for creating branches.
3138
 
 
3139
 
        This is to support testing of working tree formats that can not exist
3140
 
        in the same control directory as a branch.
3141
 
        """
3142
 
        return self._matchingbzrdir
3143
 
 
3144
 
 
3145
 
class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat):
3146
 
    """Base class for working trees that live in bzr meta directories."""
3147
 
 
3148
 
    def __init__(self):
3149
 
        WorkingTreeFormat.__init__(self)
3150
 
        bzrdir.BzrFormat.__init__(self)
3151
 
 
3152
 
    @classmethod
3153
 
    def find_format_string(klass, controldir):
3154
 
        """Return format name for the working tree object in controldir."""
3155
 
        try:
3156
 
            transport = controldir.get_workingtree_transport(None)
3157
 
            return transport.get_bytes("format")
3158
 
        except errors.NoSuchFile:
3159
 
            raise errors.NoWorkingTree(base=transport.base)
3160
 
 
3161
 
    @classmethod
3162
 
    def find_format(klass, controldir):
3163
 
        """Return the format for the working tree object in controldir."""
3164
 
        format_string = klass.find_format_string(controldir)
3165
 
        return klass._find_format(format_registry, 'working tree',
3166
 
                format_string)
3167
 
 
3168
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3169
 
            basedir=None):
3170
 
        WorkingTreeFormat.check_support_status(self,
3171
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3172
 
            basedir=basedir)
3173
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3174
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
3175
 
 
3176
 
    def get_controldir_for_branch(self):
3177
 
        """Get the control directory format for creating branches.
3178
 
 
3179
 
        This is to support testing of working tree formats that can not exist
3180
 
        in the same control directory as a branch.
3181
 
        """
3182
 
        return self._matchingbzrdir
3183
 
 
3184
 
 
3185
 
class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat):
3186
 
    """Base class for working trees that live in bzr meta directories."""
3187
 
 
3188
 
    def __init__(self):
3189
 
        WorkingTreeFormat.__init__(self)
3190
 
        bzrdir.BzrFormat.__init__(self)
3191
 
 
3192
 
    @classmethod
3193
 
    def find_format_string(klass, controldir):
3194
 
        """Return format name for the working tree object in controldir."""
3195
 
        try:
3196
 
            transport = controldir.get_workingtree_transport(None)
3197
 
            return transport.get_bytes("format")
3198
 
        except errors.NoSuchFile:
3199
 
            raise errors.NoWorkingTree(base=transport.base)
3200
 
 
3201
 
    @classmethod
3202
 
    def find_format(klass, controldir):
3203
 
        """Return the format for the working tree object in controldir."""
3204
 
        format_string = klass.find_format_string(controldir)
3205
 
        return klass._find_format(format_registry, 'working tree',
3206
 
                format_string)
3207
 
 
3208
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3209
 
            basedir=None):
3210
 
        WorkingTreeFormat.check_support_status(self,
3211
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3212
 
            basedir=basedir)
3213
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3214
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
3089
    @classmethod
 
3090
    @symbol_versioning.deprecated_method(
 
3091
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3092
    def register_format(klass, format):
 
3093
        format_registry.register(format)
 
3094
 
 
3095
    @classmethod
 
3096
    @symbol_versioning.deprecated_method(
 
3097
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3098
    def register_extra_format(klass, format):
 
3099
        format_registry.register_extra(format)
 
3100
 
 
3101
    @classmethod
 
3102
    @symbol_versioning.deprecated_method(
 
3103
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3104
    def unregister_extra_format(klass, format):
 
3105
        format_registry.unregister_extra(format)
 
3106
 
 
3107
    @classmethod
 
3108
    @symbol_versioning.deprecated_method(
 
3109
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3110
    def get_formats(klass):
 
3111
        return format_registry._get_all()
 
3112
 
 
3113
    @classmethod
 
3114
    @symbol_versioning.deprecated_method(
 
3115
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3116
    def set_default_format(klass, format):
 
3117
        format_registry.set_default(format)
 
3118
 
 
3119
    @classmethod
 
3120
    @symbol_versioning.deprecated_method(
 
3121
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3122
    def unregister_format(klass, format):
 
3123
        format_registry.remove(format)
3215
3124
 
3216
3125
 
3217
3126
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",