~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Vincent Ladeuil
  • Date: 2007-11-24 14:20:59 UTC
  • mto: (3928.1.1 bzr.integration)
  • mto: This revision was merged to the branch mainline in revision 3929.
  • Revision ID: v.ladeuil+lp@free.fr-20071124142059-2114qtsgfdv8g9p1
Ssl files needed for the test https server.

* bzrlib/tests/ssl_certs/create_ssls.py: 
Script to create the ssl keys and certificates.

* bzrlib/tests/ssl_certs/server.crt: 
Server certificate signed by the certificate authority.

* bzrlib/tests/ssl_certs/server.csr: 
Server certificate signing request.

* bzrlib/tests/ssl_certs/server_without_pass.key: 
Server key usable without password.

* bzrlib/tests/ssl_certs/server_with_pass.key: 
Server key.

* bzrlib/tests/ssl_certs/ca.key: 
Certificate authority private key.

* bzrlib/tests/ssl_certs/ca.crt: 
Certificate authority certificate.

* bzrlib/tests/ssl_certs/__init__.py: 
Provide access to ssl files (keys and certificates). 

Show diffs side-by-side

added added

removed removed

Lines of Context:
111
111
        deprecated_method,
112
112
        deprecated_function,
113
113
        DEPRECATED_PARAMETER,
 
114
        zero_eight,
 
115
        zero_eleven,
 
116
        zero_thirteen,
114
117
        )
115
118
 
116
119
 
120
123
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
121
124
 
122
125
 
 
126
@deprecated_function(zero_thirteen)
 
127
def gen_file_id(name):
 
128
    """Return new file id for the basename 'name'.
 
129
 
 
130
    Use bzrlib.generate_ids.gen_file_id() instead
 
131
    """
 
132
    return generate_ids.gen_file_id(name)
 
133
 
 
134
 
 
135
@deprecated_function(zero_thirteen)
 
136
def gen_root_id():
 
137
    """Return a new tree-root file id.
 
138
 
 
139
    This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
 
140
    """
 
141
    return generate_ids.gen_root_id()
 
142
 
 
143
 
123
144
class TreeEntry(object):
124
145
    """An entry that implements the minimum interface used by commands.
125
146
 
201
222
        if not _internal:
202
223
            raise errors.BzrError("Please use bzrdir.open_workingtree or "
203
224
                "WorkingTree.open() to obtain a WorkingTree.")
 
225
        assert isinstance(basedir, basestring), \
 
226
            "base directory %r is not a string" % basedir
204
227
        basedir = safe_unicode(basedir)
205
228
        mutter("opening working tree %r", basedir)
206
229
        if deprecated_passed(branch):
214
237
            self._control_files = self.branch.control_files
215
238
        else:
216
239
            # assume all other formats have their own control files.
 
240
            assert isinstance(_control_files, LockableFiles), \
 
241
                    "_control_files must be a LockableFiles, not %r" \
 
242
                    % _control_files
217
243
            self._control_files = _control_files
218
 
        self._transport = self._control_files._transport
219
244
        # update the whole cache up front and write to disk if anything changed;
220
245
        # in the future we might want to do this more selectively
221
246
        # two possible ways offer themselves : in self._unlock, write the cache
225
250
        wt_trans = self.bzrdir.get_workingtree_transport(None)
226
251
        cache_filename = wt_trans.local_abspath('stat-cache')
227
252
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
228
 
            self.bzrdir._get_file_mode())
 
253
                                              self._control_files._file_mode)
229
254
        hc = self._hashcache
230
255
        hc.read()
231
256
        # is this scan needed ? it makes things kinda slow.
245
270
            # the Format factory and creation methods that are
246
271
            # permitted to do this.
247
272
            self._set_inventory(_inventory, dirty=False)
248
 
        self._detect_case_handling()
249
 
        self._rules_searcher = None
250
 
 
251
 
    def _detect_case_handling(self):
252
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
253
 
        try:
254
 
            wt_trans.stat("FoRMaT")
255
 
        except errors.NoSuchFile:
256
 
            self.case_sensitive = True
257
 
        else:
258
 
            self.case_sensitive = False
259
 
 
260
 
        self._setup_directory_is_tree_reference()
261
273
 
262
274
    branch = property(
263
275
        fget=lambda self: self._branch,
294
306
            False then the inventory is the same as that on disk and any
295
307
            serialisation would be unneeded overhead.
296
308
        """
 
309
        assert inv.root is not None
297
310
        self._inventory = inv
298
311
        self._inventory_is_modified = dirty
299
312
 
334
347
        """
335
348
        return WorkingTree.open(path, _unsupported=True)
336
349
 
337
 
    @staticmethod
338
 
    def find_trees(location):
339
 
        def list_current(transport):
340
 
            return [d for d in transport.list_dir('') if d != '.bzr']
341
 
        def evaluate(bzrdir):
342
 
            try:
343
 
                tree = bzrdir.open_workingtree()
344
 
            except errors.NoWorkingTree:
345
 
                return True, None
346
 
            else:
347
 
                return True, tree
348
 
        transport = get_transport(location)
349
 
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
350
 
                                              list_current=list_current)
351
 
        return [t for t in iterator if t is not None]
352
 
 
353
350
    # should be deprecated - this is slow and in any case treating them as a
354
351
    # container is (we now know) bad style -- mbp 20070302
355
352
    ## @deprecated_method(zero_fifteen)
364
361
            if osutils.lexists(self.abspath(path)):
365
362
                yield ie.file_id
366
363
 
367
 
    def all_file_ids(self):
368
 
        """See Tree.iter_all_file_ids"""
369
 
        return set(self.inventory)
370
 
 
371
364
    def __repr__(self):
372
365
        return "<%s of %s>" % (self.__class__.__name__,
373
366
                               getattr(self, 'basedir', None))
397
390
        # at this point ?
398
391
        try:
399
392
            return self.branch.repository.revision_tree(revision_id)
400
 
        except (errors.RevisionNotPresent, errors.NoSuchRevision):
 
393
        except errors.RevisionNotPresent:
401
394
            # the basis tree *may* be a ghost or a low level error may have
402
395
            # occured. If the revision is present, its a problem, if its not
403
396
            # its a ghost.
409
402
    def _cleanup(self):
410
403
        self._flush_ignore_list_cache()
411
404
 
 
405
    @staticmethod
 
406
    @deprecated_method(zero_eight)
 
407
    def create(branch, directory):
 
408
        """Create a workingtree for branch at directory.
 
409
 
 
410
        If existing_directory already exists it must have a .bzr directory.
 
411
        If it does not exist, it will be created.
 
412
 
 
413
        This returns a new WorkingTree object for the new checkout.
 
414
 
 
415
        TODO FIXME RBC 20060124 when we have checkout formats in place this
 
416
        should accept an optional revisionid to checkout [and reject this if
 
417
        checking out into the same dir as a pre-checkout-aware branch format.]
 
418
 
 
419
        XXX: When BzrDir is present, these should be created through that 
 
420
        interface instead.
 
421
        """
 
422
        warnings.warn('delete WorkingTree.create', stacklevel=3)
 
423
        transport = get_transport(directory)
 
424
        if branch.bzrdir.root_transport.base == transport.base:
 
425
            # same dir 
 
426
            return branch.bzrdir.create_workingtree()
 
427
        # different directory, 
 
428
        # create a branch reference
 
429
        # and now a working tree.
 
430
        raise NotImplementedError
 
431
 
 
432
    @staticmethod
 
433
    @deprecated_method(zero_eight)
 
434
    def create_standalone(directory):
 
435
        """Create a checkout and a branch and a repo at directory.
 
436
 
 
437
        Directory must exist and be empty.
 
438
 
 
439
        please use BzrDir.create_standalone_workingtree
 
440
        """
 
441
        return bzrdir.BzrDir.create_standalone_workingtree(directory)
 
442
 
412
443
    def relpath(self, path):
413
444
        """Return the local path portion from a given path.
414
445
        
445
476
        basis = self.basis_tree()
446
477
        basis.lock_read()
447
478
        try:
448
 
            changes = self.iter_changes(basis, True, [self.id2path(file_id)],
 
479
            changes = self._iter_changes(basis, True, [self.id2path(file_id)],
449
480
                require_versioned=True).next()
450
481
            changed_content, kind = changes[2], changes[6]
451
482
            if not changed_content:
487
518
        else:
488
519
            parents = [last_rev]
489
520
        try:
490
 
            merges_file = self._transport.get('pending-merges')
 
521
            merges_file = self._control_files.get('pending-merges')
491
522
        except errors.NoSuchFile:
492
523
            pass
493
524
        else:
555
586
    __contains__ = has_id
556
587
 
557
588
    def get_file_size(self, file_id):
558
 
        """See Tree.get_file_size"""
559
 
        try:
560
 
            return os.path.getsize(self.id2abspath(file_id))
561
 
        except OSError, e:
562
 
            if e.errno != errno.ENOENT:
563
 
                raise
564
 
            else:
565
 
                return None
 
589
        return os.path.getsize(self.id2abspath(file_id))
566
590
 
567
591
    @needs_read_lock
568
592
    def get_file_sha1(self, file_id, path=None, stat_value=None):
608
632
        # function - they should be part of lock_write and unlock.
609
633
        inv = self.inventory
610
634
        for f, file_id, kind in zip(files, ids, kinds):
 
635
            assert kind is not None
611
636
            if file_id is None:
612
637
                inv.add_path(f, kind=kind)
613
638
            else:
708
733
        else:
709
734
            return (kind, None, None, None)
710
735
 
 
736
    @deprecated_method(zero_eleven)
 
737
    @needs_read_lock
 
738
    def pending_merges(self):
 
739
        """Return a list of pending merges.
 
740
 
 
741
        These are revisions that have been merged into the working
 
742
        directory but not yet committed.
 
743
 
 
744
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
 
745
        instead - which is available on all tree objects.
 
746
        """
 
747
        return self.get_parent_ids()[1:]
 
748
 
711
749
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
712
750
        """Common ghost checking functionality from set_parent_*.
713
751
 
722
760
 
723
761
    def _set_merges_from_parent_ids(self, parent_ids):
724
762
        merges = parent_ids[1:]
725
 
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
726
 
            mode=self._control_files._file_mode)
727
 
 
728
 
    def _filter_parent_ids_by_ancestry(self, revision_ids):
729
 
        """Check that all merged revisions are proper 'heads'.
730
 
 
731
 
        This will always return the first revision_id, and any merged revisions
732
 
        which are 
733
 
        """
734
 
        if len(revision_ids) == 0:
735
 
            return revision_ids
736
 
        graph = self.branch.repository.get_graph()
737
 
        heads = graph.heads(revision_ids)
738
 
        new_revision_ids = revision_ids[:1]
739
 
        for revision_id in revision_ids[1:]:
740
 
            if revision_id in heads and revision_id not in new_revision_ids:
741
 
                new_revision_ids.append(revision_id)
742
 
        if new_revision_ids != revision_ids:
743
 
            trace.mutter('requested to set revision_ids = %s,'
744
 
                         ' but filtered to %s', revision_ids, new_revision_ids)
745
 
        return new_revision_ids
 
763
        self._control_files.put_bytes('pending-merges', '\n'.join(merges))
746
764
 
747
765
    @needs_tree_write_lock
748
766
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
762
780
        for revision_id in revision_ids:
763
781
            _mod_revision.check_not_reserved_id(revision_id)
764
782
 
765
 
        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
766
 
 
767
783
        if len(revision_ids) > 0:
768
784
            self.set_last_revision(revision_ids[0])
769
785
        else:
781
797
        self._check_parents_for_ghosts(parent_ids,
782
798
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
783
799
 
784
 
        parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
785
 
 
786
800
        if len(parent_ids) == 0:
787
801
            leftmost_parent_id = _mod_revision.NULL_REVISION
788
802
            leftmost_parent_tree = None
828
842
    def _put_rio(self, filename, stanzas, header):
829
843
        self._must_be_locked()
830
844
        my_file = rio_file(stanzas, header)
831
 
        self._transport.put_file(filename, my_file,
832
 
            mode=self._control_files._file_mode)
 
845
        self._control_files.put(filename, my_file)
833
846
 
834
847
    @needs_write_lock # because merge pulls data into the branch.
835
848
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
894
907
        still in the working inventory and have that text hash.
895
908
        """
896
909
        try:
897
 
            hashfile = self._transport.get('merge-hashes')
 
910
            hashfile = self._control_files.get('merge-hashes')
898
911
        except errors.NoSuchFile:
899
912
            return {}
900
913
        merge_hashes = {}
967
980
            other_tree.unlock()
968
981
        other_tree.bzrdir.retire_bzrdir()
969
982
 
970
 
    def _setup_directory_is_tree_reference(self):
971
 
        if self._branch.repository._format.supports_tree_reference:
972
 
            self._directory_is_tree_reference = \
973
 
                self._directory_may_be_tree_reference
974
 
        else:
975
 
            self._directory_is_tree_reference = \
976
 
                self._directory_is_never_tree_reference
977
 
 
978
 
    def _directory_is_never_tree_reference(self, relpath):
979
 
        return False
980
 
 
981
 
    def _directory_may_be_tree_reference(self, relpath):
 
983
    def _directory_is_tree_reference(self, relpath):
982
984
        # as a special case, if a directory contains control files then 
983
985
        # it's a tree reference, except that the root of the tree is not
984
986
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1011
1013
        sub_path = self.id2path(file_id)
1012
1014
        branch_transport = mkdirs(sub_path)
1013
1015
        if format is None:
1014
 
            format = self.bzrdir.cloning_metadir()
 
1016
            format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
1015
1017
        branch_transport.ensure_base()
1016
1018
        branch_bzrdir = format.initialize_on_transport(branch_transport)
1017
1019
        try:
1018
1020
            repo = branch_bzrdir.find_repository()
1019
1021
        except errors.NoRepositoryPresent:
1020
1022
            repo = branch_bzrdir.create_repository()
1021
 
        if not repo.supports_rich_root():
1022
 
            raise errors.RootNotRich()
 
1023
            assert repo.supports_rich_root()
 
1024
        else:
 
1025
            if not repo.supports_rich_root():
 
1026
                raise errors.RootNotRich()
1023
1027
        new_branch = branch_bzrdir.create_branch()
1024
1028
        new_branch.pull(self.branch)
1025
1029
        for parent_id in self.get_parent_ids():
1057
1061
        sio = StringIO()
1058
1062
        self._serialize(self._inventory, sio)
1059
1063
        sio.seek(0)
1060
 
        self._transport.put_file('inventory', sio,
1061
 
            mode=self._control_files._file_mode)
 
1064
        self._control_files.put('inventory', sio)
1062
1065
        self._inventory_is_modified = False
1063
1066
 
1064
1067
    def _kind(self, relpath):
1225
1228
                                       DeprecationWarning)
1226
1229
 
1227
1230
        # check destination directory
1228
 
        if isinstance(from_paths, basestring):
1229
 
            raise ValueError()
 
1231
        assert not isinstance(from_paths, basestring)
1230
1232
        inv = self.inventory
1231
1233
        to_abs = self.abspath(to_dir)
1232
1234
        if not isdir(to_abs):
1316
1318
                only_change_inv = True
1317
1319
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1318
1320
                only_change_inv = False
1319
 
            elif (sys.platform == 'win32'
1320
 
                and from_rel.lower() == to_rel.lower()
1321
 
                and self.has_filename(from_rel)):
1322
 
                only_change_inv = False
1323
1321
            else:
1324
1322
                # something is wrong, so lets determine what exactly
1325
1323
                if not self.has_filename(from_rel) and \
1496
1494
            # - RBC 20060907
1497
1495
            self._write_inventory(self._inventory)
1498
1496
    
 
1497
    @deprecated_method(zero_eight)
 
1498
    def iter_conflicts(self):
 
1499
        """List all files in the tree that have text or content conflicts.
 
1500
        DEPRECATED.  Use conflicts instead."""
 
1501
        return self._iter_conflicts()
 
1502
 
1499
1503
    def _iter_conflicts(self):
1500
1504
        conflicted = set()
1501
1505
        for info in self.list_files():
1592
1596
                if subf == '.bzr':
1593
1597
                    continue
1594
1598
                if subf not in dir_entry.children:
1595
 
                    try:
1596
 
                        (subf_norm,
1597
 
                         can_access) = osutils.normalized_filename(subf)
1598
 
                    except UnicodeDecodeError:
1599
 
                        path_os_enc = path.encode(osutils._fs_enc)
1600
 
                        relpath = path_os_enc + '/' + subf
1601
 
                        raise errors.BadFilenameEncoding(relpath,
1602
 
                                                         osutils._fs_enc)
 
1599
                    subf_norm, can_access = osutils.normalized_filename(subf)
1603
1600
                    if subf_norm != subf and can_access:
1604
1601
                        if subf_norm not in dir_entry.children:
1605
1602
                            fl.append(subf_norm)
1660
1657
    def kind(self, file_id):
1661
1658
        return file_kind(self.id2abspath(file_id))
1662
1659
 
1663
 
    def stored_kind(self, file_id):
1664
 
        """See Tree.stored_kind"""
1665
 
        return self.inventory[file_id].kind
1666
 
 
1667
1660
    def _comparison_data(self, entry, path):
1668
1661
        abspath = self.abspath(path)
1669
1662
        try:
1751
1744
    def _reset_data(self):
1752
1745
        """Reset transient data that cannot be revalidated."""
1753
1746
        self._inventory_is_modified = False
1754
 
        result = self._deserialize(self._transport.get('inventory'))
 
1747
        result = self._deserialize(self._control_files.get('inventory'))
1755
1748
        self._set_inventory(result, dirty=False)
1756
1749
 
1757
1750
    @needs_tree_write_lock
1778
1771
 
1779
1772
    def _write_basis_inventory(self, xml):
1780
1773
        """Write the basis inventory XML to the basis-inventory file"""
 
1774
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
1781
1775
        path = self._basis_inventory_name()
1782
1776
        sio = StringIO(xml)
1783
 
        self._transport.put_file(path, sio,
1784
 
            mode=self._control_files._file_mode)
 
1777
        self._control_files.put(path, sio)
1785
1778
 
1786
1779
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1787
1780
        """Create the text that will be saved in basis-inventory"""
1818
1811
    def read_basis_inventory(self):
1819
1812
        """Read the cached basis inventory."""
1820
1813
        path = self._basis_inventory_name()
1821
 
        return self._transport.get_bytes(path)
 
1814
        return self._control_files.get(path).read()
1822
1815
        
1823
1816
    @needs_read_lock
1824
1817
    def read_working_inventory(self):
1833
1826
        # binary.
1834
1827
        if self._inventory_is_modified:
1835
1828
            raise errors.InventoryModified(self)
1836
 
        result = self._deserialize(self._transport.get('inventory'))
 
1829
        result = self._deserialize(self._control_files.get('inventory'))
1837
1830
        self._set_inventory(result, dirty=False)
1838
1831
        return result
1839
1832
 
1859
1852
            # Recurse directory and add all files
1860
1853
            # so we can check if they have changed.
1861
1854
            for parent_info, file_infos in\
1862
 
                self.walkdirs(directory):
1863
 
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
1855
                osutils.walkdirs(self.abspath(directory),
 
1856
                    directory):
 
1857
                for relpath, basename, kind, lstat, abspath in file_infos:
1864
1858
                    # Is it versioned or ignored?
1865
1859
                    if self.path2id(relpath) or self.is_ignored(relpath):
1866
1860
                        # Add nested content for deletion.
1876
1870
            filename = self.relpath(abspath)
1877
1871
            if len(filename) > 0:
1878
1872
                new_files.add(filename)
1879
 
                recurse_directory_to_add_files(filename)
 
1873
                if osutils.isdir(abspath):
 
1874
                    recurse_directory_to_add_files(filename)
1880
1875
 
1881
1876
        files = list(new_files)
1882
1877
 
1891
1886
            has_changed_files = len(unknown_nested_files) > 0
1892
1887
            if not has_changed_files:
1893
1888
                for (file_id, path, content_change, versioned, parent_id, name,
1894
 
                     kind, executable) in self.iter_changes(self.basis_tree(),
 
1889
                     kind, executable) in self._iter_changes(self.basis_tree(),
1895
1890
                         include_unchanged=True, require_versioned=False,
1896
1891
                         want_unversioned=True, specific_files=files):
1897
 
                    if versioned == (False, False):
1898
 
                        # The record is unknown ...
1899
 
                        if not self.is_ignored(path[1]):
1900
 
                            # ... but not ignored
1901
 
                            has_changed_files = True
1902
 
                            break
1903
 
                    elif content_change and (kind[1] is not None):
1904
 
                        # Versioned and changed, but not deleted
 
1892
                    # Check if it's an unknown (but not ignored) OR
 
1893
                    # changed (but not deleted) :
 
1894
                    if ((versioned == (False, False) or
 
1895
                         content_change and kind[1] != None)
 
1896
                        and not self.is_ignored(path[1])):
1905
1897
                        has_changed_files = True
1906
1898
                        break
1907
1899
 
1986
1978
                self.set_parent_trees(parent_trees)
1987
1979
                resolve(self)
1988
1980
            else:
1989
 
                resolve(self, filenames, ignore_misses=True, recursive=True)
 
1981
                resolve(self, filenames, ignore_misses=True)
1990
1982
        finally:
1991
1983
            if basis_tree is not None:
1992
1984
                basis_tree.unlock()
2046
2038
        """Set the root id for this tree."""
2047
2039
        # for compatability 
2048
2040
        if file_id is None:
2049
 
            raise ValueError(
2050
 
                'WorkingTree.set_root_id with fileid=None')
2051
 
        file_id = osutils.safe_file_id(file_id)
 
2041
            symbol_versioning.warn(symbol_versioning.zero_twelve
 
2042
                % 'WorkingTree.set_root_id with fileid=None',
 
2043
                DeprecationWarning,
 
2044
                stacklevel=3)
 
2045
            file_id = ROOT_ID
 
2046
        else:
 
2047
            file_id = osutils.safe_file_id(file_id)
2052
2048
        self._set_root_id(file_id)
2053
2049
 
2054
2050
    def _set_root_id(self, file_id):
2113
2109
          basis.
2114
2110
        - Do a 'normal' merge of the old branch basis if it is relevant.
2115
2111
        """
2116
 
        if self.branch.get_bound_location() is not None:
 
2112
        if self.branch.get_master_branch(possible_transports) is not None:
2117
2113
            self.lock_write()
2118
2114
            update_branch = True
2119
2115
        else:
2416
2412
                relroot = ""
2417
2413
            # FIXME: stash the node in pending
2418
2414
            entry = inv[top_id]
2419
 
            if entry.kind == 'directory':
2420
 
                for name, child in entry.sorted_children():
2421
 
                    dirblock.append((relroot + name, name, child.kind, None,
2422
 
                        child.file_id, child.kind
2423
 
                        ))
 
2415
            for name, child in entry.sorted_children():
 
2416
                dirblock.append((relroot + name, name, child.kind, None,
 
2417
                    child.file_id, child.kind
 
2418
                    ))
2424
2419
            yield (currentdir[0], entry.file_id), dirblock
2425
2420
            # push the user specified dirs from dirblock
2426
2421
            for dir in reversed(dirblock):
2459
2454
        self.set_conflicts(un_resolved)
2460
2455
        return un_resolved, resolved
2461
2456
 
2462
 
    @needs_read_lock
2463
 
    def _check(self):
2464
 
        tree_basis = self.basis_tree()
2465
 
        tree_basis.lock_read()
2466
 
        try:
2467
 
            repo_basis = self.branch.repository.revision_tree(
2468
 
                self.last_revision())
2469
 
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2470
 
                raise errors.BzrCheckError(
2471
 
                    "Mismatched basis inventory content.")
2472
 
            self._validate()
2473
 
        finally:
2474
 
            tree_basis.unlock()
2475
 
 
2476
2457
    def _validate(self):
2477
2458
        """Validate internal structures.
2478
2459
 
2484
2465
        """
2485
2466
        return
2486
2467
 
2487
 
    @needs_read_lock
2488
 
    def _get_rules_searcher(self, default_searcher):
2489
 
        """See Tree._get_rules_searcher."""
2490
 
        if self._rules_searcher is None:
2491
 
            self._rules_searcher = super(WorkingTree,
2492
 
                self)._get_rules_searcher(default_searcher)
2493
 
        return self._rules_searcher
2494
 
 
2495
2468
 
2496
2469
class WorkingTree2(WorkingTree):
2497
2470
    """This is the Format 2 working tree.
2557
2530
    def _last_revision(self):
2558
2531
        """See Mutable.last_revision."""
2559
2532
        try:
2560
 
            return self._transport.get_bytes('last-revision')
 
2533
            return self._control_files.get('last-revision').read()
2561
2534
        except errors.NoSuchFile:
2562
2535
            return _mod_revision.NULL_REVISION
2563
2536
 
2565
2538
        """See WorkingTree._change_last_revision."""
2566
2539
        if revision_id is None or revision_id == NULL_REVISION:
2567
2540
            try:
2568
 
                self._transport.delete('last-revision')
 
2541
                self._control_files._transport.delete('last-revision')
2569
2542
            except errors.NoSuchFile:
2570
2543
                pass
2571
2544
            return False
2572
2545
        else:
2573
 
            self._transport.put_bytes('last-revision', revision_id,
2574
 
                mode=self._control_files._file_mode)
 
2546
            self._control_files.put_bytes('last-revision', revision_id)
2575
2547
            return True
2576
2548
 
2577
2549
    @needs_tree_write_lock
2589
2561
    @needs_read_lock
2590
2562
    def conflicts(self):
2591
2563
        try:
2592
 
            confile = self._transport.get('conflicts')
 
2564
            confile = self._control_files.get('conflicts')
2593
2565
        except errors.NoSuchFile:
2594
2566
            return _mod_conflicts.ConflictList()
2595
2567
        try:
2620
2592
            return path[:-len(suffix)]
2621
2593
 
2622
2594
 
 
2595
@deprecated_function(zero_eight)
 
2596
def is_control_file(filename):
 
2597
    """See WorkingTree.is_control_filename(filename)."""
 
2598
    ## FIXME: better check
 
2599
    filename = normpath(filename)
 
2600
    while filename != '':
 
2601
        head, tail = os.path.split(filename)
 
2602
        ## mutter('check %r for control file' % ((head, tail),))
 
2603
        if tail == '.bzr':
 
2604
            return True
 
2605
        if filename == head:
 
2606
            break
 
2607
        filename = head
 
2608
    return False
 
2609
 
 
2610
 
2623
2611
class WorkingTreeFormat(object):
2624
2612
    """An encapsulation of the initialization and open routines for a format.
2625
2613
 
2658
2646
        except errors.NoSuchFile:
2659
2647
            raise errors.NoWorkingTree(base=transport.base)
2660
2648
        except KeyError:
2661
 
            raise errors.UnknownFormatError(format=format_string,
2662
 
                                            kind="working tree")
 
2649
            raise errors.UnknownFormatError(format=format_string)
2663
2650
 
2664
2651
    def __eq__(self, other):
2665
2652
        return self.__class__ is other.__class__
2699
2686
 
2700
2687
    @classmethod
2701
2688
    def unregister_format(klass, format):
 
2689
        assert klass._formats[format.get_format_string()] is format
2702
2690
        del klass._formats[format.get_format_string()]
2703
2691
 
2704
2692
 
2714
2702
        """See WorkingTreeFormat.get_format_description()."""
2715
2703
        return "Working tree format 2"
2716
2704
 
2717
 
    def _stub_initialize_remote(self, branch):
2718
 
        """As a special workaround create critical control files for a remote working tree.
 
2705
    def stub_initialize_remote(self, control_files):
 
2706
        """As a special workaround create critical control files for a remote working tree
2719
2707
        
2720
2708
        This ensures that it can later be updated and dealt with locally,
2721
2709
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
2725
2713
        inv = Inventory()
2726
2714
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2727
2715
        sio.seek(0)
2728
 
        branch._transport.put_file('inventory', sio,
2729
 
            mode=branch.control_files._file_mode)
2730
 
        branch._transport.put_bytes('pending-merges', '',
2731
 
            mode=branch.control_files._file_mode)
 
2716
        control_files.put('inventory', sio)
 
2717
 
 
2718
        control_files.put_bytes('pending-merges', '')
2732
2719
        
2733
2720
 
2734
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2735
 
                   accelerator_tree=None, hardlink=False):
 
2721
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None):
2736
2722
        """See WorkingTreeFormat.initialize()."""
2737
2723
        if not isinstance(a_bzrdir.transport, LocalTransport):
2738
2724
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2824
2810
        return LockableFiles(transport, self._lock_file_name, 
2825
2811
                             self._lock_class)
2826
2812
 
2827
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2828
 
                   accelerator_tree=None, hardlink=False):
 
2813
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None):
2829
2814
        """See WorkingTreeFormat.initialize().
2830
2815
        
2831
 
        :param revision_id: if supplied, create a working tree at a different
2832
 
            revision than the branch is at.
2833
 
        :param accelerator_tree: A tree which can be used for retrieving file
2834
 
            contents more quickly than the revision tree, i.e. a workingtree.
2835
 
            The revision tree will be used for cases where accelerator_tree's
2836
 
            content is different.
2837
 
        :param hardlink: If true, hard-link files from accelerator_tree,
2838
 
            where possible.
 
2816
        revision_id allows creating a working tree at a different
 
2817
        revision than the branch is at.
2839
2818
        """
2840
2819
        if not isinstance(a_bzrdir.transport, LocalTransport):
2841
2820
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2843
2822
        control_files = self._open_control_files(a_bzrdir)
2844
2823
        control_files.create_lock()
2845
2824
        control_files.lock_write()
2846
 
        transport.put_bytes('format', self.get_format_string(),
2847
 
            mode=control_files._file_mode)
 
2825
        control_files.put_utf8('format', self.get_format_string())
2848
2826
        if from_branch is not None:
2849
2827
            branch = from_branch
2850
2828
        else: