/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

MergeĀ fromĀ remote-transport

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)
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.
1584
1641
        """
1585
1642
        raise NotImplementedError(self.unlock)
1586
1643
 
1587
 
    @needs_write_lock
1588
1644
    def update(self):
1589
1645
        """Update a working tree along its branch.
1590
1646
 
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.
 
1647
        This will update the branch if its bound too, which means we have
 
1648
        multiple trees involved:
 
1649
 
 
1650
        - The new basis tree of the master.
 
1651
        - The old basis tree of the branch.
 
1652
        - The old basis tree of the working tree.
 
1653
        - The current working tree state.
 
1654
 
 
1655
        Pathologically, all three may be different, and non-ancestors of each
 
1656
        other.  Conceptually we want to:
 
1657
 
 
1658
        - Preserve the wt.basis->wt.state changes
 
1659
        - Transform the wt.basis to the new master basis.
 
1660
        - Apply a merge of the old branch basis to get any 'local' changes from
 
1661
          it into the tree.
 
1662
        - Restore the wt.basis->wt.state changes.
1602
1663
 
1603
1664
        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()
 
1665
        - Merge current state -> basis tree of the master w.r.t. the old tree
 
1666
          basis.
 
1667
        - Do a 'normal' merge of the old branch basis if it is relevant.
 
1668
        """
 
1669
        if self.branch.get_master_branch() is not None:
 
1670
            self.lock_write()
 
1671
            update_branch = True
 
1672
        else:
 
1673
            self.lock_tree_write()
 
1674
            update_branch = False
 
1675
        try:
 
1676
            if update_branch:
 
1677
                old_tip = self.branch.update()
 
1678
            else:
 
1679
                old_tip = None
 
1680
            return self._update_tree(old_tip)
 
1681
        finally:
 
1682
            self.unlock()
 
1683
 
 
1684
    @needs_tree_write_lock
 
1685
    def _update_tree(self, old_tip=None):
 
1686
        """Update a tree to the master branch.
 
1687
 
 
1688
        :param old_tip: if supplied, the previous tip revision the branch,
 
1689
            before it was changed to the master branch's tip.
 
1690
        """
1608
1691
        # here if old_tip is not None, it is the old tip of the branch before
1609
1692
        # it was updated from the master branch. This should become a pending
1610
1693
        # merge in the working tree to preserve the user existing work.  we
1624
1707
            # merge tree state up to new branch tip.
1625
1708
            basis = self.basis_tree()
1626
1709
            to_tree = self.branch.basis_tree()
 
1710
            if basis.inventory.root is None:
 
1711
                self.set_root_id(to_tree.inventory.root.file_id)
1627
1712
            result += merge.merge_inner(
1628
1713
                                  self.branch,
1629
1714
                                  to_tree,
1676
1761
    @needs_tree_write_lock
1677
1762
    def _write_inventory(self, inv):
1678
1763
        """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')
 
1764
        self._set_inventory(inv, dirty=True)
 
1765
        self.flush()
1685
1766
 
1686
1767
    def set_conflicts(self, arg):
1687
1768
        raise UnsupportedOperation(self.set_conflicts, self)
1739
1820
 
1740
1821
    def unlock(self):
1741
1822
        # we share control files:
1742
 
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1743
 
            self._hashcache.write()
 
1823
        if self._control_files._lock_count == 3:
 
1824
            # _inventory_is_modified is always False during a read lock.
 
1825
            if self._inventory_is_modified:
 
1826
                self.flush()
 
1827
            if self._hashcache.needs_write:
 
1828
                self._hashcache.write()
1744
1829
        # reverse order of locking.
1745
1830
        try:
1746
1831
            return self._control_files.unlock()
1804
1889
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1805
1890
 
1806
1891
    def unlock(self):
1807
 
        if self._hashcache.needs_write and self._control_files._lock_count==1:
1808
 
            self._hashcache.write()
 
1892
        if self._control_files._lock_count == 1:
 
1893
            # _inventory_is_modified is always False during a read lock.
 
1894
            if self._inventory_is_modified:
 
1895
                self.flush()
 
1896
            if self._hashcache.needs_write:
 
1897
                self._hashcache.write()
1809
1898
        # reverse order of locking.
1810
1899
        try:
1811
1900
            return self._control_files.unlock()
1957
2046
                         _internal=True,
1958
2047
                         _format=self,
1959
2048
                         _bzrdir=a_bzrdir)
1960
 
        wt._write_inventory(inv)
1961
 
        wt.set_root_id(inv.root.file_id)
1962
2049
        basis_tree = branch.repository.revision_tree(revision)
 
2050
        if basis_tree.inventory.root is not None:
 
2051
            wt.set_root_id(basis_tree.inventory.root.file_id)
 
2052
        # set the parent list and cache the basis tree.
1963
2053
        wt.set_parent_trees([(revision, basis_tree)])
1964
2054
        transform.build_tree(basis_tree, wt)
1965
2055
        return wt
2029
2119
        branch = a_bzrdir.open_branch()
2030
2120
        if revision_id is None:
2031
2121
            revision_id = branch.last_revision()
2032
 
        inv = Inventory() 
 
2122
        # WorkingTree3 can handle an inventory which has a unique root id.
 
2123
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
 
2124
        # those trees. And because there isn't a format bump inbetween, we
 
2125
        # are maintaining compatibility with older clients.
 
2126
        # inv = Inventory(root_id=gen_root_id())
 
2127
        inv = Inventory()
2033
2128
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2034
2129
                         branch,
2035
2130
                         inv,
2039
2134
                         _control_files=control_files)
2040
2135
        wt.lock_tree_write()
2041
2136
        try:
2042
 
            wt._write_inventory(inv)
2043
 
            wt.set_root_id(inv.root.file_id)
2044
2137
            basis_tree = branch.repository.revision_tree(revision_id)
 
2138
            # only set an explicit root id if there is one to set.
 
2139
            if basis_tree.inventory.root is not None:
 
2140
                wt.set_root_id(basis_tree.inventory.root.file_id)
2045
2141
            if revision_id == NULL_REVISION:
2046
2142
                wt.set_parent_trees([])
2047
2143
            else:
2048
2144
                wt.set_parent_trees([(revision_id, basis_tree)])
2049
2145
            transform.build_tree(basis_tree, wt)
2050
2146
        finally:
 
2147
            # Unlock in this order so that the unlock-triggers-flush in
 
2148
            # WorkingTree is given a chance to fire.
 
2149
            control_files.unlock()
2051
2150
            wt.unlock()
2052
 
            control_files.unlock()
2053
2151
        return wt
2054
2152
 
2055
2153
    def __init__(self):