~bzr-pqm/bzr/bzr.dev

« 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:
98
98
        deprecated_function,
99
99
        DEPRECATED_PARAMETER,
100
100
        zero_eight,
 
101
        zero_eleven,
101
102
        )
102
103
from bzrlib.trace import mutter, note
103
104
from bzrlib.transform import build_tree
159
160
    return gen_file_id('TREE_ROOT')
160
161
 
161
162
 
 
163
def needs_tree_write_lock(unbound):
 
164
    """Decorate unbound to take out and release a tree_write lock."""
 
165
    def tree_write_locked(self, *args, **kwargs):
 
166
        self.lock_tree_write()
 
167
        try:
 
168
            return unbound(self, *args, **kwargs)
 
169
        finally:
 
170
            self.unlock()
 
171
    tree_write_locked.__doc__ = unbound.__doc__
 
172
    tree_write_locked.__name__ = unbound.__name__
 
173
    return tree_write_locked
 
174
 
 
175
 
162
176
class TreeEntry(object):
163
177
    """An entry that implements the minimum interface used by commands.
164
178
 
387
401
        If the left most parent is a ghost then the returned tree will be an
388
402
        empty tree - one obtained by calling repository.revision_tree(None).
389
403
        """
390
 
        revision_id = self.last_revision()
391
 
        if revision_id is not None:
 
404
        try:
 
405
            revision_id = self.get_parent_ids()[0]
 
406
        except IndexError:
 
407
            # no parents, return an empty revision tree.
 
408
            # in the future this should return the tree for
 
409
            # 'empty:' - the implicit root empty tree.
 
410
            return self.branch.repository.revision_tree(None)
 
411
        else:
392
412
            try:
393
413
                xml = self.read_basis_inventory()
394
 
                inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
395
 
                inv.root.revision = revision_id
396
 
            except NoSuchFile:
397
 
                inv = None
398
 
            if inv is not None and inv.revision_id == revision_id:
399
 
                return bzrlib.revisiontree.RevisionTree(self.branch.repository,
400
 
                    inv, revision_id)
401
 
        # FIXME? RBC 20060403 should we cache the inventory here ?
 
414
                inv = bzrlib.xml6.serializer_v6.read_inventory_from_string(xml)
 
415
                if inv is not None and inv.revision_id == revision_id:
 
416
                    return bzrlib.tree.RevisionTree(self.branch.repository, 
 
417
                                                    inv, revision_id)
 
418
            except (NoSuchFile, errors.BadInventoryFormat):
 
419
                pass
 
420
        # No cached copy available, retrieve from the repository.
 
421
        # FIXME? RBC 20060403 should we cache the inventory locally
 
422
        # at this point ?
402
423
        try:
403
424
            return self.branch.repository.revision_tree(revision_id)
404
425
        except errors.RevisionNotPresent:
474
495
        This implementation reads the pending merges list and last_revision
475
496
        value and uses that to decide what the parents list should be.
476
497
        """
477
 
        last_rev = self.last_revision()
 
498
        last_rev = self._last_revision()
478
499
        if last_rev is None:
479
500
            parents = []
480
501
        else:
589
610
                inv.add_path(f, kind=kind, file_id=file_id)
590
611
        self._write_inventory(inv)
591
612
 
 
613
    @needs_tree_write_lock
592
614
    def _gather_kinds(self, files, kinds):
593
615
        """See MutableTree._gather_kinds."""
594
616
        for pos, f in enumerate(files):
616
638
        self.set_parent_ids(parents,
617
639
            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
618
640
 
619
 
    @needs_write_lock
 
641
    @needs_tree_write_lock
620
642
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
621
643
        """Add revision_id, tree tuple as a parent.
622
644
 
638
660
        self.set_parent_ids(parent_ids,
639
661
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
640
662
 
641
 
    @needs_write_lock
 
663
    @needs_tree_write_lock
642
664
    def add_pending_merge(self, *revision_ids):
643
665
        # TODO: Perhaps should check at this point that the
644
666
        # history of the revision is actually present?
652
674
        if updated:
653
675
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
654
676
 
 
677
    @deprecated_method(zero_eleven)
655
678
    @needs_read_lock
656
679
    def pending_merges(self):
657
680
        """Return a list of pending merges.
658
681
 
659
682
        These are revisions that have been merged into the working
660
683
        directory but not yet committed.
 
684
 
 
685
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
 
686
        instead - which is available on all tree objects.
661
687
        """
662
688
        return self.get_parent_ids()[1:]
663
689
 
664
 
    @needs_write_lock
 
690
    @needs_tree_write_lock
665
691
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
666
692
        """Set the parent ids to revision_ids.
667
693
        
685
711
        merges = revision_ids[1:]
686
712
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
687
713
 
688
 
    @needs_write_lock
 
714
    @needs_tree_write_lock
689
715
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
690
716
        """See MutableTree.set_parent_trees."""
691
717
        # parent trees are not used in current format trees, delegate to
693
719
        self.set_parent_ids([rev for (rev, tree) in parents_list],
694
720
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
695
721
 
696
 
    @needs_write_lock
 
722
    @needs_tree_write_lock
697
723
    def set_pending_merges(self, rev_list):
698
724
        parents = self.get_parent_ids()
699
725
        leftmost = parents[:1]
700
726
        new_parents = leftmost + rev_list
701
727
        self.set_parent_ids(new_parents)
702
728
 
703
 
    @needs_write_lock
 
729
    @needs_tree_write_lock
704
730
    def set_merge_modified(self, modified_hashes):
705
731
        def iter_stanzas():
706
732
            for file_id, hash in modified_hashes.iteritems():
707
733
                yield Stanza(file_id=file_id, hash=hash)
708
734
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
709
735
 
710
 
    @needs_write_lock
 
736
    @needs_tree_write_lock
711
737
    def _put_rio(self, filename, stanzas, header):
712
738
        my_file = rio_file(stanzas, header)
713
739
        self._control_files.put(filename, my_file)
714
740
 
715
 
    @needs_write_lock
 
741
    @needs_write_lock # because merge pulls data into the branch.
716
742
    def merge_from_branch(self, branch, to_revision=None):
717
743
        """Merge from a branch into this working tree.
718
744
 
905
931
                # if we finished all children, pop it off the stack
906
932
                stack.pop()
907
933
 
908
 
 
909
 
    @needs_write_lock
 
934
    @needs_tree_write_lock
910
935
    def move(self, from_paths, to_name):
911
936
        """Rename files.
912
937
 
931
956
        if not self.has_filename(to_name):
932
957
            raise BzrError("destination %r not in working directory" % to_abs)
933
958
        to_dir_id = inv.path2id(to_name)
934
 
        if to_dir_id == None and to_name != '':
 
959
        if to_dir_id is None and to_name != '':
935
960
            raise BzrError("destination %r is not a versioned directory" % to_name)
936
961
        to_dir_ie = inv[to_dir_id]
937
962
        if to_dir_ie.kind != 'directory':
943
968
            if not self.has_filename(f):
944
969
                raise BzrError("%r does not exist in working tree" % f)
945
970
            f_id = inv.path2id(f)
946
 
            if f_id == None:
 
971
            if f_id is None:
947
972
                raise BzrError("%r is not versioned" % f)
948
973
            name_tail = splitpath(f)[-1]
949
974
            dest_path = pathjoin(to_name, name_tail)
975
1000
        self._write_inventory(inv)
976
1001
        return result
977
1002
 
978
 
    @needs_write_lock
 
1003
    @needs_tree_write_lock
979
1004
    def rename_one(self, from_rel, to_rel):
980
1005
        """Rename one file.
981
1006
 
988
1013
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
989
1014
 
990
1015
        file_id = inv.path2id(from_rel)
991
 
        if file_id == None:
 
1016
        if file_id is None:
992
1017
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
993
1018
 
994
1019
        entry = inv[file_id]
1000
1025
 
1001
1026
        to_dir, to_tail = os.path.split(to_rel)
1002
1027
        to_dir_id = inv.path2id(to_dir)
1003
 
        if to_dir_id == None and to_dir != '':
 
1028
        if to_dir_id is None and to_dir != '':
1004
1029
            raise BzrError("can't determine destination directory id for %r" % to_dir)
1005
1030
 
1006
1031
        mutter("rename_one:")
1034
1059
            if not self.is_ignored(subp):
1035
1060
                yield subp
1036
1061
    
1037
 
    @needs_write_lock
 
1062
    @needs_tree_write_lock
1038
1063
    def unversion(self, file_ids):
1039
1064
        """Remove the file ids in file_ids from the current versioned set.
1040
1065
 
1225
1250
        """Yield list of PATH, IGNORE_PATTERN"""
1226
1251
        for subp in self.extras():
1227
1252
            pat = self.is_ignored(subp)
1228
 
            if pat != None:
 
1253
            if pat is not None:
1229
1254
                yield subp, pat
1230
1255
 
1231
1256
    def get_ignore_list(self):
1297
1322
    def kind(self, file_id):
1298
1323
        return file_kind(self.id2abspath(file_id))
1299
1324
 
1300
 
    @needs_read_lock
1301
1325
    def last_revision(self):
1302
1326
        """Return the last revision of the branch for this tree.
1303
1327
 
1306
1330
 
1307
1331
        See MutableTree.last_revision
1308
1332
        """
 
1333
        return self._last_revision()
 
1334
 
 
1335
    @needs_read_lock
 
1336
    def _last_revision(self):
 
1337
        """helper for get_parent_ids."""
1309
1338
        return self.branch.last_revision()
1310
1339
 
1311
1340
    def is_locked(self):
1320
1349
            self.branch.unlock()
1321
1350
            raise
1322
1351
 
 
1352
    def lock_tree_write(self):
 
1353
        """Lock the working tree for write, and the branch for read.
 
1354
 
 
1355
        This is useful for operations which only need to mutate the working
 
1356
        tree. Taking out branch write locks is a relatively expensive process
 
1357
        and may fail if the branch is on read only media. So branch write locks
 
1358
        should only be taken out when we are modifying branch data - such as in
 
1359
        operations like commit, pull, uncommit and update.
 
1360
        """
 
1361
        self.branch.lock_read()
 
1362
        try:
 
1363
            return self._control_files.lock_write()
 
1364
        except:
 
1365
            self.branch.unlock()
 
1366
            raise
 
1367
 
1323
1368
    def lock_write(self):
1324
1369
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1325
1370
        self.branch.lock_write()
1333
1378
        return self._control_files.get_physical_lock_status()
1334
1379
 
1335
1380
    def _basis_inventory_name(self):
1336
 
        return 'basis-inventory'
 
1381
        return 'basis-inventory-cache'
1337
1382
 
1338
 
    @needs_write_lock
 
1383
    @needs_tree_write_lock
1339
1384
    def set_last_revision(self, new_revision):
1340
1385
        """Change the last revision in the working tree."""
1341
1386
        if self._change_last_revision(new_revision):
1374
1419
            # root node id can legitimately look like 'revision_id' but cannot
1375
1420
            # contain a '"'.
1376
1421
            xml = self.branch.repository.get_inventory_xml(new_revision)
1377
 
            if not 'revision_id="' in xml.split('\n', 1)[0]:
 
1422
            firstline = xml.split('\n', 1)[0]
 
1423
            if (not 'revision_id="' in firstline or 
 
1424
                'format="6"' not in firstline):
1378
1425
                inv = self.branch.repository.deserialise_inventory(
1379
1426
                    new_revision, xml)
1380
1427
                inv.revision_id = new_revision
1381
 
                xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1428
                xml = bzrlib.xml6.serializer_v6.write_inventory_to_string(inv)
1382
1429
            assert isinstance(xml, str), 'serialised xml must be bytestring.'
1383
1430
            path = self._basis_inventory_name()
1384
1431
            sio = StringIO(xml)
1401
1448
        self._set_inventory(result)
1402
1449
        return result
1403
1450
 
1404
 
    @needs_write_lock
 
1451
    @needs_tree_write_lock
1405
1452
    def remove(self, files, verbose=False, to_file=None):
1406
1453
        """Remove nominated files from the working inventory..
1407
1454
 
1441
1488
 
1442
1489
        self._write_inventory(inv)
1443
1490
 
1444
 
    @needs_write_lock
 
1491
    @needs_tree_write_lock
1445
1492
    def revert(self, filenames, old_tree=None, backups=True, 
1446
1493
               pb=DummyProgress()):
1447
1494
        from transform import revert
1458
1505
 
1459
1506
    # XXX: This method should be deprecated in favour of taking in a proper
1460
1507
    # new Inventory object.
1461
 
    @needs_write_lock
 
1508
    @needs_tree_write_lock
1462
1509
    def set_inventory(self, new_inventory_list):
1463
1510
        from bzrlib.inventory import (Inventory,
1464
1511
                                      InventoryDirectory,
1481
1528
                raise BzrError("unknown kind %r" % kind)
1482
1529
        self._write_inventory(inv)
1483
1530
 
1484
 
    @needs_write_lock
 
1531
    @needs_tree_write_lock
1485
1532
    def set_root_id(self, file_id):
1486
1533
        """Set the root id for this tree."""
1487
1534
        inv = self.read_working_inventory()
1538
1585
        # local work is unreferenced and will appear to have been lost.
1539
1586
        # 
1540
1587
        result = 0
1541
 
        if self.last_revision() != self.branch.last_revision():
 
1588
        try:
 
1589
            last_rev = self.get_parent_ids()[0]
 
1590
        except IndexError:
 
1591
            last_rev = None
 
1592
        if last_rev != self.branch.last_revision():
1542
1593
            # merge tree state up to new branch tip.
1543
1594
            basis = self.basis_tree()
1544
1595
            to_tree = self.branch.basis_tree()
1562
1613
                parent_trees.append(
1563
1614
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
1564
1615
            self.set_parent_trees(parent_trees)
 
1616
            last_rev = parent_trees[0][0]
1565
1617
        else:
1566
1618
            # the working tree had the same last-revision as the master
1567
1619
            # branch did. We may still have pivot local work from the local
1568
1620
            # branch into old_tip:
1569
1621
            if old_tip is not None:
1570
1622
                self.add_parent_tree_id(old_tip)
1571
 
        if old_tip and old_tip != self.last_revision():
 
1623
        if old_tip and old_tip != last_rev:
1572
1624
            # our last revision was not the prior branch last revision
1573
1625
            # and we have converted that last revision to a pending merge.
1574
1626
            # base is somewhere between the branch tip now
1588
1640
                                  this_tree=self)
1589
1641
        return result
1590
1642
 
1591
 
    @needs_write_lock
 
1643
    @needs_tree_write_lock
1592
1644
    def _write_inventory(self, inv):
1593
1645
        """Write inventory as the current inventory."""
1594
1646
        sio = StringIO()
1638
1690
     - uses the branch last-revision.
1639
1691
    """
1640
1692
 
 
1693
    def lock_tree_write(self):
 
1694
        """See WorkingTree.lock_tree_write().
 
1695
 
 
1696
        In Format2 WorkingTrees we have a single lock for the branch and tree
 
1697
        so lock_tree_write() degrades to lock_write().
 
1698
        """
 
1699
        self.branch.lock_write()
 
1700
        try:
 
1701
            return self._control_files.lock_write()
 
1702
        except:
 
1703
            self.branch.unlock()
 
1704
            raise
 
1705
 
1641
1706
    def unlock(self):
1642
1707
        # we share control files:
1643
1708
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1660
1725
    """
1661
1726
 
1662
1727
    @needs_read_lock
1663
 
    def last_revision(self):
 
1728
    def _last_revision(self):
1664
1729
        """See Mutable.last_revision."""
1665
1730
        try:
1666
1731
            return self._control_files.get_utf8('last-revision').read()
1679
1744
            self._control_files.put_utf8('last-revision', revision_id)
1680
1745
            return True
1681
1746
 
1682
 
    @needs_write_lock
 
1747
    @needs_tree_write_lock
1683
1748
    def set_conflicts(self, conflicts):
1684
1749
        self._put_rio('conflicts', conflicts.to_stanzas(), 
1685
1750
                      CONFLICT_HEADER_1)
1686
1751
 
1687
 
    @needs_write_lock
 
1752
    @needs_tree_write_lock
1688
1753
    def add_conflicts(self, new_conflicts):
1689
1754
        conflict_set = set(self.conflicts())
1690
1755
        conflict_set.update(set(list(new_conflicts)))
1938
2003
                         _format=self,
1939
2004
                         _bzrdir=a_bzrdir,
1940
2005
                         _control_files=control_files)
1941
 
        wt.lock_write()
 
2006
        wt.lock_tree_write()
1942
2007
        try:
1943
2008
            wt._write_inventory(inv)
1944
2009
            wt.set_root_id(inv.root.file_id)