/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:
71
71
from bzrlib.lockable_files import LockableFiles, TransportLock
72
72
from bzrlib.lockdir import LockDir
73
73
from bzrlib.merge import merge_inner, transform_tree
 
74
import bzrlib.mutabletree
 
75
from bzrlib.mutabletree import needs_tree_write_lock
74
76
from bzrlib.osutils import (
75
77
                            abspath,
76
78
                            compact_date,
90
92
                            )
91
93
from bzrlib.progress import DummyProgress, ProgressPhase
92
94
from bzrlib.revision import NULL_REVISION
 
95
import bzrlib.revisiontree
93
96
from bzrlib.rio import RioReader, rio_file, Stanza
94
97
from bzrlib.symbol_versioning import (deprecated_passed,
95
98
        deprecated_method,
96
99
        deprecated_function,
97
100
        DEPRECATED_PARAMETER,
98
101
        zero_eight,
 
102
        zero_eleven,
99
103
        )
100
104
from bzrlib.trace import mutter, note
101
105
from bzrlib.transform import build_tree
102
106
from bzrlib.transport import get_transport
103
107
from bzrlib.transport.local import LocalTransport
104
108
from bzrlib.textui import show_status
105
 
import bzrlib.tree
106
109
import bzrlib.ui
107
110
import bzrlib.xml5
108
111
 
213
216
        return ''
214
217
 
215
218
 
216
 
class WorkingTree(bzrlib.tree.Tree):
 
219
class WorkingTree(bzrlib.mutabletree.MutableTree):
217
220
    """Working copy tree.
218
221
 
219
222
    The inventory is held in the `Branch` working-inventory, and the
324
327
    def _set_inventory(self, inv):
325
328
        assert inv.root is not None
326
329
        self._inventory = inv
327
 
        self.path2id = self._inventory.path2id
328
 
 
329
 
    def is_control_filename(self, filename):
330
 
        """True if filename is the name of a control file in this tree.
331
 
        
332
 
        :param filename: A filename within the tree. This is a relative path
333
 
        from the root of this tree.
334
 
 
335
 
        This is true IF and ONLY IF the filename is part of the meta data
336
 
        that bzr controls in this tree. I.E. a random .bzr directory placed
337
 
        on disk will not be a control file for this tree.
338
 
        """
339
 
        return self.bzrdir.is_control_filename(filename)
340
330
 
341
331
    @staticmethod
342
332
    def open(path=None, _unsupported=False):
399
389
        If the left most parent is a ghost then the returned tree will be an
400
390
        empty tree - one obtained by calling repository.revision_tree(None).
401
391
        """
402
 
        revision_id = self.last_revision()
403
 
        if revision_id is not None:
 
392
        try:
 
393
            revision_id = self.get_parent_ids()[0]
 
394
        except IndexError:
 
395
            # no parents, return an empty revision tree.
 
396
            # in the future this should return the tree for
 
397
            # 'empty:' - the implicit root empty tree.
 
398
            return self.branch.repository.revision_tree(None)
 
399
        else:
404
400
            try:
405
401
                xml = self.read_basis_inventory()
406
 
                inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
407
 
                inv.root.revision = revision_id
408
 
            except NoSuchFile:
409
 
                inv = None
410
 
            if inv is not None and inv.revision_id == revision_id:
411
 
                return bzrlib.tree.RevisionTree(self.branch.repository, inv,
412
 
                                                revision_id)
413
 
        # FIXME? RBC 20060403 should we cache the inventory here ?
 
402
                inv = bzrlib.xml6.serializer_v6.read_inventory_from_string(xml)
 
403
                if inv is not None and inv.revision_id == revision_id:
 
404
                    return bzrlib.tree.RevisionTree(self.branch.repository, 
 
405
                                                    inv, revision_id)
 
406
            except (NoSuchFile, errors.BadInventoryFormat):
 
407
                pass
 
408
        # No cached copy available, retrieve from the repository.
 
409
        # FIXME? RBC 20060403 should we cache the inventory locally
 
410
        # at this point ?
414
411
        try:
415
412
            return self.branch.repository.revision_tree(revision_id)
416
413
        except errors.RevisionNotPresent:
486
483
        This implementation reads the pending merges list and last_revision
487
484
        value and uses that to decide what the parents list should be.
488
485
        """
489
 
        last_rev = self.last_revision()
 
486
        last_rev = self._last_revision()
490
487
        if last_rev is None:
491
488
            parents = []
492
489
        else:
543
540
            transform_tree(tree, self)
544
541
            tree.set_parent_ids([revision_id])
545
542
 
546
 
    @needs_write_lock
547
 
    def commit(self, message=None, revprops=None, *args, **kwargs):
548
 
        # avoid circular imports
549
 
        from bzrlib.commit import Commit
550
 
        if revprops is None:
551
 
            revprops = {}
552
 
        if not 'branch-nick' in revprops:
553
 
            revprops['branch-nick'] = self.branch.nick
554
 
        # args for wt.commit start at message from the Commit.commit method,
555
 
        # but with branch a kwarg now, passing in args as is results in the
556
 
        #message being used for the branch
557
 
        args = (DEPRECATED_PARAMETER, message, ) + args
558
 
        committed_id = Commit().commit( working_tree=self, revprops=revprops,
559
 
            *args, **kwargs)
560
 
        self._set_inventory(self.read_working_inventory())
561
 
        return committed_id
562
 
 
563
543
    def id2abspath(self, file_id):
564
544
        return self.abspath(self.id2path(file_id))
565
545
 
603
583
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
604
584
 
605
585
    @needs_write_lock
606
 
    def add(self, files, ids=None):
607
 
        """Make files versioned.
608
 
 
609
 
        Note that the command line normally calls smart_add instead,
610
 
        which can automatically recurse.
611
 
 
612
 
        This adds the files to the inventory, so that they will be
613
 
        recorded by the next commit.
614
 
 
615
 
        files
616
 
            List of paths to add, relative to the base of the tree.
617
 
 
618
 
        ids
619
 
            If set, use these instead of automatically generated ids.
620
 
            Must be the same length as the list of files, but may
621
 
            contain None for ids that are to be autogenerated.
622
 
 
623
 
        TODO: Perhaps have an option to add the ids even if the files do
624
 
              not (yet) exist.
625
 
 
626
 
        TODO: Perhaps callback with the ids and paths as they're added.
627
 
        """
 
586
    def _add(self, files, ids, kinds):
 
587
        """See MutableTree._add."""
628
588
        # TODO: Re-adding a file that is removed in the working copy
629
589
        # should probably put it back with the previous ID.
630
 
        if isinstance(files, basestring):
631
 
            assert(ids is None or isinstance(ids, basestring))
632
 
            files = [files]
633
 
            if ids is not None:
634
 
                ids = [ids]
635
 
 
636
 
        if ids is None:
637
 
            ids = [None] * len(files)
638
 
        else:
639
 
            assert(len(ids) == len(files))
640
 
 
 
590
        # the read and write working inventory should not occur in this 
 
591
        # function - they should be part of lock_write and unlock.
641
592
        inv = self.read_working_inventory()
642
 
        for f,file_id in zip(files, ids):
643
 
            if self.is_control_filename(f):
644
 
                raise errors.ForbiddenControlFileError(filename=f)
645
 
 
646
 
            fp = splitpath(f)
647
 
 
648
 
            if len(fp) == 0:
649
 
                raise BzrError("cannot add top-level %r" % f)
650
 
 
651
 
            fullpath = normpath(self.abspath(f))
652
 
            try:
653
 
                kind = file_kind(fullpath)
654
 
            except OSError, e:
655
 
                if e.errno == errno.ENOENT:
656
 
                    raise NoSuchFile(fullpath)
657
 
            if not InventoryEntry.versionable_kind(kind):
658
 
                raise errors.BadFileKindError(filename=f, kind=kind)
 
593
        for f, file_id, kind in zip(files, ids, kinds):
 
594
            assert kind is not None
659
595
            if file_id is None:
660
596
                inv.add_path(f, kind=kind)
661
597
            else:
662
598
                inv.add_path(f, kind=kind, file_id=file_id)
663
 
 
664
599
        self._write_inventory(inv)
665
600
 
 
601
    @needs_tree_write_lock
 
602
    def _gather_kinds(self, files, kinds):
 
603
        """See MutableTree._gather_kinds."""
 
604
        for pos, f in enumerate(files):
 
605
            if kinds[pos] is None:
 
606
                fullpath = normpath(self.abspath(f))
 
607
                try:
 
608
                    kinds[pos] = file_kind(fullpath)
 
609
                except OSError, e:
 
610
                    if e.errno == errno.ENOENT:
 
611
                        raise NoSuchFile(fullpath)
 
612
 
666
613
    @needs_write_lock
667
614
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
668
615
        """Add revision_id as a parent.
679
626
        self.set_parent_ids(parents,
680
627
            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
681
628
 
682
 
    @needs_write_lock
 
629
    @needs_tree_write_lock
683
630
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
684
631
        """Add revision_id, tree tuple as a parent.
685
632
 
701
648
        self.set_parent_ids(parent_ids,
702
649
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
703
650
 
704
 
    @needs_write_lock
 
651
    @needs_tree_write_lock
705
652
    def add_pending_merge(self, *revision_ids):
706
653
        # TODO: Perhaps should check at this point that the
707
654
        # history of the revision is actually present?
715
662
        if updated:
716
663
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
717
664
 
 
665
    @deprecated_method(zero_eleven)
718
666
    @needs_read_lock
719
667
    def pending_merges(self):
720
668
        """Return a list of pending merges.
721
669
 
722
670
        These are revisions that have been merged into the working
723
671
        directory but not yet committed.
 
672
 
 
673
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
 
674
        instead - which is available on all tree objects.
724
675
        """
725
676
        return self.get_parent_ids()[1:]
726
677
 
727
 
    @needs_write_lock
 
678
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
 
679
        """Common ghost checking functionality from set_parent_*.
 
680
 
 
681
        This checks that the left hand-parent exists if there are any
 
682
        revisions present.
 
683
        """
 
684
        if len(revision_ids) > 0:
 
685
            leftmost_id = revision_ids[0]
 
686
            if (not allow_leftmost_as_ghost and not
 
687
                self.branch.repository.has_revision(leftmost_id)):
 
688
                raise errors.GhostRevisionUnusableHere(leftmost_id)
 
689
 
 
690
    def _set_merges_from_parent_ids(self, parent_ids):
 
691
        merges = parent_ids[1:]
 
692
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
 
693
 
 
694
    @needs_tree_write_lock
728
695
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
729
696
        """Set the parent ids to revision_ids.
730
697
        
737
704
        :param revision_ids: The revision_ids to set as the parent ids of this
738
705
            working tree. Any of these may be ghosts.
739
706
        """
 
707
        self._check_parents_for_ghosts(revision_ids,
 
708
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
709
 
740
710
        if len(revision_ids) > 0:
741
 
            leftmost_id = revision_ids[0]
742
 
            if (not allow_leftmost_as_ghost and not
743
 
                self.branch.repository.has_revision(leftmost_id)):
744
 
                raise errors.GhostRevisionUnusableHere(leftmost_id)
745
 
            self.set_last_revision(leftmost_id)
 
711
            self.set_last_revision(revision_ids[0])
746
712
        else:
747
713
            self.set_last_revision(None)
748
 
        merges = revision_ids[1:]
749
 
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
750
 
 
751
 
    @needs_write_lock
 
714
 
 
715
        self._set_merges_from_parent_ids(revision_ids)
 
716
 
 
717
    @needs_tree_write_lock
752
718
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
753
 
        """Set the parents of the working tree.
 
719
        """See MutableTree.set_parent_trees."""
 
720
        parent_ids = [rev for (rev, tree) in parents_list]
754
721
 
755
 
        :param parents_list: A list of (revision_id, tree) tuples. 
756
 
            If tree is None, then that element is treated as an unreachable
757
 
            parent tree - i.e. a ghost.
758
 
        """
759
 
        # parent trees are not used in current format trees, delegate to
760
 
        # set_parent_ids
761
 
        self.set_parent_ids([rev for (rev, tree) in parents_list],
 
722
        self._check_parents_for_ghosts(parent_ids,
762
723
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
763
724
 
764
 
    @needs_write_lock
 
725
        if len(parent_ids) == 0:
 
726
            leftmost_parent_id = None
 
727
            leftmost_parent_tree = None
 
728
        else:
 
729
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
 
730
 
 
731
        if self._change_last_revision(leftmost_parent_id):
 
732
            if leftmost_parent_tree is None:
 
733
                # If we don't have a tree, fall back to reading the
 
734
                # parent tree from the repository.
 
735
                self._cache_basis_inventory(leftmost_parent_id)
 
736
            else:
 
737
                inv = leftmost_parent_tree.inventory
 
738
                xml = self._create_basis_xml_from_inventory(
 
739
                                        leftmost_parent_id, inv)
 
740
                self._write_basis_inventory(xml)
 
741
        self._set_merges_from_parent_ids(parent_ids)
 
742
 
 
743
    @needs_tree_write_lock
765
744
    def set_pending_merges(self, rev_list):
766
745
        parents = self.get_parent_ids()
767
746
        leftmost = parents[:1]
768
747
        new_parents = leftmost + rev_list
769
748
        self.set_parent_ids(new_parents)
770
749
 
771
 
    @needs_write_lock
 
750
    @needs_tree_write_lock
772
751
    def set_merge_modified(self, modified_hashes):
773
752
        def iter_stanzas():
774
753
            for file_id, hash in modified_hashes.iteritems():
775
754
                yield Stanza(file_id=file_id, hash=hash)
776
755
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
777
756
 
778
 
    @needs_write_lock
 
757
    @needs_tree_write_lock
779
758
    def _put_rio(self, filename, stanzas, header):
780
759
        my_file = rio_file(stanzas, header)
781
760
        self._control_files.put(filename, my_file)
782
761
 
783
 
    @needs_write_lock
 
762
    @needs_write_lock # because merge pulls data into the branch.
784
763
    def merge_from_branch(self, branch, to_revision=None):
785
764
        """Merge from a branch into this working tree.
786
765
 
844
823
                merge_hashes[file_id] = hash
845
824
        return merge_hashes
846
825
 
 
826
    @needs_write_lock
 
827
    def mkdir(self, path, file_id=None):
 
828
        """See MutableTree.mkdir()."""
 
829
        if file_id is None:
 
830
            file_id = gen_file_id(os.path.basename(path))
 
831
        os.mkdir(self.abspath(path))
 
832
        self.add(path, file_id, 'directory')
 
833
        return file_id
 
834
 
847
835
    def get_symlink_target(self, file_id):
848
836
        return os.readlink(self.id2abspath(file_id))
849
837
 
855
843
        else:
856
844
            return '?'
857
845
 
858
 
    def list_files(self):
 
846
    def list_files(self, include_root=False):
859
847
        """Recursively list all files as (path, class, kind, id, entry).
860
848
 
861
849
        Lists, but does not descend into unversioned directories.
866
854
        Skips the control directory.
867
855
        """
868
856
        inv = self._inventory
 
857
        if include_root is True:
 
858
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
869
859
        # Convert these into local objects to save lookup times
870
860
        pathjoin = osutils.pathjoin
871
861
        file_kind = osutils.file_kind
964
954
                # if we finished all children, pop it off the stack
965
955
                stack.pop()
966
956
 
967
 
 
968
 
    @needs_write_lock
 
957
    @needs_tree_write_lock
969
958
    def move(self, from_paths, to_name):
970
959
        """Rename files.
971
960
 
990
979
        if not self.has_filename(to_name):
991
980
            raise BzrError("destination %r not in working directory" % to_abs)
992
981
        to_dir_id = inv.path2id(to_name)
993
 
        if to_dir_id == None and to_name != '':
 
982
        if to_dir_id is None and to_name != '':
994
983
            raise BzrError("destination %r is not a versioned directory" % to_name)
995
984
        to_dir_ie = inv[to_dir_id]
996
985
        if to_dir_ie.kind != 'directory':
1002
991
            if not self.has_filename(f):
1003
992
                raise BzrError("%r does not exist in working tree" % f)
1004
993
            f_id = inv.path2id(f)
1005
 
            if f_id == None:
 
994
            if f_id is None:
1006
995
                raise BzrError("%r is not versioned" % f)
1007
996
            name_tail = splitpath(f)[-1]
1008
997
            dest_path = pathjoin(to_name, name_tail)
1034
1023
        self._write_inventory(inv)
1035
1024
        return result
1036
1025
 
1037
 
    @needs_write_lock
 
1026
    @needs_tree_write_lock
1038
1027
    def rename_one(self, from_rel, to_rel):
1039
1028
        """Rename one file.
1040
1029
 
1047
1036
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
1048
1037
 
1049
1038
        file_id = inv.path2id(from_rel)
1050
 
        if file_id == None:
 
1039
        if file_id is None:
1051
1040
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1052
1041
 
1053
1042
        entry = inv[file_id]
1059
1048
 
1060
1049
        to_dir, to_tail = os.path.split(to_rel)
1061
1050
        to_dir_id = inv.path2id(to_dir)
1062
 
        if to_dir_id == None and to_dir != '':
 
1051
        if to_dir_id is None and to_dir != '':
1063
1052
            raise BzrError("can't determine destination directory id for %r" % to_dir)
1064
1053
 
1065
1054
        mutter("rename_one:")
1092
1081
        for subp in self.extras():
1093
1082
            if not self.is_ignored(subp):
1094
1083
                yield subp
1095
 
 
 
1084
    
 
1085
    @needs_tree_write_lock
 
1086
    def unversion(self, file_ids):
 
1087
        """Remove the file ids in file_ids from the current versioned set.
 
1088
 
 
1089
        When a file_id is unversioned, all of its children are automatically
 
1090
        unversioned.
 
1091
 
 
1092
        :param file_ids: The file ids to stop versioning.
 
1093
        :raises: NoSuchId if any fileid is not currently versioned.
 
1094
        """
 
1095
        for file_id in file_ids:
 
1096
            if self._inventory.has_id(file_id):
 
1097
                self._inventory.remove_recursive_id(file_id)
 
1098
            else:
 
1099
                raise errors.NoSuchId(self, file_id)
 
1100
        if len(file_ids):
 
1101
            # in the future this should just set a dirty bit to wait for the 
 
1102
            # final unlock. However, until all methods of workingtree start
 
1103
            # with the current in -memory inventory rather than triggering 
 
1104
            # a read, it is more complex - we need to teach read_inventory
 
1105
            # to know when to read, and when to not read first... and possibly
 
1106
            # to save first when the in memory one may be corrupted.
 
1107
            # so for now, we just only write it if it is indeed dirty.
 
1108
            # - RBC 20060907
 
1109
            self._write_inventory(self._inventory)
 
1110
    
1096
1111
    @deprecated_method(zero_eight)
1097
1112
    def iter_conflicts(self):
1098
1113
        """List all files in the tree that have text or content conflicts.
1155
1170
            source.unlock()
1156
1171
            top_pb.finished()
1157
1172
 
 
1173
    @needs_write_lock
 
1174
    def put_file_bytes_non_atomic(self, file_id, bytes):
 
1175
        """See MutableTree.put_file_bytes_non_atomic."""
 
1176
        stream = file(self.id2abspath(file_id), 'wb')
 
1177
        try:
 
1178
            stream.write(bytes)
 
1179
        finally:
 
1180
            stream.close()
 
1181
        # TODO: update the hashcache here ?
 
1182
 
1158
1183
    def extras(self):
1159
1184
        """Yield all unknown files in this WorkingTree.
1160
1185
 
1248
1273
        """Yield list of PATH, IGNORE_PATTERN"""
1249
1274
        for subp in self.extras():
1250
1275
            pat = self.is_ignored(subp)
1251
 
            if pat != None:
 
1276
            if pat is not None:
1252
1277
                yield subp, pat
1253
1278
 
1254
1279
    def get_ignore_list(self):
1320
1345
    def kind(self, file_id):
1321
1346
        return file_kind(self.id2abspath(file_id))
1322
1347
 
1323
 
    @needs_read_lock
1324
1348
    def last_revision(self):
1325
 
        """Return the last revision id of this working tree.
1326
 
 
1327
 
        In early branch formats this was == the branch last_revision,
1328
 
        but that cannot be relied upon - for working tree operations,
1329
 
        always use tree.last_revision().
 
1349
        """Return the last revision of the branch for this tree.
 
1350
 
 
1351
        This format tree does not support a separate marker for last-revision
 
1352
        compared to the branch.
 
1353
 
 
1354
        See MutableTree.last_revision
1330
1355
        """
 
1356
        return self._last_revision()
 
1357
 
 
1358
    @needs_read_lock
 
1359
    def _last_revision(self):
 
1360
        """helper for get_parent_ids."""
1331
1361
        return self.branch.last_revision()
1332
1362
 
1333
1363
    def is_locked(self):
1342
1372
            self.branch.unlock()
1343
1373
            raise
1344
1374
 
 
1375
    def lock_tree_write(self):
 
1376
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1377
        self.branch.lock_read()
 
1378
        try:
 
1379
            return self._control_files.lock_write()
 
1380
        except:
 
1381
            self.branch.unlock()
 
1382
            raise
 
1383
 
1345
1384
    def lock_write(self):
1346
 
        """See Branch.lock_write, and WorkingTree.unlock."""
 
1385
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1347
1386
        self.branch.lock_write()
1348
1387
        try:
1349
1388
            return self._control_files.lock_write()
1355
1394
        return self._control_files.get_physical_lock_status()
1356
1395
 
1357
1396
    def _basis_inventory_name(self):
1358
 
        return 'basis-inventory'
 
1397
        return 'basis-inventory-cache'
1359
1398
 
1360
 
    @needs_write_lock
 
1399
    @needs_tree_write_lock
1361
1400
    def set_last_revision(self, new_revision):
1362
1401
        """Change the last revision in the working tree."""
1363
1402
        if self._change_last_revision(new_revision):
1379
1418
            self.branch.set_revision_history([new_revision])
1380
1419
        return True
1381
1420
 
 
1421
    def _write_basis_inventory(self, xml):
 
1422
        """Write the basis inventory XML to the basis-inventory file"""
 
1423
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
 
1424
        path = self._basis_inventory_name()
 
1425
        sio = StringIO(xml)
 
1426
        self._control_files.put(path, sio)
 
1427
 
 
1428
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
 
1429
        """Create the text that will be saved in basis-inventory"""
 
1430
        inventory.revision_id = revision_id
 
1431
        return bzrlib.xml6.serializer_v6.write_inventory_to_string(inventory)
 
1432
 
1382
1433
    def _cache_basis_inventory(self, new_revision):
1383
1434
        """Cache new_revision as the basis inventory."""
1384
1435
        # TODO: this should allow the ready-to-use inventory to be passed in,
1396
1447
            # root node id can legitimately look like 'revision_id' but cannot
1397
1448
            # contain a '"'.
1398
1449
            xml = self.branch.repository.get_inventory_xml(new_revision)
1399
 
            if not 'revision_id="' in xml.split('\n', 1)[0]:
 
1450
            firstline = xml.split('\n', 1)[0]
 
1451
            if (not 'revision_id="' in firstline or 
 
1452
                'format="6"' not in firstline):
1400
1453
                inv = self.branch.repository.deserialise_inventory(
1401
1454
                    new_revision, xml)
1402
 
                inv.revision_id = new_revision
1403
 
                xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1404
 
            assert isinstance(xml, str), 'serialised xml must be bytestring.'
1405
 
            path = self._basis_inventory_name()
1406
 
            sio = StringIO(xml)
1407
 
            self._control_files.put(path, sio)
 
1455
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
 
1456
            self._write_basis_inventory(xml)
1408
1457
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1409
1458
            pass
1410
1459
 
1423
1472
        self._set_inventory(result)
1424
1473
        return result
1425
1474
 
1426
 
    @needs_write_lock
 
1475
    @needs_tree_write_lock
1427
1476
    def remove(self, files, verbose=False, to_file=None):
1428
1477
        """Remove nominated files from the working inventory..
1429
1478
 
1463
1512
 
1464
1513
        self._write_inventory(inv)
1465
1514
 
1466
 
    @needs_write_lock
 
1515
    @needs_tree_write_lock
1467
1516
    def revert(self, filenames, old_tree=None, backups=True, 
1468
1517
               pb=DummyProgress()):
1469
1518
        from transform import revert
1480
1529
 
1481
1530
    # XXX: This method should be deprecated in favour of taking in a proper
1482
1531
    # new Inventory object.
1483
 
    @needs_write_lock
 
1532
    @needs_tree_write_lock
1484
1533
    def set_inventory(self, new_inventory_list):
1485
1534
        from bzrlib.inventory import (Inventory,
1486
1535
                                      InventoryDirectory,
1503
1552
                raise BzrError("unknown kind %r" % kind)
1504
1553
        self._write_inventory(inv)
1505
1554
 
1506
 
    @needs_write_lock
 
1555
    @needs_tree_write_lock
1507
1556
    def set_root_id(self, file_id):
1508
1557
        """Set the root id for this tree."""
1509
1558
        inv = self.read_working_inventory()
1560
1609
        # local work is unreferenced and will appear to have been lost.
1561
1610
        # 
1562
1611
        result = 0
1563
 
        if self.last_revision() != self.branch.last_revision():
 
1612
        try:
 
1613
            last_rev = self.get_parent_ids()[0]
 
1614
        except IndexError:
 
1615
            last_rev = None
 
1616
        if last_rev != self.branch.last_revision():
1564
1617
            # merge tree state up to new branch tip.
1565
1618
            basis = self.basis_tree()
1566
1619
            to_tree = self.branch.basis_tree()
1584
1637
                parent_trees.append(
1585
1638
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
1586
1639
            self.set_parent_trees(parent_trees)
 
1640
            last_rev = parent_trees[0][0]
1587
1641
        else:
1588
1642
            # the working tree had the same last-revision as the master
1589
1643
            # branch did. We may still have pivot local work from the local
1590
1644
            # branch into old_tip:
1591
1645
            if old_tip is not None:
1592
1646
                self.add_parent_tree_id(old_tip)
1593
 
        if old_tip and old_tip != self.last_revision():
 
1647
        if old_tip and old_tip != last_rev:
1594
1648
            # our last revision was not the prior branch last revision
1595
1649
            # and we have converted that last revision to a pending merge.
1596
1650
            # base is somewhere between the branch tip now
1610
1664
                                  this_tree=self)
1611
1665
        return result
1612
1666
 
1613
 
    @needs_write_lock
 
1667
    @needs_tree_write_lock
1614
1668
    def _write_inventory(self, inv):
1615
1669
        """Write inventory as the current inventory."""
1616
1670
        sio = StringIO()
1660
1714
     - uses the branch last-revision.
1661
1715
    """
1662
1716
 
 
1717
    def lock_tree_write(self):
 
1718
        """See WorkingTree.lock_tree_write().
 
1719
 
 
1720
        In Format2 WorkingTrees we have a single lock for the branch and tree
 
1721
        so lock_tree_write() degrades to lock_write().
 
1722
        """
 
1723
        self.branch.lock_write()
 
1724
        try:
 
1725
            return self._control_files.lock_write()
 
1726
        except:
 
1727
            self.branch.unlock()
 
1728
            raise
 
1729
 
1663
1730
    def unlock(self):
1664
1731
        # we share control files:
1665
1732
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1682
1749
    """
1683
1750
 
1684
1751
    @needs_read_lock
1685
 
    def last_revision(self):
1686
 
        """See WorkingTree.last_revision."""
 
1752
    def _last_revision(self):
 
1753
        """See Mutable.last_revision."""
1687
1754
        try:
1688
1755
            return self._control_files.get_utf8('last-revision').read()
1689
1756
        except NoSuchFile:
1701
1768
            self._control_files.put_utf8('last-revision', revision_id)
1702
1769
            return True
1703
1770
 
1704
 
    @needs_write_lock
 
1771
    @needs_tree_write_lock
1705
1772
    def set_conflicts(self, conflicts):
1706
1773
        self._put_rio('conflicts', conflicts.to_stanzas(), 
1707
1774
                      CONFLICT_HEADER_1)
1708
1775
 
1709
 
    @needs_write_lock
 
1776
    @needs_tree_write_lock
1710
1777
    def add_conflicts(self, new_conflicts):
1711
1778
        conflict_set = set(self.conflicts())
1712
1779
        conflict_set.update(set(list(new_conflicts)))
1960
2027
                         _format=self,
1961
2028
                         _bzrdir=a_bzrdir,
1962
2029
                         _control_files=control_files)
1963
 
        wt.lock_write()
 
2030
        wt.lock_tree_write()
1964
2031
        try:
1965
2032
            wt._write_inventory(inv)
1966
2033
            wt.set_root_id(inv.root.file_id)