~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:13:13 UTC
  • mfrom: (6614.2.2 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201191313-wdfvmfff1djde6oq
(vila) Release 2.7.0 (Vincent Ladeuil)

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
32
33
 
33
34
from cStringIO import StringIO
34
35
import os
46
47
 
47
48
from bzrlib import (
48
49
    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,
58
57
    ignores,
59
58
    inventory,
60
59
    merge,
61
60
    revision as _mod_revision,
62
61
    revisiontree,
63
62
    rio as _mod_rio,
 
63
    shelf,
64
64
    transform,
65
65
    transport,
66
66
    ui,
70
70
    )
71
71
""")
72
72
 
73
 
from bzrlib import symbol_versioning
 
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
 
74
80
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
81
from bzrlib.i18n import gettext
75
82
from bzrlib.lock import LogicalLockResult
76
83
import bzrlib.mutabletree
77
84
from bzrlib.mutabletree import needs_tree_write_lock
84
91
    realpath,
85
92
    safe_unicode,
86
93
    splitpath,
87
 
    supports_executable,
88
94
    )
89
95
from bzrlib.trace import mutter, note
90
96
from bzrlib.revision import CURRENT_REVISION
172
178
 
173
179
    def __init__(self, basedir='.',
174
180
                 branch=DEPRECATED_PARAMETER,
175
 
                 _control_files=None,
176
181
                 _internal=False,
 
182
                 _transport=None,
177
183
                 _format=None,
178
184
                 _bzrdir=None):
179
185
        """Construct a WorkingTree instance. This is not a public API.
192
198
        else:
193
199
            self._branch = self.bzrdir.open_branch()
194
200
        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()
 
201
        self._transport = _transport
218
202
        self._rules_searcher = None
219
203
        self.views = self._make_views()
220
204
 
238
222
        """
239
223
        return self.bzrdir.is_control_filename(filename)
240
224
 
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
225
    branch = property(
253
226
        fget=lambda self: self._branch,
254
227
        doc="""The branch this WorkingTree is connected to.
257
230
            the working tree has been constructed from.
258
231
            """)
259
232
 
 
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
 
260
243
    def break_lock(self):
261
244
        """Break a lock if one is present from another instance.
262
245
 
265
248
 
266
249
        This will probe the repository for its lock as well.
267
250
        """
268
 
        self._control_files.break_lock()
269
 
        self.branch.break_lock()
 
251
        raise NotImplementedError(self.break_lock)
270
252
 
271
253
    def requires_rich_root(self):
272
254
        return self._format.requires_rich_root
280
262
    def supports_views(self):
281
263
        return self.views.supports_views()
282
264
 
 
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
 
283
273
    @staticmethod
284
274
    def open(path=None, _unsupported=False):
285
275
        """Open an existing working tree at path.
287
277
        """
288
278
        if path is None:
289
279
            path = osutils.getcwd()
290
 
        control = bzrdir.BzrDir.open(path, _unsupported)
291
 
        return control.open_workingtree(_unsupported)
 
280
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
 
281
        return control.open_workingtree(unsupported=_unsupported)
292
282
 
293
283
    @staticmethod
294
284
    def open_containing(path=None):
305
295
        """
306
296
        if path is None:
307
297
            path = osutils.getcwd()
308
 
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
298
        control, relpath = controldir.ControlDir.open_containing(path)
309
299
        return control.open_workingtree(), relpath
310
300
 
311
301
    @staticmethod
331
321
                if view_files:
332
322
                    file_list = view_files
333
323
                    view_str = views.view_display_str(view_files)
334
 
                    note("Ignoring files outside view. View is %s" % view_str)
 
324
                    note(gettext("Ignoring files outside view. View is %s") % view_str)
335
325
            return tree, file_list
336
326
        if default_directory == u'.':
337
327
            seed = file_list[0]
394
384
            else:
395
385
                return True, tree
396
386
        t = transport.get_transport(location)
397
 
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
 
387
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
398
388
                                              list_current=list_current)
399
389
        return [tr for tr in iterator if tr is not None]
400
390
 
401
 
    def all_file_ids(self):
402
 
        """See Tree.iter_all_file_ids"""
403
 
        raise NotImplementedError(self.all_file_ids)
404
 
 
405
391
    def __repr__(self):
406
392
        return "<%s of %s>" % (self.__class__.__name__,
407
393
                               getattr(self, 'basedir', None))
522
508
        raise NotImplementedError(self.get_root_id)
523
509
 
524
510
    @needs_read_lock
525
 
    def clone(self, to_bzrdir, revision_id=None):
 
511
    def clone(self, to_controldir, revision_id=None):
526
512
        """Duplicate this working tree into to_bzr, including all state.
527
513
 
528
514
        Specifically modified files are kept as modified, but
529
515
        ignored and unknown files are discarded.
530
516
 
531
 
        If you want to make a new line of development, see bzrdir.sprout()
 
517
        If you want to make a new line of development, see ControlDir.sprout()
532
518
 
533
519
        revision
534
520
            If not None, the cloned tree will have its last revision set to
536
522
            and this one merged in.
537
523
        """
538
524
        # assumes the target bzr dir format is compatible.
539
 
        result = to_bzrdir.create_workingtree()
 
525
        result = to_controldir.create_workingtree()
540
526
        self.copy_content_into(result, revision_id)
541
527
        return result
542
528
 
549
535
        else:
550
536
            # TODO now merge from tree.last_revision to revision (to preserve
551
537
            # user local changes)
552
 
            merge.transform_tree(tree, self)
553
 
            tree.set_parent_ids([revision_id])
 
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)
554
549
 
555
550
    def id2abspath(self, file_id):
556
551
        return self.abspath(self.id2path(file_id))
765
760
 
766
761
    @needs_tree_write_lock
767
762
    def set_merge_modified(self, modified_hashes):
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)
 
763
        """Set the merge modified hashes."""
 
764
        raise NotImplementedError(self.set_merge_modified)
773
765
 
774
766
    def _sha_from_stat(self, path, stat_result):
775
767
        """Get a sha digest from the tree's stat cache.
781
773
        """
782
774
        return None
783
775
 
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
 
 
790
776
    @needs_write_lock # because merge pulls data into the branch.
791
777
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
792
778
                          merge_type=None, force=False):
1032
1018
                                show_base=show_base)
1033
1019
                    basis_root_id = basis_tree.get_root_id()
1034
1020
                    new_root_id = new_basis_tree.get_root_id()
1035
 
                    if basis_root_id != new_root_id:
 
1021
                    if new_root_id is not None and basis_root_id != new_root_id:
1036
1022
                        self.set_root_id(new_root_id)
1037
1023
                finally:
1038
1024
                    basis_tree.unlock()
1039
1025
                # TODO - dedup parents list with things merged by pull ?
1040
1026
                # reuse the revisiontree we merged against to set the new
1041
1027
                # tree data.
1042
 
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
 
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))
1043
1032
                # we have to pull the merge trees out again, because
1044
1033
                # merge_inner has set the ids. - this corner is not yet
1045
1034
                # layered well enough to prevent double handling.
1062
1051
            stream.write(bytes)
1063
1052
        finally:
1064
1053
            stream.close()
1065
 
        # TODO: update the hashcache here ?
1066
1054
 
1067
1055
    def extras(self):
1068
1056
        """Yield all unversioned files in this WorkingTree.
1145
1133
        else:
1146
1134
            mode = stat_value.st_mode
1147
1135
            kind = osutils.file_kind_from_stat_mode(mode)
1148
 
            if not supports_executable():
 
1136
            if not self._supports_executable():
1149
1137
                executable = entry is not None and entry.executable
1150
1138
            else:
1151
1139
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1170
1158
        return _mod_revision.ensure_null(self.branch.last_revision())
1171
1159
 
1172
1160
    def is_locked(self):
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)
 
1161
        """Check if this tree is locked."""
 
1162
        raise NotImplementedError(self.is_locked)
1178
1163
 
1179
1164
    def lock_read(self):
1180
1165
        """Lock the tree for reading.
1183
1168
 
1184
1169
        :return: A bzrlib.lock.LogicalLockResult.
1185
1170
        """
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
 
1171
        raise NotImplementedError(self.lock_read)
1195
1172
 
1196
1173
    def lock_tree_write(self):
1197
1174
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1198
1175
 
1199
1176
        :return: A bzrlib.lock.LogicalLockResult.
1200
1177
        """
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
 
1178
        raise NotImplementedError(self.lock_tree_write)
1210
1179
 
1211
1180
    def lock_write(self):
1212
1181
        """See MutableTree.lock_write, and WorkingTree.unlock.
1213
1182
 
1214
1183
        :return: A bzrlib.lock.LogicalLockResult.
1215
1184
        """
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
 
1185
        raise NotImplementedError(self.lock_write)
1225
1186
 
1226
1187
    def get_physical_lock_status(self):
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)
 
1188
        raise NotImplementedError(self.get_physical_lock_status)
1232
1189
 
1233
1190
    def set_last_revision(self, new_revision):
1234
1191
        """Change the last revision in the working tree."""
1400
1357
                basis_tree.unlock()
1401
1358
        return conflicts
1402
1359
 
 
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
 
1403
1388
    def revision_tree(self, revision_id):
1404
1389
        """See Tree.revision_tree.
1405
1390
 
1528
1513
                                             show_base=show_base)
1529
1514
            if nb_conflicts:
1530
1515
                self.add_parent_tree((old_tip, other_tree))
1531
 
                note('Rerun update after fixing the conflicts.')
 
1516
                note(gettext('Rerun update after fixing the conflicts.'))
1532
1517
                return nb_conflicts
1533
1518
 
1534
1519
        if last_rev != _mod_revision.ensure_null(revision):
1576
1561
            last_rev = parent_trees[0][0]
1577
1562
        return nb_conflicts
1578
1563
 
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
 
 
1593
1564
    def set_conflicts(self, arg):
1594
1565
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1595
1566
 
1824
1795
        :param branch: A branch to override probing for the branch.
1825
1796
        """
1826
1797
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1827
 
            branch=branch, _control_files=_control_files, _internal=_internal,
1828
 
            _format=_format, _bzrdir=_bzrdir)
 
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()
1829
1803
 
1830
1804
        if _inventory is None:
1831
1805
            # This will be acquired on lock_read() or lock_write()
1851
1825
        self._inventory = inv
1852
1826
        self._inventory_is_modified = dirty
1853
1827
 
 
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
 
1854
1839
    def _serialize(self, inventory, out_file):
1855
1840
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1856
1841
            working=True)
1858
1843
    def _deserialize(selt, in_file):
1859
1844
        return xml5.serializer_v5.read_inventory(in_file)
1860
1845
 
 
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
 
1861
1914
    @needs_tree_write_lock
1862
1915
    def _write_inventory(self, inv):
1863
1916
        """Write inventory as the current inventory."""
1931
1984
            if entry.parent_id == orig_root_id:
1932
1985
                entry.parent_id = inv.root.file_id
1933
1986
 
1934
 
    def all_file_ids(self):
1935
 
        """See Tree.iter_all_file_ids"""
1936
 
        return set(self.inventory)
1937
 
 
1938
1987
    @needs_tree_write_lock
1939
1988
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1940
1989
        """See MutableTree.set_parent_trees."""
1959
2008
                # parent tree from the repository.
1960
2009
                self._cache_basis_inventory(leftmost_parent_id)
1961
2010
            else:
1962
 
                inv = leftmost_parent_tree.inventory
 
2011
                inv = leftmost_parent_tree.root_inventory
1963
2012
                xml = self._create_basis_xml_from_inventory(
1964
2013
                                        leftmost_parent_id, inv)
1965
2014
                self._write_basis_inventory(xml)
2062
2111
 
2063
2112
    def has_id(self, file_id):
2064
2113
        # files that have been deleted are excluded
2065
 
        inv = self.inventory
2066
 
        if not inv.has_id(file_id):
 
2114
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2115
        if not inv.has_id(inv_file_id):
2067
2116
            return False
2068
 
        path = inv.id2path(file_id)
 
2117
        path = inv.id2path(inv_file_id)
2069
2118
        return osutils.lexists(self.abspath(path))
2070
2119
 
2071
2120
    def has_or_had_id(self, file_id):
2072
 
        if file_id == self.inventory.root.file_id:
 
2121
        if file_id == self.get_root_id():
2073
2122
            return True
2074
 
        return self.inventory.has_id(file_id)
 
2123
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2124
        return inv.has_id(inv_file_id)
2075
2125
 
2076
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2077
 
    def __iter__(self):
 
2126
    def all_file_ids(self):
2078
2127
        """Iterate through file_ids for this tree.
2079
2128
 
2080
2129
        file_ids are in a WorkingTree if they are in the working inventory
2081
2130
        and the working file exists.
2082
2131
        """
2083
 
        inv = self._inventory
2084
 
        for path, ie in inv.iter_entries():
2085
 
            if osutils.lexists(self.abspath(path)):
2086
 
                yield ie.file_id
 
2132
        ret = set()
 
2133
        for path, ie in self.iter_entries_by_dir():
 
2134
            ret.add(ie.file_id)
 
2135
        return ret
2087
2136
 
2088
2137
    @needs_tree_write_lock
2089
2138
    def set_last_revision(self, new_revision):
2145
2194
                _mod_revision.NULL_REVISION)
2146
2195
        else:
2147
2196
            rt = self.branch.repository.revision_tree(revision_ids[0])
2148
 
        self._write_inventory(rt.inventory)
 
2197
        self._write_inventory(rt.root_inventory)
2149
2198
        self.set_parent_ids(revision_ids)
2150
2199
 
2151
2200
    def flush(self):
2160
2209
            mode=self.bzrdir._get_file_mode())
2161
2210
        self._inventory_is_modified = False
2162
2211
 
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
 
 
2169
2212
    def get_file_mtime(self, file_id, path=None):
2170
2213
        """See Tree.get_file_mtime."""
2171
2214
        if not path:
2172
 
            path = self.inventory.id2path(file_id)
2173
 
        return os.lstat(self.abspath(path)).st_mtime
 
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
2174
2222
 
2175
2223
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2176
 
        file_id = self.path2id(path)
 
2224
        inv, file_id = self._path2inv_file_id(path)
2177
2225
        if file_id is None:
2178
2226
            # For unversioned files on win32, we just assume they are not
2179
2227
            # executable
2180
2228
            return False
2181
 
        return self._inventory[file_id].executable
 
2229
        return inv[file_id].executable
2182
2230
 
2183
2231
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2184
2232
        mode = stat_result.st_mode
2185
2233
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2186
2234
 
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):
 
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:
2195
2240
            if not path:
2196
2241
                path = self.id2path(file_id)
2197
2242
            mode = os.lstat(self.abspath(path)).st_mode
2198
2243
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2199
2244
 
2200
 
        _is_executable_from_path_and_stat = \
2201
 
            _is_executable_from_path_and_stat_from_stat
 
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)
2202
2250
 
2203
2251
    @needs_tree_write_lock
2204
2252
    def _add(self, files, ids, kinds):
2207
2255
        # should probably put it back with the previous ID.
2208
2256
        # the read and write working inventory should not occur in this
2209
2257
        # function - they should be part of lock_write and unlock.
2210
 
        inv = self.inventory
 
2258
        # FIXME: nested trees
 
2259
        inv = self.root_inventory
2211
2260
        for f, file_id, kind in zip(files, ids, kinds):
2212
2261
            if file_id is None:
2213
2262
                inv.add_path(f, kind=kind)
2254
2303
                parent_tree = self.branch.repository.revision_tree(parent_id)
2255
2304
            parent_tree.lock_read()
2256
2305
            try:
2257
 
                if not parent_tree.has_id(file_id):
 
2306
                try:
 
2307
                    kind = parent_tree.kind(file_id)
 
2308
                except errors.NoSuchId:
2258
2309
                    continue
2259
 
                ie = parent_tree.inventory[file_id]
2260
 
                if ie.kind != 'file':
 
2310
                if kind != 'file':
2261
2311
                    # Note: this is slightly unnecessary, because symlinks and
2262
2312
                    # directories have a "text" which is the empty text, and we
2263
2313
                    # know that won't mess up annotations. But it seems cleaner
2264
2314
                    continue
2265
 
                parent_text_key = (file_id, ie.revision)
 
2315
                parent_text_key = (
 
2316
                    file_id, parent_tree.get_file_revision(file_id))
2266
2317
                if parent_text_key not in maybe_file_parent_keys:
2267
2318
                    maybe_file_parent_keys.append(parent_text_key)
2268
2319
            finally:
2283
2334
                       for key, line in annotator.annotate_flat(this_key)]
2284
2335
        return annotations
2285
2336
 
 
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
 
2286
2351
    @needs_read_lock
2287
2352
    def merge_modified(self):
2288
2353
        """Return a dictionary of files modified by a merge.
2308
2373
            for s in _mod_rio.RioReader(hashfile):
2309
2374
                # RioReader reads in Unicode, so convert file_ids back to utf8
2310
2375
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2311
 
                if not self.inventory.has_id(file_id):
 
2376
                if not self.has_id(file_id):
2312
2377
                    continue
2313
2378
                text_hash = s.get("hash")
2314
2379
                if text_hash == self.get_file_sha1(file_id):
2344
2409
        other_tree.lock_tree_write()
2345
2410
        try:
2346
2411
            new_parents = other_tree.get_parent_ids()
2347
 
            other_root = other_tree.inventory.root
 
2412
            other_root = other_tree.root_inventory.root
2348
2413
            other_root.parent_id = new_root_parent
2349
2414
            other_root.name = osutils.basename(other_tree_path)
2350
 
            self.inventory.add(other_root)
2351
 
            add_children(self.inventory, other_root)
2352
 
            self._write_inventory(self.inventory)
 
2415
            self.root_inventory.add(other_root)
 
2416
            add_children(self.root_inventory, other_root)
 
2417
            self._write_inventory(self.root_inventory)
2353
2418
            # normally we don't want to fetch whole repositories, but i think
2354
2419
            # here we really do want to consolidate the whole thing.
2355
2420
            for parent_id in other_tree.get_parent_ids():
2393
2458
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2394
2459
        if tree_transport.base != branch_transport.base:
2395
2460
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2396
 
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
2397
 
                target_branch=new_branch)
 
2461
            tree_bzrdir.set_branch_reference(new_branch)
2398
2462
        else:
2399
2463
            tree_bzrdir = branch_bzrdir
2400
2464
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2401
2465
        wt.set_parent_ids(self.get_parent_ids())
2402
 
        my_inv = self.inventory
 
2466
        # FIXME: Support nested trees
 
2467
        my_inv = self.root_inventory
2403
2468
        child_inv = inventory.Inventory(root_id=None)
2404
2469
        new_root = my_inv[file_id]
2405
2470
        my_inv.remove_recursive_id(file_id)
2425
2490
        if not self.is_locked():
2426
2491
            raise errors.ObjectNotLocked(self)
2427
2492
 
2428
 
        inv = self.inventory
2429
2493
        if from_dir is None and include_root is True:
2430
 
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
 
2494
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
2431
2495
        # Convert these into local objects to save lookup times
2432
2496
        pathjoin = osutils.pathjoin
2433
2497
        file_kind = self._kind
2440
2504
 
2441
2505
        # directory file_id, relative path, absolute path, reverse sorted children
2442
2506
        if from_dir is not None:
2443
 
            from_dir_id = inv.path2id(from_dir)
 
2507
            inv, from_dir_id = self._path2inv_file_id(from_dir)
2444
2508
            if from_dir_id is None:
2445
2509
                # Directory not versioned
2446
2510
                return
2447
2511
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2448
2512
        else:
 
2513
            inv = self.root_inventory
2449
2514
            from_dir_id = inv.root.file_id
2450
2515
            from_dir_abspath = self.basedir
2451
2516
        children = os.listdir(from_dir_abspath)
2574
2639
        rename_entries = []
2575
2640
        rename_tuples = []
2576
2641
 
 
2642
        invs_to_write = set()
 
2643
 
2577
2644
        # check for deprecated use of signature
2578
2645
        if to_dir is None:
2579
2646
            raise TypeError('You must supply a target directory')
2580
2647
        # check destination directory
2581
2648
        if isinstance(from_paths, basestring):
2582
2649
            raise ValueError()
2583
 
        inv = self.inventory
2584
2650
        to_abs = self.abspath(to_dir)
2585
2651
        if not isdir(to_abs):
2586
2652
            raise errors.BzrMoveFailedError('',to_dir,
2588
2654
        if not self.has_filename(to_dir):
2589
2655
            raise errors.BzrMoveFailedError('',to_dir,
2590
2656
                errors.NotInWorkingDirectory(to_dir))
2591
 
        to_dir_id = inv.path2id(to_dir)
 
2657
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2592
2658
        if to_dir_id is None:
2593
2659
            raise errors.BzrMoveFailedError('',to_dir,
2594
2660
                errors.NotVersionedError(path=to_dir))
2595
2661
 
2596
 
        to_dir_ie = inv[to_dir_id]
 
2662
        to_dir_ie = to_inv[to_dir_id]
2597
2663
        if to_dir_ie.kind != 'directory':
2598
2664
            raise errors.BzrMoveFailedError('',to_dir,
2599
2665
                errors.NotADirectory(to_abs))
2601
2667
        # create rename entries and tuples
2602
2668
        for from_rel in from_paths:
2603
2669
            from_tail = splitpath(from_rel)[-1]
2604
 
            from_id = inv.path2id(from_rel)
 
2670
            from_inv, from_id = self._path2inv_file_id(from_rel)
2605
2671
            if from_id is None:
2606
2672
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2607
2673
                    errors.NotVersionedError(path=from_rel))
2608
2674
 
2609
 
            from_entry = inv[from_id]
 
2675
            from_entry = from_inv[from_id]
2610
2676
            from_parent_id = from_entry.parent_id
2611
2677
            to_rel = pathjoin(to_dir, from_tail)
2612
2678
            rename_entry = InventoryWorkingTree._RenameEntry(
2631
2697
            # restore the inventory on error
2632
2698
            self._inventory_is_modified = original_modified
2633
2699
            raise
2634
 
        self._write_inventory(inv)
 
2700
        #FIXME: Should potentially also write the from_invs
 
2701
        self._write_inventory(to_inv)
2635
2702
        return rename_tuples
2636
2703
 
2637
2704
    @needs_tree_write_lock
2657
2724
 
2658
2725
        Everything else results in an error.
2659
2726
        """
2660
 
        inv = self.inventory
2661
2727
        rename_entries = []
2662
2728
 
2663
2729
        # create rename entries and tuples
2664
2730
        from_tail = splitpath(from_rel)[-1]
2665
 
        from_id = inv.path2id(from_rel)
 
2731
        from_inv, from_id = self._path2inv_file_id(from_rel)
2666
2732
        if from_id is None:
2667
2733
            # if file is missing in the inventory maybe it's in the basis_tree
2668
2734
            basis_tree = self.branch.basis_tree()
2671
2737
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2672
2738
                    errors.NotVersionedError(path=from_rel))
2673
2739
            # put entry back in the inventory so we can rename it
2674
 
            from_entry = basis_tree.inventory[from_id].copy()
2675
 
            inv.add(from_entry)
 
2740
            from_entry = basis_tree.root_inventory[from_id].copy()
 
2741
            from_inv.add(from_entry)
2676
2742
        else:
2677
 
            from_entry = inv[from_id]
 
2743
            from_inv, from_inv_id = self._unpack_file_id(from_id)
 
2744
            from_entry = from_inv[from_inv_id]
2678
2745
        from_parent_id = from_entry.parent_id
2679
2746
        to_dir, to_tail = os.path.split(to_rel)
2680
 
        to_dir_id = inv.path2id(to_dir)
 
2747
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2681
2748
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2682
2749
                                     from_id=from_id,
2683
2750
                                     from_tail=from_tail,
2705
2772
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2706
2773
 
2707
2774
        self._move(rename_entries)
2708
 
        self._write_inventory(inv)
 
2775
        self._write_inventory(to_inv)
2709
2776
 
2710
2777
    class _RenameEntry(object):
2711
2778
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2727
2794
 
2728
2795
        Also does basic plausability tests.
2729
2796
        """
2730
 
        inv = self.inventory
 
2797
        # FIXME: Handling of nested trees
 
2798
        inv = self.root_inventory
2731
2799
 
2732
2800
        for rename_entry in rename_entries:
2733
2801
            # store to local variables for easier reference
2778
2846
                # something is wrong, so lets determine what exactly
2779
2847
                if not self.has_filename(from_rel) and \
2780
2848
                   not self.has_filename(to_rel):
2781
 
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
2782
 
                        errors.PathsDoNotExist(paths=(str(from_rel),
2783
 
                        str(to_rel))))
 
2849
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
 
2850
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2784
2851
                else:
2785
2852
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2786
2853
            rename_entry.only_change_inv = only_change_inv
2792
2859
        Depending on the value of the flag 'only_change_inv', the
2793
2860
        file will be moved on the file system or not.
2794
2861
        """
2795
 
        inv = self.inventory
2796
2862
        moved = []
2797
2863
 
2798
2864
        for entry in rename_entries:
2805
2871
 
2806
2872
    def _rollback_move(self, moved):
2807
2873
        """Try to rollback a previous move in case of an filesystem error."""
2808
 
        inv = self.inventory
2809
2874
        for entry in moved:
2810
2875
            try:
2811
2876
                self._move_entry(WorkingTree._RenameEntry(
2820
2885
                        " Error message is: %s" % e)
2821
2886
 
2822
2887
    def _move_entry(self, entry):
2823
 
        inv = self.inventory
 
2888
        inv = self.root_inventory
2824
2889
        from_rel_abs = self.abspath(entry.from_rel)
2825
2890
        to_rel_abs = self.abspath(entry.to_rel)
2826
2891
        if from_rel_abs == to_rel_abs:
2867
2932
 
2868
2933
    def stored_kind(self, file_id):
2869
2934
        """See Tree.stored_kind"""
2870
 
        return self.inventory[file_id].kind
 
2935
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2936
        return inv[inv_file_id].kind
2871
2937
 
2872
2938
    def extras(self):
2873
2939
        """Yield all unversioned files in this WorkingTree.
2880
2946
        This is the same order used by 'osutils.walkdirs'.
2881
2947
        """
2882
2948
        ## TODO: Work from given directory downwards
2883
 
        for path, dir_entry in self.inventory.directories():
 
2949
        for path, dir_entry in self.iter_entries_by_dir():
 
2950
            if dir_entry.kind != 'directory':
 
2951
                continue
2884
2952
            # mutter("search for unknowns in %r", path)
2885
2953
            dirabs = self.abspath(path)
2886
2954
            if not isdir(dirabs):
2923
2991
        """
2924
2992
        _directory = 'directory'
2925
2993
        # get the root in the inventory
2926
 
        inv = self.inventory
2927
 
        top_id = inv.path2id(prefix)
 
2994
        inv, top_id = self._path2inv_file_id(prefix)
2928
2995
        if top_id is None:
2929
2996
            pending = []
2930
2997
        else:
2951
3018
                if dir[2] == _directory:
2952
3019
                    pending.append(dir)
2953
3020
 
 
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
 
2954
3031
 
2955
3032
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2956
3033
    """Registry for working tree formats."""
3012
3089
 
3013
3090
    supports_versioned_directories = None
3014
3091
 
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,
 
3092
    def initialize(self, controldir, revision_id=None, from_branch=None,
3035
3093
                   accelerator_tree=None, hardlink=False):
3036
 
        """Initialize a new working tree in a_bzrdir.
 
3094
        """Initialize a new working tree in controldir.
3037
3095
 
3038
 
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
3096
        :param controldir: ControlDir to initialize the working tree in.
3039
3097
        :param revision_id: allows creating a working tree at a different
3040
3098
            revision than the branch is at.
3041
3099
        :param from_branch: Branch to checkout
3054
3112
    def __ne__(self, other):
3055
3113
        return not (self == other)
3056
3114
 
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
 
 
3068
3115
    def get_format_description(self):
3069
3116
        """Return the short description for this format."""
3070
3117
        raise NotImplementedError(self.get_format_description)
3086
3133
        """True if this format supports stored views."""
3087
3134
        return False
3088
3135
 
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)
 
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)
3124
3215
 
3125
3216
 
3126
3217
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",