~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: v.ladeuil+lp at free
  • Date: 2006-10-17 14:01:12 UTC
  • mfrom: (2084 +trunk)
  • mto: (2145.1.1 keepalive)
  • mto: This revision was merged to the branch mainline in revision 2146.
  • Revision ID: v.ladeuil+lp@free.fr-20061017140112-8ae6aac456429ccf
Merge bzr.dev

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
164
165
 
165
166
def gen_root_id():
166
167
    """Return a new tree-root file id."""
167
 
    return gen_file_id('TREE_ROOT')
 
168
    return gen_file_id('tree_root')
168
169
 
169
170
 
170
171
class TreeEntry(object):
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
538
554
    @needs_read_lock
539
555
    def copy_content_into(self, tree, revision_id=None):
540
556
        """Copy the current content and user files of this tree into tree."""
 
557
        tree.set_root_id(self.get_root_id())
541
558
        if revision_id is None:
542
559
            merge.transform_tree(tree, self)
543
560
        else:
849
866
        else:
850
867
            return '?'
851
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
 
852
880
    def list_files(self, include_root=False):
853
881
        """Recursively list all files as (path, class, kind, id, entry).
854
882
 
1010
1038
        # create a file in this interval and then the rename might be
1011
1039
        # left half-done.  But we should have caught most problems.
1012
1040
        orig_inv = deepcopy(self.inventory)
 
1041
        original_modified = self._inventory_is_modified
1013
1042
        try:
 
1043
            if len(from_paths):
 
1044
                self._inventory_is_modified = True
1014
1045
            for f in from_paths:
1015
1046
                name_tail = splitpath(f)[-1]
1016
1047
                dest_path = pathjoin(to_name, name_tail)
1024
1055
                            ["rename rolled back"])
1025
1056
        except:
1026
1057
            # restore the inventory on error
1027
 
            self._set_inventory(orig_inv)
 
1058
            self._set_inventory(orig_inv, dirty=original_modified)
1028
1059
            raise
1029
1060
        self._write_inventory(inv)
1030
1061
        return result
1158
1189
                                basis_tree,
1159
1190
                                this_tree=self,
1160
1191
                                pb=pb)
 
1192
                    if (basis_tree.inventory.root is None and
 
1193
                        new_basis_tree.inventory.root is not None):
 
1194
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
1161
1195
                finally:
1162
1196
                    pb.finished()
1163
1197
                # TODO - dedup parents list with things merged by pull ?
1471
1505
        
1472
1506
    @needs_read_lock
1473
1507
    def read_working_inventory(self):
1474
 
        """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.
1475
1515
        # ElementTree does its own conversion from UTF-8, so open in
1476
1516
        # binary.
 
1517
        if self._inventory_is_modified:
 
1518
            raise errors.InventoryModified(self)
1477
1519
        result = xml5.serializer_v5.read_inventory(
1478
1520
            self._control_files.get('inventory'))
1479
 
        self._set_inventory(result)
 
1521
        self._set_inventory(result, dirty=False)
1480
1522
        return result
1481
1523
 
1482
1524
    @needs_tree_write_lock
1562
1604
    @needs_tree_write_lock
1563
1605
    def set_root_id(self, file_id):
1564
1606
        """Set the root id for this tree."""
1565
 
        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
1566
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
1567
1621
        del inv._byid[inv.root.file_id]
1568
1622
        inv.root.file_id = file_id
 
1623
        # and link it into the index with the new changed id.
1569
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.
1570
1628
        for fid in inv:
1571
1629
            entry = inv[fid]
1572
1630
            if entry.parent_id == orig_root_id:
1573
1631
                entry.parent_id = inv.root.file_id
1574
 
        self._write_inventory(inv)
1575
1632
 
1576
1633
    def unlock(self):
1577
1634
        """See Branch.unlock.
1588
1645
    def update(self):
1589
1646
        """Update a working tree along its branch.
1590
1647
 
1591
 
        This will update the branch if its bound too, which means we have multiple trees involved:
1592
 
        The new basis tree of the master.
1593
 
        The old basis tree of the branch.
1594
 
        The old basis tree of the working tree.
1595
 
        The current working tree state.
1596
 
        pathologically all three may be different, and non ancestors of each other.
1597
 
        Conceptually we want to:
1598
 
        Preserve the wt.basis->wt.state changes
1599
 
        Transform the wt.basis to the new master basis.
1600
 
        Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1601
 
        Restore the wt.basis->wt.state changes.
 
1648
        This will update the branch if its bound too, which means we have
 
1649
        multiple trees involved:
 
1650
 
 
1651
        - The new basis tree of the master.
 
1652
        - The old basis tree of the branch.
 
1653
        - The old basis tree of the working tree.
 
1654
        - The current working tree state.
 
1655
 
 
1656
        Pathologically, all three may be different, and non-ancestors of each
 
1657
        other.  Conceptually we want to:
 
1658
 
 
1659
        - Preserve the wt.basis->wt.state changes
 
1660
        - Transform the wt.basis to the new master basis.
 
1661
        - Apply a merge of the old branch basis to get any 'local' changes from
 
1662
          it into the tree.
 
1663
        - Restore the wt.basis->wt.state changes.
1602
1664
 
1603
1665
        There isn't a single operation at the moment to do that, so we:
1604
 
        Merge current state -> basis tree of the master w.r.t. the old tree basis.
1605
 
        Do a 'normal' merge of the old branch basis if it is relevant.
 
1666
        - Merge current state -> basis tree of the master w.r.t. the old tree
 
1667
          basis.
 
1668
        - Do a 'normal' merge of the old branch basis if it is relevant.
1606
1669
        """
1607
1670
        old_tip = self.branch.update()
 
1671
 
1608
1672
        # here if old_tip is not None, it is the old tip of the branch before
1609
1673
        # it was updated from the master branch. This should become a pending
1610
1674
        # merge in the working tree to preserve the user existing work.  we
1624
1688
            # merge tree state up to new branch tip.
1625
1689
            basis = self.basis_tree()
1626
1690
            to_tree = self.branch.basis_tree()
 
1691
            if basis.inventory.root is None:
 
1692
                self.set_root_id(to_tree.inventory.root.file_id)
1627
1693
            result += merge.merge_inner(
1628
1694
                                  self.branch,
1629
1695
                                  to_tree,
1676
1742
    @needs_tree_write_lock
1677
1743
    def _write_inventory(self, inv):
1678
1744
        """Write inventory as the current inventory."""
1679
 
        sio = StringIO()
1680
 
        xml5.serializer_v5.write_inventory(inv, sio)
1681
 
        sio.seek(0)
1682
 
        self._control_files.put('inventory', sio)
1683
 
        self._set_inventory(inv)
1684
 
        mutter('wrote working inventory')
 
1745
        self._set_inventory(inv, dirty=True)
 
1746
        self.flush()
1685
1747
 
1686
1748
    def set_conflicts(self, arg):
1687
1749
        raise UnsupportedOperation(self.set_conflicts, self)
1739
1801
 
1740
1802
    def unlock(self):
1741
1803
        # we share control files:
1742
 
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1743
 
            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()
1744
1810
        # reverse order of locking.
1745
1811
        try:
1746
1812
            return self._control_files.unlock()
1804
1870
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1805
1871
 
1806
1872
    def unlock(self):
1807
 
        if self._hashcache.needs_write and self._control_files._lock_count==1:
1808
 
            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()
1809
1879
        # reverse order of locking.
1810
1880
        try:
1811
1881
            return self._control_files.unlock()
1957
2027
                         _internal=True,
1958
2028
                         _format=self,
1959
2029
                         _bzrdir=a_bzrdir)
1960
 
        wt._write_inventory(inv)
1961
 
        wt.set_root_id(inv.root.file_id)
1962
2030
        basis_tree = branch.repository.revision_tree(revision)
 
2031
        if basis_tree.inventory.root is not None:
 
2032
            wt.set_root_id(basis_tree.inventory.root.file_id)
 
2033
        # set the parent list and cache the basis tree.
1963
2034
        wt.set_parent_trees([(revision, basis_tree)])
1964
2035
        transform.build_tree(basis_tree, wt)
1965
2036
        return wt
2029
2100
        branch = a_bzrdir.open_branch()
2030
2101
        if revision_id is None:
2031
2102
            revision_id = branch.last_revision()
2032
 
        inv = Inventory() 
 
2103
        inv = Inventory(root_id=gen_root_id())
2033
2104
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2034
2105
                         branch,
2035
2106
                         inv,
2039
2110
                         _control_files=control_files)
2040
2111
        wt.lock_tree_write()
2041
2112
        try:
2042
 
            wt._write_inventory(inv)
2043
 
            wt.set_root_id(inv.root.file_id)
2044
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)
2045
2117
            if revision_id == NULL_REVISION:
2046
2118
                wt.set_parent_trees([])
2047
2119
            else:
2048
2120
                wt.set_parent_trees([(revision_id, basis_tree)])
2049
2121
            transform.build_tree(basis_tree, wt)
2050
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()
2051
2126
            wt.unlock()
2052
 
            control_files.unlock()
2053
2127
        return wt
2054
2128
 
2055
2129
    def __init__(self):