~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Andrew Bennetts
  • Date: 2007-03-28 07:08:42 UTC
  • mfrom: (2380 +trunk)
  • mto: (2018.5.146 hpss)
  • mto: This revision was merged to the branch mainline in revision 2414.
  • Revision ID: andrew.bennetts@canonical.com-20070328070842-r843houy668oxb9o
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 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
41
41
 
42
42
from bzrlib.lazy_import import lazy_import
43
43
lazy_import(globals(), """
 
44
from bisect import bisect_left
44
45
import collections
45
46
from copy import deepcopy
46
47
import errno
 
48
import itertools
 
49
import operator
47
50
import stat
48
51
from time import time
49
52
import warnings
 
53
import re
50
54
 
51
55
import bzrlib
52
56
from bzrlib import (
 
57
    branch,
53
58
    bzrdir,
54
59
    conflicts as _mod_conflicts,
 
60
    dirstate,
55
61
    errors,
56
62
    generate_ids,
57
63
    globbing,
59
65
    ignores,
60
66
    merge,
61
67
    osutils,
 
68
    revisiontree,
 
69
    repository,
62
70
    textui,
63
71
    transform,
64
72
    urlutils,
65
73
    xml5,
66
74
    xml6,
 
75
    xml7,
67
76
    )
68
77
import bzrlib.branch
69
78
from bzrlib.transport import get_transport
70
79
import bzrlib.ui
 
80
from bzrlib.workingtree_4 import WorkingTreeFormat4
71
81
""")
72
82
 
73
83
from bzrlib import symbol_versioning
74
84
from bzrlib.decorators import needs_read_lock, needs_write_lock
75
 
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
 
85
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
76
86
from bzrlib.lockable_files import LockableFiles, TransportLock
77
87
from bzrlib.lockdir import LockDir
78
88
import bzrlib.mutabletree
91
101
    )
92
102
from bzrlib.trace import mutter, note
93
103
from bzrlib.transport.local import LocalTransport
94
 
import bzrlib.tree
95
104
from bzrlib.progress import DummyProgress, ProgressPhase
96
105
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
97
 
import bzrlib.revisiontree
98
106
from bzrlib.rio import RioReader, rio_file, Stanza
99
107
from bzrlib.symbol_versioning import (deprecated_passed,
100
108
        deprecated_method,
200
208
                 _internal=False,
201
209
                 _format=None,
202
210
                 _bzrdir=None):
203
 
        """Construct a WorkingTree for basedir.
 
211
        """Construct a WorkingTree instance. This is not a public API.
204
212
 
205
 
        If the branch is not supplied, it is opened automatically.
206
 
        If the branch is supplied, it must be the branch for this basedir.
207
 
        (branch.base is not cross checked, because for remote branches that
208
 
        would be meaningless).
 
213
        :param branch: A branch to override probing for the branch.
209
214
        """
210
215
        self._format = _format
211
216
        self.bzrdir = _bzrdir
212
217
        if not _internal:
213
 
            # not created via open etc.
214
 
            warnings.warn("WorkingTree() is deprecated as of bzr version 0.8. "
215
 
                 "Please use bzrdir.open_workingtree or WorkingTree.open().",
216
 
                 DeprecationWarning,
217
 
                 stacklevel=2)
218
 
            wt = WorkingTree.open(basedir)
219
 
            self._branch = wt.branch
220
 
            self.basedir = wt.basedir
221
 
            self._control_files = wt._control_files
222
 
            self._hashcache = wt._hashcache
223
 
            self._set_inventory(wt._inventory, dirty=False)
224
 
            self._format = wt._format
225
 
            self.bzrdir = wt.bzrdir
 
218
            raise errors.BzrError("Please use bzrdir.open_workingtree or "
 
219
                "WorkingTree.open() to obtain a WorkingTree.")
226
220
        assert isinstance(basedir, basestring), \
227
221
            "base directory %r is not a string" % basedir
228
222
        basedir = safe_unicode(basedir)
229
223
        mutter("opening working tree %r", basedir)
230
224
        if deprecated_passed(branch):
231
 
            if not _internal:
232
 
                warnings.warn("WorkingTree(..., branch=XXX) is deprecated"
233
 
                     " as of bzr 0.8. Please use bzrdir.open_workingtree() or"
234
 
                     " WorkingTree.open().",
235
 
                     DeprecationWarning,
236
 
                     stacklevel=2
237
 
                     )
238
225
            self._branch = branch
239
226
        else:
240
227
            self._branch = self.bzrdir.open_branch()
269
256
            hc.write()
270
257
 
271
258
        if _inventory is None:
 
259
            # This will be acquired on lock_read() or lock_write()
272
260
            self._inventory_is_modified = False
273
 
            self.read_working_inventory()
 
261
            self._inventory = None
274
262
        else:
275
263
            # the caller of __init__ has provided an inventory,
276
264
            # we assume they know what they are doing - as its only
297
285
        self._control_files.break_lock()
298
286
        self.branch.break_lock()
299
287
 
 
288
    def requires_rich_root(self):
 
289
        return self._format.requires_rich_root
 
290
 
 
291
    def supports_tree_reference(self):
 
292
        return False
 
293
 
300
294
    def _set_inventory(self, inv, dirty):
301
295
        """Set the internal cached inventory.
302
296
 
348
342
        """
349
343
        return WorkingTree.open(path, _unsupported=True)
350
344
 
 
345
    # should be deprecated - this is slow and in any case treating them as a
 
346
    # container is (we now know) bad style -- mbp 20070302
 
347
    ## @deprecated_method(zero_fifteen)
351
348
    def __iter__(self):
352
349
        """Iterate through file_ids for this tree.
353
350
 
379
376
            # in the future this should return the tree for
380
377
            # 'empty:' - the implicit root empty tree.
381
378
            return self.branch.repository.revision_tree(None)
382
 
        else:
383
 
            try:
384
 
                xml = self.read_basis_inventory()
385
 
                inv = xml6.serializer_v6.read_inventory_from_string(xml)
386
 
                if inv is not None and inv.revision_id == revision_id:
387
 
                    return bzrlib.revisiontree.RevisionTree(
388
 
                        self.branch.repository, inv, revision_id)
389
 
            except (errors.NoSuchFile, errors.BadInventoryFormat):
390
 
                pass
 
379
        try:
 
380
            return self.revision_tree(revision_id)
 
381
        except errors.NoSuchRevision:
 
382
            pass
391
383
        # No cached copy available, retrieve from the repository.
392
384
        # FIXME? RBC 20060403 should we cache the inventory locally
393
385
        # at this point ?
452
444
        return osutils.lexists(self.abspath(filename))
453
445
 
454
446
    def get_file(self, file_id):
 
447
        file_id = osutils.safe_file_id(file_id)
455
448
        return self.get_file_byname(self.id2path(file_id))
456
449
 
457
450
    def get_file_text(self, file_id):
 
451
        file_id = osutils.safe_file_id(file_id)
458
452
        return self.get_file(file_id).read()
459
453
 
460
454
    def get_file_byname(self, filename):
461
455
        return file(self.abspath(filename), 'rb')
462
456
 
 
457
    @needs_read_lock
463
458
    def annotate_iter(self, file_id):
464
459
        """See Tree.annotate_iter
465
460
 
470
465
        incorrectly attributed to CURRENT_REVISION (but after committing, the
471
466
        attribution will be correct).
472
467
        """
 
468
        file_id = osutils.safe_file_id(file_id)
473
469
        basis = self.basis_tree()
474
 
        changes = self._iter_changes(basis, True, [file_id]).next()
475
 
        changed_content, kind = changes[2], changes[6]
476
 
        if not changed_content:
477
 
            return basis.annotate_iter(file_id)
478
 
        if kind[1] is None:
479
 
            return None
480
 
        import annotate
481
 
        if kind[0] != 'file':
482
 
            old_lines = []
483
 
        else:
484
 
            old_lines = list(basis.annotate_iter(file_id))
485
 
        old = [old_lines]
486
 
        for tree in self.branch.repository.revision_trees(
487
 
            self.get_parent_ids()[1:]):
488
 
            if file_id not in tree:
489
 
                continue
490
 
            old.append(list(tree.annotate_iter(file_id)))
491
 
        return annotate.reannotate(old, self.get_file(file_id).readlines(),
492
 
                                   CURRENT_REVISION)
 
470
        basis.lock_read()
 
471
        try:
 
472
            changes = self._iter_changes(basis, True, [self.id2path(file_id)],
 
473
                require_versioned=True).next()
 
474
            changed_content, kind = changes[2], changes[6]
 
475
            if not changed_content:
 
476
                return basis.annotate_iter(file_id)
 
477
            if kind[1] is None:
 
478
                return None
 
479
            import annotate
 
480
            if kind[0] != 'file':
 
481
                old_lines = []
 
482
            else:
 
483
                old_lines = list(basis.annotate_iter(file_id))
 
484
            old = [old_lines]
 
485
            for tree in self.branch.repository.revision_trees(
 
486
                self.get_parent_ids()[1:]):
 
487
                if file_id not in tree:
 
488
                    continue
 
489
                old.append(list(tree.annotate_iter(file_id)))
 
490
            return annotate.reannotate(old, self.get_file(file_id).readlines(),
 
491
                                       CURRENT_REVISION)
 
492
        finally:
 
493
            basis.unlock()
493
494
 
494
495
    def get_parent_ids(self):
495
496
        """See Tree.get_parent_ids.
503
504
        else:
504
505
            parents = [last_rev]
505
506
        try:
506
 
            merges_file = self._control_files.get_utf8('pending-merges')
 
507
            merges_file = self._control_files.get('pending-merges')
507
508
        except errors.NoSuchFile:
508
509
            pass
509
510
        else:
510
511
            for l in merges_file.readlines():
511
 
                parents.append(l.rstrip('\n'))
 
512
                revision_id = osutils.safe_revision_id(l.rstrip('\n'))
 
513
                parents.append(revision_id)
512
514
        return parents
513
515
 
514
516
    @needs_read_lock
518
520
        
519
521
    def _get_store_filename(self, file_id):
520
522
        ## XXX: badly named; this is not in the store at all
 
523
        file_id = osutils.safe_file_id(file_id)
521
524
        return self.abspath(self.id2path(file_id))
522
525
 
523
526
    @needs_read_lock
556
559
            tree.set_parent_ids([revision_id])
557
560
 
558
561
    def id2abspath(self, file_id):
 
562
        file_id = osutils.safe_file_id(file_id)
559
563
        return self.abspath(self.id2path(file_id))
560
564
 
561
565
    def has_id(self, file_id):
562
566
        # files that have been deleted are excluded
563
 
        inv = self._inventory
 
567
        file_id = osutils.safe_file_id(file_id)
 
568
        inv = self.inventory
564
569
        if not inv.has_id(file_id):
565
570
            return False
566
571
        path = inv.id2path(file_id)
567
572
        return osutils.lexists(self.abspath(path))
568
573
 
569
574
    def has_or_had_id(self, file_id):
 
575
        file_id = osutils.safe_file_id(file_id)
570
576
        if file_id == self.inventory.root.file_id:
571
577
            return True
572
578
        return self.inventory.has_id(file_id)
574
580
    __contains__ = has_id
575
581
 
576
582
    def get_file_size(self, file_id):
 
583
        file_id = osutils.safe_file_id(file_id)
577
584
        return os.path.getsize(self.id2abspath(file_id))
578
585
 
579
586
    @needs_read_lock
580
587
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
588
        file_id = osutils.safe_file_id(file_id)
581
589
        if not path:
582
590
            path = self._inventory.id2path(file_id)
583
591
        return self._hashcache.get_sha1(path, stat_value)
584
592
 
585
593
    def get_file_mtime(self, file_id, path=None):
 
594
        file_id = osutils.safe_file_id(file_id)
586
595
        if not path:
587
 
            path = self._inventory.id2path(file_id)
 
596
            path = self.inventory.id2path(file_id)
588
597
        return os.lstat(self.abspath(path)).st_mtime
589
598
 
590
599
    if not supports_executable():
591
600
        def is_executable(self, file_id, path=None):
 
601
            file_id = osutils.safe_file_id(file_id)
592
602
            return self._inventory[file_id].executable
593
603
    else:
594
604
        def is_executable(self, file_id, path=None):
595
605
            if not path:
596
 
                path = self._inventory.id2path(file_id)
 
606
                file_id = osutils.safe_file_id(file_id)
 
607
                path = self.id2path(file_id)
597
608
            mode = os.lstat(self.abspath(path)).st_mode
598
609
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
599
610
 
600
 
    @needs_write_lock
 
611
    @needs_tree_write_lock
601
612
    def _add(self, files, ids, kinds):
602
613
        """See MutableTree._add."""
603
614
        # TODO: Re-adding a file that is removed in the working copy
610
621
            if file_id is None:
611
622
                inv.add_path(f, kind=kind)
612
623
            else:
 
624
                file_id = osutils.safe_file_id(file_id)
613
625
                inv.add_path(f, kind=kind, file_id=file_id)
614
626
        self._write_inventory(inv)
615
627
 
704
716
 
705
717
    def _set_merges_from_parent_ids(self, parent_ids):
706
718
        merges = parent_ids[1:]
707
 
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
 
719
        self._control_files.put_bytes('pending-merges', '\n'.join(merges))
708
720
 
709
721
    @needs_tree_write_lock
710
722
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
719
731
        :param revision_ids: The revision_ids to set as the parent ids of this
720
732
            working tree. Any of these may be ghosts.
721
733
        """
 
734
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
722
735
        self._check_parents_for_ghosts(revision_ids,
723
736
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
724
737
 
732
745
    @needs_tree_write_lock
733
746
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
734
747
        """See MutableTree.set_parent_trees."""
735
 
        parent_ids = [rev for (rev, tree) in parents_list]
 
748
        parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
736
749
 
737
750
        self._check_parents_for_ghosts(parent_ids,
738
751
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
766
779
    def set_merge_modified(self, modified_hashes):
767
780
        def iter_stanzas():
768
781
            for file_id, hash in modified_hashes.iteritems():
769
 
                yield Stanza(file_id=file_id, hash=hash)
 
782
                yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
770
783
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
771
784
 
772
 
    @needs_tree_write_lock
773
785
    def _put_rio(self, filename, stanzas, header):
 
786
        self._must_be_locked()
774
787
        my_file = rio_file(stanzas, header)
775
788
        self._control_files.put(filename, my_file)
776
789
 
795
808
            merger.check_basis(check_clean=True, require_commits=False)
796
809
            if to_revision is None:
797
810
                to_revision = branch.last_revision()
 
811
            else:
 
812
                to_revision = osutils.safe_revision_id(to_revision)
798
813
            merger.other_rev_id = to_revision
799
814
            if merger.other_rev_id is None:
800
815
                raise error.NoCommits(branch)
802
817
            merger.other_basis = merger.other_rev_id
803
818
            merger.other_tree = self.branch.repository.revision_tree(
804
819
                merger.other_rev_id)
 
820
            merger.other_branch = branch
805
821
            merger.pp.next_phase()
806
822
            merger.find_base()
807
823
            if merger.base_rev_id == merger.other_rev_id:
819
835
 
820
836
    @needs_read_lock
821
837
    def merge_modified(self):
 
838
        """Return a dictionary of files modified by a merge.
 
839
 
 
840
        The list is initialized by WorkingTree.set_merge_modified, which is 
 
841
        typically called after we make some automatic updates to the tree
 
842
        because of a merge.
 
843
 
 
844
        This returns a map of file_id->sha1, containing only files which are
 
845
        still in the working inventory and have that text hash.
 
846
        """
822
847
        try:
823
848
            hashfile = self._control_files.get('merge-hashes')
824
849
        except errors.NoSuchFile:
830
855
        except StopIteration:
831
856
            raise errors.MergeModifiedFormatError()
832
857
        for s in RioReader(hashfile):
833
 
            file_id = s.get("file_id")
 
858
            # RioReader reads in Unicode, so convert file_ids back to utf8
 
859
            file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
834
860
            if file_id not in self.inventory:
835
861
                continue
836
 
            hash = s.get("hash")
837
 
            if hash == self.get_file_sha1(file_id):
838
 
                merge_hashes[file_id] = hash
 
862
            text_hash = s.get("hash")
 
863
            if text_hash == self.get_file_sha1(file_id):
 
864
                merge_hashes[file_id] = text_hash
839
865
        return merge_hashes
840
866
 
841
867
    @needs_write_lock
848
874
        return file_id
849
875
 
850
876
    def get_symlink_target(self, file_id):
 
877
        file_id = osutils.safe_file_id(file_id)
851
878
        return os.readlink(self.id2abspath(file_id))
852
879
 
853
 
    def file_class(self, filename):
854
 
        if self.path2id(filename):
855
 
            return 'V'
856
 
        elif self.is_ignored(filename):
857
 
            return 'I'
858
 
        else:
859
 
            return '?'
 
880
    @needs_write_lock
 
881
    def subsume(self, other_tree):
 
882
        def add_children(inventory, entry):
 
883
            for child_entry in entry.children.values():
 
884
                inventory._byid[child_entry.file_id] = child_entry
 
885
                if child_entry.kind == 'directory':
 
886
                    add_children(inventory, child_entry)
 
887
        if other_tree.get_root_id() == self.get_root_id():
 
888
            raise errors.BadSubsumeSource(self, other_tree,
 
889
                                          'Trees have the same root')
 
890
        try:
 
891
            other_tree_path = self.relpath(other_tree.basedir)
 
892
        except errors.PathNotChild:
 
893
            raise errors.BadSubsumeSource(self, other_tree,
 
894
                'Tree is not contained by the other')
 
895
        new_root_parent = self.path2id(osutils.dirname(other_tree_path))
 
896
        if new_root_parent is None:
 
897
            raise errors.BadSubsumeSource(self, other_tree,
 
898
                'Parent directory is not versioned.')
 
899
        # We need to ensure that the result of a fetch will have a
 
900
        # versionedfile for the other_tree root, and only fetching into
 
901
        # RepositoryKnit2 guarantees that.
 
902
        if not self.branch.repository.supports_rich_root():
 
903
            raise errors.SubsumeTargetNeedsUpgrade(other_tree)
 
904
        other_tree.lock_tree_write()
 
905
        try:
 
906
            new_parents = other_tree.get_parent_ids()
 
907
            other_root = other_tree.inventory.root
 
908
            other_root.parent_id = new_root_parent
 
909
            other_root.name = osutils.basename(other_tree_path)
 
910
            self.inventory.add(other_root)
 
911
            add_children(self.inventory, other_root)
 
912
            self._write_inventory(self.inventory)
 
913
            # normally we don't want to fetch whole repositories, but i think
 
914
            # here we really do want to consolidate the whole thing.
 
915
            for parent_id in other_tree.get_parent_ids():
 
916
                self.branch.fetch(other_tree.branch, parent_id)
 
917
                self.add_parent_tree_id(parent_id)
 
918
        finally:
 
919
            other_tree.unlock()
 
920
        other_tree.bzrdir.retire_bzrdir()
 
921
 
 
922
    @needs_tree_write_lock
 
923
    def extract(self, file_id, format=None):
 
924
        """Extract a subtree from this tree.
 
925
        
 
926
        A new branch will be created, relative to the path for this tree.
 
927
        """
 
928
        self.flush()
 
929
        def mkdirs(path):
 
930
            segments = osutils.splitpath(path)
 
931
            transport = self.branch.bzrdir.root_transport
 
932
            for name in segments:
 
933
                transport = transport.clone(name)
 
934
                try:
 
935
                    transport.mkdir('.')
 
936
                except errors.FileExists:
 
937
                    pass
 
938
            return transport
 
939
            
 
940
        sub_path = self.id2path(file_id)
 
941
        branch_transport = mkdirs(sub_path)
 
942
        if format is None:
 
943
            format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
 
944
        try:
 
945
            branch_transport.mkdir('.')
 
946
        except errors.FileExists:
 
947
            pass
 
948
        branch_bzrdir = format.initialize_on_transport(branch_transport)
 
949
        try:
 
950
            repo = branch_bzrdir.find_repository()
 
951
        except errors.NoRepositoryPresent:
 
952
            repo = branch_bzrdir.create_repository()
 
953
            assert repo.supports_rich_root()
 
954
        else:
 
955
            if not repo.supports_rich_root():
 
956
                raise errors.RootNotRich()
 
957
        new_branch = branch_bzrdir.create_branch()
 
958
        new_branch.pull(self.branch)
 
959
        for parent_id in self.get_parent_ids():
 
960
            new_branch.fetch(self.branch, parent_id)
 
961
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
 
962
        if tree_transport.base != branch_transport.base:
 
963
            tree_bzrdir = format.initialize_on_transport(tree_transport)
 
964
            branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
 
965
        else:
 
966
            tree_bzrdir = branch_bzrdir
 
967
        wt = tree_bzrdir.create_workingtree(NULL_REVISION)
 
968
        wt.set_parent_ids(self.get_parent_ids())
 
969
        my_inv = self.inventory
 
970
        child_inv = Inventory(root_id=None)
 
971
        new_root = my_inv[file_id]
 
972
        my_inv.remove_recursive_id(file_id)
 
973
        new_root.parent_id = None
 
974
        child_inv.add(new_root)
 
975
        self._write_inventory(my_inv)
 
976
        wt._write_inventory(child_inv)
 
977
        return wt
 
978
 
 
979
    def _serialize(self, inventory, out_file):
 
980
        xml5.serializer_v5.write_inventory(self._inventory, out_file)
 
981
 
 
982
    def _deserialize(selt, in_file):
 
983
        return xml5.serializer_v5.read_inventory(in_file)
860
984
 
861
985
    def flush(self):
862
986
        """Write the in memory inventory to disk."""
864
988
        if self._control_files._lock_mode != 'w':
865
989
            raise errors.NotWriteLocked(self)
866
990
        sio = StringIO()
867
 
        xml5.serializer_v5.write_inventory(self._inventory, sio)
 
991
        self._serialize(self._inventory, sio)
868
992
        sio.seek(0)
869
993
        self._control_files.put('inventory', sio)
870
994
        self._inventory_is_modified = False
871
995
 
 
996
    def _kind(self, relpath):
 
997
        return osutils.file_kind(self.abspath(relpath))
 
998
 
872
999
    def list_files(self, include_root=False):
873
1000
        """Recursively list all files as (path, class, kind, id, entry).
874
1001
 
879
1006
 
880
1007
        Skips the control directory.
881
1008
        """
882
 
        inv = self._inventory
 
1009
        # list_files is an iterator, so @needs_read_lock doesn't work properly
 
1010
        # with it. So callers should be careful to always read_lock the tree.
 
1011
        if not self.is_locked():
 
1012
            raise errors.ObjectNotLocked(self)
 
1013
 
 
1014
        inv = self.inventory
883
1015
        if include_root is True:
884
1016
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
885
1017
        # Convert these into local objects to save lookup times
886
1018
        pathjoin = osutils.pathjoin
887
 
        file_kind = osutils.file_kind
 
1019
        file_kind = self._kind
888
1020
 
889
1021
        # transport.base ends in a slash, we want the piece
890
1022
        # between the last two slashes
950
1082
 
951
1083
                fk = file_kind(fap)
952
1084
 
953
 
                if f_ie:
954
 
                    if f_ie.kind != fk:
955
 
                        raise errors.BzrCheckError(
956
 
                            "file %r entered as kind %r id %r, now of kind %r"
957
 
                            % (fap, f_ie.kind, f_ie.file_id, fk))
958
 
 
959
1085
                # make a last minute entry
960
1086
                if f_ie:
961
1087
                    yield fp[1:], c, fk, f_ie.file_id, f_ie
1267
1393
        These are files in the working directory that are not versioned or
1268
1394
        control files or ignored.
1269
1395
        """
1270
 
        for subp in self.extras():
1271
 
            if not self.is_ignored(subp):
1272
 
                yield subp
 
1396
        # force the extras method to be fully executed before returning, to 
 
1397
        # prevent race conditions with the lock
 
1398
        return iter(
 
1399
            [subp for subp in self.extras() if not self.is_ignored(subp)])
1273
1400
    
1274
1401
    @needs_tree_write_lock
1275
1402
    def unversion(self, file_ids):
1282
1409
        :raises: NoSuchId if any fileid is not currently versioned.
1283
1410
        """
1284
1411
        for file_id in file_ids:
 
1412
            file_id = osutils.safe_file_id(file_id)
1285
1413
            if self._inventory.has_id(file_id):
1286
1414
                self._inventory.remove_recursive_id(file_id)
1287
1415
            else:
1315
1443
                yield stem
1316
1444
 
1317
1445
    @needs_write_lock
1318
 
    def pull(self, source, overwrite=False, stop_revision=None):
 
1446
    def pull(self, source, overwrite=False, stop_revision=None,
 
1447
             change_reporter=None):
1319
1448
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1320
1449
        source.lock_read()
1321
1450
        try:
1329
1458
                pp.next_phase()
1330
1459
                repository = self.branch.repository
1331
1460
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1461
                basis_tree.lock_read()
1332
1462
                try:
1333
1463
                    new_basis_tree = self.branch.basis_tree()
1334
1464
                    merge.merge_inner(
1336
1466
                                new_basis_tree,
1337
1467
                                basis_tree,
1338
1468
                                this_tree=self,
1339
 
                                pb=pb)
 
1469
                                pb=pb,
 
1470
                                change_reporter=change_reporter)
1340
1471
                    if (basis_tree.inventory.root is None and
1341
1472
                        new_basis_tree.inventory.root is not None):
1342
1473
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
1343
1474
                finally:
1344
1475
                    pb.finished()
 
1476
                    basis_tree.unlock()
1345
1477
                # TODO - dedup parents list with things merged by pull ?
1346
1478
                # reuse the revisiontree we merged against to set the new
1347
1479
                # tree data.
1349
1481
                # we have to pull the merge trees out again, because 
1350
1482
                # merge_inner has set the ids. - this corner is not yet 
1351
1483
                # layered well enough to prevent double handling.
 
1484
                # XXX TODO: Fix the double handling: telling the tree about
 
1485
                # the already known parent data is wasteful.
1352
1486
                merges = self.get_parent_ids()[1:]
1353
1487
                parent_trees.extend([
1354
1488
                    (parent, repository.revision_tree(parent)) for
1362
1496
    @needs_write_lock
1363
1497
    def put_file_bytes_non_atomic(self, file_id, bytes):
1364
1498
        """See MutableTree.put_file_bytes_non_atomic."""
 
1499
        file_id = osutils.safe_file_id(file_id)
1365
1500
        stream = file(self.id2abspath(file_id), 'wb')
1366
1501
        try:
1367
1502
            stream.write(bytes)
1370
1505
        # TODO: update the hashcache here ?
1371
1506
 
1372
1507
    def extras(self):
1373
 
        """Yield all unknown files in this WorkingTree.
 
1508
        """Yield all unversioned files in this WorkingTree.
1374
1509
 
1375
 
        If there are any unknown directories then only the directory is
1376
 
        returned, not all its children.  But if there are unknown files
 
1510
        If there are any unversioned directories then only the directory is
 
1511
        returned, not all its children.  But if there are unversioned files
1377
1512
        under a versioned subdirectory, they are returned.
1378
1513
 
1379
1514
        Currently returned depth-first, sorted by name within directories.
 
1515
        This is the same order used by 'osutils.walkdirs'.
1380
1516
        """
1381
1517
        ## TODO: Work from given directory downwards
1382
1518
        for path, dir_entry in self.inventory.directories():
1403
1539
                subp = pathjoin(path, subf)
1404
1540
                yield subp
1405
1541
 
1406
 
 
1407
1542
    def ignored_files(self):
1408
1543
        """Yield list of PATH, IGNORE_PATTERN"""
1409
1544
        for subp in self.extras():
1494
1629
    def is_locked(self):
1495
1630
        return self._control_files.is_locked()
1496
1631
 
 
1632
    def _must_be_locked(self):
 
1633
        if not self.is_locked():
 
1634
            raise errors.ObjectNotLocked(self)
 
1635
 
1497
1636
    def lock_read(self):
1498
1637
        """See Branch.lock_read, and WorkingTree.unlock."""
 
1638
        if not self.is_locked():
 
1639
            self._reset_data()
1499
1640
        self.branch.lock_read()
1500
1641
        try:
1501
1642
            return self._control_files.lock_read()
1505
1646
 
1506
1647
    def lock_tree_write(self):
1507
1648
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1649
        if not self.is_locked():
 
1650
            self._reset_data()
1508
1651
        self.branch.lock_read()
1509
1652
        try:
1510
1653
            return self._control_files.lock_write()
1514
1657
 
1515
1658
    def lock_write(self):
1516
1659
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
1660
        if not self.is_locked():
 
1661
            self._reset_data()
1517
1662
        self.branch.lock_write()
1518
1663
        try:
1519
1664
            return self._control_files.lock_write()
1527
1672
    def _basis_inventory_name(self):
1528
1673
        return 'basis-inventory-cache'
1529
1674
 
 
1675
    def _reset_data(self):
 
1676
        """Reset transient data that cannot be revalidated."""
 
1677
        self._inventory_is_modified = False
 
1678
        result = self._deserialize(self._control_files.get('inventory'))
 
1679
        self._set_inventory(result, dirty=False)
 
1680
 
1530
1681
    @needs_tree_write_lock
1531
1682
    def set_last_revision(self, new_revision):
1532
1683
        """Change the last revision in the working tree."""
 
1684
        new_revision = osutils.safe_revision_id(new_revision)
1533
1685
        if self._change_last_revision(new_revision):
1534
1686
            self._cache_basis_inventory(new_revision)
1535
1687
 
1558
1710
 
1559
1711
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1560
1712
        """Create the text that will be saved in basis-inventory"""
1561
 
        inventory.revision_id = revision_id
1562
 
        return xml6.serializer_v6.write_inventory_to_string(inventory)
 
1713
        # TODO: jam 20070209 This should be redundant, as the revision_id
 
1714
        #       as all callers should have already converted the revision_id to
 
1715
        #       utf8
 
1716
        inventory.revision_id = osutils.safe_revision_id(revision_id)
 
1717
        return xml7.serializer_v7.write_inventory_to_string(inventory)
1563
1718
 
1564
1719
    def _cache_basis_inventory(self, new_revision):
1565
1720
        """Cache new_revision as the basis inventory."""
1580
1735
            xml = self.branch.repository.get_inventory_xml(new_revision)
1581
1736
            firstline = xml.split('\n', 1)[0]
1582
1737
            if (not 'revision_id="' in firstline or 
1583
 
                'format="6"' not in firstline):
 
1738
                'format="7"' not in firstline):
1584
1739
                inv = self.branch.repository.deserialise_inventory(
1585
1740
                    new_revision, xml)
1586
1741
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
1606
1761
        # binary.
1607
1762
        if self._inventory_is_modified:
1608
1763
            raise errors.InventoryModified(self)
1609
 
        result = xml5.serializer_v5.read_inventory(
1610
 
            self._control_files.get('inventory'))
 
1764
        result = self._deserialize(self._control_files.get('inventory'))
1611
1765
        self._set_inventory(result, dirty=False)
1612
1766
        return result
1613
1767
 
1666
1820
            resolve(self, filenames, ignore_misses=True)
1667
1821
        return conflicts
1668
1822
 
 
1823
    def revision_tree(self, revision_id):
 
1824
        """See Tree.revision_tree.
 
1825
 
 
1826
        WorkingTree can supply revision_trees for the basis revision only
 
1827
        because there is only one cached inventory in the bzr directory.
 
1828
        """
 
1829
        if revision_id == self.last_revision():
 
1830
            try:
 
1831
                xml = self.read_basis_inventory()
 
1832
            except errors.NoSuchFile:
 
1833
                pass
 
1834
            else:
 
1835
                try:
 
1836
                    inv = xml7.serializer_v7.read_inventory_from_string(xml)
 
1837
                    # dont use the repository revision_tree api because we want
 
1838
                    # to supply the inventory.
 
1839
                    if inv.revision_id == revision_id:
 
1840
                        return revisiontree.RevisionTree(self.branch.repository,
 
1841
                            inv, revision_id)
 
1842
                except errors.BadInventoryFormat:
 
1843
                    pass
 
1844
        # raise if there was no inventory, or if we read the wrong inventory.
 
1845
        raise errors.NoSuchRevisionInTree(self, revision_id)
 
1846
 
1669
1847
    # XXX: This method should be deprecated in favour of taking in a proper
1670
1848
    # new Inventory object.
1671
1849
    @needs_tree_write_lock
1701
1879
                DeprecationWarning,
1702
1880
                stacklevel=3)
1703
1881
            file_id = ROOT_ID
 
1882
        else:
 
1883
            file_id = osutils.safe_file_id(file_id)
 
1884
        self._set_root_id(file_id)
 
1885
 
 
1886
    def _set_root_id(self, file_id):
 
1887
        """Set the root id for this tree, in a format specific manner.
 
1888
 
 
1889
        :param file_id: The file id to assign to the root. It must not be 
 
1890
            present in the current inventory or an error will occur. It must
 
1891
            not be None, but rather a valid file id.
 
1892
        """
1704
1893
        inv = self._inventory
1705
1894
        orig_root_id = inv.root.file_id
1706
1895
        # TODO: it might be nice to exit early if there was nothing
1796
1985
        if last_rev != self.branch.last_revision():
1797
1986
            # merge tree state up to new branch tip.
1798
1987
            basis = self.basis_tree()
1799
 
            to_tree = self.branch.basis_tree()
1800
 
            if basis.inventory.root is None:
1801
 
                self.set_root_id(to_tree.inventory.root.file_id)
1802
 
            result += merge.merge_inner(
1803
 
                                  self.branch,
1804
 
                                  to_tree,
1805
 
                                  basis,
1806
 
                                  this_tree=self)
 
1988
            basis.lock_read()
 
1989
            try:
 
1990
                to_tree = self.branch.basis_tree()
 
1991
                if basis.inventory.root is None:
 
1992
                    self.set_root_id(to_tree.inventory.root.file_id)
 
1993
                    self.flush()
 
1994
                result += merge.merge_inner(
 
1995
                                      self.branch,
 
1996
                                      to_tree,
 
1997
                                      basis,
 
1998
                                      this_tree=self)
 
1999
            finally:
 
2000
                basis.unlock()
1807
2001
            # TODO - dedup parents list with things merged by pull ?
1808
2002
            # reuse the tree we've updated to to set the basis:
1809
2003
            parent_trees = [(self.branch.last_revision(), to_tree)]
1832
2026
            # and we have converted that last revision to a pending merge.
1833
2027
            # base is somewhere between the branch tip now
1834
2028
            # and the now pending merge
 
2029
 
 
2030
            # Since we just modified the working tree and inventory, flush out
 
2031
            # the current state, before we modify it again.
 
2032
            # TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
 
2033
            #       requires it only because TreeTransform directly munges the
 
2034
            #       inventory and calls tree._write_inventory(). Ultimately we
 
2035
            #       should be able to remove this extra flush.
 
2036
            self.flush()
1835
2037
            from bzrlib.revision import common_ancestor
1836
2038
            try:
1837
2039
                base_rev_id = common_ancestor(self.branch.last_revision(),
1900
2102
                             file_id=self.path2id(conflicted)))
1901
2103
        return conflicts
1902
2104
 
 
2105
    def walkdirs(self, prefix=""):
 
2106
        """Walk the directories of this tree.
 
2107
 
 
2108
        This API returns a generator, which is only valid during the current
 
2109
        tree transaction - within a single lock_read or lock_write duration.
 
2110
 
 
2111
        If the tree is not locked, it may cause an error to be raised, depending
 
2112
        on the tree implementation.
 
2113
        """
 
2114
        disk_top = self.abspath(prefix)
 
2115
        if disk_top.endswith('/'):
 
2116
            disk_top = disk_top[:-1]
 
2117
        top_strip_len = len(disk_top) + 1
 
2118
        inventory_iterator = self._walkdirs(prefix)
 
2119
        disk_iterator = osutils.walkdirs(disk_top, prefix)
 
2120
        try:
 
2121
            current_disk = disk_iterator.next()
 
2122
            disk_finished = False
 
2123
        except OSError, e:
 
2124
            if e.errno != errno.ENOENT:
 
2125
                raise
 
2126
            current_disk = None
 
2127
            disk_finished = True
 
2128
        try:
 
2129
            current_inv = inventory_iterator.next()
 
2130
            inv_finished = False
 
2131
        except StopIteration:
 
2132
            current_inv = None
 
2133
            inv_finished = True
 
2134
        while not inv_finished or not disk_finished:
 
2135
            if not disk_finished:
 
2136
                # strip out .bzr dirs
 
2137
                if current_disk[0][1][top_strip_len:] == '':
 
2138
                    # osutils.walkdirs can be made nicer - 
 
2139
                    # yield the path-from-prefix rather than the pathjoined
 
2140
                    # value.
 
2141
                    bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
 
2142
                    if current_disk[1][bzrdir_loc][0] == '.bzr':
 
2143
                        # we dont yield the contents of, or, .bzr itself.
 
2144
                        del current_disk[1][bzrdir_loc]
 
2145
            if inv_finished:
 
2146
                # everything is unknown
 
2147
                direction = 1
 
2148
            elif disk_finished:
 
2149
                # everything is missing
 
2150
                direction = -1
 
2151
            else:
 
2152
                direction = cmp(current_inv[0][0], current_disk[0][0])
 
2153
            if direction > 0:
 
2154
                # disk is before inventory - unknown
 
2155
                dirblock = [(relpath, basename, kind, stat, None, None) for
 
2156
                    relpath, basename, kind, stat, top_path in current_disk[1]]
 
2157
                yield (current_disk[0][0], None), dirblock
 
2158
                try:
 
2159
                    current_disk = disk_iterator.next()
 
2160
                except StopIteration:
 
2161
                    disk_finished = True
 
2162
            elif direction < 0:
 
2163
                # inventory is before disk - missing.
 
2164
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
 
2165
                    for relpath, basename, dkind, stat, fileid, kind in 
 
2166
                    current_inv[1]]
 
2167
                yield (current_inv[0][0], current_inv[0][1]), dirblock
 
2168
                try:
 
2169
                    current_inv = inventory_iterator.next()
 
2170
                except StopIteration:
 
2171
                    inv_finished = True
 
2172
            else:
 
2173
                # versioned present directory
 
2174
                # merge the inventory and disk data together
 
2175
                dirblock = []
 
2176
                for relpath, subiterator in itertools.groupby(sorted(
 
2177
                    current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
 
2178
                    path_elements = list(subiterator)
 
2179
                    if len(path_elements) == 2:
 
2180
                        inv_row, disk_row = path_elements
 
2181
                        # versioned, present file
 
2182
                        dirblock.append((inv_row[0],
 
2183
                            inv_row[1], disk_row[2],
 
2184
                            disk_row[3], inv_row[4],
 
2185
                            inv_row[5]))
 
2186
                    elif len(path_elements[0]) == 5:
 
2187
                        # unknown disk file
 
2188
                        dirblock.append((path_elements[0][0],
 
2189
                            path_elements[0][1], path_elements[0][2],
 
2190
                            path_elements[0][3], None, None))
 
2191
                    elif len(path_elements[0]) == 6:
 
2192
                        # versioned, absent file.
 
2193
                        dirblock.append((path_elements[0][0],
 
2194
                            path_elements[0][1], 'unknown', None,
 
2195
                            path_elements[0][4], path_elements[0][5]))
 
2196
                    else:
 
2197
                        raise NotImplementedError('unreachable code')
 
2198
                yield current_inv[0], dirblock
 
2199
                try:
 
2200
                    current_inv = inventory_iterator.next()
 
2201
                except StopIteration:
 
2202
                    inv_finished = True
 
2203
                try:
 
2204
                    current_disk = disk_iterator.next()
 
2205
                except StopIteration:
 
2206
                    disk_finished = True
 
2207
 
 
2208
    def _walkdirs(self, prefix=""):
 
2209
        _directory = 'directory'
 
2210
        # get the root in the inventory
 
2211
        inv = self.inventory
 
2212
        top_id = inv.path2id(prefix)
 
2213
        if top_id is None:
 
2214
            pending = []
 
2215
        else:
 
2216
            pending = [(prefix, '', _directory, None, top_id, None)]
 
2217
        while pending:
 
2218
            dirblock = []
 
2219
            currentdir = pending.pop()
 
2220
            # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
 
2221
            top_id = currentdir[4]
 
2222
            if currentdir[0]:
 
2223
                relroot = currentdir[0] + '/'
 
2224
            else:
 
2225
                relroot = ""
 
2226
            # FIXME: stash the node in pending
 
2227
            entry = inv[top_id]
 
2228
            for name, child in entry.sorted_children():
 
2229
                dirblock.append((relroot + name, name, child.kind, None,
 
2230
                    child.file_id, child.kind
 
2231
                    ))
 
2232
            yield (currentdir[0], entry.file_id), dirblock
 
2233
            # push the user specified dirs from dirblock
 
2234
            for dir in reversed(dirblock):
 
2235
                if dir[2] == _directory:
 
2236
                    pending.append(dir)
 
2237
 
 
2238
    @needs_tree_write_lock
 
2239
    def auto_resolve(self):
 
2240
        """Automatically resolve text conflicts according to contents.
 
2241
 
 
2242
        Only text conflicts are auto_resolvable. Files with no conflict markers
 
2243
        are considered 'resolved', because bzr always puts conflict markers
 
2244
        into files that have text conflicts.  The corresponding .THIS .BASE and
 
2245
        .OTHER files are deleted, as per 'resolve'.
 
2246
        :return: a tuple of ConflictLists: (un_resolved, resolved).
 
2247
        """
 
2248
        un_resolved = _mod_conflicts.ConflictList()
 
2249
        resolved = _mod_conflicts.ConflictList()
 
2250
        conflict_re = re.compile('^(<{7}|={7}|>{7})')
 
2251
        for conflict in self.conflicts():
 
2252
            if (conflict.typestring != 'text conflict' or
 
2253
                self.kind(conflict.file_id) != 'file'):
 
2254
                un_resolved.append(conflict)
 
2255
                continue
 
2256
            my_file = open(self.id2abspath(conflict.file_id), 'rb')
 
2257
            try:
 
2258
                for line in my_file:
 
2259
                    if conflict_re.search(line):
 
2260
                        un_resolved.append(conflict)
 
2261
                        break
 
2262
                else:
 
2263
                    resolved.append(conflict)
 
2264
            finally:
 
2265
                my_file.close()
 
2266
        resolved.remove_files(self)
 
2267
        self.set_conflicts(un_resolved)
 
2268
        return un_resolved, resolved
 
2269
 
1903
2270
 
1904
2271
class WorkingTree2(WorkingTree):
1905
2272
    """This is the Format 2 working tree.
1909
2276
     - uses the branch last-revision.
1910
2277
    """
1911
2278
 
 
2279
    def __init__(self, *args, **kwargs):
 
2280
        super(WorkingTree2, self).__init__(*args, **kwargs)
 
2281
        # WorkingTree2 has more of a constraint that self._inventory must
 
2282
        # exist. Because this is an older format, we don't mind the overhead
 
2283
        # caused by the extra computation here.
 
2284
 
 
2285
        # Newer WorkingTree's should only have self._inventory set when they
 
2286
        # have a read lock.
 
2287
        if self._inventory is None:
 
2288
            self.read_working_inventory()
 
2289
 
1912
2290
    def lock_tree_write(self):
1913
2291
        """See WorkingTree.lock_tree_write().
1914
2292
 
1951
2329
    def _last_revision(self):
1952
2330
        """See Mutable.last_revision."""
1953
2331
        try:
1954
 
            return self._control_files.get_utf8('last-revision').read()
 
2332
            return osutils.safe_revision_id(
 
2333
                        self._control_files.get('last-revision').read())
1955
2334
        except errors.NoSuchFile:
1956
2335
            return None
1957
2336
 
1964
2343
                pass
1965
2344
            return False
1966
2345
        else:
1967
 
            self._control_files.put_utf8('last-revision', revision_id)
 
2346
            self._control_files.put_bytes('last-revision', revision_id)
1968
2347
            return True
1969
2348
 
1970
2349
    @needs_tree_write_lock
2010
2389
        if path.endswith(suffix):
2011
2390
            return path[:-len(suffix)]
2012
2391
 
 
2392
 
2013
2393
@deprecated_function(zero_eight)
2014
2394
def is_control_file(filename):
2015
2395
    """See WorkingTree.is_control_filename(filename)."""
2050
2430
    _formats = {}
2051
2431
    """The known formats."""
2052
2432
 
 
2433
    requires_rich_root = False
 
2434
 
2053
2435
    @classmethod
2054
2436
    def find_format(klass, a_bzrdir):
2055
2437
        """Return the format for the working tree object in a_bzrdir."""
2062
2444
        except KeyError:
2063
2445
            raise errors.UnknownFormatError(format=format_string)
2064
2446
 
 
2447
    def __eq__(self, other):
 
2448
        return self.__class__ is other.__class__
 
2449
 
 
2450
    def __ne__(self, other):
 
2451
        return not (self == other)
 
2452
 
2065
2453
    @classmethod
2066
2454
    def get_default_format(klass):
2067
2455
        """Return the current default format."""
2122
2510
        sio.seek(0)
2123
2511
        control_files.put('inventory', sio)
2124
2512
 
2125
 
        control_files.put_utf8('pending-merges', '')
 
2513
        control_files.put_bytes('pending-merges', '')
2126
2514
        
2127
2515
 
2128
2516
    def initialize(self, a_bzrdir, revision_id=None):
2131
2519
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2132
2520
        branch = a_bzrdir.open_branch()
2133
2521
        if revision_id is not None:
 
2522
            revision_id = osutils.safe_revision_id(revision_id)
2134
2523
            branch.lock_write()
2135
2524
            try:
2136
2525
                revision_history = branch.revision_history()
2201
2590
    _lock_file_name = 'lock'
2202
2591
    _lock_class = LockDir
2203
2592
 
 
2593
    _tree_class = WorkingTree3
 
2594
 
 
2595
    def __get_matchingbzrdir(self):
 
2596
        return bzrdir.BzrDirMetaFormat1()
 
2597
 
 
2598
    _matchingbzrdir = property(__get_matchingbzrdir)
 
2599
 
2204
2600
    def _open_control_files(self, a_bzrdir):
2205
2601
        transport = a_bzrdir.get_workingtree_transport(None)
2206
2602
        return LockableFiles(transport, self._lock_file_name, 
2222
2618
        branch = a_bzrdir.open_branch()
2223
2619
        if revision_id is None:
2224
2620
            revision_id = branch.last_revision()
 
2621
        else:
 
2622
            revision_id = osutils.safe_revision_id(revision_id)
2225
2623
        # WorkingTree3 can handle an inventory which has a unique root id.
2226
2624
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2227
2625
        # those trees. And because there isn't a format bump inbetween, we
2228
2626
        # are maintaining compatibility with older clients.
2229
2627
        # inv = Inventory(root_id=gen_root_id())
2230
 
        inv = Inventory()
2231
 
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
 
2628
        inv = self._initial_inventory()
 
2629
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2232
2630
                         branch,
2233
2631
                         inv,
2234
2632
                         _internal=True,
2253
2651
            wt.unlock()
2254
2652
        return wt
2255
2653
 
 
2654
    def _initial_inventory(self):
 
2655
        return Inventory()
 
2656
 
2256
2657
    def __init__(self):
2257
2658
        super(WorkingTreeFormat3, self).__init__()
2258
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2259
2659
 
2260
2660
    def open(self, a_bzrdir, _found=False):
2261
2661
        """Return the WorkingTree object for a_bzrdir
2276
2676
        :param a_bzrdir: the dir for the tree.
2277
2677
        :param control_files: the control files for the tree.
2278
2678
        """
2279
 
        return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2280
 
                           _internal=True,
2281
 
                           _format=self,
2282
 
                           _bzrdir=a_bzrdir,
2283
 
                           _control_files=control_files)
 
2679
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
2680
                                _internal=True,
 
2681
                                _format=self,
 
2682
                                _bzrdir=a_bzrdir,
 
2683
                                _control_files=control_files)
2284
2684
 
2285
2685
    def __str__(self):
2286
2686
        return self.get_format_string()
2287
2687
 
2288
2688
 
 
2689
__default_format = WorkingTreeFormat4()
 
2690
WorkingTreeFormat.register_format(__default_format)
 
2691
WorkingTreeFormat.register_format(WorkingTreeFormat3())
 
2692
WorkingTreeFormat.set_default_format(__default_format)
2289
2693
# formats which have no format string are not discoverable
2290
2694
# and not independently creatable, so are not registered.
2291
 
__default_format = WorkingTreeFormat3()
2292
 
WorkingTreeFormat.register_format(__default_format)
2293
 
WorkingTreeFormat.set_default_format(__default_format)
2294
2695
_legacy_formats = [WorkingTreeFormat2(),
2295
2696
                   ]
2296
2697