~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-05-11 15:04:23 UTC
  • mfrom: (5848.1.1 2.4-cython-first)
  • Revision ID: pqm@pqm.ubuntu.com-20110511150423-tpm1ablukqalkvim
(jameinel) Default to using Cython for compiling code,
 rather than Pyrex. (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
WorkingTree.open(dir).
30
30
"""
31
31
 
32
 
from __future__ import absolute_import
33
32
 
34
33
from cStringIO import StringIO
35
34
import os
47
46
 
48
47
from bzrlib import (
49
48
    branch,
 
49
    bzrdir,
50
50
    conflicts as _mod_conflicts,
51
51
    controldir,
52
52
    errors,
54
54
    generate_ids,
55
55
    globbing,
56
56
    graph as _mod_graph,
 
57
    hashcache,
57
58
    ignores,
58
59
    inventory,
59
60
    merge,
60
61
    revision as _mod_revision,
61
62
    revisiontree,
62
63
    rio as _mod_rio,
63
 
    shelf,
64
64
    transform,
65
65
    transport,
66
66
    ui,
70
70
    )
71
71
""")
72
72
 
73
 
# Explicitly import bzrlib.bzrdir so that the BzrProber
74
 
# is guaranteed to be registered.
75
 
from bzrlib import (
76
 
    bzrdir,
77
 
    symbol_versioning,
78
 
    )
79
 
 
 
73
from bzrlib import symbol_versioning
80
74
from bzrlib.decorators import needs_read_lock, needs_write_lock
81
 
from bzrlib.i18n import gettext
82
75
from bzrlib.lock import LogicalLockResult
83
76
import bzrlib.mutabletree
84
77
from bzrlib.mutabletree import needs_tree_write_lock
91
84
    realpath,
92
85
    safe_unicode,
93
86
    splitpath,
 
87
    supports_executable,
94
88
    )
95
89
from bzrlib.trace import mutter, note
96
90
from bzrlib.revision import CURRENT_REVISION
178
172
 
179
173
    def __init__(self, basedir='.',
180
174
                 branch=DEPRECATED_PARAMETER,
 
175
                 _control_files=None,
181
176
                 _internal=False,
182
 
                 _transport=None,
183
177
                 _format=None,
184
178
                 _bzrdir=None):
185
179
        """Construct a WorkingTree instance. This is not a public API.
198
192
        else:
199
193
            self._branch = self.bzrdir.open_branch()
200
194
        self.basedir = realpath(basedir)
201
 
        self._transport = _transport
 
195
        self._control_files = _control_files
 
196
        self._transport = self._control_files._transport
 
197
        # update the whole cache up front and write to disk if anything changed;
 
198
        # in the future we might want to do this more selectively
 
199
        # two possible ways offer themselves : in self._unlock, write the cache
 
200
        # if needed, or, when the cache sees a change, append it to the hash
 
201
        # cache file, and have the parser take the most recent entry for a
 
202
        # given path only.
 
203
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
204
        cache_filename = wt_trans.local_abspath('stat-cache')
 
205
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
 
206
            self.bzrdir._get_file_mode(),
 
207
            self._content_filter_stack_provider())
 
208
        hc = self._hashcache
 
209
        hc.read()
 
210
        # is this scan needed ? it makes things kinda slow.
 
211
        #hc.scan()
 
212
 
 
213
        if hc.needs_write:
 
214
            mutter("write hc")
 
215
            hc.write()
 
216
 
 
217
        self._detect_case_handling()
202
218
        self._rules_searcher = None
203
219
        self.views = self._make_views()
204
220
 
214
230
        """True if filename is the name of a control file in this tree.
215
231
 
216
232
        :param filename: A filename within the tree. This is a relative path
217
 
            from the root of this tree.
 
233
        from the root of this tree.
218
234
 
219
235
        This is true IF and ONLY IF the filename is part of the meta data
220
236
        that bzr controls in this tree. I.E. a random .bzr directory placed
222
238
        """
223
239
        return self.bzrdir.is_control_filename(filename)
224
240
 
 
241
    def _detect_case_handling(self):
 
242
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
243
        try:
 
244
            wt_trans.stat(self._format.case_sensitive_filename)
 
245
        except errors.NoSuchFile:
 
246
            self.case_sensitive = True
 
247
        else:
 
248
            self.case_sensitive = False
 
249
 
 
250
        self._setup_directory_is_tree_reference()
 
251
 
225
252
    branch = property(
226
253
        fget=lambda self: self._branch,
227
254
        doc="""The branch this WorkingTree is connected to.
230
257
            the working tree has been constructed from.
231
258
            """)
232
259
 
233
 
    def has_versioned_directories(self):
234
 
        """See `Tree.has_versioned_directories`."""
235
 
        return self._format.supports_versioned_directories
236
 
 
237
 
    def _supports_executable(self):
238
 
        if sys.platform == 'win32':
239
 
            return False
240
 
        # FIXME: Ideally this should check the file system
241
 
        return True
242
 
 
243
260
    def break_lock(self):
244
261
        """Break a lock if one is present from another instance.
245
262
 
248
265
 
249
266
        This will probe the repository for its lock as well.
250
267
        """
251
 
        raise NotImplementedError(self.break_lock)
 
268
        self._control_files.break_lock()
 
269
        self.branch.break_lock()
 
270
 
 
271
    def _get_check_refs(self):
 
272
        """Return the references needed to perform a check of this tree.
 
273
        
 
274
        The default implementation returns no refs, and is only suitable for
 
275
        trees that have no local caching and can commit on ghosts at any time.
 
276
 
 
277
        :seealso: bzrlib.check for details about check_refs.
 
278
        """
 
279
        return []
252
280
 
253
281
    def requires_rich_root(self):
254
282
        return self._format.requires_rich_root
262
290
    def supports_views(self):
263
291
        return self.views.supports_views()
264
292
 
265
 
    def get_config_stack(self):
266
 
        """Retrieve the config stack for this tree.
267
 
 
268
 
        :return: A ``bzrlib.config.Stack``
269
 
        """
270
 
        # For the moment, just provide the branch config stack.
271
 
        return self.branch.get_config_stack()
272
 
 
273
293
    @staticmethod
274
294
    def open(path=None, _unsupported=False):
275
295
        """Open an existing working tree at path.
277
297
        """
278
298
        if path is None:
279
299
            path = osutils.getcwd()
280
 
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
281
 
        return control.open_workingtree(unsupported=_unsupported)
 
300
        control = bzrdir.BzrDir.open(path, _unsupported)
 
301
        return control.open_workingtree(_unsupported)
282
302
 
283
303
    @staticmethod
284
304
    def open_containing(path=None):
295
315
        """
296
316
        if path is None:
297
317
            path = osutils.getcwd()
298
 
        control, relpath = controldir.ControlDir.open_containing(path)
 
318
        control, relpath = bzrdir.BzrDir.open_containing(path)
299
319
        return control.open_workingtree(), relpath
300
320
 
301
321
    @staticmethod
321
341
                if view_files:
322
342
                    file_list = view_files
323
343
                    view_str = views.view_display_str(view_files)
324
 
                    note(gettext("Ignoring files outside view. View is %s") % view_str)
 
344
                    note("Ignoring files outside view. View is %s" % view_str)
325
345
            return tree, file_list
326
346
        if default_directory == u'.':
327
347
            seed = file_list[0]
384
404
            else:
385
405
                return True, tree
386
406
        t = transport.get_transport(location)
387
 
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
 
407
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
388
408
                                              list_current=list_current)
389
409
        return [tr for tr in iterator if tr is not None]
390
410
 
 
411
    def all_file_ids(self):
 
412
        """See Tree.iter_all_file_ids"""
 
413
        raise NotImplementedError(self.all_file_ids)
 
414
 
391
415
    def __repr__(self):
392
416
        return "<%s of %s>" % (self.__class__.__name__,
393
417
                               getattr(self, 'basedir', None))
482
506
        finally:
483
507
            file.close()
484
508
 
 
509
    def _get_ancestors(self, default_revision):
 
510
        ancestors = set([default_revision])
 
511
        for parent_id in self.get_parent_ids():
 
512
            ancestors.update(self.branch.repository.get_ancestry(
 
513
                             parent_id, topo_sorted=False))
 
514
        return ancestors
 
515
 
485
516
    def get_parent_ids(self):
486
517
        """See Tree.get_parent_ids.
487
518
 
508
539
        raise NotImplementedError(self.get_root_id)
509
540
 
510
541
    @needs_read_lock
511
 
    def clone(self, to_controldir, revision_id=None):
 
542
    def clone(self, to_bzrdir, revision_id=None):
512
543
        """Duplicate this working tree into to_bzr, including all state.
513
544
 
514
545
        Specifically modified files are kept as modified, but
515
546
        ignored and unknown files are discarded.
516
547
 
517
 
        If you want to make a new line of development, see ControlDir.sprout()
 
548
        If you want to make a new line of development, see bzrdir.sprout()
518
549
 
519
550
        revision
520
551
            If not None, the cloned tree will have its last revision set to
522
553
            and this one merged in.
523
554
        """
524
555
        # assumes the target bzr dir format is compatible.
525
 
        result = to_controldir.create_workingtree()
 
556
        result = to_bzrdir.create_workingtree()
526
557
        self.copy_content_into(result, revision_id)
527
558
        return result
528
559
 
535
566
        else:
536
567
            # TODO now merge from tree.last_revision to revision (to preserve
537
568
            # user local changes)
538
 
            try:
539
 
                other_tree = self.revision_tree(revision_id)
540
 
            except errors.NoSuchRevision:
541
 
                other_tree = self.branch.repository.revision_tree(revision_id)
542
 
 
543
 
            merge.transform_tree(tree, other_tree)
544
 
            if revision_id == _mod_revision.NULL_REVISION:
545
 
                new_parents = []
546
 
            else:
547
 
                new_parents = [revision_id]
548
 
            tree.set_parent_ids(new_parents)
 
569
            merge.transform_tree(tree, self)
 
570
            tree.set_parent_ids([revision_id])
549
571
 
550
572
    def id2abspath(self, file_id):
551
573
        return self.abspath(self.id2path(file_id))
590
612
            else:
591
613
                return None
592
614
 
 
615
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
616
        # FIXME: Shouldn't this be in Tree?
 
617
        raise NotImplementedError(self.get_file_sha1)
 
618
 
593
619
    @needs_tree_write_lock
594
620
    def _gather_kinds(self, files, kinds):
595
621
        """See MutableTree._gather_kinds."""
610
636
        and setting the list to its value plus revision_id.
611
637
 
612
638
        :param revision_id: The revision id to add to the parent list. It may
613
 
            be a ghost revision as long as its not the first parent to be
614
 
            added, or the allow_leftmost_as_ghost parameter is set True.
 
639
        be a ghost revision as long as its not the first parent to be added,
 
640
        or the allow_leftmost_as_ghost parameter is set True.
615
641
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
616
642
        """
617
643
        parents = self.get_parent_ids() + [revision_id]
760
786
 
761
787
    @needs_tree_write_lock
762
788
    def set_merge_modified(self, modified_hashes):
763
 
        """Set the merge modified hashes."""
764
 
        raise NotImplementedError(self.set_merge_modified)
 
789
        def iter_stanzas():
 
790
            for file_id, hash in modified_hashes.iteritems():
 
791
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
 
792
                    hash=hash)
 
793
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
765
794
 
766
795
    def _sha_from_stat(self, path, stat_result):
767
796
        """Get a sha digest from the tree's stat cache.
773
802
        """
774
803
        return None
775
804
 
 
805
    def _put_rio(self, filename, stanzas, header):
 
806
        self._must_be_locked()
 
807
        my_file = _mod_rio.rio_file(stanzas, header)
 
808
        self._transport.put_file(filename, my_file,
 
809
            mode=self.bzrdir._get_file_mode())
 
810
 
776
811
    @needs_write_lock # because merge pulls data into the branch.
777
812
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
778
813
                          merge_type=None, force=False):
838
873
        self.add(path, file_id, 'directory')
839
874
        return file_id
840
875
 
841
 
    def get_symlink_target(self, file_id, path=None):
842
 
        if path is not None:
843
 
            abspath = self.abspath(path)
844
 
        else:
845
 
            abspath = self.id2abspath(file_id)
 
876
    def get_symlink_target(self, file_id):
 
877
        abspath = self.id2abspath(file_id)
846
878
        target = osutils.readlink(abspath)
847
879
        return target
848
880
 
948
980
        file and change the file_id. That is the normal mode. Second, it can
949
981
        only change the file_id without touching any physical file.
950
982
 
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.
 
983
        rename_one uses the second mode if 'after == True' and 'to_rel' is not
 
984
        versioned but present in the working tree.
953
985
 
954
986
        rename_one uses the second mode if 'after == False' and 'from_rel' is
955
987
        versioned but no longer in the working tree, and 'to_rel' is not
1018
1050
                                show_base=show_base)
1019
1051
                    basis_root_id = basis_tree.get_root_id()
1020
1052
                    new_root_id = new_basis_tree.get_root_id()
1021
 
                    if new_root_id is not None and basis_root_id != new_root_id:
 
1053
                    if basis_root_id != new_root_id:
1022
1054
                        self.set_root_id(new_root_id)
1023
1055
                finally:
1024
1056
                    basis_tree.unlock()
1025
1057
                # TODO - dedup parents list with things merged by pull ?
1026
1058
                # reuse the revisiontree we merged against to set the new
1027
1059
                # tree data.
1028
 
                parent_trees = []
1029
 
                if self.branch.last_revision() != _mod_revision.NULL_REVISION:
1030
 
                    parent_trees.append(
1031
 
                        (self.branch.last_revision(), new_basis_tree))
 
1060
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1032
1061
                # we have to pull the merge trees out again, because
1033
1062
                # merge_inner has set the ids. - this corner is not yet
1034
1063
                # layered well enough to prevent double handling.
1051
1080
            stream.write(bytes)
1052
1081
        finally:
1053
1082
            stream.close()
 
1083
        # TODO: update the hashcache here ?
1054
1084
 
1055
1085
    def extras(self):
1056
1086
        """Yield all unversioned files in this WorkingTree.
1133
1163
        else:
1134
1164
            mode = stat_value.st_mode
1135
1165
            kind = osutils.file_kind_from_stat_mode(mode)
1136
 
            if not self._supports_executable():
 
1166
            if not supports_executable():
1137
1167
                executable = entry is not None and entry.executable
1138
1168
            else:
1139
1169
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1158
1188
        return _mod_revision.ensure_null(self.branch.last_revision())
1159
1189
 
1160
1190
    def is_locked(self):
1161
 
        """Check if this tree is locked."""
1162
 
        raise NotImplementedError(self.is_locked)
 
1191
        return self._control_files.is_locked()
 
1192
 
 
1193
    def _must_be_locked(self):
 
1194
        if not self.is_locked():
 
1195
            raise errors.ObjectNotLocked(self)
1163
1196
 
1164
1197
    def lock_read(self):
1165
1198
        """Lock the tree for reading.
1168
1201
 
1169
1202
        :return: A bzrlib.lock.LogicalLockResult.
1170
1203
        """
1171
 
        raise NotImplementedError(self.lock_read)
 
1204
        if not self.is_locked():
 
1205
            self._reset_data()
 
1206
        self.branch.lock_read()
 
1207
        try:
 
1208
            self._control_files.lock_read()
 
1209
            return LogicalLockResult(self.unlock)
 
1210
        except:
 
1211
            self.branch.unlock()
 
1212
            raise
1172
1213
 
1173
1214
    def lock_tree_write(self):
1174
1215
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1175
1216
 
1176
1217
        :return: A bzrlib.lock.LogicalLockResult.
1177
1218
        """
1178
 
        raise NotImplementedError(self.lock_tree_write)
 
1219
        if not self.is_locked():
 
1220
            self._reset_data()
 
1221
        self.branch.lock_read()
 
1222
        try:
 
1223
            self._control_files.lock_write()
 
1224
            return LogicalLockResult(self.unlock)
 
1225
        except:
 
1226
            self.branch.unlock()
 
1227
            raise
1179
1228
 
1180
1229
    def lock_write(self):
1181
1230
        """See MutableTree.lock_write, and WorkingTree.unlock.
1182
1231
 
1183
1232
        :return: A bzrlib.lock.LogicalLockResult.
1184
1233
        """
1185
 
        raise NotImplementedError(self.lock_write)
 
1234
        if not self.is_locked():
 
1235
            self._reset_data()
 
1236
        self.branch.lock_write()
 
1237
        try:
 
1238
            self._control_files.lock_write()
 
1239
            return LogicalLockResult(self.unlock)
 
1240
        except:
 
1241
            self.branch.unlock()
 
1242
            raise
1186
1243
 
1187
1244
    def get_physical_lock_status(self):
1188
 
        raise NotImplementedError(self.get_physical_lock_status)
 
1245
        return self._control_files.get_physical_lock_status()
 
1246
 
 
1247
    def _reset_data(self):
 
1248
        """Reset transient data that cannot be revalidated."""
 
1249
        raise NotImplementedError(self._reset_data)
1189
1250
 
1190
1251
    def set_last_revision(self, new_revision):
1191
1252
        """Change the last revision in the working tree."""
1331
1392
    def revert(self, filenames=None, old_tree=None, backups=True,
1332
1393
               pb=None, report_changes=False):
1333
1394
        from bzrlib.conflicts import resolve
 
1395
        if filenames == []:
 
1396
            filenames = None
 
1397
            symbol_versioning.warn('Using [] to revert all files is deprecated'
 
1398
                ' as of bzr 0.91.  Please use None (the default) instead.',
 
1399
                DeprecationWarning, stacklevel=2)
1334
1400
        if old_tree is None:
1335
1401
            basis_tree = self.basis_tree()
1336
1402
            basis_tree.lock_read()
1357
1423
                basis_tree.unlock()
1358
1424
        return conflicts
1359
1425
 
1360
 
    @needs_write_lock
1361
 
    def store_uncommitted(self):
1362
 
        """Store uncommitted changes from the tree in the branch."""
1363
 
        target_tree = self.basis_tree()
1364
 
        shelf_creator = shelf.ShelfCreator(self, target_tree)
1365
 
        try:
1366
 
            if not shelf_creator.shelve_all():
1367
 
                return
1368
 
            self.branch.store_uncommitted(shelf_creator)
1369
 
            shelf_creator.transform()
1370
 
        finally:
1371
 
            shelf_creator.finalize()
1372
 
        note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1373
 
 
1374
 
    @needs_write_lock
1375
 
    def restore_uncommitted(self):
1376
 
        """Restore uncommitted changes from the branch into the tree."""
1377
 
        unshelver = self.branch.get_unshelver(self)
1378
 
        if unshelver is None:
1379
 
            return
1380
 
        try:
1381
 
            merger = unshelver.make_merger()
1382
 
            merger.ignore_zero = True
1383
 
            merger.do_merge()
1384
 
            self.branch.store_uncommitted(None)
1385
 
        finally:
1386
 
            unshelver.finalize()
1387
 
 
1388
1426
    def revision_tree(self, revision_id):
1389
1427
        """See Tree.revision_tree.
1390
1428
 
1447
1485
        - Restore the wt.basis->wt.state changes.
1448
1486
 
1449
1487
        There isn't a single operation at the moment to do that, so we:
1450
 
 
1451
1488
        - Merge current state -> basis tree of the master w.r.t. the old tree
1452
1489
          basis.
1453
1490
        - Do a 'normal' merge of the old branch basis if it is relevant.
1513
1550
                                             show_base=show_base)
1514
1551
            if nb_conflicts:
1515
1552
                self.add_parent_tree((old_tip, other_tree))
1516
 
                note(gettext('Rerun update after fixing the conflicts.'))
 
1553
                note('Rerun update after fixing the conflicts.')
1517
1554
                return nb_conflicts
1518
1555
 
1519
1556
        if last_rev != _mod_revision.ensure_null(revision):
1561
1598
            last_rev = parent_trees[0][0]
1562
1599
        return nb_conflicts
1563
1600
 
 
1601
    def _write_hashcache_if_dirty(self):
 
1602
        """Write out the hashcache if it is dirty."""
 
1603
        if self._hashcache.needs_write:
 
1604
            try:
 
1605
                self._hashcache.write()
 
1606
            except OSError, e:
 
1607
                if e.errno not in (errno.EPERM, errno.EACCES):
 
1608
                    raise
 
1609
                # TODO: jam 20061219 Should this be a warning? A single line
 
1610
                #       warning might be sufficient to let the user know what
 
1611
                #       is going on.
 
1612
                mutter('Could not write hashcache for %s\nError: %s',
 
1613
                              self._hashcache.cache_file_name(), e)
 
1614
 
1564
1615
    def set_conflicts(self, arg):
1565
1616
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1566
1617
 
1694
1745
    def _walkdirs(self, prefix=""):
1695
1746
        """Walk the directories of this tree.
1696
1747
 
1697
 
        :param prefix: is used as the directrory to start with.
1698
 
        :returns: a generator which yields items in the form::
1699
 
 
1700
 
            ((curren_directory_path, fileid),
1701
 
             [(file1_path, file1_name, file1_kind, None, file1_id,
1702
 
               file1_kind), ... ])
 
1748
           :prefix: is used as the directrory to start with.
 
1749
           returns a generator which yields items in the form:
 
1750
                ((curren_directory_path, fileid),
 
1751
                 [(file1_path, file1_name, file1_kind, None, file1_id,
 
1752
                   file1_kind), ... ])
1703
1753
        """
1704
1754
        raise NotImplementedError(self._walkdirs)
1705
1755
 
1711
1761
        are considered 'resolved', because bzr always puts conflict markers
1712
1762
        into files that have text conflicts.  The corresponding .THIS .BASE and
1713
1763
        .OTHER files are deleted, as per 'resolve'.
1714
 
 
1715
1764
        :return: a tuple of ConflictLists: (un_resolved, resolved).
1716
1765
        """
1717
1766
        un_resolved = _mod_conflicts.ConflictList()
1736
1785
        self.set_conflicts(un_resolved)
1737
1786
        return un_resolved, resolved
1738
1787
 
 
1788
    @needs_read_lock
 
1789
    def _check(self, references):
 
1790
        """Check the tree for consistency.
 
1791
 
 
1792
        :param references: A dict with keys matching the items returned by
 
1793
            self._get_check_refs(), and values from looking those keys up in
 
1794
            the repository.
 
1795
        """
 
1796
        tree_basis = self.basis_tree()
 
1797
        tree_basis.lock_read()
 
1798
        try:
 
1799
            repo_basis = references[('trees', self.last_revision())]
 
1800
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
 
1801
                raise errors.BzrCheckError(
 
1802
                    "Mismatched basis inventory content.")
 
1803
            self._validate()
 
1804
        finally:
 
1805
            tree_basis.unlock()
 
1806
 
1739
1807
    def _validate(self):
1740
1808
        """Validate internal structures.
1741
1809
 
1747
1815
        """
1748
1816
        return
1749
1817
 
 
1818
    @needs_read_lock
1750
1819
    def check_state(self):
1751
1820
        """Check that the working state is/isn't valid."""
1752
 
        raise NotImplementedError(self.check_state)
 
1821
        check_refs = self._get_check_refs()
 
1822
        refs = {}
 
1823
        for ref in check_refs:
 
1824
            kind, value = ref
 
1825
            if kind == 'trees':
 
1826
                refs[ref] = self.branch.repository.revision_tree(value)
 
1827
        self._check(refs)
1753
1828
 
1754
1829
    def reset_state(self, revision_ids=None):
1755
1830
        """Reset the state of the working tree.
1795
1870
        :param branch: A branch to override probing for the branch.
1796
1871
        """
1797
1872
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1798
 
            branch=branch, _transport=_control_files._transport,
1799
 
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
1800
 
 
1801
 
        self._control_files = _control_files
1802
 
        self._detect_case_handling()
 
1873
            branch=branch, _control_files=_control_files, _internal=_internal,
 
1874
            _format=_format, _bzrdir=_bzrdir)
1803
1875
 
1804
1876
        if _inventory is None:
1805
1877
            # This will be acquired on lock_read() or lock_write()
1825
1897
        self._inventory = inv
1826
1898
        self._inventory_is_modified = dirty
1827
1899
 
1828
 
    def _detect_case_handling(self):
1829
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
1830
 
        try:
1831
 
            wt_trans.stat(self._format.case_sensitive_filename)
1832
 
        except errors.NoSuchFile:
1833
 
            self.case_sensitive = True
1834
 
        else:
1835
 
            self.case_sensitive = False
1836
 
 
1837
 
        self._setup_directory_is_tree_reference()
1838
 
 
1839
1900
    def _serialize(self, inventory, out_file):
1840
1901
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1841
1902
            working=True)
1843
1904
    def _deserialize(selt, in_file):
1844
1905
        return xml5.serializer_v5.read_inventory(in_file)
1845
1906
 
1846
 
    def break_lock(self):
1847
 
        """Break a lock if one is present from another instance.
1848
 
 
1849
 
        Uses the ui factory to ask for confirmation if the lock may be from
1850
 
        an active process.
1851
 
 
1852
 
        This will probe the repository for its lock as well.
1853
 
        """
1854
 
        self._control_files.break_lock()
1855
 
        self.branch.break_lock()
1856
 
 
1857
 
    def is_locked(self):
1858
 
        return self._control_files.is_locked()
1859
 
 
1860
 
    def _must_be_locked(self):
1861
 
        if not self.is_locked():
1862
 
            raise errors.ObjectNotLocked(self)
1863
 
 
1864
 
    def lock_read(self):
1865
 
        """Lock the tree for reading.
1866
 
 
1867
 
        This also locks the branch, and can be unlocked via self.unlock().
1868
 
 
1869
 
        :return: A bzrlib.lock.LogicalLockResult.
1870
 
        """
1871
 
        if not self.is_locked():
1872
 
            self._reset_data()
1873
 
        self.branch.lock_read()
1874
 
        try:
1875
 
            self._control_files.lock_read()
1876
 
            return LogicalLockResult(self.unlock)
1877
 
        except:
1878
 
            self.branch.unlock()
1879
 
            raise
1880
 
 
1881
 
    def lock_tree_write(self):
1882
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1883
 
 
1884
 
        :return: A bzrlib.lock.LogicalLockResult.
1885
 
        """
1886
 
        if not self.is_locked():
1887
 
            self._reset_data()
1888
 
        self.branch.lock_read()
1889
 
        try:
1890
 
            self._control_files.lock_write()
1891
 
            return LogicalLockResult(self.unlock)
1892
 
        except:
1893
 
            self.branch.unlock()
1894
 
            raise
1895
 
 
1896
 
    def lock_write(self):
1897
 
        """See MutableTree.lock_write, and WorkingTree.unlock.
1898
 
 
1899
 
        :return: A bzrlib.lock.LogicalLockResult.
1900
 
        """
1901
 
        if not self.is_locked():
1902
 
            self._reset_data()
1903
 
        self.branch.lock_write()
1904
 
        try:
1905
 
            self._control_files.lock_write()
1906
 
            return LogicalLockResult(self.unlock)
1907
 
        except:
1908
 
            self.branch.unlock()
1909
 
            raise
1910
 
 
1911
 
    def get_physical_lock_status(self):
1912
 
        return self._control_files.get_physical_lock_status()
1913
 
 
1914
1907
    @needs_tree_write_lock
1915
1908
    def _write_inventory(self, inv):
1916
1909
        """Write inventory as the current inventory."""
1984
1977
            if entry.parent_id == orig_root_id:
1985
1978
                entry.parent_id = inv.root.file_id
1986
1979
 
 
1980
    def all_file_ids(self):
 
1981
        """See Tree.iter_all_file_ids"""
 
1982
        return set(self.inventory)
 
1983
 
1987
1984
    @needs_tree_write_lock
1988
1985
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1989
1986
        """See MutableTree.set_parent_trees."""
2008
2005
                # parent tree from the repository.
2009
2006
                self._cache_basis_inventory(leftmost_parent_id)
2010
2007
            else:
2011
 
                inv = leftmost_parent_tree.root_inventory
 
2008
                inv = leftmost_parent_tree.inventory
2012
2009
                xml = self._create_basis_xml_from_inventory(
2013
2010
                                        leftmost_parent_id, inv)
2014
2011
                self._write_basis_inventory(xml)
2111
2108
 
2112
2109
    def has_id(self, file_id):
2113
2110
        # files that have been deleted are excluded
2114
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2115
 
        if not inv.has_id(inv_file_id):
 
2111
        inv = self.inventory
 
2112
        if not inv.has_id(file_id):
2116
2113
            return False
2117
 
        path = inv.id2path(inv_file_id)
 
2114
        path = inv.id2path(file_id)
2118
2115
        return osutils.lexists(self.abspath(path))
2119
2116
 
2120
2117
    def has_or_had_id(self, file_id):
2121
 
        if file_id == self.get_root_id():
 
2118
        if file_id == self.inventory.root.file_id:
2122
2119
            return True
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):
 
2120
        return self.inventory.has_id(file_id)
 
2121
 
 
2122
    __contains__ = has_id
 
2123
 
 
2124
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
2125
    def __iter__(self):
2127
2126
        """Iterate through file_ids for this tree.
2128
2127
 
2129
2128
        file_ids are in a WorkingTree if they are in the working inventory
2130
2129
        and the working file exists.
2131
2130
        """
2132
 
        ret = set()
2133
 
        for path, ie in self.iter_entries_by_dir():
2134
 
            ret.add(ie.file_id)
2135
 
        return ret
 
2131
        inv = self._inventory
 
2132
        for path, ie in inv.iter_entries():
 
2133
            if osutils.lexists(self.abspath(path)):
 
2134
                yield ie.file_id
2136
2135
 
2137
2136
    @needs_tree_write_lock
2138
2137
    def set_last_revision(self, new_revision):
2140
2139
        if self._change_last_revision(new_revision):
2141
2140
            self._cache_basis_inventory(new_revision)
2142
2141
 
2143
 
    def _get_check_refs(self):
2144
 
        """Return the references needed to perform a check of this tree.
2145
 
        
2146
 
        The default implementation returns no refs, and is only suitable for
2147
 
        trees that have no local caching and can commit on ghosts at any time.
2148
 
 
2149
 
        :seealso: bzrlib.check for details about check_refs.
2150
 
        """
2151
 
        return []
2152
 
 
2153
 
    @needs_read_lock
2154
 
    def _check(self, references):
2155
 
        """Check the tree for consistency.
2156
 
 
2157
 
        :param references: A dict with keys matching the items returned by
2158
 
            self._get_check_refs(), and values from looking those keys up in
2159
 
            the repository.
2160
 
        """
2161
 
        tree_basis = self.basis_tree()
2162
 
        tree_basis.lock_read()
2163
 
        try:
2164
 
            repo_basis = references[('trees', self.last_revision())]
2165
 
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2166
 
                raise errors.BzrCheckError(
2167
 
                    "Mismatched basis inventory content.")
2168
 
            self._validate()
2169
 
        finally:
2170
 
            tree_basis.unlock()
2171
 
 
2172
 
    @needs_read_lock
2173
 
    def check_state(self):
2174
 
        """Check that the working state is/isn't valid."""
2175
 
        check_refs = self._get_check_refs()
2176
 
        refs = {}
2177
 
        for ref in check_refs:
2178
 
            kind, value = ref
2179
 
            if kind == 'trees':
2180
 
                refs[ref] = self.branch.repository.revision_tree(value)
2181
 
        self._check(refs)
2182
 
 
2183
2142
    @needs_tree_write_lock
2184
2143
    def reset_state(self, revision_ids=None):
2185
2144
        """Reset the state of the working tree.
2194
2153
                _mod_revision.NULL_REVISION)
2195
2154
        else:
2196
2155
            rt = self.branch.repository.revision_tree(revision_ids[0])
2197
 
        self._write_inventory(rt.root_inventory)
 
2156
        self._write_inventory(rt.inventory)
2198
2157
        self.set_parent_ids(revision_ids)
2199
2158
 
2200
2159
    def flush(self):
2209
2168
            mode=self.bzrdir._get_file_mode())
2210
2169
        self._inventory_is_modified = False
2211
2170
 
 
2171
    @needs_read_lock
 
2172
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
2173
        if not path:
 
2174
            path = self._inventory.id2path(file_id)
 
2175
        return self._hashcache.get_sha1(path, stat_value)
 
2176
 
2212
2177
    def get_file_mtime(self, file_id, path=None):
2213
2178
        """See Tree.get_file_mtime."""
2214
2179
        if not path:
2215
 
            path = self.id2path(file_id)
2216
 
        try:
2217
 
            return os.lstat(self.abspath(path)).st_mtime
2218
 
        except OSError, e:
2219
 
            if e.errno == errno.ENOENT:
2220
 
                raise errors.FileTimestampUnavailable(path)
2221
 
            raise
 
2180
            path = self.inventory.id2path(file_id)
 
2181
        return os.lstat(self.abspath(path)).st_mtime
2222
2182
 
2223
2183
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2224
 
        inv, file_id = self._path2inv_file_id(path)
 
2184
        file_id = self.path2id(path)
2225
2185
        if file_id is None:
2226
2186
            # For unversioned files on win32, we just assume they are not
2227
2187
            # executable
2228
2188
            return False
2229
 
        return inv[file_id].executable
 
2189
        return self._inventory[file_id].executable
2230
2190
 
2231
2191
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2232
2192
        mode = stat_result.st_mode
2233
2193
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2234
2194
 
2235
 
    def is_executable(self, file_id, path=None):
2236
 
        if not self._supports_executable():
2237
 
            inv, inv_file_id = self._unpack_file_id(file_id)
2238
 
            return inv[inv_file_id].executable
2239
 
        else:
 
2195
    if not supports_executable():
 
2196
        def is_executable(self, file_id, path=None):
 
2197
            return self._inventory[file_id].executable
 
2198
 
 
2199
        _is_executable_from_path_and_stat = \
 
2200
            _is_executable_from_path_and_stat_from_basis
 
2201
    else:
 
2202
        def is_executable(self, file_id, path=None):
2240
2203
            if not path:
2241
2204
                path = self.id2path(file_id)
2242
2205
            mode = os.lstat(self.abspath(path)).st_mode
2243
2206
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2244
2207
 
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)
 
2208
        _is_executable_from_path_and_stat = \
 
2209
            _is_executable_from_path_and_stat_from_stat
2250
2210
 
2251
2211
    @needs_tree_write_lock
2252
2212
    def _add(self, files, ids, kinds):
2255
2215
        # should probably put it back with the previous ID.
2256
2216
        # the read and write working inventory should not occur in this
2257
2217
        # function - they should be part of lock_write and unlock.
2258
 
        # FIXME: nested trees
2259
 
        inv = self.root_inventory
 
2218
        inv = self.inventory
2260
2219
        for f, file_id, kind in zip(files, ids, kinds):
2261
2220
            if file_id is None:
2262
2221
                inv.add_path(f, kind=kind)
2303
2262
                parent_tree = self.branch.repository.revision_tree(parent_id)
2304
2263
            parent_tree.lock_read()
2305
2264
            try:
2306
 
                try:
2307
 
                    kind = parent_tree.kind(file_id)
2308
 
                except errors.NoSuchId:
 
2265
                if file_id not in parent_tree:
2309
2266
                    continue
2310
 
                if kind != 'file':
 
2267
                ie = parent_tree.inventory[file_id]
 
2268
                if ie.kind != 'file':
2311
2269
                    # Note: this is slightly unnecessary, because symlinks and
2312
2270
                    # directories have a "text" which is the empty text, and we
2313
2271
                    # know that won't mess up annotations. But it seems cleaner
2314
2272
                    continue
2315
 
                parent_text_key = (
2316
 
                    file_id, parent_tree.get_file_revision(file_id))
 
2273
                parent_text_key = (file_id, ie.revision)
2317
2274
                if parent_text_key not in maybe_file_parent_keys:
2318
2275
                    maybe_file_parent_keys.append(parent_text_key)
2319
2276
            finally:
2334
2291
                       for key, line in annotator.annotate_flat(this_key)]
2335
2292
        return annotations
2336
2293
 
2337
 
    def _put_rio(self, filename, stanzas, header):
2338
 
        self._must_be_locked()
2339
 
        my_file = _mod_rio.rio_file(stanzas, header)
2340
 
        self._transport.put_file(filename, my_file,
2341
 
            mode=self.bzrdir._get_file_mode())
2342
 
 
2343
 
    @needs_tree_write_lock
2344
 
    def set_merge_modified(self, modified_hashes):
2345
 
        def iter_stanzas():
2346
 
            for file_id, hash in modified_hashes.iteritems():
2347
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
2348
 
                    hash=hash)
2349
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
2350
 
 
2351
2294
    @needs_read_lock
2352
2295
    def merge_modified(self):
2353
2296
        """Return a dictionary of files modified by a merge.
2373
2316
            for s in _mod_rio.RioReader(hashfile):
2374
2317
                # RioReader reads in Unicode, so convert file_ids back to utf8
2375
2318
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2376
 
                if not self.has_id(file_id):
 
2319
                if file_id not in self.inventory:
2377
2320
                    continue
2378
2321
                text_hash = s.get("hash")
2379
2322
                if text_hash == self.get_file_sha1(file_id):
2409
2352
        other_tree.lock_tree_write()
2410
2353
        try:
2411
2354
            new_parents = other_tree.get_parent_ids()
2412
 
            other_root = other_tree.root_inventory.root
 
2355
            other_root = other_tree.inventory.root
2413
2356
            other_root.parent_id = new_root_parent
2414
2357
            other_root.name = osutils.basename(other_tree_path)
2415
 
            self.root_inventory.add(other_root)
2416
 
            add_children(self.root_inventory, other_root)
2417
 
            self._write_inventory(self.root_inventory)
 
2358
            self.inventory.add(other_root)
 
2359
            add_children(self.inventory, other_root)
 
2360
            self._write_inventory(self.inventory)
2418
2361
            # normally we don't want to fetch whole repositories, but i think
2419
2362
            # here we really do want to consolidate the whole thing.
2420
2363
            for parent_id in other_tree.get_parent_ids():
2458
2401
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2459
2402
        if tree_transport.base != branch_transport.base:
2460
2403
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2461
 
            tree_bzrdir.set_branch_reference(new_branch)
 
2404
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
 
2405
                target_branch=new_branch)
2462
2406
        else:
2463
2407
            tree_bzrdir = branch_bzrdir
2464
2408
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2465
2409
        wt.set_parent_ids(self.get_parent_ids())
2466
 
        # FIXME: Support nested trees
2467
 
        my_inv = self.root_inventory
 
2410
        my_inv = self.inventory
2468
2411
        child_inv = inventory.Inventory(root_id=None)
2469
2412
        new_root = my_inv[file_id]
2470
2413
        my_inv.remove_recursive_id(file_id)
2490
2433
        if not self.is_locked():
2491
2434
            raise errors.ObjectNotLocked(self)
2492
2435
 
 
2436
        inv = self.inventory
2493
2437
        if from_dir is None and include_root is True:
2494
 
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
 
2438
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2495
2439
        # Convert these into local objects to save lookup times
2496
2440
        pathjoin = osutils.pathjoin
2497
2441
        file_kind = self._kind
2504
2448
 
2505
2449
        # directory file_id, relative path, absolute path, reverse sorted children
2506
2450
        if from_dir is not None:
2507
 
            inv, from_dir_id = self._path2inv_file_id(from_dir)
 
2451
            from_dir_id = inv.path2id(from_dir)
2508
2452
            if from_dir_id is None:
2509
2453
                # Directory not versioned
2510
2454
                return
2511
2455
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2512
2456
        else:
2513
 
            inv = self.root_inventory
2514
2457
            from_dir_id = inv.root.file_id
2515
2458
            from_dir_abspath = self.basedir
2516
2459
        children = os.listdir(from_dir_abspath)
2620
2563
        inventory. The second mode only updates the inventory without
2621
2564
        touching the file on the filesystem.
2622
2565
 
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.
 
2566
        move uses the second mode if 'after == True' and the target is not
 
2567
        versioned but present in the working tree.
2625
2568
 
2626
2569
        move uses the second mode if 'after == False' and the source is
2627
2570
        versioned but no longer in the working tree, and the target is not
2639
2582
        rename_entries = []
2640
2583
        rename_tuples = []
2641
2584
 
2642
 
        invs_to_write = set()
2643
 
 
2644
2585
        # check for deprecated use of signature
2645
2586
        if to_dir is None:
2646
2587
            raise TypeError('You must supply a target directory')
2647
2588
        # check destination directory
2648
2589
        if isinstance(from_paths, basestring):
2649
2590
            raise ValueError()
 
2591
        inv = self.inventory
2650
2592
        to_abs = self.abspath(to_dir)
2651
2593
        if not isdir(to_abs):
2652
2594
            raise errors.BzrMoveFailedError('',to_dir,
2654
2596
        if not self.has_filename(to_dir):
2655
2597
            raise errors.BzrMoveFailedError('',to_dir,
2656
2598
                errors.NotInWorkingDirectory(to_dir))
2657
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2599
        to_dir_id = inv.path2id(to_dir)
2658
2600
        if to_dir_id is None:
2659
2601
            raise errors.BzrMoveFailedError('',to_dir,
2660
2602
                errors.NotVersionedError(path=to_dir))
2661
2603
 
2662
 
        to_dir_ie = to_inv[to_dir_id]
 
2604
        to_dir_ie = inv[to_dir_id]
2663
2605
        if to_dir_ie.kind != 'directory':
2664
2606
            raise errors.BzrMoveFailedError('',to_dir,
2665
2607
                errors.NotADirectory(to_abs))
2667
2609
        # create rename entries and tuples
2668
2610
        for from_rel in from_paths:
2669
2611
            from_tail = splitpath(from_rel)[-1]
2670
 
            from_inv, from_id = self._path2inv_file_id(from_rel)
 
2612
            from_id = inv.path2id(from_rel)
2671
2613
            if from_id is None:
2672
2614
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2673
2615
                    errors.NotVersionedError(path=from_rel))
2674
2616
 
2675
 
            from_entry = from_inv[from_id]
 
2617
            from_entry = inv[from_id]
2676
2618
            from_parent_id = from_entry.parent_id
2677
2619
            to_rel = pathjoin(to_dir, from_tail)
2678
2620
            rename_entry = InventoryWorkingTree._RenameEntry(
2697
2639
            # restore the inventory on error
2698
2640
            self._inventory_is_modified = original_modified
2699
2641
            raise
2700
 
        #FIXME: Should potentially also write the from_invs
2701
 
        self._write_inventory(to_inv)
 
2642
        self._write_inventory(inv)
2702
2643
        return rename_tuples
2703
2644
 
2704
2645
    @needs_tree_write_lock
2724
2665
 
2725
2666
        Everything else results in an error.
2726
2667
        """
 
2668
        inv = self.inventory
2727
2669
        rename_entries = []
2728
2670
 
2729
2671
        # create rename entries and tuples
2730
2672
        from_tail = splitpath(from_rel)[-1]
2731
 
        from_inv, from_id = self._path2inv_file_id(from_rel)
 
2673
        from_id = inv.path2id(from_rel)
2732
2674
        if from_id is None:
2733
2675
            # if file is missing in the inventory maybe it's in the basis_tree
2734
2676
            basis_tree = self.branch.basis_tree()
2737
2679
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2738
2680
                    errors.NotVersionedError(path=from_rel))
2739
2681
            # put entry back in the inventory so we can rename it
2740
 
            from_entry = basis_tree.root_inventory[from_id].copy()
2741
 
            from_inv.add(from_entry)
 
2682
            from_entry = basis_tree.inventory[from_id].copy()
 
2683
            inv.add(from_entry)
2742
2684
        else:
2743
 
            from_inv, from_inv_id = self._unpack_file_id(from_id)
2744
 
            from_entry = from_inv[from_inv_id]
 
2685
            from_entry = inv[from_id]
2745
2686
        from_parent_id = from_entry.parent_id
2746
2687
        to_dir, to_tail = os.path.split(to_rel)
2747
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2688
        to_dir_id = inv.path2id(to_dir)
2748
2689
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2749
2690
                                     from_id=from_id,
2750
2691
                                     from_tail=from_tail,
2772
2713
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2773
2714
 
2774
2715
        self._move(rename_entries)
2775
 
        self._write_inventory(to_inv)
 
2716
        self._write_inventory(inv)
2776
2717
 
2777
2718
    class _RenameEntry(object):
2778
2719
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2779
 
                     to_rel, to_tail, to_parent_id, only_change_inv=False,
2780
 
                     change_id=False):
 
2720
                     to_rel, to_tail, to_parent_id, only_change_inv=False):
2781
2721
            self.from_rel = from_rel
2782
2722
            self.from_id = from_id
2783
2723
            self.from_tail = from_tail
2785
2725
            self.to_rel = to_rel
2786
2726
            self.to_tail = to_tail
2787
2727
            self.to_parent_id = to_parent_id
2788
 
            self.change_id = change_id
2789
2728
            self.only_change_inv = only_change_inv
2790
2729
 
2791
2730
    def _determine_mv_mode(self, rename_entries, after=False):
2794
2733
 
2795
2734
        Also does basic plausability tests.
2796
2735
        """
2797
 
        # FIXME: Handling of nested trees
2798
 
        inv = self.root_inventory
 
2736
        inv = self.inventory
2799
2737
 
2800
2738
        for rename_entry in rename_entries:
2801
2739
            # store to local variables for easier reference
2804
2742
            to_rel = rename_entry.to_rel
2805
2743
            to_id = inv.path2id(to_rel)
2806
2744
            only_change_inv = False
2807
 
            change_id = False
2808
2745
 
2809
2746
            # check the inventory for source and destination
2810
2747
            if from_id is None:
2811
2748
                raise errors.BzrMoveFailedError(from_rel,to_rel,
2812
2749
                    errors.NotVersionedError(path=from_rel))
2813
2750
            if to_id is not None:
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))
 
2751
                raise errors.BzrMoveFailedError(from_rel,to_rel,
 
2752
                    errors.AlreadyVersionedError(path=to_rel))
2828
2753
 
2829
2754
            # try to determine the mode for rename (only change inv or change
2830
2755
            # inv and file system)
2846
2771
                # something is wrong, so lets determine what exactly
2847
2772
                if not self.has_filename(from_rel) and \
2848
2773
                   not self.has_filename(to_rel):
2849
 
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
2850
 
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
 
2774
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
 
2775
                        errors.PathsDoNotExist(paths=(str(from_rel),
 
2776
                        str(to_rel))))
2851
2777
                else:
2852
2778
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2853
2779
            rename_entry.only_change_inv = only_change_inv
2859
2785
        Depending on the value of the flag 'only_change_inv', the
2860
2786
        file will be moved on the file system or not.
2861
2787
        """
 
2788
        inv = self.inventory
2862
2789
        moved = []
2863
2790
 
2864
2791
        for entry in rename_entries:
2871
2798
 
2872
2799
    def _rollback_move(self, moved):
2873
2800
        """Try to rollback a previous move in case of an filesystem error."""
 
2801
        inv = self.inventory
2874
2802
        for entry in moved:
2875
2803
            try:
2876
2804
                self._move_entry(WorkingTree._RenameEntry(
2885
2813
                        " Error message is: %s" % e)
2886
2814
 
2887
2815
    def _move_entry(self, entry):
2888
 
        inv = self.root_inventory
 
2816
        inv = self.inventory
2889
2817
        from_rel_abs = self.abspath(entry.from_rel)
2890
2818
        to_rel_abs = self.abspath(entry.to_rel)
2891
2819
        if from_rel_abs == to_rel_abs:
2898
2826
            except OSError, e:
2899
2827
                raise errors.BzrMoveFailedError(entry.from_rel,
2900
2828
                    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)
2904
2829
        inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2905
2830
 
2906
2831
    @needs_tree_write_lock
2914
2839
        :raises: NoSuchId if any fileid is not currently versioned.
2915
2840
        """
2916
2841
        for file_id in file_ids:
2917
 
            if not self._inventory.has_id(file_id):
 
2842
            if file_id not in self._inventory:
2918
2843
                raise errors.NoSuchId(self, file_id)
2919
2844
        for file_id in file_ids:
2920
2845
            if self._inventory.has_id(file_id):
2932
2857
 
2933
2858
    def stored_kind(self, file_id):
2934
2859
        """See Tree.stored_kind"""
2935
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2936
 
        return inv[inv_file_id].kind
 
2860
        return self.inventory[file_id].kind
2937
2861
 
2938
2862
    def extras(self):
2939
2863
        """Yield all unversioned files in this WorkingTree.
2946
2870
        This is the same order used by 'osutils.walkdirs'.
2947
2871
        """
2948
2872
        ## TODO: Work from given directory downwards
2949
 
        for path, dir_entry in self.iter_entries_by_dir():
2950
 
            if dir_entry.kind != 'directory':
2951
 
                continue
 
2873
        for path, dir_entry in self.inventory.directories():
2952
2874
            # mutter("search for unknowns in %r", path)
2953
2875
            dirabs = self.abspath(path)
2954
2876
            if not isdir(dirabs):
2982
2904
    def _walkdirs(self, prefix=""):
2983
2905
        """Walk the directories of this tree.
2984
2906
 
2985
 
        :param prefix: is used as the directrory to start with.
2986
 
        :returns: a generator which yields items in the form::
2987
 
 
2988
 
            ((curren_directory_path, fileid),
2989
 
             [(file1_path, file1_name, file1_kind, None, file1_id,
2990
 
               file1_kind), ... ])
 
2907
           :prefix: is used as the directrory to start with.
 
2908
           returns a generator which yields items in the form:
 
2909
                ((curren_directory_path, fileid),
 
2910
                 [(file1_path, file1_name, file1_kind, None, file1_id,
 
2911
                   file1_kind), ... ])
2991
2912
        """
2992
2913
        _directory = 'directory'
2993
2914
        # get the root in the inventory
2994
 
        inv, top_id = self._path2inv_file_id(prefix)
 
2915
        inv = self.inventory
 
2916
        top_id = inv.path2id(prefix)
2995
2917
        if top_id is None:
2996
2918
            pending = []
2997
2919
        else:
3018
2940
                if dir[2] == _directory:
3019
2941
                    pending.append(dir)
3020
2942
 
3021
 
    @needs_write_lock
3022
 
    def update_feature_flags(self, updated_flags):
3023
 
        """Update the feature flags for this branch.
3024
 
 
3025
 
        :param updated_flags: Dictionary mapping feature names to necessities
3026
 
            A necessity can be None to indicate the feature should be removed
3027
 
        """
3028
 
        self._format._update_feature_flags(updated_flags)
3029
 
        self.control_transport.put_bytes('format', self._format.as_string())
3030
 
 
3031
2943
 
3032
2944
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
3033
2945
    """Registry for working tree formats."""
3087
2999
    missing_parent_conflicts = False
3088
3000
    """If this format supports missing parent conflicts."""
3089
3001
 
3090
 
    supports_versioned_directories = None
3091
 
 
3092
 
    def initialize(self, controldir, revision_id=None, from_branch=None,
 
3002
    @classmethod
 
3003
    def find_format_string(klass, a_bzrdir):
 
3004
        """Return format name for the working tree object in a_bzrdir."""
 
3005
        try:
 
3006
            transport = a_bzrdir.get_workingtree_transport(None)
 
3007
            return transport.get_bytes("format")
 
3008
        except errors.NoSuchFile:
 
3009
            raise errors.NoWorkingTree(base=transport.base)
 
3010
 
 
3011
    @classmethod
 
3012
    def find_format(klass, a_bzrdir):
 
3013
        """Return the format for the working tree object in a_bzrdir."""
 
3014
        try:
 
3015
            format_string = klass.find_format_string(a_bzrdir)
 
3016
            return format_registry.get(format_string)
 
3017
        except KeyError:
 
3018
            raise errors.UnknownFormatError(format=format_string,
 
3019
                                            kind="working tree")
 
3020
 
 
3021
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
3093
3022
                   accelerator_tree=None, hardlink=False):
3094
 
        """Initialize a new working tree in controldir.
 
3023
        """Initialize a new working tree in a_bzrdir.
3095
3024
 
3096
 
        :param controldir: ControlDir to initialize the working tree in.
 
3025
        :param a_bzrdir: BzrDir to initialize the working tree in.
3097
3026
        :param revision_id: allows creating a working tree at a different
3098
3027
            revision than the branch is at.
3099
3028
        :param from_branch: Branch to checkout
3112
3041
    def __ne__(self, other):
3113
3042
        return not (self == other)
3114
3043
 
 
3044
    @classmethod
 
3045
    @symbol_versioning.deprecated_method(
 
3046
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3047
    def get_default_format(klass):
 
3048
        """Return the current default format."""
 
3049
        return format_registry.get_default()
 
3050
 
 
3051
    def get_format_string(self):
 
3052
        """Return the ASCII format string that identifies this format."""
 
3053
        raise NotImplementedError(self.get_format_string)
 
3054
 
3115
3055
    def get_format_description(self):
3116
3056
        """Return the short description for this format."""
3117
3057
        raise NotImplementedError(self.get_format_description)
3133
3073
        """True if this format supports stored views."""
3134
3074
        return False
3135
3075
 
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)
 
3076
    @classmethod
 
3077
    @symbol_versioning.deprecated_method(
 
3078
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3079
    def register_format(klass, format):
 
3080
        format_registry.register(format)
 
3081
 
 
3082
    @classmethod
 
3083
    @symbol_versioning.deprecated_method(
 
3084
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3085
    def register_extra_format(klass, format):
 
3086
        format_registry.register_extra(format)
 
3087
 
 
3088
    @classmethod
 
3089
    @symbol_versioning.deprecated_method(
 
3090
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3091
    def unregister_extra_format(klass, format):
 
3092
        format_registry.unregister_extra(format)
 
3093
 
 
3094
    @classmethod
 
3095
    @symbol_versioning.deprecated_method(
 
3096
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3097
    def get_formats(klass):
 
3098
        return format_registry._get_all()
 
3099
 
 
3100
    @classmethod
 
3101
    @symbol_versioning.deprecated_method(
 
3102
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3103
    def set_default_format(klass, format):
 
3104
        format_registry.set_default(format)
 
3105
 
 
3106
    @classmethod
 
3107
    @symbol_versioning.deprecated_method(
 
3108
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3109
    def unregister_format(klass, format):
 
3110
        format_registry.remove(format)
3215
3111
 
3216
3112
 
3217
3113
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",