~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Jelmer Vernooij
  • Date: 2016-04-03 16:32:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6617.
  • Revision ID: jelmer@jelmer.uk-20160403163231-h72bo0uyek2gikw0
Don't put French text in doc/en/user-reference when LANGUAGE=fr_CH.UTF_8.

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))
595
590
            else:
596
591
                return None
597
592
 
598
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
599
 
        # FIXME: Shouldn't this be in Tree?
600
 
        raise NotImplementedError(self.get_file_sha1)
601
 
 
602
593
    @needs_tree_write_lock
603
594
    def _gather_kinds(self, files, kinds):
604
595
        """See MutableTree._gather_kinds."""
769
760
 
770
761
    @needs_tree_write_lock
771
762
    def set_merge_modified(self, modified_hashes):
772
 
        def iter_stanzas():
773
 
            for file_id, hash in modified_hashes.iteritems():
774
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
775
 
                    hash=hash)
776
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
763
        """Set the merge modified hashes."""
 
764
        raise NotImplementedError(self.set_merge_modified)
777
765
 
778
766
    def _sha_from_stat(self, path, stat_result):
779
767
        """Get a sha digest from the tree's stat cache.
785
773
        """
786
774
        return None
787
775
 
788
 
    def _put_rio(self, filename, stanzas, header):
789
 
        self._must_be_locked()
790
 
        my_file = _mod_rio.rio_file(stanzas, header)
791
 
        self._transport.put_file(filename, my_file,
792
 
            mode=self.bzrdir._get_file_mode())
793
 
 
794
776
    @needs_write_lock # because merge pulls data into the branch.
795
777
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
796
778
                          merge_type=None, force=False):
1036
1018
                                show_base=show_base)
1037
1019
                    basis_root_id = basis_tree.get_root_id()
1038
1020
                    new_root_id = new_basis_tree.get_root_id()
1039
 
                    if basis_root_id != new_root_id:
 
1021
                    if new_root_id is not None and basis_root_id != new_root_id:
1040
1022
                        self.set_root_id(new_root_id)
1041
1023
                finally:
1042
1024
                    basis_tree.unlock()
1043
1025
                # TODO - dedup parents list with things merged by pull ?
1044
1026
                # reuse the revisiontree we merged against to set the new
1045
1027
                # tree data.
1046
 
                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))
1047
1032
                # we have to pull the merge trees out again, because
1048
1033
                # merge_inner has set the ids. - this corner is not yet
1049
1034
                # layered well enough to prevent double handling.
1066
1051
            stream.write(bytes)
1067
1052
        finally:
1068
1053
            stream.close()
1069
 
        # TODO: update the hashcache here ?
1070
1054
 
1071
1055
    def extras(self):
1072
1056
        """Yield all unversioned files in this WorkingTree.
1149
1133
        else:
1150
1134
            mode = stat_value.st_mode
1151
1135
            kind = osutils.file_kind_from_stat_mode(mode)
1152
 
            if not supports_executable():
 
1136
            if not self._supports_executable():
1153
1137
                executable = entry is not None and entry.executable
1154
1138
            else:
1155
1139
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1174
1158
        return _mod_revision.ensure_null(self.branch.last_revision())
1175
1159
 
1176
1160
    def is_locked(self):
1177
 
        return self._control_files.is_locked()
1178
 
 
1179
 
    def _must_be_locked(self):
1180
 
        if not self.is_locked():
1181
 
            raise errors.ObjectNotLocked(self)
 
1161
        """Check if this tree is locked."""
 
1162
        raise NotImplementedError(self.is_locked)
1182
1163
 
1183
1164
    def lock_read(self):
1184
1165
        """Lock the tree for reading.
1187
1168
 
1188
1169
        :return: A bzrlib.lock.LogicalLockResult.
1189
1170
        """
1190
 
        if not self.is_locked():
1191
 
            self._reset_data()
1192
 
        self.branch.lock_read()
1193
 
        try:
1194
 
            self._control_files.lock_read()
1195
 
            return LogicalLockResult(self.unlock)
1196
 
        except:
1197
 
            self.branch.unlock()
1198
 
            raise
 
1171
        raise NotImplementedError(self.lock_read)
1199
1172
 
1200
1173
    def lock_tree_write(self):
1201
1174
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1202
1175
 
1203
1176
        :return: A bzrlib.lock.LogicalLockResult.
1204
1177
        """
1205
 
        if not self.is_locked():
1206
 
            self._reset_data()
1207
 
        self.branch.lock_read()
1208
 
        try:
1209
 
            self._control_files.lock_write()
1210
 
            return LogicalLockResult(self.unlock)
1211
 
        except:
1212
 
            self.branch.unlock()
1213
 
            raise
 
1178
        raise NotImplementedError(self.lock_tree_write)
1214
1179
 
1215
1180
    def lock_write(self):
1216
1181
        """See MutableTree.lock_write, and WorkingTree.unlock.
1217
1182
 
1218
1183
        :return: A bzrlib.lock.LogicalLockResult.
1219
1184
        """
1220
 
        if not self.is_locked():
1221
 
            self._reset_data()
1222
 
        self.branch.lock_write()
1223
 
        try:
1224
 
            self._control_files.lock_write()
1225
 
            return LogicalLockResult(self.unlock)
1226
 
        except:
1227
 
            self.branch.unlock()
1228
 
            raise
 
1185
        raise NotImplementedError(self.lock_write)
1229
1186
 
1230
1187
    def get_physical_lock_status(self):
1231
 
        return self._control_files.get_physical_lock_status()
1232
 
 
1233
 
    def _reset_data(self):
1234
 
        """Reset transient data that cannot be revalidated."""
1235
 
        raise NotImplementedError(self._reset_data)
 
1188
        raise NotImplementedError(self.get_physical_lock_status)
1236
1189
 
1237
1190
    def set_last_revision(self, new_revision):
1238
1191
        """Change the last revision in the working tree."""
1404
1357
                basis_tree.unlock()
1405
1358
        return conflicts
1406
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
 
1407
1388
    def revision_tree(self, revision_id):
1408
1389
        """See Tree.revision_tree.
1409
1390
 
1532
1513
                                             show_base=show_base)
1533
1514
            if nb_conflicts:
1534
1515
                self.add_parent_tree((old_tip, other_tree))
1535
 
                note('Rerun update after fixing the conflicts.')
 
1516
                note(gettext('Rerun update after fixing the conflicts.'))
1536
1517
                return nb_conflicts
1537
1518
 
1538
1519
        if last_rev != _mod_revision.ensure_null(revision):
1580
1561
            last_rev = parent_trees[0][0]
1581
1562
        return nb_conflicts
1582
1563
 
1583
 
    def _write_hashcache_if_dirty(self):
1584
 
        """Write out the hashcache if it is dirty."""
1585
 
        if self._hashcache.needs_write:
1586
 
            try:
1587
 
                self._hashcache.write()
1588
 
            except OSError, e:
1589
 
                if e.errno not in (errno.EPERM, errno.EACCES):
1590
 
                    raise
1591
 
                # TODO: jam 20061219 Should this be a warning? A single line
1592
 
                #       warning might be sufficient to let the user know what
1593
 
                #       is going on.
1594
 
                mutter('Could not write hashcache for %s\nError: %s',
1595
 
                              self._hashcache.cache_file_name(), e)
1596
 
 
1597
1564
    def set_conflicts(self, arg):
1598
1565
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1599
1566
 
1828
1795
        :param branch: A branch to override probing for the branch.
1829
1796
        """
1830
1797
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1831
 
            branch=branch, _control_files=_control_files, _internal=_internal,
1832
 
            _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()
1833
1803
 
1834
1804
        if _inventory is None:
1835
1805
            # This will be acquired on lock_read() or lock_write()
1855
1825
        self._inventory = inv
1856
1826
        self._inventory_is_modified = dirty
1857
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
 
1858
1839
    def _serialize(self, inventory, out_file):
1859
1840
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1860
1841
            working=True)
1862
1843
    def _deserialize(selt, in_file):
1863
1844
        return xml5.serializer_v5.read_inventory(in_file)
1864
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
 
1865
1914
    @needs_tree_write_lock
1866
1915
    def _write_inventory(self, inv):
1867
1916
        """Write inventory as the current inventory."""
1935
1984
            if entry.parent_id == orig_root_id:
1936
1985
                entry.parent_id = inv.root.file_id
1937
1986
 
1938
 
    def all_file_ids(self):
1939
 
        """See Tree.iter_all_file_ids"""
1940
 
        return set(self.inventory)
1941
 
 
1942
1987
    @needs_tree_write_lock
1943
1988
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1944
1989
        """See MutableTree.set_parent_trees."""
1963
2008
                # parent tree from the repository.
1964
2009
                self._cache_basis_inventory(leftmost_parent_id)
1965
2010
            else:
1966
 
                inv = leftmost_parent_tree.inventory
 
2011
                inv = leftmost_parent_tree.root_inventory
1967
2012
                xml = self._create_basis_xml_from_inventory(
1968
2013
                                        leftmost_parent_id, inv)
1969
2014
                self._write_basis_inventory(xml)
2066
2111
 
2067
2112
    def has_id(self, file_id):
2068
2113
        # files that have been deleted are excluded
2069
 
        inv = self.inventory
2070
 
        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):
2071
2116
            return False
2072
 
        path = inv.id2path(file_id)
 
2117
        path = inv.id2path(inv_file_id)
2073
2118
        return osutils.lexists(self.abspath(path))
2074
2119
 
2075
2120
    def has_or_had_id(self, file_id):
2076
 
        if file_id == self.inventory.root.file_id:
 
2121
        if file_id == self.get_root_id():
2077
2122
            return True
2078
 
        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)
2079
2125
 
2080
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2081
 
    def __iter__(self):
 
2126
    def all_file_ids(self):
2082
2127
        """Iterate through file_ids for this tree.
2083
2128
 
2084
2129
        file_ids are in a WorkingTree if they are in the working inventory
2085
2130
        and the working file exists.
2086
2131
        """
2087
 
        inv = self._inventory
2088
 
        for path, ie in inv.iter_entries():
2089
 
            if osutils.lexists(self.abspath(path)):
2090
 
                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
2091
2136
 
2092
2137
    @needs_tree_write_lock
2093
2138
    def set_last_revision(self, new_revision):
2149
2194
                _mod_revision.NULL_REVISION)
2150
2195
        else:
2151
2196
            rt = self.branch.repository.revision_tree(revision_ids[0])
2152
 
        self._write_inventory(rt.inventory)
 
2197
        self._write_inventory(rt.root_inventory)
2153
2198
        self.set_parent_ids(revision_ids)
2154
2199
 
2155
2200
    def flush(self):
2164
2209
            mode=self.bzrdir._get_file_mode())
2165
2210
        self._inventory_is_modified = False
2166
2211
 
2167
 
    @needs_read_lock
2168
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2169
 
        if not path:
2170
 
            path = self._inventory.id2path(file_id)
2171
 
        return self._hashcache.get_sha1(path, stat_value)
2172
 
 
2173
2212
    def get_file_mtime(self, file_id, path=None):
2174
2213
        """See Tree.get_file_mtime."""
2175
2214
        if not path:
2176
 
            path = self.inventory.id2path(file_id)
2177
 
        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
2178
2222
 
2179
2223
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2180
 
        file_id = self.path2id(path)
 
2224
        inv, file_id = self._path2inv_file_id(path)
2181
2225
        if file_id is None:
2182
2226
            # For unversioned files on win32, we just assume they are not
2183
2227
            # executable
2184
2228
            return False
2185
 
        return self._inventory[file_id].executable
 
2229
        return inv[file_id].executable
2186
2230
 
2187
2231
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2188
2232
        mode = stat_result.st_mode
2189
2233
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2190
2234
 
2191
 
    if not supports_executable():
2192
 
        def is_executable(self, file_id, path=None):
2193
 
            return self._inventory[file_id].executable
2194
 
 
2195
 
        _is_executable_from_path_and_stat = \
2196
 
            _is_executable_from_path_and_stat_from_basis
2197
 
    else:
2198
 
        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:
2199
2240
            if not path:
2200
2241
                path = self.id2path(file_id)
2201
2242
            mode = os.lstat(self.abspath(path)).st_mode
2202
2243
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2203
2244
 
2204
 
        _is_executable_from_path_and_stat = \
2205
 
            _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)
2206
2250
 
2207
2251
    @needs_tree_write_lock
2208
2252
    def _add(self, files, ids, kinds):
2211
2255
        # should probably put it back with the previous ID.
2212
2256
        # the read and write working inventory should not occur in this
2213
2257
        # function - they should be part of lock_write and unlock.
2214
 
        inv = self.inventory
 
2258
        # FIXME: nested trees
 
2259
        inv = self.root_inventory
2215
2260
        for f, file_id, kind in zip(files, ids, kinds):
2216
2261
            if file_id is None:
2217
2262
                inv.add_path(f, kind=kind)
2258
2303
                parent_tree = self.branch.repository.revision_tree(parent_id)
2259
2304
            parent_tree.lock_read()
2260
2305
            try:
2261
 
                if not parent_tree.has_id(file_id):
 
2306
                try:
 
2307
                    kind = parent_tree.kind(file_id)
 
2308
                except errors.NoSuchId:
2262
2309
                    continue
2263
 
                ie = parent_tree.inventory[file_id]
2264
 
                if ie.kind != 'file':
 
2310
                if kind != 'file':
2265
2311
                    # Note: this is slightly unnecessary, because symlinks and
2266
2312
                    # directories have a "text" which is the empty text, and we
2267
2313
                    # know that won't mess up annotations. But it seems cleaner
2268
2314
                    continue
2269
 
                parent_text_key = (file_id, ie.revision)
 
2315
                parent_text_key = (
 
2316
                    file_id, parent_tree.get_file_revision(file_id))
2270
2317
                if parent_text_key not in maybe_file_parent_keys:
2271
2318
                    maybe_file_parent_keys.append(parent_text_key)
2272
2319
            finally:
2287
2334
                       for key, line in annotator.annotate_flat(this_key)]
2288
2335
        return annotations
2289
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
 
2290
2351
    @needs_read_lock
2291
2352
    def merge_modified(self):
2292
2353
        """Return a dictionary of files modified by a merge.
2312
2373
            for s in _mod_rio.RioReader(hashfile):
2313
2374
                # RioReader reads in Unicode, so convert file_ids back to utf8
2314
2375
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2315
 
                if not self.inventory.has_id(file_id):
 
2376
                if not self.has_id(file_id):
2316
2377
                    continue
2317
2378
                text_hash = s.get("hash")
2318
2379
                if text_hash == self.get_file_sha1(file_id):
2348
2409
        other_tree.lock_tree_write()
2349
2410
        try:
2350
2411
            new_parents = other_tree.get_parent_ids()
2351
 
            other_root = other_tree.inventory.root
 
2412
            other_root = other_tree.root_inventory.root
2352
2413
            other_root.parent_id = new_root_parent
2353
2414
            other_root.name = osutils.basename(other_tree_path)
2354
 
            self.inventory.add(other_root)
2355
 
            add_children(self.inventory, other_root)
2356
 
            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)
2357
2418
            # normally we don't want to fetch whole repositories, but i think
2358
2419
            # here we really do want to consolidate the whole thing.
2359
2420
            for parent_id in other_tree.get_parent_ids():
2397
2458
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2398
2459
        if tree_transport.base != branch_transport.base:
2399
2460
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2400
 
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
2401
 
                target_branch=new_branch)
 
2461
            tree_bzrdir.set_branch_reference(new_branch)
2402
2462
        else:
2403
2463
            tree_bzrdir = branch_bzrdir
2404
2464
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2405
2465
        wt.set_parent_ids(self.get_parent_ids())
2406
 
        my_inv = self.inventory
 
2466
        # FIXME: Support nested trees
 
2467
        my_inv = self.root_inventory
2407
2468
        child_inv = inventory.Inventory(root_id=None)
2408
2469
        new_root = my_inv[file_id]
2409
2470
        my_inv.remove_recursive_id(file_id)
2429
2490
        if not self.is_locked():
2430
2491
            raise errors.ObjectNotLocked(self)
2431
2492
 
2432
 
        inv = self.inventory
2433
2493
        if from_dir is None and include_root is True:
2434
 
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
 
2494
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
2435
2495
        # Convert these into local objects to save lookup times
2436
2496
        pathjoin = osutils.pathjoin
2437
2497
        file_kind = self._kind
2444
2504
 
2445
2505
        # directory file_id, relative path, absolute path, reverse sorted children
2446
2506
        if from_dir is not None:
2447
 
            from_dir_id = inv.path2id(from_dir)
 
2507
            inv, from_dir_id = self._path2inv_file_id(from_dir)
2448
2508
            if from_dir_id is None:
2449
2509
                # Directory not versioned
2450
2510
                return
2451
2511
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2452
2512
        else:
 
2513
            inv = self.root_inventory
2453
2514
            from_dir_id = inv.root.file_id
2454
2515
            from_dir_abspath = self.basedir
2455
2516
        children = os.listdir(from_dir_abspath)
2578
2639
        rename_entries = []
2579
2640
        rename_tuples = []
2580
2641
 
 
2642
        invs_to_write = set()
 
2643
 
2581
2644
        # check for deprecated use of signature
2582
2645
        if to_dir is None:
2583
2646
            raise TypeError('You must supply a target directory')
2584
2647
        # check destination directory
2585
2648
        if isinstance(from_paths, basestring):
2586
2649
            raise ValueError()
2587
 
        inv = self.inventory
2588
2650
        to_abs = self.abspath(to_dir)
2589
2651
        if not isdir(to_abs):
2590
2652
            raise errors.BzrMoveFailedError('',to_dir,
2592
2654
        if not self.has_filename(to_dir):
2593
2655
            raise errors.BzrMoveFailedError('',to_dir,
2594
2656
                errors.NotInWorkingDirectory(to_dir))
2595
 
        to_dir_id = inv.path2id(to_dir)
 
2657
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2596
2658
        if to_dir_id is None:
2597
2659
            raise errors.BzrMoveFailedError('',to_dir,
2598
2660
                errors.NotVersionedError(path=to_dir))
2599
2661
 
2600
 
        to_dir_ie = inv[to_dir_id]
 
2662
        to_dir_ie = to_inv[to_dir_id]
2601
2663
        if to_dir_ie.kind != 'directory':
2602
2664
            raise errors.BzrMoveFailedError('',to_dir,
2603
2665
                errors.NotADirectory(to_abs))
2605
2667
        # create rename entries and tuples
2606
2668
        for from_rel in from_paths:
2607
2669
            from_tail = splitpath(from_rel)[-1]
2608
 
            from_id = inv.path2id(from_rel)
 
2670
            from_inv, from_id = self._path2inv_file_id(from_rel)
2609
2671
            if from_id is None:
2610
2672
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2611
2673
                    errors.NotVersionedError(path=from_rel))
2612
2674
 
2613
 
            from_entry = inv[from_id]
 
2675
            from_entry = from_inv[from_id]
2614
2676
            from_parent_id = from_entry.parent_id
2615
2677
            to_rel = pathjoin(to_dir, from_tail)
2616
2678
            rename_entry = InventoryWorkingTree._RenameEntry(
2635
2697
            # restore the inventory on error
2636
2698
            self._inventory_is_modified = original_modified
2637
2699
            raise
2638
 
        self._write_inventory(inv)
 
2700
        #FIXME: Should potentially also write the from_invs
 
2701
        self._write_inventory(to_inv)
2639
2702
        return rename_tuples
2640
2703
 
2641
2704
    @needs_tree_write_lock
2661
2724
 
2662
2725
        Everything else results in an error.
2663
2726
        """
2664
 
        inv = self.inventory
2665
2727
        rename_entries = []
2666
2728
 
2667
2729
        # create rename entries and tuples
2668
2730
        from_tail = splitpath(from_rel)[-1]
2669
 
        from_id = inv.path2id(from_rel)
 
2731
        from_inv, from_id = self._path2inv_file_id(from_rel)
2670
2732
        if from_id is None:
2671
2733
            # if file is missing in the inventory maybe it's in the basis_tree
2672
2734
            basis_tree = self.branch.basis_tree()
2675
2737
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2676
2738
                    errors.NotVersionedError(path=from_rel))
2677
2739
            # put entry back in the inventory so we can rename it
2678
 
            from_entry = basis_tree.inventory[from_id].copy()
2679
 
            inv.add(from_entry)
 
2740
            from_entry = basis_tree.root_inventory[from_id].copy()
 
2741
            from_inv.add(from_entry)
2680
2742
        else:
2681
 
            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]
2682
2745
        from_parent_id = from_entry.parent_id
2683
2746
        to_dir, to_tail = os.path.split(to_rel)
2684
 
        to_dir_id = inv.path2id(to_dir)
 
2747
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2685
2748
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2686
2749
                                     from_id=from_id,
2687
2750
                                     from_tail=from_tail,
2709
2772
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2710
2773
 
2711
2774
        self._move(rename_entries)
2712
 
        self._write_inventory(inv)
 
2775
        self._write_inventory(to_inv)
2713
2776
 
2714
2777
    class _RenameEntry(object):
2715
2778
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2731
2794
 
2732
2795
        Also does basic plausability tests.
2733
2796
        """
2734
 
        inv = self.inventory
 
2797
        # FIXME: Handling of nested trees
 
2798
        inv = self.root_inventory
2735
2799
 
2736
2800
        for rename_entry in rename_entries:
2737
2801
            # store to local variables for easier reference
2782
2846
                # something is wrong, so lets determine what exactly
2783
2847
                if not self.has_filename(from_rel) and \
2784
2848
                   not self.has_filename(to_rel):
2785
 
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
2786
 
                        errors.PathsDoNotExist(paths=(str(from_rel),
2787
 
                        str(to_rel))))
 
2849
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
 
2850
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2788
2851
                else:
2789
2852
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2790
2853
            rename_entry.only_change_inv = only_change_inv
2796
2859
        Depending on the value of the flag 'only_change_inv', the
2797
2860
        file will be moved on the file system or not.
2798
2861
        """
2799
 
        inv = self.inventory
2800
2862
        moved = []
2801
2863
 
2802
2864
        for entry in rename_entries:
2809
2871
 
2810
2872
    def _rollback_move(self, moved):
2811
2873
        """Try to rollback a previous move in case of an filesystem error."""
2812
 
        inv = self.inventory
2813
2874
        for entry in moved:
2814
2875
            try:
2815
2876
                self._move_entry(WorkingTree._RenameEntry(
2824
2885
                        " Error message is: %s" % e)
2825
2886
 
2826
2887
    def _move_entry(self, entry):
2827
 
        inv = self.inventory
 
2888
        inv = self.root_inventory
2828
2889
        from_rel_abs = self.abspath(entry.from_rel)
2829
2890
        to_rel_abs = self.abspath(entry.to_rel)
2830
2891
        if from_rel_abs == to_rel_abs:
2871
2932
 
2872
2933
    def stored_kind(self, file_id):
2873
2934
        """See Tree.stored_kind"""
2874
 
        return self.inventory[file_id].kind
 
2935
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2936
        return inv[inv_file_id].kind
2875
2937
 
2876
2938
    def extras(self):
2877
2939
        """Yield all unversioned files in this WorkingTree.
2884
2946
        This is the same order used by 'osutils.walkdirs'.
2885
2947
        """
2886
2948
        ## TODO: Work from given directory downwards
2887
 
        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
2888
2952
            # mutter("search for unknowns in %r", path)
2889
2953
            dirabs = self.abspath(path)
2890
2954
            if not isdir(dirabs):
2927
2991
        """
2928
2992
        _directory = 'directory'
2929
2993
        # get the root in the inventory
2930
 
        inv = self.inventory
2931
 
        top_id = inv.path2id(prefix)
 
2994
        inv, top_id = self._path2inv_file_id(prefix)
2932
2995
        if top_id is None:
2933
2996
            pending = []
2934
2997
        else:
2955
3018
                if dir[2] == _directory:
2956
3019
                    pending.append(dir)
2957
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
 
2958
3031
 
2959
3032
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2960
3033
    """Registry for working tree formats."""
3016
3089
 
3017
3090
    supports_versioned_directories = None
3018
3091
 
3019
 
    @classmethod
3020
 
    def find_format_string(klass, a_bzrdir):
3021
 
        """Return format name for the working tree object in a_bzrdir."""
3022
 
        try:
3023
 
            transport = a_bzrdir.get_workingtree_transport(None)
3024
 
            return transport.get_bytes("format")
3025
 
        except errors.NoSuchFile:
3026
 
            raise errors.NoWorkingTree(base=transport.base)
3027
 
 
3028
 
    @classmethod
3029
 
    def find_format(klass, a_bzrdir):
3030
 
        """Return the format for the working tree object in a_bzrdir."""
3031
 
        try:
3032
 
            format_string = klass.find_format_string(a_bzrdir)
3033
 
            format_string = bzrdir.extract_format_string(format_string)
3034
 
            return format_registry.get(format_string)
3035
 
        except KeyError:
3036
 
            raise errors.UnknownFormatError(format=format_string,
3037
 
                                            kind="working tree")
3038
 
 
3039
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
3092
    def initialize(self, controldir, revision_id=None, from_branch=None,
3040
3093
                   accelerator_tree=None, hardlink=False):
3041
 
        """Initialize a new working tree in a_bzrdir.
 
3094
        """Initialize a new working tree in controldir.
3042
3095
 
3043
 
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
3096
        :param controldir: ControlDir to initialize the working tree in.
3044
3097
        :param revision_id: allows creating a working tree at a different
3045
3098
            revision than the branch is at.
3046
3099
        :param from_branch: Branch to checkout
3059
3112
    def __ne__(self, other):
3060
3113
        return not (self == other)
3061
3114
 
3062
 
    @classmethod
3063
 
    @symbol_versioning.deprecated_method(
3064
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3065
 
    def get_default_format(klass):
3066
 
        """Return the current default format."""
3067
 
        return format_registry.get_default()
3068
 
 
3069
 
    def get_format_string(self):
3070
 
        """Return the ASCII format string that identifies this format."""
3071
 
        raise NotImplementedError(self.get_format_string)
3072
 
 
3073
3115
    def get_format_description(self):
3074
3116
        """Return the short description for this format."""
3075
3117
        raise NotImplementedError(self.get_format_description)
3091
3133
        """True if this format supports stored views."""
3092
3134
        return False
3093
3135
 
3094
 
    @classmethod
3095
 
    @symbol_versioning.deprecated_method(
3096
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3097
 
    def register_format(klass, format):
3098
 
        format_registry.register(format)
3099
 
 
3100
 
    @classmethod
3101
 
    @symbol_versioning.deprecated_method(
3102
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3103
 
    def register_extra_format(klass, format):
3104
 
        format_registry.register_extra(format)
3105
 
 
3106
 
    @classmethod
3107
 
    @symbol_versioning.deprecated_method(
3108
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3109
 
    def unregister_extra_format(klass, format):
3110
 
        format_registry.unregister_extra(format)
3111
 
 
3112
 
    @classmethod
3113
 
    @symbol_versioning.deprecated_method(
3114
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3115
 
    def get_formats(klass):
3116
 
        return format_registry._get_all()
3117
 
 
3118
 
    @classmethod
3119
 
    @symbol_versioning.deprecated_method(
3120
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3121
 
    def set_default_format(klass, format):
3122
 
        format_registry.set_default(format)
3123
 
 
3124
 
    @classmethod
3125
 
    @symbol_versioning.deprecated_method(
3126
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3127
 
    def unregister_format(klass, format):
3128
 
        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)
3129
3215
 
3130
3216
 
3131
3217
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",