~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:
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):
552
541
            transform_tree(tree, self)
553
542
            tree.set_parent_ids([revision_id])
554
543
 
555
 
    @needs_write_lock
556
 
    def commit(self, message=None, revprops=None, *args, **kwargs):
557
 
        # avoid circular imports
558
 
        from bzrlib.commit import Commit
559
 
        if revprops is None:
560
 
            revprops = {}
561
 
        if not 'branch-nick' in revprops:
562
 
            revprops['branch-nick'] = self.branch.nick
563
 
        # args for wt.commit start at message from the Commit.commit method,
564
 
        # but with branch a kwarg now, passing in args as is results in the
565
 
        #message being used for the branch
566
 
        args = (DEPRECATED_PARAMETER, message, ) + args
567
 
        committed_id = Commit().commit( working_tree=self, revprops=revprops,
568
 
            *args, **kwargs)
569
 
        return committed_id
570
 
 
571
544
    def id2abspath(self, file_id):
572
545
        return self.abspath(self.id2path(file_id))
573
546
 
611
584
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
612
585
 
613
586
    @needs_write_lock
614
 
    def add(self, files, ids=None):
615
 
        """Make files versioned.
616
 
 
617
 
        Note that the command line normally calls smart_add instead,
618
 
        which can automatically recurse.
619
 
 
620
 
        This adds the files to the inventory, so that they will be
621
 
        recorded by the next commit.
622
 
 
623
 
        files
624
 
            List of paths to add, relative to the base of the tree.
625
 
 
626
 
        ids
627
 
            If set, use these instead of automatically generated ids.
628
 
            Must be the same length as the list of files, but may
629
 
            contain None for ids that are to be autogenerated.
630
 
 
631
 
        TODO: Perhaps have an option to add the ids even if the files do
632
 
              not (yet) exist.
633
 
 
634
 
        TODO: Perhaps callback with the ids and paths as they're added.
635
 
        """
 
587
    def _add(self, files, ids, kinds):
 
588
        """See MutableTree._add."""
636
589
        # TODO: Re-adding a file that is removed in the working copy
637
590
        # should probably put it back with the previous ID.
638
 
        if isinstance(files, basestring):
639
 
            assert(ids is None or isinstance(ids, basestring))
640
 
            files = [files]
641
 
            if ids is not None:
642
 
                ids = [ids]
643
 
 
644
 
        if ids is None:
645
 
            ids = [None] * len(files)
646
 
        else:
647
 
            assert(len(ids) == len(files))
648
 
 
 
591
        # the read and write working inventory should not occur in this 
 
592
        # function - they should be part of lock_write and unlock.
649
593
        inv = self.read_working_inventory()
650
 
        for f,file_id in zip(files, ids):
651
 
            if self.is_control_filename(f):
652
 
                raise errors.ForbiddenControlFileError(filename=f)
653
 
 
654
 
            fp = splitpath(f)
655
 
 
656
 
            if len(fp) == 0:
657
 
                raise BzrError("cannot add top-level %r" % f)
658
 
 
659
 
            fullpath = normpath(self.abspath(f))
660
 
            try:
661
 
                kind = file_kind(fullpath)
662
 
            except OSError, e:
663
 
                if e.errno == errno.ENOENT:
664
 
                    raise NoSuchFile(fullpath)
665
 
            if not InventoryEntry.versionable_kind(kind):
666
 
                raise errors.BadFileKindError(filename=f, kind=kind)
 
594
        for f, file_id, kind in zip(files, ids, kinds):
 
595
            assert kind is not None
667
596
            if file_id is None:
668
597
                inv.add_path(f, kind=kind)
669
598
            else:
670
599
                inv.add_path(f, kind=kind, file_id=file_id)
671
 
 
672
600
        self._write_inventory(inv)
673
601
 
 
602
    @needs_tree_write_lock
 
603
    def _gather_kinds(self, files, kinds):
 
604
        """See MutableTree._gather_kinds."""
 
605
        for pos, f in enumerate(files):
 
606
            if kinds[pos] is None:
 
607
                fullpath = normpath(self.abspath(f))
 
608
                try:
 
609
                    kinds[pos] = file_kind(fullpath)
 
610
                except OSError, e:
 
611
                    if e.errno == errno.ENOENT:
 
612
                        raise NoSuchFile(fullpath)
 
613
 
674
614
    @needs_write_lock
675
615
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
676
616
        """Add revision_id as a parent.
687
627
        self.set_parent_ids(parents,
688
628
            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
689
629
 
690
 
    @needs_write_lock
 
630
    @needs_tree_write_lock
691
631
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
692
632
        """Add revision_id, tree tuple as a parent.
693
633
 
709
649
        self.set_parent_ids(parent_ids,
710
650
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
711
651
 
712
 
    @needs_write_lock
 
652
    @needs_tree_write_lock
713
653
    def add_pending_merge(self, *revision_ids):
714
654
        # TODO: Perhaps should check at this point that the
715
655
        # history of the revision is actually present?
736
676
        """
737
677
        return self.get_parent_ids()[1:]
738
678
 
739
 
    @needs_write_lock
 
679
    @needs_tree_write_lock
740
680
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
741
681
        """Set the parent ids to revision_ids.
742
682
        
760
700
        merges = revision_ids[1:]
761
701
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
762
702
 
763
 
    @needs_write_lock
 
703
    @needs_tree_write_lock
764
704
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
765
 
        """Set the parents of the working tree.
766
 
 
767
 
        :param parents_list: A list of (revision_id, tree) tuples. 
768
 
            If tree is None, then that element is treated as an unreachable
769
 
            parent tree - i.e. a ghost.
770
 
        """
 
705
        """See MutableTree.set_parent_trees."""
771
706
        # parent trees are not used in current format trees, delegate to
772
707
        # set_parent_ids
773
708
        self.set_parent_ids([rev for (rev, tree) in parents_list],
774
709
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
775
710
 
776
 
    @needs_write_lock
 
711
    @needs_tree_write_lock
777
712
    def set_pending_merges(self, rev_list):
778
713
        parents = self.get_parent_ids()
779
714
        leftmost = parents[:1]
780
715
        new_parents = leftmost + rev_list
781
716
        self.set_parent_ids(new_parents)
782
717
 
783
 
    @needs_write_lock
 
718
    @needs_tree_write_lock
784
719
    def set_merge_modified(self, modified_hashes):
785
720
        def iter_stanzas():
786
721
            for file_id, hash in modified_hashes.iteritems():
787
722
                yield Stanza(file_id=file_id, hash=hash)
788
723
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
789
724
 
790
 
    @needs_write_lock
 
725
    @needs_tree_write_lock
791
726
    def _put_rio(self, filename, stanzas, header):
792
727
        my_file = rio_file(stanzas, header)
793
728
        self._control_files.put(filename, my_file)
794
729
 
795
 
    @needs_write_lock
 
730
    @needs_write_lock # because merge pulls data into the branch.
796
731
    def merge_from_branch(self, branch, to_revision=None):
797
732
        """Merge from a branch into this working tree.
798
733
 
856
791
                merge_hashes[file_id] = hash
857
792
        return merge_hashes
858
793
 
 
794
    @needs_write_lock
 
795
    def mkdir(self, path, file_id=None):
 
796
        """See MutableTree.mkdir()."""
 
797
        if file_id is None:
 
798
            file_id = gen_file_id(os.path.basename(path))
 
799
        os.mkdir(self.abspath(path))
 
800
        self.add(path, file_id, 'directory')
 
801
        return file_id
 
802
 
859
803
    def get_symlink_target(self, file_id):
860
804
        return os.readlink(self.id2abspath(file_id))
861
805
 
978
922
                # if we finished all children, pop it off the stack
979
923
                stack.pop()
980
924
 
981
 
 
982
 
    @needs_write_lock
 
925
    @needs_tree_write_lock
983
926
    def move(self, from_paths, to_name):
984
927
        """Rename files.
985
928
 
1048
991
        self._write_inventory(inv)
1049
992
        return result
1050
993
 
1051
 
    @needs_write_lock
 
994
    @needs_tree_write_lock
1052
995
    def rename_one(self, from_rel, to_rel):
1053
996
        """Rename one file.
1054
997
 
1107
1050
            if not self.is_ignored(subp):
1108
1051
                yield subp
1109
1052
    
1110
 
    @needs_write_lock
 
1053
    @needs_tree_write_lock
1111
1054
    def unversion(self, file_ids):
1112
1055
        """Remove the file ids in file_ids from the current versioned set.
1113
1056
 
1198
1141
            source.unlock()
1199
1142
            top_pb.finished()
1200
1143
 
 
1144
    @needs_write_lock
 
1145
    def put_file_bytes_non_atomic(self, file_id, bytes):
 
1146
        """See MutableTree.put_file_bytes_non_atomic."""
 
1147
        stream = file(self.id2abspath(file_id), 'wb')
 
1148
        try:
 
1149
            stream.write(bytes)
 
1150
        finally:
 
1151
            stream.close()
 
1152
        # TODO: update the hashcache here ?
 
1153
 
1201
1154
    def extras(self):
1202
1155
        """Yield all unknown files in this WorkingTree.
1203
1156
 
1364
1317
        return file_kind(self.id2abspath(file_id))
1365
1318
 
1366
1319
    def last_revision(self):
1367
 
        """Return the last revision id of this working tree.
1368
 
 
1369
 
        In early branch formats this was the same as the branch last_revision,
1370
 
        but that cannot be relied upon - for working tree operations,
1371
 
        always use tree.last_revision(). This returns the left most parent id,
1372
 
        or None if there are no parents.
1373
 
 
1374
 
        This was deprecated as of 0.11. Please use get_parent_ids instead.
 
1320
        """Return the last revision of the branch for this tree.
 
1321
 
 
1322
        This format tree does not support a separate marker for last-revision
 
1323
        compared to the branch.
 
1324
 
 
1325
        See MutableTree.last_revision
1375
1326
        """
1376
1327
        return self._last_revision()
1377
1328
 
1392
1343
            self.branch.unlock()
1393
1344
            raise
1394
1345
 
 
1346
    def lock_tree_write(self):
 
1347
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1348
        self.branch.lock_read()
 
1349
        try:
 
1350
            return self._control_files.lock_write()
 
1351
        except:
 
1352
            self.branch.unlock()
 
1353
            raise
 
1354
 
1395
1355
    def lock_write(self):
1396
 
        """See Branch.lock_write, and WorkingTree.unlock."""
 
1356
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1397
1357
        self.branch.lock_write()
1398
1358
        try:
1399
1359
            return self._control_files.lock_write()
1407
1367
    def _basis_inventory_name(self):
1408
1368
        return 'basis-inventory-cache'
1409
1369
 
1410
 
    @needs_write_lock
 
1370
    @needs_tree_write_lock
1411
1371
    def set_last_revision(self, new_revision):
1412
1372
        """Change the last revision in the working tree."""
1413
1373
        if self._change_last_revision(new_revision):
1475
1435
        self._set_inventory(result)
1476
1436
        return result
1477
1437
 
1478
 
    @needs_write_lock
 
1438
    @needs_tree_write_lock
1479
1439
    def remove(self, files, verbose=False, to_file=None):
1480
1440
        """Remove nominated files from the working inventory..
1481
1441
 
1515
1475
 
1516
1476
        self._write_inventory(inv)
1517
1477
 
1518
 
    @needs_write_lock
 
1478
    @needs_tree_write_lock
1519
1479
    def revert(self, filenames, old_tree=None, backups=True, 
1520
1480
               pb=DummyProgress()):
1521
1481
        from transform import revert
1532
1492
 
1533
1493
    # XXX: This method should be deprecated in favour of taking in a proper
1534
1494
    # new Inventory object.
1535
 
    @needs_write_lock
 
1495
    @needs_tree_write_lock
1536
1496
    def set_inventory(self, new_inventory_list):
1537
1497
        from bzrlib.inventory import (Inventory,
1538
1498
                                      InventoryDirectory,
1555
1515
                raise BzrError("unknown kind %r" % kind)
1556
1516
        self._write_inventory(inv)
1557
1517
 
1558
 
    @needs_write_lock
 
1518
    @needs_tree_write_lock
1559
1519
    def set_root_id(self, file_id):
1560
1520
        """Set the root id for this tree."""
1561
1521
        inv = self.read_working_inventory()
1676
1636
                                  this_tree=self)
1677
1637
        return result
1678
1638
 
1679
 
    @needs_write_lock
 
1639
    @needs_tree_write_lock
1680
1640
    def _write_inventory(self, inv):
1681
1641
        """Write inventory as the current inventory."""
1682
1642
        sio = StringIO()
1726
1686
     - uses the branch last-revision.
1727
1687
    """
1728
1688
 
 
1689
    def lock_tree_write(self):
 
1690
        """See WorkingTree.lock_tree_write().
 
1691
 
 
1692
        In Format2 WorkingTrees we have a single lock for the branch and tree
 
1693
        so lock_tree_write() degrades to lock_write().
 
1694
        """
 
1695
        self.branch.lock_write()
 
1696
        try:
 
1697
            return self._control_files.lock_write()
 
1698
        except:
 
1699
            self.branch.unlock()
 
1700
            raise
 
1701
 
1729
1702
    def unlock(self):
1730
1703
        # we share control files:
1731
1704
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1749
1722
 
1750
1723
    @needs_read_lock
1751
1724
    def _last_revision(self):
1752
 
        """See WorkingTree._last_revision."""
 
1725
        """See Mutable.last_revision."""
1753
1726
        try:
1754
1727
            return self._control_files.get_utf8('last-revision').read()
1755
1728
        except NoSuchFile:
1767
1740
            self._control_files.put_utf8('last-revision', revision_id)
1768
1741
            return True
1769
1742
 
1770
 
    @needs_write_lock
 
1743
    @needs_tree_write_lock
1771
1744
    def set_conflicts(self, conflicts):
1772
1745
        self._put_rio('conflicts', conflicts.to_stanzas(), 
1773
1746
                      CONFLICT_HEADER_1)
1774
1747
 
1775
 
    @needs_write_lock
 
1748
    @needs_tree_write_lock
1776
1749
    def add_conflicts(self, new_conflicts):
1777
1750
        conflict_set = set(self.conflicts())
1778
1751
        conflict_set.update(set(list(new_conflicts)))
2028
2001
                         _format=self,
2029
2002
                         _bzrdir=a_bzrdir,
2030
2003
                         _control_files=control_files)
2031
 
        wt.lock_write()
 
2004
        wt.lock_tree_write()
2032
2005
        try:
2033
2006
            wt.set_last_revision(revision_id)
2034
2007
            basis_tree = wt.basis_tree()