~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-10-13 06:08:53 UTC
  • mfrom: (4737.1.1 merge-2.0-into-devel)
  • Revision ID: pqm@pqm.ubuntu.com-20091013060853-erk2aaj80fnkrv25
(andrew) Merge lp:bzr/2.0 into lp:bzr, including fixes for #322807,
        #389413, #402623 and documentation improvements.

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