~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
28
28
 
29
29
from bzrlib.lazy_import import lazy_import
30
30
lazy_import(globals(), """
 
31
from bisect import bisect_left
 
32
import collections
 
33
from copy import deepcopy
31
34
import errno
 
35
import itertools
 
36
import operator
32
37
import stat
 
38
from time import time
 
39
import warnings
33
40
 
 
41
import bzrlib
34
42
from bzrlib import (
35
43
    bzrdir,
36
44
    cache_utf8,
37
45
    conflicts as _mod_conflicts,
38
46
    debug,
 
47
    delta,
39
48
    dirstate,
40
49
    errors,
41
 
    filters as _mod_filters,
42
50
    generate_ids,
 
51
    globbing,
 
52
    ignores,
 
53
    merge,
43
54
    osutils,
44
55
    revision as _mod_revision,
45
56
    revisiontree,
 
57
    textui,
46
58
    trace,
47
59
    transform,
 
60
    urlutils,
48
61
    views,
 
62
    xml5,
 
63
    xml6,
49
64
    )
 
65
import bzrlib.branch
 
66
from bzrlib.transport import get_transport
 
67
import bzrlib.ui
50
68
""")
51
69
 
 
70
from bzrlib import symbol_versioning
52
71
from bzrlib.decorators import needs_read_lock, needs_write_lock
53
 
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
54
 
from bzrlib.lock import LogicalLockResult
55
 
from bzrlib.lockable_files import LockableFiles
56
 
from bzrlib.lockdir import LockDir
 
72
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
 
73
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
 
74
import bzrlib.mutabletree
57
75
from bzrlib.mutabletree import needs_tree_write_lock
58
76
from bzrlib.osutils import (
59
77
    file_kind,
60
78
    isdir,
 
79
    normpath,
61
80
    pathjoin,
 
81
    rand_chars,
62
82
    realpath,
63
83
    safe_unicode,
 
84
    splitpath,
64
85
    )
 
86
from bzrlib.trace import mutter, note
65
87
from bzrlib.transport.local import LocalTransport
66
 
from bzrlib.tree import (
67
 
    InterTree,
68
 
    InventoryTree,
69
 
    )
70
 
from bzrlib.workingtree import (
71
 
    InventoryWorkingTree,
72
 
    WorkingTree,
73
 
    WorkingTreeFormat,
74
 
    )
75
 
 
76
 
 
77
 
class DirStateWorkingTree(InventoryWorkingTree):
78
 
 
 
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):
79
102
    def __init__(self, basedir,
80
103
                 branch,
81
104
                 _control_files=None,
91
114
        self._format = _format
92
115
        self.bzrdir = _bzrdir
93
116
        basedir = safe_unicode(basedir)
94
 
        trace.mutter("opening working tree %r", basedir)
 
117
        mutter("opening working tree %r", basedir)
95
118
        self._branch = branch
96
119
        self.basedir = realpath(basedir)
97
120
        # if branch is at our basedir and is a format 6 or less
131
154
            state.add(f, file_id, kind, None, '')
132
155
        self._make_dirty(reset_inventory=True)
133
156
 
134
 
    def _get_check_refs(self):
135
 
        """Return the references needed to perform a check of this tree."""
136
 
        return [('trees', self.last_revision())]
137
 
 
138
157
    def _make_dirty(self, reset_inventory):
139
158
        """Make the tree state dirty.
140
159
 
192
211
 
193
212
    def _comparison_data(self, entry, path):
194
213
        kind, executable, stat_value = \
195
 
            WorkingTree._comparison_data(self, entry, path)
 
214
            WorkingTree3._comparison_data(self, entry, path)
196
215
        # it looks like a plain directory, but it's really a reference -- see
197
216
        # also kind()
198
217
        if (self._repo_supports_tree_reference and kind == 'directory'
204
223
    def commit(self, message=None, revprops=None, *args, **kwargs):
205
224
        # mark the tree as dirty post commit - commit
206
225
        # can change the current versioned list by doing deletes.
207
 
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
 
226
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
208
227
        self._make_dirty(reset_inventory=True)
209
228
        return result
210
229
 
379
398
        state = self.current_dirstate()
380
399
        if stat_value is None:
381
400
            try:
382
 
                stat_value = osutils.lstat(file_abspath)
 
401
                stat_value = os.lstat(file_abspath)
383
402
            except OSError, e:
384
403
                if e.errno == errno.ENOENT:
385
404
                    return None
445
464
        return osutils.lexists(pathjoin(
446
465
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
447
466
 
448
 
    def has_or_had_id(self, file_id):
449
 
        state = self.current_dirstate()
450
 
        row, parents = self._get_entry(file_id=file_id)
451
 
        return row is not None
452
 
 
453
467
    @needs_read_lock
454
468
    def id2path(self, file_id):
455
469
        "Convert a file-id to a path."
488
502
            self._must_be_locked()
489
503
            if not path:
490
504
                path = self.id2path(file_id)
491
 
            mode = osutils.lstat(self.abspath(path)).st_mode
 
505
            mode = os.lstat(self.abspath(path)).st_mode
492
506
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
493
507
 
494
508
    def all_file_ids(self):
578
592
            return _mod_revision.NULL_REVISION
579
593
 
580
594
    def lock_read(self):
581
 
        """See Branch.lock_read, and WorkingTree.unlock.
582
 
 
583
 
        :return: A bzrlib.lock.LogicalLockResult.
584
 
        """
 
595
        """See Branch.lock_read, and WorkingTree.unlock."""
585
596
        self.branch.lock_read()
586
597
        try:
587
598
            self._control_files.lock_read()
600
611
        except:
601
612
            self.branch.unlock()
602
613
            raise
603
 
        return LogicalLockResult(self.unlock)
604
614
 
605
615
    def _lock_self_write(self):
606
616
        """This should be called after the branch is locked."""
621
631
        except:
622
632
            self.branch.unlock()
623
633
            raise
624
 
        return LogicalLockResult(self.unlock)
625
634
 
626
635
    def lock_tree_write(self):
627
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
628
 
 
629
 
        :return: A bzrlib.lock.LogicalLockResult.
630
 
        """
 
636
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
631
637
        self.branch.lock_read()
632
 
        return self._lock_self_write()
 
638
        self._lock_self_write()
633
639
 
634
640
    def lock_write(self):
635
 
        """See MutableTree.lock_write, and WorkingTree.unlock.
636
 
 
637
 
        :return: A bzrlib.lock.LogicalLockResult.
638
 
        """
 
641
        """See MutableTree.lock_write, and WorkingTree.unlock."""
639
642
        self.branch.lock_write()
640
 
        return self._lock_self_write()
 
643
        self._lock_self_write()
641
644
 
642
645
    @needs_tree_write_lock
643
646
    def move(self, from_paths, to_dir, after=False):
713
716
            from_entry = self._get_entry(path=from_rel)
714
717
            if from_entry == (None, None):
715
718
                raise errors.BzrMoveFailedError(from_rel,to_dir,
716
 
                    errors.NotVersionedError(path=from_rel))
 
719
                    errors.NotVersionedError(path=str(from_rel)))
717
720
 
718
721
            from_id = from_entry[0][2]
719
722
            to_rel = pathjoin(to_dir, from_tail)
1048
1051
    def set_last_revision(self, new_revision):
1049
1052
        """Change the last revision in the working tree."""
1050
1053
        parents = self.get_parent_ids()
1051
 
        if new_revision in (_mod_revision.NULL_REVISION, None):
 
1054
        if new_revision in (NULL_REVISION, None):
1052
1055
            if len(parents) >= 2:
1053
1056
                raise AssertionError(
1054
1057
                    "setting the last parent to none with a pending merge is "
1221
1224
                # just forget the whole block.
1222
1225
                entry_index = 0
1223
1226
                while entry_index < len(block[1]):
 
1227
                    # Mark this file id as having been removed
1224
1228
                    entry = block[1][entry_index]
1225
 
                    if entry[1][0][0] in 'ar':
1226
 
                        # 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)):
1227
1233
                        entry_index += 1
1228
 
                    else:
1229
 
                        # Mark this file id as having been removed
1230
 
                        ids_to_unversion.discard(entry[0][2])
1231
 
                        if not state._make_absent(entry):
1232
 
                            # The block has not shrunk.
1233
 
                            entry_index += 1
1234
1234
                # go to the next block. (At the moment we dont delete empty
1235
1235
                # dirblocks)
1236
1236
                block_index += 1
1257
1257
        # have to change the legacy inventory too.
1258
1258
        if self._inventory is not None:
1259
1259
            for file_id in file_ids:
1260
 
                if self._inventory.has_id(file_id):
1261
 
                    self._inventory.remove_recursive_id(file_id)
 
1260
                self._inventory.remove_recursive_id(file_id)
1262
1261
 
1263
1262
    @needs_tree_write_lock
1264
1263
    def rename_one(self, from_rel, to_rel, after=False):
1265
1264
        """See WorkingTree.rename_one"""
1266
1265
        self.flush()
1267
 
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
 
1266
        WorkingTree.rename_one(self, from_rel, to_rel, after)
1268
1267
 
1269
1268
    @needs_tree_write_lock
1270
1269
    def apply_inventory_delta(self, changes):
1289
1288
        if self._dirty:
1290
1289
            raise AssertionError("attempting to write an inventory when the "
1291
1290
                "dirstate is dirty will lose pending changes")
1292
 
        had_inventory = self._inventory is not None
1293
 
        # Setting self._inventory = None forces the dirstate to regenerate the
1294
 
        # working inventory. We do this because self.inventory may be inv, or
1295
 
        # may have been modified, and either case would prevent a clean delta
1296
 
        # being created.
1297
 
        self._inventory = None
1298
 
        # generate a delta,
1299
 
        delta = inv._make_delta(self.inventory)
1300
 
        # and apply it.
1301
 
        self.apply_inventory_delta(delta)
1302
 
        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:
1303
1294
            self._inventory = inv
1304
1295
        self.flush()
1305
1296
 
1306
 
    @needs_tree_write_lock
1307
 
    def reset_state(self, revision_ids=None):
1308
 
        """Reset the state of the working tree.
1309
 
 
1310
 
        This does a hard-reset to a last-known-good state. This is a way to
1311
 
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
1312
 
        """
1313
 
        if revision_ids is None:
1314
 
            revision_ids = self.get_parent_ids()
1315
 
        if not revision_ids:
1316
 
            base_tree = self.branch.repository.revision_tree(
1317
 
                _mod_revision.NULL_REVISION)
1318
 
            trees = []
1319
 
        else:
1320
 
            trees = zip(revision_ids,
1321
 
                        self.branch.repository.revision_trees(revision_ids))
1322
 
            base_tree = trees[0][1]
1323
 
        state = self.current_dirstate()
1324
 
        # We don't support ghosts yet
1325
 
        state.set_state_from_scratch(base_tree.inventory, trees, [])
1326
 
 
1327
1297
 
1328
1298
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1329
1299
 
1334
1304
        """See dirstate.SHA1Provider.sha1()."""
1335
1305
        filters = self.tree._content_filter_stack(
1336
1306
            self.tree.relpath(osutils.safe_unicode(abspath)))
1337
 
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
 
1307
        return internal_size_sha_file_byname(abspath, filters)[1]
1338
1308
 
1339
1309
    def stat_and_sha1(self, abspath):
1340
1310
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1344
1314
        try:
1345
1315
            statvalue = os.fstat(file_obj.fileno())
1346
1316
            if filters:
1347
 
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
 
1317
                file_obj = filtered_input_file(file_obj, filters)
1348
1318
            sha1 = osutils.size_sha_file(file_obj)[1]
1349
1319
        finally:
1350
1320
            file_obj.close()
1351
1321
        return statvalue, sha1
1352
1322
 
1353
1323
 
1354
 
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1355
 
    """Dirstate working tree that supports content filtering.
1356
 
 
1357
 
    The dirstate holds the hash and size of the canonical form of the file, 
1358
 
    and most methods must return that.
1359
 
    """
1360
 
 
1361
 
    def _file_content_summary(self, path, stat_result):
1362
 
        # This is to support the somewhat obsolete path_content_summary method
1363
 
        # with content filtering: see
1364
 
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1365
 
        #
1366
 
        # If the dirstate cache is up to date and knows the hash and size,
1367
 
        # return that.
1368
 
        # Otherwise if there are no content filters, return the on-disk size
1369
 
        # and leave the hash blank.
1370
 
        # Otherwise, read and filter the on-disk file and use its size and
1371
 
        # hash.
1372
 
        #
1373
 
        # The dirstate doesn't store the size of the canonical form so we
1374
 
        # can't trust it for content-filtered trees.  We just return None.
1375
 
        dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1376
 
        executable = self._is_executable_from_path_and_stat(path, stat_result)
1377
 
        return ('file', None, executable, dirstate_sha1)
1378
 
 
1379
 
 
1380
1324
class WorkingTree4(DirStateWorkingTree):
1381
1325
    """This is the Format 4 working tree.
1382
1326
 
1383
 
    This differs from WorkingTree by:
 
1327
    This differs from WorkingTree3 by:
1384
1328
     - Having a consolidated internal dirstate, stored in a
1385
1329
       randomly-accessible sorted file on disk.
1386
1330
     - Not having a regular inventory attribute.  One can be synthesized
1390
1334
    """
1391
1335
 
1392
1336
 
1393
 
class WorkingTree5(ContentFilteringDirStateWorkingTree):
 
1337
class WorkingTree5(DirStateWorkingTree):
1394
1338
    """This is the Format 5 working tree.
1395
1339
 
1396
1340
    This differs from WorkingTree4 by:
1400
1344
    """
1401
1345
 
1402
1346
 
1403
 
class WorkingTree6(ContentFilteringDirStateWorkingTree):
 
1347
class WorkingTree6(DirStateWorkingTree):
1404
1348
    """This is the Format 6 working tree.
1405
1349
 
1406
1350
    This differs from WorkingTree5 by:
1414
1358
        return views.PathBasedViews(self)
1415
1359
 
1416
1360
 
1417
 
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1418
 
 
1419
 
    missing_parent_conflicts = True
1420
 
 
1421
 
    _lock_class = LockDir
1422
 
    _lock_file_name = 'lock'
1423
 
 
1424
 
    def _open_control_files(self, a_bzrdir):
1425
 
        transport = a_bzrdir.get_workingtree_transport(None)
1426
 
        return LockableFiles(transport, self._lock_file_name,
1427
 
                             self._lock_class)
1428
 
 
 
1361
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1429
1362
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1430
1363
                   accelerator_tree=None, hardlink=False):
1431
1364
        """See WorkingTreeFormat.initialize().
1470
1403
        wt.lock_tree_write()
1471
1404
        try:
1472
1405
            self._init_custom_control_files(wt)
1473
 
            if revision_id in (None, _mod_revision.NULL_REVISION):
 
1406
            if revision_id in (None, NULL_REVISION):
1474
1407
                if branch.repository.supports_rich_root():
1475
1408
                    wt._set_root_id(generate_ids.gen_root_id())
1476
1409
                else:
1487
1420
                    pass
1488
1421
            if basis is None:
1489
1422
                basis = branch.repository.revision_tree(revision_id)
1490
 
            if revision_id == _mod_revision.NULL_REVISION:
 
1423
            if revision_id == NULL_REVISION:
1491
1424
                parents_list = []
1492
1425
            else:
1493
1426
                parents_list = [(revision_id, basis)]
1501
1434
                if basis_root_id is not None:
1502
1435
                    wt._set_root_id(basis_root_id)
1503
1436
                    wt.flush()
1504
 
                if wt.supports_content_filtering():
1505
 
                    # The original tree may not have the same content filters
1506
 
                    # applied so we can't safely build the inventory delta from
1507
 
                    # the source tree.
1508
 
                    delta_from_tree = False
1509
 
                else:
1510
 
                    delta_from_tree = True
1511
1437
                # delta_from_tree is safe even for DirStateRevisionTrees,
1512
1438
                # because wt4.apply_inventory_delta does not mutate the input
1513
1439
                # inventory entries.
1514
1440
                transform.build_tree(basis, wt, accelerator_tree,
1515
 
                                     hardlink=hardlink,
1516
 
                                     delta_from_tree=delta_from_tree)
 
1441
                                     hardlink=hardlink, delta_from_tree=True)
1517
1442
            finally:
1518
1443
                basis.unlock()
1519
1444
        finally:
1530
1455
        :param wt: the WorkingTree object
1531
1456
        """
1532
1457
 
1533
 
    def open(self, a_bzrdir, _found=False):
1534
 
        """Return the WorkingTree object for a_bzrdir
1535
 
 
1536
 
        _found is a private parameter, do not use it. It is used to indicate
1537
 
               if format probing has already been done.
1538
 
        """
1539
 
        if not _found:
1540
 
            # we are being called directly and must probe.
1541
 
            raise NotImplementedError
1542
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
1543
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1544
 
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1545
 
        return wt
1546
 
 
1547
1458
    def _open(self, a_bzrdir, control_files):
1548
1459
        """Open the tree itself.
1549
1460
 
1640
1551
        return True
1641
1552
 
1642
1553
 
1643
 
class DirStateRevisionTree(InventoryTree):
1644
 
    """A revision tree pulling the inventory from a dirstate.
1645
 
    
1646
 
    Note that this is one of the historical (ie revision) trees cached in the
1647
 
    dirstate for easy access, not the workingtree.
1648
 
    """
 
1554
class DirStateRevisionTree(Tree):
 
1555
    """A revision tree pulling the inventory from a dirstate."""
1649
1556
 
1650
1557
    def __init__(self, dirstate, revision_id, repository):
1651
1558
        self._dirstate = dirstate
1665
1572
    def annotate_iter(self, file_id,
1666
1573
                      default_revision=_mod_revision.CURRENT_REVISION):
1667
1574
        """See Tree.annotate_iter"""
1668
 
        text_key = (file_id, self.get_file_revision(file_id))
 
1575
        text_key = (file_id, self.inventory[file_id].revision)
1669
1576
        annotations = self._repository.texts.annotate(text_key)
1670
1577
        return [(key[-1], line) for (key, line) in annotations]
1671
1578
 
1793
1700
                elif kind == 'directory':
1794
1701
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1795
1702
                elif kind == 'symlink':
 
1703
                    inv_entry.executable = False
 
1704
                    inv_entry.text_size = None
1796
1705
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1797
1706
                elif kind == 'tree-reference':
1798
1707
                    inv_entry.reference_revision = fingerprint or None
1821
1730
            return None
1822
1731
        parent_index = self._get_parent_index()
1823
1732
        last_changed_revision = entry[1][parent_index][4]
1824
 
        try:
1825
 
            rev = self._repository.get_revision(last_changed_revision)
1826
 
        except errors.NoSuchRevision:
1827
 
            raise errors.FileTimestampUnavailable(self.id2path(file_id))
1828
 
        return rev.timestamp
 
1733
        return self._repository.get_revision(last_changed_revision).timestamp
1829
1734
 
1830
1735
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1831
1736
        entry = self._get_entry(file_id=file_id, path=path)
1835
1740
            return parent_details[1]
1836
1741
        return None
1837
1742
 
1838
 
    @needs_read_lock
1839
 
    def get_file_revision(self, file_id):
1840
 
        return self.inventory[file_id].revision
1841
 
 
1842
1743
    def get_file(self, file_id, path=None):
1843
1744
        return StringIO(self.get_file_text(file_id))
1844
1745
 
1902
1803
        entry = self._get_entry(file_id=file_id)[1]
1903
1804
        if entry is None:
1904
1805
            raise errors.NoSuchId(tree=self, file_id=file_id)
1905
 
        parent_index = self._get_parent_index()
1906
 
        return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
 
1806
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
1907
1807
 
1908
1808
    def stored_kind(self, file_id):
1909
1809
        """See Tree.stored_kind"""
1926
1826
    def is_executable(self, file_id, path=None):
1927
1827
        ie = self.inventory[file_id]
1928
1828
        if ie.kind != "file":
1929
 
            return False
 
1829
            return None
1930
1830
        return ie.executable
1931
1831
 
1932
 
    def is_locked(self):
1933
 
        return self._locked
1934
 
 
1935
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
1832
    def list_files(self, include_root=False):
1936
1833
        # We use a standard implementation, because DirStateRevisionTree is
1937
1834
        # dealing with one of the parents of the current state
1938
1835
        inv = self._get_inventory()
1939
 
        if from_dir is None:
1940
 
            from_dir_id = None
1941
 
        else:
1942
 
            from_dir_id = inv.path2id(from_dir)
1943
 
            if from_dir_id is None:
1944
 
                # Directory not versioned
1945
 
                return
1946
 
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1947
 
        if inv.root is not None and not include_root and from_dir is None:
 
1836
        entries = inv.iter_entries()
 
1837
        if self.inventory.root is not None and not include_root:
1948
1838
            entries.next()
1949
1839
        for path, entry in entries:
1950
1840
            yield path, 'V', entry.kind, entry.file_id, entry
1951
1841
 
1952
1842
    def lock_read(self):
1953
 
        """Lock the tree for a set of operations.
1954
 
 
1955
 
        :return: A bzrlib.lock.LogicalLockResult.
1956
 
        """
 
1843
        """Lock the tree for a set of operations."""
1957
1844
        if not self._locked:
1958
1845
            self._repository.lock_read()
1959
1846
            if self._dirstate._lock_token is None:
1960
1847
                self._dirstate.lock_read()
1961
1848
                self._dirstate_locked = True
1962
1849
        self._locked += 1
1963
 
        return LogicalLockResult(self.unlock)
1964
1850
 
1965
1851
    def _must_be_locked(self):
1966
1852
        if not self._locked:
2045
1931
    def make_source_parent_tree(source, target):
2046
1932
        """Change the source tree into a parent of the target."""
2047
1933
        revid = source.commit('record tree')
2048
 
        target.branch.fetch(source.branch, revid)
 
1934
        target.branch.repository.fetch(source.branch.repository, revid)
2049
1935
        target.set_parent_ids([revid])
2050
1936
        return target.basis_tree(), target
2051
1937
 
2056
1942
        return result
2057
1943
 
2058
1944
    @classmethod
2059
 
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2060
 
                                                  target):
 
1945
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2061
1946
        from bzrlib.tests.test__dirstate_helpers import \
2062
 
            compiled_dirstate_helpers_feature
2063
 
        test_case.requireFeature(compiled_dirstate_helpers_feature)
2064
 
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
 
1947
            CompiledDirstateHelpersFeature
 
1948
        if not CompiledDirstateHelpersFeature.available():
 
1949
            from bzrlib.tests import UnavailableFeature
 
1950
            raise UnavailableFeature(CompiledDirstateHelpersFeature)
 
1951
        from bzrlib._dirstate_helpers_c import ProcessEntryC
2065
1952
        result = klass.make_source_parent_tree(source, target)
2066
1953
        result[1]._iter_changes = ProcessEntryC
2067
1954
        return result
2097
1984
            output. An unversioned file is defined as one with (False, False)
2098
1985
            for the versioned pair.
2099
1986
        """
 
1987
        # NB: show_status depends on being able to pass in non-versioned files
 
1988
        # and report them as unknown
2100
1989
        # TODO: handle extra trees in the dirstate.
2101
1990
        if (extra_trees or specific_files == []):
2102
1991
            # we can't fast-path these cases (yet)
2105
1994
                require_versioned, want_unversioned=want_unversioned)
2106
1995
        parent_ids = self.target.get_parent_ids()
2107
1996
        if not (self.source._revision_id in parent_ids
2108
 
                or self.source._revision_id == _mod_revision.NULL_REVISION):
 
1997
                or self.source._revision_id == NULL_REVISION):
2109
1998
            raise AssertionError(
2110
1999
                "revision {%s} is not stored in {%s}, but %s "
2111
2000
                "can only be used for trees stored in the dirstate"
2112
2001
                % (self.source._revision_id, self.target, self.iter_changes))
2113
2002
        target_index = 0
2114
 
        if self.source._revision_id == _mod_revision.NULL_REVISION:
 
2003
        if self.source._revision_id == NULL_REVISION:
2115
2004
            source_index = None
2116
2005
            indices = (target_index,)
2117
2006
        else:
2138
2027
        state._read_dirblocks_if_needed()
2139
2028
        if require_versioned:
2140
2029
            # -- check all supplied paths are versioned in a search tree. --
2141
 
            not_versioned = []
 
2030
            all_versioned = True
2142
2031
            for path in specific_files:
2143
2032
                path_entries = state._entries_for_path(path)
2144
2033
                if not path_entries:
2145
2034
                    # this specified path is not present at all: error
2146
 
                    not_versioned.append(path)
2147
 
                    continue
 
2035
                    all_versioned = False
 
2036
                    break
2148
2037
                found_versioned = False
2149
2038
                # for each id at this path
2150
2039
                for entry in path_entries:
2157
2046
                if not found_versioned:
2158
2047
                    # none of the indexes was not 'absent' at all ids for this
2159
2048
                    # path.
2160
 
                    not_versioned.append(path)
2161
 
            if len(not_versioned) > 0:
2162
 
                raise errors.PathsNotVersionedError(not_versioned)
 
2049
                    all_versioned = False
 
2050
                    break
 
2051
            if not all_versioned:
 
2052
                raise errors.PathsNotVersionedError(specific_files)
2163
2053
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2164
2054
        search_specific_files = osutils.minimum_path_selection(specific_files)
2165
2055
 
2179
2069
            (revisiontree.RevisionTree, DirStateRevisionTree)):
2180
2070
            return False
2181
2071
        # the source revid must be in the target dirstate
2182
 
        if not (source._revision_id == _mod_revision.NULL_REVISION or
 
2072
        if not (source._revision_id == NULL_REVISION or
2183
2073
            source._revision_id in target.get_parent_ids()):
2184
2074
            # TODO: what about ghosts? it may well need to
2185
2075
            # check for them explicitly.