/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Ross Lagerwall
  • Date: 2012-02-24 20:25:48 UTC
  • mto: This revision was merged to the branch mainline in revision 6480.
  • Revision ID: rosslagerwall@gmail.com-20120224202548-yvz0hy5hv5i4ittc
Update docs & contrib to use new --listen & --port options to bzr serve.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
22
22
WorkingTree.open(dir).
23
23
"""
24
24
 
 
25
from __future__ import absolute_import
 
26
 
25
27
from cStringIO import StringIO
26
28
import os
27
29
import sys
31
33
import errno
32
34
import stat
33
35
 
34
 
import bzrlib
35
36
from bzrlib import (
36
37
    bzrdir,
37
38
    cache_utf8,
 
39
    config,
 
40
    conflicts as _mod_conflicts,
38
41
    debug,
39
42
    dirstate,
40
43
    errors,
 
44
    filters as _mod_filters,
41
45
    generate_ids,
42
46
    osutils,
43
47
    revision as _mod_revision,
46
50
    transform,
47
51
    views,
48
52
    )
49
 
import bzrlib.branch
50
 
import bzrlib.ui
51
53
""")
52
54
 
53
55
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
 
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
56
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
 
from bzrlib.mutabletree import needs_tree_write_lock
 
57
from bzrlib.lock import LogicalLockResult
 
58
from bzrlib.lockable_files import LockableFiles
 
59
from bzrlib.lockdir import LockDir
 
60
from bzrlib.mutabletree import (
 
61
    MutableTree,
 
62
    needs_tree_write_lock,
 
63
    )
57
64
from bzrlib.osutils import (
58
65
    file_kind,
59
66
    isdir,
61
68
    realpath,
62
69
    safe_unicode,
63
70
    )
64
 
from bzrlib.trace import mutter
 
71
from bzrlib.symbol_versioning import (
 
72
    deprecated_in,
 
73
    deprecated_method,
 
74
    )
65
75
from bzrlib.transport.local import LocalTransport
66
 
from bzrlib.tree import InterTree
67
 
from bzrlib.tree import Tree
68
 
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
69
 
 
70
 
 
71
 
class DirStateWorkingTree(WorkingTree3):
 
76
from bzrlib.tree import (
 
77
    InterTree,
 
78
    InventoryTree,
 
79
    )
 
80
from bzrlib.workingtree import (
 
81
    InventoryWorkingTree,
 
82
    WorkingTree,
 
83
    WorkingTreeFormatMetaDir,
 
84
    )
 
85
 
 
86
 
 
87
class DirStateWorkingTree(InventoryWorkingTree):
 
88
 
72
89
    def __init__(self, basedir,
73
90
                 branch,
74
91
                 _control_files=None,
84
101
        self._format = _format
85
102
        self.bzrdir = _bzrdir
86
103
        basedir = safe_unicode(basedir)
87
 
        mutter("opening working tree %r", basedir)
 
104
        trace.mutter("opening working tree %r", basedir)
88
105
        self._branch = branch
89
106
        self.basedir = realpath(basedir)
90
107
        # if branch is at our basedir and is a format 6 or less
124
141
            state.add(f, file_id, kind, None, '')
125
142
        self._make_dirty(reset_inventory=True)
126
143
 
 
144
    def _get_check_refs(self):
 
145
        """Return the references needed to perform a check of this tree."""
 
146
        return [('trees', self.last_revision())]
 
147
 
127
148
    def _make_dirty(self, reset_inventory):
128
149
        """Make the tree state dirty.
129
150
 
181
202
 
182
203
    def _comparison_data(self, entry, path):
183
204
        kind, executable, stat_value = \
184
 
            WorkingTree3._comparison_data(self, entry, path)
 
205
            WorkingTree._comparison_data(self, entry, path)
185
206
        # it looks like a plain directory, but it's really a reference -- see
186
207
        # also kind()
187
208
        if (self._repo_supports_tree_reference and kind == 'directory'
193
214
    def commit(self, message=None, revprops=None, *args, **kwargs):
194
215
        # mark the tree as dirty post commit - commit
195
216
        # can change the current versioned list by doing deletes.
196
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
217
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
197
218
        self._make_dirty(reset_inventory=True)
198
219
        return result
199
220
 
218
239
        local_path = self.bzrdir.get_workingtree_transport(None
219
240
            ).local_abspath('dirstate')
220
241
        self._dirstate = dirstate.DirState.on_file(local_path,
221
 
            self._sha1_provider())
 
242
            self._sha1_provider(), self._worth_saving_limit())
222
243
        return self._dirstate
223
244
 
224
245
    def _sha1_provider(self):
233
254
        else:
234
255
            return None
235
256
 
 
257
    def _worth_saving_limit(self):
 
258
        """How many hash changes are ok before we must save the dirstate.
 
259
 
 
260
        :return: an integer. -1 means never save.
 
261
        """
 
262
        conf = self.get_config_stack()
 
263
        return conf.get('bzr.workingtree.worth_saving_limit')
 
264
 
236
265
    def filter_unversioned_files(self, paths):
237
266
        """Filter out paths that are versioned.
238
267
 
368
397
        state = self.current_dirstate()
369
398
        if stat_value is None:
370
399
            try:
371
 
                stat_value = os.lstat(file_abspath)
 
400
                stat_value = osutils.lstat(file_abspath)
372
401
            except OSError, e:
373
402
                if e.errno == errno.ENOENT:
374
403
                    return None
389
418
                return link_or_sha1
390
419
        return None
391
420
 
392
 
    def _get_inventory(self):
 
421
    def _get_root_inventory(self):
393
422
        """Get the inventory for the tree. This is only valid within a lock."""
394
423
        if 'evil' in debug.debug_flags:
395
424
            trace.mutter_callsite(2,
400
429
        self._generate_inventory()
401
430
        return self._inventory
402
431
 
 
432
    @deprecated_method(deprecated_in((2, 5, 0)))
 
433
    def _get_inventory(self):
 
434
        return self.root_inventory
 
435
 
403
436
    inventory = property(_get_inventory,
404
437
                         doc="Inventory of this Tree")
405
438
 
 
439
    root_inventory = property(_get_root_inventory,
 
440
        "Root inventory of this tree")
 
441
 
406
442
    @needs_read_lock
407
443
    def get_parent_ids(self):
408
444
        """See Tree.get_parent_ids.
455
491
            return False # Missing entries are not executable
456
492
        return entry[1][0][3] # Executable?
457
493
 
458
 
    if not osutils.supports_executable():
459
 
        def is_executable(self, file_id, path=None):
460
 
            """Test if a file is executable or not.
 
494
    def is_executable(self, file_id, path=None):
 
495
        """Test if a file is executable or not.
461
496
 
462
 
            Note: The caller is expected to take a read-lock before calling this.
463
 
            """
 
497
        Note: The caller is expected to take a read-lock before calling this.
 
498
        """
 
499
        if not self._supports_executable():
464
500
            entry = self._get_entry(file_id=file_id, path=path)
465
501
            if entry == (None, None):
466
502
                return False
467
503
            return entry[1][0][3]
468
 
 
469
 
        _is_executable_from_path_and_stat = \
470
 
            _is_executable_from_path_and_stat_from_basis
471
 
    else:
472
 
        def is_executable(self, file_id, path=None):
473
 
            """Test if a file is executable or not.
474
 
 
475
 
            Note: The caller is expected to take a read-lock before calling this.
476
 
            """
 
504
        else:
477
505
            self._must_be_locked()
478
506
            if not path:
479
507
                path = self.id2path(file_id)
480
 
            mode = os.lstat(self.abspath(path)).st_mode
 
508
            mode = osutils.lstat(self.abspath(path)).st_mode
481
509
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
482
510
 
483
511
    def all_file_ids(self):
567
595
            return _mod_revision.NULL_REVISION
568
596
 
569
597
    def lock_read(self):
570
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
598
        """See Branch.lock_read, and WorkingTree.unlock.
 
599
 
 
600
        :return: A bzrlib.lock.LogicalLockResult.
 
601
        """
571
602
        self.branch.lock_read()
572
603
        try:
573
604
            self._control_files.lock_read()
586
617
        except:
587
618
            self.branch.unlock()
588
619
            raise
 
620
        return LogicalLockResult(self.unlock)
589
621
 
590
622
    def _lock_self_write(self):
591
623
        """This should be called after the branch is locked."""
606
638
        except:
607
639
            self.branch.unlock()
608
640
            raise
 
641
        return LogicalLockResult(self.unlock)
609
642
 
610
643
    def lock_tree_write(self):
611
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
644
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
645
 
 
646
        :return: A bzrlib.lock.LogicalLockResult.
 
647
        """
612
648
        self.branch.lock_read()
613
 
        self._lock_self_write()
 
649
        return self._lock_self_write()
614
650
 
615
651
    def lock_write(self):
616
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
652
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
653
 
 
654
        :return: A bzrlib.lock.LogicalLockResult.
 
655
        """
617
656
        self.branch.lock_write()
618
 
        self._lock_self_write()
 
657
        return self._lock_self_write()
619
658
 
620
659
    @needs_tree_write_lock
621
660
    def move(self, from_paths, to_dir, after=False):
652
691
 
653
692
        if self._inventory is not None:
654
693
            update_inventory = True
655
 
            inv = self.inventory
 
694
            inv = self.root_inventory
656
695
            to_dir_id = to_entry[0][2]
657
696
            to_dir_ie = inv[to_dir_id]
658
697
        else:
838
877
                rollback_rename()
839
878
                raise
840
879
            result.append((from_rel, to_rel))
841
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
880
            state._mark_modified()
842
881
            self._make_dirty(reset_inventory=False)
843
882
 
844
883
        return result
937
976
                    all_versioned = False
938
977
                    break
939
978
            if not all_versioned:
940
 
                raise errors.PathsNotVersionedError(paths)
 
979
                raise errors.PathsNotVersionedError(
 
980
                    [p.decode('utf-8') for p in paths])
941
981
        # -- remove redundancy in supplied paths to prevent over-scanning --
942
982
        search_paths = osutils.minimum_path_selection(paths)
943
983
        # sketch:
992
1032
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
993
1033
            for dir_name in split_paths:
994
1034
                if dir_name not in found_dir_names:
995
 
                    raise errors.PathsNotVersionedError(paths)
 
1035
                    raise errors.PathsNotVersionedError(
 
1036
                        [p.decode('utf-8') for p in paths])
996
1037
 
997
1038
        for dir_name_id, trees_info in found.iteritems():
998
1039
            for index in search_indexes:
1005
1046
 
1006
1047
        This is a meaningless operation for dirstate, but we obey it anyhow.
1007
1048
        """
1008
 
        return self.inventory
 
1049
        return self.root_inventory
1009
1050
 
1010
1051
    @needs_read_lock
1011
1052
    def revision_tree(self, revision_id):
1101
1142
                        _mod_revision.NULL_REVISION)))
1102
1143
                ghosts.append(rev_id)
1103
1144
            accepted_revisions.add(rev_id)
1104
 
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1145
        updated = False
 
1146
        if (len(real_trees) == 1
 
1147
            and not ghosts
 
1148
            and self.branch.repository._format.fast_deltas
 
1149
            and isinstance(real_trees[0][1],
 
1150
                revisiontree.InventoryRevisionTree)
 
1151
            and self.get_parent_ids()):
 
1152
            rev_id, rev_tree = real_trees[0]
 
1153
            basis_id = self.get_parent_ids()[0]
 
1154
            # There are times when basis_tree won't be in
 
1155
            # self.branch.repository, (switch, for example)
 
1156
            try:
 
1157
                basis_tree = self.branch.repository.revision_tree(basis_id)
 
1158
            except errors.NoSuchRevision:
 
1159
                # Fall back to the set_parent_trees(), since we can't use
 
1160
                # _make_delta if we can't get the RevisionTree
 
1161
                pass
 
1162
            else:
 
1163
                delta = rev_tree.root_inventory._make_delta(
 
1164
                    basis_tree.root_inventory)
 
1165
                dirstate.update_basis_by_delta(delta, rev_id)
 
1166
                updated = True
 
1167
        if not updated:
 
1168
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1105
1169
        self._make_dirty(reset_inventory=False)
1106
1170
 
1107
1171
    def _set_root_id(self, file_id):
1127
1191
 
1128
1192
    def unlock(self):
1129
1193
        """Unlock in format 4 trees needs to write the entire dirstate."""
1130
 
        # do non-implementation specific cleanup
1131
 
        self._cleanup()
1132
 
 
1133
1194
        if self._control_files._lock_count == 1:
 
1195
            # do non-implementation specific cleanup
 
1196
            self._cleanup()
 
1197
 
1134
1198
            # eventually we should do signature checking during read locks for
1135
1199
            # dirstate updates.
1136
1200
            if self._control_files._lock_mode == 'w':
1235
1299
        # have to change the legacy inventory too.
1236
1300
        if self._inventory is not None:
1237
1301
            for file_id in file_ids:
1238
 
                self._inventory.remove_recursive_id(file_id)
 
1302
                if self._inventory.has_id(file_id):
 
1303
                    self._inventory.remove_recursive_id(file_id)
1239
1304
 
1240
1305
    @needs_tree_write_lock
1241
1306
    def rename_one(self, from_rel, to_rel, after=False):
1242
1307
        """See WorkingTree.rename_one"""
1243
1308
        self.flush()
1244
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1309
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1245
1310
 
1246
1311
    @needs_tree_write_lock
1247
1312
    def apply_inventory_delta(self, changes):
1273
1338
        # being created.
1274
1339
        self._inventory = None
1275
1340
        # generate a delta,
1276
 
        delta = inv._make_delta(self.inventory)
 
1341
        delta = inv._make_delta(self.root_inventory)
1277
1342
        # and apply it.
1278
1343
        self.apply_inventory_delta(delta)
1279
1344
        if had_inventory:
1280
1345
            self._inventory = inv
1281
1346
        self.flush()
1282
1347
 
 
1348
    @needs_tree_write_lock
 
1349
    def reset_state(self, revision_ids=None):
 
1350
        """Reset the state of the working tree.
 
1351
 
 
1352
        This does a hard-reset to a last-known-good state. This is a way to
 
1353
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
 
1354
        """
 
1355
        if revision_ids is None:
 
1356
            revision_ids = self.get_parent_ids()
 
1357
        if not revision_ids:
 
1358
            base_tree = self.branch.repository.revision_tree(
 
1359
                _mod_revision.NULL_REVISION)
 
1360
            trees = []
 
1361
        else:
 
1362
            trees = zip(revision_ids,
 
1363
                        self.branch.repository.revision_trees(revision_ids))
 
1364
            base_tree = trees[0][1]
 
1365
        state = self.current_dirstate()
 
1366
        # We don't support ghosts yet
 
1367
        state.set_state_from_scratch(base_tree.root_inventory, trees, [])
 
1368
 
1283
1369
 
1284
1370
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1285
1371
 
1290
1376
        """See dirstate.SHA1Provider.sha1()."""
1291
1377
        filters = self.tree._content_filter_stack(
1292
1378
            self.tree.relpath(osutils.safe_unicode(abspath)))
1293
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1379
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1294
1380
 
1295
1381
    def stat_and_sha1(self, abspath):
1296
1382
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1300
1386
        try:
1301
1387
            statvalue = os.fstat(file_obj.fileno())
1302
1388
            if filters:
1303
 
                file_obj = filtered_input_file(file_obj, filters)
 
1389
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1304
1390
            sha1 = osutils.size_sha_file(file_obj)[1]
1305
1391
        finally:
1306
1392
            file_obj.close()
1317
1403
    def _file_content_summary(self, path, stat_result):
1318
1404
        # This is to support the somewhat obsolete path_content_summary method
1319
1405
        # with content filtering: see
1320
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1406
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1321
1407
        #
1322
1408
        # If the dirstate cache is up to date and knows the hash and size,
1323
1409
        # return that.
1336
1422
class WorkingTree4(DirStateWorkingTree):
1337
1423
    """This is the Format 4 working tree.
1338
1424
 
1339
 
    This differs from WorkingTree3 by:
 
1425
    This differs from WorkingTree by:
1340
1426
     - Having a consolidated internal dirstate, stored in a
1341
1427
       randomly-accessible sorted file on disk.
1342
1428
     - Not having a regular inventory attribute.  One can be synthesized
1370
1456
        return views.PathBasedViews(self)
1371
1457
 
1372
1458
 
1373
 
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1459
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
 
1460
 
 
1461
    missing_parent_conflicts = True
 
1462
 
 
1463
    supports_versioned_directories = True
 
1464
 
 
1465
    _lock_class = LockDir
 
1466
    _lock_file_name = 'lock'
 
1467
 
 
1468
    def _open_control_files(self, a_bzrdir):
 
1469
        transport = a_bzrdir.get_workingtree_transport(None)
 
1470
        return LockableFiles(transport, self._lock_file_name,
 
1471
                             self._lock_class)
1374
1472
 
1375
1473
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1376
1474
                   accelerator_tree=None, hardlink=False):
1377
1475
        """See WorkingTreeFormat.initialize().
1378
1476
 
1379
1477
        :param revision_id: allows creating a working tree at a different
1380
 
        revision than the branch is at.
 
1478
            revision than the branch is at.
1381
1479
        :param accelerator_tree: A tree which can be used for retrieving file
1382
1480
            contents more quickly than the revision tree, i.e. a workingtree.
1383
1481
            The revision tree will be used for cases where accelerator_tree's
1394
1492
        control_files = self._open_control_files(a_bzrdir)
1395
1493
        control_files.create_lock()
1396
1494
        control_files.lock_write()
1397
 
        transport.put_bytes('format', self.get_format_string(),
 
1495
        transport.put_bytes('format', self.as_string(),
1398
1496
            mode=a_bzrdir._get_file_mode())
1399
1497
        if from_branch is not None:
1400
1498
            branch = from_branch
1460
1558
                transform.build_tree(basis, wt, accelerator_tree,
1461
1559
                                     hardlink=hardlink,
1462
1560
                                     delta_from_tree=delta_from_tree)
 
1561
                for hook in MutableTree.hooks['post_build_tree']:
 
1562
                    hook(wt)
1463
1563
            finally:
1464
1564
                basis.unlock()
1465
1565
        finally:
1476
1576
        :param wt: the WorkingTree object
1477
1577
        """
1478
1578
 
 
1579
    def open(self, a_bzrdir, _found=False):
 
1580
        """Return the WorkingTree object for a_bzrdir
 
1581
 
 
1582
        _found is a private parameter, do not use it. It is used to indicate
 
1583
               if format probing has already been done.
 
1584
        """
 
1585
        if not _found:
 
1586
            # we are being called directly and must probe.
 
1587
            raise NotImplementedError
 
1588
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
1589
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
1590
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
1591
        return wt
 
1592
 
1479
1593
    def _open(self, a_bzrdir, control_files):
1480
1594
        """Open the tree itself.
1481
1595
 
1495
1609
        """Overrideable method to get a bzrdir for testing."""
1496
1610
        # please test against something that will let us do tree references
1497
1611
        return bzrdir.format_registry.make_bzrdir(
1498
 
            'dirstate-with-subtree')
 
1612
            'development-subtree')
1499
1613
 
1500
1614
    _matchingbzrdir = property(__get_matchingbzrdir)
1501
1615
 
1506
1620
    This format:
1507
1621
        - exists within a metadir controlling .bzr
1508
1622
        - includes an explicit version marker for the workingtree control
1509
 
          files, separate from the BzrDir format
 
1623
          files, separate from the ControlDir format
1510
1624
        - modifies the hash cache format
1511
1625
        - is new in bzr 0.15
1512
1626
        - uses a LockDir to guard access to it.
1516
1630
 
1517
1631
    _tree_class = WorkingTree4
1518
1632
 
1519
 
    def get_format_string(self):
 
1633
    @classmethod
 
1634
    def get_format_string(cls):
1520
1635
        """See WorkingTreeFormat.get_format_string()."""
1521
1636
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1522
1637
 
1533
1648
 
1534
1649
    _tree_class = WorkingTree5
1535
1650
 
1536
 
    def get_format_string(self):
 
1651
    @classmethod
 
1652
    def get_format_string(cls):
1537
1653
        """See WorkingTreeFormat.get_format_string()."""
1538
1654
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1539
1655
 
1553
1669
 
1554
1670
    _tree_class = WorkingTree6
1555
1671
 
1556
 
    def get_format_string(self):
 
1672
    @classmethod
 
1673
    def get_format_string(cls):
1557
1674
        """See WorkingTreeFormat.get_format_string()."""
1558
1675
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1559
1676
 
1572
1689
        return True
1573
1690
 
1574
1691
 
1575
 
class DirStateRevisionTree(Tree):
 
1692
class DirStateRevisionTree(InventoryTree):
1576
1693
    """A revision tree pulling the inventory from a dirstate.
1577
1694
    
1578
1695
    Note that this is one of the historical (ie revision) trees cached in the
1597
1714
    def annotate_iter(self, file_id,
1598
1715
                      default_revision=_mod_revision.CURRENT_REVISION):
1599
1716
        """See Tree.annotate_iter"""
1600
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1717
        text_key = (file_id, self.get_file_revision(file_id))
1601
1718
        annotations = self._repository.texts.annotate(text_key)
1602
1719
        return [(key[-1], line) for (key, line) in annotations]
1603
1720
 
1604
 
    def _get_ancestors(self, default_revision):
1605
 
        return set(self._repository.get_ancestry(self._revision_id,
1606
 
                                                 topo_sorted=False))
1607
1721
    def _comparison_data(self, entry, path):
1608
1722
        """See Tree._comparison_data."""
1609
1723
        if entry is None:
1725
1839
                elif kind == 'directory':
1726
1840
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1727
1841
                elif kind == 'symlink':
1728
 
                    inv_entry.executable = False
1729
 
                    inv_entry.text_size = None
1730
1842
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1731
1843
                elif kind == 'tree-reference':
1732
1844
                    inv_entry.reference_revision = fingerprint or None
1752
1864
        # Make sure the file exists
1753
1865
        entry = self._get_entry(file_id, path=path)
1754
1866
        if entry == (None, None): # do we raise?
1755
 
            return None
 
1867
            raise errors.NoSuchId(self, file_id)
1756
1868
        parent_index = self._get_parent_index()
1757
1869
        last_changed_revision = entry[1][parent_index][4]
1758
1870
        try:
1769
1881
            return parent_details[1]
1770
1882
        return None
1771
1883
 
 
1884
    @needs_read_lock
 
1885
    def get_file_revision(self, file_id):
 
1886
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1887
        return inv[inv_file_id].revision
 
1888
 
1772
1889
    def get_file(self, file_id, path=None):
1773
1890
        return StringIO(self.get_file_text(file_id))
1774
1891
 
1775
1892
    def get_file_size(self, file_id):
1776
1893
        """See Tree.get_file_size"""
1777
 
        return self.inventory[file_id].text_size
 
1894
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1895
        return inv[inv_file_id].text_size
1778
1896
 
1779
1897
    def get_file_text(self, file_id, path=None):
1780
1898
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1781
1899
        return ''.join(content)
1782
1900
 
1783
1901
    def get_reference_revision(self, file_id, path=None):
1784
 
        return self.inventory[file_id].reference_revision
 
1902
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1903
        return inv[inv_file_id].reference_revision
1785
1904
 
1786
1905
    def iter_files_bytes(self, desired_files):
1787
1906
        """See Tree.iter_files_bytes.
1797
1916
                                       identifier))
1798
1917
        return self._repository.iter_files_bytes(repo_desired_files)
1799
1918
 
1800
 
    def get_symlink_target(self, file_id):
 
1919
    def get_symlink_target(self, file_id, path=None):
1801
1920
        entry = self._get_entry(file_id=file_id)
1802
1921
        parent_index = self._get_parent_index()
1803
1922
        if entry[1][parent_index][0] != 'l':
1811
1930
        """Return the revision id for this tree."""
1812
1931
        return self._revision_id
1813
1932
 
1814
 
    def _get_inventory(self):
 
1933
    def _get_root_inventory(self):
1815
1934
        if self._inventory is not None:
1816
1935
            return self._inventory
1817
1936
        self._must_be_locked()
1818
1937
        self._generate_inventory()
1819
1938
        return self._inventory
1820
1939
 
 
1940
    root_inventory = property(_get_root_inventory,
 
1941
                         doc="Inventory of this Tree")
 
1942
 
 
1943
    @deprecated_method(deprecated_in((2, 5, 0)))
 
1944
    def _get_inventory(self):
 
1945
        return self.root_inventory
 
1946
 
1821
1947
    inventory = property(_get_inventory,
1822
1948
                         doc="Inventory of this Tree")
1823
1949
 
1841
1967
 
1842
1968
    def path_content_summary(self, path):
1843
1969
        """See Tree.path_content_summary."""
1844
 
        id = self.inventory.path2id(path)
1845
 
        if id is None:
 
1970
        inv, inv_file_id = self._path2inv_file_id(path)
 
1971
        if inv_file_id is None:
1846
1972
            return ('missing', None, None, None)
1847
 
        entry = self._inventory[id]
 
1973
        entry = inv[inv_file_id]
1848
1974
        kind = entry.kind
1849
1975
        if kind == 'file':
1850
1976
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
1854
1980
            return (kind, None, None, None)
1855
1981
 
1856
1982
    def is_executable(self, file_id, path=None):
1857
 
        ie = self.inventory[file_id]
 
1983
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1984
        ie = inv[inv_file_id]
1858
1985
        if ie.kind != "file":
1859
 
            return None
 
1986
            return False
1860
1987
        return ie.executable
1861
1988
 
 
1989
    def is_locked(self):
 
1990
        return self._locked
 
1991
 
1862
1992
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1863
1993
        # We use a standard implementation, because DirStateRevisionTree is
1864
1994
        # dealing with one of the parents of the current state
1865
 
        inv = self._get_inventory()
1866
1995
        if from_dir is None:
 
1996
            inv = self.root_inventory
1867
1997
            from_dir_id = None
1868
1998
        else:
1869
 
            from_dir_id = inv.path2id(from_dir)
 
1999
            inv, from_dir_id = self._path2inv_file_id(from_dir)
1870
2000
            if from_dir_id is None:
1871
2001
                # Directory not versioned
1872
2002
                return
 
2003
        # FIXME: Support nested trees
1873
2004
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1874
2005
        if inv.root is not None and not include_root and from_dir is None:
1875
2006
            entries.next()
1877
2008
            yield path, 'V', entry.kind, entry.file_id, entry
1878
2009
 
1879
2010
    def lock_read(self):
1880
 
        """Lock the tree for a set of operations."""
 
2011
        """Lock the tree for a set of operations.
 
2012
 
 
2013
        :return: A bzrlib.lock.LogicalLockResult.
 
2014
        """
1881
2015
        if not self._locked:
1882
2016
            self._repository.lock_read()
1883
2017
            if self._dirstate._lock_token is None:
1884
2018
                self._dirstate.lock_read()
1885
2019
                self._dirstate_locked = True
1886
2020
        self._locked += 1
 
2021
        return LogicalLockResult(self.unlock)
1887
2022
 
1888
2023
    def _must_be_locked(self):
1889
2024
        if not self._locked:
1921
2056
        # So for now, we just build up the parent inventory, and extract
1922
2057
        # it the same way RevisionTree does.
1923
2058
        _directory = 'directory'
1924
 
        inv = self._get_inventory()
 
2059
        inv = self._get_root_inventory()
1925
2060
        top_id = inv.path2id(prefix)
1926
2061
        if top_id is None:
1927
2062
            pending = []
1968
2103
    def make_source_parent_tree(source, target):
1969
2104
        """Change the source tree into a parent of the target."""
1970
2105
        revid = source.commit('record tree')
1971
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2106
        target.branch.fetch(source.branch, revid)
1972
2107
        target.set_parent_ids([revid])
1973
2108
        return target.basis_tree(), target
1974
2109
 
2066
2201
                path_entries = state._entries_for_path(path)
2067
2202
                if not path_entries:
2068
2203
                    # this specified path is not present at all: error
2069
 
                    not_versioned.append(path)
 
2204
                    not_versioned.append(path.decode('utf-8'))
2070
2205
                    continue
2071
2206
                found_versioned = False
2072
2207
                # for each id at this path
2080
2215
                if not found_versioned:
2081
2216
                    # none of the indexes was not 'absent' at all ids for this
2082
2217
                    # path.
2083
 
                    not_versioned.append(path)
 
2218
                    not_versioned.append(path.decode('utf-8'))
2084
2219
            if len(not_versioned) > 0:
2085
2220
                raise errors.PathsNotVersionedError(not_versioned)
2086
2221
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2153
2288
    def update_format(self, tree):
2154
2289
        """Change the format marker."""
2155
2290
        tree._transport.put_bytes('format',
2156
 
            self.target_format.get_format_string(),
 
2291
            self.target_format.as_string(),
2157
2292
            mode=tree.bzrdir._get_file_mode())
2158
2293
 
2159
2294
 
2176
2311
    def update_format(self, tree):
2177
2312
        """Change the format marker."""
2178
2313
        tree._transport.put_bytes('format',
2179
 
            self.target_format.get_format_string(),
 
2314
            self.target_format.as_string(),
2180
2315
            mode=tree.bzrdir._get_file_mode())
2181
2316
 
2182
2317
 
2205
2340
    def update_format(self, tree):
2206
2341
        """Change the format marker."""
2207
2342
        tree._transport.put_bytes('format',
2208
 
            self.target_format.get_format_string(),
 
2343
            self.target_format.as_string(),
2209
2344
            mode=tree.bzrdir._get_file_mode())