~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Robert Collins
  • Author(s): Vincent Ladeuil
  • Date: 2009-04-07 07:20:17 UTC
  • mto: This revision was merged to the branch mainline in revision 4262.
  • Revision ID: robertc@robertcollins.net-20090407072017-rxvtpahno3yfb52e
Add InterCHKRevisionTree

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 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
 
1375
1301
        self.tree = tree
1376
1302
 
1377
1303
    def sha1(self, abspath):
1378
 
        """See dirstate.SHA1Provider.sha1()."""
1379
 
        filters = self.tree._content_filter_stack(
1380
 
            self.tree.relpath(osutils.safe_unicode(abspath)))
1381
 
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
 
1304
        """Return the sha1 of a file given its absolute path."""
 
1305
        filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
 
1306
        return internal_size_sha_file_byname(abspath, filters)[1]
1382
1307
 
1383
1308
    def stat_and_sha1(self, abspath):
1384
 
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1385
 
        filters = self.tree._content_filter_stack(
1386
 
            self.tree.relpath(osutils.safe_unicode(abspath)))
 
1309
        """Return the stat and sha1 of a file given its absolute path."""
 
1310
        filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1387
1311
        file_obj = file(abspath, 'rb', 65000)
1388
1312
        try:
1389
1313
            statvalue = os.fstat(file_obj.fileno())
1390
1314
            if filters:
1391
 
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
 
1315
                file_obj = filtered_input_file(file_obj, filters)
1392
1316
            sha1 = osutils.size_sha_file(file_obj)[1]
1393
1317
        finally:
1394
1318
            file_obj.close()
1395
1319
        return statvalue, sha1
1396
1320
 
1397
1321
 
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
1322
class WorkingTree4(DirStateWorkingTree):
1425
1323
    """This is the Format 4 working tree.
1426
1324
 
1427
 
    This differs from WorkingTree by:
 
1325
    This differs from WorkingTree3 by:
1428
1326
     - Having a consolidated internal dirstate, stored in a
1429
1327
       randomly-accessible sorted file on disk.
1430
1328
     - Not having a regular inventory attribute.  One can be synthesized
1434
1332
    """
1435
1333
 
1436
1334
 
1437
 
class WorkingTree5(ContentFilteringDirStateWorkingTree):
 
1335
class WorkingTree5(DirStateWorkingTree):
1438
1336
    """This is the Format 5 working tree.
1439
1337
 
1440
1338
    This differs from WorkingTree4 by:
1444
1342
    """
1445
1343
 
1446
1344
 
1447
 
class WorkingTree6(ContentFilteringDirStateWorkingTree):
 
1345
class WorkingTree6(DirStateWorkingTree):
1448
1346
    """This is the Format 6 working tree.
1449
1347
 
1450
1348
    This differs from WorkingTree5 by:
1458
1356
        return views.PathBasedViews(self)
1459
1357
 
1460
1358
 
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
 
 
 
1359
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1475
1360
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1476
1361
                   accelerator_tree=None, hardlink=False):
1477
1362
        """See WorkingTreeFormat.initialize().
1478
1363
 
1479
1364
        :param revision_id: allows creating a working tree at a different
1480
 
            revision than the branch is at.
 
1365
        revision than the branch is at.
1481
1366
        :param accelerator_tree: A tree which can be used for retrieving file
1482
1367
            contents more quickly than the revision tree, i.e. a workingtree.
1483
1368
            The revision tree will be used for cases where accelerator_tree's
1516
1401
        wt.lock_tree_write()
1517
1402
        try:
1518
1403
            self._init_custom_control_files(wt)
1519
 
            if revision_id in (None, _mod_revision.NULL_REVISION):
 
1404
            if revision_id in (None, NULL_REVISION):
1520
1405
                if branch.repository.supports_rich_root():
1521
1406
                    wt._set_root_id(generate_ids.gen_root_id())
1522
1407
                else:
1533
1418
                    pass
1534
1419
            if basis is None:
1535
1420
                basis = branch.repository.revision_tree(revision_id)
1536
 
            if revision_id == _mod_revision.NULL_REVISION:
 
1421
            if revision_id == NULL_REVISION:
1537
1422
                parents_list = []
1538
1423
            else:
1539
1424
                parents_list = [(revision_id, basis)]
1547
1432
                if basis_root_id is not None:
1548
1433
                    wt._set_root_id(basis_root_id)
1549
1434
                    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
1435
                # delta_from_tree is safe even for DirStateRevisionTrees,
1558
1436
                # because wt4.apply_inventory_delta does not mutate the input
1559
1437
                # inventory entries.
1560
1438
                transform.build_tree(basis, wt, accelerator_tree,
1561
 
                                     hardlink=hardlink,
1562
 
                                     delta_from_tree=delta_from_tree)
 
1439
                                     hardlink=hardlink, delta_from_tree=True)
1563
1440
            finally:
1564
1441
                basis.unlock()
1565
1442
        finally:
1576
1453
        :param wt: the WorkingTree object
1577
1454
        """
1578
1455
 
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
1456
    def _open(self, a_bzrdir, control_files):
1594
1457
        """Open the tree itself.
1595
1458
 
1686
1549
        return True
1687
1550
 
1688
1551
 
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
 
    """
 
1552
class DirStateRevisionTree(Tree):
 
1553
    """A revision tree pulling the inventory from a dirstate."""
1695
1554
 
1696
1555
    def __init__(self, dirstate, revision_id, repository):
1697
1556
        self._dirstate = dirstate
1711
1570
    def annotate_iter(self, file_id,
1712
1571
                      default_revision=_mod_revision.CURRENT_REVISION):
1713
1572
        """See Tree.annotate_iter"""
1714
 
        text_key = (file_id, self.get_file_revision(file_id))
 
1573
        text_key = (file_id, self.inventory[file_id].revision)
1715
1574
        annotations = self._repository.texts.annotate(text_key)
1716
1575
        return [(key[-1], line) for (key, line) in annotations]
1717
1576
 
 
1577
    def _get_ancestors(self, default_revision):
 
1578
        return set(self._repository.get_ancestry(self._revision_id,
 
1579
                                                 topo_sorted=False))
1718
1580
    def _comparison_data(self, entry, path):
1719
1581
        """See Tree._comparison_data."""
1720
1582
        if entry is None:
1836
1698
                elif kind == 'directory':
1837
1699
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1838
1700
                elif kind == 'symlink':
 
1701
                    inv_entry.executable = False
 
1702
                    inv_entry.text_size = None
1839
1703
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1840
1704
                elif kind == 'tree-reference':
1841
1705
                    inv_entry.reference_revision = fingerprint or None
1864
1728
            return None
1865
1729
        parent_index = self._get_parent_index()
1866
1730
        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
 
1731
        return self._repository.get_revision(last_changed_revision).timestamp
1872
1732
 
1873
1733
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1874
1734
        entry = self._get_entry(file_id=file_id, path=path)
1878
1738
            return parent_details[1]
1879
1739
        return None
1880
1740
 
1881
 
    @needs_read_lock
1882
 
    def get_file_revision(self, file_id):
1883
 
        return self.inventory[file_id].revision
1884
 
 
1885
1741
    def get_file(self, file_id, path=None):
1886
1742
        return StringIO(self.get_file_text(file_id))
1887
1743
 
1910
1766
                                       identifier))
1911
1767
        return self._repository.iter_files_bytes(repo_desired_files)
1912
1768
 
1913
 
    def get_symlink_target(self, file_id, path=None):
 
1769
    def get_symlink_target(self, file_id):
1914
1770
        entry = self._get_entry(file_id=file_id)
1915
1771
        parent_index = self._get_parent_index()
1916
1772
        if entry[1][parent_index][0] != 'l':
1945
1801
        entry = self._get_entry(file_id=file_id)[1]
1946
1802
        if entry is None:
1947
1803
            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]]
 
1804
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
1950
1805
 
1951
1806
    def stored_kind(self, file_id):
1952
1807
        """See Tree.stored_kind"""
1969
1824
    def is_executable(self, file_id, path=None):
1970
1825
        ie = self.inventory[file_id]
1971
1826
        if ie.kind != "file":
1972
 
            return False
 
1827
            return None
1973
1828
        return ie.executable
1974
1829
 
1975
 
    def is_locked(self):
1976
 
        return self._locked
1977
 
 
1978
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
1830
    def list_files(self, include_root=False):
1979
1831
        # We use a standard implementation, because DirStateRevisionTree is
1980
1832
        # dealing with one of the parents of the current state
1981
1833
        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:
 
1834
        entries = inv.iter_entries()
 
1835
        if self.inventory.root is not None and not include_root:
1991
1836
            entries.next()
1992
1837
        for path, entry in entries:
1993
1838
            yield path, 'V', entry.kind, entry.file_id, entry
1994
1839
 
1995
1840
    def lock_read(self):
1996
 
        """Lock the tree for a set of operations.
1997
 
 
1998
 
        :return: A bzrlib.lock.LogicalLockResult.
1999
 
        """
 
1841
        """Lock the tree for a set of operations."""
2000
1842
        if not self._locked:
2001
1843
            self._repository.lock_read()
2002
1844
            if self._dirstate._lock_token is None:
2003
1845
                self._dirstate.lock_read()
2004
1846
                self._dirstate_locked = True
2005
1847
        self._locked += 1
2006
 
        return LogicalLockResult(self.unlock)
2007
1848
 
2008
1849
    def _must_be_locked(self):
2009
1850
        if not self._locked:
2088
1929
    def make_source_parent_tree(source, target):
2089
1930
        """Change the source tree into a parent of the target."""
2090
1931
        revid = source.commit('record tree')
2091
 
        target.branch.fetch(source.branch, revid)
 
1932
        target.branch.repository.fetch(source.branch.repository, revid)
2092
1933
        target.set_parent_ids([revid])
2093
1934
        return target.basis_tree(), target
2094
1935
 
2099
1940
        return result
2100
1941
 
2101
1942
    @classmethod
2102
 
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2103
 
                                                  target):
 
1943
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2104
1944
        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
 
1945
            CompiledDirstateHelpersFeature
 
1946
        if not CompiledDirstateHelpersFeature.available():
 
1947
            from bzrlib.tests import UnavailableFeature
 
1948
            raise UnavailableFeature(CompiledDirstateHelpersFeature)
 
1949
        from bzrlib._dirstate_helpers_c import ProcessEntryC
2108
1950
        result = klass.make_source_parent_tree(source, target)
2109
1951
        result[1]._iter_changes = ProcessEntryC
2110
1952
        return result
2140
1982
            output. An unversioned file is defined as one with (False, False)
2141
1983
            for the versioned pair.
2142
1984
        """
 
1985
        # NB: show_status depends on being able to pass in non-versioned files
 
1986
        # and report them as unknown
2143
1987
        # TODO: handle extra trees in the dirstate.
2144
1988
        if (extra_trees or specific_files == []):
2145
1989
            # we can't fast-path these cases (yet)
2148
1992
                require_versioned, want_unversioned=want_unversioned)
2149
1993
        parent_ids = self.target.get_parent_ids()
2150
1994
        if not (self.source._revision_id in parent_ids
2151
 
                or self.source._revision_id == _mod_revision.NULL_REVISION):
 
1995
                or self.source._revision_id == NULL_REVISION):
2152
1996
            raise AssertionError(
2153
1997
                "revision {%s} is not stored in {%s}, but %s "
2154
1998
                "can only be used for trees stored in the dirstate"
2155
1999
                % (self.source._revision_id, self.target, self.iter_changes))
2156
2000
        target_index = 0
2157
 
        if self.source._revision_id == _mod_revision.NULL_REVISION:
 
2001
        if self.source._revision_id == NULL_REVISION:
2158
2002
            source_index = None
2159
2003
            indices = (target_index,)
2160
2004
        else:
2175
2019
        else:
2176
2020
            specific_files = set([''])
2177
2021
        # -- specific_files is now a utf8 path set --
2178
 
 
 
2022
        search_specific_files = set()
2179
2023
        # -- get the state object and prepare it.
2180
2024
        state = self.target.current_dirstate()
2181
2025
        state._read_dirblocks_if_needed()
2182
2026
        if require_versioned:
2183
2027
            # -- check all supplied paths are versioned in a search tree. --
2184
 
            not_versioned = []
 
2028
            all_versioned = True
2185
2029
            for path in specific_files:
2186
2030
                path_entries = state._entries_for_path(path)
2187
2031
                if not path_entries:
2188
2032
                    # this specified path is not present at all: error
2189
 
                    not_versioned.append(path)
2190
 
                    continue
 
2033
                    all_versioned = False
 
2034
                    break
2191
2035
                found_versioned = False
2192
2036
                # for each id at this path
2193
2037
                for entry in path_entries:
2200
2044
                if not found_versioned:
2201
2045
                    # none of the indexes was not 'absent' at all ids for this
2202
2046
                    # path.
2203
 
                    not_versioned.append(path)
2204
 
            if len(not_versioned) > 0:
2205
 
                raise errors.PathsNotVersionedError(not_versioned)
 
2047
                    all_versioned = False
 
2048
                    break
 
2049
            if not all_versioned:
 
2050
                raise errors.PathsNotVersionedError(specific_files)
2206
2051
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2207
 
        search_specific_files = osutils.minimum_path_selection(specific_files)
 
2052
        for path in specific_files:
 
2053
            other_specific_files = specific_files.difference(set([path]))
 
2054
            if not osutils.is_inside_any(other_specific_files, path):
 
2055
                # this is a top level path, we must check it.
 
2056
                search_specific_files.add(path)
2208
2057
 
2209
2058
        use_filesystem_for_exec = (sys.platform != 'win32')
2210
2059
        iter_changes = self.target._iter_changes(include_unchanged,
2222
2071
            (revisiontree.RevisionTree, DirStateRevisionTree)):
2223
2072
            return False
2224
2073
        # the source revid must be in the target dirstate
2225
 
        if not (source._revision_id == _mod_revision.NULL_REVISION or
 
2074
        if not (source._revision_id == NULL_REVISION or
2226
2075
            source._revision_id in target.get_parent_ids()):
2227
2076
            # TODO: what about ghosts? it may well need to
2228
2077
            # check for them explicitly.