/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 bzr.dev

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, 
 
426
                    return bzrlib.tree.RevisionTree(self.branch.repository,
405
427
                                                    inv, revision_id)
406
428
            except (NoSuchFile, errors.BadInventoryFormat):
407
429
                pass
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):
675
698
        """
676
699
        return self.get_parent_ids()[1:]
677
700
 
 
701
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
 
702
        """Common ghost checking functionality from set_parent_*.
 
703
 
 
704
        This checks that the left hand-parent exists if there are any
 
705
        revisions present.
 
706
        """
 
707
        if len(revision_ids) > 0:
 
708
            leftmost_id = revision_ids[0]
 
709
            if (not allow_leftmost_as_ghost and not
 
710
                self.branch.repository.has_revision(leftmost_id)):
 
711
                raise errors.GhostRevisionUnusableHere(leftmost_id)
 
712
 
 
713
    def _set_merges_from_parent_ids(self, parent_ids):
 
714
        merges = parent_ids[1:]
 
715
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
 
716
 
678
717
    @needs_tree_write_lock
679
718
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
680
719
        """Set the parent ids to revision_ids.
688
727
        :param revision_ids: The revision_ids to set as the parent ids of this
689
728
            working tree. Any of these may be ghosts.
690
729
        """
 
730
        self._check_parents_for_ghosts(revision_ids,
 
731
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
732
 
691
733
        if len(revision_ids) > 0:
692
 
            leftmost_id = revision_ids[0]
693
 
            if (not allow_leftmost_as_ghost and not
694
 
                self.branch.repository.has_revision(leftmost_id)):
695
 
                raise errors.GhostRevisionUnusableHere(leftmost_id)
696
 
            self.set_last_revision(leftmost_id)
 
734
            self.set_last_revision(revision_ids[0])
697
735
        else:
698
736
            self.set_last_revision(None)
699
 
        merges = revision_ids[1:]
700
 
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
 
737
 
 
738
        self._set_merges_from_parent_ids(revision_ids)
701
739
 
702
740
    @needs_tree_write_lock
703
741
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
704
742
        """See MutableTree.set_parent_trees."""
705
 
        # parent trees are not used in current format trees, delegate to
706
 
        # set_parent_ids
707
 
        self.set_parent_ids([rev for (rev, tree) in parents_list],
 
743
        parent_ids = [rev for (rev, tree) in parents_list]
 
744
 
 
745
        self._check_parents_for_ghosts(parent_ids,
708
746
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
709
747
 
 
748
        if len(parent_ids) == 0:
 
749
            leftmost_parent_id = None
 
750
            leftmost_parent_tree = None
 
751
        else:
 
752
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
 
753
 
 
754
        if self._change_last_revision(leftmost_parent_id):
 
755
            if leftmost_parent_tree is None:
 
756
                # If we don't have a tree, fall back to reading the
 
757
                # parent tree from the repository.
 
758
                self._cache_basis_inventory(leftmost_parent_id)
 
759
            else:
 
760
                inv = leftmost_parent_tree.inventory
 
761
                xml = self._create_basis_xml_from_inventory(
 
762
                                        leftmost_parent_id, inv)
 
763
                self._write_basis_inventory(xml)
 
764
        self._set_merges_from_parent_ids(parent_ids)
 
765
 
710
766
    @needs_tree_write_lock
711
767
    def set_pending_merges(self, rev_list):
712
768
        parents = self.get_parent_ids()
810
866
        else:
811
867
            return '?'
812
868
 
813
 
    def list_files(self):
 
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
 
 
880
    def list_files(self, include_root=False):
814
881
        """Recursively list all files as (path, class, kind, id, entry).
815
882
 
816
883
        Lists, but does not descend into unversioned directories.
821
888
        Skips the control directory.
822
889
        """
823
890
        inv = self._inventory
 
891
        if include_root is True:
 
892
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
824
893
        # Convert these into local objects to save lookup times
825
894
        pathjoin = osutils.pathjoin
826
895
        file_kind = osutils.file_kind
969
1038
        # create a file in this interval and then the rename might be
970
1039
        # left half-done.  But we should have caught most problems.
971
1040
        orig_inv = deepcopy(self.inventory)
 
1041
        original_modified = self._inventory_is_modified
972
1042
        try:
 
1043
            if len(from_paths):
 
1044
                self._inventory_is_modified = True
973
1045
            for f in from_paths:
974
1046
                name_tail = splitpath(f)[-1]
975
1047
                dest_path = pathjoin(to_name, name_tail)
976
1048
                result.append((f, dest_path))
977
1049
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
978
1050
                try:
979
 
                    rename(self.abspath(f), self.abspath(dest_path))
 
1051
                    osutils.rename(self.abspath(f), self.abspath(dest_path))
980
1052
                except OSError, e:
981
1053
                    raise BzrError("failed to rename %r to %r: %s" %
982
1054
                                   (f, dest_path, e[1]),
983
1055
                            ["rename rolled back"])
984
1056
        except:
985
1057
            # restore the inventory on error
986
 
            self._set_inventory(orig_inv)
 
1058
            self._set_inventory(orig_inv, dirty=original_modified)
987
1059
            raise
988
1060
        self._write_inventory(inv)
989
1061
        return result
1028
1100
        from_abs = self.abspath(from_rel)
1029
1101
        to_abs = self.abspath(to_rel)
1030
1102
        try:
1031
 
            rename(from_abs, to_abs)
 
1103
            osutils.rename(from_abs, to_abs)
1032
1104
        except OSError, e:
1033
1105
            inv.rename(file_id, from_parent, from_name)
1034
1106
            raise BzrError("failed to rename %r to %r: %s"
1111
1183
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
1112
1184
                try:
1113
1185
                    new_basis_tree = self.branch.basis_tree()
1114
 
                    merge_inner(self.branch,
 
1186
                    merge.merge_inner(
 
1187
                                self.branch,
1115
1188
                                new_basis_tree,
1116
1189
                                basis_tree,
1117
1190
                                this_tree=self,
1118
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)
1119
1195
                finally:
1120
1196
                    pb.finished()
1121
1197
                # TODO - dedup parents list with things merged by pull ?
1383
1459
            self.branch.set_revision_history([new_revision])
1384
1460
        return True
1385
1461
 
 
1462
    def _write_basis_inventory(self, xml):
 
1463
        """Write the basis inventory XML to the basis-inventory file"""
 
1464
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
 
1465
        path = self._basis_inventory_name()
 
1466
        sio = StringIO(xml)
 
1467
        self._control_files.put(path, sio)
 
1468
 
 
1469
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
 
1470
        """Create the text that will be saved in basis-inventory"""
 
1471
        inventory.revision_id = revision_id
 
1472
        return xml6.serializer_v6.write_inventory_to_string(inventory)
 
1473
 
1386
1474
    def _cache_basis_inventory(self, new_revision):
1387
1475
        """Cache new_revision as the basis inventory."""
1388
1476
        # TODO: this should allow the ready-to-use inventory to be passed in,
1405
1493
                'format="6"' not in firstline):
1406
1494
                inv = self.branch.repository.deserialise_inventory(
1407
1495
                    new_revision, xml)
1408
 
                inv.revision_id = new_revision
1409
 
                xml = bzrlib.xml6.serializer_v6.write_inventory_to_string(inv)
1410
 
            assert isinstance(xml, str), 'serialised xml must be bytestring.'
1411
 
            path = self._basis_inventory_name()
1412
 
            sio = StringIO(xml)
1413
 
            self._control_files.put(path, sio)
 
1496
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
 
1497
            self._write_basis_inventory(xml)
1414
1498
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1415
1499
            pass
1416
1500
 
1421
1505
        
1422
1506
    @needs_read_lock
1423
1507
    def read_working_inventory(self):
1424
 
        """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.
1425
1515
        # ElementTree does its own conversion from UTF-8, so open in
1426
1516
        # binary.
1427
 
        result = bzrlib.xml5.serializer_v5.read_inventory(
 
1517
        if self._inventory_is_modified:
 
1518
            raise errors.InventoryModified(self)
 
1519
        result = xml5.serializer_v5.read_inventory(
1428
1520
            self._control_files.get('inventory'))
1429
 
        self._set_inventory(result)
 
1521
        self._set_inventory(result, dirty=False)
1430
1522
        return result
1431
1523
 
1432
1524
    @needs_tree_write_lock
1464
1556
                    new_status = 'I'
1465
1557
                else:
1466
1558
                    new_status = '?'
1467
 
                show_status(new_status, inv[fid].kind, f, to_file=to_file)
 
1559
                textui.show_status(new_status, inv[fid].kind, f,
 
1560
                                   to_file=to_file)
1468
1561
            del inv[fid]
1469
1562
 
1470
1563
        self._write_inventory(inv)
1472
1565
    @needs_tree_write_lock
1473
1566
    def revert(self, filenames, old_tree=None, backups=True, 
1474
1567
               pb=DummyProgress()):
1475
 
        from transform import revert
1476
 
        from conflicts import resolve
 
1568
        from bzrlib.conflicts import resolve
1477
1569
        if old_tree is None:
1478
1570
            old_tree = self.basis_tree()
1479
 
        conflicts = revert(self, old_tree, filenames, backups, pb)
 
1571
        conflicts = transform.revert(self, old_tree, filenames, backups, pb)
1480
1572
        if not len(filenames):
1481
1573
            self.set_parent_ids(self.get_parent_ids()[:1])
1482
1574
            resolve(self)
1512
1604
    @needs_tree_write_lock
1513
1605
    def set_root_id(self, file_id):
1514
1606
        """Set the root id for this tree."""
1515
 
        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
1516
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
1517
1621
        del inv._byid[inv.root.file_id]
1518
1622
        inv.root.file_id = file_id
 
1623
        # and link it into the index with the new changed id.
1519
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.
1520
1628
        for fid in inv:
1521
1629
            entry = inv[fid]
1522
1630
            if entry.parent_id == orig_root_id:
1523
1631
                entry.parent_id = inv.root.file_id
1524
 
        self._write_inventory(inv)
1525
1632
 
1526
1633
    def unlock(self):
1527
1634
        """See Branch.unlock.
1538
1645
    def update(self):
1539
1646
        """Update a working tree along its branch.
1540
1647
 
1541
 
        This will update the branch if its bound too, which means we have multiple trees involved:
1542
 
        The new basis tree of the master.
1543
 
        The old basis tree of the branch.
1544
 
        The old basis tree of the working tree.
1545
 
        The current working tree state.
1546
 
        pathologically all three may be different, and non ancestors of each other.
1547
 
        Conceptually we want to:
1548
 
        Preserve the wt.basis->wt.state changes
1549
 
        Transform the wt.basis to the new master basis.
1550
 
        Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1551
 
        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.
1552
1664
 
1553
1665
        There isn't a single operation at the moment to do that, so we:
1554
 
        Merge current state -> basis tree of the master w.r.t. the old tree basis.
1555
 
        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.
1556
1669
        """
1557
1670
        old_tip = self.branch.update()
 
1671
 
1558
1672
        # here if old_tip is not None, it is the old tip of the branch before
1559
1673
        # it was updated from the master branch. This should become a pending
1560
1674
        # merge in the working tree to preserve the user existing work.  we
1574
1688
            # merge tree state up to new branch tip.
1575
1689
            basis = self.basis_tree()
1576
1690
            to_tree = self.branch.basis_tree()
1577
 
            result += merge_inner(self.branch,
 
1691
            if basis.inventory.root is None:
 
1692
                self.set_root_id(to_tree.inventory.root.file_id)
 
1693
            result += merge.merge_inner(
 
1694
                                  self.branch,
1578
1695
                                  to_tree,
1579
1696
                                  basis,
1580
1697
                                  this_tree=self)
1615
1732
                base_rev_id = None
1616
1733
            base_tree = self.branch.repository.revision_tree(base_rev_id)
1617
1734
            other_tree = self.branch.repository.revision_tree(old_tip)
1618
 
            result += merge_inner(self.branch,
 
1735
            result += merge.merge_inner(
 
1736
                                  self.branch,
1619
1737
                                  other_tree,
1620
1738
                                  base_tree,
1621
1739
                                  this_tree=self)
1624
1742
    @needs_tree_write_lock
1625
1743
    def _write_inventory(self, inv):
1626
1744
        """Write inventory as the current inventory."""
1627
 
        sio = StringIO()
1628
 
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
1629
 
        sio.seek(0)
1630
 
        self._control_files.put('inventory', sio)
1631
 
        self._set_inventory(inv)
1632
 
        mutter('wrote working inventory')
 
1745
        self._set_inventory(inv, dirty=True)
 
1746
        self.flush()
1633
1747
 
1634
1748
    def set_conflicts(self, arg):
1635
1749
        raise UnsupportedOperation(self.set_conflicts, self)
1639
1753
 
1640
1754
    @needs_read_lock
1641
1755
    def conflicts(self):
1642
 
        conflicts = ConflictList()
 
1756
        conflicts = _mod_conflicts.ConflictList()
1643
1757
        for conflicted in self._iter_conflicts():
1644
1758
            text = True
1645
1759
            try:
1658
1772
                    if text == False:
1659
1773
                        break
1660
1774
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1661
 
            conflicts.append(Conflict.factory(ctype, path=conflicted,
 
1775
            conflicts.append(_mod_conflicts.Conflict.factory(ctype,
 
1776
                             path=conflicted,
1662
1777
                             file_id=self.path2id(conflicted)))
1663
1778
        return conflicts
1664
1779
 
1686
1801
 
1687
1802
    def unlock(self):
1688
1803
        # we share control files:
1689
 
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1690
 
            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()
1691
1810
        # reverse order of locking.
1692
1811
        try:
1693
1812
            return self._control_files.unlock()
1734
1853
    def add_conflicts(self, new_conflicts):
1735
1854
        conflict_set = set(self.conflicts())
1736
1855
        conflict_set.update(set(list(new_conflicts)))
1737
 
        self.set_conflicts(ConflictList(sorted(conflict_set,
1738
 
                                               key=Conflict.sort_key)))
 
1856
        self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
 
1857
                                       key=_mod_conflicts.Conflict.sort_key)))
1739
1858
 
1740
1859
    @needs_read_lock
1741
1860
    def conflicts(self):
1742
1861
        try:
1743
1862
            confile = self._control_files.get('conflicts')
1744
1863
        except NoSuchFile:
1745
 
            return ConflictList()
 
1864
            return _mod_conflicts.ConflictList()
1746
1865
        try:
1747
1866
            if confile.next() != CONFLICT_HEADER_1 + '\n':
1748
1867
                raise ConflictFormatError()
1749
1868
        except StopIteration:
1750
1869
            raise ConflictFormatError()
1751
 
        return ConflictList.from_stanzas(RioReader(confile))
 
1870
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1752
1871
 
1753
1872
    def unlock(self):
1754
 
        if self._hashcache.needs_write and self._control_files._lock_count==1:
1755
 
            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()
1756
1879
        # reverse order of locking.
1757
1880
        try:
1758
1881
            return self._control_files.unlock()
1761
1884
 
1762
1885
 
1763
1886
def get_conflicted_stem(path):
1764
 
    for suffix in CONFLICT_SUFFIXES:
 
1887
    for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
1765
1888
        if path.endswith(suffix):
1766
1889
            return path[:-len(suffix)]
1767
1890
 
1873
1996
        """
1874
1997
        sio = StringIO()
1875
1998
        inv = Inventory()
1876
 
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
 
1999
        xml5.serializer_v5.write_inventory(inv, sio)
1877
2000
        sio.seek(0)
1878
2001
        control_files.put('inventory', sio)
1879
2002
 
1904
2027
                         _internal=True,
1905
2028
                         _format=self,
1906
2029
                         _bzrdir=a_bzrdir)
1907
 
        wt._write_inventory(inv)
1908
 
        wt.set_root_id(inv.root.file_id)
1909
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.
1910
2034
        wt.set_parent_trees([(revision, basis_tree)])
1911
 
        build_tree(basis_tree, wt)
 
2035
        transform.build_tree(basis_tree, wt)
1912
2036
        return wt
1913
2037
 
1914
2038
    def __init__(self):
1976
2100
        branch = a_bzrdir.open_branch()
1977
2101
        if revision_id is None:
1978
2102
            revision_id = branch.last_revision()
1979
 
        inv = Inventory() 
 
2103
        inv = Inventory(root_id=gen_root_id())
1980
2104
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1981
2105
                         branch,
1982
2106
                         inv,
1986
2110
                         _control_files=control_files)
1987
2111
        wt.lock_tree_write()
1988
2112
        try:
1989
 
            wt._write_inventory(inv)
1990
 
            wt.set_root_id(inv.root.file_id)
1991
2113
            basis_tree = branch.repository.revision_tree(revision_id)
1992
 
            if revision_id == bzrlib.revision.NULL_REVISION:
 
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:
1993
2118
                wt.set_parent_trees([])
1994
2119
            else:
1995
2120
                wt.set_parent_trees([(revision_id, basis_tree)])
1996
 
            build_tree(basis_tree, wt)
 
2121
            transform.build_tree(basis_tree, wt)
1997
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()
1998
2126
            wt.unlock()
1999
 
            control_files.unlock()
2000
2127
        return wt
2001
2128
 
2002
2129
    def __init__(self):