/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 brzlib/workingtree_4.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-21 12:41:27 UTC
  • mto: This revision was merged to the branch mainline in revision 6623.
  • Revision ID: jelmer@jelmer.uk-20170521124127-iv8etg0vwymyai6y
s/bzr/brz/ in apport config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007-2012 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
28
30
 
29
 
from bzrlib.lazy_import import lazy_import
 
31
from brzlib.lazy_import import lazy_import
30
32
lazy_import(globals(), """
31
33
import errno
32
34
import stat
33
35
 
34
 
import bzrlib
35
 
from bzrlib import (
 
36
from brzlib 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
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
 
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
 
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
 
from bzrlib.mutabletree import needs_tree_write_lock
57
 
from bzrlib.osutils import (
 
56
from brzlib.decorators import needs_read_lock, needs_write_lock
 
57
from brzlib.inventory import Inventory, ROOT_ID, entry_factory
 
58
from brzlib.lock import LogicalLockResult
 
59
from brzlib.lockable_files import LockableFiles
 
60
from brzlib.lockdir import LockDir
 
61
from brzlib.mutabletree import (
 
62
    MutableTree,
 
63
    needs_tree_write_lock,
 
64
    )
 
65
from brzlib.osutils import (
58
66
    file_kind,
59
67
    isdir,
60
68
    pathjoin,
61
69
    realpath,
62
70
    safe_unicode,
63
71
    )
64
 
from bzrlib.trace import mutter
65
 
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):
 
72
from brzlib.symbol_versioning import (
 
73
    deprecated_in,
 
74
    deprecated_method,
 
75
    )
 
76
from brzlib.transport.local import LocalTransport
 
77
from brzlib.tree import (
 
78
    InterTree,
 
79
    InventoryTree,
 
80
    )
 
81
from brzlib.workingtree import (
 
82
    InventoryWorkingTree,
 
83
    WorkingTree,
 
84
    WorkingTreeFormatMetaDir,
 
85
    )
 
86
 
 
87
 
 
88
class DirStateWorkingTree(InventoryWorkingTree):
 
89
 
72
90
    def __init__(self, basedir,
73
91
                 branch,
74
92
                 _control_files=None,
84
102
        self._format = _format
85
103
        self.bzrdir = _bzrdir
86
104
        basedir = safe_unicode(basedir)
87
 
        mutter("opening working tree %r", basedir)
 
105
        trace.mutter("opening working tree %r", basedir)
88
106
        self._branch = branch
89
107
        self.basedir = realpath(basedir)
90
108
        # if branch is at our basedir and is a format 6 or less
124
142
            state.add(f, file_id, kind, None, '')
125
143
        self._make_dirty(reset_inventory=True)
126
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
 
127
149
    def _make_dirty(self, reset_inventory):
128
150
        """Make the tree state dirty.
129
151
 
181
203
 
182
204
    def _comparison_data(self, entry, path):
183
205
        kind, executable, stat_value = \
184
 
            WorkingTree3._comparison_data(self, entry, path)
 
206
            WorkingTree._comparison_data(self, entry, path)
185
207
        # it looks like a plain directory, but it's really a reference -- see
186
208
        # also kind()
187
209
        if (self._repo_supports_tree_reference and kind == 'directory'
193
215
    def commit(self, message=None, revprops=None, *args, **kwargs):
194
216
        # mark the tree as dirty post commit - commit
195
217
        # can change the current versioned list by doing deletes.
196
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
218
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
197
219
        self._make_dirty(reset_inventory=True)
198
220
        return result
199
221
 
218
240
        local_path = self.bzrdir.get_workingtree_transport(None
219
241
            ).local_abspath('dirstate')
220
242
        self._dirstate = dirstate.DirState.on_file(local_path,
221
 
            self._sha1_provider())
 
243
            self._sha1_provider(), self._worth_saving_limit())
222
244
        return self._dirstate
223
245
 
224
246
    def _sha1_provider(self):
233
255
        else:
234
256
            return None
235
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
 
236
266
    def filter_unversioned_files(self, paths):
237
267
        """Filter out paths that are versioned.
238
268
 
368
398
        state = self.current_dirstate()
369
399
        if stat_value is None:
370
400
            try:
371
 
                stat_value = os.lstat(file_abspath)
 
401
                stat_value = osutils.lstat(file_abspath)
372
402
            except OSError, e:
373
403
                if e.errno == errno.ENOENT:
374
404
                    return None
389
419
                return link_or_sha1
390
420
        return None
391
421
 
392
 
    def _get_inventory(self):
 
422
    def _get_root_inventory(self):
393
423
        """Get the inventory for the tree. This is only valid within a lock."""
394
424
        if 'evil' in debug.debug_flags:
395
425
            trace.mutter_callsite(2,
400
430
        self._generate_inventory()
401
431
        return self._inventory
402
432
 
 
433
    @deprecated_method(deprecated_in((2, 5, 0)))
 
434
    def _get_inventory(self):
 
435
        return self.root_inventory
 
436
 
403
437
    inventory = property(_get_inventory,
404
438
                         doc="Inventory of this Tree")
405
439
 
 
440
    root_inventory = property(_get_root_inventory,
 
441
        "Root inventory of this tree")
 
442
 
406
443
    @needs_read_lock
407
444
    def get_parent_ids(self):
408
445
        """See Tree.get_parent_ids.
455
492
            return False # Missing entries are not executable
456
493
        return entry[1][0][3] # Executable?
457
494
 
458
 
    if not osutils.supports_executable():
459
 
        def is_executable(self, file_id, path=None):
460
 
            """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.
461
497
 
462
 
            Note: The caller is expected to take a read-lock before calling this.
463
 
            """
 
498
        Note: The caller is expected to take a read-lock before calling this.
 
499
        """
 
500
        if not self._supports_executable():
464
501
            entry = self._get_entry(file_id=file_id, path=path)
465
502
            if entry == (None, None):
466
503
                return False
467
504
            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
 
            """
 
505
        else:
477
506
            self._must_be_locked()
478
507
            if not path:
479
508
                path = self.id2path(file_id)
480
 
            mode = os.lstat(self.abspath(path)).st_mode
 
509
            mode = osutils.lstat(self.abspath(path)).st_mode
481
510
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
482
511
 
483
512
    def all_file_ids(self):
569
598
    def lock_read(self):
570
599
        """See Branch.lock_read, and WorkingTree.unlock.
571
600
 
572
 
        :return: An object with an unlock method which will release the lock
573
 
            obtained.
 
601
        :return: A brzlib.lock.LogicalLockResult.
574
602
        """
575
603
        self.branch.lock_read()
576
604
        try:
590
618
        except:
591
619
            self.branch.unlock()
592
620
            raise
593
 
        return self
 
621
        return LogicalLockResult(self.unlock)
594
622
 
595
623
    def _lock_self_write(self):
596
624
        """This should be called after the branch is locked."""
611
639
        except:
612
640
            self.branch.unlock()
613
641
            raise
614
 
        return self
 
642
        return LogicalLockResult(self.unlock)
615
643
 
616
644
    def lock_tree_write(self):
617
645
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
618
646
 
619
 
        :return: An object with an unlock method which will release the lock
620
 
            obtained.
 
647
        :return: A brzlib.lock.LogicalLockResult.
621
648
        """
622
649
        self.branch.lock_read()
623
650
        return self._lock_self_write()
625
652
    def lock_write(self):
626
653
        """See MutableTree.lock_write, and WorkingTree.unlock.
627
654
 
628
 
        :return: An object with an unlock method which will release the lock
629
 
            obtained.
 
655
        :return: A brzlib.lock.LogicalLockResult.
630
656
        """
631
657
        self.branch.lock_write()
632
658
        return self._lock_self_write()
666
692
 
667
693
        if self._inventory is not None:
668
694
            update_inventory = True
669
 
            inv = self.inventory
 
695
            inv = self.root_inventory
670
696
            to_dir_id = to_entry[0][2]
671
697
            to_dir_ie = inv[to_dir_id]
672
698
        else:
852
878
                rollback_rename()
853
879
                raise
854
880
            result.append((from_rel, to_rel))
855
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
881
            state._mark_modified()
856
882
            self._make_dirty(reset_inventory=False)
857
883
 
858
884
        return result
868
894
    @needs_read_lock
869
895
    def path2id(self, path):
870
896
        """Return the id for path in this tree."""
 
897
        if isinstance(path, list):
 
898
            if path == []:
 
899
                path = [""]
 
900
            path = osutils.pathjoin(*path)
871
901
        path = path.strip('/')
872
902
        entry = self._get_entry(path=path)
873
903
        if entry == (None, None):
951
981
                    all_versioned = False
952
982
                    break
953
983
            if not all_versioned:
954
 
                raise errors.PathsNotVersionedError(paths)
 
984
                raise errors.PathsNotVersionedError(
 
985
                    [p.decode('utf-8') for p in paths])
955
986
        # -- remove redundancy in supplied paths to prevent over-scanning --
956
987
        search_paths = osutils.minimum_path_selection(paths)
957
988
        # sketch:
1006
1037
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1007
1038
            for dir_name in split_paths:
1008
1039
                if dir_name not in found_dir_names:
1009
 
                    raise errors.PathsNotVersionedError(paths)
 
1040
                    raise errors.PathsNotVersionedError(
 
1041
                        [p.decode('utf-8') for p in paths])
1010
1042
 
1011
1043
        for dir_name_id, trees_info in found.iteritems():
1012
1044
            for index in search_indexes:
1019
1051
 
1020
1052
        This is a meaningless operation for dirstate, but we obey it anyhow.
1021
1053
        """
1022
 
        return self.inventory
 
1054
        return self.root_inventory
1023
1055
 
1024
1056
    @needs_read_lock
1025
1057
    def revision_tree(self, revision_id):
1115
1147
                        _mod_revision.NULL_REVISION)))
1116
1148
                ghosts.append(rev_id)
1117
1149
            accepted_revisions.add(rev_id)
1118
 
        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)
1119
1174
        self._make_dirty(reset_inventory=False)
1120
1175
 
1121
1176
    def _set_root_id(self, file_id):
1141
1196
 
1142
1197
    def unlock(self):
1143
1198
        """Unlock in format 4 trees needs to write the entire dirstate."""
1144
 
        # do non-implementation specific cleanup
1145
 
        self._cleanup()
1146
 
 
1147
1199
        if self._control_files._lock_count == 1:
 
1200
            # do non-implementation specific cleanup
 
1201
            self._cleanup()
 
1202
 
1148
1203
            # eventually we should do signature checking during read locks for
1149
1204
            # dirstate updates.
1150
1205
            if self._control_files._lock_mode == 'w':
1249
1304
        # have to change the legacy inventory too.
1250
1305
        if self._inventory is not None:
1251
1306
            for file_id in file_ids:
1252
 
                self._inventory.remove_recursive_id(file_id)
 
1307
                if self._inventory.has_id(file_id):
 
1308
                    self._inventory.remove_recursive_id(file_id)
1253
1309
 
1254
1310
    @needs_tree_write_lock
1255
1311
    def rename_one(self, from_rel, to_rel, after=False):
1256
1312
        """See WorkingTree.rename_one"""
1257
1313
        self.flush()
1258
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1314
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1259
1315
 
1260
1316
    @needs_tree_write_lock
1261
1317
    def apply_inventory_delta(self, changes):
1287
1343
        # being created.
1288
1344
        self._inventory = None
1289
1345
        # generate a delta,
1290
 
        delta = inv._make_delta(self.inventory)
 
1346
        delta = inv._make_delta(self.root_inventory)
1291
1347
        # and apply it.
1292
1348
        self.apply_inventory_delta(delta)
1293
1349
        if had_inventory:
1294
1350
            self._inventory = inv
1295
1351
        self.flush()
1296
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
 
1297
1374
 
1298
1375
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1299
1376
 
1304
1381
        """See dirstate.SHA1Provider.sha1()."""
1305
1382
        filters = self.tree._content_filter_stack(
1306
1383
            self.tree.relpath(osutils.safe_unicode(abspath)))
1307
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1384
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1308
1385
 
1309
1386
    def stat_and_sha1(self, abspath):
1310
1387
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1314
1391
        try:
1315
1392
            statvalue = os.fstat(file_obj.fileno())
1316
1393
            if filters:
1317
 
                file_obj = filtered_input_file(file_obj, filters)
 
1394
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1318
1395
            sha1 = osutils.size_sha_file(file_obj)[1]
1319
1396
        finally:
1320
1397
            file_obj.close()
1331
1408
    def _file_content_summary(self, path, stat_result):
1332
1409
        # This is to support the somewhat obsolete path_content_summary method
1333
1410
        # with content filtering: see
1334
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1411
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1335
1412
        #
1336
1413
        # If the dirstate cache is up to date and knows the hash and size,
1337
1414
        # return that.
1350
1427
class WorkingTree4(DirStateWorkingTree):
1351
1428
    """This is the Format 4 working tree.
1352
1429
 
1353
 
    This differs from WorkingTree3 by:
 
1430
    This differs from WorkingTree by:
1354
1431
     - Having a consolidated internal dirstate, stored in a
1355
1432
       randomly-accessible sorted file on disk.
1356
1433
     - Not having a regular inventory attribute.  One can be synthesized
1384
1461
        return views.PathBasedViews(self)
1385
1462
 
1386
1463
 
1387
 
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)
1388
1477
 
1389
1478
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1390
1479
                   accelerator_tree=None, hardlink=False):
1391
1480
        """See WorkingTreeFormat.initialize().
1392
1481
 
1393
1482
        :param revision_id: allows creating a working tree at a different
1394
 
        revision than the branch is at.
 
1483
            revision than the branch is at.
1395
1484
        :param accelerator_tree: A tree which can be used for retrieving file
1396
1485
            contents more quickly than the revision tree, i.e. a workingtree.
1397
1486
            The revision tree will be used for cases where accelerator_tree's
1408
1497
        control_files = self._open_control_files(a_bzrdir)
1409
1498
        control_files.create_lock()
1410
1499
        control_files.lock_write()
1411
 
        transport.put_bytes('format', self.get_format_string(),
 
1500
        transport.put_bytes('format', self.as_string(),
1412
1501
            mode=a_bzrdir._get_file_mode())
1413
1502
        if from_branch is not None:
1414
1503
            branch = from_branch
1474
1563
                transform.build_tree(basis, wt, accelerator_tree,
1475
1564
                                     hardlink=hardlink,
1476
1565
                                     delta_from_tree=delta_from_tree)
 
1566
                for hook in MutableTree.hooks['post_build_tree']:
 
1567
                    hook(wt)
1477
1568
            finally:
1478
1569
                basis.unlock()
1479
1570
        finally:
1490
1581
        :param wt: the WorkingTree object
1491
1582
        """
1492
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
 
1493
1598
    def _open(self, a_bzrdir, control_files):
1494
1599
        """Open the tree itself.
1495
1600
 
1508
1613
    def _get_matchingbzrdir(self):
1509
1614
        """Overrideable method to get a bzrdir for testing."""
1510
1615
        # please test against something that will let us do tree references
1511
 
        return bzrdir.format_registry.make_bzrdir(
1512
 
            'dirstate-with-subtree')
 
1616
        return controldir.format_registry.make_bzrdir(
 
1617
            'development-subtree')
1513
1618
 
1514
1619
    _matchingbzrdir = property(__get_matchingbzrdir)
1515
1620
 
1520
1625
    This format:
1521
1626
        - exists within a metadir controlling .bzr
1522
1627
        - includes an explicit version marker for the workingtree control
1523
 
          files, separate from the BzrDir format
 
1628
          files, separate from the ControlDir format
1524
1629
        - modifies the hash cache format
1525
1630
        - is new in bzr 0.15
1526
1631
        - uses a LockDir to guard access to it.
1530
1635
 
1531
1636
    _tree_class = WorkingTree4
1532
1637
 
1533
 
    def get_format_string(self):
 
1638
    @classmethod
 
1639
    def get_format_string(cls):
1534
1640
        """See WorkingTreeFormat.get_format_string()."""
1535
1641
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1536
1642
 
1547
1653
 
1548
1654
    _tree_class = WorkingTree5
1549
1655
 
1550
 
    def get_format_string(self):
 
1656
    @classmethod
 
1657
    def get_format_string(cls):
1551
1658
        """See WorkingTreeFormat.get_format_string()."""
1552
1659
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1553
1660
 
1567
1674
 
1568
1675
    _tree_class = WorkingTree6
1569
1676
 
1570
 
    def get_format_string(self):
 
1677
    @classmethod
 
1678
    def get_format_string(cls):
1571
1679
        """See WorkingTreeFormat.get_format_string()."""
1572
1680
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1573
1681
 
1585
1693
    def supports_views(self):
1586
1694
        return True
1587
1695
 
1588
 
 
1589
 
class DirStateRevisionTree(Tree):
 
1696
    def _get_matchingbzrdir(self):
 
1697
        """Overrideable method to get a bzrdir for testing."""
 
1698
        # We use 'development-subtree' instead of '2a', because we have a
 
1699
        # few tests that want to test tree references
 
1700
        return bzrdir.format_registry.make_bzrdir('development-subtree')
 
1701
 
 
1702
 
 
1703
class DirStateRevisionTree(InventoryTree):
1590
1704
    """A revision tree pulling the inventory from a dirstate.
1591
1705
    
1592
1706
    Note that this is one of the historical (ie revision) trees cached in the
1611
1725
    def annotate_iter(self, file_id,
1612
1726
                      default_revision=_mod_revision.CURRENT_REVISION):
1613
1727
        """See Tree.annotate_iter"""
1614
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1728
        text_key = (file_id, self.get_file_revision(file_id))
1615
1729
        annotations = self._repository.texts.annotate(text_key)
1616
1730
        return [(key[-1], line) for (key, line) in annotations]
1617
1731
 
1618
 
    def _get_ancestors(self, default_revision):
1619
 
        return set(self._repository.get_ancestry(self._revision_id,
1620
 
                                                 topo_sorted=False))
1621
1732
    def _comparison_data(self, entry, path):
1622
1733
        """See Tree._comparison_data."""
1623
1734
        if entry is None:
1676
1787
        if path is not None:
1677
1788
            path = path.encode('utf8')
1678
1789
        parent_index = self._get_parent_index()
1679
 
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
 
1790
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id,
 
1791
            path_utf8=path)
1680
1792
 
1681
1793
    def _generate_inventory(self):
1682
1794
        """Create and set self.inventory from the dirstate object.
1739
1851
                elif kind == 'directory':
1740
1852
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1741
1853
                elif kind == 'symlink':
1742
 
                    inv_entry.executable = False
1743
 
                    inv_entry.text_size = None
1744
1854
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1745
1855
                elif kind == 'tree-reference':
1746
1856
                    inv_entry.reference_revision = fingerprint or None
1766
1876
        # Make sure the file exists
1767
1877
        entry = self._get_entry(file_id, path=path)
1768
1878
        if entry == (None, None): # do we raise?
1769
 
            return None
 
1879
            raise errors.NoSuchId(self, file_id)
1770
1880
        parent_index = self._get_parent_index()
1771
1881
        last_changed_revision = entry[1][parent_index][4]
1772
1882
        try:
1783
1893
            return parent_details[1]
1784
1894
        return None
1785
1895
 
 
1896
    @needs_read_lock
 
1897
    def get_file_revision(self, file_id):
 
1898
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1899
        return inv[inv_file_id].revision
 
1900
 
1786
1901
    def get_file(self, file_id, path=None):
1787
1902
        return StringIO(self.get_file_text(file_id))
1788
1903
 
1789
1904
    def get_file_size(self, file_id):
1790
1905
        """See Tree.get_file_size"""
1791
 
        return self.inventory[file_id].text_size
 
1906
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1907
        return inv[inv_file_id].text_size
1792
1908
 
1793
1909
    def get_file_text(self, file_id, path=None):
1794
 
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1795
 
        return ''.join(content)
 
1910
        content = None
 
1911
        for _, content_iter in self.iter_files_bytes([(file_id, None)]):
 
1912
            if content is not None:
 
1913
                raise AssertionError('iter_files_bytes returned'
 
1914
                    ' too many entries')
 
1915
            # For each entry returned by iter_files_bytes, we must consume the
 
1916
            # content_iter before we step the files iterator.
 
1917
            content = ''.join(content_iter)
 
1918
        if content is None:
 
1919
            raise AssertionError('iter_files_bytes did not return'
 
1920
                ' the requested data')
 
1921
        return content
1796
1922
 
1797
1923
    def get_reference_revision(self, file_id, path=None):
1798
 
        return self.inventory[file_id].reference_revision
 
1924
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1925
        return inv[inv_file_id].reference_revision
1799
1926
 
1800
1927
    def iter_files_bytes(self, desired_files):
1801
1928
        """See Tree.iter_files_bytes.
1811
1938
                                       identifier))
1812
1939
        return self._repository.iter_files_bytes(repo_desired_files)
1813
1940
 
1814
 
    def get_symlink_target(self, file_id):
 
1941
    def get_symlink_target(self, file_id, path=None):
1815
1942
        entry = self._get_entry(file_id=file_id)
1816
1943
        parent_index = self._get_parent_index()
1817
1944
        if entry[1][parent_index][0] != 'l':
1825
1952
        """Return the revision id for this tree."""
1826
1953
        return self._revision_id
1827
1954
 
1828
 
    def _get_inventory(self):
 
1955
    def _get_root_inventory(self):
1829
1956
        if self._inventory is not None:
1830
1957
            return self._inventory
1831
1958
        self._must_be_locked()
1832
1959
        self._generate_inventory()
1833
1960
        return self._inventory
1834
1961
 
 
1962
    root_inventory = property(_get_root_inventory,
 
1963
                         doc="Inventory of this Tree")
 
1964
 
 
1965
    @deprecated_method(deprecated_in((2, 5, 0)))
 
1966
    def _get_inventory(self):
 
1967
        return self.root_inventory
 
1968
 
1835
1969
    inventory = property(_get_inventory,
1836
1970
                         doc="Inventory of this Tree")
1837
1971
 
1855
1989
 
1856
1990
    def path_content_summary(self, path):
1857
1991
        """See Tree.path_content_summary."""
1858
 
        id = self.inventory.path2id(path)
1859
 
        if id is None:
 
1992
        inv, inv_file_id = self._path2inv_file_id(path)
 
1993
        if inv_file_id is None:
1860
1994
            return ('missing', None, None, None)
1861
 
        entry = self._inventory[id]
 
1995
        entry = inv[inv_file_id]
1862
1996
        kind = entry.kind
1863
1997
        if kind == 'file':
1864
1998
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
1868
2002
            return (kind, None, None, None)
1869
2003
 
1870
2004
    def is_executable(self, file_id, path=None):
1871
 
        ie = self.inventory[file_id]
 
2005
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2006
        ie = inv[inv_file_id]
1872
2007
        if ie.kind != "file":
1873
 
            return None
 
2008
            return False
1874
2009
        return ie.executable
1875
2010
 
1876
2011
    def is_locked(self):
1879
2014
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1880
2015
        # We use a standard implementation, because DirStateRevisionTree is
1881
2016
        # dealing with one of the parents of the current state
1882
 
        inv = self._get_inventory()
1883
2017
        if from_dir is None:
 
2018
            inv = self.root_inventory
1884
2019
            from_dir_id = None
1885
2020
        else:
1886
 
            from_dir_id = inv.path2id(from_dir)
 
2021
            inv, from_dir_id = self._path2inv_file_id(from_dir)
1887
2022
            if from_dir_id is None:
1888
2023
                # Directory not versioned
1889
2024
                return
 
2025
        # FIXME: Support nested trees
1890
2026
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1891
2027
        if inv.root is not None and not include_root and from_dir is None:
1892
2028
            entries.next()
1896
2032
    def lock_read(self):
1897
2033
        """Lock the tree for a set of operations.
1898
2034
 
1899
 
        :return: An object with an unlock method which will release the lock
1900
 
            obtained.
 
2035
        :return: A brzlib.lock.LogicalLockResult.
1901
2036
        """
1902
2037
        if not self._locked:
1903
2038
            self._repository.lock_read()
1905
2040
                self._dirstate.lock_read()
1906
2041
                self._dirstate_locked = True
1907
2042
        self._locked += 1
1908
 
        return self
 
2043
        return LogicalLockResult(self.unlock)
1909
2044
 
1910
2045
    def _must_be_locked(self):
1911
2046
        if not self._locked:
1915
2050
    def path2id(self, path):
1916
2051
        """Return the id for path in this tree."""
1917
2052
        # lookup by path: faster than splitting and walking the ivnentory.
 
2053
        if isinstance(path, list):
 
2054
            if path == []:
 
2055
                path = [""]
 
2056
            path = osutils.pathjoin(*path)
1918
2057
        entry = self._get_entry(path=path)
1919
2058
        if entry == (None, None):
1920
2059
            return None
1943
2082
        # So for now, we just build up the parent inventory, and extract
1944
2083
        # it the same way RevisionTree does.
1945
2084
        _directory = 'directory'
1946
 
        inv = self._get_inventory()
 
2085
        inv = self._get_root_inventory()
1947
2086
        top_id = inv.path2id(prefix)
1948
2087
        if top_id is None:
1949
2088
            pending = []
1990
2129
    def make_source_parent_tree(source, target):
1991
2130
        """Change the source tree into a parent of the target."""
1992
2131
        revid = source.commit('record tree')
1993
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2132
        target.branch.fetch(source.branch, revid)
1994
2133
        target.set_parent_ids([revid])
1995
2134
        return target.basis_tree(), target
1996
2135
 
2003
2142
    @classmethod
2004
2143
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2005
2144
                                                  target):
2006
 
        from bzrlib.tests.test__dirstate_helpers import \
 
2145
        from brzlib.tests.test__dirstate_helpers import \
2007
2146
            compiled_dirstate_helpers_feature
2008
2147
        test_case.requireFeature(compiled_dirstate_helpers_feature)
2009
 
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
 
2148
        from brzlib._dirstate_helpers_pyx import ProcessEntryC
2010
2149
        result = klass.make_source_parent_tree(source, target)
2011
2150
        result[1]._iter_changes = ProcessEntryC
2012
2151
        return result
2088
2227
                path_entries = state._entries_for_path(path)
2089
2228
                if not path_entries:
2090
2229
                    # this specified path is not present at all: error
2091
 
                    not_versioned.append(path)
 
2230
                    not_versioned.append(path.decode('utf-8'))
2092
2231
                    continue
2093
2232
                found_versioned = False
2094
2233
                # for each id at this path
2102
2241
                if not found_versioned:
2103
2242
                    # none of the indexes was not 'absent' at all ids for this
2104
2243
                    # path.
2105
 
                    not_versioned.append(path)
 
2244
                    not_versioned.append(path.decode('utf-8'))
2106
2245
            if len(not_versioned) > 0:
2107
2246
                raise errors.PathsNotVersionedError(not_versioned)
2108
2247
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2175
2314
    def update_format(self, tree):
2176
2315
        """Change the format marker."""
2177
2316
        tree._transport.put_bytes('format',
2178
 
            self.target_format.get_format_string(),
 
2317
            self.target_format.as_string(),
2179
2318
            mode=tree.bzrdir._get_file_mode())
2180
2319
 
2181
2320
 
2198
2337
    def update_format(self, tree):
2199
2338
        """Change the format marker."""
2200
2339
        tree._transport.put_bytes('format',
2201
 
            self.target_format.get_format_string(),
 
2340
            self.target_format.as_string(),
2202
2341
            mode=tree.bzrdir._get_file_mode())
2203
2342
 
2204
2343
 
2227
2366
    def update_format(self, tree):
2228
2367
        """Change the format marker."""
2229
2368
        tree._transport.put_bytes('format',
2230
 
            self.target_format.get_format_string(),
 
2369
            self.target_format.as_string(),
2231
2370
            mode=tree.bzrdir._get_file_mode())