~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-10-16 04:21:07 UTC
  • mfrom: (1986.5.8 init-tree-overhead)
  • Revision ID: pqm@pqm.ubuntu.com-20061016042107-3868eeaa654059ae
(Robert Collins, John Arbash Meinel) Reduce inventory write operations during tree construction by making tree inventory writes occur at tree unlock time. This adds WorkingTree.flush() and alters set_root_id to not write the inventory.

Show diffs side-by-side

added added

removed removed

Lines of Context:
58
58
    ignores,
59
59
    merge,
60
60
    osutils,
61
 
    urlutils,
62
61
    textui,
63
62
    transform,
 
63
    urlutils,
64
64
    xml5,
65
65
    xml6,
66
66
    )
69
69
import bzrlib.ui
70
70
""")
71
71
 
 
72
from bzrlib import symbol_versioning
72
73
from bzrlib.decorators import needs_read_lock, needs_write_lock
73
74
from bzrlib.errors import (BzrCheckError,
74
75
                           BzrError,
80
81
                           MergeModifiedFormatError,
81
82
                           UnsupportedOperation,
82
83
                           )
83
 
from bzrlib.inventory import InventoryEntry, Inventory
 
84
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
84
85
from bzrlib.lockable_files import LockableFiles, TransportLock
85
86
from bzrlib.lockdir import LockDir
86
87
import bzrlib.mutabletree
259
260
            self.basedir = wt.basedir
260
261
            self._control_files = wt._control_files
261
262
            self._hashcache = wt._hashcache
262
 
            self._set_inventory(wt._inventory)
 
263
            self._set_inventory(wt._inventory, dirty=False)
263
264
            self._format = wt._format
264
265
            self.bzrdir = wt.bzrdir
265
266
        from bzrlib.hashcache import HashCache
307
308
            hc.write()
308
309
 
309
310
        if _inventory is None:
310
 
            self._set_inventory(self.read_working_inventory())
 
311
            self._inventory_is_modified = False
 
312
            self.read_working_inventory()
311
313
        else:
312
 
            self._set_inventory(_inventory)
 
314
            # the caller of __init__ has provided an inventory,
 
315
            # we assume they know what they are doing - as its only
 
316
            # the Format factory and creation methods that are
 
317
            # permitted to do this.
 
318
            self._set_inventory(_inventory, dirty=False)
313
319
 
314
320
    branch = property(
315
321
        fget=lambda self: self._branch,
330
336
        self._control_files.break_lock()
331
337
        self.branch.break_lock()
332
338
 
333
 
    def _set_inventory(self, inv):
 
339
    def _set_inventory(self, inv, dirty):
 
340
        """Set the internal cached inventory.
 
341
 
 
342
        :param inv: The inventory to set.
 
343
        :param dirty: A boolean indicating whether the inventory is the same
 
344
            logical inventory as whats on disk. If True the inventory is not
 
345
            the same and should be written to disk or data will be lost, if
 
346
            False then the inventory is the same as that on disk and any
 
347
            serialisation would be unneeded overhead.
 
348
        """
334
349
        assert inv.root is not None
335
350
        self._inventory = inv
 
351
        self._inventory_is_modified = dirty
336
352
 
337
353
    @staticmethod
338
354
    def open(path=None, _unsupported=False):
503
519
                parents.append(l.rstrip('\n'))
504
520
        return parents
505
521
 
 
522
    @needs_read_lock
506
523
    def get_root_id(self):
507
524
        """Return the id of this trees root"""
508
 
        inv = self.read_working_inventory()
509
 
        return inv.root.file_id
 
525
        return self._inventory.root.file_id
510
526
        
511
527
    def _get_store_filename(self, file_id):
512
528
        ## XXX: badly named; this is not in the store at all
850
866
        else:
851
867
            return '?'
852
868
 
 
869
    def flush(self):
 
870
        """Write the in memory inventory to disk."""
 
871
        # TODO: Maybe this should only write on dirty ?
 
872
        if self._control_files._lock_mode != 'w':
 
873
            raise errors.NotWriteLocked(self)
 
874
        sio = StringIO()
 
875
        xml5.serializer_v5.write_inventory(self._inventory, sio)
 
876
        sio.seek(0)
 
877
        self._control_files.put('inventory', sio)
 
878
        self._inventory_is_modified = False
 
879
 
853
880
    def list_files(self, include_root=False):
854
881
        """Recursively list all files as (path, class, kind, id, entry).
855
882
 
1011
1038
        # create a file in this interval and then the rename might be
1012
1039
        # left half-done.  But we should have caught most problems.
1013
1040
        orig_inv = deepcopy(self.inventory)
 
1041
        original_modified = self._inventory_is_modified
1014
1042
        try:
 
1043
            if len(from_paths):
 
1044
                self._inventory_is_modified = True
1015
1045
            for f in from_paths:
1016
1046
                name_tail = splitpath(f)[-1]
1017
1047
                dest_path = pathjoin(to_name, name_tail)
1025
1055
                            ["rename rolled back"])
1026
1056
        except:
1027
1057
            # restore the inventory on error
1028
 
            self._set_inventory(orig_inv)
 
1058
            self._set_inventory(orig_inv, dirty=original_modified)
1029
1059
            raise
1030
1060
        self._write_inventory(inv)
1031
1061
        return result
1475
1505
        
1476
1506
    @needs_read_lock
1477
1507
    def read_working_inventory(self):
1478
 
        """Read the working inventory."""
 
1508
        """Read the working inventory.
 
1509
        
 
1510
        :raises errors.InventoryModified: read_working_inventory will fail
 
1511
            when the current in memory inventory has been modified.
 
1512
        """
 
1513
        # conceptually this should be an implementation detail of the tree. 
 
1514
        # XXX: Deprecate this.
1479
1515
        # ElementTree does its own conversion from UTF-8, so open in
1480
1516
        # binary.
 
1517
        if self._inventory_is_modified:
 
1518
            raise errors.InventoryModified(self)
1481
1519
        result = xml5.serializer_v5.read_inventory(
1482
1520
            self._control_files.get('inventory'))
1483
 
        self._set_inventory(result)
 
1521
        self._set_inventory(result, dirty=False)
1484
1522
        return result
1485
1523
 
1486
1524
    @needs_tree_write_lock
1566
1604
    @needs_tree_write_lock
1567
1605
    def set_root_id(self, file_id):
1568
1606
        """Set the root id for this tree."""
1569
 
        inv = self.read_working_inventory()
 
1607
        # for compatability 
 
1608
        if file_id is None:
 
1609
            symbol_versioning.warn(symbol_versioning.zero_twelve
 
1610
                % 'WorkingTree.set_root_id with fileid=None',
 
1611
                DeprecationWarning,
 
1612
                stacklevel=3)
 
1613
            file_id = ROOT_ID
 
1614
        inv = self._inventory
1570
1615
        orig_root_id = inv.root.file_id
 
1616
        # TODO: it might be nice to exit early if there was nothing
 
1617
        # to do, saving us from trigger a sync on unlock.
 
1618
        self._inventory_is_modified = True
 
1619
        # we preserve the root inventory entry object, but
 
1620
        # unlinkit from the byid index
1571
1621
        del inv._byid[inv.root.file_id]
1572
1622
        inv.root.file_id = file_id
 
1623
        # and link it into the index with the new changed id.
1573
1624
        inv._byid[inv.root.file_id] = inv.root
 
1625
        # and finally update all children to reference the new id.
 
1626
        # XXX: this should be safe to just look at the root.children
 
1627
        # list, not the WHOLE INVENTORY.
1574
1628
        for fid in inv:
1575
1629
            entry = inv[fid]
1576
1630
            if entry.parent_id == orig_root_id:
1577
1631
                entry.parent_id = inv.root.file_id
1578
 
        self._write_inventory(inv)
1579
1632
 
1580
1633
    def unlock(self):
1581
1634
        """See Branch.unlock.
1689
1742
    @needs_tree_write_lock
1690
1743
    def _write_inventory(self, inv):
1691
1744
        """Write inventory as the current inventory."""
1692
 
        sio = StringIO()
1693
 
        xml5.serializer_v5.write_inventory(inv, sio)
1694
 
        sio.seek(0)
1695
 
        self._control_files.put('inventory', sio)
1696
 
        self._set_inventory(inv)
1697
 
        mutter('wrote working inventory')
 
1745
        self._set_inventory(inv, dirty=True)
 
1746
        self.flush()
1698
1747
 
1699
1748
    def set_conflicts(self, arg):
1700
1749
        raise UnsupportedOperation(self.set_conflicts, self)
1752
1801
 
1753
1802
    def unlock(self):
1754
1803
        # we share control files:
1755
 
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1756
 
            self._hashcache.write()
 
1804
        if self._control_files._lock_count == 3:
 
1805
            # _inventory_is_modified is always False during a read lock.
 
1806
            if self._inventory_is_modified:
 
1807
                self.flush()
 
1808
            if self._hashcache.needs_write:
 
1809
                self._hashcache.write()
1757
1810
        # reverse order of locking.
1758
1811
        try:
1759
1812
            return self._control_files.unlock()
1817
1870
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1818
1871
 
1819
1872
    def unlock(self):
1820
 
        if self._hashcache.needs_write and self._control_files._lock_count==1:
1821
 
            self._hashcache.write()
 
1873
        if self._control_files._lock_count == 1:
 
1874
            # _inventory_is_modified is always False during a read lock.
 
1875
            if self._inventory_is_modified:
 
1876
                self.flush()
 
1877
            if self._hashcache.needs_write:
 
1878
                self._hashcache.write()
1822
1879
        # reverse order of locking.
1823
1880
        try:
1824
1881
            return self._control_files.unlock()
1970
2027
                         _internal=True,
1971
2028
                         _format=self,
1972
2029
                         _bzrdir=a_bzrdir)
1973
 
        wt.set_last_revision(revision)
1974
 
        basis_tree = wt.basis_tree()
 
2030
        basis_tree = branch.repository.revision_tree(revision)
1975
2031
        if basis_tree.inventory.root is not None:
1976
 
            inv.root.file_id = basis_tree.inventory.root.file_id
1977
 
        wt._write_inventory(inv)
 
2032
            wt.set_root_id(basis_tree.inventory.root.file_id)
 
2033
        # set the parent list and cache the basis tree.
1978
2034
        wt.set_parent_trees([(revision, basis_tree)])
1979
2035
        transform.build_tree(basis_tree, wt)
1980
2036
        return wt
2044
2100
        branch = a_bzrdir.open_branch()
2045
2101
        if revision_id is None:
2046
2102
            revision_id = branch.last_revision()
2047
 
        inv = Inventory(root_id=gen_root_id()) 
 
2103
        inv = Inventory(root_id=gen_root_id())
2048
2104
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2049
2105
                         branch,
2050
2106
                         inv,
2054
2110
                         _control_files=control_files)
2055
2111
        wt.lock_tree_write()
2056
2112
        try:
2057
 
            wt.set_last_revision(revision_id)
2058
 
            basis_tree = wt.basis_tree()
2059
 
            wt._write_inventory(inv)
2060
 
            wt.set_pending_merges([])
2061
 
            if revision_id == bzrlib.revision.NULL_REVISION:
 
2113
            basis_tree = branch.repository.revision_tree(revision_id)
 
2114
            # only set an explicit root id if there is one to set.
 
2115
            if basis_tree.inventory.root is not None:
 
2116
                wt.set_root_id(basis_tree.inventory.root.file_id)
 
2117
            if revision_id == NULL_REVISION:
2062
2118
                wt.set_parent_trees([])
2063
2119
            else:
2064
2120
                wt.set_parent_trees([(revision_id, basis_tree)])
2065
2121
            transform.build_tree(basis_tree, wt)
2066
2122
        finally:
 
2123
            # Unlock in this order so that the unlock-triggers-flush in
 
2124
            # WorkingTree is given a chance to fire.
 
2125
            control_files.unlock()
2067
2126
            wt.unlock()
2068
 
            control_files.unlock()
2069
2127
        return wt
2070
2128
 
2071
2129
    def __init__(self):