~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

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
 
55
from bzrlib.lock import LogicalLockResult
 
56
from bzrlib.lockable_files import LockableFiles
 
57
from bzrlib.lockdir import LockDir
56
58
from bzrlib.mutabletree import needs_tree_write_lock
57
59
from bzrlib.osutils import (
58
60
    file_kind,
61
63
    realpath,
62
64
    safe_unicode,
63
65
    )
64
 
from bzrlib.trace import mutter
65
66
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):
 
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
 
72
80
    def __init__(self, basedir,
73
81
                 branch,
74
82
                 _control_files=None,
84
92
        self._format = _format
85
93
        self.bzrdir = _bzrdir
86
94
        basedir = safe_unicode(basedir)
87
 
        mutter("opening working tree %r", basedir)
 
95
        trace.mutter("opening working tree %r", basedir)
88
96
        self._branch = branch
89
97
        self.basedir = realpath(basedir)
90
98
        # if branch is at our basedir and is a format 6 or less
124
132
            state.add(f, file_id, kind, None, '')
125
133
        self._make_dirty(reset_inventory=True)
126
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
 
127
139
    def _make_dirty(self, reset_inventory):
128
140
        """Make the tree state dirty.
129
141
 
181
193
 
182
194
    def _comparison_data(self, entry, path):
183
195
        kind, executable, stat_value = \
184
 
            WorkingTree3._comparison_data(self, entry, path)
 
196
            WorkingTree._comparison_data(self, entry, path)
185
197
        # it looks like a plain directory, but it's really a reference -- see
186
198
        # also kind()
187
199
        if (self._repo_supports_tree_reference and kind == 'directory'
193
205
    def commit(self, message=None, revprops=None, *args, **kwargs):
194
206
        # mark the tree as dirty post commit - commit
195
207
        # can change the current versioned list by doing deletes.
196
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
208
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
197
209
        self._make_dirty(reset_inventory=True)
198
210
        return result
199
211
 
218
230
        local_path = self.bzrdir.get_workingtree_transport(None
219
231
            ).local_abspath('dirstate')
220
232
        self._dirstate = dirstate.DirState.on_file(local_path,
221
 
            self._sha1_provider())
 
233
            self._sha1_provider(), self._worth_saving_limit())
222
234
        return self._dirstate
223
235
 
224
236
    def _sha1_provider(self):
233
245
        else:
234
246
            return None
235
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
 
236
257
    def filter_unversioned_files(self, paths):
237
258
        """Filter out paths that are versioned.
238
259
 
368
389
        state = self.current_dirstate()
369
390
        if stat_value is None:
370
391
            try:
371
 
                stat_value = os.lstat(file_abspath)
 
392
                stat_value = osutils.lstat(file_abspath)
372
393
            except OSError, e:
373
394
                if e.errno == errno.ENOENT:
374
395
                    return None
477
498
            self._must_be_locked()
478
499
            if not path:
479
500
                path = self.id2path(file_id)
480
 
            mode = os.lstat(self.abspath(path)).st_mode
 
501
            mode = osutils.lstat(self.abspath(path)).st_mode
481
502
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
482
503
 
483
504
    def all_file_ids(self):
567
588
            return _mod_revision.NULL_REVISION
568
589
 
569
590
    def lock_read(self):
570
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
591
        """See Branch.lock_read, and WorkingTree.unlock.
 
592
 
 
593
        :return: A bzrlib.lock.LogicalLockResult.
 
594
        """
571
595
        self.branch.lock_read()
572
596
        try:
573
597
            self._control_files.lock_read()
586
610
        except:
587
611
            self.branch.unlock()
588
612
            raise
 
613
        return LogicalLockResult(self.unlock)
589
614
 
590
615
    def _lock_self_write(self):
591
616
        """This should be called after the branch is locked."""
606
631
        except:
607
632
            self.branch.unlock()
608
633
            raise
 
634
        return LogicalLockResult(self.unlock)
609
635
 
610
636
    def lock_tree_write(self):
611
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
637
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
638
 
 
639
        :return: A bzrlib.lock.LogicalLockResult.
 
640
        """
612
641
        self.branch.lock_read()
613
 
        self._lock_self_write()
 
642
        return self._lock_self_write()
614
643
 
615
644
    def lock_write(self):
616
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
645
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
646
 
 
647
        :return: A bzrlib.lock.LogicalLockResult.
 
648
        """
617
649
        self.branch.lock_write()
618
 
        self._lock_self_write()
 
650
        return self._lock_self_write()
619
651
 
620
652
    @needs_tree_write_lock
621
653
    def move(self, from_paths, to_dir, after=False):
838
870
                rollback_rename()
839
871
                raise
840
872
            result.append((from_rel, to_rel))
841
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
873
            state._mark_modified()
842
874
            self._make_dirty(reset_inventory=False)
843
875
 
844
876
        return result
1101
1133
                        _mod_revision.NULL_REVISION)))
1102
1134
                ghosts.append(rev_id)
1103
1135
            accepted_revisions.add(rev_id)
1104
 
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
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)
1105
1159
        self._make_dirty(reset_inventory=False)
1106
1160
 
1107
1161
    def _set_root_id(self, file_id):
1127
1181
 
1128
1182
    def unlock(self):
1129
1183
        """Unlock in format 4 trees needs to write the entire dirstate."""
1130
 
        # do non-implementation specific cleanup
1131
 
        self._cleanup()
1132
 
 
1133
1184
        if self._control_files._lock_count == 1:
 
1185
            # do non-implementation specific cleanup
 
1186
            self._cleanup()
 
1187
 
1134
1188
            # eventually we should do signature checking during read locks for
1135
1189
            # dirstate updates.
1136
1190
            if self._control_files._lock_mode == 'w':
1235
1289
        # have to change the legacy inventory too.
1236
1290
        if self._inventory is not None:
1237
1291
            for file_id in file_ids:
1238
 
                self._inventory.remove_recursive_id(file_id)
 
1292
                if self._inventory.has_id(file_id):
 
1293
                    self._inventory.remove_recursive_id(file_id)
1239
1294
 
1240
1295
    @needs_tree_write_lock
1241
1296
    def rename_one(self, from_rel, to_rel, after=False):
1242
1297
        """See WorkingTree.rename_one"""
1243
1298
        self.flush()
1244
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1299
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1245
1300
 
1246
1301
    @needs_tree_write_lock
1247
1302
    def apply_inventory_delta(self, changes):
1280
1335
            self._inventory = inv
1281
1336
        self.flush()
1282
1337
 
 
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
 
1283
1359
 
1284
1360
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1285
1361
 
1290
1366
        """See dirstate.SHA1Provider.sha1()."""
1291
1367
        filters = self.tree._content_filter_stack(
1292
1368
            self.tree.relpath(osutils.safe_unicode(abspath)))
1293
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1369
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1294
1370
 
1295
1371
    def stat_and_sha1(self, abspath):
1296
1372
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1300
1376
        try:
1301
1377
            statvalue = os.fstat(file_obj.fileno())
1302
1378
            if filters:
1303
 
                file_obj = filtered_input_file(file_obj, filters)
 
1379
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1304
1380
            sha1 = osutils.size_sha_file(file_obj)[1]
1305
1381
        finally:
1306
1382
            file_obj.close()
1317
1393
    def _file_content_summary(self, path, stat_result):
1318
1394
        # This is to support the somewhat obsolete path_content_summary method
1319
1395
        # with content filtering: see
1320
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1396
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1321
1397
        #
1322
1398
        # If the dirstate cache is up to date and knows the hash and size,
1323
1399
        # return that.
1336
1412
class WorkingTree4(DirStateWorkingTree):
1337
1413
    """This is the Format 4 working tree.
1338
1414
 
1339
 
    This differs from WorkingTree3 by:
 
1415
    This differs from WorkingTree by:
1340
1416
     - Having a consolidated internal dirstate, stored in a
1341
1417
       randomly-accessible sorted file on disk.
1342
1418
     - Not having a regular inventory attribute.  One can be synthesized
1370
1446
        return views.PathBasedViews(self)
1371
1447
 
1372
1448
 
1373
 
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
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)
1374
1462
 
1375
1463
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1376
1464
                   accelerator_tree=None, hardlink=False):
1377
1465
        """See WorkingTreeFormat.initialize().
1378
1466
 
1379
1467
        :param revision_id: allows creating a working tree at a different
1380
 
        revision than the branch is at.
 
1468
            revision than the branch is at.
1381
1469
        :param accelerator_tree: A tree which can be used for retrieving file
1382
1470
            contents more quickly than the revision tree, i.e. a workingtree.
1383
1471
            The revision tree will be used for cases where accelerator_tree's
1476
1564
        :param wt: the WorkingTree object
1477
1565
        """
1478
1566
 
 
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
 
1479
1581
    def _open(self, a_bzrdir, control_files):
1480
1582
        """Open the tree itself.
1481
1583
 
1572
1674
        return True
1573
1675
 
1574
1676
 
1575
 
class DirStateRevisionTree(Tree):
 
1677
class DirStateRevisionTree(InventoryTree):
1576
1678
    """A revision tree pulling the inventory from a dirstate.
1577
1679
    
1578
1680
    Note that this is one of the historical (ie revision) trees cached in the
1597
1699
    def annotate_iter(self, file_id,
1598
1700
                      default_revision=_mod_revision.CURRENT_REVISION):
1599
1701
        """See Tree.annotate_iter"""
1600
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1702
        text_key = (file_id, self.get_file_revision(file_id))
1601
1703
        annotations = self._repository.texts.annotate(text_key)
1602
1704
        return [(key[-1], line) for (key, line) in annotations]
1603
1705
 
1604
 
    def _get_ancestors(self, default_revision):
1605
 
        return set(self._repository.get_ancestry(self._revision_id,
1606
 
                                                 topo_sorted=False))
1607
1706
    def _comparison_data(self, entry, path):
1608
1707
        """See Tree._comparison_data."""
1609
1708
        if entry is None:
1725
1824
                elif kind == 'directory':
1726
1825
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1727
1826
                elif kind == 'symlink':
1728
 
                    inv_entry.executable = False
1729
 
                    inv_entry.text_size = None
1730
1827
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1731
1828
                elif kind == 'tree-reference':
1732
1829
                    inv_entry.reference_revision = fingerprint or None
1752
1849
        # Make sure the file exists
1753
1850
        entry = self._get_entry(file_id, path=path)
1754
1851
        if entry == (None, None): # do we raise?
1755
 
            return None
 
1852
            raise errors.NoSuchId(self, file_id)
1756
1853
        parent_index = self._get_parent_index()
1757
1854
        last_changed_revision = entry[1][parent_index][4]
1758
1855
        try:
1769
1866
            return parent_details[1]
1770
1867
        return None
1771
1868
 
 
1869
    @needs_read_lock
 
1870
    def get_file_revision(self, file_id):
 
1871
        return self.inventory[file_id].revision
 
1872
 
1772
1873
    def get_file(self, file_id, path=None):
1773
1874
        return StringIO(self.get_file_text(file_id))
1774
1875
 
1797
1898
                                       identifier))
1798
1899
        return self._repository.iter_files_bytes(repo_desired_files)
1799
1900
 
1800
 
    def get_symlink_target(self, file_id):
 
1901
    def get_symlink_target(self, file_id, path=None):
1801
1902
        entry = self._get_entry(file_id=file_id)
1802
1903
        parent_index = self._get_parent_index()
1803
1904
        if entry[1][parent_index][0] != 'l':
1856
1957
    def is_executable(self, file_id, path=None):
1857
1958
        ie = self.inventory[file_id]
1858
1959
        if ie.kind != "file":
1859
 
            return None
 
1960
            return False
1860
1961
        return ie.executable
1861
1962
 
 
1963
    def is_locked(self):
 
1964
        return self._locked
 
1965
 
1862
1966
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1863
1967
        # We use a standard implementation, because DirStateRevisionTree is
1864
1968
        # dealing with one of the parents of the current state
1877
1981
            yield path, 'V', entry.kind, entry.file_id, entry
1878
1982
 
1879
1983
    def lock_read(self):
1880
 
        """Lock the tree for a set of operations."""
 
1984
        """Lock the tree for a set of operations.
 
1985
 
 
1986
        :return: A bzrlib.lock.LogicalLockResult.
 
1987
        """
1881
1988
        if not self._locked:
1882
1989
            self._repository.lock_read()
1883
1990
            if self._dirstate._lock_token is None:
1884
1991
                self._dirstate.lock_read()
1885
1992
                self._dirstate_locked = True
1886
1993
        self._locked += 1
 
1994
        return LogicalLockResult(self.unlock)
1887
1995
 
1888
1996
    def _must_be_locked(self):
1889
1997
        if not self._locked:
1968
2076
    def make_source_parent_tree(source, target):
1969
2077
        """Change the source tree into a parent of the target."""
1970
2078
        revid = source.commit('record tree')
1971
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2079
        target.branch.fetch(source.branch, revid)
1972
2080
        target.set_parent_ids([revid])
1973
2081
        return target.basis_tree(), target
1974
2082