~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Patch Queue Manager
  • Date: 2012-03-28 16:13:49 UTC
  • mfrom: (6499.2.3 948339-config-caching)
  • Revision ID: pqm@pqm.ubuntu.com-20120328161349-2gsc0g11fcu43hlc
(vila) Properly share mutable config sections and save the branch config
 only during the final unlock (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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,
 
41
    controldir,
38
42
    debug,
39
43
    dirstate,
40
44
    errors,
 
45
    filters as _mod_filters,
41
46
    generate_ids,
42
47
    osutils,
43
48
    revision as _mod_revision,
46
51
    transform,
47
52
    views,
48
53
    )
49
 
import bzrlib.branch
50
 
import bzrlib.ui
51
54
""")
52
55
 
53
56
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
 
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
57
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
 
import bzrlib.mutabletree
57
 
from bzrlib.mutabletree import needs_tree_write_lock
 
58
from bzrlib.lock import LogicalLockResult
 
59
from bzrlib.lockable_files import LockableFiles
 
60
from bzrlib.lockdir import LockDir
 
61
from bzrlib.mutabletree import (
 
62
    MutableTree,
 
63
    needs_tree_write_lock,
 
64
    )
58
65
from bzrlib.osutils import (
59
66
    file_kind,
60
67
    isdir,
62
69
    realpath,
63
70
    safe_unicode,
64
71
    )
65
 
from bzrlib.trace import mutter
 
72
from bzrlib.symbol_versioning import (
 
73
    deprecated_in,
 
74
    deprecated_method,
 
75
    )
66
76
from bzrlib.transport.local import LocalTransport
67
 
from bzrlib.tree import InterTree
68
 
from bzrlib.tree import Tree
69
 
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
70
 
 
71
 
 
72
 
class DirStateWorkingTree(WorkingTree3):
 
77
from bzrlib.tree import (
 
78
    InterTree,
 
79
    InventoryTree,
 
80
    )
 
81
from bzrlib.workingtree import (
 
82
    InventoryWorkingTree,
 
83
    WorkingTree,
 
84
    WorkingTreeFormatMetaDir,
 
85
    )
 
86
 
 
87
 
 
88
class DirStateWorkingTree(InventoryWorkingTree):
 
89
 
73
90
    def __init__(self, basedir,
74
91
                 branch,
75
92
                 _control_files=None,
85
102
        self._format = _format
86
103
        self.bzrdir = _bzrdir
87
104
        basedir = safe_unicode(basedir)
88
 
        mutter("opening working tree %r", basedir)
 
105
        trace.mutter("opening working tree %r", basedir)
89
106
        self._branch = branch
90
107
        self.basedir = realpath(basedir)
91
108
        # if branch is at our basedir and is a format 6 or less
125
142
            state.add(f, file_id, kind, None, '')
126
143
        self._make_dirty(reset_inventory=True)
127
144
 
 
145
    def _get_check_refs(self):
 
146
        """Return the references needed to perform a check of this tree."""
 
147
        return [('trees', self.last_revision())]
 
148
 
128
149
    def _make_dirty(self, reset_inventory):
129
150
        """Make the tree state dirty.
130
151
 
182
203
 
183
204
    def _comparison_data(self, entry, path):
184
205
        kind, executable, stat_value = \
185
 
            WorkingTree3._comparison_data(self, entry, path)
 
206
            WorkingTree._comparison_data(self, entry, path)
186
207
        # it looks like a plain directory, but it's really a reference -- see
187
208
        # also kind()
188
209
        if (self._repo_supports_tree_reference and kind == 'directory'
194
215
    def commit(self, message=None, revprops=None, *args, **kwargs):
195
216
        # mark the tree as dirty post commit - commit
196
217
        # can change the current versioned list by doing deletes.
197
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
218
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
198
219
        self._make_dirty(reset_inventory=True)
199
220
        return result
200
221
 
219
240
        local_path = self.bzrdir.get_workingtree_transport(None
220
241
            ).local_abspath('dirstate')
221
242
        self._dirstate = dirstate.DirState.on_file(local_path,
222
 
            self._sha1_provider())
 
243
            self._sha1_provider(), self._worth_saving_limit())
223
244
        return self._dirstate
224
245
 
225
246
    def _sha1_provider(self):
234
255
        else:
235
256
            return None
236
257
 
 
258
    def _worth_saving_limit(self):
 
259
        """How many hash changes are ok before we must save the dirstate.
 
260
 
 
261
        :return: an integer. -1 means never save.
 
262
        """
 
263
        conf = self.get_config_stack()
 
264
        return conf.get('bzr.workingtree.worth_saving_limit')
 
265
 
237
266
    def filter_unversioned_files(self, paths):
238
267
        """Filter out paths that are versioned.
239
268
 
369
398
        state = self.current_dirstate()
370
399
        if stat_value is None:
371
400
            try:
372
 
                stat_value = os.lstat(file_abspath)
 
401
                stat_value = osutils.lstat(file_abspath)
373
402
            except OSError, e:
374
403
                if e.errno == errno.ENOENT:
375
404
                    return None
390
419
                return link_or_sha1
391
420
        return None
392
421
 
393
 
    def _get_inventory(self):
 
422
    def _get_root_inventory(self):
394
423
        """Get the inventory for the tree. This is only valid within a lock."""
395
424
        if 'evil' in debug.debug_flags:
396
425
            trace.mutter_callsite(2,
401
430
        self._generate_inventory()
402
431
        return self._inventory
403
432
 
 
433
    @deprecated_method(deprecated_in((2, 5, 0)))
 
434
    def _get_inventory(self):
 
435
        return self.root_inventory
 
436
 
404
437
    inventory = property(_get_inventory,
405
438
                         doc="Inventory of this Tree")
406
439
 
 
440
    root_inventory = property(_get_root_inventory,
 
441
        "Root inventory of this tree")
 
442
 
407
443
    @needs_read_lock
408
444
    def get_parent_ids(self):
409
445
        """See Tree.get_parent_ids.
456
492
            return False # Missing entries are not executable
457
493
        return entry[1][0][3] # Executable?
458
494
 
459
 
    if not osutils.supports_executable():
460
 
        def is_executable(self, file_id, path=None):
461
 
            """Test if a file is executable or not.
 
495
    def is_executable(self, file_id, path=None):
 
496
        """Test if a file is executable or not.
462
497
 
463
 
            Note: The caller is expected to take a read-lock before calling this.
464
 
            """
 
498
        Note: The caller is expected to take a read-lock before calling this.
 
499
        """
 
500
        if not self._supports_executable():
465
501
            entry = self._get_entry(file_id=file_id, path=path)
466
502
            if entry == (None, None):
467
503
                return False
468
504
            return entry[1][0][3]
469
 
 
470
 
        _is_executable_from_path_and_stat = \
471
 
            _is_executable_from_path_and_stat_from_basis
472
 
    else:
473
 
        def is_executable(self, file_id, path=None):
474
 
            """Test if a file is executable or not.
475
 
 
476
 
            Note: The caller is expected to take a read-lock before calling this.
477
 
            """
 
505
        else:
478
506
            self._must_be_locked()
479
507
            if not path:
480
508
                path = self.id2path(file_id)
481
 
            mode = os.lstat(self.abspath(path)).st_mode
 
509
            mode = osutils.lstat(self.abspath(path)).st_mode
482
510
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
483
511
 
484
512
    def all_file_ids(self):
568
596
            return _mod_revision.NULL_REVISION
569
597
 
570
598
    def lock_read(self):
571
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
599
        """See Branch.lock_read, and WorkingTree.unlock.
 
600
 
 
601
        :return: A bzrlib.lock.LogicalLockResult.
 
602
        """
572
603
        self.branch.lock_read()
573
604
        try:
574
605
            self._control_files.lock_read()
587
618
        except:
588
619
            self.branch.unlock()
589
620
            raise
 
621
        return LogicalLockResult(self.unlock)
590
622
 
591
623
    def _lock_self_write(self):
592
624
        """This should be called after the branch is locked."""
607
639
        except:
608
640
            self.branch.unlock()
609
641
            raise
 
642
        return LogicalLockResult(self.unlock)
610
643
 
611
644
    def lock_tree_write(self):
612
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
645
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
646
 
 
647
        :return: A bzrlib.lock.LogicalLockResult.
 
648
        """
613
649
        self.branch.lock_read()
614
 
        self._lock_self_write()
 
650
        return self._lock_self_write()
615
651
 
616
652
    def lock_write(self):
617
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
653
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
654
 
 
655
        :return: A bzrlib.lock.LogicalLockResult.
 
656
        """
618
657
        self.branch.lock_write()
619
 
        self._lock_self_write()
 
658
        return self._lock_self_write()
620
659
 
621
660
    @needs_tree_write_lock
622
661
    def move(self, from_paths, to_dir, after=False):
653
692
 
654
693
        if self._inventory is not None:
655
694
            update_inventory = True
656
 
            inv = self.inventory
 
695
            inv = self.root_inventory
657
696
            to_dir_id = to_entry[0][2]
658
697
            to_dir_ie = inv[to_dir_id]
659
698
        else:
839
878
                rollback_rename()
840
879
                raise
841
880
            result.append((from_rel, to_rel))
842
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
881
            state._mark_modified()
843
882
            self._make_dirty(reset_inventory=False)
844
883
 
845
884
        return result
855
894
    @needs_read_lock
856
895
    def path2id(self, path):
857
896
        """Return the id for path in this tree."""
 
897
        if isinstance(path, list):
 
898
            if path == []:
 
899
                path = [""]
 
900
            path = osutils.pathjoin(*path)
858
901
        path = path.strip('/')
859
902
        entry = self._get_entry(path=path)
860
903
        if entry == (None, None):
938
981
                    all_versioned = False
939
982
                    break
940
983
            if not all_versioned:
941
 
                raise errors.PathsNotVersionedError(paths)
 
984
                raise errors.PathsNotVersionedError(
 
985
                    [p.decode('utf-8') for p in paths])
942
986
        # -- remove redundancy in supplied paths to prevent over-scanning --
943
987
        search_paths = osutils.minimum_path_selection(paths)
944
988
        # sketch:
993
1037
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
994
1038
            for dir_name in split_paths:
995
1039
                if dir_name not in found_dir_names:
996
 
                    raise errors.PathsNotVersionedError(paths)
 
1040
                    raise errors.PathsNotVersionedError(
 
1041
                        [p.decode('utf-8') for p in paths])
997
1042
 
998
1043
        for dir_name_id, trees_info in found.iteritems():
999
1044
            for index in search_indexes:
1006
1051
 
1007
1052
        This is a meaningless operation for dirstate, but we obey it anyhow.
1008
1053
        """
1009
 
        return self.inventory
 
1054
        return self.root_inventory
1010
1055
 
1011
1056
    @needs_read_lock
1012
1057
    def revision_tree(self, revision_id):
1102
1147
                        _mod_revision.NULL_REVISION)))
1103
1148
                ghosts.append(rev_id)
1104
1149
            accepted_revisions.add(rev_id)
1105
 
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1150
        updated = False
 
1151
        if (len(real_trees) == 1
 
1152
            and not ghosts
 
1153
            and self.branch.repository._format.fast_deltas
 
1154
            and isinstance(real_trees[0][1],
 
1155
                revisiontree.InventoryRevisionTree)
 
1156
            and self.get_parent_ids()):
 
1157
            rev_id, rev_tree = real_trees[0]
 
1158
            basis_id = self.get_parent_ids()[0]
 
1159
            # There are times when basis_tree won't be in
 
1160
            # self.branch.repository, (switch, for example)
 
1161
            try:
 
1162
                basis_tree = self.branch.repository.revision_tree(basis_id)
 
1163
            except errors.NoSuchRevision:
 
1164
                # Fall back to the set_parent_trees(), since we can't use
 
1165
                # _make_delta if we can't get the RevisionTree
 
1166
                pass
 
1167
            else:
 
1168
                delta = rev_tree.root_inventory._make_delta(
 
1169
                    basis_tree.root_inventory)
 
1170
                dirstate.update_basis_by_delta(delta, rev_id)
 
1171
                updated = True
 
1172
        if not updated:
 
1173
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1106
1174
        self._make_dirty(reset_inventory=False)
1107
1175
 
1108
1176
    def _set_root_id(self, file_id):
1128
1196
 
1129
1197
    def unlock(self):
1130
1198
        """Unlock in format 4 trees needs to write the entire dirstate."""
1131
 
        # do non-implementation specific cleanup
1132
 
        self._cleanup()
1133
 
 
1134
1199
        if self._control_files._lock_count == 1:
 
1200
            # do non-implementation specific cleanup
 
1201
            self._cleanup()
 
1202
 
1135
1203
            # eventually we should do signature checking during read locks for
1136
1204
            # dirstate updates.
1137
1205
            if self._control_files._lock_mode == 'w':
1236
1304
        # have to change the legacy inventory too.
1237
1305
        if self._inventory is not None:
1238
1306
            for file_id in file_ids:
1239
 
                self._inventory.remove_recursive_id(file_id)
 
1307
                if self._inventory.has_id(file_id):
 
1308
                    self._inventory.remove_recursive_id(file_id)
1240
1309
 
1241
1310
    @needs_tree_write_lock
1242
1311
    def rename_one(self, from_rel, to_rel, after=False):
1243
1312
        """See WorkingTree.rename_one"""
1244
1313
        self.flush()
1245
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1314
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1246
1315
 
1247
1316
    @needs_tree_write_lock
1248
1317
    def apply_inventory_delta(self, changes):
1274
1343
        # being created.
1275
1344
        self._inventory = None
1276
1345
        # generate a delta,
1277
 
        delta = inv._make_delta(self.inventory)
 
1346
        delta = inv._make_delta(self.root_inventory)
1278
1347
        # and apply it.
1279
1348
        self.apply_inventory_delta(delta)
1280
1349
        if had_inventory:
1281
1350
            self._inventory = inv
1282
1351
        self.flush()
1283
1352
 
 
1353
    @needs_tree_write_lock
 
1354
    def reset_state(self, revision_ids=None):
 
1355
        """Reset the state of the working tree.
 
1356
 
 
1357
        This does a hard-reset to a last-known-good state. This is a way to
 
1358
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
 
1359
        """
 
1360
        if revision_ids is None:
 
1361
            revision_ids = self.get_parent_ids()
 
1362
        if not revision_ids:
 
1363
            base_tree = self.branch.repository.revision_tree(
 
1364
                _mod_revision.NULL_REVISION)
 
1365
            trees = []
 
1366
        else:
 
1367
            trees = zip(revision_ids,
 
1368
                        self.branch.repository.revision_trees(revision_ids))
 
1369
            base_tree = trees[0][1]
 
1370
        state = self.current_dirstate()
 
1371
        # We don't support ghosts yet
 
1372
        state.set_state_from_scratch(base_tree.root_inventory, trees, [])
 
1373
 
1284
1374
 
1285
1375
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1286
1376
 
1291
1381
        """See dirstate.SHA1Provider.sha1()."""
1292
1382
        filters = self.tree._content_filter_stack(
1293
1383
            self.tree.relpath(osutils.safe_unicode(abspath)))
1294
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1384
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1295
1385
 
1296
1386
    def stat_and_sha1(self, abspath):
1297
1387
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1301
1391
        try:
1302
1392
            statvalue = os.fstat(file_obj.fileno())
1303
1393
            if filters:
1304
 
                file_obj = filtered_input_file(file_obj, filters)
 
1394
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1305
1395
            sha1 = osutils.size_sha_file(file_obj)[1]
1306
1396
        finally:
1307
1397
            file_obj.close()
1318
1408
    def _file_content_summary(self, path, stat_result):
1319
1409
        # This is to support the somewhat obsolete path_content_summary method
1320
1410
        # with content filtering: see
1321
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1411
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1322
1412
        #
1323
1413
        # If the dirstate cache is up to date and knows the hash and size,
1324
1414
        # return that.
1337
1427
class WorkingTree4(DirStateWorkingTree):
1338
1428
    """This is the Format 4 working tree.
1339
1429
 
1340
 
    This differs from WorkingTree3 by:
 
1430
    This differs from WorkingTree by:
1341
1431
     - Having a consolidated internal dirstate, stored in a
1342
1432
       randomly-accessible sorted file on disk.
1343
1433
     - Not having a regular inventory attribute.  One can be synthesized
1371
1461
        return views.PathBasedViews(self)
1372
1462
 
1373
1463
 
1374
 
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1464
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
 
1465
 
 
1466
    missing_parent_conflicts = True
 
1467
 
 
1468
    supports_versioned_directories = True
 
1469
 
 
1470
    _lock_class = LockDir
 
1471
    _lock_file_name = 'lock'
 
1472
 
 
1473
    def _open_control_files(self, a_bzrdir):
 
1474
        transport = a_bzrdir.get_workingtree_transport(None)
 
1475
        return LockableFiles(transport, self._lock_file_name,
 
1476
                             self._lock_class)
 
1477
 
1375
1478
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1376
1479
                   accelerator_tree=None, hardlink=False):
1377
1480
        """See WorkingTreeFormat.initialize().
1378
1481
 
1379
1482
        :param revision_id: allows creating a working tree at a different
1380
 
        revision than the branch is at.
 
1483
            revision than the branch is at.
1381
1484
        :param accelerator_tree: A tree which can be used for retrieving file
1382
1485
            contents more quickly than the revision tree, i.e. a workingtree.
1383
1486
            The revision tree will be used for cases where accelerator_tree's
1394
1497
        control_files = self._open_control_files(a_bzrdir)
1395
1498
        control_files.create_lock()
1396
1499
        control_files.lock_write()
1397
 
        transport.put_bytes('format', self.get_format_string(),
 
1500
        transport.put_bytes('format', self.as_string(),
1398
1501
            mode=a_bzrdir._get_file_mode())
1399
1502
        if from_branch is not None:
1400
1503
            branch = from_branch
1460
1563
                transform.build_tree(basis, wt, accelerator_tree,
1461
1564
                                     hardlink=hardlink,
1462
1565
                                     delta_from_tree=delta_from_tree)
 
1566
                for hook in MutableTree.hooks['post_build_tree']:
 
1567
                    hook(wt)
1463
1568
            finally:
1464
1569
                basis.unlock()
1465
1570
        finally:
1476
1581
        :param wt: the WorkingTree object
1477
1582
        """
1478
1583
 
 
1584
    def open(self, a_bzrdir, _found=False):
 
1585
        """Return the WorkingTree object for a_bzrdir
 
1586
 
 
1587
        _found is a private parameter, do not use it. It is used to indicate
 
1588
               if format probing has already been done.
 
1589
        """
 
1590
        if not _found:
 
1591
            # we are being called directly and must probe.
 
1592
            raise NotImplementedError
 
1593
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
1594
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
1595
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
1596
        return wt
 
1597
 
1479
1598
    def _open(self, a_bzrdir, control_files):
1480
1599
        """Open the tree itself.
1481
1600
 
1494
1613
    def _get_matchingbzrdir(self):
1495
1614
        """Overrideable method to get a bzrdir for testing."""
1496
1615
        # please test against something that will let us do tree references
1497
 
        return bzrdir.format_registry.make_bzrdir(
1498
 
            'dirstate-with-subtree')
 
1616
        return controldir.format_registry.make_bzrdir(
 
1617
            'development-subtree')
1499
1618
 
1500
1619
    _matchingbzrdir = property(__get_matchingbzrdir)
1501
1620
 
1506
1625
    This format:
1507
1626
        - exists within a metadir controlling .bzr
1508
1627
        - includes an explicit version marker for the workingtree control
1509
 
          files, separate from the BzrDir format
 
1628
          files, separate from the ControlDir format
1510
1629
        - modifies the hash cache format
1511
1630
        - is new in bzr 0.15
1512
1631
        - uses a LockDir to guard access to it.
1516
1635
 
1517
1636
    _tree_class = WorkingTree4
1518
1637
 
1519
 
    def get_format_string(self):
 
1638
    @classmethod
 
1639
    def get_format_string(cls):
1520
1640
        """See WorkingTreeFormat.get_format_string()."""
1521
1641
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1522
1642
 
1533
1653
 
1534
1654
    _tree_class = WorkingTree5
1535
1655
 
1536
 
    def get_format_string(self):
 
1656
    @classmethod
 
1657
    def get_format_string(cls):
1537
1658
        """See WorkingTreeFormat.get_format_string()."""
1538
1659
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1539
1660
 
1553
1674
 
1554
1675
    _tree_class = WorkingTree6
1555
1676
 
1556
 
    def get_format_string(self):
 
1677
    @classmethod
 
1678
    def get_format_string(cls):
1557
1679
        """See WorkingTreeFormat.get_format_string()."""
1558
1680
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1559
1681
 
1572
1694
        return True
1573
1695
 
1574
1696
 
1575
 
class DirStateRevisionTree(Tree):
 
1697
class DirStateRevisionTree(InventoryTree):
1576
1698
    """A revision tree pulling the inventory from a dirstate.
1577
1699
    
1578
1700
    Note that this is one of the historical (ie revision) trees cached in the
1597
1719
    def annotate_iter(self, file_id,
1598
1720
                      default_revision=_mod_revision.CURRENT_REVISION):
1599
1721
        """See Tree.annotate_iter"""
1600
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1722
        text_key = (file_id, self.get_file_revision(file_id))
1601
1723
        annotations = self._repository.texts.annotate(text_key)
1602
1724
        return [(key[-1], line) for (key, line) in annotations]
1603
1725
 
1604
 
    def _get_ancestors(self, default_revision):
1605
 
        return set(self._repository.get_ancestry(self._revision_id,
1606
 
                                                 topo_sorted=False))
1607
1726
    def _comparison_data(self, entry, path):
1608
1727
        """See Tree._comparison_data."""
1609
1728
        if entry is None:
1662
1781
        if path is not None:
1663
1782
            path = path.encode('utf8')
1664
1783
        parent_index = self._get_parent_index()
1665
 
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
 
1784
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id,
 
1785
            path_utf8=path)
1666
1786
 
1667
1787
    def _generate_inventory(self):
1668
1788
        """Create and set self.inventory from the dirstate object.
1725
1845
                elif kind == 'directory':
1726
1846
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1727
1847
                elif kind == 'symlink':
1728
 
                    inv_entry.executable = False
1729
 
                    inv_entry.text_size = None
1730
1848
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1731
1849
                elif kind == 'tree-reference':
1732
1850
                    inv_entry.reference_revision = fingerprint or None
1752
1870
        # Make sure the file exists
1753
1871
        entry = self._get_entry(file_id, path=path)
1754
1872
        if entry == (None, None): # do we raise?
1755
 
            return None
 
1873
            raise errors.NoSuchId(self, file_id)
1756
1874
        parent_index = self._get_parent_index()
1757
1875
        last_changed_revision = entry[1][parent_index][4]
1758
 
        return self._repository.get_revision(last_changed_revision).timestamp
 
1876
        try:
 
1877
            rev = self._repository.get_revision(last_changed_revision)
 
1878
        except errors.NoSuchRevision:
 
1879
            raise errors.FileTimestampUnavailable(self.id2path(file_id))
 
1880
        return rev.timestamp
1759
1881
 
1760
1882
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1761
1883
        entry = self._get_entry(file_id=file_id, path=path)
1765
1887
            return parent_details[1]
1766
1888
        return None
1767
1889
 
 
1890
    @needs_read_lock
 
1891
    def get_file_revision(self, file_id):
 
1892
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1893
        return inv[inv_file_id].revision
 
1894
 
1768
1895
    def get_file(self, file_id, path=None):
1769
1896
        return StringIO(self.get_file_text(file_id))
1770
1897
 
1771
1898
    def get_file_size(self, file_id):
1772
1899
        """See Tree.get_file_size"""
1773
 
        return self.inventory[file_id].text_size
 
1900
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1901
        return inv[inv_file_id].text_size
1774
1902
 
1775
1903
    def get_file_text(self, file_id, path=None):
1776
1904
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1777
1905
        return ''.join(content)
1778
1906
 
1779
1907
    def get_reference_revision(self, file_id, path=None):
1780
 
        return self.inventory[file_id].reference_revision
 
1908
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1909
        return inv[inv_file_id].reference_revision
1781
1910
 
1782
1911
    def iter_files_bytes(self, desired_files):
1783
1912
        """See Tree.iter_files_bytes.
1793
1922
                                       identifier))
1794
1923
        return self._repository.iter_files_bytes(repo_desired_files)
1795
1924
 
1796
 
    def get_symlink_target(self, file_id):
 
1925
    def get_symlink_target(self, file_id, path=None):
1797
1926
        entry = self._get_entry(file_id=file_id)
1798
1927
        parent_index = self._get_parent_index()
1799
1928
        if entry[1][parent_index][0] != 'l':
1807
1936
        """Return the revision id for this tree."""
1808
1937
        return self._revision_id
1809
1938
 
1810
 
    def _get_inventory(self):
 
1939
    def _get_root_inventory(self):
1811
1940
        if self._inventory is not None:
1812
1941
            return self._inventory
1813
1942
        self._must_be_locked()
1814
1943
        self._generate_inventory()
1815
1944
        return self._inventory
1816
1945
 
 
1946
    root_inventory = property(_get_root_inventory,
 
1947
                         doc="Inventory of this Tree")
 
1948
 
 
1949
    @deprecated_method(deprecated_in((2, 5, 0)))
 
1950
    def _get_inventory(self):
 
1951
        return self.root_inventory
 
1952
 
1817
1953
    inventory = property(_get_inventory,
1818
1954
                         doc="Inventory of this Tree")
1819
1955
 
1828
1964
        entry = self._get_entry(file_id=file_id)[1]
1829
1965
        if entry is None:
1830
1966
            raise errors.NoSuchId(tree=self, file_id=file_id)
1831
 
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
 
1967
        parent_index = self._get_parent_index()
 
1968
        return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1832
1969
 
1833
1970
    def stored_kind(self, file_id):
1834
1971
        """See Tree.stored_kind"""
1836
1973
 
1837
1974
    def path_content_summary(self, path):
1838
1975
        """See Tree.path_content_summary."""
1839
 
        id = self.inventory.path2id(path)
1840
 
        if id is None:
 
1976
        inv, inv_file_id = self._path2inv_file_id(path)
 
1977
        if inv_file_id is None:
1841
1978
            return ('missing', None, None, None)
1842
 
        entry = self._inventory[id]
 
1979
        entry = inv[inv_file_id]
1843
1980
        kind = entry.kind
1844
1981
        if kind == 'file':
1845
1982
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
1849
1986
            return (kind, None, None, None)
1850
1987
 
1851
1988
    def is_executable(self, file_id, path=None):
1852
 
        ie = self.inventory[file_id]
 
1989
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1990
        ie = inv[inv_file_id]
1853
1991
        if ie.kind != "file":
1854
 
            return None
 
1992
            return False
1855
1993
        return ie.executable
1856
1994
 
 
1995
    def is_locked(self):
 
1996
        return self._locked
 
1997
 
1857
1998
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1858
1999
        # We use a standard implementation, because DirStateRevisionTree is
1859
2000
        # dealing with one of the parents of the current state
1860
 
        inv = self._get_inventory()
1861
2001
        if from_dir is None:
 
2002
            inv = self.root_inventory
1862
2003
            from_dir_id = None
1863
2004
        else:
1864
 
            from_dir_id = inv.path2id(from_dir)
 
2005
            inv, from_dir_id = self._path2inv_file_id(from_dir)
1865
2006
            if from_dir_id is None:
1866
2007
                # Directory not versioned
1867
2008
                return
 
2009
        # FIXME: Support nested trees
1868
2010
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1869
2011
        if inv.root is not None and not include_root and from_dir is None:
1870
2012
            entries.next()
1872
2014
            yield path, 'V', entry.kind, entry.file_id, entry
1873
2015
 
1874
2016
    def lock_read(self):
1875
 
        """Lock the tree for a set of operations."""
 
2017
        """Lock the tree for a set of operations.
 
2018
 
 
2019
        :return: A bzrlib.lock.LogicalLockResult.
 
2020
        """
1876
2021
        if not self._locked:
1877
2022
            self._repository.lock_read()
1878
2023
            if self._dirstate._lock_token is None:
1879
2024
                self._dirstate.lock_read()
1880
2025
                self._dirstate_locked = True
1881
2026
        self._locked += 1
 
2027
        return LogicalLockResult(self.unlock)
1882
2028
 
1883
2029
    def _must_be_locked(self):
1884
2030
        if not self._locked:
1888
2034
    def path2id(self, path):
1889
2035
        """Return the id for path in this tree."""
1890
2036
        # lookup by path: faster than splitting and walking the ivnentory.
 
2037
        if isinstance(path, list):
 
2038
            if path == []:
 
2039
                path = [""]
 
2040
            path = osutils.pathjoin(*path)
1891
2041
        entry = self._get_entry(path=path)
1892
2042
        if entry == (None, None):
1893
2043
            return None
1916
2066
        # So for now, we just build up the parent inventory, and extract
1917
2067
        # it the same way RevisionTree does.
1918
2068
        _directory = 'directory'
1919
 
        inv = self._get_inventory()
 
2069
        inv = self._get_root_inventory()
1920
2070
        top_id = inv.path2id(prefix)
1921
2071
        if top_id is None:
1922
2072
            pending = []
1963
2113
    def make_source_parent_tree(source, target):
1964
2114
        """Change the source tree into a parent of the target."""
1965
2115
        revid = source.commit('record tree')
1966
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2116
        target.branch.fetch(source.branch, revid)
1967
2117
        target.set_parent_ids([revid])
1968
2118
        return target.basis_tree(), target
1969
2119
 
2061
2211
                path_entries = state._entries_for_path(path)
2062
2212
                if not path_entries:
2063
2213
                    # this specified path is not present at all: error
2064
 
                    not_versioned.append(path)
 
2214
                    not_versioned.append(path.decode('utf-8'))
2065
2215
                    continue
2066
2216
                found_versioned = False
2067
2217
                # for each id at this path
2075
2225
                if not found_versioned:
2076
2226
                    # none of the indexes was not 'absent' at all ids for this
2077
2227
                    # path.
2078
 
                    not_versioned.append(path)
 
2228
                    not_versioned.append(path.decode('utf-8'))
2079
2229
            if len(not_versioned) > 0:
2080
2230
                raise errors.PathsNotVersionedError(not_versioned)
2081
2231
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2148
2298
    def update_format(self, tree):
2149
2299
        """Change the format marker."""
2150
2300
        tree._transport.put_bytes('format',
2151
 
            self.target_format.get_format_string(),
 
2301
            self.target_format.as_string(),
2152
2302
            mode=tree.bzrdir._get_file_mode())
2153
2303
 
2154
2304
 
2171
2321
    def update_format(self, tree):
2172
2322
        """Change the format marker."""
2173
2323
        tree._transport.put_bytes('format',
2174
 
            self.target_format.get_format_string(),
 
2324
            self.target_format.as_string(),
2175
2325
            mode=tree.bzrdir._get_file_mode())
2176
2326
 
2177
2327
 
2200
2350
    def update_format(self, tree):
2201
2351
        """Change the format marker."""
2202
2352
        tree._transport.put_bytes('format',
2203
 
            self.target_format.get_format_string(),
 
2353
            self.target_format.as_string(),
2204
2354
            mode=tree.bzrdir._get_file_mode())