~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

Merge bzr.dev, update to use new hooks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 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
31
31
import errno
32
32
import stat
33
33
 
34
 
import bzrlib
35
34
from bzrlib import (
36
35
    bzrdir,
37
36
    cache_utf8,
 
37
    config,
 
38
    conflicts as _mod_conflicts,
38
39
    debug,
39
40
    dirstate,
40
41
    errors,
 
42
    filters as _mod_filters,
41
43
    generate_ids,
42
44
    osutils,
43
45
    revision as _mod_revision,
46
48
    transform,
47
49
    views,
48
50
    )
49
 
import bzrlib.branch
50
 
import bzrlib.ui
51
51
""")
52
52
 
53
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
 
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
54
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
55
from bzrlib.lock import LogicalLockResult
 
56
from bzrlib.lockable_files import LockableFiles
 
57
from bzrlib.lockdir import LockDir
57
58
from bzrlib.mutabletree import needs_tree_write_lock
58
59
from bzrlib.osutils import (
59
60
    file_kind,
62
63
    realpath,
63
64
    safe_unicode,
64
65
    )
65
 
from bzrlib.trace import mutter
66
66
from bzrlib.transport.local import LocalTransport
67
 
from bzrlib.tree import InterTree
68
 
from bzrlib.tree import Tree
69
 
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
70
 
 
71
 
 
72
 
class DirStateWorkingTree(WorkingTree3):
 
67
from bzrlib.tree import (
 
68
    InterTree,
 
69
    InventoryTree,
 
70
    )
 
71
from bzrlib.workingtree import (
 
72
    InventoryWorkingTree,
 
73
    WorkingTree,
 
74
    WorkingTreeFormatMetaDir,
 
75
    )
 
76
 
 
77
 
 
78
class DirStateWorkingTree(InventoryWorkingTree):
 
79
 
73
80
    def __init__(self, basedir,
74
81
                 branch,
75
82
                 _control_files=None,
85
92
        self._format = _format
86
93
        self.bzrdir = _bzrdir
87
94
        basedir = safe_unicode(basedir)
88
 
        mutter("opening working tree %r", basedir)
 
95
        trace.mutter("opening working tree %r", basedir)
89
96
        self._branch = branch
90
97
        self.basedir = realpath(basedir)
91
98
        # if branch is at our basedir and is a format 6 or less
125
132
            state.add(f, file_id, kind, None, '')
126
133
        self._make_dirty(reset_inventory=True)
127
134
 
 
135
    def _get_check_refs(self):
 
136
        """Return the references needed to perform a check of this tree."""
 
137
        return [('trees', self.last_revision())]
 
138
 
128
139
    def _make_dirty(self, reset_inventory):
129
140
        """Make the tree state dirty.
130
141
 
182
193
 
183
194
    def _comparison_data(self, entry, path):
184
195
        kind, executable, stat_value = \
185
 
            WorkingTree3._comparison_data(self, entry, path)
 
196
            WorkingTree._comparison_data(self, entry, path)
186
197
        # it looks like a plain directory, but it's really a reference -- see
187
198
        # also kind()
188
199
        if (self._repo_supports_tree_reference and kind == 'directory'
194
205
    def commit(self, message=None, revprops=None, *args, **kwargs):
195
206
        # mark the tree as dirty post commit - commit
196
207
        # can change the current versioned list by doing deletes.
197
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
208
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
198
209
        self._make_dirty(reset_inventory=True)
199
210
        return result
200
211
 
219
230
        local_path = self.bzrdir.get_workingtree_transport(None
220
231
            ).local_abspath('dirstate')
221
232
        self._dirstate = dirstate.DirState.on_file(local_path,
222
 
            self._sha1_provider())
 
233
            self._sha1_provider(), self._worth_saving_limit())
223
234
        return self._dirstate
224
235
 
225
236
    def _sha1_provider(self):
234
245
        else:
235
246
            return None
236
247
 
 
248
    def _worth_saving_limit(self):
 
249
        """How many hash changes are ok before we must save the dirstate.
 
250
 
 
251
        :return: an integer. -1 means never save.
 
252
        """
 
253
        # FIXME: We want a WorkingTreeStack here -- vila 20110812
 
254
        conf = config.BranchStack(self.branch)
 
255
        return conf.get('bzr.workingtree.worth_saving_limit')
 
256
 
237
257
    def filter_unversioned_files(self, paths):
238
258
        """Filter out paths that are versioned.
239
259
 
369
389
        state = self.current_dirstate()
370
390
        if stat_value is None:
371
391
            try:
372
 
                stat_value = os.lstat(file_abspath)
 
392
                stat_value = osutils.lstat(file_abspath)
373
393
            except OSError, e:
374
394
                if e.errno == errno.ENOENT:
375
395
                    return None
478
498
            self._must_be_locked()
479
499
            if not path:
480
500
                path = self.id2path(file_id)
481
 
            mode = os.lstat(self.abspath(path)).st_mode
 
501
            mode = osutils.lstat(self.abspath(path)).st_mode
482
502
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
483
503
 
484
504
    def all_file_ids(self):
850
870
                rollback_rename()
851
871
                raise
852
872
            result.append((from_rel, to_rel))
853
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
873
            state._mark_modified()
854
874
            self._make_dirty(reset_inventory=False)
855
875
 
856
876
        return result
949
969
                    all_versioned = False
950
970
                    break
951
971
            if not all_versioned:
952
 
                raise errors.PathsNotVersionedError(paths)
 
972
                raise errors.PathsNotVersionedError(
 
973
                    [p.decode('utf-8') for p in paths])
953
974
        # -- remove redundancy in supplied paths to prevent over-scanning --
954
975
        search_paths = osutils.minimum_path_selection(paths)
955
976
        # sketch:
1004
1025
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1005
1026
            for dir_name in split_paths:
1006
1027
                if dir_name not in found_dir_names:
1007
 
                    raise errors.PathsNotVersionedError(paths)
 
1028
                    raise errors.PathsNotVersionedError(
 
1029
                        [p.decode('utf-8') for p in paths])
1008
1030
 
1009
1031
        for dir_name_id, trees_info in found.iteritems():
1010
1032
            for index in search_indexes:
1113
1135
                        _mod_revision.NULL_REVISION)))
1114
1136
                ghosts.append(rev_id)
1115
1137
            accepted_revisions.add(rev_id)
1116
 
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1138
        updated = False
 
1139
        if (len(real_trees) == 1
 
1140
            and not ghosts
 
1141
            and self.branch.repository._format.fast_deltas
 
1142
            and isinstance(real_trees[0][1],
 
1143
                revisiontree.InventoryRevisionTree)
 
1144
            and self.get_parent_ids()):
 
1145
            rev_id, rev_tree = real_trees[0]
 
1146
            basis_id = self.get_parent_ids()[0]
 
1147
            # There are times when basis_tree won't be in
 
1148
            # self.branch.repository, (switch, for example)
 
1149
            try:
 
1150
                basis_tree = self.branch.repository.revision_tree(basis_id)
 
1151
            except errors.NoSuchRevision:
 
1152
                # Fall back to the set_parent_trees(), since we can't use
 
1153
                # _make_delta if we can't get the RevisionTree
 
1154
                pass
 
1155
            else:
 
1156
                delta = rev_tree.inventory._make_delta(basis_tree.inventory)
 
1157
                dirstate.update_basis_by_delta(delta, rev_id)
 
1158
                updated = True
 
1159
        if not updated:
 
1160
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1117
1161
        self._make_dirty(reset_inventory=False)
1118
1162
 
1119
1163
    def _set_root_id(self, file_id):
1139
1183
 
1140
1184
    def unlock(self):
1141
1185
        """Unlock in format 4 trees needs to write the entire dirstate."""
1142
 
        # do non-implementation specific cleanup
1143
 
        self._cleanup()
1144
 
 
1145
1186
        if self._control_files._lock_count == 1:
 
1187
            # do non-implementation specific cleanup
 
1188
            self._cleanup()
 
1189
 
1146
1190
            # eventually we should do signature checking during read locks for
1147
1191
            # dirstate updates.
1148
1192
            if self._control_files._lock_mode == 'w':
1254
1298
    def rename_one(self, from_rel, to_rel, after=False):
1255
1299
        """See WorkingTree.rename_one"""
1256
1300
        self.flush()
1257
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1301
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1258
1302
 
1259
1303
    @needs_tree_write_lock
1260
1304
    def apply_inventory_delta(self, changes):
1293
1337
            self._inventory = inv
1294
1338
        self.flush()
1295
1339
 
 
1340
    @needs_tree_write_lock
 
1341
    def reset_state(self, revision_ids=None):
 
1342
        """Reset the state of the working tree.
 
1343
 
 
1344
        This does a hard-reset to a last-known-good state. This is a way to
 
1345
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
 
1346
        """
 
1347
        if revision_ids is None:
 
1348
            revision_ids = self.get_parent_ids()
 
1349
        if not revision_ids:
 
1350
            base_tree = self.branch.repository.revision_tree(
 
1351
                _mod_revision.NULL_REVISION)
 
1352
            trees = []
 
1353
        else:
 
1354
            trees = zip(revision_ids,
 
1355
                        self.branch.repository.revision_trees(revision_ids))
 
1356
            base_tree = trees[0][1]
 
1357
        state = self.current_dirstate()
 
1358
        # We don't support ghosts yet
 
1359
        state.set_state_from_scratch(base_tree.inventory, trees, [])
 
1360
 
1296
1361
 
1297
1362
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1298
1363
 
1303
1368
        """See dirstate.SHA1Provider.sha1()."""
1304
1369
        filters = self.tree._content_filter_stack(
1305
1370
            self.tree.relpath(osutils.safe_unicode(abspath)))
1306
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1371
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1307
1372
 
1308
1373
    def stat_and_sha1(self, abspath):
1309
1374
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1313
1378
        try:
1314
1379
            statvalue = os.fstat(file_obj.fileno())
1315
1380
            if filters:
1316
 
                file_obj = filtered_input_file(file_obj, filters)
 
1381
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1317
1382
            sha1 = osutils.size_sha_file(file_obj)[1]
1318
1383
        finally:
1319
1384
            file_obj.close()
1349
1414
class WorkingTree4(DirStateWorkingTree):
1350
1415
    """This is the Format 4 working tree.
1351
1416
 
1352
 
    This differs from WorkingTree3 by:
 
1417
    This differs from WorkingTree by:
1353
1418
     - Having a consolidated internal dirstate, stored in a
1354
1419
       randomly-accessible sorted file on disk.
1355
1420
     - Not having a regular inventory attribute.  One can be synthesized
1383
1448
        return views.PathBasedViews(self)
1384
1449
 
1385
1450
 
1386
 
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1451
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
 
1452
 
 
1453
    missing_parent_conflicts = True
 
1454
 
 
1455
    supports_versioned_directories = True
 
1456
 
 
1457
    _lock_class = LockDir
 
1458
    _lock_file_name = 'lock'
 
1459
 
 
1460
    def _open_control_files(self, a_bzrdir):
 
1461
        transport = a_bzrdir.get_workingtree_transport(None)
 
1462
        return LockableFiles(transport, self._lock_file_name,
 
1463
                             self._lock_class)
1387
1464
 
1388
1465
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1389
1466
                   accelerator_tree=None, hardlink=False):
1390
1467
        """See WorkingTreeFormat.initialize().
1391
1468
 
1392
1469
        :param revision_id: allows creating a working tree at a different
1393
 
        revision than the branch is at.
 
1470
            revision than the branch is at.
1394
1471
        :param accelerator_tree: A tree which can be used for retrieving file
1395
1472
            contents more quickly than the revision tree, i.e. a workingtree.
1396
1473
            The revision tree will be used for cases where accelerator_tree's
1489
1566
        :param wt: the WorkingTree object
1490
1567
        """
1491
1568
 
 
1569
    def open(self, a_bzrdir, _found=False):
 
1570
        """Return the WorkingTree object for a_bzrdir
 
1571
 
 
1572
        _found is a private parameter, do not use it. It is used to indicate
 
1573
               if format probing has already been done.
 
1574
        """
 
1575
        if not _found:
 
1576
            # we are being called directly and must probe.
 
1577
            raise NotImplementedError
 
1578
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
1579
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
1580
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
1581
        return wt
 
1582
 
1492
1583
    def _open(self, a_bzrdir, control_files):
1493
1584
        """Open the tree itself.
1494
1585
 
1519
1610
    This format:
1520
1611
        - exists within a metadir controlling .bzr
1521
1612
        - includes an explicit version marker for the workingtree control
1522
 
          files, separate from the BzrDir format
 
1613
          files, separate from the ControlDir format
1523
1614
        - modifies the hash cache format
1524
1615
        - is new in bzr 0.15
1525
1616
        - uses a LockDir to guard access to it.
1529
1620
 
1530
1621
    _tree_class = WorkingTree4
1531
1622
 
1532
 
    def get_format_string(self):
 
1623
    @classmethod
 
1624
    def get_format_string(cls):
1533
1625
        """See WorkingTreeFormat.get_format_string()."""
1534
1626
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1535
1627
 
1546
1638
 
1547
1639
    _tree_class = WorkingTree5
1548
1640
 
1549
 
    def get_format_string(self):
 
1641
    @classmethod
 
1642
    def get_format_string(cls):
1550
1643
        """See WorkingTreeFormat.get_format_string()."""
1551
1644
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1552
1645
 
1566
1659
 
1567
1660
    _tree_class = WorkingTree6
1568
1661
 
1569
 
    def get_format_string(self):
 
1662
    @classmethod
 
1663
    def get_format_string(cls):
1570
1664
        """See WorkingTreeFormat.get_format_string()."""
1571
1665
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1572
1666
 
1585
1679
        return True
1586
1680
 
1587
1681
 
1588
 
class DirStateRevisionTree(Tree):
 
1682
class DirStateRevisionTree(InventoryTree):
1589
1683
    """A revision tree pulling the inventory from a dirstate.
1590
1684
    
1591
1685
    Note that this is one of the historical (ie revision) trees cached in the
1610
1704
    def annotate_iter(self, file_id,
1611
1705
                      default_revision=_mod_revision.CURRENT_REVISION):
1612
1706
        """See Tree.annotate_iter"""
1613
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1707
        text_key = (file_id, self.get_file_revision(file_id))
1614
1708
        annotations = self._repository.texts.annotate(text_key)
1615
1709
        return [(key[-1], line) for (key, line) in annotations]
1616
1710
 
1617
 
    def _get_ancestors(self, default_revision):
1618
 
        return set(self._repository.get_ancestry(self._revision_id,
1619
 
                                                 topo_sorted=False))
1620
1711
    def _comparison_data(self, entry, path):
1621
1712
        """See Tree._comparison_data."""
1622
1713
        if entry is None:
1763
1854
        # Make sure the file exists
1764
1855
        entry = self._get_entry(file_id, path=path)
1765
1856
        if entry == (None, None): # do we raise?
1766
 
            return None
 
1857
            raise errors.NoSuchId(self, file_id)
1767
1858
        parent_index = self._get_parent_index()
1768
1859
        last_changed_revision = entry[1][parent_index][4]
1769
1860
        try:
1780
1871
            return parent_details[1]
1781
1872
        return None
1782
1873
 
 
1874
    @needs_read_lock
 
1875
    def get_file_revision(self, file_id):
 
1876
        return self.inventory[file_id].revision
 
1877
 
1783
1878
    def get_file(self, file_id, path=None):
1784
1879
        return StringIO(self.get_file_text(file_id))
1785
1880
 
1808
1903
                                       identifier))
1809
1904
        return self._repository.iter_files_bytes(repo_desired_files)
1810
1905
 
1811
 
    def get_symlink_target(self, file_id):
 
1906
    def get_symlink_target(self, file_id, path=None):
1812
1907
        entry = self._get_entry(file_id=file_id)
1813
1908
        parent_index = self._get_parent_index()
1814
1909
        if entry[1][parent_index][0] != 'l':
1867
1962
    def is_executable(self, file_id, path=None):
1868
1963
        ie = self.inventory[file_id]
1869
1964
        if ie.kind != "file":
1870
 
            return None
 
1965
            return False
1871
1966
        return ie.executable
1872
1967
 
1873
1968
    def is_locked(self):
1986
2081
    def make_source_parent_tree(source, target):
1987
2082
        """Change the source tree into a parent of the target."""
1988
2083
        revid = source.commit('record tree')
1989
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2084
        target.branch.fetch(source.branch, revid)
1990
2085
        target.set_parent_ids([revid])
1991
2086
        return target.basis_tree(), target
1992
2087
 
2084
2179
                path_entries = state._entries_for_path(path)
2085
2180
                if not path_entries:
2086
2181
                    # this specified path is not present at all: error
2087
 
                    not_versioned.append(path)
 
2182
                    not_versioned.append(path.decode('utf-8'))
2088
2183
                    continue
2089
2184
                found_versioned = False
2090
2185
                # for each id at this path
2098
2193
                if not found_versioned:
2099
2194
                    # none of the indexes was not 'absent' at all ids for this
2100
2195
                    # path.
2101
 
                    not_versioned.append(path)
 
2196
                    not_versioned.append(path.decode('utf-8'))
2102
2197
            if len(not_versioned) > 0:
2103
2198
                raise errors.PathsNotVersionedError(not_versioned)
2104
2199
        # -- remove redundancy in supplied specific_files to prevent over-scanning --