~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Patch Queue Manager
  • Date: 2015-12-17 18:39:00 UTC
  • mfrom: (6606.1.2 fix-float)
  • Revision ID: pqm@pqm.ubuntu.com-20151217183900-0719du2uv1kwu3lc
(vila) Inline testtools private method to fix an issue in xenial (the
 private implementation has changed in an backward incompatible way).
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2007-2012 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
 
25
27
from cStringIO import StringIO
26
28
import os
27
29
import sys
36
38
    cache_utf8,
37
39
    config,
38
40
    conflicts as _mod_conflicts,
 
41
    controldir,
39
42
    debug,
40
43
    dirstate,
41
44
    errors,
55
58
from bzrlib.lock import LogicalLockResult
56
59
from bzrlib.lockable_files import LockableFiles
57
60
from bzrlib.lockdir import LockDir
58
 
from bzrlib.mutabletree import needs_tree_write_lock
 
61
from bzrlib.mutabletree import (
 
62
    MutableTree,
 
63
    needs_tree_write_lock,
 
64
    )
59
65
from bzrlib.osutils import (
60
66
    file_kind,
61
67
    isdir,
63
69
    realpath,
64
70
    safe_unicode,
65
71
    )
 
72
from bzrlib.symbol_versioning import (
 
73
    deprecated_in,
 
74
    deprecated_method,
 
75
    )
66
76
from bzrlib.transport.local import LocalTransport
67
77
from bzrlib.tree import (
68
78
    InterTree,
71
81
from bzrlib.workingtree import (
72
82
    InventoryWorkingTree,
73
83
    WorkingTree,
74
 
    WorkingTreeFormat,
 
84
    WorkingTreeFormatMetaDir,
75
85
    )
76
86
 
77
87
 
250
260
 
251
261
        :return: an integer. -1 means never save.
252
262
        """
253
 
        # FIXME: We want a WorkingTreeStack here -- vila 20110812
254
 
        conf = config.BranchStack(self.branch)
 
263
        conf = self.get_config_stack()
255
264
        return conf.get('bzr.workingtree.worth_saving_limit')
256
265
 
257
266
    def filter_unversioned_files(self, paths):
410
419
                return link_or_sha1
411
420
        return None
412
421
 
413
 
    def _get_inventory(self):
 
422
    def _get_root_inventory(self):
414
423
        """Get the inventory for the tree. This is only valid within a lock."""
415
424
        if 'evil' in debug.debug_flags:
416
425
            trace.mutter_callsite(2,
421
430
        self._generate_inventory()
422
431
        return self._inventory
423
432
 
 
433
    @deprecated_method(deprecated_in((2, 5, 0)))
 
434
    def _get_inventory(self):
 
435
        return self.root_inventory
 
436
 
424
437
    inventory = property(_get_inventory,
425
438
                         doc="Inventory of this Tree")
426
439
 
 
440
    root_inventory = property(_get_root_inventory,
 
441
        "Root inventory of this tree")
 
442
 
427
443
    @needs_read_lock
428
444
    def get_parent_ids(self):
429
445
        """See Tree.get_parent_ids.
476
492
            return False # Missing entries are not executable
477
493
        return entry[1][0][3] # Executable?
478
494
 
479
 
    if not osutils.supports_executable():
480
 
        def is_executable(self, file_id, path=None):
481
 
            """Test if a file is executable or not.
 
495
    def is_executable(self, file_id, path=None):
 
496
        """Test if a file is executable or not.
482
497
 
483
 
            Note: The caller is expected to take a read-lock before calling this.
484
 
            """
 
498
        Note: The caller is expected to take a read-lock before calling this.
 
499
        """
 
500
        if not self._supports_executable():
485
501
            entry = self._get_entry(file_id=file_id, path=path)
486
502
            if entry == (None, None):
487
503
                return False
488
504
            return entry[1][0][3]
489
 
 
490
 
        _is_executable_from_path_and_stat = \
491
 
            _is_executable_from_path_and_stat_from_basis
492
 
    else:
493
 
        def is_executable(self, file_id, path=None):
494
 
            """Test if a file is executable or not.
495
 
 
496
 
            Note: The caller is expected to take a read-lock before calling this.
497
 
            """
 
505
        else:
498
506
            self._must_be_locked()
499
507
            if not path:
500
508
                path = self.id2path(file_id)
684
692
 
685
693
        if self._inventory is not None:
686
694
            update_inventory = True
687
 
            inv = self.inventory
 
695
            inv = self.root_inventory
688
696
            to_dir_id = to_entry[0][2]
689
697
            to_dir_ie = inv[to_dir_id]
690
698
        else:
886
894
    @needs_read_lock
887
895
    def path2id(self, path):
888
896
        """Return the id for path in this tree."""
 
897
        if isinstance(path, list):
 
898
            if path == []:
 
899
                path = [""]
 
900
            path = osutils.pathjoin(*path)
889
901
        path = path.strip('/')
890
902
        entry = self._get_entry(path=path)
891
903
        if entry == (None, None):
969
981
                    all_versioned = False
970
982
                    break
971
983
            if not all_versioned:
972
 
                raise errors.PathsNotVersionedError(paths)
 
984
                raise errors.PathsNotVersionedError(
 
985
                    [p.decode('utf-8') for p in paths])
973
986
        # -- remove redundancy in supplied paths to prevent over-scanning --
974
987
        search_paths = osutils.minimum_path_selection(paths)
975
988
        # sketch:
1024
1037
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1025
1038
            for dir_name in split_paths:
1026
1039
                if dir_name not in found_dir_names:
1027
 
                    raise errors.PathsNotVersionedError(paths)
 
1040
                    raise errors.PathsNotVersionedError(
 
1041
                        [p.decode('utf-8') for p in paths])
1028
1042
 
1029
1043
        for dir_name_id, trees_info in found.iteritems():
1030
1044
            for index in search_indexes:
1037
1051
 
1038
1052
        This is a meaningless operation for dirstate, but we obey it anyhow.
1039
1053
        """
1040
 
        return self.inventory
 
1054
        return self.root_inventory
1041
1055
 
1042
1056
    @needs_read_lock
1043
1057
    def revision_tree(self, revision_id):
1151
1165
                # _make_delta if we can't get the RevisionTree
1152
1166
                pass
1153
1167
            else:
1154
 
                delta = rev_tree.inventory._make_delta(basis_tree.inventory)
 
1168
                delta = rev_tree.root_inventory._make_delta(
 
1169
                    basis_tree.root_inventory)
1155
1170
                dirstate.update_basis_by_delta(delta, rev_id)
1156
1171
                updated = True
1157
1172
        if not updated:
1328
1343
        # being created.
1329
1344
        self._inventory = None
1330
1345
        # generate a delta,
1331
 
        delta = inv._make_delta(self.inventory)
 
1346
        delta = inv._make_delta(self.root_inventory)
1332
1347
        # and apply it.
1333
1348
        self.apply_inventory_delta(delta)
1334
1349
        if had_inventory:
1354
1369
            base_tree = trees[0][1]
1355
1370
        state = self.current_dirstate()
1356
1371
        # We don't support ghosts yet
1357
 
        state.set_state_from_scratch(base_tree.inventory, trees, [])
 
1372
        state.set_state_from_scratch(base_tree.root_inventory, trees, [])
1358
1373
 
1359
1374
 
1360
1375
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1446
1461
        return views.PathBasedViews(self)
1447
1462
 
1448
1463
 
1449
 
class DirStateWorkingTreeFormat(WorkingTreeFormat):
 
1464
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
1450
1465
 
1451
1466
    missing_parent_conflicts = True
1452
1467
 
1482
1497
        control_files = self._open_control_files(a_bzrdir)
1483
1498
        control_files.create_lock()
1484
1499
        control_files.lock_write()
1485
 
        transport.put_bytes('format', self.get_format_string(),
 
1500
        transport.put_bytes('format', self.as_string(),
1486
1501
            mode=a_bzrdir._get_file_mode())
1487
1502
        if from_branch is not None:
1488
1503
            branch = from_branch
1548
1563
                transform.build_tree(basis, wt, accelerator_tree,
1549
1564
                                     hardlink=hardlink,
1550
1565
                                     delta_from_tree=delta_from_tree)
 
1566
                for hook in MutableTree.hooks['post_build_tree']:
 
1567
                    hook(wt)
1551
1568
            finally:
1552
1569
                basis.unlock()
1553
1570
        finally:
1596
1613
    def _get_matchingbzrdir(self):
1597
1614
        """Overrideable method to get a bzrdir for testing."""
1598
1615
        # please test against something that will let us do tree references
1599
 
        return bzrdir.format_registry.make_bzrdir(
1600
 
            'dirstate-with-subtree')
 
1616
        return controldir.format_registry.make_bzrdir(
 
1617
            'development-subtree')
1601
1618
 
1602
1619
    _matchingbzrdir = property(__get_matchingbzrdir)
1603
1620
 
1608
1625
    This format:
1609
1626
        - exists within a metadir controlling .bzr
1610
1627
        - includes an explicit version marker for the workingtree control
1611
 
          files, separate from the BzrDir format
 
1628
          files, separate from the ControlDir format
1612
1629
        - modifies the hash cache format
1613
1630
        - is new in bzr 0.15
1614
1631
        - uses a LockDir to guard access to it.
1618
1635
 
1619
1636
    _tree_class = WorkingTree4
1620
1637
 
1621
 
    def get_format_string(self):
 
1638
    @classmethod
 
1639
    def get_format_string(cls):
1622
1640
        """See WorkingTreeFormat.get_format_string()."""
1623
1641
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1624
1642
 
1635
1653
 
1636
1654
    _tree_class = WorkingTree5
1637
1655
 
1638
 
    def get_format_string(self):
 
1656
    @classmethod
 
1657
    def get_format_string(cls):
1639
1658
        """See WorkingTreeFormat.get_format_string()."""
1640
1659
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1641
1660
 
1655
1674
 
1656
1675
    _tree_class = WorkingTree6
1657
1676
 
1658
 
    def get_format_string(self):
 
1677
    @classmethod
 
1678
    def get_format_string(cls):
1659
1679
        """See WorkingTreeFormat.get_format_string()."""
1660
1680
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1661
1681
 
1673
1693
    def supports_views(self):
1674
1694
        return True
1675
1695
 
 
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
 
1676
1702
 
1677
1703
class DirStateRevisionTree(InventoryTree):
1678
1704
    """A revision tree pulling the inventory from a dirstate.
1761
1787
        if path is not None:
1762
1788
            path = path.encode('utf8')
1763
1789
        parent_index = self._get_parent_index()
1764
 
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
 
1790
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id,
 
1791
            path_utf8=path)
1765
1792
 
1766
1793
    def _generate_inventory(self):
1767
1794
        """Create and set self.inventory from the dirstate object.
1868
1895
 
1869
1896
    @needs_read_lock
1870
1897
    def get_file_revision(self, file_id):
1871
 
        return self.inventory[file_id].revision
 
1898
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1899
        return inv[inv_file_id].revision
1872
1900
 
1873
1901
    def get_file(self, file_id, path=None):
1874
1902
        return StringIO(self.get_file_text(file_id))
1875
1903
 
1876
1904
    def get_file_size(self, file_id):
1877
1905
        """See Tree.get_file_size"""
1878
 
        return self.inventory[file_id].text_size
 
1906
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1907
        return inv[inv_file_id].text_size
1879
1908
 
1880
1909
    def get_file_text(self, file_id, path=None):
1881
 
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1882
 
        return ''.join(content)
 
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
1883
1922
 
1884
1923
    def get_reference_revision(self, file_id, path=None):
1885
 
        return self.inventory[file_id].reference_revision
 
1924
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1925
        return inv[inv_file_id].reference_revision
1886
1926
 
1887
1927
    def iter_files_bytes(self, desired_files):
1888
1928
        """See Tree.iter_files_bytes.
1912
1952
        """Return the revision id for this tree."""
1913
1953
        return self._revision_id
1914
1954
 
1915
 
    def _get_inventory(self):
 
1955
    def _get_root_inventory(self):
1916
1956
        if self._inventory is not None:
1917
1957
            return self._inventory
1918
1958
        self._must_be_locked()
1919
1959
        self._generate_inventory()
1920
1960
        return self._inventory
1921
1961
 
 
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
 
1922
1969
    inventory = property(_get_inventory,
1923
1970
                         doc="Inventory of this Tree")
1924
1971
 
1942
1989
 
1943
1990
    def path_content_summary(self, path):
1944
1991
        """See Tree.path_content_summary."""
1945
 
        id = self.inventory.path2id(path)
1946
 
        if id is None:
 
1992
        inv, inv_file_id = self._path2inv_file_id(path)
 
1993
        if inv_file_id is None:
1947
1994
            return ('missing', None, None, None)
1948
 
        entry = self._inventory[id]
 
1995
        entry = inv[inv_file_id]
1949
1996
        kind = entry.kind
1950
1997
        if kind == 'file':
1951
1998
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
1955
2002
            return (kind, None, None, None)
1956
2003
 
1957
2004
    def is_executable(self, file_id, path=None):
1958
 
        ie = self.inventory[file_id]
 
2005
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2006
        ie = inv[inv_file_id]
1959
2007
        if ie.kind != "file":
1960
2008
            return False
1961
2009
        return ie.executable
1966
2014
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1967
2015
        # We use a standard implementation, because DirStateRevisionTree is
1968
2016
        # dealing with one of the parents of the current state
1969
 
        inv = self._get_inventory()
1970
2017
        if from_dir is None:
 
2018
            inv = self.root_inventory
1971
2019
            from_dir_id = None
1972
2020
        else:
1973
 
            from_dir_id = inv.path2id(from_dir)
 
2021
            inv, from_dir_id = self._path2inv_file_id(from_dir)
1974
2022
            if from_dir_id is None:
1975
2023
                # Directory not versioned
1976
2024
                return
 
2025
        # FIXME: Support nested trees
1977
2026
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1978
2027
        if inv.root is not None and not include_root and from_dir is None:
1979
2028
            entries.next()
2001
2050
    def path2id(self, path):
2002
2051
        """Return the id for path in this tree."""
2003
2052
        # 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)
2004
2057
        entry = self._get_entry(path=path)
2005
2058
        if entry == (None, None):
2006
2059
            return None
2029
2082
        # So for now, we just build up the parent inventory, and extract
2030
2083
        # it the same way RevisionTree does.
2031
2084
        _directory = 'directory'
2032
 
        inv = self._get_inventory()
 
2085
        inv = self._get_root_inventory()
2033
2086
        top_id = inv.path2id(prefix)
2034
2087
        if top_id is None:
2035
2088
            pending = []
2174
2227
                path_entries = state._entries_for_path(path)
2175
2228
                if not path_entries:
2176
2229
                    # this specified path is not present at all: error
2177
 
                    not_versioned.append(path)
 
2230
                    not_versioned.append(path.decode('utf-8'))
2178
2231
                    continue
2179
2232
                found_versioned = False
2180
2233
                # for each id at this path
2188
2241
                if not found_versioned:
2189
2242
                    # none of the indexes was not 'absent' at all ids for this
2190
2243
                    # path.
2191
 
                    not_versioned.append(path)
 
2244
                    not_versioned.append(path.decode('utf-8'))
2192
2245
            if len(not_versioned) > 0:
2193
2246
                raise errors.PathsNotVersionedError(not_versioned)
2194
2247
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2261
2314
    def update_format(self, tree):
2262
2315
        """Change the format marker."""
2263
2316
        tree._transport.put_bytes('format',
2264
 
            self.target_format.get_format_string(),
 
2317
            self.target_format.as_string(),
2265
2318
            mode=tree.bzrdir._get_file_mode())
2266
2319
 
2267
2320
 
2284
2337
    def update_format(self, tree):
2285
2338
        """Change the format marker."""
2286
2339
        tree._transport.put_bytes('format',
2287
 
            self.target_format.get_format_string(),
 
2340
            self.target_format.as_string(),
2288
2341
            mode=tree.bzrdir._get_file_mode())
2289
2342
 
2290
2343
 
2313
2366
    def update_format(self, tree):
2314
2367
        """Change the format marker."""
2315
2368
        tree._transport.put_bytes('format',
2316
 
            self.target_format.get_format_string(),
 
2369
            self.target_format.as_string(),
2317
2370
            mode=tree.bzrdir._get_file_mode())