~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Michael Hudson
  • Date: 2007-11-26 13:45:49 UTC
  • mto: (3008.1.10 tree-less-merge-diffs)
  • mto: This revision was merged to the branch mainline in revision 3189.
  • Revision ID: michael.hudson@canonical.com-20071126134549-8l3hqyg2bie5yqdv
extract merger creation into a method

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
79
79
import bzrlib.branch
80
80
from bzrlib.transport import get_transport
81
81
import bzrlib.ui
82
 
from bzrlib.workingtree_4 import WorkingTreeFormat4, WorkingTreeFormat5
 
82
from bzrlib.workingtree_4 import WorkingTreeFormat4
83
83
""")
84
84
 
85
85
from bzrlib import symbol_versioning
86
86
from bzrlib.decorators import needs_read_lock, needs_write_lock
87
87
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
88
 
from bzrlib.lockable_files import LockableFiles
 
88
from bzrlib.lockable_files import LockableFiles, TransportLock
89
89
from bzrlib.lockdir import LockDir
90
90
import bzrlib.mutabletree
91
91
from bzrlib.mutabletree import needs_tree_write_lock
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,
284
296
    def supports_tree_reference(self):
285
297
        return False
286
298
 
287
 
    def supports_content_filtering(self):
288
 
        return self._format.supports_content_filtering()
289
 
 
290
 
    def supports_views(self):
291
 
        return self._format.supports_views()
292
 
 
293
299
    def _set_inventory(self, inv, dirty):
294
300
        """Set the internal cached inventory.
295
301
 
300
306
            False then the inventory is the same as that on disk and any
301
307
            serialisation would be unneeded overhead.
302
308
        """
 
309
        assert inv.root is not None
303
310
        self._inventory = inv
304
311
        self._inventory_is_modified = dirty
305
312
 
309
316
 
310
317
        """
311
318
        if path is None:
312
 
            path = osutils.getcwd()
 
319
            path = os.path.getcwdu()
313
320
        control = bzrdir.BzrDir.open(path, _unsupported)
314
321
        return control.open_workingtree(_unsupported)
315
 
 
 
322
        
316
323
    @staticmethod
317
324
    def open_containing(path=None):
318
325
        """Open an existing working tree which has its root about path.
319
 
 
 
326
        
320
327
        This probes for a working tree at path and searches upwards from there.
321
328
 
322
329
        Basically we keep looking up until we find the control directory or
340
347
        """
341
348
        return WorkingTree.open(path, _unsupported=True)
342
349
 
343
 
    @staticmethod
344
 
    def find_trees(location):
345
 
        def list_current(transport):
346
 
            return [d for d in transport.list_dir('') if d != '.bzr']
347
 
        def evaluate(bzrdir):
348
 
            try:
349
 
                tree = bzrdir.open_workingtree()
350
 
            except errors.NoWorkingTree:
351
 
                return True, None
352
 
            else:
353
 
                return True, tree
354
 
        transport = get_transport(location)
355
 
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
356
 
                                              list_current=list_current)
357
 
        return [t for t in iterator if t is not None]
358
 
 
359
350
    # should be deprecated - this is slow and in any case treating them as a
360
351
    # container is (we now know) bad style -- mbp 20070302
361
352
    ## @deprecated_method(zero_fifteen)
370
361
            if osutils.lexists(self.abspath(path)):
371
362
                yield ie.file_id
372
363
 
373
 
    def all_file_ids(self):
374
 
        """See Tree.iter_all_file_ids"""
375
 
        return set(self.inventory)
376
 
 
377
364
    def __repr__(self):
378
365
        return "<%s of %s>" % (self.__class__.__name__,
379
366
                               getattr(self, 'basedir', None))
385
372
        """Return RevisionTree for the current last revision.
386
373
        
387
374
        If the left most parent is a ghost then the returned tree will be an
388
 
        empty tree - one obtained by calling 
389
 
        repository.revision_tree(NULL_REVISION).
 
375
        empty tree - one obtained by calling repository.revision_tree(None).
390
376
        """
391
377
        try:
392
378
            revision_id = self.get_parent_ids()[0]
394
380
            # no parents, return an empty revision tree.
395
381
            # in the future this should return the tree for
396
382
            # 'empty:' - the implicit root empty tree.
397
 
            return self.branch.repository.revision_tree(
398
 
                       _mod_revision.NULL_REVISION)
 
383
            return self.branch.repository.revision_tree(None)
399
384
        try:
400
385
            return self.revision_tree(revision_id)
401
386
        except errors.NoSuchRevision:
405
390
        # at this point ?
406
391
        try:
407
392
            return self.branch.repository.revision_tree(revision_id)
408
 
        except (errors.RevisionNotPresent, errors.NoSuchRevision):
 
393
        except errors.RevisionNotPresent:
409
394
            # the basis tree *may* be a ghost or a low level error may have
410
395
            # occured. If the revision is present, its a problem, if its not
411
396
            # its a ghost.
412
397
            if self.branch.repository.has_revision(revision_id):
413
398
                raise
414
399
            # the basis tree is a ghost so return an empty tree.
415
 
            return self.branch.repository.revision_tree(
416
 
                       _mod_revision.NULL_REVISION)
 
400
            return self.branch.repository.revision_tree(None)
417
401
 
418
402
    def _cleanup(self):
419
403
        self._flush_ignore_list_cache()
420
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
 
421
443
    def relpath(self, path):
422
444
        """Return the local path portion from a given path.
423
445
        
430
452
        return osutils.lexists(self.abspath(filename))
431
453
 
432
454
    def get_file(self, file_id, path=None):
433
 
        return self.get_file_with_stat(file_id, path)[0]
434
 
 
435
 
    def get_file_with_stat(self, file_id, path=None, _fstat=os.fstat):
436
 
        """See MutableTree.get_file_with_stat."""
437
455
        if path is None:
438
456
            path = self.id2path(file_id)
439
 
        file_obj = self.get_file_byname(path)
440
 
        return (file_obj, _fstat(file_obj.fileno()))
 
457
        return self.get_file_byname(path)
 
458
 
 
459
    def get_file_text(self, file_id):
 
460
        return self.get_file(file_id).read()
441
461
 
442
462
    def get_file_byname(self, filename):
443
463
        return file(self.abspath(filename), 'rb')
444
464
 
445
 
    def get_file_lines(self, file_id, path=None):
446
 
        """See Tree.get_file_lines()"""
447
 
        file = self.get_file(file_id, path)
448
 
        try:
449
 
            return file.readlines()
450
 
        finally:
451
 
            file.close()
452
 
 
453
465
    @needs_read_lock
454
466
    def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
455
467
        """See Tree.annotate_iter
464
476
        basis = self.basis_tree()
465
477
        basis.lock_read()
466
478
        try:
467
 
            changes = self.iter_changes(basis, True, [self.id2path(file_id)],
 
479
            changes = self._iter_changes(basis, True, [self.id2path(file_id)],
468
480
                require_versioned=True).next()
469
481
            changed_content, kind = changes[2], changes[6]
470
482
            if not changed_content:
506
518
        else:
507
519
            parents = [last_rev]
508
520
        try:
509
 
            merges_file = self._transport.get('pending-merges')
 
521
            merges_file = self._control_files.get('pending-merges')
510
522
        except errors.NoSuchFile:
511
523
            pass
512
524
        else:
539
551
            and this one merged in.
540
552
        """
541
553
        # assumes the target bzr dir format is compatible.
542
 
        result = to_bzrdir.create_workingtree()
 
554
        result = self._format.initialize(to_bzrdir)
543
555
        self.copy_content_into(result, revision_id)
544
556
        return result
545
557
 
574
586
    __contains__ = has_id
575
587
 
576
588
    def get_file_size(self, file_id):
577
 
        """See Tree.get_file_size"""
578
 
        try:
579
 
            return os.path.getsize(self.id2abspath(file_id))
580
 
        except OSError, e:
581
 
            if e.errno != errno.ENOENT:
582
 
                raise
583
 
            else:
584
 
                return None
 
589
        return os.path.getsize(self.id2abspath(file_id))
585
590
 
586
591
    @needs_read_lock
587
592
    def get_file_sha1(self, file_id, path=None, stat_value=None):
627
632
        # function - they should be part of lock_write and unlock.
628
633
        inv = self.inventory
629
634
        for f, file_id, kind in zip(files, ids, kinds):
 
635
            assert kind is not None
630
636
            if file_id is None:
631
637
                inv.add_path(f, kind=kind)
632
638
            else:
723
729
                kind = 'tree-reference'
724
730
            return kind, None, None, None
725
731
        elif kind == 'symlink':
726
 
            return ('symlink', None, None, os.readlink(abspath.encode(osutils._fs_enc)))
 
732
            return ('symlink', None, None, os.readlink(abspath))
727
733
        else:
728
734
            return (kind, None, None, None)
729
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
 
730
749
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
731
750
        """Common ghost checking functionality from set_parent_*.
732
751
 
741
760
 
742
761
    def _set_merges_from_parent_ids(self, parent_ids):
743
762
        merges = parent_ids[1:]
744
 
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
745
 
            mode=self._control_files._file_mode)
746
 
 
747
 
    def _filter_parent_ids_by_ancestry(self, revision_ids):
748
 
        """Check that all merged revisions are proper 'heads'.
749
 
 
750
 
        This will always return the first revision_id, and any merged revisions
751
 
        which are 
752
 
        """
753
 
        if len(revision_ids) == 0:
754
 
            return revision_ids
755
 
        graph = self.branch.repository.get_graph()
756
 
        heads = graph.heads(revision_ids)
757
 
        new_revision_ids = revision_ids[:1]
758
 
        for revision_id in revision_ids[1:]:
759
 
            if revision_id in heads and revision_id not in new_revision_ids:
760
 
                new_revision_ids.append(revision_id)
761
 
        if new_revision_ids != revision_ids:
762
 
            trace.mutter('requested to set revision_ids = %s,'
763
 
                         ' but filtered to %s', revision_ids, new_revision_ids)
764
 
        return new_revision_ids
 
763
        self._control_files.put_bytes('pending-merges', '\n'.join(merges))
765
764
 
766
765
    @needs_tree_write_lock
767
766
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
781
780
        for revision_id in revision_ids:
782
781
            _mod_revision.check_not_reserved_id(revision_id)
783
782
 
784
 
        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
785
 
 
786
783
        if len(revision_ids) > 0:
787
784
            self.set_last_revision(revision_ids[0])
788
785
        else:
800
797
        self._check_parents_for_ghosts(parent_ids,
801
798
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
802
799
 
803
 
        parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
804
 
 
805
800
        if len(parent_ids) == 0:
806
801
            leftmost_parent_id = _mod_revision.NULL_REVISION
807
802
            leftmost_parent_tree = None
847
842
    def _put_rio(self, filename, stanzas, header):
848
843
        self._must_be_locked()
849
844
        my_file = rio_file(stanzas, header)
850
 
        self._transport.put_file(filename, my_file,
851
 
            mode=self._control_files._file_mode)
 
845
        self._control_files.put(filename, my_file)
852
846
 
853
847
    @needs_write_lock # because merge pulls data into the branch.
854
848
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
913
907
        still in the working inventory and have that text hash.
914
908
        """
915
909
        try:
916
 
            hashfile = self._transport.get('merge-hashes')
 
910
            hashfile = self._control_files.get('merge-hashes')
917
911
        except errors.NoSuchFile:
918
912
            return {}
 
913
        merge_hashes = {}
919
914
        try:
920
 
            merge_hashes = {}
921
 
            try:
922
 
                if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
923
 
                    raise errors.MergeModifiedFormatError()
924
 
            except StopIteration:
 
915
            if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
925
916
                raise errors.MergeModifiedFormatError()
926
 
            for s in RioReader(hashfile):
927
 
                # RioReader reads in Unicode, so convert file_ids back to utf8
928
 
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
929
 
                if file_id not in self.inventory:
930
 
                    continue
931
 
                text_hash = s.get("hash")
932
 
                if text_hash == self.get_file_sha1(file_id):
933
 
                    merge_hashes[file_id] = text_hash
934
 
            return merge_hashes
935
 
        finally:
936
 
            hashfile.close()
 
917
        except StopIteration:
 
918
            raise errors.MergeModifiedFormatError()
 
919
        for s in RioReader(hashfile):
 
920
            # RioReader reads in Unicode, so convert file_ids back to utf8
 
921
            file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
 
922
            if file_id not in self.inventory:
 
923
                continue
 
924
            text_hash = s.get("hash")
 
925
            if text_hash == self.get_file_sha1(file_id):
 
926
                merge_hashes[file_id] = text_hash
 
927
        return merge_hashes
937
928
 
938
929
    @needs_write_lock
939
930
    def mkdir(self, path, file_id=None):
945
936
        return file_id
946
937
 
947
938
    def get_symlink_target(self, file_id):
948
 
        return os.readlink(self.id2abspath(file_id).encode(osutils._fs_enc))
 
939
        return os.readlink(self.id2abspath(file_id))
949
940
 
950
941
    @needs_write_lock
951
942
    def subsume(self, other_tree):
989
980
            other_tree.unlock()
990
981
        other_tree.bzrdir.retire_bzrdir()
991
982
 
992
 
    def _setup_directory_is_tree_reference(self):
993
 
        if self._branch.repository._format.supports_tree_reference:
994
 
            self._directory_is_tree_reference = \
995
 
                self._directory_may_be_tree_reference
996
 
        else:
997
 
            self._directory_is_tree_reference = \
998
 
                self._directory_is_never_tree_reference
999
 
 
1000
 
    def _directory_is_never_tree_reference(self, relpath):
1001
 
        return False
1002
 
 
1003
 
    def _directory_may_be_tree_reference(self, relpath):
 
983
    def _directory_is_tree_reference(self, relpath):
1004
984
        # as a special case, if a directory contains control files then 
1005
985
        # it's a tree reference, except that the root of the tree is not
1006
986
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1033
1013
        sub_path = self.id2path(file_id)
1034
1014
        branch_transport = mkdirs(sub_path)
1035
1015
        if format is None:
1036
 
            format = self.bzrdir.cloning_metadir()
 
1016
            format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
1037
1017
        branch_transport.ensure_base()
1038
1018
        branch_bzrdir = format.initialize_on_transport(branch_transport)
1039
1019
        try:
1040
1020
            repo = branch_bzrdir.find_repository()
1041
1021
        except errors.NoRepositoryPresent:
1042
1022
            repo = branch_bzrdir.create_repository()
1043
 
        if not repo.supports_rich_root():
1044
 
            raise errors.RootNotRich()
 
1023
            assert repo.supports_rich_root()
 
1024
        else:
 
1025
            if not repo.supports_rich_root():
 
1026
                raise errors.RootNotRich()
1045
1027
        new_branch = branch_bzrdir.create_branch()
1046
1028
        new_branch.pull(self.branch)
1047
1029
        for parent_id in self.get_parent_ids():
1079
1061
        sio = StringIO()
1080
1062
        self._serialize(self._inventory, sio)
1081
1063
        sio.seek(0)
1082
 
        self._transport.put_file('inventory', sio,
1083
 
            mode=self._control_files._file_mode)
 
1064
        self._control_files.put('inventory', sio)
1084
1065
        self._inventory_is_modified = False
1085
1066
 
1086
1067
    def _kind(self, relpath):
1247
1228
                                       DeprecationWarning)
1248
1229
 
1249
1230
        # check destination directory
1250
 
        if isinstance(from_paths, basestring):
1251
 
            raise ValueError()
 
1231
        assert not isinstance(from_paths, basestring)
1252
1232
        inv = self.inventory
1253
1233
        to_abs = self.abspath(to_dir)
1254
1234
        if not isdir(to_abs):
1338
1318
                only_change_inv = True
1339
1319
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1340
1320
                only_change_inv = False
1341
 
            elif (not self.case_sensitive
1342
 
                  and from_rel.lower() == to_rel.lower()
1343
 
                  and self.has_filename(from_rel)):
 
1321
            elif (sys.platform == 'win32'
 
1322
                and from_rel.lower() == to_rel.lower()
 
1323
                and self.has_filename(from_rel)):
1344
1324
                only_change_inv = False
1345
1325
            else:
1346
1326
                # something is wrong, so lets determine what exactly
1518
1498
            # - RBC 20060907
1519
1499
            self._write_inventory(self._inventory)
1520
1500
    
 
1501
    @deprecated_method(zero_eight)
 
1502
    def iter_conflicts(self):
 
1503
        """List all files in the tree that have text or content conflicts.
 
1504
        DEPRECATED.  Use conflicts instead."""
 
1505
        return self._iter_conflicts()
 
1506
 
1521
1507
    def _iter_conflicts(self):
1522
1508
        conflicted = set()
1523
1509
        for info in self.list_files():
1614
1600
                if subf == '.bzr':
1615
1601
                    continue
1616
1602
                if subf not in dir_entry.children:
1617
 
                    try:
1618
 
                        (subf_norm,
1619
 
                         can_access) = osutils.normalized_filename(subf)
1620
 
                    except UnicodeDecodeError:
1621
 
                        path_os_enc = path.encode(osutils._fs_enc)
1622
 
                        relpath = path_os_enc + '/' + subf
1623
 
                        raise errors.BadFilenameEncoding(relpath,
1624
 
                                                         osutils._fs_enc)
 
1603
                    subf_norm, can_access = osutils.normalized_filename(subf)
1625
1604
                    if subf_norm != subf and can_access:
1626
1605
                        if subf_norm not in dir_entry.children:
1627
1606
                            fl.append(subf_norm)
1682
1661
    def kind(self, file_id):
1683
1662
        return file_kind(self.id2abspath(file_id))
1684
1663
 
1685
 
    def stored_kind(self, file_id):
1686
 
        """See Tree.stored_kind"""
1687
 
        return self.inventory[file_id].kind
1688
 
 
1689
1664
    def _comparison_data(self, entry, path):
1690
1665
        abspath = self.abspath(path)
1691
1666
        try:
1773
1748
    def _reset_data(self):
1774
1749
        """Reset transient data that cannot be revalidated."""
1775
1750
        self._inventory_is_modified = False
1776
 
        result = self._deserialize(self._transport.get('inventory'))
 
1751
        result = self._deserialize(self._control_files.get('inventory'))
1777
1752
        self._set_inventory(result, dirty=False)
1778
1753
 
1779
1754
    @needs_tree_write_lock
1800
1775
 
1801
1776
    def _write_basis_inventory(self, xml):
1802
1777
        """Write the basis inventory XML to the basis-inventory file"""
 
1778
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
1803
1779
        path = self._basis_inventory_name()
1804
1780
        sio = StringIO(xml)
1805
 
        self._transport.put_file(path, sio,
1806
 
            mode=self._control_files._file_mode)
 
1781
        self._control_files.put(path, sio)
1807
1782
 
1808
1783
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1809
1784
        """Create the text that will be saved in basis-inventory"""
1840
1815
    def read_basis_inventory(self):
1841
1816
        """Read the cached basis inventory."""
1842
1817
        path = self._basis_inventory_name()
1843
 
        return self._transport.get_bytes(path)
 
1818
        return self._control_files.get(path).read()
1844
1819
        
1845
1820
    @needs_read_lock
1846
1821
    def read_working_inventory(self):
1855
1830
        # binary.
1856
1831
        if self._inventory_is_modified:
1857
1832
            raise errors.InventoryModified(self)
1858
 
        result = self._deserialize(self._transport.get('inventory'))
 
1833
        result = self._deserialize(self._control_files.get('inventory'))
1859
1834
        self._set_inventory(result, dirty=False)
1860
1835
        return result
1861
1836
 
1881
1856
            # Recurse directory and add all files
1882
1857
            # so we can check if they have changed.
1883
1858
            for parent_info, file_infos in\
1884
 
                self.walkdirs(directory):
1885
 
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
1859
                osutils.walkdirs(self.abspath(directory),
 
1860
                    directory):
 
1861
                for relpath, basename, kind, lstat, abspath in file_infos:
1886
1862
                    # Is it versioned or ignored?
1887
1863
                    if self.path2id(relpath) or self.is_ignored(relpath):
1888
1864
                        # Add nested content for deletion.
1898
1874
            filename = self.relpath(abspath)
1899
1875
            if len(filename) > 0:
1900
1876
                new_files.add(filename)
1901
 
                recurse_directory_to_add_files(filename)
 
1877
                if osutils.isdir(abspath):
 
1878
                    recurse_directory_to_add_files(filename)
1902
1879
 
1903
1880
        files = list(new_files)
1904
1881
 
1913
1890
            has_changed_files = len(unknown_nested_files) > 0
1914
1891
            if not has_changed_files:
1915
1892
                for (file_id, path, content_change, versioned, parent_id, name,
1916
 
                     kind, executable) in self.iter_changes(self.basis_tree(),
 
1893
                     kind, executable) in self._iter_changes(self.basis_tree(),
1917
1894
                         include_unchanged=True, require_versioned=False,
1918
1895
                         want_unversioned=True, specific_files=files):
1919
 
                    if versioned == (False, False):
1920
 
                        # The record is unknown ...
1921
 
                        if not self.is_ignored(path[1]):
1922
 
                            # ... but not ignored
1923
 
                            has_changed_files = True
1924
 
                            break
1925
 
                    elif content_change and (kind[1] is not None):
1926
 
                        # Versioned and changed, but not deleted
 
1896
                    # Check if it's an unknown (but not ignored) OR
 
1897
                    # changed (but not deleted) :
 
1898
                    if ((versioned == (False, False) or
 
1899
                         content_change and kind[1] != None)
 
1900
                        and not self.is_ignored(path[1])):
1927
1901
                        has_changed_files = True
1928
1902
                        break
1929
1903
 
2008
1982
                self.set_parent_trees(parent_trees)
2009
1983
                resolve(self)
2010
1984
            else:
2011
 
                resolve(self, filenames, ignore_misses=True, recursive=True)
 
1985
                resolve(self, filenames, ignore_misses=True)
2012
1986
        finally:
2013
1987
            if basis_tree is not None:
2014
1988
                basis_tree.unlock()
2068
2042
        """Set the root id for this tree."""
2069
2043
        # for compatability 
2070
2044
        if file_id is None:
2071
 
            raise ValueError(
2072
 
                'WorkingTree.set_root_id with fileid=None')
2073
 
        file_id = osutils.safe_file_id(file_id)
 
2045
            symbol_versioning.warn(symbol_versioning.zero_twelve
 
2046
                % 'WorkingTree.set_root_id with fileid=None',
 
2047
                DeprecationWarning,
 
2048
                stacklevel=3)
 
2049
            file_id = ROOT_ID
 
2050
        else:
 
2051
            file_id = osutils.safe_file_id(file_id)
2074
2052
        self._set_root_id(file_id)
2075
2053
 
2076
2054
    def _set_root_id(self, file_id):
2135
2113
          basis.
2136
2114
        - Do a 'normal' merge of the old branch basis if it is relevant.
2137
2115
        """
2138
 
        if self.branch.get_bound_location() is not None:
 
2116
        if self.branch.get_master_branch(possible_transports) is not None:
2139
2117
            self.lock_write()
2140
2118
            update_branch = True
2141
2119
        else:
2342
2320
                    # value.
2343
2321
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
2344
2322
                        ('.bzr', '.bzr'))
2345
 
                    if (bzrdir_loc < len(cur_disk_dir_content)
2346
 
                        and cur_disk_dir_content[bzrdir_loc][0] == '.bzr'):
 
2323
                    if cur_disk_dir_content[bzrdir_loc][0] == '.bzr':
2347
2324
                        # we dont yield the contents of, or, .bzr itself.
2348
2325
                        del cur_disk_dir_content[bzrdir_loc]
2349
2326
            if inv_finished:
2439
2416
                relroot = ""
2440
2417
            # FIXME: stash the node in pending
2441
2418
            entry = inv[top_id]
2442
 
            if entry.kind == 'directory':
2443
 
                for name, child in entry.sorted_children():
2444
 
                    dirblock.append((relroot + name, name, child.kind, None,
2445
 
                        child.file_id, child.kind
2446
 
                        ))
 
2419
            for name, child in entry.sorted_children():
 
2420
                dirblock.append((relroot + name, name, child.kind, None,
 
2421
                    child.file_id, child.kind
 
2422
                    ))
2447
2423
            yield (currentdir[0], entry.file_id), dirblock
2448
2424
            # push the user specified dirs from dirblock
2449
2425
            for dir in reversed(dirblock):
2482
2458
        self.set_conflicts(un_resolved)
2483
2459
        return un_resolved, resolved
2484
2460
 
2485
 
    @needs_read_lock
2486
 
    def _check(self):
2487
 
        tree_basis = self.basis_tree()
2488
 
        tree_basis.lock_read()
2489
 
        try:
2490
 
            repo_basis = self.branch.repository.revision_tree(
2491
 
                self.last_revision())
2492
 
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2493
 
                raise errors.BzrCheckError(
2494
 
                    "Mismatched basis inventory content.")
2495
 
            self._validate()
2496
 
        finally:
2497
 
            tree_basis.unlock()
2498
 
 
2499
2461
    def _validate(self):
2500
2462
        """Validate internal structures.
2501
2463
 
2507
2469
        """
2508
2470
        return
2509
2471
 
2510
 
    @needs_read_lock
2511
 
    def _get_rules_searcher(self, default_searcher):
2512
 
        """See Tree._get_rules_searcher."""
2513
 
        if self._rules_searcher is None:
2514
 
            self._rules_searcher = super(WorkingTree,
2515
 
                self)._get_rules_searcher(default_searcher)
2516
 
        return self._rules_searcher
2517
 
 
2518
 
    def get_shelf_manager(self):
2519
 
        """Return the ShelfManager for this WorkingTree."""
2520
 
        from bzrlib.shelf import ShelfManager
2521
 
        return ShelfManager(self, self._transport)
2522
 
 
2523
2472
 
2524
2473
class WorkingTree2(WorkingTree):
2525
2474
    """This is the Format 2 working tree.
2585
2534
    def _last_revision(self):
2586
2535
        """See Mutable.last_revision."""
2587
2536
        try:
2588
 
            return self._transport.get_bytes('last-revision')
 
2537
            return self._control_files.get('last-revision').read()
2589
2538
        except errors.NoSuchFile:
2590
2539
            return _mod_revision.NULL_REVISION
2591
2540
 
2593
2542
        """See WorkingTree._change_last_revision."""
2594
2543
        if revision_id is None or revision_id == NULL_REVISION:
2595
2544
            try:
2596
 
                self._transport.delete('last-revision')
 
2545
                self._control_files._transport.delete('last-revision')
2597
2546
            except errors.NoSuchFile:
2598
2547
                pass
2599
2548
            return False
2600
2549
        else:
2601
 
            self._transport.put_bytes('last-revision', revision_id,
2602
 
                mode=self._control_files._file_mode)
 
2550
            self._control_files.put_bytes('last-revision', revision_id)
2603
2551
            return True
2604
2552
 
2605
2553
    @needs_tree_write_lock
2617
2565
    @needs_read_lock
2618
2566
    def conflicts(self):
2619
2567
        try:
2620
 
            confile = self._transport.get('conflicts')
 
2568
            confile = self._control_files.get('conflicts')
2621
2569
        except errors.NoSuchFile:
2622
2570
            return _mod_conflicts.ConflictList()
2623
2571
        try:
2624
 
            try:
2625
 
                if confile.next() != CONFLICT_HEADER_1 + '\n':
2626
 
                    raise errors.ConflictFormatError()
2627
 
            except StopIteration:
 
2572
            if confile.next() != CONFLICT_HEADER_1 + '\n':
2628
2573
                raise errors.ConflictFormatError()
2629
 
            return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2630
 
        finally:
2631
 
            confile.close()
 
2574
        except StopIteration:
 
2575
            raise errors.ConflictFormatError()
 
2576
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2632
2577
 
2633
2578
    def unlock(self):
2634
2579
        # do non-implementation specific cleanup
2651
2596
            return path[:-len(suffix)]
2652
2597
 
2653
2598
 
 
2599
@deprecated_function(zero_eight)
 
2600
def is_control_file(filename):
 
2601
    """See WorkingTree.is_control_filename(filename)."""
 
2602
    ## FIXME: better check
 
2603
    filename = normpath(filename)
 
2604
    while filename != '':
 
2605
        head, tail = os.path.split(filename)
 
2606
        ## mutter('check %r for control file' % ((head, tail),))
 
2607
        if tail == '.bzr':
 
2608
            return True
 
2609
        if filename == head:
 
2610
            break
 
2611
        filename = head
 
2612
    return False
 
2613
 
 
2614
 
2654
2615
class WorkingTreeFormat(object):
2655
2616
    """An encapsulation of the initialization and open routines for a format.
2656
2617
 
2689
2650
        except errors.NoSuchFile:
2690
2651
            raise errors.NoWorkingTree(base=transport.base)
2691
2652
        except KeyError:
2692
 
            raise errors.UnknownFormatError(format=format_string,
2693
 
                                            kind="working tree")
 
2653
            raise errors.UnknownFormatError(format=format_string)
2694
2654
 
2695
2655
    def __eq__(self, other):
2696
2656
        return self.__class__ is other.__class__
2720
2680
        """
2721
2681
        return True
2722
2682
 
2723
 
    def supports_content_filtering(self):
2724
 
        """True if this format supports content filtering."""
2725
 
        return False
2726
 
 
2727
 
    def supports_views(self):
2728
 
        """True if this format supports stored views."""
2729
 
        return False
2730
 
 
2731
2683
    @classmethod
2732
2684
    def register_format(klass, format):
2733
2685
        klass._formats[format.get_format_string()] = format
2738
2690
 
2739
2691
    @classmethod
2740
2692
    def unregister_format(klass, format):
 
2693
        assert klass._formats[format.get_format_string()] is format
2741
2694
        del klass._formats[format.get_format_string()]
2742
2695
 
2743
2696
 
2753
2706
        """See WorkingTreeFormat.get_format_description()."""
2754
2707
        return "Working tree format 2"
2755
2708
 
2756
 
    def _stub_initialize_on_transport(self, transport, file_mode):
2757
 
        """Workaround: create control files for a remote working tree.
2758
 
 
 
2709
    def stub_initialize_remote(self, control_files):
 
2710
        """As a special workaround create critical control files for a remote working tree
 
2711
        
2759
2712
        This ensures that it can later be updated and dealt with locally,
2760
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
 
2713
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
2761
2714
        no working tree.  (See bug #43064).
2762
2715
        """
2763
2716
        sio = StringIO()
2764
2717
        inv = Inventory()
2765
2718
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2766
2719
        sio.seek(0)
2767
 
        transport.put_file('inventory', sio, file_mode)
2768
 
        transport.put_bytes('pending-merges', '', file_mode)
2769
 
 
2770
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2771
 
                   accelerator_tree=None, hardlink=False):
 
2720
        control_files.put('inventory', sio)
 
2721
 
 
2722
        control_files.put_bytes('pending-merges', '')
 
2723
        
 
2724
 
 
2725
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None):
2772
2726
        """See WorkingTreeFormat.initialize()."""
2773
2727
        if not isinstance(a_bzrdir.transport, LocalTransport):
2774
2728
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2860
2814
        return LockableFiles(transport, self._lock_file_name, 
2861
2815
                             self._lock_class)
2862
2816
 
2863
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2864
 
                   accelerator_tree=None, hardlink=False):
 
2817
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None):
2865
2818
        """See WorkingTreeFormat.initialize().
2866
2819
        
2867
 
        :param revision_id: if supplied, create a working tree at a different
2868
 
            revision than the branch is at.
2869
 
        :param accelerator_tree: A tree which can be used for retrieving file
2870
 
            contents more quickly than the revision tree, i.e. a workingtree.
2871
 
            The revision tree will be used for cases where accelerator_tree's
2872
 
            content is different.
2873
 
        :param hardlink: If true, hard-link files from accelerator_tree,
2874
 
            where possible.
 
2820
        revision_id allows creating a working tree at a different
 
2821
        revision than the branch is at.
2875
2822
        """
2876
2823
        if not isinstance(a_bzrdir.transport, LocalTransport):
2877
2824
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2879
2826
        control_files = self._open_control_files(a_bzrdir)
2880
2827
        control_files.create_lock()
2881
2828
        control_files.lock_write()
2882
 
        transport.put_bytes('format', self.get_format_string(),
2883
 
            mode=control_files._file_mode)
 
2829
        control_files.put_utf8('format', self.get_format_string())
2884
2830
        if from_branch is not None:
2885
2831
            branch = from_branch
2886
2832
        else:
2956
2902
 
2957
2903
__default_format = WorkingTreeFormat4()
2958
2904
WorkingTreeFormat.register_format(__default_format)
2959
 
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2960
2905
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2961
2906
WorkingTreeFormat.set_default_format(__default_format)
2962
2907
# formats which have no format string are not discoverable