/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,
103
106
from bzrlib.transport import get_transport
104
107
from bzrlib.transport.local import LocalTransport
105
108
from bzrlib.textui import show_status
106
 
import bzrlib.tree
107
109
import bzrlib.ui
108
110
import bzrlib.xml5
109
111
 
214
216
        return ''
215
217
 
216
218
 
217
 
class WorkingTree(bzrlib.tree.Tree):
 
219
class WorkingTree(bzrlib.mutabletree.MutableTree):
218
220
    """Working copy tree.
219
221
 
220
222
    The inventory is held in the `Branch` working-inventory, and the
325
327
    def _set_inventory(self, inv):
326
328
        assert inv.root is not None
327
329
        self._inventory = inv
328
 
        self.path2id = self._inventory.path2id
329
 
 
330
 
    def is_control_filename(self, filename):
331
 
        """True if filename is the name of a control file in this tree.
332
 
        
333
 
        :param filename: A filename within the tree. This is a relative path
334
 
        from the root of this tree.
335
 
 
336
 
        This is true IF and ONLY IF the filename is part of the meta data
337
 
        that bzr controls in this tree. I.E. a random .bzr directory placed
338
 
        on disk will not be a control file for this tree.
339
 
        """
340
 
        return self.bzrdir.is_control_filename(filename)
341
330
 
342
331
    @staticmethod
343
332
    def open(path=None, _unsupported=False):
545
534
            transform_tree(tree, self)
546
535
            tree.set_parent_ids([revision_id])
547
536
 
548
 
    @needs_write_lock
549
 
    def commit(self, message=None, revprops=None, *args, **kwargs):
550
 
        # avoid circular imports
551
 
        from bzrlib.commit import Commit
552
 
        if revprops is None:
553
 
            revprops = {}
554
 
        if not 'branch-nick' in revprops:
555
 
            revprops['branch-nick'] = self.branch.nick
556
 
        # args for wt.commit start at message from the Commit.commit method,
557
 
        # but with branch a kwarg now, passing in args as is results in the
558
 
        #message being used for the branch
559
 
        args = (DEPRECATED_PARAMETER, message, ) + args
560
 
        committed_id = Commit().commit( working_tree=self, revprops=revprops,
561
 
            *args, **kwargs)
562
 
        return committed_id
563
 
 
564
537
    def id2abspath(self, file_id):
565
538
        return self.abspath(self.id2path(file_id))
566
539
 
604
577
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
605
578
 
606
579
    @needs_write_lock
607
 
    def add(self, files, ids=None):
608
 
        """Make files versioned.
609
 
 
610
 
        Note that the command line normally calls smart_add instead,
611
 
        which can automatically recurse.
612
 
 
613
 
        This adds the files to the inventory, so that they will be
614
 
        recorded by the next commit.
615
 
 
616
 
        files
617
 
            List of paths to add, relative to the base of the tree.
618
 
 
619
 
        ids
620
 
            If set, use these instead of automatically generated ids.
621
 
            Must be the same length as the list of files, but may
622
 
            contain None for ids that are to be autogenerated.
623
 
 
624
 
        TODO: Perhaps have an option to add the ids even if the files do
625
 
              not (yet) exist.
626
 
 
627
 
        TODO: Perhaps callback with the ids and paths as they're added.
628
 
        """
 
580
    def _add(self, files, ids, kinds):
 
581
        """See MutableTree._add."""
629
582
        # TODO: Re-adding a file that is removed in the working copy
630
583
        # should probably put it back with the previous ID.
631
 
        if isinstance(files, basestring):
632
 
            assert(ids is None or isinstance(ids, basestring))
633
 
            files = [files]
634
 
            if ids is not None:
635
 
                ids = [ids]
636
 
 
637
 
        if ids is None:
638
 
            ids = [None] * len(files)
639
 
        else:
640
 
            assert(len(ids) == len(files))
641
 
 
 
584
        # the read and write working inventory should not occur in this 
 
585
        # function - they should be part of lock_write and unlock.
642
586
        inv = self.read_working_inventory()
643
 
        for f,file_id in zip(files, ids):
644
 
            if self.is_control_filename(f):
645
 
                raise errors.ForbiddenControlFileError(filename=f)
646
 
 
647
 
            fp = splitpath(f)
648
 
 
649
 
            if len(fp) == 0:
650
 
                raise BzrError("cannot add top-level %r" % f)
651
 
 
652
 
            fullpath = normpath(self.abspath(f))
653
 
            try:
654
 
                kind = file_kind(fullpath)
655
 
            except OSError, e:
656
 
                if e.errno == errno.ENOENT:
657
 
                    raise NoSuchFile(fullpath)
658
 
            if not InventoryEntry.versionable_kind(kind):
659
 
                raise errors.BadFileKindError(filename=f, kind=kind)
 
587
        for f, file_id, kind in zip(files, ids, kinds):
 
588
            assert kind is not None
660
589
            if file_id is None:
661
590
                inv.add_path(f, kind=kind)
662
591
            else:
663
592
                inv.add_path(f, kind=kind, file_id=file_id)
664
 
 
665
593
        self._write_inventory(inv)
666
594
 
 
595
    @needs_tree_write_lock
 
596
    def _gather_kinds(self, files, kinds):
 
597
        """See MutableTree._gather_kinds."""
 
598
        for pos, f in enumerate(files):
 
599
            if kinds[pos] is None:
 
600
                fullpath = normpath(self.abspath(f))
 
601
                try:
 
602
                    kinds[pos] = file_kind(fullpath)
 
603
                except OSError, e:
 
604
                    if e.errno == errno.ENOENT:
 
605
                        raise NoSuchFile(fullpath)
 
606
 
667
607
    @needs_write_lock
668
608
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
669
609
        """Add revision_id as a parent.
680
620
        self.set_parent_ids(parents,
681
621
            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
682
622
 
683
 
    @needs_write_lock
 
623
    @needs_tree_write_lock
684
624
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
685
625
        """Add revision_id, tree tuple as a parent.
686
626
 
702
642
        self.set_parent_ids(parent_ids,
703
643
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
704
644
 
705
 
    @needs_write_lock
 
645
    @needs_tree_write_lock
706
646
    def add_pending_merge(self, *revision_ids):
707
647
        # TODO: Perhaps should check at this point that the
708
648
        # history of the revision is actually present?
729
669
        """
730
670
        return self.get_parent_ids()[1:]
731
671
 
732
 
    @needs_write_lock
 
672
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
 
673
        """Common ghost checking functionality from set_parent_*.
 
674
 
 
675
        This checks that the left hand-parent exists if there are any
 
676
        revisions present.
 
677
        """
 
678
        if len(revision_ids) > 0:
 
679
            leftmost_id = revision_ids[0]
 
680
            if (not allow_leftmost_as_ghost and not
 
681
                self.branch.repository.has_revision(leftmost_id)):
 
682
                raise errors.GhostRevisionUnusableHere(leftmost_id)
 
683
 
 
684
    def _set_merges_from_parent_ids(self, parent_ids):
 
685
        merges = parent_ids[1:]
 
686
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
 
687
 
 
688
    @needs_tree_write_lock
733
689
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
734
690
        """Set the parent ids to revision_ids.
735
691
        
742
698
        :param revision_ids: The revision_ids to set as the parent ids of this
743
699
            working tree. Any of these may be ghosts.
744
700
        """
 
701
        self._check_parents_for_ghosts(revision_ids,
 
702
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
703
 
745
704
        if len(revision_ids) > 0:
746
 
            leftmost_id = revision_ids[0]
747
 
            if (not allow_leftmost_as_ghost and not
748
 
                self.branch.repository.has_revision(leftmost_id)):
749
 
                raise errors.GhostRevisionUnusableHere(leftmost_id)
750
 
            self.set_last_revision(leftmost_id)
 
705
            self.set_last_revision(revision_ids[0])
751
706
        else:
752
707
            self.set_last_revision(None)
753
 
        merges = revision_ids[1:]
754
 
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
755
 
 
756
 
    @needs_write_lock
 
708
 
 
709
        self._set_merges_from_parent_ids(revision_ids)
 
710
 
 
711
    @needs_tree_write_lock
757
712
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
758
 
        """Set the parents of the working tree.
 
713
        """See MutableTree.set_parent_trees."""
 
714
        parent_ids = [rev for (rev, tree) in parents_list]
759
715
 
760
 
        :param parents_list: A list of (revision_id, tree) tuples. 
761
 
            If tree is None, then that element is treated as an unreachable
762
 
            parent tree - i.e. a ghost.
763
 
        """
764
 
        # parent trees are not used in current format trees, delegate to
765
 
        # set_parent_ids
766
 
        self.set_parent_ids([rev for (rev, tree) in parents_list],
 
716
        self._check_parents_for_ghosts(parent_ids,
767
717
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
768
718
 
769
 
    @needs_write_lock
 
719
        if len(parent_ids) == 0:
 
720
            leftmost_parent_id = None
 
721
            leftmost_parent_tree = None
 
722
        else:
 
723
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
 
724
 
 
725
        if self._change_last_revision(leftmost_parent_id):
 
726
            if leftmost_parent_tree is None:
 
727
                # If we don't have a tree, fall back to reading the
 
728
                # parent tree from the repository.
 
729
                self._cache_basis_inventory(leftmost_parent_id)
 
730
            else:
 
731
                inv = leftmost_parent_tree.inventory
 
732
                xml = self._create_basis_xml_from_inventory(
 
733
                                        leftmost_parent_id, inv)
 
734
                self._write_basis_inventory(xml)
 
735
        self._set_merges_from_parent_ids(parent_ids)
 
736
 
 
737
    @needs_tree_write_lock
770
738
    def set_pending_merges(self, rev_list):
771
739
        parents = self.get_parent_ids()
772
740
        leftmost = parents[:1]
773
741
        new_parents = leftmost + rev_list
774
742
        self.set_parent_ids(new_parents)
775
743
 
776
 
    @needs_write_lock
 
744
    @needs_tree_write_lock
777
745
    def set_merge_modified(self, modified_hashes):
778
746
        def iter_stanzas():
779
747
            for file_id, hash in modified_hashes.iteritems():
780
748
                yield Stanza(file_id=file_id, hash=hash)
781
749
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
782
750
 
783
 
    @needs_write_lock
 
751
    @needs_tree_write_lock
784
752
    def _put_rio(self, filename, stanzas, header):
785
753
        my_file = rio_file(stanzas, header)
786
754
        self._control_files.put(filename, my_file)
787
755
 
788
 
    @needs_write_lock
 
756
    @needs_write_lock # because merge pulls data into the branch.
789
757
    def merge_from_branch(self, branch, to_revision=None):
790
758
        """Merge from a branch into this working tree.
791
759
 
849
817
                merge_hashes[file_id] = hash
850
818
        return merge_hashes
851
819
 
 
820
    @needs_write_lock
 
821
    def mkdir(self, path, file_id=None):
 
822
        """See MutableTree.mkdir()."""
 
823
        if file_id is None:
 
824
            file_id = gen_file_id(os.path.basename(path))
 
825
        os.mkdir(self.abspath(path))
 
826
        self.add(path, file_id, 'directory')
 
827
        return file_id
 
828
 
852
829
    def get_symlink_target(self, file_id):
853
830
        return os.readlink(self.id2abspath(file_id))
854
831
 
860
837
        else:
861
838
            return '?'
862
839
 
863
 
    def list_files(self):
 
840
    def list_files(self, include_root=False):
864
841
        """Recursively list all files as (path, class, kind, id, entry).
865
842
 
866
843
        Lists, but does not descend into unversioned directories.
871
848
        Skips the control directory.
872
849
        """
873
850
        inv = self._inventory
 
851
        if include_root is True:
 
852
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
874
853
        # Convert these into local objects to save lookup times
875
854
        pathjoin = osutils.pathjoin
876
855
        file_kind = osutils.file_kind
969
948
                # if we finished all children, pop it off the stack
970
949
                stack.pop()
971
950
 
972
 
 
973
 
    @needs_write_lock
 
951
    @needs_tree_write_lock
974
952
    def move(self, from_paths, to_name):
975
953
        """Rename files.
976
954
 
995
973
        if not self.has_filename(to_name):
996
974
            raise BzrError("destination %r not in working directory" % to_abs)
997
975
        to_dir_id = inv.path2id(to_name)
998
 
        if to_dir_id == None and to_name != '':
 
976
        if to_dir_id is None and to_name != '':
999
977
            raise BzrError("destination %r is not a versioned directory" % to_name)
1000
978
        to_dir_ie = inv[to_dir_id]
1001
979
        if to_dir_ie.kind != 'directory':
1007
985
            if not self.has_filename(f):
1008
986
                raise BzrError("%r does not exist in working tree" % f)
1009
987
            f_id = inv.path2id(f)
1010
 
            if f_id == None:
 
988
            if f_id is None:
1011
989
                raise BzrError("%r is not versioned" % f)
1012
990
            name_tail = splitpath(f)[-1]
1013
991
            dest_path = pathjoin(to_name, name_tail)
1039
1017
        self._write_inventory(inv)
1040
1018
        return result
1041
1019
 
1042
 
    @needs_write_lock
 
1020
    @needs_tree_write_lock
1043
1021
    def rename_one(self, from_rel, to_rel):
1044
1022
        """Rename one file.
1045
1023
 
1052
1030
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
1053
1031
 
1054
1032
        file_id = inv.path2id(from_rel)
1055
 
        if file_id == None:
 
1033
        if file_id is None:
1056
1034
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1057
1035
 
1058
1036
        entry = inv[file_id]
1064
1042
 
1065
1043
        to_dir, to_tail = os.path.split(to_rel)
1066
1044
        to_dir_id = inv.path2id(to_dir)
1067
 
        if to_dir_id == None and to_dir != '':
 
1045
        if to_dir_id is None and to_dir != '':
1068
1046
            raise BzrError("can't determine destination directory id for %r" % to_dir)
1069
1047
 
1070
1048
        mutter("rename_one:")
1098
1076
            if not self.is_ignored(subp):
1099
1077
                yield subp
1100
1078
    
1101
 
    @needs_write_lock
 
1079
    @needs_tree_write_lock
1102
1080
    def unversion(self, file_ids):
1103
1081
        """Remove the file ids in file_ids from the current versioned set.
1104
1082
 
1186
1164
            source.unlock()
1187
1165
            top_pb.finished()
1188
1166
 
 
1167
    @needs_write_lock
 
1168
    def put_file_bytes_non_atomic(self, file_id, bytes):
 
1169
        """See MutableTree.put_file_bytes_non_atomic."""
 
1170
        stream = file(self.id2abspath(file_id), 'wb')
 
1171
        try:
 
1172
            stream.write(bytes)
 
1173
        finally:
 
1174
            stream.close()
 
1175
        # TODO: update the hashcache here ?
 
1176
 
1189
1177
    def extras(self):
1190
1178
        """Yield all unknown files in this WorkingTree.
1191
1179
 
1279
1267
        """Yield list of PATH, IGNORE_PATTERN"""
1280
1268
        for subp in self.extras():
1281
1269
            pat = self.is_ignored(subp)
1282
 
            if pat != None:
 
1270
            if pat is not None:
1283
1271
                yield subp, pat
1284
1272
 
1285
1273
    def get_ignore_list(self):
1352
1340
        return file_kind(self.id2abspath(file_id))
1353
1341
 
1354
1342
    def last_revision(self):
1355
 
        """Return the last revision id of this working tree.
1356
 
 
1357
 
        In early branch formats this was the same as the branch last_revision,
1358
 
        but that cannot be relied upon - for working tree operations,
1359
 
        always use tree.last_revision(). This returns the left most parent id,
1360
 
        or None if there are no parents.
1361
 
 
1362
 
        This was deprecated as of 0.11. Please use get_parent_ids instead.
 
1343
        """Return the last revision of the branch for this tree.
 
1344
 
 
1345
        This format tree does not support a separate marker for last-revision
 
1346
        compared to the branch.
 
1347
 
 
1348
        See MutableTree.last_revision
1363
1349
        """
1364
1350
        return self._last_revision()
1365
1351
 
1380
1366
            self.branch.unlock()
1381
1367
            raise
1382
1368
 
 
1369
    def lock_tree_write(self):
 
1370
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1371
        self.branch.lock_read()
 
1372
        try:
 
1373
            return self._control_files.lock_write()
 
1374
        except:
 
1375
            self.branch.unlock()
 
1376
            raise
 
1377
 
1383
1378
    def lock_write(self):
1384
 
        """See Branch.lock_write, and WorkingTree.unlock."""
 
1379
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1385
1380
        self.branch.lock_write()
1386
1381
        try:
1387
1382
            return self._control_files.lock_write()
1393
1388
        return self._control_files.get_physical_lock_status()
1394
1389
 
1395
1390
    def _basis_inventory_name(self):
1396
 
        return 'basis-inventory'
 
1391
        return 'basis-inventory-cache'
1397
1392
 
1398
 
    @needs_write_lock
 
1393
    @needs_tree_write_lock
1399
1394
    def set_last_revision(self, new_revision):
1400
1395
        """Change the last revision in the working tree."""
1401
1396
        if self._change_last_revision(new_revision):
1417
1412
            self.branch.set_revision_history([new_revision])
1418
1413
        return True
1419
1414
 
 
1415
    def _write_basis_inventory(self, xml):
 
1416
        """Write the basis inventory XML to the basis-inventory file"""
 
1417
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
 
1418
        path = self._basis_inventory_name()
 
1419
        sio = StringIO(xml)
 
1420
        self._control_files.put(path, sio)
 
1421
 
 
1422
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
 
1423
        """Create the text that will be saved in basis-inventory"""
 
1424
        inventory.revision_id = revision_id
 
1425
        return bzrlib.xml6.serializer_v6.write_inventory_to_string(inventory)
 
1426
 
1420
1427
    def _cache_basis_inventory(self, new_revision):
1421
1428
        """Cache new_revision as the basis inventory."""
1422
1429
        # TODO: this should allow the ready-to-use inventory to be passed in,
1434
1441
            # root node id can legitimately look like 'revision_id' but cannot
1435
1442
            # contain a '"'.
1436
1443
            xml = self.branch.repository.get_inventory_xml(new_revision)
1437
 
            if not 'revision_id="' in xml.split('\n', 1)[0]:
 
1444
            firstline = xml.split('\n', 1)[0]
 
1445
            if (not 'revision_id="' in firstline or 
 
1446
                'format="6"' not in firstline):
1438
1447
                inv = self.branch.repository.deserialise_inventory(
1439
1448
                    new_revision, xml)
1440
 
                inv.revision_id = new_revision
1441
 
                xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1442
 
            assert isinstance(xml, str), 'serialised xml must be bytestring.'
1443
 
            path = self._basis_inventory_name()
1444
 
            sio = StringIO(xml)
1445
 
            self._control_files.put(path, sio)
 
1449
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
 
1450
            self._write_basis_inventory(xml)
1446
1451
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1447
1452
            pass
1448
1453
 
1461
1466
        self._set_inventory(result)
1462
1467
        return result
1463
1468
 
1464
 
    @needs_write_lock
 
1469
    @needs_tree_write_lock
1465
1470
    def remove(self, files, verbose=False, to_file=None):
1466
1471
        """Remove nominated files from the working inventory..
1467
1472
 
1501
1506
 
1502
1507
        self._write_inventory(inv)
1503
1508
 
1504
 
    @needs_write_lock
 
1509
    @needs_tree_write_lock
1505
1510
    def revert(self, filenames, old_tree=None, backups=True, 
1506
1511
               pb=DummyProgress()):
1507
1512
        from transform import revert
1528
1533
            except NoSuchFile:
1529
1534
                pass
1530
1535
            else:
1531
 
                inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
1532
 
                # Fixup old inventory serialization that has a missing root
1533
 
                # revision_id attribute.
1534
 
                inv.root.revision = revision_id
1535
 
                # dont use the repository revision_tree api because we want
1536
 
                # to supply the inventory.
1537
 
                if inv.revision_id == revision_id:
1538
 
                    return bzrlib.tree.RevisionTree(self.branch.repository,
1539
 
                        inv, revision_id)
 
1536
                try:
 
1537
                    inv = bzrlib.xml6.serializer_v6.read_inventory_from_string(xml)
 
1538
                    # dont use the repository revision_tree api because we want
 
1539
                    # to supply the inventory.
 
1540
                    if inv.revision_id == revision_id:
 
1541
                        return bzrlib.tree.RevisionTree(self.branch.repository,
 
1542
                            inv, revision_id)
 
1543
                except errors.BadInventoryFormat:
 
1544
                    pass
1540
1545
        # raise if there was no inventory, or if we read the wrong inventory.
1541
1546
        raise errors.NoSuchRevisionInTree(self, revision_id)
1542
1547
 
1543
1548
    # XXX: This method should be deprecated in favour of taking in a proper
1544
1549
    # new Inventory object.
1545
 
    @needs_write_lock
 
1550
    @needs_tree_write_lock
1546
1551
    def set_inventory(self, new_inventory_list):
1547
1552
        from bzrlib.inventory import (Inventory,
1548
1553
                                      InventoryDirectory,
1565
1570
                raise BzrError("unknown kind %r" % kind)
1566
1571
        self._write_inventory(inv)
1567
1572
 
1568
 
    @needs_write_lock
 
1573
    @needs_tree_write_lock
1569
1574
    def set_root_id(self, file_id):
1570
1575
        """Set the root id for this tree."""
1571
1576
        inv = self.read_working_inventory()
1677
1682
                                  this_tree=self)
1678
1683
        return result
1679
1684
 
1680
 
    @needs_write_lock
 
1685
    @needs_tree_write_lock
1681
1686
    def _write_inventory(self, inv):
1682
1687
        """Write inventory as the current inventory."""
1683
1688
        sio = StringIO()
1727
1732
     - uses the branch last-revision.
1728
1733
    """
1729
1734
 
 
1735
    def lock_tree_write(self):
 
1736
        """See WorkingTree.lock_tree_write().
 
1737
 
 
1738
        In Format2 WorkingTrees we have a single lock for the branch and tree
 
1739
        so lock_tree_write() degrades to lock_write().
 
1740
        """
 
1741
        self.branch.lock_write()
 
1742
        try:
 
1743
            return self._control_files.lock_write()
 
1744
        except:
 
1745
            self.branch.unlock()
 
1746
            raise
 
1747
 
1730
1748
    def unlock(self):
1731
1749
        # we share control files:
1732
1750
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1750
1768
 
1751
1769
    @needs_read_lock
1752
1770
    def _last_revision(self):
1753
 
        """See WorkingTree._last_revision."""
 
1771
        """See Mutable.last_revision."""
1754
1772
        try:
1755
1773
            return self._control_files.get_utf8('last-revision').read()
1756
1774
        except NoSuchFile:
1768
1786
            self._control_files.put_utf8('last-revision', revision_id)
1769
1787
            return True
1770
1788
 
1771
 
    @needs_write_lock
 
1789
    @needs_tree_write_lock
1772
1790
    def set_conflicts(self, conflicts):
1773
1791
        self._put_rio('conflicts', conflicts.to_stanzas(), 
1774
1792
                      CONFLICT_HEADER_1)
1775
1793
 
1776
 
    @needs_write_lock
 
1794
    @needs_tree_write_lock
1777
1795
    def add_conflicts(self, new_conflicts):
1778
1796
        conflict_set = set(self.conflicts())
1779
1797
        conflict_set.update(set(list(new_conflicts)))
2027
2045
                         _format=self,
2028
2046
                         _bzrdir=a_bzrdir,
2029
2047
                         _control_files=control_files)
2030
 
        wt.lock_write()
 
2048
        wt.lock_tree_write()
2031
2049
        try:
2032
2050
            wt._write_inventory(inv)
2033
2051
            wt.set_root_id(inv.root.file_id)