~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: 2010-01-14 00:01:32 UTC
  • mfrom: (4957.1.1 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20100114000132-3p3rabnonjw3gzqb
(jam) Merge bzr.stable, bringing in bug fixes #175839, #504390

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 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
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
 
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
74
56
import bzrlib.mutabletree
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."
716
692
            from_entry = self._get_entry(path=from_rel)
717
693
            if from_entry == (None, None):
718
694
                raise errors.BzrMoveFailedError(from_rel,to_dir,
719
 
                    errors.NotVersionedError(path=str(from_rel)))
 
695
                    errors.NotVersionedError(path=from_rel))
720
696
 
721
697
            from_id = from_entry[0][2]
722
698
            to_rel = pathjoin(to_dir, from_tail)
1051
1027
    def set_last_revision(self, new_revision):
1052
1028
        """Change the last revision in the working tree."""
1053
1029
        parents = self.get_parent_ids()
1054
 
        if new_revision in (NULL_REVISION, None):
 
1030
        if new_revision in (_mod_revision.NULL_REVISION, None):
1055
1031
            if len(parents) >= 2:
1056
1032
                raise AssertionError(
1057
1033
                    "setting the last parent to none with a pending merge is "
1224
1200
                # just forget the whole block.
1225
1201
                entry_index = 0
1226
1202
                while entry_index < len(block[1]):
1227
 
                    # Mark this file id as having been removed
1228
1203
                    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)):
 
1204
                    if entry[1][0][0] in 'ar':
 
1205
                        # don't remove absent or renamed entries
1233
1206
                        entry_index += 1
 
1207
                    else:
 
1208
                        # Mark this file id as having been removed
 
1209
                        ids_to_unversion.discard(entry[0][2])
 
1210
                        if not state._make_absent(entry):
 
1211
                            # The block has not shrunk.
 
1212
                            entry_index += 1
1234
1213
                # go to the next block. (At the moment we dont delete empty
1235
1214
                # dirblocks)
1236
1215
                block_index += 1
1288
1267
        if self._dirty:
1289
1268
            raise AssertionError("attempting to write an inventory when the "
1290
1269
                "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:
 
1270
        had_inventory = self._inventory is not None
 
1271
        # Setting self._inventory = None forces the dirstate to regenerate the
 
1272
        # working inventory. We do this because self.inventory may be inv, or
 
1273
        # may have been modified, and either case would prevent a clean delta
 
1274
        # being created.
 
1275
        self._inventory = None
 
1276
        # generate a delta,
 
1277
        delta = inv._make_delta(self.inventory)
 
1278
        # and apply it.
 
1279
        self.apply_inventory_delta(delta)
 
1280
        if had_inventory:
1294
1281
            self._inventory = inv
1295
1282
        self.flush()
1296
1283
 
1321
1308
        return statvalue, sha1
1322
1309
 
1323
1310
 
 
1311
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
 
1312
    """Dirstate working tree that supports content filtering.
 
1313
 
 
1314
    The dirstate holds the hash and size of the canonical form of the file, 
 
1315
    and most methods must return that.
 
1316
    """
 
1317
 
 
1318
    def _file_content_summary(self, path, stat_result):
 
1319
        # This is to support the somewhat obsolete path_content_summary method
 
1320
        # with content filtering: see
 
1321
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1322
        #
 
1323
        # If the dirstate cache is up to date and knows the hash and size,
 
1324
        # return that.
 
1325
        # Otherwise if there are no content filters, return the on-disk size
 
1326
        # and leave the hash blank.
 
1327
        # Otherwise, read and filter the on-disk file and use its size and
 
1328
        # hash.
 
1329
        #
 
1330
        # The dirstate doesn't store the size of the canonical form so we
 
1331
        # can't trust it for content-filtered trees.  We just return None.
 
1332
        dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
 
1333
        executable = self._is_executable_from_path_and_stat(path, stat_result)
 
1334
        return ('file', None, executable, dirstate_sha1)
 
1335
 
 
1336
 
1324
1337
class WorkingTree4(DirStateWorkingTree):
1325
1338
    """This is the Format 4 working tree.
1326
1339
 
1334
1347
    """
1335
1348
 
1336
1349
 
1337
 
class WorkingTree5(DirStateWorkingTree):
 
1350
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1338
1351
    """This is the Format 5 working tree.
1339
1352
 
1340
1353
    This differs from WorkingTree4 by:
1344
1357
    """
1345
1358
 
1346
1359
 
1347
 
class WorkingTree6(DirStateWorkingTree):
 
1360
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1348
1361
    """This is the Format 6 working tree.
1349
1362
 
1350
1363
    This differs from WorkingTree5 by:
1403
1416
        wt.lock_tree_write()
1404
1417
        try:
1405
1418
            self._init_custom_control_files(wt)
1406
 
            if revision_id in (None, NULL_REVISION):
 
1419
            if revision_id in (None, _mod_revision.NULL_REVISION):
1407
1420
                if branch.repository.supports_rich_root():
1408
1421
                    wt._set_root_id(generate_ids.gen_root_id())
1409
1422
                else:
1420
1433
                    pass
1421
1434
            if basis is None:
1422
1435
                basis = branch.repository.revision_tree(revision_id)
1423
 
            if revision_id == NULL_REVISION:
 
1436
            if revision_id == _mod_revision.NULL_REVISION:
1424
1437
                parents_list = []
1425
1438
            else:
1426
1439
                parents_list = [(revision_id, basis)]
1434
1447
                if basis_root_id is not None:
1435
1448
                    wt._set_root_id(basis_root_id)
1436
1449
                    wt.flush()
 
1450
                if wt.supports_content_filtering():
 
1451
                    # The original tree may not have the same content filters
 
1452
                    # applied so we can't safely build the inventory delta from
 
1453
                    # the source tree.
 
1454
                    delta_from_tree = False
 
1455
                else:
 
1456
                    delta_from_tree = True
1437
1457
                # delta_from_tree is safe even for DirStateRevisionTrees,
1438
1458
                # because wt4.apply_inventory_delta does not mutate the input
1439
1459
                # inventory entries.
1440
1460
                transform.build_tree(basis, wt, accelerator_tree,
1441
 
                                     hardlink=hardlink, delta_from_tree=True)
 
1461
                                     hardlink=hardlink,
 
1462
                                     delta_from_tree=delta_from_tree)
1442
1463
            finally:
1443
1464
                basis.unlock()
1444
1465
        finally:
1552
1573
 
1553
1574
 
1554
1575
class DirStateRevisionTree(Tree):
1555
 
    """A revision tree pulling the inventory from a dirstate."""
 
1576
    """A revision tree pulling the inventory from a dirstate.
 
1577
    
 
1578
    Note that this is one of the historical (ie revision) trees cached in the
 
1579
    dirstate for easy access, not the workingtree.
 
1580
    """
1556
1581
 
1557
1582
    def __init__(self, dirstate, revision_id, repository):
1558
1583
        self._dirstate = dirstate
1829
1854
            return None
1830
1855
        return ie.executable
1831
1856
 
1832
 
    def list_files(self, include_root=False):
 
1857
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1833
1858
        # We use a standard implementation, because DirStateRevisionTree is
1834
1859
        # dealing with one of the parents of the current state
1835
1860
        inv = self._get_inventory()
1836
 
        entries = inv.iter_entries()
1837
 
        if self.inventory.root is not None and not include_root:
 
1861
        if from_dir is None:
 
1862
            from_dir_id = None
 
1863
        else:
 
1864
            from_dir_id = inv.path2id(from_dir)
 
1865
            if from_dir_id is None:
 
1866
                # Directory not versioned
 
1867
                return
 
1868
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
 
1869
        if inv.root is not None and not include_root and from_dir is None:
1838
1870
            entries.next()
1839
1871
        for path, entry in entries:
1840
1872
            yield path, 'V', entry.kind, entry.file_id, entry
1942
1974
        return result
1943
1975
 
1944
1976
    @classmethod
1945
 
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
 
1977
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
 
1978
                                                  target):
1946
1979
        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
 
1980
            compiled_dirstate_helpers_feature
 
1981
        test_case.requireFeature(compiled_dirstate_helpers_feature)
 
1982
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1952
1983
        result = klass.make_source_parent_tree(source, target)
1953
1984
        result[1]._iter_changes = ProcessEntryC
1954
1985
        return result
1984
2015
            output. An unversioned file is defined as one with (False, False)
1985
2016
            for the versioned pair.
1986
2017
        """
1987
 
        # NB: show_status depends on being able to pass in non-versioned files
1988
 
        # and report them as unknown
1989
2018
        # TODO: handle extra trees in the dirstate.
1990
2019
        if (extra_trees or specific_files == []):
1991
2020
            # we can't fast-path these cases (yet)
1994
2023
                require_versioned, want_unversioned=want_unversioned)
1995
2024
        parent_ids = self.target.get_parent_ids()
1996
2025
        if not (self.source._revision_id in parent_ids
1997
 
                or self.source._revision_id == NULL_REVISION):
 
2026
                or self.source._revision_id == _mod_revision.NULL_REVISION):
1998
2027
            raise AssertionError(
1999
2028
                "revision {%s} is not stored in {%s}, but %s "
2000
2029
                "can only be used for trees stored in the dirstate"
2001
2030
                % (self.source._revision_id, self.target, self.iter_changes))
2002
2031
        target_index = 0
2003
 
        if self.source._revision_id == NULL_REVISION:
 
2032
        if self.source._revision_id == _mod_revision.NULL_REVISION:
2004
2033
            source_index = None
2005
2034
            indices = (target_index,)
2006
2035
        else:
2021
2050
        else:
2022
2051
            specific_files = set([''])
2023
2052
        # -- specific_files is now a utf8 path set --
2024
 
        search_specific_files = set()
 
2053
 
2025
2054
        # -- get the state object and prepare it.
2026
2055
        state = self.target.current_dirstate()
2027
2056
        state._read_dirblocks_if_needed()
2028
2057
        if require_versioned:
2029
2058
            # -- check all supplied paths are versioned in a search tree. --
2030
 
            all_versioned = True
 
2059
            not_versioned = []
2031
2060
            for path in specific_files:
2032
2061
                path_entries = state._entries_for_path(path)
2033
2062
                if not path_entries:
2034
2063
                    # this specified path is not present at all: error
2035
 
                    all_versioned = False
2036
 
                    break
 
2064
                    not_versioned.append(path)
 
2065
                    continue
2037
2066
                found_versioned = False
2038
2067
                # for each id at this path
2039
2068
                for entry in path_entries:
2046
2075
                if not found_versioned:
2047
2076
                    # none of the indexes was not 'absent' at all ids for this
2048
2077
                    # path.
2049
 
                    all_versioned = False
2050
 
                    break
2051
 
            if not all_versioned:
2052
 
                raise errors.PathsNotVersionedError(specific_files)
 
2078
                    not_versioned.append(path)
 
2079
            if len(not_versioned) > 0:
 
2080
                raise errors.PathsNotVersionedError(not_versioned)
2053
2081
        # -- 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)
 
2082
        search_specific_files = osutils.minimum_path_selection(specific_files)
2059
2083
 
2060
2084
        use_filesystem_for_exec = (sys.platform != 'win32')
2061
2085
        iter_changes = self.target._iter_changes(include_unchanged,
2073
2097
            (revisiontree.RevisionTree, DirStateRevisionTree)):
2074
2098
            return False
2075
2099
        # the source revid must be in the target dirstate
2076
 
        if not (source._revision_id == NULL_REVISION or
 
2100
        if not (source._revision_id == _mod_revision.NULL_REVISION or
2077
2101
            source._revision_id in target.get_parent_ids()):
2078
2102
            # TODO: what about ghosts? it may well need to
2079
2103
            # check for them explicitly.