~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

(vila) Make all transport put_bytes() raises TypeError when given unicode
 strings rather than bytes (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))
496
482
        finally:
497
483
            file.close()
498
484
 
499
 
    def _get_ancestors(self, default_revision):
500
 
        ancestors = set([default_revision])
501
 
        for parent_id in self.get_parent_ids():
502
 
            ancestors.update(self.branch.repository.get_ancestry(
503
 
                             parent_id, topo_sorted=False))
504
 
        return ancestors
505
 
 
506
485
    def get_parent_ids(self):
507
486
        """See Tree.get_parent_ids.
508
487
 
529
508
        raise NotImplementedError(self.get_root_id)
530
509
 
531
510
    @needs_read_lock
532
 
    def clone(self, to_bzrdir, revision_id=None):
 
511
    def clone(self, to_controldir, revision_id=None):
533
512
        """Duplicate this working tree into to_bzr, including all state.
534
513
 
535
514
        Specifically modified files are kept as modified, but
536
515
        ignored and unknown files are discarded.
537
516
 
538
 
        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()
539
518
 
540
519
        revision
541
520
            If not None, the cloned tree will have its last revision set to
543
522
            and this one merged in.
544
523
        """
545
524
        # assumes the target bzr dir format is compatible.
546
 
        result = to_bzrdir.create_workingtree()
 
525
        result = to_controldir.create_workingtree()
547
526
        self.copy_content_into(result, revision_id)
548
527
        return result
549
528
 
556
535
        else:
557
536
            # TODO now merge from tree.last_revision to revision (to preserve
558
537
            # user local changes)
559
 
            merge.transform_tree(tree, self)
560
 
            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)
561
549
 
562
550
    def id2abspath(self, file_id):
563
551
        return self.abspath(self.id2path(file_id))
602
590
            else:
603
591
                return None
604
592
 
605
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
606
 
        # FIXME: Shouldn't this be in Tree?
607
 
        raise NotImplementedError(self.get_file_sha1)
608
 
 
609
593
    @needs_tree_write_lock
610
594
    def _gather_kinds(self, files, kinds):
611
595
        """See MutableTree._gather_kinds."""
776
760
 
777
761
    @needs_tree_write_lock
778
762
    def set_merge_modified(self, modified_hashes):
779
 
        def iter_stanzas():
780
 
            for file_id, hash in modified_hashes.iteritems():
781
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
782
 
                    hash=hash)
783
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
763
        """Set the merge modified hashes."""
 
764
        raise NotImplementedError(self.set_merge_modified)
784
765
 
785
766
    def _sha_from_stat(self, path, stat_result):
786
767
        """Get a sha digest from the tree's stat cache.
792
773
        """
793
774
        return None
794
775
 
795
 
    def _put_rio(self, filename, stanzas, header):
796
 
        self._must_be_locked()
797
 
        my_file = _mod_rio.rio_file(stanzas, header)
798
 
        self._transport.put_file(filename, my_file,
799
 
            mode=self.bzrdir._get_file_mode())
800
 
 
801
776
    @needs_write_lock # because merge pulls data into the branch.
802
777
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
803
778
                          merge_type=None, force=False):
973
948
        file and change the file_id. That is the normal mode. Second, it can
974
949
        only change the file_id without touching any physical file.
975
950
 
976
 
        rename_one uses the second mode if 'after == True' and 'to_rel' is not
977
 
        versioned but present in the working tree.
 
951
        rename_one uses the second mode if 'after == True' and 'to_rel' is
 
952
        either not versioned or newly added, and present in the working tree.
978
953
 
979
954
        rename_one uses the second mode if 'after == False' and 'from_rel' is
980
955
        versioned but no longer in the working tree, and 'to_rel' is not
1043
1018
                                show_base=show_base)
1044
1019
                    basis_root_id = basis_tree.get_root_id()
1045
1020
                    new_root_id = new_basis_tree.get_root_id()
1046
 
                    if basis_root_id != new_root_id:
 
1021
                    if new_root_id is not None and basis_root_id != new_root_id:
1047
1022
                        self.set_root_id(new_root_id)
1048
1023
                finally:
1049
1024
                    basis_tree.unlock()
1050
1025
                # TODO - dedup parents list with things merged by pull ?
1051
1026
                # reuse the revisiontree we merged against to set the new
1052
1027
                # tree data.
1053
 
                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))
1054
1032
                # we have to pull the merge trees out again, because
1055
1033
                # merge_inner has set the ids. - this corner is not yet
1056
1034
                # layered well enough to prevent double handling.
1073
1051
            stream.write(bytes)
1074
1052
        finally:
1075
1053
            stream.close()
1076
 
        # TODO: update the hashcache here ?
1077
1054
 
1078
1055
    def extras(self):
1079
1056
        """Yield all unversioned files in this WorkingTree.
1156
1133
        else:
1157
1134
            mode = stat_value.st_mode
1158
1135
            kind = osutils.file_kind_from_stat_mode(mode)
1159
 
            if not supports_executable():
 
1136
            if not self._supports_executable():
1160
1137
                executable = entry is not None and entry.executable
1161
1138
            else:
1162
1139
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1181
1158
        return _mod_revision.ensure_null(self.branch.last_revision())
1182
1159
 
1183
1160
    def is_locked(self):
1184
 
        return self._control_files.is_locked()
1185
 
 
1186
 
    def _must_be_locked(self):
1187
 
        if not self.is_locked():
1188
 
            raise errors.ObjectNotLocked(self)
 
1161
        """Check if this tree is locked."""
 
1162
        raise NotImplementedError(self.is_locked)
1189
1163
 
1190
1164
    def lock_read(self):
1191
1165
        """Lock the tree for reading.
1194
1168
 
1195
1169
        :return: A bzrlib.lock.LogicalLockResult.
1196
1170
        """
1197
 
        if not self.is_locked():
1198
 
            self._reset_data()
1199
 
        self.branch.lock_read()
1200
 
        try:
1201
 
            self._control_files.lock_read()
1202
 
            return LogicalLockResult(self.unlock)
1203
 
        except:
1204
 
            self.branch.unlock()
1205
 
            raise
 
1171
        raise NotImplementedError(self.lock_read)
1206
1172
 
1207
1173
    def lock_tree_write(self):
1208
1174
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1209
1175
 
1210
1176
        :return: A bzrlib.lock.LogicalLockResult.
1211
1177
        """
1212
 
        if not self.is_locked():
1213
 
            self._reset_data()
1214
 
        self.branch.lock_read()
1215
 
        try:
1216
 
            self._control_files.lock_write()
1217
 
            return LogicalLockResult(self.unlock)
1218
 
        except:
1219
 
            self.branch.unlock()
1220
 
            raise
 
1178
        raise NotImplementedError(self.lock_tree_write)
1221
1179
 
1222
1180
    def lock_write(self):
1223
1181
        """See MutableTree.lock_write, and WorkingTree.unlock.
1224
1182
 
1225
1183
        :return: A bzrlib.lock.LogicalLockResult.
1226
1184
        """
1227
 
        if not self.is_locked():
1228
 
            self._reset_data()
1229
 
        self.branch.lock_write()
1230
 
        try:
1231
 
            self._control_files.lock_write()
1232
 
            return LogicalLockResult(self.unlock)
1233
 
        except:
1234
 
            self.branch.unlock()
1235
 
            raise
 
1185
        raise NotImplementedError(self.lock_write)
1236
1186
 
1237
1187
    def get_physical_lock_status(self):
1238
 
        return self._control_files.get_physical_lock_status()
1239
 
 
1240
 
    def _reset_data(self):
1241
 
        """Reset transient data that cannot be revalidated."""
1242
 
        raise NotImplementedError(self._reset_data)
 
1188
        raise NotImplementedError(self.get_physical_lock_status)
1243
1189
 
1244
1190
    def set_last_revision(self, new_revision):
1245
1191
        """Change the last revision in the working tree."""
1385
1331
    def revert(self, filenames=None, old_tree=None, backups=True,
1386
1332
               pb=None, report_changes=False):
1387
1333
        from bzrlib.conflicts import resolve
1388
 
        if filenames == []:
1389
 
            filenames = None
1390
 
            symbol_versioning.warn('Using [] to revert all files is deprecated'
1391
 
                ' as of bzr 0.91.  Please use None (the default) instead.',
1392
 
                DeprecationWarning, stacklevel=2)
1393
1334
        if old_tree is None:
1394
1335
            basis_tree = self.basis_tree()
1395
1336
            basis_tree.lock_read()
1416
1357
                basis_tree.unlock()
1417
1358
        return conflicts
1418
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
 
1419
1388
    def revision_tree(self, revision_id):
1420
1389
        """See Tree.revision_tree.
1421
1390
 
1544
1513
                                             show_base=show_base)
1545
1514
            if nb_conflicts:
1546
1515
                self.add_parent_tree((old_tip, other_tree))
1547
 
                note('Rerun update after fixing the conflicts.')
 
1516
                note(gettext('Rerun update after fixing the conflicts.'))
1548
1517
                return nb_conflicts
1549
1518
 
1550
1519
        if last_rev != _mod_revision.ensure_null(revision):
1592
1561
            last_rev = parent_trees[0][0]
1593
1562
        return nb_conflicts
1594
1563
 
1595
 
    def _write_hashcache_if_dirty(self):
1596
 
        """Write out the hashcache if it is dirty."""
1597
 
        if self._hashcache.needs_write:
1598
 
            try:
1599
 
                self._hashcache.write()
1600
 
            except OSError, e:
1601
 
                if e.errno not in (errno.EPERM, errno.EACCES):
1602
 
                    raise
1603
 
                # TODO: jam 20061219 Should this be a warning? A single line
1604
 
                #       warning might be sufficient to let the user know what
1605
 
                #       is going on.
1606
 
                mutter('Could not write hashcache for %s\nError: %s',
1607
 
                              self._hashcache.cache_file_name(), e)
1608
 
 
1609
1564
    def set_conflicts(self, arg):
1610
1565
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1611
1566
 
1840
1795
        :param branch: A branch to override probing for the branch.
1841
1796
        """
1842
1797
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1843
 
            branch=branch, _control_files=_control_files, _internal=_internal,
1844
 
            _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()
1845
1803
 
1846
1804
        if _inventory is None:
1847
1805
            # This will be acquired on lock_read() or lock_write()
1867
1825
        self._inventory = inv
1868
1826
        self._inventory_is_modified = dirty
1869
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
 
1870
1839
    def _serialize(self, inventory, out_file):
1871
1840
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1872
1841
            working=True)
1874
1843
    def _deserialize(selt, in_file):
1875
1844
        return xml5.serializer_v5.read_inventory(in_file)
1876
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
 
1877
1914
    @needs_tree_write_lock
1878
1915
    def _write_inventory(self, inv):
1879
1916
        """Write inventory as the current inventory."""
1947
1984
            if entry.parent_id == orig_root_id:
1948
1985
                entry.parent_id = inv.root.file_id
1949
1986
 
1950
 
    def all_file_ids(self):
1951
 
        """See Tree.iter_all_file_ids"""
1952
 
        return set(self.inventory)
1953
 
 
1954
1987
    @needs_tree_write_lock
1955
1988
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1956
1989
        """See MutableTree.set_parent_trees."""
1975
2008
                # parent tree from the repository.
1976
2009
                self._cache_basis_inventory(leftmost_parent_id)
1977
2010
            else:
1978
 
                inv = leftmost_parent_tree.inventory
 
2011
                inv = leftmost_parent_tree.root_inventory
1979
2012
                xml = self._create_basis_xml_from_inventory(
1980
2013
                                        leftmost_parent_id, inv)
1981
2014
                self._write_basis_inventory(xml)
2078
2111
 
2079
2112
    def has_id(self, file_id):
2080
2113
        # files that have been deleted are excluded
2081
 
        inv = self.inventory
2082
 
        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):
2083
2116
            return False
2084
 
        path = inv.id2path(file_id)
 
2117
        path = inv.id2path(inv_file_id)
2085
2118
        return osutils.lexists(self.abspath(path))
2086
2119
 
2087
2120
    def has_or_had_id(self, file_id):
2088
 
        if file_id == self.inventory.root.file_id:
 
2121
        if file_id == self.get_root_id():
2089
2122
            return True
2090
 
        return self.inventory.has_id(file_id)
2091
 
 
2092
 
    __contains__ = has_id
2093
 
 
2094
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2095
 
    def __iter__(self):
 
2123
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2124
        return inv.has_id(inv_file_id)
 
2125
 
 
2126
    def all_file_ids(self):
2096
2127
        """Iterate through file_ids for this tree.
2097
2128
 
2098
2129
        file_ids are in a WorkingTree if they are in the working inventory
2099
2130
        and the working file exists.
2100
2131
        """
2101
 
        inv = self._inventory
2102
 
        for path, ie in inv.iter_entries():
2103
 
            if osutils.lexists(self.abspath(path)):
2104
 
                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
2105
2136
 
2106
2137
    @needs_tree_write_lock
2107
2138
    def set_last_revision(self, new_revision):
2163
2194
                _mod_revision.NULL_REVISION)
2164
2195
        else:
2165
2196
            rt = self.branch.repository.revision_tree(revision_ids[0])
2166
 
        self._write_inventory(rt.inventory)
 
2197
        self._write_inventory(rt.root_inventory)
2167
2198
        self.set_parent_ids(revision_ids)
2168
2199
 
2169
2200
    def flush(self):
2178
2209
            mode=self.bzrdir._get_file_mode())
2179
2210
        self._inventory_is_modified = False
2180
2211
 
2181
 
    @needs_read_lock
2182
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2183
 
        if not path:
2184
 
            path = self._inventory.id2path(file_id)
2185
 
        return self._hashcache.get_sha1(path, stat_value)
2186
 
 
2187
2212
    def get_file_mtime(self, file_id, path=None):
2188
2213
        """See Tree.get_file_mtime."""
2189
2214
        if not path:
2190
 
            path = self.inventory.id2path(file_id)
2191
 
        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
2192
2222
 
2193
2223
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2194
 
        file_id = self.path2id(path)
 
2224
        inv, file_id = self._path2inv_file_id(path)
2195
2225
        if file_id is None:
2196
2226
            # For unversioned files on win32, we just assume they are not
2197
2227
            # executable
2198
2228
            return False
2199
 
        return self._inventory[file_id].executable
 
2229
        return inv[file_id].executable
2200
2230
 
2201
2231
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2202
2232
        mode = stat_result.st_mode
2203
2233
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2204
2234
 
2205
 
    if not supports_executable():
2206
 
        def is_executable(self, file_id, path=None):
2207
 
            return self._inventory[file_id].executable
2208
 
 
2209
 
        _is_executable_from_path_and_stat = \
2210
 
            _is_executable_from_path_and_stat_from_basis
2211
 
    else:
2212
 
        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:
2213
2240
            if not path:
2214
2241
                path = self.id2path(file_id)
2215
2242
            mode = os.lstat(self.abspath(path)).st_mode
2216
2243
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2217
2244
 
2218
 
        _is_executable_from_path_and_stat = \
2219
 
            _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)
2220
2250
 
2221
2251
    @needs_tree_write_lock
2222
2252
    def _add(self, files, ids, kinds):
2225
2255
        # should probably put it back with the previous ID.
2226
2256
        # the read and write working inventory should not occur in this
2227
2257
        # function - they should be part of lock_write and unlock.
2228
 
        inv = self.inventory
 
2258
        # FIXME: nested trees
 
2259
        inv = self.root_inventory
2229
2260
        for f, file_id, kind in zip(files, ids, kinds):
2230
2261
            if file_id is None:
2231
2262
                inv.add_path(f, kind=kind)
2272
2303
                parent_tree = self.branch.repository.revision_tree(parent_id)
2273
2304
            parent_tree.lock_read()
2274
2305
            try:
2275
 
                if file_id not in parent_tree:
 
2306
                try:
 
2307
                    kind = parent_tree.kind(file_id)
 
2308
                except errors.NoSuchId:
2276
2309
                    continue
2277
 
                ie = parent_tree.inventory[file_id]
2278
 
                if ie.kind != 'file':
 
2310
                if kind != 'file':
2279
2311
                    # Note: this is slightly unnecessary, because symlinks and
2280
2312
                    # directories have a "text" which is the empty text, and we
2281
2313
                    # know that won't mess up annotations. But it seems cleaner
2282
2314
                    continue
2283
 
                parent_text_key = (file_id, ie.revision)
 
2315
                parent_text_key = (
 
2316
                    file_id, parent_tree.get_file_revision(file_id))
2284
2317
                if parent_text_key not in maybe_file_parent_keys:
2285
2318
                    maybe_file_parent_keys.append(parent_text_key)
2286
2319
            finally:
2301
2334
                       for key, line in annotator.annotate_flat(this_key)]
2302
2335
        return annotations
2303
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
 
2304
2351
    @needs_read_lock
2305
2352
    def merge_modified(self):
2306
2353
        """Return a dictionary of files modified by a merge.
2326
2373
            for s in _mod_rio.RioReader(hashfile):
2327
2374
                # RioReader reads in Unicode, so convert file_ids back to utf8
2328
2375
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2329
 
                if file_id not in self.inventory:
 
2376
                if not self.has_id(file_id):
2330
2377
                    continue
2331
2378
                text_hash = s.get("hash")
2332
2379
                if text_hash == self.get_file_sha1(file_id):
2362
2409
        other_tree.lock_tree_write()
2363
2410
        try:
2364
2411
            new_parents = other_tree.get_parent_ids()
2365
 
            other_root = other_tree.inventory.root
 
2412
            other_root = other_tree.root_inventory.root
2366
2413
            other_root.parent_id = new_root_parent
2367
2414
            other_root.name = osutils.basename(other_tree_path)
2368
 
            self.inventory.add(other_root)
2369
 
            add_children(self.inventory, other_root)
2370
 
            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)
2371
2418
            # normally we don't want to fetch whole repositories, but i think
2372
2419
            # here we really do want to consolidate the whole thing.
2373
2420
            for parent_id in other_tree.get_parent_ids():
2411
2458
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2412
2459
        if tree_transport.base != branch_transport.base:
2413
2460
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2414
 
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
2415
 
                target_branch=new_branch)
 
2461
            tree_bzrdir.set_branch_reference(new_branch)
2416
2462
        else:
2417
2463
            tree_bzrdir = branch_bzrdir
2418
2464
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2419
2465
        wt.set_parent_ids(self.get_parent_ids())
2420
 
        my_inv = self.inventory
 
2466
        # FIXME: Support nested trees
 
2467
        my_inv = self.root_inventory
2421
2468
        child_inv = inventory.Inventory(root_id=None)
2422
2469
        new_root = my_inv[file_id]
2423
2470
        my_inv.remove_recursive_id(file_id)
2443
2490
        if not self.is_locked():
2444
2491
            raise errors.ObjectNotLocked(self)
2445
2492
 
2446
 
        inv = self.inventory
2447
2493
        if from_dir is None and include_root is True:
2448
 
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
 
2494
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
2449
2495
        # Convert these into local objects to save lookup times
2450
2496
        pathjoin = osutils.pathjoin
2451
2497
        file_kind = self._kind
2458
2504
 
2459
2505
        # directory file_id, relative path, absolute path, reverse sorted children
2460
2506
        if from_dir is not None:
2461
 
            from_dir_id = inv.path2id(from_dir)
 
2507
            inv, from_dir_id = self._path2inv_file_id(from_dir)
2462
2508
            if from_dir_id is None:
2463
2509
                # Directory not versioned
2464
2510
                return
2465
2511
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2466
2512
        else:
 
2513
            inv = self.root_inventory
2467
2514
            from_dir_id = inv.root.file_id
2468
2515
            from_dir_abspath = self.basedir
2469
2516
        children = os.listdir(from_dir_abspath)
2573
2620
        inventory. The second mode only updates the inventory without
2574
2621
        touching the file on the filesystem.
2575
2622
 
2576
 
        move uses the second mode if 'after == True' and the target is not
2577
 
        versioned but present in the working tree.
 
2623
        move uses the second mode if 'after == True' and the target is
 
2624
        either not versioned or newly added, and present in the working tree.
2578
2625
 
2579
2626
        move uses the second mode if 'after == False' and the source is
2580
2627
        versioned but no longer in the working tree, and the target is not
2592
2639
        rename_entries = []
2593
2640
        rename_tuples = []
2594
2641
 
 
2642
        invs_to_write = set()
 
2643
 
2595
2644
        # check for deprecated use of signature
2596
2645
        if to_dir is None:
2597
2646
            raise TypeError('You must supply a target directory')
2598
2647
        # check destination directory
2599
2648
        if isinstance(from_paths, basestring):
2600
2649
            raise ValueError()
2601
 
        inv = self.inventory
2602
2650
        to_abs = self.abspath(to_dir)
2603
2651
        if not isdir(to_abs):
2604
2652
            raise errors.BzrMoveFailedError('',to_dir,
2606
2654
        if not self.has_filename(to_dir):
2607
2655
            raise errors.BzrMoveFailedError('',to_dir,
2608
2656
                errors.NotInWorkingDirectory(to_dir))
2609
 
        to_dir_id = inv.path2id(to_dir)
 
2657
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2610
2658
        if to_dir_id is None:
2611
2659
            raise errors.BzrMoveFailedError('',to_dir,
2612
2660
                errors.NotVersionedError(path=to_dir))
2613
2661
 
2614
 
        to_dir_ie = inv[to_dir_id]
 
2662
        to_dir_ie = to_inv[to_dir_id]
2615
2663
        if to_dir_ie.kind != 'directory':
2616
2664
            raise errors.BzrMoveFailedError('',to_dir,
2617
2665
                errors.NotADirectory(to_abs))
2619
2667
        # create rename entries and tuples
2620
2668
        for from_rel in from_paths:
2621
2669
            from_tail = splitpath(from_rel)[-1]
2622
 
            from_id = inv.path2id(from_rel)
 
2670
            from_inv, from_id = self._path2inv_file_id(from_rel)
2623
2671
            if from_id is None:
2624
2672
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2625
2673
                    errors.NotVersionedError(path=from_rel))
2626
2674
 
2627
 
            from_entry = inv[from_id]
 
2675
            from_entry = from_inv[from_id]
2628
2676
            from_parent_id = from_entry.parent_id
2629
2677
            to_rel = pathjoin(to_dir, from_tail)
2630
2678
            rename_entry = InventoryWorkingTree._RenameEntry(
2649
2697
            # restore the inventory on error
2650
2698
            self._inventory_is_modified = original_modified
2651
2699
            raise
2652
 
        self._write_inventory(inv)
 
2700
        #FIXME: Should potentially also write the from_invs
 
2701
        self._write_inventory(to_inv)
2653
2702
        return rename_tuples
2654
2703
 
2655
2704
    @needs_tree_write_lock
2675
2724
 
2676
2725
        Everything else results in an error.
2677
2726
        """
2678
 
        inv = self.inventory
2679
2727
        rename_entries = []
2680
2728
 
2681
2729
        # create rename entries and tuples
2682
2730
        from_tail = splitpath(from_rel)[-1]
2683
 
        from_id = inv.path2id(from_rel)
 
2731
        from_inv, from_id = self._path2inv_file_id(from_rel)
2684
2732
        if from_id is None:
2685
2733
            # if file is missing in the inventory maybe it's in the basis_tree
2686
2734
            basis_tree = self.branch.basis_tree()
2689
2737
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2690
2738
                    errors.NotVersionedError(path=from_rel))
2691
2739
            # put entry back in the inventory so we can rename it
2692
 
            from_entry = basis_tree.inventory[from_id].copy()
2693
 
            inv.add(from_entry)
 
2740
            from_entry = basis_tree.root_inventory[from_id].copy()
 
2741
            from_inv.add(from_entry)
2694
2742
        else:
2695
 
            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]
2696
2745
        from_parent_id = from_entry.parent_id
2697
2746
        to_dir, to_tail = os.path.split(to_rel)
2698
 
        to_dir_id = inv.path2id(to_dir)
 
2747
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2699
2748
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2700
2749
                                     from_id=from_id,
2701
2750
                                     from_tail=from_tail,
2723
2772
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2724
2773
 
2725
2774
        self._move(rename_entries)
2726
 
        self._write_inventory(inv)
 
2775
        self._write_inventory(to_inv)
2727
2776
 
2728
2777
    class _RenameEntry(object):
2729
2778
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2730
 
                     to_rel, to_tail, to_parent_id, only_change_inv=False):
 
2779
                     to_rel, to_tail, to_parent_id, only_change_inv=False,
 
2780
                     change_id=False):
2731
2781
            self.from_rel = from_rel
2732
2782
            self.from_id = from_id
2733
2783
            self.from_tail = from_tail
2735
2785
            self.to_rel = to_rel
2736
2786
            self.to_tail = to_tail
2737
2787
            self.to_parent_id = to_parent_id
 
2788
            self.change_id = change_id
2738
2789
            self.only_change_inv = only_change_inv
2739
2790
 
2740
2791
    def _determine_mv_mode(self, rename_entries, after=False):
2743
2794
 
2744
2795
        Also does basic plausability tests.
2745
2796
        """
2746
 
        inv = self.inventory
 
2797
        # FIXME: Handling of nested trees
 
2798
        inv = self.root_inventory
2747
2799
 
2748
2800
        for rename_entry in rename_entries:
2749
2801
            # store to local variables for easier reference
2752
2804
            to_rel = rename_entry.to_rel
2753
2805
            to_id = inv.path2id(to_rel)
2754
2806
            only_change_inv = False
 
2807
            change_id = False
2755
2808
 
2756
2809
            # check the inventory for source and destination
2757
2810
            if from_id is None:
2758
2811
                raise errors.BzrMoveFailedError(from_rel,to_rel,
2759
2812
                    errors.NotVersionedError(path=from_rel))
2760
2813
            if to_id is not None:
2761
 
                raise errors.BzrMoveFailedError(from_rel,to_rel,
2762
 
                    errors.AlreadyVersionedError(path=to_rel))
 
2814
                allowed = False
 
2815
                # allow it with --after but only if dest is newly added
 
2816
                if after:
 
2817
                    basis = self.basis_tree()
 
2818
                    basis.lock_read()
 
2819
                    try:
 
2820
                        if not basis.has_id(to_id):
 
2821
                            rename_entry.change_id = True
 
2822
                            allowed = True
 
2823
                    finally:
 
2824
                        basis.unlock()
 
2825
                if not allowed:
 
2826
                    raise errors.BzrMoveFailedError(from_rel,to_rel,
 
2827
                        errors.AlreadyVersionedError(path=to_rel))
2763
2828
 
2764
2829
            # try to determine the mode for rename (only change inv or change
2765
2830
            # inv and file system)
2781
2846
                # something is wrong, so lets determine what exactly
2782
2847
                if not self.has_filename(from_rel) and \
2783
2848
                   not self.has_filename(to_rel):
2784
 
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
2785
 
                        errors.PathsDoNotExist(paths=(str(from_rel),
2786
 
                        str(to_rel))))
 
2849
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
 
2850
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2787
2851
                else:
2788
2852
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2789
2853
            rename_entry.only_change_inv = only_change_inv
2795
2859
        Depending on the value of the flag 'only_change_inv', the
2796
2860
        file will be moved on the file system or not.
2797
2861
        """
2798
 
        inv = self.inventory
2799
2862
        moved = []
2800
2863
 
2801
2864
        for entry in rename_entries:
2808
2871
 
2809
2872
    def _rollback_move(self, moved):
2810
2873
        """Try to rollback a previous move in case of an filesystem error."""
2811
 
        inv = self.inventory
2812
2874
        for entry in moved:
2813
2875
            try:
2814
2876
                self._move_entry(WorkingTree._RenameEntry(
2823
2885
                        " Error message is: %s" % e)
2824
2886
 
2825
2887
    def _move_entry(self, entry):
2826
 
        inv = self.inventory
 
2888
        inv = self.root_inventory
2827
2889
        from_rel_abs = self.abspath(entry.from_rel)
2828
2890
        to_rel_abs = self.abspath(entry.to_rel)
2829
2891
        if from_rel_abs == to_rel_abs:
2836
2898
            except OSError, e:
2837
2899
                raise errors.BzrMoveFailedError(entry.from_rel,
2838
2900
                    entry.to_rel, e[1])
 
2901
        if entry.change_id:
 
2902
            to_id = inv.path2id(entry.to_rel)
 
2903
            inv.remove_recursive_id(to_id)
2839
2904
        inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2840
2905
 
2841
2906
    @needs_tree_write_lock
2849
2914
        :raises: NoSuchId if any fileid is not currently versioned.
2850
2915
        """
2851
2916
        for file_id in file_ids:
2852
 
            if file_id not in self._inventory:
 
2917
            if not self._inventory.has_id(file_id):
2853
2918
                raise errors.NoSuchId(self, file_id)
2854
2919
        for file_id in file_ids:
2855
2920
            if self._inventory.has_id(file_id):
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."""
3010
3087
    missing_parent_conflicts = False
3011
3088
    """If this format supports missing parent conflicts."""
3012
3089
 
3013
 
    @classmethod
3014
 
    def find_format_string(klass, a_bzrdir):
3015
 
        """Return format name for the working tree object in a_bzrdir."""
3016
 
        try:
3017
 
            transport = a_bzrdir.get_workingtree_transport(None)
3018
 
            return transport.get_bytes("format")
3019
 
        except errors.NoSuchFile:
3020
 
            raise errors.NoWorkingTree(base=transport.base)
3021
 
 
3022
 
    @classmethod
3023
 
    def find_format(klass, a_bzrdir):
3024
 
        """Return the format for the working tree object in a_bzrdir."""
3025
 
        try:
3026
 
            format_string = klass.find_format_string(a_bzrdir)
3027
 
            return format_registry.get(format_string)
3028
 
        except KeyError:
3029
 
            raise errors.UnknownFormatError(format=format_string,
3030
 
                                            kind="working tree")
3031
 
 
3032
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
3090
    supports_versioned_directories = None
 
3091
 
 
3092
    def initialize(self, controldir, revision_id=None, from_branch=None,
3033
3093
                   accelerator_tree=None, hardlink=False):
3034
 
        """Initialize a new working tree in a_bzrdir.
 
3094
        """Initialize a new working tree in controldir.
3035
3095
 
3036
 
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
3096
        :param controldir: ControlDir to initialize the working tree in.
3037
3097
        :param revision_id: allows creating a working tree at a different
3038
3098
            revision than the branch is at.
3039
3099
        :param from_branch: Branch to checkout
3052
3112
    def __ne__(self, other):
3053
3113
        return not (self == other)
3054
3114
 
3055
 
    @classmethod
3056
 
    @symbol_versioning.deprecated_method(
3057
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3058
 
    def get_default_format(klass):
3059
 
        """Return the current default format."""
3060
 
        return format_registry.get_default()
3061
 
 
3062
 
    def get_format_string(self):
3063
 
        """Return the ASCII format string that identifies this format."""
3064
 
        raise NotImplementedError(self.get_format_string)
3065
 
 
3066
3115
    def get_format_description(self):
3067
3116
        """Return the short description for this format."""
3068
3117
        raise NotImplementedError(self.get_format_description)
3084
3133
        """True if this format supports stored views."""
3085
3134
        return False
3086
3135
 
3087
 
    @classmethod
3088
 
    @symbol_versioning.deprecated_method(
3089
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3090
 
    def register_format(klass, format):
3091
 
        format_registry.register(format)
3092
 
 
3093
 
    @classmethod
3094
 
    @symbol_versioning.deprecated_method(
3095
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3096
 
    def register_extra_format(klass, format):
3097
 
        format_registry.register_extra(format)
3098
 
 
3099
 
    @classmethod
3100
 
    @symbol_versioning.deprecated_method(
3101
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3102
 
    def unregister_extra_format(klass, format):
3103
 
        format_registry.unregister_extra(format)
3104
 
 
3105
 
    @classmethod
3106
 
    @symbol_versioning.deprecated_method(
3107
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3108
 
    def get_formats(klass):
3109
 
        return format_registry._get_all()
3110
 
 
3111
 
    @classmethod
3112
 
    @symbol_versioning.deprecated_method(
3113
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3114
 
    def set_default_format(klass, format):
3115
 
        format_registry.set_default(format)
3116
 
 
3117
 
    @classmethod
3118
 
    @symbol_versioning.deprecated_method(
3119
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3120
 
    def unregister_format(klass, format):
3121
 
        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)
3122
3215
 
3123
3216
 
3124
3217
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",