~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Alexander Belchenko
  • Date: 2007-08-10 09:04:38 UTC
  • mto: This revision was merged to the branch mainline in revision 2694.
  • Revision ID: bialix@ukr.net-20070810090438-0835xdz0rl8825qv
fixes after Ian's review

Show diffs side-by-side

added added

removed removed

Lines of Context:
64
64
    hashcache,
65
65
    ignores,
66
66
    merge,
 
67
    osutils,
67
68
    revision as _mod_revision,
68
69
    revisiontree,
69
70
    repository,
89
90
from bzrlib.lockdir import LockDir
90
91
import bzrlib.mutabletree
91
92
from bzrlib.mutabletree import needs_tree_write_lock
92
 
from bzrlib import osutils
93
93
from bzrlib.osutils import (
94
94
    compact_date,
95
95
    file_kind,
270
270
            # the Format factory and creation methods that are
271
271
            # permitted to do this.
272
272
            self._set_inventory(_inventory, dirty=False)
273
 
        self._detect_case_handling()
274
 
 
275
 
    def _detect_case_handling(self):
276
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
277
 
        try:
278
 
            wt_trans.stat("FoRMaT")
279
 
        except errors.NoSuchFile:
280
 
            self.case_sensitive = True
281
 
        else:
282
 
            self.case_sensitive = False
283
 
 
284
 
        self._setup_directory_is_tree_reference()
285
273
 
286
274
    branch = property(
287
275
        fget=lambda self: self._branch,
359
347
        """
360
348
        return WorkingTree.open(path, _unsupported=True)
361
349
 
362
 
    @staticmethod
363
 
    def find_trees(location):
364
 
        def list_current(transport):
365
 
            return [d for d in transport.list_dir('') if d != '.bzr']
366
 
        def evaluate(bzrdir):
367
 
            try:
368
 
                tree = bzrdir.open_workingtree()
369
 
            except errors.NoWorkingTree:
370
 
                return True, None
371
 
            else:
372
 
                return True, tree
373
 
        transport = get_transport(location)
374
 
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
375
 
                                              list_current=list_current)
376
 
        return [t for t in iterator if t is not None]
377
 
 
378
350
    # should be deprecated - this is slow and in any case treating them as a
379
351
    # container is (we now know) bad style -- mbp 20070302
380
352
    ## @deprecated_method(zero_fifteen)
389
361
            if osutils.lexists(self.abspath(path)):
390
362
                yield ie.file_id
391
363
 
392
 
    def all_file_ids(self):
393
 
        """See Tree.iter_all_file_ids"""
394
 
        return set(self.inventory)
395
 
 
396
364
    def __repr__(self):
397
365
        return "<%s of %s>" % (self.__class__.__name__,
398
366
                               getattr(self, 'basedir', None))
431
399
            # the basis tree is a ghost so return an empty tree.
432
400
            return self.branch.repository.revision_tree(None)
433
401
 
434
 
    def _cleanup(self):
435
 
        self._flush_ignore_list_cache()
436
 
 
437
402
    @staticmethod
438
403
    @deprecated_method(zero_eight)
439
404
    def create(branch, directory):
483
448
    def has_filename(self, filename):
484
449
        return osutils.lexists(self.abspath(filename))
485
450
 
486
 
    def get_file(self, file_id, path=None):
487
 
        if path is None:
488
 
            path = self.id2path(file_id)
489
 
        return self.get_file_byname(path)
 
451
    def get_file(self, file_id):
 
452
        file_id = osutils.safe_file_id(file_id)
 
453
        return self.get_file_byname(self.id2path(file_id))
490
454
 
491
455
    def get_file_text(self, file_id):
 
456
        file_id = osutils.safe_file_id(file_id)
492
457
        return self.get_file(file_id).read()
493
458
 
494
459
    def get_file_byname(self, filename):
505
470
        incorrectly attributed to CURRENT_REVISION (but after committing, the
506
471
        attribution will be correct).
507
472
        """
 
473
        file_id = osutils.safe_file_id(file_id)
508
474
        basis = self.basis_tree()
509
475
        basis.lock_read()
510
476
        try:
511
 
            changes = self.iter_changes(basis, True, [self.id2path(file_id)],
 
477
            changes = self._iter_changes(basis, True, [self.id2path(file_id)],
512
478
                require_versioned=True).next()
513
479
            changed_content, kind = changes[2], changes[6]
514
480
            if not changed_content:
555
521
            pass
556
522
        else:
557
523
            for l in merges_file.readlines():
558
 
                revision_id = l.rstrip('\n')
 
524
                revision_id = osutils.safe_revision_id(l.rstrip('\n'))
559
525
                parents.append(revision_id)
560
526
        return parents
561
527
 
566
532
        
567
533
    def _get_store_filename(self, file_id):
568
534
        ## XXX: badly named; this is not in the store at all
 
535
        file_id = osutils.safe_file_id(file_id)
569
536
        return self.abspath(self.id2path(file_id))
570
537
 
571
538
    @needs_read_lock
600
567
            tree.set_parent_ids([revision_id])
601
568
 
602
569
    def id2abspath(self, file_id):
 
570
        file_id = osutils.safe_file_id(file_id)
603
571
        return self.abspath(self.id2path(file_id))
604
572
 
605
573
    def has_id(self, file_id):
606
574
        # files that have been deleted are excluded
 
575
        file_id = osutils.safe_file_id(file_id)
607
576
        inv = self.inventory
608
577
        if not inv.has_id(file_id):
609
578
            return False
611
580
        return osutils.lexists(self.abspath(path))
612
581
 
613
582
    def has_or_had_id(self, file_id):
 
583
        file_id = osutils.safe_file_id(file_id)
614
584
        if file_id == self.inventory.root.file_id:
615
585
            return True
616
586
        return self.inventory.has_id(file_id)
618
588
    __contains__ = has_id
619
589
 
620
590
    def get_file_size(self, file_id):
 
591
        file_id = osutils.safe_file_id(file_id)
621
592
        return os.path.getsize(self.id2abspath(file_id))
622
593
 
623
594
    @needs_read_lock
624
595
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
596
        file_id = osutils.safe_file_id(file_id)
625
597
        if not path:
626
598
            path = self._inventory.id2path(file_id)
627
599
        return self._hashcache.get_sha1(path, stat_value)
628
600
 
629
601
    def get_file_mtime(self, file_id, path=None):
 
602
        file_id = osutils.safe_file_id(file_id)
630
603
        if not path:
631
604
            path = self.inventory.id2path(file_id)
632
605
        return os.lstat(self.abspath(path)).st_mtime
633
606
 
634
 
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
635
 
        file_id = self.path2id(path)
636
 
        return self._inventory[file_id].executable
637
 
 
638
 
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
639
 
        mode = stat_result.st_mode
640
 
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
641
 
 
642
607
    if not supports_executable():
643
608
        def is_executable(self, file_id, path=None):
 
609
            file_id = osutils.safe_file_id(file_id)
644
610
            return self._inventory[file_id].executable
645
 
 
646
 
        _is_executable_from_path_and_stat = \
647
 
            _is_executable_from_path_and_stat_from_basis
648
611
    else:
649
612
        def is_executable(self, file_id, path=None):
650
613
            if not path:
 
614
                file_id = osutils.safe_file_id(file_id)
651
615
                path = self.id2path(file_id)
652
616
            mode = os.lstat(self.abspath(path)).st_mode
653
617
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
654
618
 
655
 
        _is_executable_from_path_and_stat = \
656
 
            _is_executable_from_path_and_stat_from_stat
657
 
 
658
619
    @needs_tree_write_lock
659
620
    def _add(self, files, ids, kinds):
660
621
        """See MutableTree._add."""
668
629
            if file_id is None:
669
630
                inv.add_path(f, kind=kind)
670
631
            else:
 
632
                file_id = osutils.safe_file_id(file_id)
671
633
                inv.add_path(f, kind=kind, file_id=file_id)
672
634
            self._inventory_is_modified = True
673
635
 
735
697
        if updated:
736
698
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
737
699
 
738
 
    def path_content_summary(self, path, _lstat=os.lstat,
739
 
        _mapper=osutils.file_kind_from_stat_mode):
740
 
        """See Tree.path_content_summary."""
741
 
        abspath = self.abspath(path)
742
 
        try:
743
 
            stat_result = _lstat(abspath)
744
 
        except OSError, e:
745
 
            if getattr(e, 'errno', None) == errno.ENOENT:
746
 
                # no file.
747
 
                return ('missing', None, None, None)
748
 
            # propagate other errors
749
 
            raise
750
 
        kind = _mapper(stat_result.st_mode)
751
 
        if kind == 'file':
752
 
            size = stat_result.st_size
753
 
            # try for a stat cache lookup
754
 
            executable = self._is_executable_from_path_and_stat(path, stat_result)
755
 
            return (kind, size, executable, self._sha_from_stat(
756
 
                path, stat_result))
757
 
        elif kind == 'directory':
758
 
            # perhaps it looks like a plain directory, but it's really a
759
 
            # reference.
760
 
            if self._directory_is_tree_reference(path):
761
 
                kind = 'tree-reference'
762
 
            return kind, None, None, None
763
 
        elif kind == 'symlink':
764
 
            return ('symlink', None, None, os.readlink(abspath))
765
 
        else:
766
 
            return (kind, None, None, None)
767
 
 
768
700
    @deprecated_method(zero_eleven)
769
701
    @needs_read_lock
770
702
    def pending_merges(self):
807
739
        :param revision_ids: The revision_ids to set as the parent ids of this
808
740
            working tree. Any of these may be ghosts.
809
741
        """
 
742
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
810
743
        self._check_parents_for_ghosts(revision_ids,
811
744
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
812
745
        for revision_id in revision_ids:
822
755
    @needs_tree_write_lock
823
756
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
824
757
        """See MutableTree.set_parent_trees."""
825
 
        parent_ids = [rev for (rev, tree) in parents_list]
 
758
        parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
826
759
        for revision_id in parent_ids:
827
760
            _mod_revision.check_not_reserved_id(revision_id)
828
761
 
861
794
                yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
862
795
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
863
796
 
864
 
    def _sha_from_stat(self, path, stat_result):
865
 
        """Get a sha digest from the tree's stat cache.
866
 
 
867
 
        The default implementation assumes no stat cache is present.
868
 
 
869
 
        :param path: The path.
870
 
        :param stat_result: The stat result being looked up.
871
 
        """
872
 
        return None
873
 
 
874
797
    def _put_rio(self, filename, stanzas, header):
875
798
        self._must_be_locked()
876
799
        my_file = rio_file(stanzas, header)
898
821
            merger.check_basis(check_clean=True, require_commits=False)
899
822
            if to_revision is None:
900
823
                to_revision = _mod_revision.ensure_null(branch.last_revision())
 
824
            else:
 
825
                to_revision = osutils.safe_revision_id(to_revision)
901
826
            merger.other_rev_id = to_revision
902
827
            if _mod_revision.is_null(merger.other_rev_id):
903
828
                raise errors.NoCommits(branch)
968
893
        return file_id
969
894
 
970
895
    def get_symlink_target(self, file_id):
 
896
        file_id = osutils.safe_file_id(file_id)
971
897
        return os.readlink(self.id2abspath(file_id))
972
898
 
973
899
    @needs_write_lock
1012
938
            other_tree.unlock()
1013
939
        other_tree.bzrdir.retire_bzrdir()
1014
940
 
1015
 
    def _setup_directory_is_tree_reference(self):
1016
 
        if self._branch.repository._format.supports_tree_reference:
1017
 
            self._directory_is_tree_reference = \
1018
 
                self._directory_may_be_tree_reference
1019
 
        else:
1020
 
            self._directory_is_tree_reference = \
1021
 
                self._directory_is_never_tree_reference
1022
 
 
1023
 
    def _directory_is_never_tree_reference(self, relpath):
1024
 
        return False
1025
 
 
1026
 
    def _directory_may_be_tree_reference(self, relpath):
1027
 
        # as a special case, if a directory contains control files then 
1028
 
        # it's a tree reference, except that the root of the tree is not
1029
 
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1030
 
        # TODO: We could ask all the control formats whether they
1031
 
        # recognize this directory, but at the moment there's no cheap api
1032
 
        # to do that.  Since we probably can only nest bzr checkouts and
1033
 
        # they always use this name it's ok for now.  -- mbp 20060306
1034
 
        #
1035
 
        # FIXME: There is an unhandled case here of a subdirectory
1036
 
        # containing .bzr but not a branch; that will probably blow up
1037
 
        # when you try to commit it.  It might happen if there is a
1038
 
        # checkout in a subdirectory.  This can be avoided by not adding
1039
 
        # it.  mbp 20070306
1040
 
 
1041
941
    @needs_tree_write_lock
1042
942
    def extract(self, file_id, format=None):
1043
943
        """Extract a subtree from this tree.
1056
956
        sub_path = self.id2path(file_id)
1057
957
        branch_transport = mkdirs(sub_path)
1058
958
        if format is None:
1059
 
            format = self.bzrdir.cloning_metadir()
 
959
            format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
1060
960
        branch_transport.ensure_base()
1061
961
        branch_bzrdir = format.initialize_on_transport(branch_transport)
1062
962
        try:
1063
963
            repo = branch_bzrdir.find_repository()
1064
964
        except errors.NoRepositoryPresent:
1065
965
            repo = branch_bzrdir.create_repository()
1066
 
        if not repo.supports_rich_root():
1067
 
            raise errors.RootNotRich()
 
966
            assert repo.supports_rich_root()
 
967
        else:
 
968
            if not repo.supports_rich_root():
 
969
                raise errors.RootNotRich()
1068
970
        new_branch = branch_bzrdir.create_branch()
1069
971
        new_branch.pull(self.branch)
1070
972
        for parent_id in self.get_parent_ids():
1088
990
        return wt
1089
991
 
1090
992
    def _serialize(self, inventory, out_file):
1091
 
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1092
 
            working=True)
 
993
        xml5.serializer_v5.write_inventory(self._inventory, out_file)
1093
994
 
1094
995
    def _deserialize(selt, in_file):
1095
996
        return xml5.serializer_v5.read_inventory(in_file)
1359
1260
                only_change_inv = True
1360
1261
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1361
1262
                only_change_inv = False
1362
 
            elif (sys.platform == 'win32'
1363
 
                and from_rel.lower() == to_rel.lower()
1364
 
                and self.has_filename(from_rel)):
1365
 
                only_change_inv = False
1366
1263
            else:
1367
1264
                # something is wrong, so lets determine what exactly
1368
1265
                if not self.has_filename(from_rel) and \
1371
1268
                        errors.PathsDoNotExist(paths=(str(from_rel),
1372
1269
                        str(to_rel))))
1373
1270
                else:
1374
 
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
 
1271
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
 
1272
                        extra="(Use --after to update the Bazaar id)")
1375
1273
            rename_entry.only_change_inv = only_change_inv
1376
1274
        return rename_entries
1377
1275
 
1524
1422
        :raises: NoSuchId if any fileid is not currently versioned.
1525
1423
        """
1526
1424
        for file_id in file_ids:
 
1425
            file_id = osutils.safe_file_id(file_id)
1527
1426
            if self._inventory.has_id(file_id):
1528
1427
                self._inventory.remove_recursive_id(file_id)
1529
1428
            else:
1558
1457
 
1559
1458
    @needs_write_lock
1560
1459
    def pull(self, source, overwrite=False, stop_revision=None,
1561
 
             change_reporter=None, possible_transports=None):
 
1460
             change_reporter=None):
1562
1461
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1563
1462
        source.lock_read()
1564
1463
        try:
1566
1465
            pp.next_phase()
1567
1466
            old_revision_info = self.branch.last_revision_info()
1568
1467
            basis_tree = self.basis_tree()
1569
 
            count = self.branch.pull(source, overwrite, stop_revision,
1570
 
                                     possible_transports=possible_transports)
 
1468
            count = self.branch.pull(source, overwrite, stop_revision)
1571
1469
            new_revision_info = self.branch.last_revision_info()
1572
1470
            if new_revision_info != old_revision_info:
1573
1471
                pp.next_phase()
1585
1483
                                change_reporter=change_reporter)
1586
1484
                    if (basis_tree.inventory.root is None and
1587
1485
                        new_basis_tree.inventory.root is not None):
1588
 
                        self.set_root_id(new_basis_tree.get_root_id())
 
1486
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
1589
1487
                finally:
1590
1488
                    pb.finished()
1591
1489
                    basis_tree.unlock()
1611
1509
    @needs_write_lock
1612
1510
    def put_file_bytes_non_atomic(self, file_id, bytes):
1613
1511
        """See MutableTree.put_file_bytes_non_atomic."""
 
1512
        file_id = osutils.safe_file_id(file_id)
1614
1513
        stream = file(self.id2abspath(file_id), 'wb')
1615
1514
        try:
1616
1515
            stream.write(bytes)
1669
1568
        if ignoreset is not None:
1670
1569
            return ignoreset
1671
1570
 
1672
 
        ignore_globs = set()
 
1571
        ignore_globs = set(bzrlib.DEFAULT_IGNORE)
1673
1572
        ignore_globs.update(ignores.get_runtime_ignores())
1674
1573
        ignore_globs.update(ignores.get_user_ignores())
1675
1574
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1702
1601
    def kind(self, file_id):
1703
1602
        return file_kind(self.id2abspath(file_id))
1704
1603
 
1705
 
    def stored_kind(self, file_id):
1706
 
        """See Tree.stored_kind"""
1707
 
        return self.inventory[file_id].kind
1708
 
 
1709
1604
    def _comparison_data(self, entry, path):
1710
1605
        abspath = self.abspath(path)
1711
1606
        try:
1742
1637
    @needs_read_lock
1743
1638
    def _last_revision(self):
1744
1639
        """helper for get_parent_ids."""
1745
 
        return _mod_revision.ensure_null(self.branch.last_revision())
 
1640
        return self.branch.last_revision()
1746
1641
 
1747
1642
    def is_locked(self):
1748
1643
        return self._control_files.is_locked()
1799
1694
    @needs_tree_write_lock
1800
1695
    def set_last_revision(self, new_revision):
1801
1696
        """Change the last revision in the working tree."""
 
1697
        new_revision = osutils.safe_revision_id(new_revision)
1802
1698
        if self._change_last_revision(new_revision):
1803
1699
            self._cache_basis_inventory(new_revision)
1804
1700
 
1827
1723
 
1828
1724
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1829
1725
        """Create the text that will be saved in basis-inventory"""
1830
 
        inventory.revision_id = revision_id
 
1726
        # TODO: jam 20070209 This should be redundant, as the revision_id
 
1727
        #       as all callers should have already converted the revision_id to
 
1728
        #       utf8
 
1729
        inventory.revision_id = osutils.safe_revision_id(revision_id)
1831
1730
        return xml7.serializer_v7.write_inventory_to_string(inventory)
1832
1731
 
1833
1732
    def _cache_basis_inventory(self, new_revision):
1889
1788
        :force: Delete files and directories, even if they are changed and
1890
1789
            even if the directories are not empty.
1891
1790
        """
 
1791
        ## TODO: Normalize names
 
1792
 
1892
1793
        if isinstance(files, basestring):
1893
1794
            files = [files]
1894
1795
 
1895
1796
        inv_delta = []
1896
1797
 
1897
1798
        new_files=set()
1898
 
        unknown_nested_files=set()
 
1799
        unknown_files_in_directory=set()
1899
1800
 
1900
1801
        def recurse_directory_to_add_files(directory):
1901
 
            # Recurse directory and add all files
 
1802
            # recurse directory and add all files
1902
1803
            # so we can check if they have changed.
1903
1804
            for parent_info, file_infos in\
1904
1805
                osutils.walkdirs(self.abspath(directory),
1905
1806
                    directory):
1906
1807
                for relpath, basename, kind, lstat, abspath in file_infos:
1907
 
                    # Is it versioned or ignored?
1908
 
                    if self.path2id(relpath) or self.is_ignored(relpath):
1909
 
                        # Add nested content for deletion.
1910
 
                        new_files.add(relpath)
1911
 
                    else:
1912
 
                        # Files which are not versioned and not ignored
1913
 
                        # should be treated as unknown.
1914
 
                        unknown_nested_files.add((relpath, None, kind))
 
1808
                    if kind == 'file':
 
1809
                        if self.path2id(relpath): #is it versioned?
 
1810
                            new_files.add(relpath)
 
1811
                        else:
 
1812
                            unknown_files_in_directory.add(
 
1813
                                (relpath, None, kind))
1915
1814
 
1916
1815
        for filename in files:
1917
1816
            # Get file name into canonical form.
1921
1820
                new_files.add(filename)
1922
1821
                if osutils.isdir(abspath):
1923
1822
                    recurse_directory_to_add_files(filename)
1924
 
 
1925
 
        files = list(new_files)
 
1823
        files = [f for f in new_files]
1926
1824
 
1927
1825
        if len(files) == 0:
1928
1826
            return # nothing to do
1929
1827
 
1930
1828
        # Sort needed to first handle directory content before the directory
1931
1829
        files.sort(reverse=True)
1932
 
 
1933
 
        # Bail out if we are going to delete files we shouldn't
1934
1830
        if not keep_files and not force:
1935
 
            has_changed_files = len(unknown_nested_files) > 0
 
1831
            has_changed_files = len(unknown_files_in_directory) > 0
1936
1832
            if not has_changed_files:
1937
1833
                for (file_id, path, content_change, versioned, parent_id, name,
1938
 
                     kind, executable) in self.iter_changes(self.basis_tree(),
 
1834
                     kind, executable) in self._iter_changes(self.basis_tree(),
1939
1835
                         include_unchanged=True, require_versioned=False,
1940
1836
                         want_unversioned=True, specific_files=files):
1941
 
                    if versioned == (False, False):
1942
 
                        # The record is unknown ...
1943
 
                        if not self.is_ignored(path[1]):
1944
 
                            # ... but not ignored
1945
 
                            has_changed_files = True
1946
 
                            break
1947
 
                    elif content_change and (kind[1] != None):
1948
 
                        # Versioned and changed, but not deleted
 
1837
                    # check if it's unknown OR changed but not deleted:
 
1838
                    if (versioned == (False, False)
 
1839
                        or (content_change and kind[1] != None)):
1949
1840
                        has_changed_files = True
1950
1841
                        break
1951
1842
 
1952
1843
            if has_changed_files:
1953
 
                # Make delta show ALL applicable changes in error message.
 
1844
                # make delta to show ALL applicable changes in error message.
1954
1845
                tree_delta = self.changes_from(self.basis_tree(),
1955
 
                    require_versioned=False, want_unversioned=True,
1956
1846
                    specific_files=files)
1957
 
                for unknown_file in unknown_nested_files:
1958
 
                    if unknown_file not in tree_delta.unversioned:
1959
 
                        tree_delta.unversioned.extend((unknown_file,))
 
1847
                for unknown_file in unknown_files_in_directory:
 
1848
                    tree_delta.unversioned.extend((unknown_file,))
1960
1849
                raise errors.BzrRemoveChangedFilesError(tree_delta)
1961
1850
 
1962
 
        # Build inv_delta and delete files where applicaple,
1963
 
        # do this before any modifications to inventory.
 
1851
        # do this before any modifications
1964
1852
        for f in files:
1965
1853
            fid = self.path2id(f)
1966
 
            message = None
 
1854
            message=None
1967
1855
            if not fid:
1968
 
                message = "%s is not versioned." % (f,)
 
1856
                message="%s is not versioned." % (f,)
1969
1857
            else:
1970
1858
                if verbose:
1971
1859
                    # having removed it, it must be either ignored or unknown
1975
1863
                        new_status = '?'
1976
1864
                    textui.show_status(new_status, self.kind(fid), f,
1977
1865
                                       to_file=to_file)
1978
 
                # Unversion file
 
1866
                # unversion file
1979
1867
                inv_delta.append((f, None, fid, None))
1980
 
                message = "removed %s" % (f,)
 
1868
                message="removed %s" % (f,)
1981
1869
 
1982
1870
            if not keep_files:
1983
1871
                abs_path = self.abspath(f)
1984
1872
                if osutils.lexists(abs_path):
1985
1873
                    if (osutils.isdir(abs_path) and
1986
1874
                        len(os.listdir(abs_path)) > 0):
1987
 
                        if force:
1988
 
                            osutils.rmtree(abs_path)
1989
 
                        else:
1990
 
                            message = "%s is not an empty directory "\
1991
 
                                "and won't be deleted." % (f,)
 
1875
                        message="%s is not empty directory "\
 
1876
                            "and won't be deleted." % (f,)
1992
1877
                    else:
1993
1878
                        osutils.delete_any(abs_path)
1994
 
                        message = "deleted %s" % (f,)
 
1879
                        message="deleted %s" % (f,)
1995
1880
                elif message is not None:
1996
 
                    # Only care if we haven't done anything yet.
1997
 
                    message = "%s does not exist." % (f,)
 
1881
                    # only care if we haven't done anything yet.
 
1882
                    message="%s does not exist." % (f,)
1998
1883
 
1999
 
            # Print only one message (if any) per file.
 
1884
            # print only one message (if any) per file.
2000
1885
            if message is not None:
2001
1886
                note(message)
2002
1887
        self.apply_inventory_delta(inv_delta)
2003
1888
 
2004
1889
    @needs_tree_write_lock
2005
 
    def revert(self, filenames=None, old_tree=None, backups=True,
 
1890
    def revert(self, filenames, old_tree=None, backups=True, 
2006
1891
               pb=DummyProgress(), report_changes=False):
2007
1892
        from bzrlib.conflicts import resolve
2008
 
        if filenames == []:
2009
 
            filenames = None
2010
 
            symbol_versioning.warn('Using [] to revert all files is deprecated'
2011
 
                ' as of bzr 0.91.  Please use None (the default) instead.',
2012
 
                DeprecationWarning, stacklevel=2)
2013
1893
        if old_tree is None:
2014
 
            basis_tree = self.basis_tree()
2015
 
            basis_tree.lock_read()
2016
 
            old_tree = basis_tree
 
1894
            old_tree = self.basis_tree()
 
1895
        conflicts = transform.revert(self, old_tree, filenames, backups, pb,
 
1896
                                     report_changes)
 
1897
        if not len(filenames):
 
1898
            self.set_parent_ids(self.get_parent_ids()[:1])
 
1899
            resolve(self)
2017
1900
        else:
2018
 
            basis_tree = None
2019
 
        try:
2020
 
            conflicts = transform.revert(self, old_tree, filenames, backups, pb,
2021
 
                                         report_changes)
2022
 
            if filenames is None and len(self.get_parent_ids()) > 1:
2023
 
                parent_trees = []
2024
 
                last_revision = self.last_revision()
2025
 
                if last_revision != NULL_REVISION:
2026
 
                    if basis_tree is None:
2027
 
                        basis_tree = self.basis_tree()
2028
 
                        basis_tree.lock_read()
2029
 
                    parent_trees.append((last_revision, basis_tree))
2030
 
                self.set_parent_trees(parent_trees)
2031
 
                resolve(self)
2032
 
            else:
2033
 
                resolve(self, filenames, ignore_misses=True, recursive=True)
2034
 
        finally:
2035
 
            if basis_tree is not None:
2036
 
                basis_tree.unlock()
 
1901
            resolve(self, filenames, ignore_misses=True)
2037
1902
        return conflicts
2038
1903
 
2039
1904
    def revision_tree(self, revision_id):
2136
2001
        """
2137
2002
        raise NotImplementedError(self.unlock)
2138
2003
 
2139
 
    def update(self, change_reporter=None, possible_transports=None):
 
2004
    def update(self, change_reporter=None):
2140
2005
        """Update a working tree along its branch.
2141
2006
 
2142
2007
        This will update the branch if its bound too, which means we have
2161
2026
          basis.
2162
2027
        - Do a 'normal' merge of the old branch basis if it is relevant.
2163
2028
        """
2164
 
        if self.branch.get_master_branch(possible_transports) is not None:
 
2029
        if self.branch.get_master_branch() is not None:
2165
2030
            self.lock_write()
2166
2031
            update_branch = True
2167
2032
        else:
2169
2034
            update_branch = False
2170
2035
        try:
2171
2036
            if update_branch:
2172
 
                old_tip = self.branch.update(possible_transports)
 
2037
                old_tip = self.branch.update()
2173
2038
            else:
2174
2039
                old_tip = None
2175
2040
            return self._update_tree(old_tip, change_reporter)
2205
2070
            try:
2206
2071
                to_tree = self.branch.basis_tree()
2207
2072
                if basis.inventory.root is None:
2208
 
                    self.set_root_id(to_tree.get_root_id())
 
2073
                    self.set_root_id(to_tree.inventory.root.file_id)
2209
2074
                    self.flush()
2210
2075
                result += merge.merge_inner(
2211
2076
                                      self.branch,
2353
2218
            current_inv = None
2354
2219
            inv_finished = True
2355
2220
        while not inv_finished or not disk_finished:
2356
 
            if current_disk:
2357
 
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2358
 
                    cur_disk_dir_content) = current_disk
2359
 
            else:
2360
 
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2361
 
                    cur_disk_dir_content) = ((None, None), None)
2362
2221
            if not disk_finished:
2363
2222
                # strip out .bzr dirs
2364
 
                if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
2365
 
                    len(cur_disk_dir_content) > 0):
2366
 
                    # osutils.walkdirs can be made nicer -
 
2223
                if current_disk[0][1][top_strip_len:] == '':
 
2224
                    # osutils.walkdirs can be made nicer - 
2367
2225
                    # yield the path-from-prefix rather than the pathjoined
2368
2226
                    # value.
2369
 
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
2370
 
                        ('.bzr', '.bzr'))
2371
 
                    if cur_disk_dir_content[bzrdir_loc][0] == '.bzr':
 
2227
                    bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
 
2228
                    if current_disk[1][bzrdir_loc][0] == '.bzr':
2372
2229
                        # we dont yield the contents of, or, .bzr itself.
2373
 
                        del cur_disk_dir_content[bzrdir_loc]
 
2230
                        del current_disk[1][bzrdir_loc]
2374
2231
            if inv_finished:
2375
2232
                # everything is unknown
2376
2233
                direction = 1
2378
2235
                # everything is missing
2379
2236
                direction = -1
2380
2237
            else:
2381
 
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
 
2238
                direction = cmp(current_inv[0][0], current_disk[0][0])
2382
2239
            if direction > 0:
2383
2240
                # disk is before inventory - unknown
2384
2241
                dirblock = [(relpath, basename, kind, stat, None, None) for
2385
 
                    relpath, basename, kind, stat, top_path in
2386
 
                    cur_disk_dir_content]
2387
 
                yield (cur_disk_dir_relpath, None), dirblock
 
2242
                    relpath, basename, kind, stat, top_path in current_disk[1]]
 
2243
                yield (current_disk[0][0], None), dirblock
2388
2244
                try:
2389
2245
                    current_disk = disk_iterator.next()
2390
2246
                except StopIteration:
2392
2248
            elif direction < 0:
2393
2249
                # inventory is before disk - missing.
2394
2250
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2395
 
                    for relpath, basename, dkind, stat, fileid, kind in
 
2251
                    for relpath, basename, dkind, stat, fileid, kind in 
2396
2252
                    current_inv[1]]
2397
2253
                yield (current_inv[0][0], current_inv[0][1]), dirblock
2398
2254
                try:
2404
2260
                # merge the inventory and disk data together
2405
2261
                dirblock = []
2406
2262
                for relpath, subiterator in itertools.groupby(sorted(
2407
 
                    current_inv[1] + cur_disk_dir_content,
2408
 
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
 
2263
                    current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2409
2264
                    path_elements = list(subiterator)
2410
2265
                    if len(path_elements) == 2:
2411
2266
                        inv_row, disk_row = path_elements
2551
2406
            raise
2552
2407
 
2553
2408
    def unlock(self):
2554
 
        # do non-implementation specific cleanup
2555
 
        self._cleanup()
2556
 
 
2557
2409
        # we share control files:
2558
2410
        if self._control_files._lock_count == 3:
2559
2411
            # _inventory_is_modified is always False during a read lock.
2582
2434
    def _last_revision(self):
2583
2435
        """See Mutable.last_revision."""
2584
2436
        try:
2585
 
            return self._control_files.get('last-revision').read()
 
2437
            return osutils.safe_revision_id(
 
2438
                        self._control_files.get('last-revision').read())
2586
2439
        except errors.NoSuchFile:
2587
 
            return _mod_revision.NULL_REVISION
 
2440
            return None
2588
2441
 
2589
2442
    def _change_last_revision(self, revision_id):
2590
2443
        """See WorkingTree._change_last_revision."""
2624
2477
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2625
2478
 
2626
2479
    def unlock(self):
2627
 
        # do non-implementation specific cleanup
2628
 
        self._cleanup()
2629
2480
        if self._control_files._lock_count == 1:
2630
2481
            # _inventory_is_modified is always False during a read lock.
2631
2482
            if self._inventory_is_modified:
2698
2549
        except errors.NoSuchFile:
2699
2550
            raise errors.NoWorkingTree(base=transport.base)
2700
2551
        except KeyError:
2701
 
            raise errors.UnknownFormatError(format=format_string,
2702
 
                                            kind="working tree")
 
2552
            raise errors.UnknownFormatError(format=format_string)
2703
2553
 
2704
2554
    def __eq__(self, other):
2705
2555
        return self.__class__ is other.__class__
2764
2614
        """
2765
2615
        sio = StringIO()
2766
2616
        inv = Inventory()
2767
 
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
 
2617
        xml5.serializer_v5.write_inventory(inv, sio)
2768
2618
        sio.seek(0)
2769
2619
        control_files.put('inventory', sio)
2770
2620
 
2771
2621
        control_files.put_bytes('pending-merges', '')
2772
2622
        
2773
2623
 
2774
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2775
 
                   accelerator_tree=None, hardlink=False):
 
2624
    def initialize(self, a_bzrdir, revision_id=None):
2776
2625
        """See WorkingTreeFormat.initialize()."""
2777
2626
        if not isinstance(a_bzrdir.transport, LocalTransport):
2778
2627
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2779
 
        if from_branch is not None:
2780
 
            branch = from_branch
2781
 
        else:
2782
 
            branch = a_bzrdir.open_branch()
 
2628
        branch = a_bzrdir.open_branch()
2783
2629
        if revision_id is None:
2784
2630
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2631
        else:
 
2632
            revision_id = osutils.safe_revision_id(revision_id)
2785
2633
        branch.lock_write()
2786
2634
        try:
2787
2635
            branch.generate_revision_history(revision_id)
2796
2644
                         _bzrdir=a_bzrdir)
2797
2645
        basis_tree = branch.repository.revision_tree(revision_id)
2798
2646
        if basis_tree.inventory.root is not None:
2799
 
            wt.set_root_id(basis_tree.get_root_id())
 
2647
            wt.set_root_id(basis_tree.inventory.root.file_id)
2800
2648
        # set the parent list and cache the basis tree.
2801
2649
        if _mod_revision.is_null(revision_id):
2802
2650
            parent_trees = []
2864
2712
        return LockableFiles(transport, self._lock_file_name, 
2865
2713
                             self._lock_class)
2866
2714
 
2867
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2868
 
                   accelerator_tree=None, hardlink=False):
 
2715
    def initialize(self, a_bzrdir, revision_id=None):
2869
2716
        """See WorkingTreeFormat.initialize().
2870
2717
        
2871
 
        :param revision_id: if supplied, create a working tree at a different
2872
 
            revision than the branch is at.
2873
 
        :param accelerator_tree: A tree which can be used for retrieving file
2874
 
            contents more quickly than the revision tree, i.e. a workingtree.
2875
 
            The revision tree will be used for cases where accelerator_tree's
2876
 
            content is different.
2877
 
        :param hardlink: If true, hard-link files from accelerator_tree,
2878
 
            where possible.
 
2718
        revision_id allows creating a working tree at a different
 
2719
        revision than the branch is at.
2879
2720
        """
2880
2721
        if not isinstance(a_bzrdir.transport, LocalTransport):
2881
2722
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2884
2725
        control_files.create_lock()
2885
2726
        control_files.lock_write()
2886
2727
        control_files.put_utf8('format', self.get_format_string())
2887
 
        if from_branch is not None:
2888
 
            branch = from_branch
2889
 
        else:
2890
 
            branch = a_bzrdir.open_branch()
 
2728
        branch = a_bzrdir.open_branch()
2891
2729
        if revision_id is None:
2892
2730
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2731
        else:
 
2732
            revision_id = osutils.safe_revision_id(revision_id)
2893
2733
        # WorkingTree3 can handle an inventory which has a unique root id.
2894
2734
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2895
2735
        # those trees. And because there isn't a format bump inbetween, we
2908
2748
            basis_tree = branch.repository.revision_tree(revision_id)
2909
2749
            # only set an explicit root id if there is one to set.
2910
2750
            if basis_tree.inventory.root is not None:
2911
 
                wt.set_root_id(basis_tree.get_root_id())
 
2751
                wt.set_root_id(basis_tree.inventory.root.file_id)
2912
2752
            if revision_id == NULL_REVISION:
2913
2753
                wt.set_parent_trees([])
2914
2754
            else: