~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: 2011-06-30 18:28:17 UTC
  • mfrom: (5967.10.2 test-cat)
  • Revision ID: pqm@pqm.ubuntu.com-20110630182817-83a5q9r9rxfkdn8r
(mbp) don't use subprocesses for testing cat (Martin Pool)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2012 Canonical Ltd
 
1
# Copyright (C) 2007-2011 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
36
34
from bzrlib import (
37
35
    bzrdir,
38
36
    cache_utf8,
39
 
    config,
40
37
    conflicts as _mod_conflicts,
41
 
    controldir,
42
38
    debug,
43
39
    dirstate,
44
40
    errors,
58
54
from bzrlib.lock import LogicalLockResult
59
55
from bzrlib.lockable_files import LockableFiles
60
56
from bzrlib.lockdir import LockDir
61
 
from bzrlib.mutabletree import (
62
 
    MutableTree,
63
 
    needs_tree_write_lock,
64
 
    )
 
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
 
    )
76
65
from bzrlib.transport.local import LocalTransport
77
66
from bzrlib.tree import (
78
67
    InterTree,
81
70
from bzrlib.workingtree import (
82
71
    InventoryWorkingTree,
83
72
    WorkingTree,
84
 
    WorkingTreeFormatMetaDir,
 
73
    WorkingTreeFormat,
85
74
    )
86
75
 
87
76
 
88
77
class DirStateWorkingTree(InventoryWorkingTree):
89
78
 
 
79
    _DEFAULT_WORTH_SAVING_LIMIT = 10
 
80
 
90
81
    def __init__(self, basedir,
91
82
                 branch,
92
83
                 _control_files=None,
260
251
 
261
252
        :return: an integer. -1 means never save.
262
253
        """
263
 
        conf = self.get_config_stack()
264
 
        return conf.get('bzr.workingtree.worth_saving_limit')
 
254
        config = self.branch.get_config()
 
255
        val = config.get_user_option('bzr.workingtree.worth_saving_limit')
 
256
        if val is None:
 
257
            val = self._DEFAULT_WORTH_SAVING_LIMIT
 
258
        else:
 
259
            try:
 
260
                val = int(val)
 
261
            except ValueError, e:
 
262
                trace.warning('Invalid config value for'
 
263
                              ' "bzr.workingtree.worth_saving_limit"'
 
264
                              ' value %r is not an integer.'
 
265
                              % (val,))
 
266
                val = self._DEFAULT_WORTH_SAVING_LIMIT
 
267
        return val
265
268
 
266
269
    def filter_unversioned_files(self, paths):
267
270
        """Filter out paths that are versioned.
419
422
                return link_or_sha1
420
423
        return None
421
424
 
422
 
    def _get_root_inventory(self):
 
425
    def _get_inventory(self):
423
426
        """Get the inventory for the tree. This is only valid within a lock."""
424
427
        if 'evil' in debug.debug_flags:
425
428
            trace.mutter_callsite(2,
430
433
        self._generate_inventory()
431
434
        return self._inventory
432
435
 
433
 
    @deprecated_method(deprecated_in((2, 5, 0)))
434
 
    def _get_inventory(self):
435
 
        return self.root_inventory
436
 
 
437
436
    inventory = property(_get_inventory,
438
437
                         doc="Inventory of this Tree")
439
438
 
440
 
    root_inventory = property(_get_root_inventory,
441
 
        "Root inventory of this tree")
442
 
 
443
439
    @needs_read_lock
444
440
    def get_parent_ids(self):
445
441
        """See Tree.get_parent_ids.
492
488
            return False # Missing entries are not executable
493
489
        return entry[1][0][3] # Executable?
494
490
 
495
 
    def is_executable(self, file_id, path=None):
496
 
        """Test if a file is executable or not.
 
491
    if not osutils.supports_executable():
 
492
        def is_executable(self, file_id, path=None):
 
493
            """Test if a file is executable or not.
497
494
 
498
 
        Note: The caller is expected to take a read-lock before calling this.
499
 
        """
500
 
        if not self._supports_executable():
 
495
            Note: The caller is expected to take a read-lock before calling this.
 
496
            """
501
497
            entry = self._get_entry(file_id=file_id, path=path)
502
498
            if entry == (None, None):
503
499
                return False
504
500
            return entry[1][0][3]
505
 
        else:
 
501
 
 
502
        _is_executable_from_path_and_stat = \
 
503
            _is_executable_from_path_and_stat_from_basis
 
504
    else:
 
505
        def is_executable(self, file_id, path=None):
 
506
            """Test if a file is executable or not.
 
507
 
 
508
            Note: The caller is expected to take a read-lock before calling this.
 
509
            """
506
510
            self._must_be_locked()
507
511
            if not path:
508
512
                path = self.id2path(file_id)
692
696
 
693
697
        if self._inventory is not None:
694
698
            update_inventory = True
695
 
            inv = self.root_inventory
 
699
            inv = self.inventory
696
700
            to_dir_id = to_entry[0][2]
697
701
            to_dir_ie = inv[to_dir_id]
698
702
        else:
894
898
    @needs_read_lock
895
899
    def path2id(self, path):
896
900
        """Return the id for path in this tree."""
897
 
        if isinstance(path, list):
898
 
            if path == []:
899
 
                path = [""]
900
 
            path = osutils.pathjoin(*path)
901
901
        path = path.strip('/')
902
902
        entry = self._get_entry(path=path)
903
903
        if entry == (None, None):
981
981
                    all_versioned = False
982
982
                    break
983
983
            if not all_versioned:
984
 
                raise errors.PathsNotVersionedError(
985
 
                    [p.decode('utf-8') for p in paths])
 
984
                raise errors.PathsNotVersionedError(paths)
986
985
        # -- remove redundancy in supplied paths to prevent over-scanning --
987
986
        search_paths = osutils.minimum_path_selection(paths)
988
987
        # sketch:
1037
1036
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1038
1037
            for dir_name in split_paths:
1039
1038
                if dir_name not in found_dir_names:
1040
 
                    raise errors.PathsNotVersionedError(
1041
 
                        [p.decode('utf-8') for p in paths])
 
1039
                    raise errors.PathsNotVersionedError(paths)
1042
1040
 
1043
1041
        for dir_name_id, trees_info in found.iteritems():
1044
1042
            for index in search_indexes:
1051
1049
 
1052
1050
        This is a meaningless operation for dirstate, but we obey it anyhow.
1053
1051
        """
1054
 
        return self.root_inventory
 
1052
        return self.inventory
1055
1053
 
1056
1054
    @needs_read_lock
1057
1055
    def revision_tree(self, revision_id):
1165
1163
                # _make_delta if we can't get the RevisionTree
1166
1164
                pass
1167
1165
            else:
1168
 
                delta = rev_tree.root_inventory._make_delta(
1169
 
                    basis_tree.root_inventory)
 
1166
                delta = rev_tree.inventory._make_delta(basis_tree.inventory)
1170
1167
                dirstate.update_basis_by_delta(delta, rev_id)
1171
1168
                updated = True
1172
1169
        if not updated:
1343
1340
        # being created.
1344
1341
        self._inventory = None
1345
1342
        # generate a delta,
1346
 
        delta = inv._make_delta(self.root_inventory)
 
1343
        delta = inv._make_delta(self.inventory)
1347
1344
        # and apply it.
1348
1345
        self.apply_inventory_delta(delta)
1349
1346
        if had_inventory:
1369
1366
            base_tree = trees[0][1]
1370
1367
        state = self.current_dirstate()
1371
1368
        # We don't support ghosts yet
1372
 
        state.set_state_from_scratch(base_tree.root_inventory, trees, [])
 
1369
        state.set_state_from_scratch(base_tree.inventory, trees, [])
1373
1370
 
1374
1371
 
1375
1372
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1461
1458
        return views.PathBasedViews(self)
1462
1459
 
1463
1460
 
1464
 
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
 
1461
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1465
1462
 
1466
1463
    missing_parent_conflicts = True
1467
1464
 
1497
1494
        control_files = self._open_control_files(a_bzrdir)
1498
1495
        control_files.create_lock()
1499
1496
        control_files.lock_write()
1500
 
        transport.put_bytes('format', self.as_string(),
 
1497
        transport.put_bytes('format', self.get_format_string(),
1501
1498
            mode=a_bzrdir._get_file_mode())
1502
1499
        if from_branch is not None:
1503
1500
            branch = from_branch
1563
1560
                transform.build_tree(basis, wt, accelerator_tree,
1564
1561
                                     hardlink=hardlink,
1565
1562
                                     delta_from_tree=delta_from_tree)
1566
 
                for hook in MutableTree.hooks['post_build_tree']:
1567
 
                    hook(wt)
1568
1563
            finally:
1569
1564
                basis.unlock()
1570
1565
        finally:
1613
1608
    def _get_matchingbzrdir(self):
1614
1609
        """Overrideable method to get a bzrdir for testing."""
1615
1610
        # please test against something that will let us do tree references
1616
 
        return controldir.format_registry.make_bzrdir(
1617
 
            'development-subtree')
 
1611
        return bzrdir.format_registry.make_bzrdir(
 
1612
            'dirstate-with-subtree')
1618
1613
 
1619
1614
    _matchingbzrdir = property(__get_matchingbzrdir)
1620
1615
 
1625
1620
    This format:
1626
1621
        - exists within a metadir controlling .bzr
1627
1622
        - includes an explicit version marker for the workingtree control
1628
 
          files, separate from the ControlDir format
 
1623
          files, separate from the BzrDir format
1629
1624
        - modifies the hash cache format
1630
1625
        - is new in bzr 0.15
1631
1626
        - uses a LockDir to guard access to it.
1635
1630
 
1636
1631
    _tree_class = WorkingTree4
1637
1632
 
1638
 
    @classmethod
1639
 
    def get_format_string(cls):
 
1633
    def get_format_string(self):
1640
1634
        """See WorkingTreeFormat.get_format_string()."""
1641
1635
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1642
1636
 
1653
1647
 
1654
1648
    _tree_class = WorkingTree5
1655
1649
 
1656
 
    @classmethod
1657
 
    def get_format_string(cls):
 
1650
    def get_format_string(self):
1658
1651
        """See WorkingTreeFormat.get_format_string()."""
1659
1652
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1660
1653
 
1674
1667
 
1675
1668
    _tree_class = WorkingTree6
1676
1669
 
1677
 
    @classmethod
1678
 
    def get_format_string(cls):
 
1670
    def get_format_string(self):
1679
1671
        """See WorkingTreeFormat.get_format_string()."""
1680
1672
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1681
1673
 
1693
1685
    def supports_views(self):
1694
1686
        return True
1695
1687
 
1696
 
    def _get_matchingbzrdir(self):
1697
 
        """Overrideable method to get a bzrdir for testing."""
1698
 
        # We use 'development-subtree' instead of '2a', because we have a
1699
 
        # few tests that want to test tree references
1700
 
        return bzrdir.format_registry.make_bzrdir('development-subtree')
1701
 
 
1702
1688
 
1703
1689
class DirStateRevisionTree(InventoryTree):
1704
1690
    """A revision tree pulling the inventory from a dirstate.
1787
1773
        if path is not None:
1788
1774
            path = path.encode('utf8')
1789
1775
        parent_index = self._get_parent_index()
1790
 
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id,
1791
 
            path_utf8=path)
 
1776
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1792
1777
 
1793
1778
    def _generate_inventory(self):
1794
1779
        """Create and set self.inventory from the dirstate object.
1876
1861
        # Make sure the file exists
1877
1862
        entry = self._get_entry(file_id, path=path)
1878
1863
        if entry == (None, None): # do we raise?
1879
 
            raise errors.NoSuchId(self, file_id)
 
1864
            return None
1880
1865
        parent_index = self._get_parent_index()
1881
1866
        last_changed_revision = entry[1][parent_index][4]
1882
1867
        try:
1895
1880
 
1896
1881
    @needs_read_lock
1897
1882
    def get_file_revision(self, file_id):
1898
 
        inv, inv_file_id = self._unpack_file_id(file_id)
1899
 
        return inv[inv_file_id].revision
 
1883
        return self.inventory[file_id].revision
1900
1884
 
1901
1885
    def get_file(self, file_id, path=None):
1902
1886
        return StringIO(self.get_file_text(file_id))
1903
1887
 
1904
1888
    def get_file_size(self, file_id):
1905
1889
        """See Tree.get_file_size"""
1906
 
        inv, inv_file_id = self._unpack_file_id(file_id)
1907
 
        return inv[inv_file_id].text_size
 
1890
        return self.inventory[file_id].text_size
1908
1891
 
1909
1892
    def get_file_text(self, file_id, path=None):
1910
 
        content = None
1911
 
        for _, content_iter in self.iter_files_bytes([(file_id, None)]):
1912
 
            if content is not None:
1913
 
                raise AssertionError('iter_files_bytes returned'
1914
 
                    ' too many entries')
1915
 
            # For each entry returned by iter_files_bytes, we must consume the
1916
 
            # content_iter before we step the files iterator.
1917
 
            content = ''.join(content_iter)
1918
 
        if content is None:
1919
 
            raise AssertionError('iter_files_bytes did not return'
1920
 
                ' the requested data')
1921
 
        return content
 
1893
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
 
1894
        return ''.join(content)
1922
1895
 
1923
1896
    def get_reference_revision(self, file_id, path=None):
1924
 
        inv, inv_file_id = self._unpack_file_id(file_id)
1925
 
        return inv[inv_file_id].reference_revision
 
1897
        return self.inventory[file_id].reference_revision
1926
1898
 
1927
1899
    def iter_files_bytes(self, desired_files):
1928
1900
        """See Tree.iter_files_bytes.
1952
1924
        """Return the revision id for this tree."""
1953
1925
        return self._revision_id
1954
1926
 
1955
 
    def _get_root_inventory(self):
 
1927
    def _get_inventory(self):
1956
1928
        if self._inventory is not None:
1957
1929
            return self._inventory
1958
1930
        self._must_be_locked()
1959
1931
        self._generate_inventory()
1960
1932
        return self._inventory
1961
1933
 
1962
 
    root_inventory = property(_get_root_inventory,
1963
 
                         doc="Inventory of this Tree")
1964
 
 
1965
 
    @deprecated_method(deprecated_in((2, 5, 0)))
1966
 
    def _get_inventory(self):
1967
 
        return self.root_inventory
1968
 
 
1969
1934
    inventory = property(_get_inventory,
1970
1935
                         doc="Inventory of this Tree")
1971
1936
 
1989
1954
 
1990
1955
    def path_content_summary(self, path):
1991
1956
        """See Tree.path_content_summary."""
1992
 
        inv, inv_file_id = self._path2inv_file_id(path)
1993
 
        if inv_file_id is None:
 
1957
        id = self.inventory.path2id(path)
 
1958
        if id is None:
1994
1959
            return ('missing', None, None, None)
1995
 
        entry = inv[inv_file_id]
 
1960
        entry = self._inventory[id]
1996
1961
        kind = entry.kind
1997
1962
        if kind == 'file':
1998
1963
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
2002
1967
            return (kind, None, None, None)
2003
1968
 
2004
1969
    def is_executable(self, file_id, path=None):
2005
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2006
 
        ie = inv[inv_file_id]
 
1970
        ie = self.inventory[file_id]
2007
1971
        if ie.kind != "file":
2008
1972
            return False
2009
1973
        return ie.executable
2014
1978
    def list_files(self, include_root=False, from_dir=None, recursive=True):
2015
1979
        # We use a standard implementation, because DirStateRevisionTree is
2016
1980
        # dealing with one of the parents of the current state
 
1981
        inv = self._get_inventory()
2017
1982
        if from_dir is None:
2018
 
            inv = self.root_inventory
2019
1983
            from_dir_id = None
2020
1984
        else:
2021
 
            inv, from_dir_id = self._path2inv_file_id(from_dir)
 
1985
            from_dir_id = inv.path2id(from_dir)
2022
1986
            if from_dir_id is None:
2023
1987
                # Directory not versioned
2024
1988
                return
2025
 
        # FIXME: Support nested trees
2026
1989
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
2027
1990
        if inv.root is not None and not include_root and from_dir is None:
2028
1991
            entries.next()
2050
2013
    def path2id(self, path):
2051
2014
        """Return the id for path in this tree."""
2052
2015
        # lookup by path: faster than splitting and walking the ivnentory.
2053
 
        if isinstance(path, list):
2054
 
            if path == []:
2055
 
                path = [""]
2056
 
            path = osutils.pathjoin(*path)
2057
2016
        entry = self._get_entry(path=path)
2058
2017
        if entry == (None, None):
2059
2018
            return None
2082
2041
        # So for now, we just build up the parent inventory, and extract
2083
2042
        # it the same way RevisionTree does.
2084
2043
        _directory = 'directory'
2085
 
        inv = self._get_root_inventory()
 
2044
        inv = self._get_inventory()
2086
2045
        top_id = inv.path2id(prefix)
2087
2046
        if top_id is None:
2088
2047
            pending = []
2227
2186
                path_entries = state._entries_for_path(path)
2228
2187
                if not path_entries:
2229
2188
                    # this specified path is not present at all: error
2230
 
                    not_versioned.append(path.decode('utf-8'))
 
2189
                    not_versioned.append(path)
2231
2190
                    continue
2232
2191
                found_versioned = False
2233
2192
                # for each id at this path
2241
2200
                if not found_versioned:
2242
2201
                    # none of the indexes was not 'absent' at all ids for this
2243
2202
                    # path.
2244
 
                    not_versioned.append(path.decode('utf-8'))
 
2203
                    not_versioned.append(path)
2245
2204
            if len(not_versioned) > 0:
2246
2205
                raise errors.PathsNotVersionedError(not_versioned)
2247
2206
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2314
2273
    def update_format(self, tree):
2315
2274
        """Change the format marker."""
2316
2275
        tree._transport.put_bytes('format',
2317
 
            self.target_format.as_string(),
 
2276
            self.target_format.get_format_string(),
2318
2277
            mode=tree.bzrdir._get_file_mode())
2319
2278
 
2320
2279
 
2337
2296
    def update_format(self, tree):
2338
2297
        """Change the format marker."""
2339
2298
        tree._transport.put_bytes('format',
2340
 
            self.target_format.as_string(),
 
2299
            self.target_format.get_format_string(),
2341
2300
            mode=tree.bzrdir._get_file_mode())
2342
2301
 
2343
2302
 
2366
2325
    def update_format(self, tree):
2367
2326
        """Change the format marker."""
2368
2327
        tree._transport.put_bytes('format',
2369
 
            self.target_format.as_string(),
 
2328
            self.target_format.get_format_string(),
2370
2329
            mode=tree.bzrdir._get_file_mode())