~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-05-11 11:47:36 UTC
  • mfrom: (5200.3.8 lock_return)
  • Revision ID: pqm@pqm.ubuntu.com-20100511114736-mc1sq9zyo3vufec7
(lifeless) Provide a consistent interface to Tree, Branch,
 Repository where lock methods return an object with an unlock method to
 unlock the lock. This breaks the API for Branch,
 Repository on their lock_write methods. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2007-2010 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
34
35
from bzrlib import (
35
36
    bzrdir,
36
37
    cache_utf8,
37
 
    config,
38
 
    conflicts as _mod_conflicts,
39
38
    debug,
40
39
    dirstate,
41
40
    errors,
42
 
    filters as _mod_filters,
43
41
    generate_ids,
44
42
    osutils,
45
43
    revision as _mod_revision,
48
46
    transform,
49
47
    views,
50
48
    )
 
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
54
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
55
56
from bzrlib.lock import LogicalLockResult
56
 
from bzrlib.lockable_files import LockableFiles
57
 
from bzrlib.lockdir import LockDir
58
57
from bzrlib.mutabletree import needs_tree_write_lock
59
58
from bzrlib.osutils import (
60
59
    file_kind,
63
62
    realpath,
64
63
    safe_unicode,
65
64
    )
 
65
from bzrlib.trace import mutter
66
66
from bzrlib.transport.local import LocalTransport
67
 
from bzrlib.tree import (
68
 
    InterTree,
69
 
    InventoryTree,
70
 
    )
71
 
from bzrlib.workingtree import (
72
 
    InventoryWorkingTree,
73
 
    WorkingTree,
74
 
    WorkingTreeFormat,
75
 
    )
76
 
 
77
 
 
78
 
class DirStateWorkingTree(InventoryWorkingTree):
79
 
 
 
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):
80
73
    def __init__(self, basedir,
81
74
                 branch,
82
75
                 _control_files=None,
92
85
        self._format = _format
93
86
        self.bzrdir = _bzrdir
94
87
        basedir = safe_unicode(basedir)
95
 
        trace.mutter("opening working tree %r", basedir)
 
88
        mutter("opening working tree %r", basedir)
96
89
        self._branch = branch
97
90
        self.basedir = realpath(basedir)
98
91
        # if branch is at our basedir and is a format 6 or less
132
125
            state.add(f, file_id, kind, None, '')
133
126
        self._make_dirty(reset_inventory=True)
134
127
 
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
 
 
139
128
    def _make_dirty(self, reset_inventory):
140
129
        """Make the tree state dirty.
141
130
 
193
182
 
194
183
    def _comparison_data(self, entry, path):
195
184
        kind, executable, stat_value = \
196
 
            WorkingTree._comparison_data(self, entry, path)
 
185
            WorkingTree3._comparison_data(self, entry, path)
197
186
        # it looks like a plain directory, but it's really a reference -- see
198
187
        # also kind()
199
188
        if (self._repo_supports_tree_reference and kind == 'directory'
205
194
    def commit(self, message=None, revprops=None, *args, **kwargs):
206
195
        # mark the tree as dirty post commit - commit
207
196
        # can change the current versioned list by doing deletes.
208
 
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
 
197
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
209
198
        self._make_dirty(reset_inventory=True)
210
199
        return result
211
200
 
230
219
        local_path = self.bzrdir.get_workingtree_transport(None
231
220
            ).local_abspath('dirstate')
232
221
        self._dirstate = dirstate.DirState.on_file(local_path,
233
 
            self._sha1_provider(), self._worth_saving_limit())
 
222
            self._sha1_provider())
234
223
        return self._dirstate
235
224
 
236
225
    def _sha1_provider(self):
245
234
        else:
246
235
            return None
247
236
 
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
 
 
257
237
    def filter_unversioned_files(self, paths):
258
238
        """Filter out paths that are versioned.
259
239
 
389
369
        state = self.current_dirstate()
390
370
        if stat_value is None:
391
371
            try:
392
 
                stat_value = osutils.lstat(file_abspath)
 
372
                stat_value = os.lstat(file_abspath)
393
373
            except OSError, e:
394
374
                if e.errno == errno.ENOENT:
395
375
                    return None
498
478
            self._must_be_locked()
499
479
            if not path:
500
480
                path = self.id2path(file_id)
501
 
            mode = osutils.lstat(self.abspath(path)).st_mode
 
481
            mode = os.lstat(self.abspath(path)).st_mode
502
482
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
503
483
 
504
484
    def all_file_ids(self):
870
850
                rollback_rename()
871
851
                raise
872
852
            result.append((from_rel, to_rel))
873
 
            state._mark_modified()
 
853
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
874
854
            self._make_dirty(reset_inventory=False)
875
855
 
876
856
        return result
1133
1113
                        _mod_revision.NULL_REVISION)))
1134
1114
                ghosts.append(rev_id)
1135
1115
            accepted_revisions.add(rev_id)
1136
 
        updated = False
1137
 
        if (len(real_trees) == 1
1138
 
            and not ghosts
1139
 
            and self.branch.repository._format.fast_deltas
1140
 
            and isinstance(real_trees[0][1],
1141
 
                revisiontree.InventoryRevisionTree)
1142
 
            and self.get_parent_ids()):
1143
 
            rev_id, rev_tree = real_trees[0]
1144
 
            basis_id = self.get_parent_ids()[0]
1145
 
            # There are times when basis_tree won't be in
1146
 
            # self.branch.repository, (switch, for example)
1147
 
            try:
1148
 
                basis_tree = self.branch.repository.revision_tree(basis_id)
1149
 
            except errors.NoSuchRevision:
1150
 
                # Fall back to the set_parent_trees(), since we can't use
1151
 
                # _make_delta if we can't get the RevisionTree
1152
 
                pass
1153
 
            else:
1154
 
                delta = rev_tree.inventory._make_delta(basis_tree.inventory)
1155
 
                dirstate.update_basis_by_delta(delta, rev_id)
1156
 
                updated = True
1157
 
        if not updated:
1158
 
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1116
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1159
1117
        self._make_dirty(reset_inventory=False)
1160
1118
 
1161
1119
    def _set_root_id(self, file_id):
1181
1139
 
1182
1140
    def unlock(self):
1183
1141
        """Unlock in format 4 trees needs to write the entire dirstate."""
 
1142
        # do non-implementation specific cleanup
 
1143
        self._cleanup()
 
1144
 
1184
1145
        if self._control_files._lock_count == 1:
1185
 
            # do non-implementation specific cleanup
1186
 
            self._cleanup()
1187
 
 
1188
1146
            # eventually we should do signature checking during read locks for
1189
1147
            # dirstate updates.
1190
1148
            if self._control_files._lock_mode == 'w':
1289
1247
        # have to change the legacy inventory too.
1290
1248
        if self._inventory is not None:
1291
1249
            for file_id in file_ids:
1292
 
                if self._inventory.has_id(file_id):
1293
 
                    self._inventory.remove_recursive_id(file_id)
 
1250
                self._inventory.remove_recursive_id(file_id)
1294
1251
 
1295
1252
    @needs_tree_write_lock
1296
1253
    def rename_one(self, from_rel, to_rel, after=False):
1297
1254
        """See WorkingTree.rename_one"""
1298
1255
        self.flush()
1299
 
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
 
1256
        WorkingTree.rename_one(self, from_rel, to_rel, after)
1300
1257
 
1301
1258
    @needs_tree_write_lock
1302
1259
    def apply_inventory_delta(self, changes):
1335
1292
            self._inventory = inv
1336
1293
        self.flush()
1337
1294
 
1338
 
    @needs_tree_write_lock
1339
 
    def reset_state(self, revision_ids=None):
1340
 
        """Reset the state of the working tree.
1341
 
 
1342
 
        This does a hard-reset to a last-known-good state. This is a way to
1343
 
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
1344
 
        """
1345
 
        if revision_ids is None:
1346
 
            revision_ids = self.get_parent_ids()
1347
 
        if not revision_ids:
1348
 
            base_tree = self.branch.repository.revision_tree(
1349
 
                _mod_revision.NULL_REVISION)
1350
 
            trees = []
1351
 
        else:
1352
 
            trees = zip(revision_ids,
1353
 
                        self.branch.repository.revision_trees(revision_ids))
1354
 
            base_tree = trees[0][1]
1355
 
        state = self.current_dirstate()
1356
 
        # We don't support ghosts yet
1357
 
        state.set_state_from_scratch(base_tree.inventory, trees, [])
1358
 
 
1359
1295
 
1360
1296
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1361
1297
 
1366
1302
        """See dirstate.SHA1Provider.sha1()."""
1367
1303
        filters = self.tree._content_filter_stack(
1368
1304
            self.tree.relpath(osutils.safe_unicode(abspath)))
1369
 
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
 
1305
        return internal_size_sha_file_byname(abspath, filters)[1]
1370
1306
 
1371
1307
    def stat_and_sha1(self, abspath):
1372
1308
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1376
1312
        try:
1377
1313
            statvalue = os.fstat(file_obj.fileno())
1378
1314
            if filters:
1379
 
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
 
1315
                file_obj = filtered_input_file(file_obj, filters)
1380
1316
            sha1 = osutils.size_sha_file(file_obj)[1]
1381
1317
        finally:
1382
1318
            file_obj.close()
1393
1329
    def _file_content_summary(self, path, stat_result):
1394
1330
        # This is to support the somewhat obsolete path_content_summary method
1395
1331
        # with content filtering: see
1396
 
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
 
1332
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
1397
1333
        #
1398
1334
        # If the dirstate cache is up to date and knows the hash and size,
1399
1335
        # return that.
1412
1348
class WorkingTree4(DirStateWorkingTree):
1413
1349
    """This is the Format 4 working tree.
1414
1350
 
1415
 
    This differs from WorkingTree by:
 
1351
    This differs from WorkingTree3 by:
1416
1352
     - Having a consolidated internal dirstate, stored in a
1417
1353
       randomly-accessible sorted file on disk.
1418
1354
     - Not having a regular inventory attribute.  One can be synthesized
1446
1382
        return views.PathBasedViews(self)
1447
1383
 
1448
1384
 
1449
 
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1450
 
 
1451
 
    missing_parent_conflicts = True
1452
 
 
1453
 
    supports_versioned_directories = True
1454
 
 
1455
 
    _lock_class = LockDir
1456
 
    _lock_file_name = 'lock'
1457
 
 
1458
 
    def _open_control_files(self, a_bzrdir):
1459
 
        transport = a_bzrdir.get_workingtree_transport(None)
1460
 
        return LockableFiles(transport, self._lock_file_name,
1461
 
                             self._lock_class)
 
1385
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1462
1386
 
1463
1387
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1464
1388
                   accelerator_tree=None, hardlink=False):
1465
1389
        """See WorkingTreeFormat.initialize().
1466
1390
 
1467
1391
        :param revision_id: allows creating a working tree at a different
1468
 
            revision than the branch is at.
 
1392
        revision than the branch is at.
1469
1393
        :param accelerator_tree: A tree which can be used for retrieving file
1470
1394
            contents more quickly than the revision tree, i.e. a workingtree.
1471
1395
            The revision tree will be used for cases where accelerator_tree's
1564
1488
        :param wt: the WorkingTree object
1565
1489
        """
1566
1490
 
1567
 
    def open(self, a_bzrdir, _found=False):
1568
 
        """Return the WorkingTree object for a_bzrdir
1569
 
 
1570
 
        _found is a private parameter, do not use it. It is used to indicate
1571
 
               if format probing has already been done.
1572
 
        """
1573
 
        if not _found:
1574
 
            # we are being called directly and must probe.
1575
 
            raise NotImplementedError
1576
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
1577
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1578
 
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1579
 
        return wt
1580
 
 
1581
1491
    def _open(self, a_bzrdir, control_files):
1582
1492
        """Open the tree itself.
1583
1493
 
1674
1584
        return True
1675
1585
 
1676
1586
 
1677
 
class DirStateRevisionTree(InventoryTree):
 
1587
class DirStateRevisionTree(Tree):
1678
1588
    """A revision tree pulling the inventory from a dirstate.
1679
1589
    
1680
1590
    Note that this is one of the historical (ie revision) trees cached in the
1699
1609
    def annotate_iter(self, file_id,
1700
1610
                      default_revision=_mod_revision.CURRENT_REVISION):
1701
1611
        """See Tree.annotate_iter"""
1702
 
        text_key = (file_id, self.get_file_revision(file_id))
 
1612
        text_key = (file_id, self.inventory[file_id].revision)
1703
1613
        annotations = self._repository.texts.annotate(text_key)
1704
1614
        return [(key[-1], line) for (key, line) in annotations]
1705
1615
 
 
1616
    def _get_ancestors(self, default_revision):
 
1617
        return set(self._repository.get_ancestry(self._revision_id,
 
1618
                                                 topo_sorted=False))
1706
1619
    def _comparison_data(self, entry, path):
1707
1620
        """See Tree._comparison_data."""
1708
1621
        if entry is None:
1824
1737
                elif kind == 'directory':
1825
1738
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1826
1739
                elif kind == 'symlink':
 
1740
                    inv_entry.executable = False
 
1741
                    inv_entry.text_size = None
1827
1742
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1828
1743
                elif kind == 'tree-reference':
1829
1744
                    inv_entry.reference_revision = fingerprint or None
1866
1781
            return parent_details[1]
1867
1782
        return None
1868
1783
 
1869
 
    @needs_read_lock
1870
 
    def get_file_revision(self, file_id):
1871
 
        return self.inventory[file_id].revision
1872
 
 
1873
1784
    def get_file(self, file_id, path=None):
1874
1785
        return StringIO(self.get_file_text(file_id))
1875
1786
 
1898
1809
                                       identifier))
1899
1810
        return self._repository.iter_files_bytes(repo_desired_files)
1900
1811
 
1901
 
    def get_symlink_target(self, file_id, path=None):
 
1812
    def get_symlink_target(self, file_id):
1902
1813
        entry = self._get_entry(file_id=file_id)
1903
1814
        parent_index = self._get_parent_index()
1904
1815
        if entry[1][parent_index][0] != 'l':
1957
1868
    def is_executable(self, file_id, path=None):
1958
1869
        ie = self.inventory[file_id]
1959
1870
        if ie.kind != "file":
1960
 
            return False
 
1871
            return None
1961
1872
        return ie.executable
1962
1873
 
1963
1874
    def is_locked(self):
2076
1987
    def make_source_parent_tree(source, target):
2077
1988
        """Change the source tree into a parent of the target."""
2078
1989
        revid = source.commit('record tree')
2079
 
        target.branch.fetch(source.branch, revid)
 
1990
        target.branch.repository.fetch(source.branch.repository, revid)
2080
1991
        target.set_parent_ids([revid])
2081
1992
        return target.basis_tree(), target
2082
1993