~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Johan Walles
  • Date: 2009-05-07 05:08:46 UTC
  • mfrom: (4342 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4343.
  • Revision ID: johan.walles@gmail.com-20090507050846-nkwvcyauf1eh653q
Merge from upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
28
28
 
29
29
from bzrlib.lazy_import import lazy_import
30
30
lazy_import(globals(), """
 
31
from bisect import bisect_left
 
32
import collections
 
33
from copy import deepcopy
31
34
import errno
 
35
import itertools
 
36
import operator
32
37
import stat
 
38
from time import time
 
39
import warnings
33
40
 
 
41
import bzrlib
34
42
from bzrlib import (
35
43
    bzrdir,
36
44
    cache_utf8,
37
45
    conflicts as _mod_conflicts,
38
46
    debug,
 
47
    delta,
39
48
    dirstate,
40
49
    errors,
41
 
    filters as _mod_filters,
42
50
    generate_ids,
 
51
    globbing,
 
52
    ignores,
 
53
    merge,
43
54
    osutils,
44
55
    revision as _mod_revision,
45
56
    revisiontree,
 
57
    textui,
46
58
    trace,
47
59
    transform,
 
60
    urlutils,
48
61
    views,
 
62
    xml5,
 
63
    xml6,
49
64
    )
 
65
import bzrlib.branch
 
66
from bzrlib.transport import get_transport
 
67
import bzrlib.ui
50
68
""")
51
69
 
 
70
from bzrlib import symbol_versioning
52
71
from bzrlib.decorators import needs_read_lock, needs_write_lock
53
 
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
54
 
from bzrlib.lock import LogicalLockResult
55
 
from bzrlib.lockable_files import LockableFiles
56
 
from bzrlib.lockdir import LockDir
 
72
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
 
73
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
 
74
import bzrlib.mutabletree
57
75
from bzrlib.mutabletree import needs_tree_write_lock
58
76
from bzrlib.osutils import (
59
77
    file_kind,
60
78
    isdir,
 
79
    normpath,
61
80
    pathjoin,
 
81
    rand_chars,
62
82
    realpath,
63
83
    safe_unicode,
 
84
    splitpath,
64
85
    )
 
86
from bzrlib.trace import mutter, note
65
87
from bzrlib.transport.local import LocalTransport
66
 
from bzrlib.tree import (
67
 
    InterTree,
68
 
    InventoryTree,
69
 
    )
70
 
from bzrlib.workingtree import (
71
 
    InventoryWorkingTree,
72
 
    WorkingTree,
73
 
    WorkingTreeFormat,
74
 
    )
75
 
 
76
 
 
77
 
class DirStateWorkingTree(InventoryWorkingTree):
78
 
 
79
 
    _DEFAULT_WORTH_SAVING_LIMIT = 10
80
 
 
 
88
from bzrlib.tree import InterTree
 
89
from bzrlib.progress import DummyProgress, ProgressPhase
 
90
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
 
91
from bzrlib.rio import RioReader, rio_file, Stanza
 
92
from bzrlib.symbol_versioning import (deprecated_passed,
 
93
        deprecated_method,
 
94
        deprecated_function,
 
95
        DEPRECATED_PARAMETER,
 
96
        )
 
97
from bzrlib.tree import Tree
 
98
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
 
99
 
 
100
 
 
101
class DirStateWorkingTree(WorkingTree3):
81
102
    def __init__(self, basedir,
82
103
                 branch,
83
104
                 _control_files=None,
93
114
        self._format = _format
94
115
        self.bzrdir = _bzrdir
95
116
        basedir = safe_unicode(basedir)
96
 
        trace.mutter("opening working tree %r", basedir)
 
117
        mutter("opening working tree %r", basedir)
97
118
        self._branch = branch
98
119
        self.basedir = realpath(basedir)
99
120
        # if branch is at our basedir and is a format 6 or less
133
154
            state.add(f, file_id, kind, None, '')
134
155
        self._make_dirty(reset_inventory=True)
135
156
 
136
 
    def _get_check_refs(self):
137
 
        """Return the references needed to perform a check of this tree."""
138
 
        return [('trees', self.last_revision())]
139
 
 
140
157
    def _make_dirty(self, reset_inventory):
141
158
        """Make the tree state dirty.
142
159
 
194
211
 
195
212
    def _comparison_data(self, entry, path):
196
213
        kind, executable, stat_value = \
197
 
            WorkingTree._comparison_data(self, entry, path)
 
214
            WorkingTree3._comparison_data(self, entry, path)
198
215
        # it looks like a plain directory, but it's really a reference -- see
199
216
        # also kind()
200
217
        if (self._repo_supports_tree_reference and kind == 'directory'
206
223
    def commit(self, message=None, revprops=None, *args, **kwargs):
207
224
        # mark the tree as dirty post commit - commit
208
225
        # can change the current versioned list by doing deletes.
209
 
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
 
226
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
210
227
        self._make_dirty(reset_inventory=True)
211
228
        return result
212
229
 
231
248
        local_path = self.bzrdir.get_workingtree_transport(None
232
249
            ).local_abspath('dirstate')
233
250
        self._dirstate = dirstate.DirState.on_file(local_path,
234
 
            self._sha1_provider(), self._worth_saving_limit())
 
251
            self._sha1_provider())
235
252
        return self._dirstate
236
253
 
237
254
    def _sha1_provider(self):
246
263
        else:
247
264
            return None
248
265
 
249
 
    def _worth_saving_limit(self):
250
 
        """How many hash changes are ok before we must save the dirstate.
251
 
 
252
 
        :return: an integer. -1 means never save.
253
 
        """
254
 
        config = self.branch.get_config()
255
 
        val = config.get_user_option('bzr.workingtree.worth_saving_limit')
256
 
        if val is None:
257
 
            val = self._DEFAULT_WORTH_SAVING_LIMIT
258
 
        else:
259
 
            try:
260
 
                val = int(val)
261
 
            except ValueError, e:
262
 
                trace.warning('Invalid config value for'
263
 
                              ' "bzr.workingtree.worth_saving_limit"'
264
 
                              ' value %r is not an integer.'
265
 
                              % (val,))
266
 
                val = self._DEFAULT_WORTH_SAVING_LIMIT
267
 
        return val
268
 
 
269
266
    def filter_unversioned_files(self, paths):
270
267
        """Filter out paths that are versioned.
271
268
 
401
398
        state = self.current_dirstate()
402
399
        if stat_value is None:
403
400
            try:
404
 
                stat_value = osutils.lstat(file_abspath)
 
401
                stat_value = os.lstat(file_abspath)
405
402
            except OSError, e:
406
403
                if e.errno == errno.ENOENT:
407
404
                    return None
467
464
        return osutils.lexists(pathjoin(
468
465
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
469
466
 
470
 
    def has_or_had_id(self, file_id):
471
 
        state = self.current_dirstate()
472
 
        row, parents = self._get_entry(file_id=file_id)
473
 
        return row is not None
474
 
 
475
467
    @needs_read_lock
476
468
    def id2path(self, file_id):
477
469
        "Convert a file-id to a path."
510
502
            self._must_be_locked()
511
503
            if not path:
512
504
                path = self.id2path(file_id)
513
 
            mode = osutils.lstat(self.abspath(path)).st_mode
 
505
            mode = os.lstat(self.abspath(path)).st_mode
514
506
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
515
507
 
516
508
    def all_file_ids(self):
600
592
            return _mod_revision.NULL_REVISION
601
593
 
602
594
    def lock_read(self):
603
 
        """See Branch.lock_read, and WorkingTree.unlock.
604
 
 
605
 
        :return: A bzrlib.lock.LogicalLockResult.
606
 
        """
 
595
        """See Branch.lock_read, and WorkingTree.unlock."""
607
596
        self.branch.lock_read()
608
597
        try:
609
598
            self._control_files.lock_read()
622
611
        except:
623
612
            self.branch.unlock()
624
613
            raise
625
 
        return LogicalLockResult(self.unlock)
626
614
 
627
615
    def _lock_self_write(self):
628
616
        """This should be called after the branch is locked."""
643
631
        except:
644
632
            self.branch.unlock()
645
633
            raise
646
 
        return LogicalLockResult(self.unlock)
647
634
 
648
635
    def lock_tree_write(self):
649
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
650
 
 
651
 
        :return: A bzrlib.lock.LogicalLockResult.
652
 
        """
 
636
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
653
637
        self.branch.lock_read()
654
 
        return self._lock_self_write()
 
638
        self._lock_self_write()
655
639
 
656
640
    def lock_write(self):
657
 
        """See MutableTree.lock_write, and WorkingTree.unlock.
658
 
 
659
 
        :return: A bzrlib.lock.LogicalLockResult.
660
 
        """
 
641
        """See MutableTree.lock_write, and WorkingTree.unlock."""
661
642
        self.branch.lock_write()
662
 
        return self._lock_self_write()
 
643
        self._lock_self_write()
663
644
 
664
645
    @needs_tree_write_lock
665
646
    def move(self, from_paths, to_dir, after=False):
735
716
            from_entry = self._get_entry(path=from_rel)
736
717
            if from_entry == (None, None):
737
718
                raise errors.BzrMoveFailedError(from_rel,to_dir,
738
 
                    errors.NotVersionedError(path=from_rel))
 
719
                    errors.NotVersionedError(path=str(from_rel)))
739
720
 
740
721
            from_id = from_entry[0][2]
741
722
            to_rel = pathjoin(to_dir, from_tail)
882
863
                rollback_rename()
883
864
                raise
884
865
            result.append((from_rel, to_rel))
885
 
            state._mark_modified()
 
866
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
886
867
            self._make_dirty(reset_inventory=False)
887
868
 
888
869
        return result
1070
1051
    def set_last_revision(self, new_revision):
1071
1052
        """Change the last revision in the working tree."""
1072
1053
        parents = self.get_parent_ids()
1073
 
        if new_revision in (_mod_revision.NULL_REVISION, None):
 
1054
        if new_revision in (NULL_REVISION, None):
1074
1055
            if len(parents) >= 2:
1075
1056
                raise AssertionError(
1076
1057
                    "setting the last parent to none with a pending merge is "
1145
1126
                        _mod_revision.NULL_REVISION)))
1146
1127
                ghosts.append(rev_id)
1147
1128
            accepted_revisions.add(rev_id)
1148
 
        updated = False
1149
 
        if (len(real_trees) == 1
1150
 
            and not ghosts
1151
 
            and self.branch.repository._format.fast_deltas
1152
 
            and isinstance(real_trees[0][1],
1153
 
                revisiontree.InventoryRevisionTree)
1154
 
            and self.get_parent_ids()):
1155
 
            rev_id, rev_tree = real_trees[0]
1156
 
            basis_id = self.get_parent_ids()[0]
1157
 
            # There are times when basis_tree won't be in
1158
 
            # self.branch.repository, (switch, for example)
1159
 
            try:
1160
 
                basis_tree = self.branch.repository.revision_tree(basis_id)
1161
 
            except errors.NoSuchRevision:
1162
 
                # Fall back to the set_parent_trees(), since we can't use
1163
 
                # _make_delta if we can't get the RevisionTree
1164
 
                pass
1165
 
            else:
1166
 
                delta = rev_tree.inventory._make_delta(basis_tree.inventory)
1167
 
                dirstate.update_basis_by_delta(delta, rev_id)
1168
 
                updated = True
1169
 
        if not updated:
1170
 
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1129
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1171
1130
        self._make_dirty(reset_inventory=False)
1172
1131
 
1173
1132
    def _set_root_id(self, file_id):
1193
1152
 
1194
1153
    def unlock(self):
1195
1154
        """Unlock in format 4 trees needs to write the entire dirstate."""
 
1155
        # do non-implementation specific cleanup
 
1156
        self._cleanup()
 
1157
 
1196
1158
        if self._control_files._lock_count == 1:
1197
 
            # do non-implementation specific cleanup
1198
 
            self._cleanup()
1199
 
 
1200
1159
            # eventually we should do signature checking during read locks for
1201
1160
            # dirstate updates.
1202
1161
            if self._control_files._lock_mode == 'w':
1265
1224
                # just forget the whole block.
1266
1225
                entry_index = 0
1267
1226
                while entry_index < len(block[1]):
 
1227
                    # Mark this file id as having been removed
1268
1228
                    entry = block[1][entry_index]
1269
 
                    if entry[1][0][0] in 'ar':
1270
 
                        # don't remove absent or renamed entries
 
1229
                    ids_to_unversion.discard(entry[0][2])
 
1230
                    if (entry[1][0][0] in 'ar' # don't remove absent or renamed
 
1231
                                               # entries
 
1232
                        or not state._make_absent(entry)):
1271
1233
                        entry_index += 1
1272
 
                    else:
1273
 
                        # Mark this file id as having been removed
1274
 
                        ids_to_unversion.discard(entry[0][2])
1275
 
                        if not state._make_absent(entry):
1276
 
                            # The block has not shrunk.
1277
 
                            entry_index += 1
1278
1234
                # go to the next block. (At the moment we dont delete empty
1279
1235
                # dirblocks)
1280
1236
                block_index += 1
1301
1257
        # have to change the legacy inventory too.
1302
1258
        if self._inventory is not None:
1303
1259
            for file_id in file_ids:
1304
 
                if self._inventory.has_id(file_id):
1305
 
                    self._inventory.remove_recursive_id(file_id)
 
1260
                self._inventory.remove_recursive_id(file_id)
1306
1261
 
1307
1262
    @needs_tree_write_lock
1308
1263
    def rename_one(self, from_rel, to_rel, after=False):
1309
1264
        """See WorkingTree.rename_one"""
1310
1265
        self.flush()
1311
 
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
 
1266
        WorkingTree.rename_one(self, from_rel, to_rel, after)
1312
1267
 
1313
1268
    @needs_tree_write_lock
1314
1269
    def apply_inventory_delta(self, changes):
1333
1288
        if self._dirty:
1334
1289
            raise AssertionError("attempting to write an inventory when the "
1335
1290
                "dirstate is dirty will lose pending changes")
1336
 
        had_inventory = self._inventory is not None
1337
 
        # Setting self._inventory = None forces the dirstate to regenerate the
1338
 
        # working inventory. We do this because self.inventory may be inv, or
1339
 
        # may have been modified, and either case would prevent a clean delta
1340
 
        # being created.
1341
 
        self._inventory = None
1342
 
        # generate a delta,
1343
 
        delta = inv._make_delta(self.inventory)
1344
 
        # and apply it.
1345
 
        self.apply_inventory_delta(delta)
1346
 
        if had_inventory:
 
1291
        self.current_dirstate().set_state_from_inventory(inv)
 
1292
        self._make_dirty(reset_inventory=False)
 
1293
        if self._inventory is not None:
1347
1294
            self._inventory = inv
1348
1295
        self.flush()
1349
1296
 
1350
 
    @needs_tree_write_lock
1351
 
    def reset_state(self, revision_ids=None):
1352
 
        """Reset the state of the working tree.
1353
 
 
1354
 
        This does a hard-reset to a last-known-good state. This is a way to
1355
 
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
1356
 
        """
1357
 
        if revision_ids is None:
1358
 
            revision_ids = self.get_parent_ids()
1359
 
        if not revision_ids:
1360
 
            base_tree = self.branch.repository.revision_tree(
1361
 
                _mod_revision.NULL_REVISION)
1362
 
            trees = []
1363
 
        else:
1364
 
            trees = zip(revision_ids,
1365
 
                        self.branch.repository.revision_trees(revision_ids))
1366
 
            base_tree = trees[0][1]
1367
 
        state = self.current_dirstate()
1368
 
        # We don't support ghosts yet
1369
 
        state.set_state_from_scratch(base_tree.inventory, trees, [])
1370
 
 
1371
1297
 
1372
1298
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1373
1299
 
1378
1304
        """See dirstate.SHA1Provider.sha1()."""
1379
1305
        filters = self.tree._content_filter_stack(
1380
1306
            self.tree.relpath(osutils.safe_unicode(abspath)))
1381
 
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
 
1307
        return internal_size_sha_file_byname(abspath, filters)[1]
1382
1308
 
1383
1309
    def stat_and_sha1(self, abspath):
1384
1310
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1388
1314
        try:
1389
1315
            statvalue = os.fstat(file_obj.fileno())
1390
1316
            if filters:
1391
 
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
 
1317
                file_obj = filtered_input_file(file_obj, filters)
1392
1318
            sha1 = osutils.size_sha_file(file_obj)[1]
1393
1319
        finally:
1394
1320
            file_obj.close()
1395
1321
        return statvalue, sha1
1396
1322
 
1397
1323
 
1398
 
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1399
 
    """Dirstate working tree that supports content filtering.
1400
 
 
1401
 
    The dirstate holds the hash and size of the canonical form of the file, 
1402
 
    and most methods must return that.
1403
 
    """
1404
 
 
1405
 
    def _file_content_summary(self, path, stat_result):
1406
 
        # This is to support the somewhat obsolete path_content_summary method
1407
 
        # with content filtering: see
1408
 
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1409
 
        #
1410
 
        # If the dirstate cache is up to date and knows the hash and size,
1411
 
        # return that.
1412
 
        # Otherwise if there are no content filters, return the on-disk size
1413
 
        # and leave the hash blank.
1414
 
        # Otherwise, read and filter the on-disk file and use its size and
1415
 
        # hash.
1416
 
        #
1417
 
        # The dirstate doesn't store the size of the canonical form so we
1418
 
        # can't trust it for content-filtered trees.  We just return None.
1419
 
        dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1420
 
        executable = self._is_executable_from_path_and_stat(path, stat_result)
1421
 
        return ('file', None, executable, dirstate_sha1)
1422
 
 
1423
 
 
1424
1324
class WorkingTree4(DirStateWorkingTree):
1425
1325
    """This is the Format 4 working tree.
1426
1326
 
1427
 
    This differs from WorkingTree by:
 
1327
    This differs from WorkingTree3 by:
1428
1328
     - Having a consolidated internal dirstate, stored in a
1429
1329
       randomly-accessible sorted file on disk.
1430
1330
     - Not having a regular inventory attribute.  One can be synthesized
1434
1334
    """
1435
1335
 
1436
1336
 
1437
 
class WorkingTree5(ContentFilteringDirStateWorkingTree):
 
1337
class WorkingTree5(DirStateWorkingTree):
1438
1338
    """This is the Format 5 working tree.
1439
1339
 
1440
1340
    This differs from WorkingTree4 by:
1444
1344
    """
1445
1345
 
1446
1346
 
1447
 
class WorkingTree6(ContentFilteringDirStateWorkingTree):
 
1347
class WorkingTree6(DirStateWorkingTree):
1448
1348
    """This is the Format 6 working tree.
1449
1349
 
1450
1350
    This differs from WorkingTree5 by:
1458
1358
        return views.PathBasedViews(self)
1459
1359
 
1460
1360
 
1461
 
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1462
 
 
1463
 
    missing_parent_conflicts = True
1464
 
 
1465
 
    supports_versioned_directories = True
1466
 
 
1467
 
    _lock_class = LockDir
1468
 
    _lock_file_name = 'lock'
1469
 
 
1470
 
    def _open_control_files(self, a_bzrdir):
1471
 
        transport = a_bzrdir.get_workingtree_transport(None)
1472
 
        return LockableFiles(transport, self._lock_file_name,
1473
 
                             self._lock_class)
1474
 
 
 
1361
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1475
1362
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1476
1363
                   accelerator_tree=None, hardlink=False):
1477
1364
        """See WorkingTreeFormat.initialize().
1478
1365
 
1479
1366
        :param revision_id: allows creating a working tree at a different
1480
 
            revision than the branch is at.
 
1367
        revision than the branch is at.
1481
1368
        :param accelerator_tree: A tree which can be used for retrieving file
1482
1369
            contents more quickly than the revision tree, i.e. a workingtree.
1483
1370
            The revision tree will be used for cases where accelerator_tree's
1516
1403
        wt.lock_tree_write()
1517
1404
        try:
1518
1405
            self._init_custom_control_files(wt)
1519
 
            if revision_id in (None, _mod_revision.NULL_REVISION):
 
1406
            if revision_id in (None, NULL_REVISION):
1520
1407
                if branch.repository.supports_rich_root():
1521
1408
                    wt._set_root_id(generate_ids.gen_root_id())
1522
1409
                else:
1533
1420
                    pass
1534
1421
            if basis is None:
1535
1422
                basis = branch.repository.revision_tree(revision_id)
1536
 
            if revision_id == _mod_revision.NULL_REVISION:
 
1423
            if revision_id == NULL_REVISION:
1537
1424
                parents_list = []
1538
1425
            else:
1539
1426
                parents_list = [(revision_id, basis)]
1547
1434
                if basis_root_id is not None:
1548
1435
                    wt._set_root_id(basis_root_id)
1549
1436
                    wt.flush()
1550
 
                if wt.supports_content_filtering():
1551
 
                    # The original tree may not have the same content filters
1552
 
                    # applied so we can't safely build the inventory delta from
1553
 
                    # the source tree.
1554
 
                    delta_from_tree = False
1555
 
                else:
1556
 
                    delta_from_tree = True
1557
1437
                # delta_from_tree is safe even for DirStateRevisionTrees,
1558
1438
                # because wt4.apply_inventory_delta does not mutate the input
1559
1439
                # inventory entries.
1560
1440
                transform.build_tree(basis, wt, accelerator_tree,
1561
 
                                     hardlink=hardlink,
1562
 
                                     delta_from_tree=delta_from_tree)
 
1441
                                     hardlink=hardlink, delta_from_tree=True)
1563
1442
            finally:
1564
1443
                basis.unlock()
1565
1444
        finally:
1576
1455
        :param wt: the WorkingTree object
1577
1456
        """
1578
1457
 
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
 
 
1593
1458
    def _open(self, a_bzrdir, control_files):
1594
1459
        """Open the tree itself.
1595
1460
 
1686
1551
        return True
1687
1552
 
1688
1553
 
1689
 
class DirStateRevisionTree(InventoryTree):
1690
 
    """A revision tree pulling the inventory from a dirstate.
1691
 
    
1692
 
    Note that this is one of the historical (ie revision) trees cached in the
1693
 
    dirstate for easy access, not the workingtree.
1694
 
    """
 
1554
class DirStateRevisionTree(Tree):
 
1555
    """A revision tree pulling the inventory from a dirstate."""
1695
1556
 
1696
1557
    def __init__(self, dirstate, revision_id, repository):
1697
1558
        self._dirstate = dirstate
1711
1572
    def annotate_iter(self, file_id,
1712
1573
                      default_revision=_mod_revision.CURRENT_REVISION):
1713
1574
        """See Tree.annotate_iter"""
1714
 
        text_key = (file_id, self.get_file_revision(file_id))
 
1575
        text_key = (file_id, self.inventory[file_id].revision)
1715
1576
        annotations = self._repository.texts.annotate(text_key)
1716
1577
        return [(key[-1], line) for (key, line) in annotations]
1717
1578
 
 
1579
    def _get_ancestors(self, default_revision):
 
1580
        return set(self._repository.get_ancestry(self._revision_id,
 
1581
                                                 topo_sorted=False))
1718
1582
    def _comparison_data(self, entry, path):
1719
1583
        """See Tree._comparison_data."""
1720
1584
        if entry is None:
1836
1700
                elif kind == 'directory':
1837
1701
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1838
1702
                elif kind == 'symlink':
 
1703
                    inv_entry.executable = False
 
1704
                    inv_entry.text_size = None
1839
1705
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1840
1706
                elif kind == 'tree-reference':
1841
1707
                    inv_entry.reference_revision = fingerprint or None
1864
1730
            return None
1865
1731
        parent_index = self._get_parent_index()
1866
1732
        last_changed_revision = entry[1][parent_index][4]
1867
 
        try:
1868
 
            rev = self._repository.get_revision(last_changed_revision)
1869
 
        except errors.NoSuchRevision:
1870
 
            raise errors.FileTimestampUnavailable(self.id2path(file_id))
1871
 
        return rev.timestamp
 
1733
        return self._repository.get_revision(last_changed_revision).timestamp
1872
1734
 
1873
1735
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1874
1736
        entry = self._get_entry(file_id=file_id, path=path)
1878
1740
            return parent_details[1]
1879
1741
        return None
1880
1742
 
1881
 
    @needs_read_lock
1882
 
    def get_file_revision(self, file_id):
1883
 
        return self.inventory[file_id].revision
1884
 
 
1885
1743
    def get_file(self, file_id, path=None):
1886
1744
        return StringIO(self.get_file_text(file_id))
1887
1745
 
1910
1768
                                       identifier))
1911
1769
        return self._repository.iter_files_bytes(repo_desired_files)
1912
1770
 
1913
 
    def get_symlink_target(self, file_id, path=None):
 
1771
    def get_symlink_target(self, file_id):
1914
1772
        entry = self._get_entry(file_id=file_id)
1915
1773
        parent_index = self._get_parent_index()
1916
1774
        if entry[1][parent_index][0] != 'l':
1945
1803
        entry = self._get_entry(file_id=file_id)[1]
1946
1804
        if entry is None:
1947
1805
            raise errors.NoSuchId(tree=self, file_id=file_id)
1948
 
        parent_index = self._get_parent_index()
1949
 
        return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
 
1806
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
1950
1807
 
1951
1808
    def stored_kind(self, file_id):
1952
1809
        """See Tree.stored_kind"""
1969
1826
    def is_executable(self, file_id, path=None):
1970
1827
        ie = self.inventory[file_id]
1971
1828
        if ie.kind != "file":
1972
 
            return False
 
1829
            return None
1973
1830
        return ie.executable
1974
1831
 
1975
 
    def is_locked(self):
1976
 
        return self._locked
1977
 
 
1978
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
1832
    def list_files(self, include_root=False):
1979
1833
        # We use a standard implementation, because DirStateRevisionTree is
1980
1834
        # dealing with one of the parents of the current state
1981
1835
        inv = self._get_inventory()
1982
 
        if from_dir is None:
1983
 
            from_dir_id = None
1984
 
        else:
1985
 
            from_dir_id = inv.path2id(from_dir)
1986
 
            if from_dir_id is None:
1987
 
                # Directory not versioned
1988
 
                return
1989
 
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1990
 
        if inv.root is not None and not include_root and from_dir is None:
 
1836
        entries = inv.iter_entries()
 
1837
        if self.inventory.root is not None and not include_root:
1991
1838
            entries.next()
1992
1839
        for path, entry in entries:
1993
1840
            yield path, 'V', entry.kind, entry.file_id, entry
1994
1841
 
1995
1842
    def lock_read(self):
1996
 
        """Lock the tree for a set of operations.
1997
 
 
1998
 
        :return: A bzrlib.lock.LogicalLockResult.
1999
 
        """
 
1843
        """Lock the tree for a set of operations."""
2000
1844
        if not self._locked:
2001
1845
            self._repository.lock_read()
2002
1846
            if self._dirstate._lock_token is None:
2003
1847
                self._dirstate.lock_read()
2004
1848
                self._dirstate_locked = True
2005
1849
        self._locked += 1
2006
 
        return LogicalLockResult(self.unlock)
2007
1850
 
2008
1851
    def _must_be_locked(self):
2009
1852
        if not self._locked:
2088
1931
    def make_source_parent_tree(source, target):
2089
1932
        """Change the source tree into a parent of the target."""
2090
1933
        revid = source.commit('record tree')
2091
 
        target.branch.fetch(source.branch, revid)
 
1934
        target.branch.repository.fetch(source.branch.repository, revid)
2092
1935
        target.set_parent_ids([revid])
2093
1936
        return target.basis_tree(), target
2094
1937
 
2099
1942
        return result
2100
1943
 
2101
1944
    @classmethod
2102
 
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2103
 
                                                  target):
 
1945
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2104
1946
        from bzrlib.tests.test__dirstate_helpers import \
2105
 
            compiled_dirstate_helpers_feature
2106
 
        test_case.requireFeature(compiled_dirstate_helpers_feature)
2107
 
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
 
1947
            CompiledDirstateHelpersFeature
 
1948
        if not CompiledDirstateHelpersFeature.available():
 
1949
            from bzrlib.tests import UnavailableFeature
 
1950
            raise UnavailableFeature(CompiledDirstateHelpersFeature)
 
1951
        from bzrlib._dirstate_helpers_c import ProcessEntryC
2108
1952
        result = klass.make_source_parent_tree(source, target)
2109
1953
        result[1]._iter_changes = ProcessEntryC
2110
1954
        return result
2140
1984
            output. An unversioned file is defined as one with (False, False)
2141
1985
            for the versioned pair.
2142
1986
        """
 
1987
        # NB: show_status depends on being able to pass in non-versioned files
 
1988
        # and report them as unknown
2143
1989
        # TODO: handle extra trees in the dirstate.
2144
1990
        if (extra_trees or specific_files == []):
2145
1991
            # we can't fast-path these cases (yet)
2148
1994
                require_versioned, want_unversioned=want_unversioned)
2149
1995
        parent_ids = self.target.get_parent_ids()
2150
1996
        if not (self.source._revision_id in parent_ids
2151
 
                or self.source._revision_id == _mod_revision.NULL_REVISION):
 
1997
                or self.source._revision_id == NULL_REVISION):
2152
1998
            raise AssertionError(
2153
1999
                "revision {%s} is not stored in {%s}, but %s "
2154
2000
                "can only be used for trees stored in the dirstate"
2155
2001
                % (self.source._revision_id, self.target, self.iter_changes))
2156
2002
        target_index = 0
2157
 
        if self.source._revision_id == _mod_revision.NULL_REVISION:
 
2003
        if self.source._revision_id == NULL_REVISION:
2158
2004
            source_index = None
2159
2005
            indices = (target_index,)
2160
2006
        else:
2181
2027
        state._read_dirblocks_if_needed()
2182
2028
        if require_versioned:
2183
2029
            # -- check all supplied paths are versioned in a search tree. --
2184
 
            not_versioned = []
 
2030
            all_versioned = True
2185
2031
            for path in specific_files:
2186
2032
                path_entries = state._entries_for_path(path)
2187
2033
                if not path_entries:
2188
2034
                    # this specified path is not present at all: error
2189
 
                    not_versioned.append(path)
2190
 
                    continue
 
2035
                    all_versioned = False
 
2036
                    break
2191
2037
                found_versioned = False
2192
2038
                # for each id at this path
2193
2039
                for entry in path_entries:
2200
2046
                if not found_versioned:
2201
2047
                    # none of the indexes was not 'absent' at all ids for this
2202
2048
                    # path.
2203
 
                    not_versioned.append(path)
2204
 
            if len(not_versioned) > 0:
2205
 
                raise errors.PathsNotVersionedError(not_versioned)
 
2049
                    all_versioned = False
 
2050
                    break
 
2051
            if not all_versioned:
 
2052
                raise errors.PathsNotVersionedError(specific_files)
2206
2053
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2207
2054
        search_specific_files = osutils.minimum_path_selection(specific_files)
2208
2055
 
2222
2069
            (revisiontree.RevisionTree, DirStateRevisionTree)):
2223
2070
            return False
2224
2071
        # the source revid must be in the target dirstate
2225
 
        if not (source._revision_id == _mod_revision.NULL_REVISION or
 
2072
        if not (source._revision_id == NULL_REVISION or
2226
2073
            source._revision_id in target.get_parent_ids()):
2227
2074
            # TODO: what about ghosts? it may well need to
2228
2075
            # check for them explicitly.