~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

Merge from 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):
407
423
                xml = self.read_basis_inventory()
408
424
                inv = xml6.serializer_v6.read_inventory_from_string(xml)
409
425
                if inv is not None and inv.revision_id == revision_id:
410
 
                    return bzrlib.tree.RevisionTree(self.branch.repository,
411
 
                                                    inv, revision_id)
 
426
                    return bzrlib.revisiontree.RevisionTree(
 
427
                        self.branch.repository, inv, revision_id)
412
428
            except (NoSuchFile, errors.BadInventoryFormat):
413
429
                pass
414
430
        # No cached copy available, retrieve from the repository.
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)
1020
1051
                    osutils.rename(self.abspath(f), self.abspath(dest_path))
1021
1052
                except OSError, e:
1022
1053
                    raise BzrError("failed to rename %r to %r: %s" %
1023
 
                                   (f, dest_path, e[1]),
1024
 
                            ["rename rolled back"])
 
1054
                                   (f, dest_path, e[1]))
1025
1055
        except:
1026
1056
            # restore the inventory on error
1027
 
            self._set_inventory(orig_inv)
 
1057
            self._set_inventory(orig_inv, dirty=original_modified)
1028
1058
            raise
1029
1059
        self._write_inventory(inv)
1030
1060
        return result
1073
1103
        except OSError, e:
1074
1104
            inv.rename(file_id, from_parent, from_name)
1075
1105
            raise BzrError("failed to rename %r to %r: %s"
1076
 
                    % (from_abs, to_abs, e[1]),
1077
 
                    ["rename rolled back"])
 
1106
                    % (from_abs, to_abs, e[1]))
1078
1107
        self._write_inventory(inv)
1079
1108
 
1080
1109
    @needs_read_lock
1158
1187
                                basis_tree,
1159
1188
                                this_tree=self,
1160
1189
                                pb=pb)
 
1190
                    if (basis_tree.inventory.root is None and
 
1191
                        new_basis_tree.inventory.root is not None):
 
1192
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
1161
1193
                finally:
1162
1194
                    pb.finished()
1163
1195
                # TODO - dedup parents list with things merged by pull ?
1471
1503
        
1472
1504
    @needs_read_lock
1473
1505
    def read_working_inventory(self):
1474
 
        """Read the working inventory."""
 
1506
        """Read the working inventory.
 
1507
        
 
1508
        :raises errors.InventoryModified: read_working_inventory will fail
 
1509
            when the current in memory inventory has been modified.
 
1510
        """
 
1511
        # conceptually this should be an implementation detail of the tree. 
 
1512
        # XXX: Deprecate this.
1475
1513
        # ElementTree does its own conversion from UTF-8, so open in
1476
1514
        # binary.
 
1515
        if self._inventory_is_modified:
 
1516
            raise errors.InventoryModified(self)
1477
1517
        result = xml5.serializer_v5.read_inventory(
1478
1518
            self._control_files.get('inventory'))
1479
 
        self._set_inventory(result)
 
1519
        self._set_inventory(result, dirty=False)
1480
1520
        return result
1481
1521
 
1482
1522
    @needs_tree_write_lock
1562
1602
    @needs_tree_write_lock
1563
1603
    def set_root_id(self, file_id):
1564
1604
        """Set the root id for this tree."""
1565
 
        inv = self.read_working_inventory()
 
1605
        # for compatability 
 
1606
        if file_id is None:
 
1607
            symbol_versioning.warn(symbol_versioning.zero_twelve
 
1608
                % 'WorkingTree.set_root_id with fileid=None',
 
1609
                DeprecationWarning,
 
1610
                stacklevel=3)
 
1611
            file_id = ROOT_ID
 
1612
        inv = self._inventory
1566
1613
        orig_root_id = inv.root.file_id
 
1614
        # TODO: it might be nice to exit early if there was nothing
 
1615
        # to do, saving us from trigger a sync on unlock.
 
1616
        self._inventory_is_modified = True
 
1617
        # we preserve the root inventory entry object, but
 
1618
        # unlinkit from the byid index
1567
1619
        del inv._byid[inv.root.file_id]
1568
1620
        inv.root.file_id = file_id
 
1621
        # and link it into the index with the new changed id.
1569
1622
        inv._byid[inv.root.file_id] = inv.root
 
1623
        # and finally update all children to reference the new id.
 
1624
        # XXX: this should be safe to just look at the root.children
 
1625
        # list, not the WHOLE INVENTORY.
1570
1626
        for fid in inv:
1571
1627
            entry = inv[fid]
1572
1628
            if entry.parent_id == orig_root_id:
1573
1629
                entry.parent_id = inv.root.file_id
1574
 
        self._write_inventory(inv)
1575
1630
 
1576
1631
    def unlock(self):
1577
1632
        """See Branch.unlock.
1584
1639
        """
1585
1640
        raise NotImplementedError(self.unlock)
1586
1641
 
1587
 
    @needs_write_lock
1588
1642
    def update(self):
1589
1643
        """Update a working tree along its branch.
1590
1644
 
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.
 
1645
        This will update the branch if its bound too, which means we have
 
1646
        multiple trees involved:
 
1647
 
 
1648
        - The new basis tree of the master.
 
1649
        - The old basis tree of the branch.
 
1650
        - The old basis tree of the working tree.
 
1651
        - The current working tree state.
 
1652
 
 
1653
        Pathologically, all three may be different, and non-ancestors of each
 
1654
        other.  Conceptually we want to:
 
1655
 
 
1656
        - Preserve the wt.basis->wt.state changes
 
1657
        - Transform the wt.basis to the new master basis.
 
1658
        - Apply a merge of the old branch basis to get any 'local' changes from
 
1659
          it into the tree.
 
1660
        - Restore the wt.basis->wt.state changes.
1602
1661
 
1603
1662
        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.
1606
 
        """
1607
 
        old_tip = self.branch.update()
 
1663
        - Merge current state -> basis tree of the master w.r.t. the old tree
 
1664
          basis.
 
1665
        - Do a 'normal' merge of the old branch basis if it is relevant.
 
1666
        """
 
1667
        if self.branch.get_master_branch() is not None:
 
1668
            self.lock_write()
 
1669
            update_branch = True
 
1670
        else:
 
1671
            self.lock_tree_write()
 
1672
            update_branch = False
 
1673
        try:
 
1674
            if update_branch:
 
1675
                old_tip = self.branch.update()
 
1676
            else:
 
1677
                old_tip = None
 
1678
            return self._update_tree(old_tip)
 
1679
        finally:
 
1680
            self.unlock()
 
1681
 
 
1682
    @needs_tree_write_lock
 
1683
    def _update_tree(self, old_tip=None):
 
1684
        """Update a tree to the master branch.
 
1685
 
 
1686
        :param old_tip: if supplied, the previous tip revision the branch,
 
1687
            before it was changed to the master branch's tip.
 
1688
        """
1608
1689
        # here if old_tip is not None, it is the old tip of the branch before
1609
1690
        # it was updated from the master branch. This should become a pending
1610
1691
        # merge in the working tree to preserve the user existing work.  we
1624
1705
            # merge tree state up to new branch tip.
1625
1706
            basis = self.basis_tree()
1626
1707
            to_tree = self.branch.basis_tree()
 
1708
            if basis.inventory.root is None:
 
1709
                self.set_root_id(to_tree.inventory.root.file_id)
1627
1710
            result += merge.merge_inner(
1628
1711
                                  self.branch,
1629
1712
                                  to_tree,
1676
1759
    @needs_tree_write_lock
1677
1760
    def _write_inventory(self, inv):
1678
1761
        """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')
 
1762
        self._set_inventory(inv, dirty=True)
 
1763
        self.flush()
1685
1764
 
1686
1765
    def set_conflicts(self, arg):
1687
1766
        raise UnsupportedOperation(self.set_conflicts, self)
1739
1818
 
1740
1819
    def unlock(self):
1741
1820
        # we share control files:
1742
 
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1743
 
            self._hashcache.write()
 
1821
        if self._control_files._lock_count == 3:
 
1822
            # _inventory_is_modified is always False during a read lock.
 
1823
            if self._inventory_is_modified:
 
1824
                self.flush()
 
1825
            if self._hashcache.needs_write:
 
1826
                self._hashcache.write()
1744
1827
        # reverse order of locking.
1745
1828
        try:
1746
1829
            return self._control_files.unlock()
1804
1887
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1805
1888
 
1806
1889
    def unlock(self):
1807
 
        if self._hashcache.needs_write and self._control_files._lock_count==1:
1808
 
            self._hashcache.write()
 
1890
        if self._control_files._lock_count == 1:
 
1891
            # _inventory_is_modified is always False during a read lock.
 
1892
            if self._inventory_is_modified:
 
1893
                self.flush()
 
1894
            if self._hashcache.needs_write:
 
1895
                self._hashcache.write()
1809
1896
        # reverse order of locking.
1810
1897
        try:
1811
1898
            return self._control_files.unlock()
1957
2044
                         _internal=True,
1958
2045
                         _format=self,
1959
2046
                         _bzrdir=a_bzrdir)
1960
 
        wt._write_inventory(inv)
1961
 
        wt.set_root_id(inv.root.file_id)
1962
2047
        basis_tree = branch.repository.revision_tree(revision)
 
2048
        if basis_tree.inventory.root is not None:
 
2049
            wt.set_root_id(basis_tree.inventory.root.file_id)
 
2050
        # set the parent list and cache the basis tree.
1963
2051
        wt.set_parent_trees([(revision, basis_tree)])
1964
2052
        transform.build_tree(basis_tree, wt)
1965
2053
        return wt
2029
2117
        branch = a_bzrdir.open_branch()
2030
2118
        if revision_id is None:
2031
2119
            revision_id = branch.last_revision()
2032
 
        inv = Inventory() 
 
2120
        # WorkingTree3 can handle an inventory which has a unique root id.
 
2121
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
 
2122
        # those trees. And because there isn't a format bump inbetween, we
 
2123
        # are maintaining compatibility with older clients.
 
2124
        # inv = Inventory(root_id=gen_root_id())
 
2125
        inv = Inventory()
2033
2126
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2034
2127
                         branch,
2035
2128
                         inv,
2039
2132
                         _control_files=control_files)
2040
2133
        wt.lock_tree_write()
2041
2134
        try:
2042
 
            wt._write_inventory(inv)
2043
 
            wt.set_root_id(inv.root.file_id)
2044
2135
            basis_tree = branch.repository.revision_tree(revision_id)
 
2136
            # only set an explicit root id if there is one to set.
 
2137
            if basis_tree.inventory.root is not None:
 
2138
                wt.set_root_id(basis_tree.inventory.root.file_id)
2045
2139
            if revision_id == NULL_REVISION:
2046
2140
                wt.set_parent_trees([])
2047
2141
            else:
2048
2142
                wt.set_parent_trees([(revision_id, basis_tree)])
2049
2143
            transform.build_tree(basis_tree, wt)
2050
2144
        finally:
 
2145
            # Unlock in this order so that the unlock-triggers-flush in
 
2146
            # WorkingTree is given a chance to fire.
 
2147
            control_files.unlock()
2051
2148
            wt.unlock()
2052
 
            control_files.unlock()
2053
2149
        return wt
2054
2150
 
2055
2151
    def __init__(self):