/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

  • Committer: Martin Pool
  • Date: 2006-11-02 10:20:19 UTC
  • mfrom: (2114 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2119.
  • Revision ID: mbp@sourcefrog.net-20061102102019-9a5a02f485dff6f6
merge bzr.dev and reconcile several changes, also some test fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
WorkingTree.open(dir).
30
30
"""
31
31
 
32
 
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
33
 
CONFLICT_HEADER_1 = "BZR conflict list format 1"
34
 
 
35
32
# TODO: Give the workingtree sole responsibility for the working inventory;
36
33
# remove the variable and references to it from the branch.  This may require
37
34
# updating the commit code so as to update the inventory within the working
39
36
# At the moment they may alias the inventory and have old copies of it in
40
37
# memory.  (Now done? -- mbp 20060309)
41
38
 
42
 
from binascii import hexlify
 
39
from cStringIO import StringIO
 
40
import os
 
41
import re
 
42
 
 
43
from bzrlib.lazy_import import lazy_import
 
44
lazy_import(globals(), """
43
45
import collections
44
46
from copy import deepcopy
45
 
from cStringIO import StringIO
46
47
import errno
47
48
import fnmatch
48
 
import os
49
 
import re
50
49
import stat
51
50
from time import time
52
51
import warnings
53
52
 
54
53
import bzrlib
55
 
from bzrlib import bzrdir, errors, ignores, osutils, urlutils
56
 
from bzrlib.atomicfile import AtomicFile
 
54
from bzrlib import (
 
55
    bzrdir,
 
56
    conflicts as _mod_conflicts,
 
57
    errors,
 
58
    ignores,
 
59
    merge,
 
60
    osutils,
 
61
    textui,
 
62
    transform,
 
63
    urlutils,
 
64
    xml5,
 
65
    xml6,
 
66
    )
57
67
import bzrlib.branch
58
 
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
 
68
from bzrlib.transport import get_transport
 
69
import bzrlib.ui
 
70
""")
 
71
 
 
72
from bzrlib import symbol_versioning
59
73
from bzrlib.decorators import needs_read_lock, needs_write_lock
60
74
from bzrlib.errors import (BzrCheckError,
61
75
                           BzrError,
67
81
                           MergeModifiedFormatError,
68
82
                           UnsupportedOperation,
69
83
                           )
70
 
from bzrlib.inventory import InventoryEntry, Inventory
 
84
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
71
85
from bzrlib.lockable_files import LockableFiles, TransportLock
72
86
from bzrlib.lockdir import LockDir
73
 
from bzrlib.merge import merge_inner, transform_tree
74
87
import bzrlib.mutabletree
75
88
from bzrlib.mutabletree import needs_tree_write_lock
76
89
from bzrlib.osutils import (
77
 
                            abspath,
78
 
                            compact_date,
79
 
                            file_kind,
80
 
                            isdir,
81
 
                            getcwd,
82
 
                            pathjoin,
83
 
                            pumpfile,
84
 
                            safe_unicode,
85
 
                            splitpath,
86
 
                            rand_chars,
87
 
                            normpath,
88
 
                            realpath,
89
 
                            relpath,
90
 
                            rename,
91
 
                            supports_executable,
92
 
                            )
 
90
    compact_date,
 
91
    file_kind,
 
92
    isdir,
 
93
    pathjoin,
 
94
    safe_unicode,
 
95
    splitpath,
 
96
    rand_chars,
 
97
    normpath,
 
98
    realpath,
 
99
    supports_executable,
 
100
    )
 
101
from bzrlib.trace import mutter, note
 
102
from bzrlib.transport.local import LocalTransport
 
103
import bzrlib.tree
93
104
from bzrlib.progress import DummyProgress, ProgressPhase
94
105
from bzrlib.revision import NULL_REVISION
95
106
import bzrlib.revisiontree
101
112
        zero_eight,
102
113
        zero_eleven,
103
114
        )
104
 
from bzrlib.trace import mutter, note
105
 
from bzrlib.transform import build_tree
106
 
from bzrlib.transport import get_transport
107
 
from bzrlib.transport.local import LocalTransport
108
 
from bzrlib.textui import show_status
109
 
import bzrlib.ui
110
 
import bzrlib.xml5
111
 
 
 
115
 
 
116
 
 
117
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
 
118
CONFLICT_HEADER_1 = "BZR conflict list format 1"
112
119
 
113
120
# the regex removes any weird characters; we don't escape them 
114
121
# but rather just pull them out
158
165
 
159
166
def gen_root_id():
160
167
    """Return a new tree-root file id."""
161
 
    return gen_file_id('TREE_ROOT')
 
168
    return gen_file_id('tree_root')
162
169
 
163
170
 
164
171
class TreeEntry(object):
253
260
            self.basedir = wt.basedir
254
261
            self._control_files = wt._control_files
255
262
            self._hashcache = wt._hashcache
256
 
            self._set_inventory(wt._inventory)
 
263
            self._set_inventory(wt._inventory, dirty=False)
257
264
            self._format = wt._format
258
265
            self.bzrdir = wt.bzrdir
259
266
        from bzrlib.hashcache import HashCache
301
308
            hc.write()
302
309
 
303
310
        if _inventory is None:
304
 
            self._set_inventory(self.read_working_inventory())
 
311
            self._inventory_is_modified = False
 
312
            self.read_working_inventory()
305
313
        else:
306
 
            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)
307
319
 
308
320
    branch = property(
309
321
        fget=lambda self: self._branch,
324
336
        self._control_files.break_lock()
325
337
        self.branch.break_lock()
326
338
 
327
 
    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
        """
328
349
        assert inv.root is not None
329
350
        self._inventory = inv
 
351
        self._inventory_is_modified = dirty
330
352
 
331
353
    @staticmethod
332
354
    def open(path=None, _unsupported=False):
399
421
        else:
400
422
            try:
401
423
                xml = self.read_basis_inventory()
402
 
                inv = bzrlib.xml6.serializer_v6.read_inventory_from_string(xml)
 
424
                inv = xml6.serializer_v6.read_inventory_from_string(xml)
403
425
                if inv is not None and inv.revision_id == revision_id:
404
 
                    return bzrlib.tree.RevisionTree(self.branch.repository, 
405
 
                                                    inv, revision_id)
 
426
                    return bzrlib.revisiontree.RevisionTree(
 
427
                        self.branch.repository, inv, revision_id)
406
428
            except (NoSuchFile, errors.BadInventoryFormat):
407
429
                pass
408
430
        # No cached copy available, retrieve from the repository.
463
485
        The path may be absolute or relative. If its a relative path it is 
464
486
        interpreted relative to the python current working directory.
465
487
        """
466
 
        return relpath(self.basedir, path)
 
488
        return osutils.relpath(self.basedir, path)
467
489
 
468
490
    def has_filename(self, filename):
469
491
        return osutils.lexists(self.abspath(filename))
497
519
                parents.append(l.rstrip('\n'))
498
520
        return parents
499
521
 
 
522
    @needs_read_lock
500
523
    def get_root_id(self):
501
524
        """Return the id of this trees root"""
502
 
        inv = self.read_working_inventory()
503
 
        return inv.root.file_id
 
525
        return self._inventory.root.file_id
504
526
        
505
527
    def _get_store_filename(self, file_id):
506
528
        ## XXX: badly named; this is not in the store at all
532
554
    @needs_read_lock
533
555
    def copy_content_into(self, tree, revision_id=None):
534
556
        """Copy the current content and user files of this tree into tree."""
 
557
        tree.set_root_id(self.get_root_id())
535
558
        if revision_id is None:
536
 
            transform_tree(tree, self)
 
559
            merge.transform_tree(tree, self)
537
560
        else:
538
561
            # TODO now merge from tree.last_revision to revision (to preserve
539
562
            # user local changes)
540
 
            transform_tree(tree, self)
 
563
            merge.transform_tree(tree, self)
541
564
            tree.set_parent_ids([revision_id])
542
565
 
543
566
    def id2abspath(self, file_id):
843
866
        else:
844
867
            return '?'
845
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
 
846
880
    def list_files(self, include_root=False):
847
881
        """Recursively list all files as (path, class, kind, id, entry).
848
882
 
1004
1038
        # create a file in this interval and then the rename might be
1005
1039
        # left half-done.  But we should have caught most problems.
1006
1040
        orig_inv = deepcopy(self.inventory)
 
1041
        original_modified = self._inventory_is_modified
1007
1042
        try:
 
1043
            if len(from_paths):
 
1044
                self._inventory_is_modified = True
1008
1045
            for f in from_paths:
1009
1046
                name_tail = splitpath(f)[-1]
1010
1047
                dest_path = pathjoin(to_name, name_tail)
1011
1048
                result.append((f, dest_path))
1012
1049
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
1013
1050
                try:
1014
 
                    rename(self.abspath(f), self.abspath(dest_path))
 
1051
                    osutils.rename(self.abspath(f), self.abspath(dest_path))
1015
1052
                except OSError, e:
1016
1053
                    raise BzrError("failed to rename %r to %r: %s" %
1017
1054
                                   (f, dest_path, e[1]))
1018
1055
        except:
1019
1056
            # restore the inventory on error
1020
 
            self._set_inventory(orig_inv)
 
1057
            self._set_inventory(orig_inv, dirty=original_modified)
1021
1058
            raise
1022
1059
        self._write_inventory(inv)
1023
1060
        return result
1062
1099
        from_abs = self.abspath(from_rel)
1063
1100
        to_abs = self.abspath(to_rel)
1064
1101
        try:
1065
 
            rename(from_abs, to_abs)
 
1102
            osutils.rename(from_abs, to_abs)
1066
1103
        except OSError, e:
1067
1104
            inv.rename(file_id, from_parent, from_name)
1068
1105
            raise BzrError("failed to rename %r to %r: %s"
1144
1181
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
1145
1182
                try:
1146
1183
                    new_basis_tree = self.branch.basis_tree()
1147
 
                    merge_inner(self.branch,
 
1184
                    merge.merge_inner(
 
1185
                                self.branch,
1148
1186
                                new_basis_tree,
1149
1187
                                basis_tree,
1150
1188
                                this_tree=self,
1151
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)
1152
1193
                finally:
1153
1194
                    pb.finished()
1154
1195
                # TODO - dedup parents list with things merged by pull ?
1426
1467
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1427
1468
        """Create the text that will be saved in basis-inventory"""
1428
1469
        inventory.revision_id = revision_id
1429
 
        return bzrlib.xml6.serializer_v6.write_inventory_to_string(inventory)
 
1470
        return xml6.serializer_v6.write_inventory_to_string(inventory)
1430
1471
 
1431
1472
    def _cache_basis_inventory(self, new_revision):
1432
1473
        """Cache new_revision as the basis inventory."""
1462
1503
        
1463
1504
    @needs_read_lock
1464
1505
    def read_working_inventory(self):
1465
 
        """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.
1466
1513
        # ElementTree does its own conversion from UTF-8, so open in
1467
1514
        # binary.
1468
 
        result = bzrlib.xml5.serializer_v5.read_inventory(
 
1515
        if self._inventory_is_modified:
 
1516
            raise errors.InventoryModified(self)
 
1517
        result = xml5.serializer_v5.read_inventory(
1469
1518
            self._control_files.get('inventory'))
1470
 
        self._set_inventory(result)
 
1519
        self._set_inventory(result, dirty=False)
1471
1520
        return result
1472
1521
 
1473
1522
    @needs_tree_write_lock
1505
1554
                    new_status = 'I'
1506
1555
                else:
1507
1556
                    new_status = '?'
1508
 
                show_status(new_status, inv[fid].kind, f, to_file=to_file)
 
1557
                textui.show_status(new_status, inv[fid].kind, f,
 
1558
                                   to_file=to_file)
1509
1559
            del inv[fid]
1510
1560
 
1511
1561
        self._write_inventory(inv)
1513
1563
    @needs_tree_write_lock
1514
1564
    def revert(self, filenames, old_tree=None, backups=True, 
1515
1565
               pb=DummyProgress()):
1516
 
        from transform import revert
1517
 
        from conflicts import resolve
 
1566
        from bzrlib.conflicts import resolve
1518
1567
        if old_tree is None:
1519
1568
            old_tree = self.basis_tree()
1520
 
        conflicts = revert(self, old_tree, filenames, backups, pb)
 
1569
        conflicts = transform.revert(self, old_tree, filenames, backups, pb)
1521
1570
        if not len(filenames):
1522
1571
            self.set_parent_ids(self.get_parent_ids()[:1])
1523
1572
            resolve(self)
1553
1602
    @needs_tree_write_lock
1554
1603
    def set_root_id(self, file_id):
1555
1604
        """Set the root id for this tree."""
1556
 
        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
1557
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
1558
1619
        del inv._byid[inv.root.file_id]
1559
1620
        inv.root.file_id = file_id
 
1621
        # and link it into the index with the new changed id.
1560
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.
1561
1626
        for fid in inv:
1562
1627
            entry = inv[fid]
1563
1628
            if entry.parent_id == orig_root_id:
1564
1629
                entry.parent_id = inv.root.file_id
1565
 
        self._write_inventory(inv)
1566
1630
 
1567
1631
    def unlock(self):
1568
1632
        """See Branch.unlock.
1575
1639
        """
1576
1640
        raise NotImplementedError(self.unlock)
1577
1641
 
1578
 
    @needs_write_lock
1579
1642
    def update(self):
1580
1643
        """Update a working tree along its branch.
1581
1644
 
1582
 
        This will update the branch if its bound too, which means we have multiple trees involved:
1583
 
        The new basis tree of the master.
1584
 
        The old basis tree of the branch.
1585
 
        The old basis tree of the working tree.
1586
 
        The current working tree state.
1587
 
        pathologically all three may be different, and non ancestors of each other.
1588
 
        Conceptually we want to:
1589
 
        Preserve the wt.basis->wt.state changes
1590
 
        Transform the wt.basis to the new master basis.
1591
 
        Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1592
 
        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.
1593
1661
 
1594
1662
        There isn't a single operation at the moment to do that, so we:
1595
 
        Merge current state -> basis tree of the master w.r.t. the old tree basis.
1596
 
        Do a 'normal' merge of the old branch basis if it is relevant.
1597
 
        """
1598
 
        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
        """
1599
1689
        # here if old_tip is not None, it is the old tip of the branch before
1600
1690
        # it was updated from the master branch. This should become a pending
1601
1691
        # merge in the working tree to preserve the user existing work.  we
1615
1705
            # merge tree state up to new branch tip.
1616
1706
            basis = self.basis_tree()
1617
1707
            to_tree = self.branch.basis_tree()
1618
 
            result += merge_inner(self.branch,
 
1708
            if basis.inventory.root is None:
 
1709
                self.set_root_id(to_tree.inventory.root.file_id)
 
1710
            result += merge.merge_inner(
 
1711
                                  self.branch,
1619
1712
                                  to_tree,
1620
1713
                                  basis,
1621
1714
                                  this_tree=self)
1656
1749
                base_rev_id = None
1657
1750
            base_tree = self.branch.repository.revision_tree(base_rev_id)
1658
1751
            other_tree = self.branch.repository.revision_tree(old_tip)
1659
 
            result += merge_inner(self.branch,
 
1752
            result += merge.merge_inner(
 
1753
                                  self.branch,
1660
1754
                                  other_tree,
1661
1755
                                  base_tree,
1662
1756
                                  this_tree=self)
1665
1759
    @needs_tree_write_lock
1666
1760
    def _write_inventory(self, inv):
1667
1761
        """Write inventory as the current inventory."""
1668
 
        sio = StringIO()
1669
 
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
1670
 
        sio.seek(0)
1671
 
        self._control_files.put('inventory', sio)
1672
 
        self._set_inventory(inv)
1673
 
        mutter('wrote working inventory')
 
1762
        self._set_inventory(inv, dirty=True)
 
1763
        self.flush()
1674
1764
 
1675
1765
    def set_conflicts(self, arg):
1676
1766
        raise UnsupportedOperation(self.set_conflicts, self)
1680
1770
 
1681
1771
    @needs_read_lock
1682
1772
    def conflicts(self):
1683
 
        conflicts = ConflictList()
 
1773
        conflicts = _mod_conflicts.ConflictList()
1684
1774
        for conflicted in self._iter_conflicts():
1685
1775
            text = True
1686
1776
            try:
1699
1789
                    if text == False:
1700
1790
                        break
1701
1791
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1702
 
            conflicts.append(Conflict.factory(ctype, path=conflicted,
 
1792
            conflicts.append(_mod_conflicts.Conflict.factory(ctype,
 
1793
                             path=conflicted,
1703
1794
                             file_id=self.path2id(conflicted)))
1704
1795
        return conflicts
1705
1796
 
1727
1818
 
1728
1819
    def unlock(self):
1729
1820
        # we share control files:
1730
 
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1731
 
            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()
1732
1827
        # reverse order of locking.
1733
1828
        try:
1734
1829
            return self._control_files.unlock()
1775
1870
    def add_conflicts(self, new_conflicts):
1776
1871
        conflict_set = set(self.conflicts())
1777
1872
        conflict_set.update(set(list(new_conflicts)))
1778
 
        self.set_conflicts(ConflictList(sorted(conflict_set,
1779
 
                                               key=Conflict.sort_key)))
 
1873
        self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
 
1874
                                       key=_mod_conflicts.Conflict.sort_key)))
1780
1875
 
1781
1876
    @needs_read_lock
1782
1877
    def conflicts(self):
1783
1878
        try:
1784
1879
            confile = self._control_files.get('conflicts')
1785
1880
        except NoSuchFile:
1786
 
            return ConflictList()
 
1881
            return _mod_conflicts.ConflictList()
1787
1882
        try:
1788
1883
            if confile.next() != CONFLICT_HEADER_1 + '\n':
1789
1884
                raise ConflictFormatError()
1790
1885
        except StopIteration:
1791
1886
            raise ConflictFormatError()
1792
 
        return ConflictList.from_stanzas(RioReader(confile))
 
1887
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1793
1888
 
1794
1889
    def unlock(self):
1795
 
        if self._hashcache.needs_write and self._control_files._lock_count==1:
1796
 
            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()
1797
1896
        # reverse order of locking.
1798
1897
        try:
1799
1898
            return self._control_files.unlock()
1802
1901
 
1803
1902
 
1804
1903
def get_conflicted_stem(path):
1805
 
    for suffix in CONFLICT_SUFFIXES:
 
1904
    for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
1806
1905
        if path.endswith(suffix):
1807
1906
            return path[:-len(suffix)]
1808
1907
 
1914
2013
        """
1915
2014
        sio = StringIO()
1916
2015
        inv = Inventory()
1917
 
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
 
2016
        xml5.serializer_v5.write_inventory(inv, sio)
1918
2017
        sio.seek(0)
1919
2018
        control_files.put('inventory', sio)
1920
2019
 
1945
2044
                         _internal=True,
1946
2045
                         _format=self,
1947
2046
                         _bzrdir=a_bzrdir)
1948
 
        wt._write_inventory(inv)
1949
 
        wt.set_root_id(inv.root.file_id)
1950
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.
1951
2051
        wt.set_parent_trees([(revision, basis_tree)])
1952
 
        build_tree(basis_tree, wt)
 
2052
        transform.build_tree(basis_tree, wt)
1953
2053
        return wt
1954
2054
 
1955
2055
    def __init__(self):
2017
2117
        branch = a_bzrdir.open_branch()
2018
2118
        if revision_id is None:
2019
2119
            revision_id = branch.last_revision()
2020
 
        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()
2021
2126
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2022
2127
                         branch,
2023
2128
                         inv,
2027
2132
                         _control_files=control_files)
2028
2133
        wt.lock_tree_write()
2029
2134
        try:
2030
 
            wt._write_inventory(inv)
2031
 
            wt.set_root_id(inv.root.file_id)
2032
2135
            basis_tree = branch.repository.revision_tree(revision_id)
2033
 
            if revision_id == bzrlib.revision.NULL_REVISION:
 
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)
 
2139
            if revision_id == NULL_REVISION:
2034
2140
                wt.set_parent_trees([])
2035
2141
            else:
2036
2142
                wt.set_parent_trees([(revision_id, basis_tree)])
2037
 
            build_tree(basis_tree, wt)
 
2143
            transform.build_tree(basis_tree, wt)
2038
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()
2039
2148
            wt.unlock()
2040
 
            control_files.unlock()
2041
2149
        return wt
2042
2150
 
2043
2151
    def __init__(self):