~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2007-2010 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
34
31
import errno
35
 
import itertools
36
 
import operator
37
32
import stat
38
 
from time import time
39
 
import warnings
40
33
 
41
34
import bzrlib
42
35
from bzrlib import (
43
36
    bzrdir,
44
37
    cache_utf8,
45
 
    conflicts as _mod_conflicts,
46
38
    debug,
47
 
    delta,
48
39
    dirstate,
49
40
    errors,
50
41
    generate_ids,
51
 
    globbing,
52
 
    ignores,
53
 
    merge,
54
42
    osutils,
55
43
    revision as _mod_revision,
56
44
    revisiontree,
57
 
    textui,
58
45
    trace,
59
46
    transform,
60
 
    urlutils,
61
47
    views,
62
 
    xml5,
63
 
    xml6,
64
48
    )
65
49
import bzrlib.branch
66
 
from bzrlib.transport import get_transport
67
50
import bzrlib.ui
68
51
""")
69
52
 
70
 
from bzrlib import symbol_versioning
71
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
72
54
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
 
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
 
56
from bzrlib.lock import LogicalLockResult
75
57
from bzrlib.mutabletree import needs_tree_write_lock
76
58
from bzrlib.osutils import (
77
59
    file_kind,
78
60
    isdir,
79
 
    normpath,
80
61
    pathjoin,
81
 
    rand_chars,
82
62
    realpath,
83
63
    safe_unicode,
84
 
    splitpath,
85
64
    )
86
 
from bzrlib.trace import mutter, note
 
65
from bzrlib.trace import mutter
87
66
from bzrlib.transport.local import LocalTransport
88
67
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
68
from bzrlib.tree import Tree
98
69
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
99
70
 
464
435
        return osutils.lexists(pathjoin(
465
436
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
466
437
 
 
438
    def has_or_had_id(self, file_id):
 
439
        state = self.current_dirstate()
 
440
        row, parents = self._get_entry(file_id=file_id)
 
441
        return row is not None
 
442
 
467
443
    @needs_read_lock
468
444
    def id2path(self, file_id):
469
445
        "Convert a file-id to a path."
592
568
            return _mod_revision.NULL_REVISION
593
569
 
594
570
    def lock_read(self):
595
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
571
        """See Branch.lock_read, and WorkingTree.unlock.
 
572
 
 
573
        :return: A bzrlib.lock.LogicalLockResult.
 
574
        """
596
575
        self.branch.lock_read()
597
576
        try:
598
577
            self._control_files.lock_read()
611
590
        except:
612
591
            self.branch.unlock()
613
592
            raise
 
593
        return LogicalLockResult(self.unlock)
614
594
 
615
595
    def _lock_self_write(self):
616
596
        """This should be called after the branch is locked."""
631
611
        except:
632
612
            self.branch.unlock()
633
613
            raise
 
614
        return LogicalLockResult(self.unlock)
634
615
 
635
616
    def lock_tree_write(self):
636
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
617
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
618
 
 
619
        :return: A bzrlib.lock.LogicalLockResult.
 
620
        """
637
621
        self.branch.lock_read()
638
 
        self._lock_self_write()
 
622
        return self._lock_self_write()
639
623
 
640
624
    def lock_write(self):
641
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
625
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
626
 
 
627
        :return: A bzrlib.lock.LogicalLockResult.
 
628
        """
642
629
        self.branch.lock_write()
643
 
        self._lock_self_write()
 
630
        return self._lock_self_write()
644
631
 
645
632
    @needs_tree_write_lock
646
633
    def move(self, from_paths, to_dir, after=False):
716
703
            from_entry = self._get_entry(path=from_rel)
717
704
            if from_entry == (None, None):
718
705
                raise errors.BzrMoveFailedError(from_rel,to_dir,
719
 
                    errors.NotVersionedError(path=str(from_rel)))
 
706
                    errors.NotVersionedError(path=from_rel))
720
707
 
721
708
            from_id = from_entry[0][2]
722
709
            to_rel = pathjoin(to_dir, from_tail)
1051
1038
    def set_last_revision(self, new_revision):
1052
1039
        """Change the last revision in the working tree."""
1053
1040
        parents = self.get_parent_ids()
1054
 
        if new_revision in (NULL_REVISION, None):
 
1041
        if new_revision in (_mod_revision.NULL_REVISION, None):
1055
1042
            if len(parents) >= 2:
1056
1043
                raise AssertionError(
1057
1044
                    "setting the last parent to none with a pending merge is "
1224
1211
                # just forget the whole block.
1225
1212
                entry_index = 0
1226
1213
                while entry_index < len(block[1]):
1227
 
                    # Mark this file id as having been removed
1228
1214
                    entry = block[1][entry_index]
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)):
 
1215
                    if entry[1][0][0] in 'ar':
 
1216
                        # don't remove absent or renamed entries
1233
1217
                        entry_index += 1
 
1218
                    else:
 
1219
                        # Mark this file id as having been removed
 
1220
                        ids_to_unversion.discard(entry[0][2])
 
1221
                        if not state._make_absent(entry):
 
1222
                            # The block has not shrunk.
 
1223
                            entry_index += 1
1234
1224
                # go to the next block. (At the moment we dont delete empty
1235
1225
                # dirblocks)
1236
1226
                block_index += 1
1257
1247
        # have to change the legacy inventory too.
1258
1248
        if self._inventory is not None:
1259
1249
            for file_id in file_ids:
1260
 
                self._inventory.remove_recursive_id(file_id)
 
1250
                if self._inventory.has_id(file_id):
 
1251
                    self._inventory.remove_recursive_id(file_id)
1261
1252
 
1262
1253
    @needs_tree_write_lock
1263
1254
    def rename_one(self, from_rel, to_rel, after=False):
1288
1279
        if self._dirty:
1289
1280
            raise AssertionError("attempting to write an inventory when the "
1290
1281
                "dirstate is dirty will lose pending changes")
1291
 
        self.current_dirstate().set_state_from_inventory(inv)
1292
 
        self._make_dirty(reset_inventory=False)
1293
 
        if self._inventory is not None:
 
1282
        had_inventory = self._inventory is not None
 
1283
        # Setting self._inventory = None forces the dirstate to regenerate the
 
1284
        # working inventory. We do this because self.inventory may be inv, or
 
1285
        # may have been modified, and either case would prevent a clean delta
 
1286
        # being created.
 
1287
        self._inventory = None
 
1288
        # generate a delta,
 
1289
        delta = inv._make_delta(self.inventory)
 
1290
        # and apply it.
 
1291
        self.apply_inventory_delta(delta)
 
1292
        if had_inventory:
1294
1293
            self._inventory = inv
1295
1294
        self.flush()
1296
1295
 
1321
1320
        return statvalue, sha1
1322
1321
 
1323
1322
 
 
1323
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
 
1324
    """Dirstate working tree that supports content filtering.
 
1325
 
 
1326
    The dirstate holds the hash and size of the canonical form of the file, 
 
1327
    and most methods must return that.
 
1328
    """
 
1329
 
 
1330
    def _file_content_summary(self, path, stat_result):
 
1331
        # This is to support the somewhat obsolete path_content_summary method
 
1332
        # with content filtering: see
 
1333
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
 
1334
        #
 
1335
        # If the dirstate cache is up to date and knows the hash and size,
 
1336
        # return that.
 
1337
        # Otherwise if there are no content filters, return the on-disk size
 
1338
        # and leave the hash blank.
 
1339
        # Otherwise, read and filter the on-disk file and use its size and
 
1340
        # hash.
 
1341
        #
 
1342
        # The dirstate doesn't store the size of the canonical form so we
 
1343
        # can't trust it for content-filtered trees.  We just return None.
 
1344
        dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
 
1345
        executable = self._is_executable_from_path_and_stat(path, stat_result)
 
1346
        return ('file', None, executable, dirstate_sha1)
 
1347
 
 
1348
 
1324
1349
class WorkingTree4(DirStateWorkingTree):
1325
1350
    """This is the Format 4 working tree.
1326
1351
 
1334
1359
    """
1335
1360
 
1336
1361
 
1337
 
class WorkingTree5(DirStateWorkingTree):
 
1362
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1338
1363
    """This is the Format 5 working tree.
1339
1364
 
1340
1365
    This differs from WorkingTree4 by:
1344
1369
    """
1345
1370
 
1346
1371
 
1347
 
class WorkingTree6(DirStateWorkingTree):
 
1372
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1348
1373
    """This is the Format 6 working tree.
1349
1374
 
1350
1375
    This differs from WorkingTree5 by:
1359
1384
 
1360
1385
 
1361
1386
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1387
 
1362
1388
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1363
1389
                   accelerator_tree=None, hardlink=False):
1364
1390
        """See WorkingTreeFormat.initialize().
1403
1429
        wt.lock_tree_write()
1404
1430
        try:
1405
1431
            self._init_custom_control_files(wt)
1406
 
            if revision_id in (None, NULL_REVISION):
 
1432
            if revision_id in (None, _mod_revision.NULL_REVISION):
1407
1433
                if branch.repository.supports_rich_root():
1408
1434
                    wt._set_root_id(generate_ids.gen_root_id())
1409
1435
                else:
1420
1446
                    pass
1421
1447
            if basis is None:
1422
1448
                basis = branch.repository.revision_tree(revision_id)
1423
 
            if revision_id == NULL_REVISION:
 
1449
            if revision_id == _mod_revision.NULL_REVISION:
1424
1450
                parents_list = []
1425
1451
            else:
1426
1452
                parents_list = [(revision_id, basis)]
1434
1460
                if basis_root_id is not None:
1435
1461
                    wt._set_root_id(basis_root_id)
1436
1462
                    wt.flush()
 
1463
                if wt.supports_content_filtering():
 
1464
                    # The original tree may not have the same content filters
 
1465
                    # applied so we can't safely build the inventory delta from
 
1466
                    # the source tree.
 
1467
                    delta_from_tree = False
 
1468
                else:
 
1469
                    delta_from_tree = True
1437
1470
                # delta_from_tree is safe even for DirStateRevisionTrees,
1438
1471
                # because wt4.apply_inventory_delta does not mutate the input
1439
1472
                # inventory entries.
1440
1473
                transform.build_tree(basis, wt, accelerator_tree,
1441
 
                                     hardlink=hardlink, delta_from_tree=True)
 
1474
                                     hardlink=hardlink,
 
1475
                                     delta_from_tree=delta_from_tree)
1442
1476
            finally:
1443
1477
                basis.unlock()
1444
1478
        finally:
1552
1586
 
1553
1587
 
1554
1588
class DirStateRevisionTree(Tree):
1555
 
    """A revision tree pulling the inventory from a dirstate."""
 
1589
    """A revision tree pulling the inventory from a dirstate.
 
1590
    
 
1591
    Note that this is one of the historical (ie revision) trees cached in the
 
1592
    dirstate for easy access, not the workingtree.
 
1593
    """
1556
1594
 
1557
1595
    def __init__(self, dirstate, revision_id, repository):
1558
1596
        self._dirstate = dirstate
1730
1768
            return None
1731
1769
        parent_index = self._get_parent_index()
1732
1770
        last_changed_revision = entry[1][parent_index][4]
1733
 
        return self._repository.get_revision(last_changed_revision).timestamp
 
1771
        try:
 
1772
            rev = self._repository.get_revision(last_changed_revision)
 
1773
        except errors.NoSuchRevision:
 
1774
            raise errors.FileTimestampUnavailable(self.id2path(file_id))
 
1775
        return rev.timestamp
1734
1776
 
1735
1777
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1736
1778
        entry = self._get_entry(file_id=file_id, path=path)
1803
1845
        entry = self._get_entry(file_id=file_id)[1]
1804
1846
        if entry is None:
1805
1847
            raise errors.NoSuchId(tree=self, file_id=file_id)
1806
 
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
 
1848
        parent_index = self._get_parent_index()
 
1849
        return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1807
1850
 
1808
1851
    def stored_kind(self, file_id):
1809
1852
        """See Tree.stored_kind"""
1829
1872
            return None
1830
1873
        return ie.executable
1831
1874
 
1832
 
    def list_files(self, include_root=False):
 
1875
    def is_locked(self):
 
1876
        return self._locked
 
1877
 
 
1878
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1833
1879
        # We use a standard implementation, because DirStateRevisionTree is
1834
1880
        # dealing with one of the parents of the current state
1835
1881
        inv = self._get_inventory()
1836
 
        entries = inv.iter_entries()
1837
 
        if self.inventory.root is not None and not include_root:
 
1882
        if from_dir is None:
 
1883
            from_dir_id = None
 
1884
        else:
 
1885
            from_dir_id = inv.path2id(from_dir)
 
1886
            if from_dir_id is None:
 
1887
                # Directory not versioned
 
1888
                return
 
1889
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
 
1890
        if inv.root is not None and not include_root and from_dir is None:
1838
1891
            entries.next()
1839
1892
        for path, entry in entries:
1840
1893
            yield path, 'V', entry.kind, entry.file_id, entry
1841
1894
 
1842
1895
    def lock_read(self):
1843
 
        """Lock the tree for a set of operations."""
 
1896
        """Lock the tree for a set of operations.
 
1897
 
 
1898
        :return: A bzrlib.lock.LogicalLockResult.
 
1899
        """
1844
1900
        if not self._locked:
1845
1901
            self._repository.lock_read()
1846
1902
            if self._dirstate._lock_token is None:
1847
1903
                self._dirstate.lock_read()
1848
1904
                self._dirstate_locked = True
1849
1905
        self._locked += 1
 
1906
        return LogicalLockResult(self.unlock)
1850
1907
 
1851
1908
    def _must_be_locked(self):
1852
1909
        if not self._locked:
1942
1999
        return result
1943
2000
 
1944
2001
    @classmethod
1945
 
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
 
2002
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
 
2003
                                                  target):
1946
2004
        from bzrlib.tests.test__dirstate_helpers import \
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
 
2005
            compiled_dirstate_helpers_feature
 
2006
        test_case.requireFeature(compiled_dirstate_helpers_feature)
 
2007
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1952
2008
        result = klass.make_source_parent_tree(source, target)
1953
2009
        result[1]._iter_changes = ProcessEntryC
1954
2010
        return result
1984
2040
            output. An unversioned file is defined as one with (False, False)
1985
2041
            for the versioned pair.
1986
2042
        """
1987
 
        # NB: show_status depends on being able to pass in non-versioned files
1988
 
        # and report them as unknown
1989
2043
        # TODO: handle extra trees in the dirstate.
1990
2044
        if (extra_trees or specific_files == []):
1991
2045
            # we can't fast-path these cases (yet)
1994
2048
                require_versioned, want_unversioned=want_unversioned)
1995
2049
        parent_ids = self.target.get_parent_ids()
1996
2050
        if not (self.source._revision_id in parent_ids
1997
 
                or self.source._revision_id == NULL_REVISION):
 
2051
                or self.source._revision_id == _mod_revision.NULL_REVISION):
1998
2052
            raise AssertionError(
1999
2053
                "revision {%s} is not stored in {%s}, but %s "
2000
2054
                "can only be used for trees stored in the dirstate"
2001
2055
                % (self.source._revision_id, self.target, self.iter_changes))
2002
2056
        target_index = 0
2003
 
        if self.source._revision_id == NULL_REVISION:
 
2057
        if self.source._revision_id == _mod_revision.NULL_REVISION:
2004
2058
            source_index = None
2005
2059
            indices = (target_index,)
2006
2060
        else:
2021
2075
        else:
2022
2076
            specific_files = set([''])
2023
2077
        # -- specific_files is now a utf8 path set --
2024
 
        search_specific_files = set()
 
2078
 
2025
2079
        # -- get the state object and prepare it.
2026
2080
        state = self.target.current_dirstate()
2027
2081
        state._read_dirblocks_if_needed()
2028
2082
        if require_versioned:
2029
2083
            # -- check all supplied paths are versioned in a search tree. --
2030
 
            all_versioned = True
 
2084
            not_versioned = []
2031
2085
            for path in specific_files:
2032
2086
                path_entries = state._entries_for_path(path)
2033
2087
                if not path_entries:
2034
2088
                    # this specified path is not present at all: error
2035
 
                    all_versioned = False
2036
 
                    break
 
2089
                    not_versioned.append(path)
 
2090
                    continue
2037
2091
                found_versioned = False
2038
2092
                # for each id at this path
2039
2093
                for entry in path_entries:
2046
2100
                if not found_versioned:
2047
2101
                    # none of the indexes was not 'absent' at all ids for this
2048
2102
                    # path.
2049
 
                    all_versioned = False
2050
 
                    break
2051
 
            if not all_versioned:
2052
 
                raise errors.PathsNotVersionedError(specific_files)
 
2103
                    not_versioned.append(path)
 
2104
            if len(not_versioned) > 0:
 
2105
                raise errors.PathsNotVersionedError(not_versioned)
2053
2106
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2054
 
        for path in specific_files:
2055
 
            other_specific_files = specific_files.difference(set([path]))
2056
 
            if not osutils.is_inside_any(other_specific_files, path):
2057
 
                # this is a top level path, we must check it.
2058
 
                search_specific_files.add(path)
 
2107
        search_specific_files = osutils.minimum_path_selection(specific_files)
2059
2108
 
2060
2109
        use_filesystem_for_exec = (sys.platform != 'win32')
2061
2110
        iter_changes = self.target._iter_changes(include_unchanged,
2073
2122
            (revisiontree.RevisionTree, DirStateRevisionTree)):
2074
2123
            return False
2075
2124
        # the source revid must be in the target dirstate
2076
 
        if not (source._revision_id == NULL_REVISION or
 
2125
        if not (source._revision_id == _mod_revision.NULL_REVISION or
2077
2126
            source._revision_id in target.get_parent_ids()):
2078
2127
            # TODO: what about ghosts? it may well need to
2079
2128
            # check for them explicitly.