~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Jelmer Vernooij
  • Date: 2011-12-19 10:58:39 UTC
  • mfrom: (6383 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6386.
  • Revision ID: jelmer@canonical.com-20111219105839-uji05ck4rkm1mj4j
Merge bzr.dev.

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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
 
18
 
17
19
"""WorkingTree4 format and implementation.
18
20
 
19
21
WorkingTree4 provides the dirstate based working tree logic.
31
33
import errno
32
34
import stat
33
35
 
34
 
import bzrlib
35
36
from bzrlib import (
36
37
    bzrdir,
37
38
    cache_utf8,
 
39
    config,
 
40
    conflicts as _mod_conflicts,
38
41
    debug,
39
42
    dirstate,
40
43
    errors,
 
44
    filters as _mod_filters,
41
45
    generate_ids,
42
46
    osutils,
43
47
    revision as _mod_revision,
46
50
    transform,
47
51
    views,
48
52
    )
49
 
import bzrlib.branch
50
 
import bzrlib.ui
51
53
""")
52
54
 
53
55
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
 
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
56
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
 
57
from bzrlib.lock import LogicalLockResult
 
58
from bzrlib.lockable_files import LockableFiles
 
59
from bzrlib.lockdir import LockDir
56
60
from bzrlib.mutabletree import needs_tree_write_lock
57
61
from bzrlib.osutils import (
58
62
    file_kind,
61
65
    realpath,
62
66
    safe_unicode,
63
67
    )
64
 
from bzrlib.trace import mutter
65
68
from bzrlib.transport.local import LocalTransport
66
 
from bzrlib.tree import InterTree
67
 
from bzrlib.tree import Tree
68
 
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
69
 
 
70
 
 
71
 
class DirStateWorkingTree(WorkingTree3):
 
69
from bzrlib.tree import (
 
70
    InterTree,
 
71
    InventoryTree,
 
72
    )
 
73
from bzrlib.workingtree import (
 
74
    InventoryWorkingTree,
 
75
    WorkingTree,
 
76
    WorkingTreeFormatMetaDir,
 
77
    )
 
78
 
 
79
 
 
80
class DirStateWorkingTree(InventoryWorkingTree):
 
81
 
72
82
    def __init__(self, basedir,
73
83
                 branch,
74
84
                 _control_files=None,
84
94
        self._format = _format
85
95
        self.bzrdir = _bzrdir
86
96
        basedir = safe_unicode(basedir)
87
 
        mutter("opening working tree %r", basedir)
 
97
        trace.mutter("opening working tree %r", basedir)
88
98
        self._branch = branch
89
99
        self.basedir = realpath(basedir)
90
100
        # if branch is at our basedir and is a format 6 or less
124
134
            state.add(f, file_id, kind, None, '')
125
135
        self._make_dirty(reset_inventory=True)
126
136
 
 
137
    def _get_check_refs(self):
 
138
        """Return the references needed to perform a check of this tree."""
 
139
        return [('trees', self.last_revision())]
 
140
 
127
141
    def _make_dirty(self, reset_inventory):
128
142
        """Make the tree state dirty.
129
143
 
181
195
 
182
196
    def _comparison_data(self, entry, path):
183
197
        kind, executable, stat_value = \
184
 
            WorkingTree3._comparison_data(self, entry, path)
 
198
            WorkingTree._comparison_data(self, entry, path)
185
199
        # it looks like a plain directory, but it's really a reference -- see
186
200
        # also kind()
187
201
        if (self._repo_supports_tree_reference and kind == 'directory'
193
207
    def commit(self, message=None, revprops=None, *args, **kwargs):
194
208
        # mark the tree as dirty post commit - commit
195
209
        # can change the current versioned list by doing deletes.
196
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
210
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
197
211
        self._make_dirty(reset_inventory=True)
198
212
        return result
199
213
 
218
232
        local_path = self.bzrdir.get_workingtree_transport(None
219
233
            ).local_abspath('dirstate')
220
234
        self._dirstate = dirstate.DirState.on_file(local_path,
221
 
            self._sha1_provider())
 
235
            self._sha1_provider(), self._worth_saving_limit())
222
236
        return self._dirstate
223
237
 
224
238
    def _sha1_provider(self):
233
247
        else:
234
248
            return None
235
249
 
 
250
    def _worth_saving_limit(self):
 
251
        """How many hash changes are ok before we must save the dirstate.
 
252
 
 
253
        :return: an integer. -1 means never save.
 
254
        """
 
255
        # FIXME: We want a WorkingTreeStack here -- vila 20110812
 
256
        conf = config.BranchStack(self.branch)
 
257
        return conf.get('bzr.workingtree.worth_saving_limit')
 
258
 
236
259
    def filter_unversioned_files(self, paths):
237
260
        """Filter out paths that are versioned.
238
261
 
368
391
        state = self.current_dirstate()
369
392
        if stat_value is None:
370
393
            try:
371
 
                stat_value = os.lstat(file_abspath)
 
394
                stat_value = osutils.lstat(file_abspath)
372
395
            except OSError, e:
373
396
                if e.errno == errno.ENOENT:
374
397
                    return None
477
500
            self._must_be_locked()
478
501
            if not path:
479
502
                path = self.id2path(file_id)
480
 
            mode = os.lstat(self.abspath(path)).st_mode
 
503
            mode = osutils.lstat(self.abspath(path)).st_mode
481
504
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
482
505
 
483
506
    def all_file_ids(self):
567
590
            return _mod_revision.NULL_REVISION
568
591
 
569
592
    def lock_read(self):
570
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
593
        """See Branch.lock_read, and WorkingTree.unlock.
 
594
 
 
595
        :return: A bzrlib.lock.LogicalLockResult.
 
596
        """
571
597
        self.branch.lock_read()
572
598
        try:
573
599
            self._control_files.lock_read()
586
612
        except:
587
613
            self.branch.unlock()
588
614
            raise
 
615
        return LogicalLockResult(self.unlock)
589
616
 
590
617
    def _lock_self_write(self):
591
618
        """This should be called after the branch is locked."""
606
633
        except:
607
634
            self.branch.unlock()
608
635
            raise
 
636
        return LogicalLockResult(self.unlock)
609
637
 
610
638
    def lock_tree_write(self):
611
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
639
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
640
 
 
641
        :return: A bzrlib.lock.LogicalLockResult.
 
642
        """
612
643
        self.branch.lock_read()
613
 
        self._lock_self_write()
 
644
        return self._lock_self_write()
614
645
 
615
646
    def lock_write(self):
616
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
647
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
648
 
 
649
        :return: A bzrlib.lock.LogicalLockResult.
 
650
        """
617
651
        self.branch.lock_write()
618
 
        self._lock_self_write()
 
652
        return self._lock_self_write()
619
653
 
620
654
    @needs_tree_write_lock
621
655
    def move(self, from_paths, to_dir, after=False):
838
872
                rollback_rename()
839
873
                raise
840
874
            result.append((from_rel, to_rel))
841
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
875
            state._mark_modified()
842
876
            self._make_dirty(reset_inventory=False)
843
877
 
844
878
        return result
937
971
                    all_versioned = False
938
972
                    break
939
973
            if not all_versioned:
940
 
                raise errors.PathsNotVersionedError(paths)
 
974
                raise errors.PathsNotVersionedError(
 
975
                    [p.decode('utf-8') for p in paths])
941
976
        # -- remove redundancy in supplied paths to prevent over-scanning --
942
977
        search_paths = osutils.minimum_path_selection(paths)
943
978
        # sketch:
992
1027
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
993
1028
            for dir_name in split_paths:
994
1029
                if dir_name not in found_dir_names:
995
 
                    raise errors.PathsNotVersionedError(paths)
 
1030
                    raise errors.PathsNotVersionedError(
 
1031
                        [p.decode('utf-8') for p in paths])
996
1032
 
997
1033
        for dir_name_id, trees_info in found.iteritems():
998
1034
            for index in search_indexes:
1101
1137
                        _mod_revision.NULL_REVISION)))
1102
1138
                ghosts.append(rev_id)
1103
1139
            accepted_revisions.add(rev_id)
1104
 
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1140
        updated = False
 
1141
        if (len(real_trees) == 1
 
1142
            and not ghosts
 
1143
            and self.branch.repository._format.fast_deltas
 
1144
            and isinstance(real_trees[0][1],
 
1145
                revisiontree.InventoryRevisionTree)
 
1146
            and self.get_parent_ids()):
 
1147
            rev_id, rev_tree = real_trees[0]
 
1148
            basis_id = self.get_parent_ids()[0]
 
1149
            # There are times when basis_tree won't be in
 
1150
            # self.branch.repository, (switch, for example)
 
1151
            try:
 
1152
                basis_tree = self.branch.repository.revision_tree(basis_id)
 
1153
            except errors.NoSuchRevision:
 
1154
                # Fall back to the set_parent_trees(), since we can't use
 
1155
                # _make_delta if we can't get the RevisionTree
 
1156
                pass
 
1157
            else:
 
1158
                delta = rev_tree.inventory._make_delta(basis_tree.inventory)
 
1159
                dirstate.update_basis_by_delta(delta, rev_id)
 
1160
                updated = True
 
1161
        if not updated:
 
1162
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1105
1163
        self._make_dirty(reset_inventory=False)
1106
1164
 
1107
1165
    def _set_root_id(self, file_id):
1127
1185
 
1128
1186
    def unlock(self):
1129
1187
        """Unlock in format 4 trees needs to write the entire dirstate."""
1130
 
        # do non-implementation specific cleanup
1131
 
        self._cleanup()
1132
 
 
1133
1188
        if self._control_files._lock_count == 1:
 
1189
            # do non-implementation specific cleanup
 
1190
            self._cleanup()
 
1191
 
1134
1192
            # eventually we should do signature checking during read locks for
1135
1193
            # dirstate updates.
1136
1194
            if self._control_files._lock_mode == 'w':
1235
1293
        # have to change the legacy inventory too.
1236
1294
        if self._inventory is not None:
1237
1295
            for file_id in file_ids:
1238
 
                self._inventory.remove_recursive_id(file_id)
 
1296
                if self._inventory.has_id(file_id):
 
1297
                    self._inventory.remove_recursive_id(file_id)
1239
1298
 
1240
1299
    @needs_tree_write_lock
1241
1300
    def rename_one(self, from_rel, to_rel, after=False):
1242
1301
        """See WorkingTree.rename_one"""
1243
1302
        self.flush()
1244
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1303
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1245
1304
 
1246
1305
    @needs_tree_write_lock
1247
1306
    def apply_inventory_delta(self, changes):
1280
1339
            self._inventory = inv
1281
1340
        self.flush()
1282
1341
 
 
1342
    @needs_tree_write_lock
 
1343
    def reset_state(self, revision_ids=None):
 
1344
        """Reset the state of the working tree.
 
1345
 
 
1346
        This does a hard-reset to a last-known-good state. This is a way to
 
1347
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
 
1348
        """
 
1349
        if revision_ids is None:
 
1350
            revision_ids = self.get_parent_ids()
 
1351
        if not revision_ids:
 
1352
            base_tree = self.branch.repository.revision_tree(
 
1353
                _mod_revision.NULL_REVISION)
 
1354
            trees = []
 
1355
        else:
 
1356
            trees = zip(revision_ids,
 
1357
                        self.branch.repository.revision_trees(revision_ids))
 
1358
            base_tree = trees[0][1]
 
1359
        state = self.current_dirstate()
 
1360
        # We don't support ghosts yet
 
1361
        state.set_state_from_scratch(base_tree.inventory, trees, [])
 
1362
 
1283
1363
 
1284
1364
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1285
1365
 
1290
1370
        """See dirstate.SHA1Provider.sha1()."""
1291
1371
        filters = self.tree._content_filter_stack(
1292
1372
            self.tree.relpath(osutils.safe_unicode(abspath)))
1293
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1373
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1294
1374
 
1295
1375
    def stat_and_sha1(self, abspath):
1296
1376
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1300
1380
        try:
1301
1381
            statvalue = os.fstat(file_obj.fileno())
1302
1382
            if filters:
1303
 
                file_obj = filtered_input_file(file_obj, filters)
 
1383
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1304
1384
            sha1 = osutils.size_sha_file(file_obj)[1]
1305
1385
        finally:
1306
1386
            file_obj.close()
1317
1397
    def _file_content_summary(self, path, stat_result):
1318
1398
        # This is to support the somewhat obsolete path_content_summary method
1319
1399
        # with content filtering: see
1320
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1400
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1321
1401
        #
1322
1402
        # If the dirstate cache is up to date and knows the hash and size,
1323
1403
        # return that.
1336
1416
class WorkingTree4(DirStateWorkingTree):
1337
1417
    """This is the Format 4 working tree.
1338
1418
 
1339
 
    This differs from WorkingTree3 by:
 
1419
    This differs from WorkingTree by:
1340
1420
     - Having a consolidated internal dirstate, stored in a
1341
1421
       randomly-accessible sorted file on disk.
1342
1422
     - Not having a regular inventory attribute.  One can be synthesized
1370
1450
        return views.PathBasedViews(self)
1371
1451
 
1372
1452
 
1373
 
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1453
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
 
1454
 
 
1455
    missing_parent_conflicts = True
 
1456
 
 
1457
    supports_versioned_directories = True
 
1458
 
 
1459
    _lock_class = LockDir
 
1460
    _lock_file_name = 'lock'
 
1461
 
 
1462
    def _open_control_files(self, a_bzrdir):
 
1463
        transport = a_bzrdir.get_workingtree_transport(None)
 
1464
        return LockableFiles(transport, self._lock_file_name,
 
1465
                             self._lock_class)
1374
1466
 
1375
1467
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1376
1468
                   accelerator_tree=None, hardlink=False):
1377
1469
        """See WorkingTreeFormat.initialize().
1378
1470
 
1379
1471
        :param revision_id: allows creating a working tree at a different
1380
 
        revision than the branch is at.
 
1472
            revision than the branch is at.
1381
1473
        :param accelerator_tree: A tree which can be used for retrieving file
1382
1474
            contents more quickly than the revision tree, i.e. a workingtree.
1383
1475
            The revision tree will be used for cases where accelerator_tree's
1476
1568
        :param wt: the WorkingTree object
1477
1569
        """
1478
1570
 
 
1571
    def open(self, a_bzrdir, _found=False):
 
1572
        """Return the WorkingTree object for a_bzrdir
 
1573
 
 
1574
        _found is a private parameter, do not use it. It is used to indicate
 
1575
               if format probing has already been done.
 
1576
        """
 
1577
        if not _found:
 
1578
            # we are being called directly and must probe.
 
1579
            raise NotImplementedError
 
1580
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
1581
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
1582
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
1583
        return wt
 
1584
 
1479
1585
    def _open(self, a_bzrdir, control_files):
1480
1586
        """Open the tree itself.
1481
1587
 
1506
1612
    This format:
1507
1613
        - exists within a metadir controlling .bzr
1508
1614
        - includes an explicit version marker for the workingtree control
1509
 
          files, separate from the BzrDir format
 
1615
          files, separate from the ControlDir format
1510
1616
        - modifies the hash cache format
1511
1617
        - is new in bzr 0.15
1512
1618
        - uses a LockDir to guard access to it.
1516
1622
 
1517
1623
    _tree_class = WorkingTree4
1518
1624
 
1519
 
    def get_format_string(self):
 
1625
    @classmethod
 
1626
    def get_format_string(cls):
1520
1627
        """See WorkingTreeFormat.get_format_string()."""
1521
1628
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1522
1629
 
1533
1640
 
1534
1641
    _tree_class = WorkingTree5
1535
1642
 
1536
 
    def get_format_string(self):
 
1643
    @classmethod
 
1644
    def get_format_string(cls):
1537
1645
        """See WorkingTreeFormat.get_format_string()."""
1538
1646
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1539
1647
 
1553
1661
 
1554
1662
    _tree_class = WorkingTree6
1555
1663
 
1556
 
    def get_format_string(self):
 
1664
    @classmethod
 
1665
    def get_format_string(cls):
1557
1666
        """See WorkingTreeFormat.get_format_string()."""
1558
1667
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1559
1668
 
1572
1681
        return True
1573
1682
 
1574
1683
 
1575
 
class DirStateRevisionTree(Tree):
 
1684
class DirStateRevisionTree(InventoryTree):
1576
1685
    """A revision tree pulling the inventory from a dirstate.
1577
1686
    
1578
1687
    Note that this is one of the historical (ie revision) trees cached in the
1597
1706
    def annotate_iter(self, file_id,
1598
1707
                      default_revision=_mod_revision.CURRENT_REVISION):
1599
1708
        """See Tree.annotate_iter"""
1600
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1709
        text_key = (file_id, self.get_file_revision(file_id))
1601
1710
        annotations = self._repository.texts.annotate(text_key)
1602
1711
        return [(key[-1], line) for (key, line) in annotations]
1603
1712
 
1604
 
    def _get_ancestors(self, default_revision):
1605
 
        return set(self._repository.get_ancestry(self._revision_id,
1606
 
                                                 topo_sorted=False))
1607
1713
    def _comparison_data(self, entry, path):
1608
1714
        """See Tree._comparison_data."""
1609
1715
        if entry is None:
1725
1831
                elif kind == 'directory':
1726
1832
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1727
1833
                elif kind == 'symlink':
1728
 
                    inv_entry.executable = False
1729
 
                    inv_entry.text_size = None
1730
1834
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1731
1835
                elif kind == 'tree-reference':
1732
1836
                    inv_entry.reference_revision = fingerprint or None
1752
1856
        # Make sure the file exists
1753
1857
        entry = self._get_entry(file_id, path=path)
1754
1858
        if entry == (None, None): # do we raise?
1755
 
            return None
 
1859
            raise errors.NoSuchId(self, file_id)
1756
1860
        parent_index = self._get_parent_index()
1757
1861
        last_changed_revision = entry[1][parent_index][4]
1758
1862
        try:
1769
1873
            return parent_details[1]
1770
1874
        return None
1771
1875
 
 
1876
    @needs_read_lock
 
1877
    def get_file_revision(self, file_id):
 
1878
        return self.inventory[file_id].revision
 
1879
 
1772
1880
    def get_file(self, file_id, path=None):
1773
1881
        return StringIO(self.get_file_text(file_id))
1774
1882
 
1797
1905
                                       identifier))
1798
1906
        return self._repository.iter_files_bytes(repo_desired_files)
1799
1907
 
1800
 
    def get_symlink_target(self, file_id):
 
1908
    def get_symlink_target(self, file_id, path=None):
1801
1909
        entry = self._get_entry(file_id=file_id)
1802
1910
        parent_index = self._get_parent_index()
1803
1911
        if entry[1][parent_index][0] != 'l':
1856
1964
    def is_executable(self, file_id, path=None):
1857
1965
        ie = self.inventory[file_id]
1858
1966
        if ie.kind != "file":
1859
 
            return None
 
1967
            return False
1860
1968
        return ie.executable
1861
1969
 
 
1970
    def is_locked(self):
 
1971
        return self._locked
 
1972
 
1862
1973
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1863
1974
        # We use a standard implementation, because DirStateRevisionTree is
1864
1975
        # dealing with one of the parents of the current state
1877
1988
            yield path, 'V', entry.kind, entry.file_id, entry
1878
1989
 
1879
1990
    def lock_read(self):
1880
 
        """Lock the tree for a set of operations."""
 
1991
        """Lock the tree for a set of operations.
 
1992
 
 
1993
        :return: A bzrlib.lock.LogicalLockResult.
 
1994
        """
1881
1995
        if not self._locked:
1882
1996
            self._repository.lock_read()
1883
1997
            if self._dirstate._lock_token is None:
1884
1998
                self._dirstate.lock_read()
1885
1999
                self._dirstate_locked = True
1886
2000
        self._locked += 1
 
2001
        return LogicalLockResult(self.unlock)
1887
2002
 
1888
2003
    def _must_be_locked(self):
1889
2004
        if not self._locked:
1968
2083
    def make_source_parent_tree(source, target):
1969
2084
        """Change the source tree into a parent of the target."""
1970
2085
        revid = source.commit('record tree')
1971
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2086
        target.branch.fetch(source.branch, revid)
1972
2087
        target.set_parent_ids([revid])
1973
2088
        return target.basis_tree(), target
1974
2089
 
2066
2181
                path_entries = state._entries_for_path(path)
2067
2182
                if not path_entries:
2068
2183
                    # this specified path is not present at all: error
2069
 
                    not_versioned.append(path)
 
2184
                    not_versioned.append(path.decode('utf-8'))
2070
2185
                    continue
2071
2186
                found_versioned = False
2072
2187
                # for each id at this path
2080
2195
                if not found_versioned:
2081
2196
                    # none of the indexes was not 'absent' at all ids for this
2082
2197
                    # path.
2083
 
                    not_versioned.append(path)
 
2198
                    not_versioned.append(path.decode('utf-8'))
2084
2199
            if len(not_versioned) > 0:
2085
2200
                raise errors.PathsNotVersionedError(not_versioned)
2086
2201
        # -- remove redundancy in supplied specific_files to prevent over-scanning --