/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

  • Committer: John Arbash Meinel
  • Date: 2006-10-06 05:53:44 UTC
  • mfrom: (2063 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2071.
  • Revision ID: john@arbash-meinel.com-20061006055344-e73b97b7c6ca6e72
[merge] bzr.dev 2063

Show diffs side-by-side

added added

removed removed

Lines of Context:
62
62
    textui,
63
63
    transform,
64
64
    xml5,
 
65
    xml6,
65
66
    )
66
67
import bzrlib.branch
67
68
from bzrlib.transport import get_transport
82
83
from bzrlib.inventory import InventoryEntry, Inventory
83
84
from bzrlib.lockable_files import LockableFiles, TransportLock
84
85
from bzrlib.lockdir import LockDir
 
86
from bzrlib.merge import merge_inner, transform_tree
 
87
import bzrlib.mutabletree
 
88
from bzrlib.mutabletree import needs_tree_write_lock
85
89
from bzrlib.osutils import (
86
90
    compact_date,
87
91
    file_kind,
99
103
import bzrlib.tree
100
104
from bzrlib.progress import DummyProgress, ProgressPhase
101
105
from bzrlib.revision import NULL_REVISION
 
106
import bzrlib.revisiontree
102
107
from bzrlib.rio import RioReader, rio_file, Stanza
103
108
from bzrlib.symbol_versioning import (deprecated_passed,
104
109
        deprecated_method,
218
223
        return ''
219
224
 
220
225
 
221
 
class WorkingTree(bzrlib.tree.Tree):
 
226
class WorkingTree(bzrlib.mutabletree.MutableTree):
222
227
    """Working copy tree.
223
228
 
224
229
    The inventory is held in the `Branch` working-inventory, and the
329
334
    def _set_inventory(self, inv):
330
335
        assert inv.root is not None
331
336
        self._inventory = inv
332
 
        self.path2id = self._inventory.path2id
333
 
 
334
 
    def is_control_filename(self, filename):
335
 
        """True if filename is the name of a control file in this tree.
336
 
        
337
 
        :param filename: A filename within the tree. This is a relative path
338
 
        from the root of this tree.
339
 
 
340
 
        This is true IF and ONLY IF the filename is part of the meta data
341
 
        that bzr controls in this tree. I.E. a random .bzr directory placed
342
 
        on disk will not be a control file for this tree.
343
 
        """
344
 
        return self.bzrdir.is_control_filename(filename)
345
337
 
346
338
    @staticmethod
347
339
    def open(path=None, _unsupported=False):
414
406
        else:
415
407
            try:
416
408
                xml = self.read_basis_inventory()
417
 
                inv = xml5.serializer_v5.read_inventory_from_string(xml)
418
 
                inv.root.revision = revision_id
419
 
            except NoSuchFile:
420
 
                inv = None
421
 
            if inv is not None and inv.revision_id == revision_id:
422
 
                return bzrlib.tree.RevisionTree(self.branch.repository, inv,
423
 
                                                revision_id)
 
409
                inv = xml6.serializer_v6.read_inventory_from_string(xml)
 
410
                if inv is not None and inv.revision_id == revision_id:
 
411
                    return bzrlib.tree.RevisionTree(self.branch.repository,
 
412
                                                    inv, revision_id)
 
413
            except (NoSuchFile, errors.BadInventoryFormat):
 
414
                pass
424
415
        # No cached copy available, retrieve from the repository.
425
416
        # FIXME? RBC 20060403 should we cache the inventory locally
426
417
        # at this point ?
556
547
            merge.transform_tree(tree, self)
557
548
            tree.set_parent_ids([revision_id])
558
549
 
559
 
    @needs_write_lock
560
 
    def commit(self, message=None, revprops=None, *args, **kwargs):
561
 
        # avoid circular imports
562
 
        from bzrlib.commit import Commit
563
 
        if revprops is None:
564
 
            revprops = {}
565
 
        if not 'branch-nick' in revprops:
566
 
            revprops['branch-nick'] = self.branch.nick
567
 
        # args for wt.commit start at message from the Commit.commit method,
568
 
        # but with branch a kwarg now, passing in args as is results in the
569
 
        #message being used for the branch
570
 
        args = (DEPRECATED_PARAMETER, message, ) + args
571
 
        committed_id = Commit().commit( working_tree=self, revprops=revprops,
572
 
            *args, **kwargs)
573
 
        return committed_id
574
 
 
575
550
    def id2abspath(self, file_id):
576
551
        return self.abspath(self.id2path(file_id))
577
552
 
615
590
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
616
591
 
617
592
    @needs_write_lock
618
 
    def add(self, files, ids=None):
619
 
        """Make files versioned.
620
 
 
621
 
        Note that the command line normally calls smart_add instead,
622
 
        which can automatically recurse.
623
 
 
624
 
        This adds the files to the inventory, so that they will be
625
 
        recorded by the next commit.
626
 
 
627
 
        files
628
 
            List of paths to add, relative to the base of the tree.
629
 
 
630
 
        ids
631
 
            If set, use these instead of automatically generated ids.
632
 
            Must be the same length as the list of files, but may
633
 
            contain None for ids that are to be autogenerated.
634
 
 
635
 
        TODO: Perhaps have an option to add the ids even if the files do
636
 
              not (yet) exist.
637
 
 
638
 
        TODO: Perhaps callback with the ids and paths as they're added.
639
 
        """
 
593
    def _add(self, files, ids, kinds):
 
594
        """See MutableTree._add."""
640
595
        # TODO: Re-adding a file that is removed in the working copy
641
596
        # should probably put it back with the previous ID.
642
 
        if isinstance(files, basestring):
643
 
            assert(ids is None or isinstance(ids, basestring))
644
 
            files = [files]
645
 
            if ids is not None:
646
 
                ids = [ids]
647
 
 
648
 
        if ids is None:
649
 
            ids = [None] * len(files)
650
 
        else:
651
 
            assert(len(ids) == len(files))
652
 
 
 
597
        # the read and write working inventory should not occur in this 
 
598
        # function - they should be part of lock_write and unlock.
653
599
        inv = self.read_working_inventory()
654
 
        for f,file_id in zip(files, ids):
655
 
            if self.is_control_filename(f):
656
 
                raise errors.ForbiddenControlFileError(filename=f)
657
 
 
658
 
            fp = splitpath(f)
659
 
 
660
 
            if len(fp) == 0:
661
 
                raise BzrError("cannot add top-level %r" % f)
662
 
 
663
 
            fullpath = normpath(self.abspath(f))
664
 
            try:
665
 
                kind = file_kind(fullpath)
666
 
            except OSError, e:
667
 
                if e.errno == errno.ENOENT:
668
 
                    raise NoSuchFile(fullpath)
669
 
            if not InventoryEntry.versionable_kind(kind):
670
 
                raise errors.BadFileKindError(filename=f, kind=kind)
 
600
        for f, file_id, kind in zip(files, ids, kinds):
 
601
            assert kind is not None
671
602
            if file_id is None:
672
603
                inv.add_path(f, kind=kind)
673
604
            else:
674
605
                inv.add_path(f, kind=kind, file_id=file_id)
675
 
 
676
606
        self._write_inventory(inv)
677
607
 
 
608
    @needs_tree_write_lock
 
609
    def _gather_kinds(self, files, kinds):
 
610
        """See MutableTree._gather_kinds."""
 
611
        for pos, f in enumerate(files):
 
612
            if kinds[pos] is None:
 
613
                fullpath = normpath(self.abspath(f))
 
614
                try:
 
615
                    kinds[pos] = file_kind(fullpath)
 
616
                except OSError, e:
 
617
                    if e.errno == errno.ENOENT:
 
618
                        raise NoSuchFile(fullpath)
 
619
 
678
620
    @needs_write_lock
679
621
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
680
622
        """Add revision_id as a parent.
691
633
        self.set_parent_ids(parents,
692
634
            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
693
635
 
694
 
    @needs_write_lock
 
636
    @needs_tree_write_lock
695
637
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
696
638
        """Add revision_id, tree tuple as a parent.
697
639
 
713
655
        self.set_parent_ids(parent_ids,
714
656
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
715
657
 
716
 
    @needs_write_lock
 
658
    @needs_tree_write_lock
717
659
    def add_pending_merge(self, *revision_ids):
718
660
        # TODO: Perhaps should check at this point that the
719
661
        # history of the revision is actually present?
740
682
        """
741
683
        return self.get_parent_ids()[1:]
742
684
 
743
 
    @needs_write_lock
 
685
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
 
686
        """Common ghost checking functionality from set_parent_*.
 
687
 
 
688
        This checks that the left hand-parent exists if there are any
 
689
        revisions present.
 
690
        """
 
691
        if len(revision_ids) > 0:
 
692
            leftmost_id = revision_ids[0]
 
693
            if (not allow_leftmost_as_ghost and not
 
694
                self.branch.repository.has_revision(leftmost_id)):
 
695
                raise errors.GhostRevisionUnusableHere(leftmost_id)
 
696
 
 
697
    def _set_merges_from_parent_ids(self, parent_ids):
 
698
        merges = parent_ids[1:]
 
699
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
 
700
 
 
701
    @needs_tree_write_lock
744
702
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
745
703
        """Set the parent ids to revision_ids.
746
704
        
753
711
        :param revision_ids: The revision_ids to set as the parent ids of this
754
712
            working tree. Any of these may be ghosts.
755
713
        """
 
714
        self._check_parents_for_ghosts(revision_ids,
 
715
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
716
 
756
717
        if len(revision_ids) > 0:
757
 
            leftmost_id = revision_ids[0]
758
 
            if (not allow_leftmost_as_ghost and not
759
 
                self.branch.repository.has_revision(leftmost_id)):
760
 
                raise errors.GhostRevisionUnusableHere(leftmost_id)
761
 
            self.set_last_revision(leftmost_id)
 
718
            self.set_last_revision(revision_ids[0])
762
719
        else:
763
720
            self.set_last_revision(None)
764
 
        merges = revision_ids[1:]
765
 
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
766
 
 
767
 
    @needs_write_lock
 
721
 
 
722
        self._set_merges_from_parent_ids(revision_ids)
 
723
 
 
724
    @needs_tree_write_lock
768
725
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
769
 
        """Set the parents of the working tree.
 
726
        """See MutableTree.set_parent_trees."""
 
727
        parent_ids = [rev for (rev, tree) in parents_list]
770
728
 
771
 
        :param parents_list: A list of (revision_id, tree) tuples. 
772
 
            If tree is None, then that element is treated as an unreachable
773
 
            parent tree - i.e. a ghost.
774
 
        """
775
 
        # parent trees are not used in current format trees, delegate to
776
 
        # set_parent_ids
777
 
        self.set_parent_ids([rev for (rev, tree) in parents_list],
 
729
        self._check_parents_for_ghosts(parent_ids,
778
730
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
779
731
 
780
 
    @needs_write_lock
 
732
        if len(parent_ids) == 0:
 
733
            leftmost_parent_id = None
 
734
            leftmost_parent_tree = None
 
735
        else:
 
736
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
 
737
 
 
738
        if self._change_last_revision(leftmost_parent_id):
 
739
            if leftmost_parent_tree is None:
 
740
                # If we don't have a tree, fall back to reading the
 
741
                # parent tree from the repository.
 
742
                self._cache_basis_inventory(leftmost_parent_id)
 
743
            else:
 
744
                inv = leftmost_parent_tree.inventory
 
745
                xml = self._create_basis_xml_from_inventory(
 
746
                                        leftmost_parent_id, inv)
 
747
                self._write_basis_inventory(xml)
 
748
        self._set_merges_from_parent_ids(parent_ids)
 
749
 
 
750
    @needs_tree_write_lock
781
751
    def set_pending_merges(self, rev_list):
782
752
        parents = self.get_parent_ids()
783
753
        leftmost = parents[:1]
784
754
        new_parents = leftmost + rev_list
785
755
        self.set_parent_ids(new_parents)
786
756
 
787
 
    @needs_write_lock
 
757
    @needs_tree_write_lock
788
758
    def set_merge_modified(self, modified_hashes):
789
759
        def iter_stanzas():
790
760
            for file_id, hash in modified_hashes.iteritems():
791
761
                yield Stanza(file_id=file_id, hash=hash)
792
762
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
793
763
 
794
 
    @needs_write_lock
 
764
    @needs_tree_write_lock
795
765
    def _put_rio(self, filename, stanzas, header):
796
766
        my_file = rio_file(stanzas, header)
797
767
        self._control_files.put(filename, my_file)
798
768
 
799
 
    @needs_write_lock
 
769
    @needs_write_lock # because merge pulls data into the branch.
800
770
    def merge_from_branch(self, branch, to_revision=None):
801
771
        """Merge from a branch into this working tree.
802
772
 
860
830
                merge_hashes[file_id] = hash
861
831
        return merge_hashes
862
832
 
 
833
    @needs_write_lock
 
834
    def mkdir(self, path, file_id=None):
 
835
        """See MutableTree.mkdir()."""
 
836
        if file_id is None:
 
837
            file_id = gen_file_id(os.path.basename(path))
 
838
        os.mkdir(self.abspath(path))
 
839
        self.add(path, file_id, 'directory')
 
840
        return file_id
 
841
 
863
842
    def get_symlink_target(self, file_id):
864
843
        return os.readlink(self.id2abspath(file_id))
865
844
 
871
850
        else:
872
851
            return '?'
873
852
 
874
 
    def list_files(self):
 
853
    def list_files(self, include_root=False):
875
854
        """Recursively list all files as (path, class, kind, id, entry).
876
855
 
877
856
        Lists, but does not descend into unversioned directories.
882
861
        Skips the control directory.
883
862
        """
884
863
        inv = self._inventory
 
864
        if include_root is True:
 
865
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
885
866
        # Convert these into local objects to save lookup times
886
867
        pathjoin = osutils.pathjoin
887
868
        file_kind = osutils.file_kind
980
961
                # if we finished all children, pop it off the stack
981
962
                stack.pop()
982
963
 
983
 
 
984
 
    @needs_write_lock
 
964
    @needs_tree_write_lock
985
965
    def move(self, from_paths, to_name):
986
966
        """Rename files.
987
967
 
1050
1030
        self._write_inventory(inv)
1051
1031
        return result
1052
1032
 
1053
 
    @needs_write_lock
 
1033
    @needs_tree_write_lock
1054
1034
    def rename_one(self, from_rel, to_rel):
1055
1035
        """Rename one file.
1056
1036
 
1109
1089
            if not self.is_ignored(subp):
1110
1090
                yield subp
1111
1091
    
1112
 
    @needs_write_lock
 
1092
    @needs_tree_write_lock
1113
1093
    def unversion(self, file_ids):
1114
1094
        """Remove the file ids in file_ids from the current versioned set.
1115
1095
 
1198
1178
            source.unlock()
1199
1179
            top_pb.finished()
1200
1180
 
 
1181
    @needs_write_lock
 
1182
    def put_file_bytes_non_atomic(self, file_id, bytes):
 
1183
        """See MutableTree.put_file_bytes_non_atomic."""
 
1184
        stream = file(self.id2abspath(file_id), 'wb')
 
1185
        try:
 
1186
            stream.write(bytes)
 
1187
        finally:
 
1188
            stream.close()
 
1189
        # TODO: update the hashcache here ?
 
1190
 
1201
1191
    def extras(self):
1202
1192
        """Yield all unknown files in this WorkingTree.
1203
1193
 
1364
1354
        return file_kind(self.id2abspath(file_id))
1365
1355
 
1366
1356
    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.
 
1357
        """Return the last revision of the branch for this tree.
 
1358
 
 
1359
        This format tree does not support a separate marker for last-revision
 
1360
        compared to the branch.
 
1361
 
 
1362
        See MutableTree.last_revision
1375
1363
        """
1376
1364
        return self._last_revision()
1377
1365
 
1392
1380
            self.branch.unlock()
1393
1381
            raise
1394
1382
 
 
1383
    def lock_tree_write(self):
 
1384
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1385
        self.branch.lock_read()
 
1386
        try:
 
1387
            return self._control_files.lock_write()
 
1388
        except:
 
1389
            self.branch.unlock()
 
1390
            raise
 
1391
 
1395
1392
    def lock_write(self):
1396
 
        """See Branch.lock_write, and WorkingTree.unlock."""
 
1393
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1397
1394
        self.branch.lock_write()
1398
1395
        try:
1399
1396
            return self._control_files.lock_write()
1405
1402
        return self._control_files.get_physical_lock_status()
1406
1403
 
1407
1404
    def _basis_inventory_name(self):
1408
 
        return 'basis-inventory'
 
1405
        return 'basis-inventory-cache'
1409
1406
 
1410
 
    @needs_write_lock
 
1407
    @needs_tree_write_lock
1411
1408
    def set_last_revision(self, new_revision):
1412
1409
        """Change the last revision in the working tree."""
1413
1410
        if self._change_last_revision(new_revision):
1429
1426
            self.branch.set_revision_history([new_revision])
1430
1427
        return True
1431
1428
 
 
1429
    def _write_basis_inventory(self, xml):
 
1430
        """Write the basis inventory XML to the basis-inventory file"""
 
1431
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
 
1432
        path = self._basis_inventory_name()
 
1433
        sio = StringIO(xml)
 
1434
        self._control_files.put(path, sio)
 
1435
 
 
1436
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
 
1437
        """Create the text that will be saved in basis-inventory"""
 
1438
        inventory.revision_id = revision_id
 
1439
        return xml6.serializer_v6.write_inventory_to_string(inventory)
 
1440
 
1432
1441
    def _cache_basis_inventory(self, new_revision):
1433
1442
        """Cache new_revision as the basis inventory."""
1434
1443
        # TODO: this should allow the ready-to-use inventory to be passed in,
1446
1455
            # root node id can legitimately look like 'revision_id' but cannot
1447
1456
            # contain a '"'.
1448
1457
            xml = self.branch.repository.get_inventory_xml(new_revision)
1449
 
            if not 'revision_id="' in xml.split('\n', 1)[0]:
 
1458
            firstline = xml.split('\n', 1)[0]
 
1459
            if (not 'revision_id="' in firstline or 
 
1460
                'format="6"' not in firstline):
1450
1461
                inv = self.branch.repository.deserialise_inventory(
1451
1462
                    new_revision, xml)
1452
 
                inv.revision_id = new_revision
1453
 
                xml = xml5.serializer_v5.write_inventory_to_string(inv)
1454
 
            assert isinstance(xml, str), 'serialised xml must be bytestring.'
1455
 
            path = self._basis_inventory_name()
1456
 
            sio = StringIO(xml)
1457
 
            self._control_files.put(path, sio)
 
1463
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
 
1464
            self._write_basis_inventory(xml)
1458
1465
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1459
1466
            pass
1460
1467
 
1473
1480
        self._set_inventory(result)
1474
1481
        return result
1475
1482
 
1476
 
    @needs_write_lock
 
1483
    @needs_tree_write_lock
1477
1484
    def remove(self, files, verbose=False, to_file=None):
1478
1485
        """Remove nominated files from the working inventory..
1479
1486
 
1514
1521
 
1515
1522
        self._write_inventory(inv)
1516
1523
 
1517
 
    @needs_write_lock
 
1524
    @needs_tree_write_lock
1518
1525
    def revert(self, filenames, old_tree=None, backups=True, 
1519
1526
               pb=DummyProgress()):
1520
1527
        from bzrlib.conflicts import resolve
1530
1537
 
1531
1538
    # XXX: This method should be deprecated in favour of taking in a proper
1532
1539
    # new Inventory object.
1533
 
    @needs_write_lock
 
1540
    @needs_tree_write_lock
1534
1541
    def set_inventory(self, new_inventory_list):
1535
1542
        from bzrlib.inventory import (Inventory,
1536
1543
                                      InventoryDirectory,
1553
1560
                raise BzrError("unknown kind %r" % kind)
1554
1561
        self._write_inventory(inv)
1555
1562
 
1556
 
    @needs_write_lock
 
1563
    @needs_tree_write_lock
1557
1564
    def set_root_id(self, file_id):
1558
1565
        """Set the root id for this tree."""
1559
1566
        inv = self.read_working_inventory()
1667
1674
                                  this_tree=self)
1668
1675
        return result
1669
1676
 
1670
 
    @needs_write_lock
 
1677
    @needs_tree_write_lock
1671
1678
    def _write_inventory(self, inv):
1672
1679
        """Write inventory as the current inventory."""
1673
1680
        sio = StringIO()
1718
1725
     - uses the branch last-revision.
1719
1726
    """
1720
1727
 
 
1728
    def lock_tree_write(self):
 
1729
        """See WorkingTree.lock_tree_write().
 
1730
 
 
1731
        In Format2 WorkingTrees we have a single lock for the branch and tree
 
1732
        so lock_tree_write() degrades to lock_write().
 
1733
        """
 
1734
        self.branch.lock_write()
 
1735
        try:
 
1736
            return self._control_files.lock_write()
 
1737
        except:
 
1738
            self.branch.unlock()
 
1739
            raise
 
1740
 
1721
1741
    def unlock(self):
1722
1742
        # we share control files:
1723
1743
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1741
1761
 
1742
1762
    @needs_read_lock
1743
1763
    def _last_revision(self):
1744
 
        """See WorkingTree._last_revision."""
 
1764
        """See Mutable.last_revision."""
1745
1765
        try:
1746
1766
            return self._control_files.get_utf8('last-revision').read()
1747
1767
        except NoSuchFile:
1759
1779
            self._control_files.put_utf8('last-revision', revision_id)
1760
1780
            return True
1761
1781
 
1762
 
    @needs_write_lock
 
1782
    @needs_tree_write_lock
1763
1783
    def set_conflicts(self, conflicts):
1764
1784
        self._put_rio('conflicts', conflicts.to_stanzas(), 
1765
1785
                      CONFLICT_HEADER_1)
1766
1786
 
1767
 
    @needs_write_lock
 
1787
    @needs_tree_write_lock
1768
1788
    def add_conflicts(self, new_conflicts):
1769
1789
        conflict_set = set(self.conflicts())
1770
1790
        conflict_set.update(set(list(new_conflicts)))
2018
2038
                         _format=self,
2019
2039
                         _bzrdir=a_bzrdir,
2020
2040
                         _control_files=control_files)
2021
 
        wt.lock_write()
 
2041
        wt.lock_tree_write()
2022
2042
        try:
2023
2043
            wt._write_inventory(inv)
2024
2044
            wt.set_root_id(inv.root.file_id)