~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

Various changes to allow non-workingtree specific tests to run entirely
from MemoryTransports:
 * Create MemoryTree and pull up common code for it from WorkingTree to
   a new common base class MutableTree.
 * Add MutableTree.mkdir().
 * Add MutableTree.put_file_bytes_nonatomic().
 * New test helper make_branch_and_memory_tree().
(Robert Collins)

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
74
75
from bzrlib.osutils import (
75
76
                            abspath,
76
77
                            compact_date,
90
91
                            )
91
92
from bzrlib.progress import DummyProgress, ProgressPhase
92
93
from bzrlib.revision import NULL_REVISION
 
94
import bzrlib.revisiontree
93
95
from bzrlib.rio import RioReader, rio_file, Stanza
94
96
from bzrlib.symbol_versioning import (deprecated_passed,
95
97
        deprecated_method,
102
104
from bzrlib.transport import get_transport
103
105
from bzrlib.transport.local import LocalTransport
104
106
from bzrlib.textui import show_status
105
 
import bzrlib.tree
106
107
import bzrlib.ui
107
108
import bzrlib.xml5
108
109
 
213
214
        return ''
214
215
 
215
216
 
216
 
class WorkingTree(bzrlib.tree.Tree):
 
217
class WorkingTree(bzrlib.mutabletree.MutableTree):
217
218
    """Working copy tree.
218
219
 
219
220
    The inventory is held in the `Branch` working-inventory, and the
324
325
    def _set_inventory(self, inv):
325
326
        assert inv.root is not None
326
327
        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
328
 
341
329
    @staticmethod
342
330
    def open(path=None, _unsupported=False):
408
396
            except NoSuchFile:
409
397
                inv = None
410
398
            if inv is not None and inv.revision_id == revision_id:
411
 
                return bzrlib.tree.RevisionTree(self.branch.repository, inv,
412
 
                                                revision_id)
 
399
                return bzrlib.revisiontree.RevisionTree(self.branch.repository,
 
400
                    inv, revision_id)
413
401
        # FIXME? RBC 20060403 should we cache the inventory here ?
414
402
        try:
415
403
            return self.branch.repository.revision_tree(revision_id)
543
531
            transform_tree(tree, self)
544
532
            tree.set_parent_ids([revision_id])
545
533
 
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
534
    def id2abspath(self, file_id):
564
535
        return self.abspath(self.id2path(file_id))
565
536
 
603
574
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
604
575
 
605
576
    @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
 
        """
 
577
    def _add(self, files, ids, kinds):
 
578
        """See MutableTree._add."""
628
579
        # TODO: Re-adding a file that is removed in the working copy
629
580
        # 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
 
 
 
581
        # the read and write working inventory should not occur in this 
 
582
        # function - they should be part of lock_write and unlock.
641
583
        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)
 
584
        for f, file_id, kind in zip(files, ids, kinds):
 
585
            assert kind is not None
659
586
            if file_id is None:
660
587
                inv.add_path(f, kind=kind)
661
588
            else:
662
589
                inv.add_path(f, kind=kind, file_id=file_id)
663
 
 
664
590
        self._write_inventory(inv)
665
591
 
 
592
    def _gather_kinds(self, files, kinds):
 
593
        """See MutableTree._gather_kinds."""
 
594
        for pos, f in enumerate(files):
 
595
            if kinds[pos] is None:
 
596
                fullpath = normpath(self.abspath(f))
 
597
                try:
 
598
                    kinds[pos] = file_kind(fullpath)
 
599
                except OSError, e:
 
600
                    if e.errno == errno.ENOENT:
 
601
                        raise NoSuchFile(fullpath)
 
602
 
666
603
    @needs_write_lock
667
604
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
668
605
        """Add revision_id as a parent.
750
687
 
751
688
    @needs_write_lock
752
689
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
753
 
        """Set the parents of the working tree.
754
 
 
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
 
        """
 
690
        """See MutableTree.set_parent_trees."""
759
691
        # parent trees are not used in current format trees, delegate to
760
692
        # set_parent_ids
761
693
        self.set_parent_ids([rev for (rev, tree) in parents_list],
844
776
                merge_hashes[file_id] = hash
845
777
        return merge_hashes
846
778
 
 
779
    @needs_write_lock
 
780
    def mkdir(self, path, file_id=None):
 
781
        """See MutableTree.mkdir()."""
 
782
        if file_id is None:
 
783
            file_id = gen_file_id(os.path.basename(path))
 
784
        os.mkdir(self.abspath(path))
 
785
        self.add(path, file_id, 'directory')
 
786
        return file_id
 
787
 
847
788
    def get_symlink_target(self, file_id):
848
789
        return os.readlink(self.id2abspath(file_id))
849
790
 
1155
1096
            source.unlock()
1156
1097
            top_pb.finished()
1157
1098
 
 
1099
    @needs_write_lock
 
1100
    def put_file_bytes_non_atomic(self, file_id, bytes):
 
1101
        """See MutableTree.put_file_bytes_non_atomic."""
 
1102
        stream = file(self.id2abspath(file_id), 'wb')
 
1103
        try:
 
1104
            stream.write(bytes)
 
1105
        finally:
 
1106
            stream.close()
 
1107
        # TODO: update the hashcache here ?
 
1108
 
1158
1109
    def extras(self):
1159
1110
        """Yield all unknown files in this WorkingTree.
1160
1111
 
1343
1294
            raise
1344
1295
 
1345
1296
    def lock_write(self):
1346
 
        """See Branch.lock_write, and WorkingTree.unlock."""
 
1297
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1347
1298
        self.branch.lock_write()
1348
1299
        try:
1349
1300
            return self._control_files.lock_write()