~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Samuel Bronson
  • Date: 2009-07-14 02:15:03 UTC
  • mto: (4547.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4548.
  • Revision ID: naesten@gmail.com-20090714021503-t8g55xpm2tp53fhs
Clarify comment about bzrlib.foreign.VcsMapping's variable revid_prefix.

Remove the oxymoron "native foreign revisions".

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
31
31
import errno
32
32
import stat
33
33
 
 
34
import bzrlib
34
35
from bzrlib import (
35
36
    bzrdir,
36
37
    cache_utf8,
37
 
    config,
38
 
    conflicts as _mod_conflicts,
39
38
    debug,
40
39
    dirstate,
41
40
    errors,
42
 
    filters as _mod_filters,
43
41
    generate_ids,
44
42
    osutils,
45
43
    revision as _mod_revision,
48
46
    transform,
49
47
    views,
50
48
    )
 
49
import bzrlib.branch
 
50
import bzrlib.ui
51
51
""")
52
52
 
53
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
54
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
54
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
55
 
from bzrlib.lock import LogicalLockResult
56
 
from bzrlib.lockable_files import LockableFiles
57
 
from bzrlib.lockdir import LockDir
 
56
import bzrlib.mutabletree
58
57
from bzrlib.mutabletree import needs_tree_write_lock
59
58
from bzrlib.osutils import (
60
59
    file_kind,
63
62
    realpath,
64
63
    safe_unicode,
65
64
    )
 
65
from bzrlib.trace import mutter
66
66
from bzrlib.transport.local import LocalTransport
67
 
from bzrlib.tree import (
68
 
    InterTree,
69
 
    InventoryTree,
70
 
    )
71
 
from bzrlib.workingtree import (
72
 
    InventoryWorkingTree,
73
 
    WorkingTree,
74
 
    WorkingTreeFormat,
75
 
    )
76
 
 
77
 
 
78
 
class DirStateWorkingTree(InventoryWorkingTree):
79
 
 
 
67
from bzrlib.tree import InterTree
 
68
from bzrlib.tree import Tree
 
69
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
 
70
 
 
71
 
 
72
class DirStateWorkingTree(WorkingTree3):
80
73
    def __init__(self, basedir,
81
74
                 branch,
82
75
                 _control_files=None,
92
85
        self._format = _format
93
86
        self.bzrdir = _bzrdir
94
87
        basedir = safe_unicode(basedir)
95
 
        trace.mutter("opening working tree %r", basedir)
 
88
        mutter("opening working tree %r", basedir)
96
89
        self._branch = branch
97
90
        self.basedir = realpath(basedir)
98
91
        # if branch is at our basedir and is a format 6 or less
132
125
            state.add(f, file_id, kind, None, '')
133
126
        self._make_dirty(reset_inventory=True)
134
127
 
135
 
    def _get_check_refs(self):
136
 
        """Return the references needed to perform a check of this tree."""
137
 
        return [('trees', self.last_revision())]
138
 
 
139
128
    def _make_dirty(self, reset_inventory):
140
129
        """Make the tree state dirty.
141
130
 
193
182
 
194
183
    def _comparison_data(self, entry, path):
195
184
        kind, executable, stat_value = \
196
 
            WorkingTree._comparison_data(self, entry, path)
 
185
            WorkingTree3._comparison_data(self, entry, path)
197
186
        # it looks like a plain directory, but it's really a reference -- see
198
187
        # also kind()
199
188
        if (self._repo_supports_tree_reference and kind == 'directory'
205
194
    def commit(self, message=None, revprops=None, *args, **kwargs):
206
195
        # mark the tree as dirty post commit - commit
207
196
        # can change the current versioned list by doing deletes.
208
 
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
 
197
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
209
198
        self._make_dirty(reset_inventory=True)
210
199
        return result
211
200
 
230
219
        local_path = self.bzrdir.get_workingtree_transport(None
231
220
            ).local_abspath('dirstate')
232
221
        self._dirstate = dirstate.DirState.on_file(local_path,
233
 
            self._sha1_provider(), self._worth_saving_limit())
 
222
            self._sha1_provider())
234
223
        return self._dirstate
235
224
 
236
225
    def _sha1_provider(self):
245
234
        else:
246
235
            return None
247
236
 
248
 
    def _worth_saving_limit(self):
249
 
        """How many hash changes are ok before we must save the dirstate.
250
 
 
251
 
        :return: an integer. -1 means never save.
252
 
        """
253
 
        # FIXME: We want a WorkingTreeStack here -- vila 20110812
254
 
        conf = config.BranchStack(self.branch)
255
 
        return conf.get('bzr.workingtree.worth_saving_limit')
256
 
 
257
237
    def filter_unversioned_files(self, paths):
258
238
        """Filter out paths that are versioned.
259
239
 
389
369
        state = self.current_dirstate()
390
370
        if stat_value is None:
391
371
            try:
392
 
                stat_value = osutils.lstat(file_abspath)
 
372
                stat_value = os.lstat(file_abspath)
393
373
            except OSError, e:
394
374
                if e.errno == errno.ENOENT:
395
375
                    return None
455
435
        return osutils.lexists(pathjoin(
456
436
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
457
437
 
458
 
    def has_or_had_id(self, file_id):
459
 
        state = self.current_dirstate()
460
 
        row, parents = self._get_entry(file_id=file_id)
461
 
        return row is not None
462
 
 
463
438
    @needs_read_lock
464
439
    def id2path(self, file_id):
465
440
        "Convert a file-id to a path."
498
473
            self._must_be_locked()
499
474
            if not path:
500
475
                path = self.id2path(file_id)
501
 
            mode = osutils.lstat(self.abspath(path)).st_mode
 
476
            mode = os.lstat(self.abspath(path)).st_mode
502
477
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
503
478
 
504
479
    def all_file_ids(self):
588
563
            return _mod_revision.NULL_REVISION
589
564
 
590
565
    def lock_read(self):
591
 
        """See Branch.lock_read, and WorkingTree.unlock.
592
 
 
593
 
        :return: A bzrlib.lock.LogicalLockResult.
594
 
        """
 
566
        """See Branch.lock_read, and WorkingTree.unlock."""
595
567
        self.branch.lock_read()
596
568
        try:
597
569
            self._control_files.lock_read()
610
582
        except:
611
583
            self.branch.unlock()
612
584
            raise
613
 
        return LogicalLockResult(self.unlock)
614
585
 
615
586
    def _lock_self_write(self):
616
587
        """This should be called after the branch is locked."""
631
602
        except:
632
603
            self.branch.unlock()
633
604
            raise
634
 
        return LogicalLockResult(self.unlock)
635
605
 
636
606
    def lock_tree_write(self):
637
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
638
 
 
639
 
        :return: A bzrlib.lock.LogicalLockResult.
640
 
        """
 
607
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
641
608
        self.branch.lock_read()
642
 
        return self._lock_self_write()
 
609
        self._lock_self_write()
643
610
 
644
611
    def lock_write(self):
645
 
        """See MutableTree.lock_write, and WorkingTree.unlock.
646
 
 
647
 
        :return: A bzrlib.lock.LogicalLockResult.
648
 
        """
 
612
        """See MutableTree.lock_write, and WorkingTree.unlock."""
649
613
        self.branch.lock_write()
650
 
        return self._lock_self_write()
 
614
        self._lock_self_write()
651
615
 
652
616
    @needs_tree_write_lock
653
617
    def move(self, from_paths, to_dir, after=False):
870
834
                rollback_rename()
871
835
                raise
872
836
            result.append((from_rel, to_rel))
873
 
            state._mark_modified()
 
837
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
874
838
            self._make_dirty(reset_inventory=False)
875
839
 
876
840
        return result
1133
1097
                        _mod_revision.NULL_REVISION)))
1134
1098
                ghosts.append(rev_id)
1135
1099
            accepted_revisions.add(rev_id)
1136
 
        updated = False
1137
 
        if (len(real_trees) == 1
1138
 
            and not ghosts
1139
 
            and self.branch.repository._format.fast_deltas
1140
 
            and isinstance(real_trees[0][1],
1141
 
                revisiontree.InventoryRevisionTree)
1142
 
            and self.get_parent_ids()):
1143
 
            rev_id, rev_tree = real_trees[0]
1144
 
            basis_id = self.get_parent_ids()[0]
1145
 
            # There are times when basis_tree won't be in
1146
 
            # self.branch.repository, (switch, for example)
1147
 
            try:
1148
 
                basis_tree = self.branch.repository.revision_tree(basis_id)
1149
 
            except errors.NoSuchRevision:
1150
 
                # Fall back to the set_parent_trees(), since we can't use
1151
 
                # _make_delta if we can't get the RevisionTree
1152
 
                pass
1153
 
            else:
1154
 
                delta = rev_tree.inventory._make_delta(basis_tree.inventory)
1155
 
                dirstate.update_basis_by_delta(delta, rev_id)
1156
 
                updated = True
1157
 
        if not updated:
1158
 
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1100
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1159
1101
        self._make_dirty(reset_inventory=False)
1160
1102
 
1161
1103
    def _set_root_id(self, file_id):
1181
1123
 
1182
1124
    def unlock(self):
1183
1125
        """Unlock in format 4 trees needs to write the entire dirstate."""
 
1126
        # do non-implementation specific cleanup
 
1127
        self._cleanup()
 
1128
 
1184
1129
        if self._control_files._lock_count == 1:
1185
 
            # do non-implementation specific cleanup
1186
 
            self._cleanup()
1187
 
 
1188
1130
            # eventually we should do signature checking during read locks for
1189
1131
            # dirstate updates.
1190
1132
            if self._control_files._lock_mode == 'w':
1253
1195
                # just forget the whole block.
1254
1196
                entry_index = 0
1255
1197
                while entry_index < len(block[1]):
 
1198
                    # Mark this file id as having been removed
1256
1199
                    entry = block[1][entry_index]
1257
 
                    if entry[1][0][0] in 'ar':
1258
 
                        # don't remove absent or renamed entries
 
1200
                    ids_to_unversion.discard(entry[0][2])
 
1201
                    if (entry[1][0][0] in 'ar' # don't remove absent or renamed
 
1202
                                               # entries
 
1203
                        or not state._make_absent(entry)):
1259
1204
                        entry_index += 1
1260
 
                    else:
1261
 
                        # Mark this file id as having been removed
1262
 
                        ids_to_unversion.discard(entry[0][2])
1263
 
                        if not state._make_absent(entry):
1264
 
                            # The block has not shrunk.
1265
 
                            entry_index += 1
1266
1205
                # go to the next block. (At the moment we dont delete empty
1267
1206
                # dirblocks)
1268
1207
                block_index += 1
1289
1228
        # have to change the legacy inventory too.
1290
1229
        if self._inventory is not None:
1291
1230
            for file_id in file_ids:
1292
 
                if self._inventory.has_id(file_id):
1293
 
                    self._inventory.remove_recursive_id(file_id)
 
1231
                self._inventory.remove_recursive_id(file_id)
1294
1232
 
1295
1233
    @needs_tree_write_lock
1296
1234
    def rename_one(self, from_rel, to_rel, after=False):
1297
1235
        """See WorkingTree.rename_one"""
1298
1236
        self.flush()
1299
 
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
 
1237
        WorkingTree.rename_one(self, from_rel, to_rel, after)
1300
1238
 
1301
1239
    @needs_tree_write_lock
1302
1240
    def apply_inventory_delta(self, changes):
1321
1259
        if self._dirty:
1322
1260
            raise AssertionError("attempting to write an inventory when the "
1323
1261
                "dirstate is dirty will lose pending changes")
1324
 
        had_inventory = self._inventory is not None
1325
 
        # Setting self._inventory = None forces the dirstate to regenerate the
1326
 
        # working inventory. We do this because self.inventory may be inv, or
1327
 
        # may have been modified, and either case would prevent a clean delta
1328
 
        # being created.
1329
 
        self._inventory = None
1330
 
        # generate a delta,
1331
 
        delta = inv._make_delta(self.inventory)
1332
 
        # and apply it.
1333
 
        self.apply_inventory_delta(delta)
1334
 
        if had_inventory:
 
1262
        self.current_dirstate().set_state_from_inventory(inv)
 
1263
        self._make_dirty(reset_inventory=False)
 
1264
        if self._inventory is not None:
1335
1265
            self._inventory = inv
1336
1266
        self.flush()
1337
1267
 
1338
 
    @needs_tree_write_lock
1339
 
    def reset_state(self, revision_ids=None):
1340
 
        """Reset the state of the working tree.
1341
 
 
1342
 
        This does a hard-reset to a last-known-good state. This is a way to
1343
 
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
1344
 
        """
1345
 
        if revision_ids is None:
1346
 
            revision_ids = self.get_parent_ids()
1347
 
        if not revision_ids:
1348
 
            base_tree = self.branch.repository.revision_tree(
1349
 
                _mod_revision.NULL_REVISION)
1350
 
            trees = []
1351
 
        else:
1352
 
            trees = zip(revision_ids,
1353
 
                        self.branch.repository.revision_trees(revision_ids))
1354
 
            base_tree = trees[0][1]
1355
 
        state = self.current_dirstate()
1356
 
        # We don't support ghosts yet
1357
 
        state.set_state_from_scratch(base_tree.inventory, trees, [])
1358
 
 
1359
1268
 
1360
1269
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1361
1270
 
1366
1275
        """See dirstate.SHA1Provider.sha1()."""
1367
1276
        filters = self.tree._content_filter_stack(
1368
1277
            self.tree.relpath(osutils.safe_unicode(abspath)))
1369
 
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
 
1278
        return internal_size_sha_file_byname(abspath, filters)[1]
1370
1279
 
1371
1280
    def stat_and_sha1(self, abspath):
1372
1281
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1376
1285
        try:
1377
1286
            statvalue = os.fstat(file_obj.fileno())
1378
1287
            if filters:
1379
 
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
 
1288
                file_obj = filtered_input_file(file_obj, filters)
1380
1289
            sha1 = osutils.size_sha_file(file_obj)[1]
1381
1290
        finally:
1382
1291
            file_obj.close()
1383
1292
        return statvalue, sha1
1384
1293
 
1385
1294
 
1386
 
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1387
 
    """Dirstate working tree that supports content filtering.
1388
 
 
1389
 
    The dirstate holds the hash and size of the canonical form of the file, 
1390
 
    and most methods must return that.
1391
 
    """
1392
 
 
1393
 
    def _file_content_summary(self, path, stat_result):
1394
 
        # This is to support the somewhat obsolete path_content_summary method
1395
 
        # with content filtering: see
1396
 
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1397
 
        #
1398
 
        # If the dirstate cache is up to date and knows the hash and size,
1399
 
        # return that.
1400
 
        # Otherwise if there are no content filters, return the on-disk size
1401
 
        # and leave the hash blank.
1402
 
        # Otherwise, read and filter the on-disk file and use its size and
1403
 
        # hash.
1404
 
        #
1405
 
        # The dirstate doesn't store the size of the canonical form so we
1406
 
        # can't trust it for content-filtered trees.  We just return None.
1407
 
        dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1408
 
        executable = self._is_executable_from_path_and_stat(path, stat_result)
1409
 
        return ('file', None, executable, dirstate_sha1)
1410
 
 
1411
 
 
1412
1295
class WorkingTree4(DirStateWorkingTree):
1413
1296
    """This is the Format 4 working tree.
1414
1297
 
1415
 
    This differs from WorkingTree by:
 
1298
    This differs from WorkingTree3 by:
1416
1299
     - Having a consolidated internal dirstate, stored in a
1417
1300
       randomly-accessible sorted file on disk.
1418
1301
     - Not having a regular inventory attribute.  One can be synthesized
1422
1305
    """
1423
1306
 
1424
1307
 
1425
 
class WorkingTree5(ContentFilteringDirStateWorkingTree):
 
1308
class WorkingTree5(DirStateWorkingTree):
1426
1309
    """This is the Format 5 working tree.
1427
1310
 
1428
1311
    This differs from WorkingTree4 by:
1432
1315
    """
1433
1316
 
1434
1317
 
1435
 
class WorkingTree6(ContentFilteringDirStateWorkingTree):
 
1318
class WorkingTree6(DirStateWorkingTree):
1436
1319
    """This is the Format 6 working tree.
1437
1320
 
1438
1321
    This differs from WorkingTree5 by:
1446
1329
        return views.PathBasedViews(self)
1447
1330
 
1448
1331
 
1449
 
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1450
 
 
1451
 
    missing_parent_conflicts = True
1452
 
 
1453
 
    supports_versioned_directories = True
1454
 
 
1455
 
    _lock_class = LockDir
1456
 
    _lock_file_name = 'lock'
1457
 
 
1458
 
    def _open_control_files(self, a_bzrdir):
1459
 
        transport = a_bzrdir.get_workingtree_transport(None)
1460
 
        return LockableFiles(transport, self._lock_file_name,
1461
 
                             self._lock_class)
1462
 
 
 
1332
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1463
1333
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1464
1334
                   accelerator_tree=None, hardlink=False):
1465
1335
        """See WorkingTreeFormat.initialize().
1466
1336
 
1467
1337
        :param revision_id: allows creating a working tree at a different
1468
 
            revision than the branch is at.
 
1338
        revision than the branch is at.
1469
1339
        :param accelerator_tree: A tree which can be used for retrieving file
1470
1340
            contents more quickly than the revision tree, i.e. a workingtree.
1471
1341
            The revision tree will be used for cases where accelerator_tree's
1535
1405
                if basis_root_id is not None:
1536
1406
                    wt._set_root_id(basis_root_id)
1537
1407
                    wt.flush()
 
1408
                # If content filtering is supported, do not use the accelerator
 
1409
                # tree - the cost of transforming the content both ways and
 
1410
                # checking for changed content can outweight the gains it gives.
 
1411
                # Note: do NOT move this logic up higher - using the basis from
 
1412
                # the accelerator tree is still desirable because that can save
 
1413
                # a minute or more of processing on large trees!
 
1414
                # The original tree may not have the same content filters
 
1415
                # applied so we can't safely build the inventory delta from
 
1416
                # the source tree.
1538
1417
                if wt.supports_content_filtering():
1539
 
                    # The original tree may not have the same content filters
1540
 
                    # applied so we can't safely build the inventory delta from
1541
 
                    # the source tree.
 
1418
                    accelerator_tree = None
1542
1419
                    delta_from_tree = False
1543
1420
                else:
1544
1421
                    delta_from_tree = True
1564
1441
        :param wt: the WorkingTree object
1565
1442
        """
1566
1443
 
1567
 
    def open(self, a_bzrdir, _found=False):
1568
 
        """Return the WorkingTree object for a_bzrdir
1569
 
 
1570
 
        _found is a private parameter, do not use it. It is used to indicate
1571
 
               if format probing has already been done.
1572
 
        """
1573
 
        if not _found:
1574
 
            # we are being called directly and must probe.
1575
 
            raise NotImplementedError
1576
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
1577
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1578
 
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1579
 
        return wt
1580
 
 
1581
1444
    def _open(self, a_bzrdir, control_files):
1582
1445
        """Open the tree itself.
1583
1446
 
1674
1537
        return True
1675
1538
 
1676
1539
 
1677
 
class DirStateRevisionTree(InventoryTree):
1678
 
    """A revision tree pulling the inventory from a dirstate.
1679
 
    
1680
 
    Note that this is one of the historical (ie revision) trees cached in the
1681
 
    dirstate for easy access, not the workingtree.
1682
 
    """
 
1540
class DirStateRevisionTree(Tree):
 
1541
    """A revision tree pulling the inventory from a dirstate."""
1683
1542
 
1684
1543
    def __init__(self, dirstate, revision_id, repository):
1685
1544
        self._dirstate = dirstate
1699
1558
    def annotate_iter(self, file_id,
1700
1559
                      default_revision=_mod_revision.CURRENT_REVISION):
1701
1560
        """See Tree.annotate_iter"""
1702
 
        text_key = (file_id, self.get_file_revision(file_id))
 
1561
        text_key = (file_id, self.inventory[file_id].revision)
1703
1562
        annotations = self._repository.texts.annotate(text_key)
1704
1563
        return [(key[-1], line) for (key, line) in annotations]
1705
1564
 
 
1565
    def _get_ancestors(self, default_revision):
 
1566
        return set(self._repository.get_ancestry(self._revision_id,
 
1567
                                                 topo_sorted=False))
1706
1568
    def _comparison_data(self, entry, path):
1707
1569
        """See Tree._comparison_data."""
1708
1570
        if entry is None:
1824
1686
                elif kind == 'directory':
1825
1687
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1826
1688
                elif kind == 'symlink':
 
1689
                    inv_entry.executable = False
 
1690
                    inv_entry.text_size = None
1827
1691
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1828
1692
                elif kind == 'tree-reference':
1829
1693
                    inv_entry.reference_revision = fingerprint or None
1852
1716
            return None
1853
1717
        parent_index = self._get_parent_index()
1854
1718
        last_changed_revision = entry[1][parent_index][4]
1855
 
        try:
1856
 
            rev = self._repository.get_revision(last_changed_revision)
1857
 
        except errors.NoSuchRevision:
1858
 
            raise errors.FileTimestampUnavailable(self.id2path(file_id))
1859
 
        return rev.timestamp
 
1719
        return self._repository.get_revision(last_changed_revision).timestamp
1860
1720
 
1861
1721
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1862
1722
        entry = self._get_entry(file_id=file_id, path=path)
1866
1726
            return parent_details[1]
1867
1727
        return None
1868
1728
 
1869
 
    @needs_read_lock
1870
 
    def get_file_revision(self, file_id):
1871
 
        return self.inventory[file_id].revision
1872
 
 
1873
1729
    def get_file(self, file_id, path=None):
1874
1730
        return StringIO(self.get_file_text(file_id))
1875
1731
 
1898
1754
                                       identifier))
1899
1755
        return self._repository.iter_files_bytes(repo_desired_files)
1900
1756
 
1901
 
    def get_symlink_target(self, file_id, path=None):
 
1757
    def get_symlink_target(self, file_id):
1902
1758
        entry = self._get_entry(file_id=file_id)
1903
1759
        parent_index = self._get_parent_index()
1904
1760
        if entry[1][parent_index][0] != 'l':
1933
1789
        entry = self._get_entry(file_id=file_id)[1]
1934
1790
        if entry is None:
1935
1791
            raise errors.NoSuchId(tree=self, file_id=file_id)
1936
 
        parent_index = self._get_parent_index()
1937
 
        return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
 
1792
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
1938
1793
 
1939
1794
    def stored_kind(self, file_id):
1940
1795
        """See Tree.stored_kind"""
1957
1812
    def is_executable(self, file_id, path=None):
1958
1813
        ie = self.inventory[file_id]
1959
1814
        if ie.kind != "file":
1960
 
            return False
 
1815
            return None
1961
1816
        return ie.executable
1962
1817
 
1963
 
    def is_locked(self):
1964
 
        return self._locked
1965
 
 
1966
1818
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1967
1819
        # We use a standard implementation, because DirStateRevisionTree is
1968
1820
        # dealing with one of the parents of the current state
1981
1833
            yield path, 'V', entry.kind, entry.file_id, entry
1982
1834
 
1983
1835
    def lock_read(self):
1984
 
        """Lock the tree for a set of operations.
1985
 
 
1986
 
        :return: A bzrlib.lock.LogicalLockResult.
1987
 
        """
 
1836
        """Lock the tree for a set of operations."""
1988
1837
        if not self._locked:
1989
1838
            self._repository.lock_read()
1990
1839
            if self._dirstate._lock_token is None:
1991
1840
                self._dirstate.lock_read()
1992
1841
                self._dirstate_locked = True
1993
1842
        self._locked += 1
1994
 
        return LogicalLockResult(self.unlock)
1995
1843
 
1996
1844
    def _must_be_locked(self):
1997
1845
        if not self._locked:
2076
1924
    def make_source_parent_tree(source, target):
2077
1925
        """Change the source tree into a parent of the target."""
2078
1926
        revid = source.commit('record tree')
2079
 
        target.branch.fetch(source.branch, revid)
 
1927
        target.branch.repository.fetch(source.branch.repository, revid)
2080
1928
        target.set_parent_ids([revid])
2081
1929
        return target.basis_tree(), target
2082
1930
 
2087
1935
        return result
2088
1936
 
2089
1937
    @classmethod
2090
 
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2091
 
                                                  target):
 
1938
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2092
1939
        from bzrlib.tests.test__dirstate_helpers import \
2093
 
            compiled_dirstate_helpers_feature
2094
 
        test_case.requireFeature(compiled_dirstate_helpers_feature)
 
1940
            CompiledDirstateHelpersFeature
 
1941
        if not CompiledDirstateHelpersFeature.available():
 
1942
            from bzrlib.tests import UnavailableFeature
 
1943
            raise UnavailableFeature(CompiledDirstateHelpersFeature)
2095
1944
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
2096
1945
        result = klass.make_source_parent_tree(source, target)
2097
1946
        result[1]._iter_changes = ProcessEntryC
2128
1977
            output. An unversioned file is defined as one with (False, False)
2129
1978
            for the versioned pair.
2130
1979
        """
 
1980
        # NB: show_status depends on being able to pass in non-versioned files
 
1981
        # and report them as unknown
2131
1982
        # TODO: handle extra trees in the dirstate.
2132
1983
        if (extra_trees or specific_files == []):
2133
1984
            # we can't fast-path these cases (yet)