~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Robert Collins
  • Date: 2007-09-05 05:51:34 UTC
  • mto: (2592.3.126 repository)
  • mto: This revision was merged to the branch mainline in revision 2879.
  • Revision ID: robertc@robertcollins.net-20070905055134-pwbueao0qq6krf9u
nuke _read_tree_state and snapshot from inventory, moving responsibility into the commit builder.

Show diffs side-by-side

added added

removed removed

Lines of Context:
64
64
    hashcache,
65
65
    ignores,
66
66
    merge,
 
67
    osutils,
67
68
    revision as _mod_revision,
68
69
    revisiontree,
69
70
    repository,
89
90
from bzrlib.lockdir import LockDir
90
91
import bzrlib.mutabletree
91
92
from bzrlib.mutabletree import needs_tree_write_lock
92
 
from bzrlib import osutils
93
93
from bzrlib.osutils import (
94
94
    compact_date,
95
95
    file_kind,
111
111
        deprecated_method,
112
112
        deprecated_function,
113
113
        DEPRECATED_PARAMETER,
 
114
        zero_eight,
 
115
        zero_eleven,
 
116
        zero_thirteen,
114
117
        )
115
118
 
116
119
 
120
123
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
121
124
 
122
125
 
 
126
@deprecated_function(zero_thirteen)
 
127
def gen_file_id(name):
 
128
    """Return new file id for the basename 'name'.
 
129
 
 
130
    Use bzrlib.generate_ids.gen_file_id() instead
 
131
    """
 
132
    return generate_ids.gen_file_id(name)
 
133
 
 
134
 
 
135
@deprecated_function(zero_thirteen)
 
136
def gen_root_id():
 
137
    """Return a new tree-root file id.
 
138
 
 
139
    This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
 
140
    """
 
141
    return generate_ids.gen_root_id()
 
142
 
 
143
 
123
144
class TreeEntry(object):
124
145
    """An entry that implements the minimum interface used by commands.
125
146
 
201
222
        if not _internal:
202
223
            raise errors.BzrError("Please use bzrdir.open_workingtree or "
203
224
                "WorkingTree.open() to obtain a WorkingTree.")
 
225
        assert isinstance(basedir, basestring), \
 
226
            "base directory %r is not a string" % basedir
204
227
        basedir = safe_unicode(basedir)
205
228
        mutter("opening working tree %r", basedir)
206
229
        if deprecated_passed(branch):
214
237
            self._control_files = self.branch.control_files
215
238
        else:
216
239
            # assume all other formats have their own control files.
 
240
            assert isinstance(_control_files, LockableFiles), \
 
241
                    "_control_files must be a LockableFiles, not %r" \
 
242
                    % _control_files
217
243
            self._control_files = _control_files
218
 
        self._transport = self._control_files._transport
219
244
        # update the whole cache up front and write to disk if anything changed;
220
245
        # in the future we might want to do this more selectively
221
246
        # two possible ways offer themselves : in self._unlock, write the cache
225
250
        wt_trans = self.bzrdir.get_workingtree_transport(None)
226
251
        cache_filename = wt_trans.local_abspath('stat-cache')
227
252
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
228
 
            self.bzrdir._get_file_mode())
 
253
                                              self._control_files._file_mode)
229
254
        hc = self._hashcache
230
255
        hc.read()
231
256
        # is this scan needed ? it makes things kinda slow.
245
270
            # the Format factory and creation methods that are
246
271
            # permitted to do this.
247
272
            self._set_inventory(_inventory, dirty=False)
248
 
        self._detect_case_handling()
249
 
        self._rules_searcher = None
250
 
 
251
 
    def _detect_case_handling(self):
252
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
253
 
        try:
254
 
            wt_trans.stat("FoRMaT")
255
 
        except errors.NoSuchFile:
256
 
            self.case_sensitive = True
257
 
        else:
258
 
            self.case_sensitive = False
259
 
 
260
 
        self._setup_directory_is_tree_reference()
261
273
 
262
274
    branch = property(
263
275
        fget=lambda self: self._branch,
294
306
            False then the inventory is the same as that on disk and any
295
307
            serialisation would be unneeded overhead.
296
308
        """
 
309
        assert inv.root is not None
297
310
        self._inventory = inv
298
311
        self._inventory_is_modified = dirty
299
312
 
334
347
        """
335
348
        return WorkingTree.open(path, _unsupported=True)
336
349
 
337
 
    @staticmethod
338
 
    def find_trees(location):
339
 
        def list_current(transport):
340
 
            return [d for d in transport.list_dir('') if d != '.bzr']
341
 
        def evaluate(bzrdir):
342
 
            try:
343
 
                tree = bzrdir.open_workingtree()
344
 
            except errors.NoWorkingTree:
345
 
                return True, None
346
 
            else:
347
 
                return True, tree
348
 
        transport = get_transport(location)
349
 
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
350
 
                                              list_current=list_current)
351
 
        return [t for t in iterator if t is not None]
352
 
 
353
350
    # should be deprecated - this is slow and in any case treating them as a
354
351
    # container is (we now know) bad style -- mbp 20070302
355
352
    ## @deprecated_method(zero_fifteen)
364
361
            if osutils.lexists(self.abspath(path)):
365
362
                yield ie.file_id
366
363
 
367
 
    def all_file_ids(self):
368
 
        """See Tree.iter_all_file_ids"""
369
 
        return set(self.inventory)
370
 
 
371
364
    def __repr__(self):
372
365
        return "<%s of %s>" % (self.__class__.__name__,
373
366
                               getattr(self, 'basedir', None))
397
390
        # at this point ?
398
391
        try:
399
392
            return self.branch.repository.revision_tree(revision_id)
400
 
        except (errors.RevisionNotPresent, errors.NoSuchRevision):
 
393
        except errors.RevisionNotPresent:
401
394
            # the basis tree *may* be a ghost or a low level error may have
402
395
            # occured. If the revision is present, its a problem, if its not
403
396
            # its a ghost.
409
402
    def _cleanup(self):
410
403
        self._flush_ignore_list_cache()
411
404
 
 
405
    @staticmethod
 
406
    @deprecated_method(zero_eight)
 
407
    def create(branch, directory):
 
408
        """Create a workingtree for branch at directory.
 
409
 
 
410
        If existing_directory already exists it must have a .bzr directory.
 
411
        If it does not exist, it will be created.
 
412
 
 
413
        This returns a new WorkingTree object for the new checkout.
 
414
 
 
415
        TODO FIXME RBC 20060124 when we have checkout formats in place this
 
416
        should accept an optional revisionid to checkout [and reject this if
 
417
        checking out into the same dir as a pre-checkout-aware branch format.]
 
418
 
 
419
        XXX: When BzrDir is present, these should be created through that 
 
420
        interface instead.
 
421
        """
 
422
        warnings.warn('delete WorkingTree.create', stacklevel=3)
 
423
        transport = get_transport(directory)
 
424
        if branch.bzrdir.root_transport.base == transport.base:
 
425
            # same dir 
 
426
            return branch.bzrdir.create_workingtree()
 
427
        # different directory, 
 
428
        # create a branch reference
 
429
        # and now a working tree.
 
430
        raise NotImplementedError
 
431
 
 
432
    @staticmethod
 
433
    @deprecated_method(zero_eight)
 
434
    def create_standalone(directory):
 
435
        """Create a checkout and a branch and a repo at directory.
 
436
 
 
437
        Directory must exist and be empty.
 
438
 
 
439
        please use BzrDir.create_standalone_workingtree
 
440
        """
 
441
        return bzrdir.BzrDir.create_standalone_workingtree(directory)
 
442
 
412
443
    def relpath(self, path):
413
444
        """Return the local path portion from a given path.
414
445
        
422
453
 
423
454
    def get_file(self, file_id, path=None):
424
455
        if path is None:
 
456
            file_id = osutils.safe_file_id(file_id)
425
457
            path = self.id2path(file_id)
426
458
        return self.get_file_byname(path)
427
459
 
428
460
    def get_file_text(self, file_id):
 
461
        file_id = osutils.safe_file_id(file_id)
429
462
        return self.get_file(file_id).read()
430
463
 
431
464
    def get_file_byname(self, filename):
442
475
        incorrectly attributed to CURRENT_REVISION (but after committing, the
443
476
        attribution will be correct).
444
477
        """
 
478
        file_id = osutils.safe_file_id(file_id)
445
479
        basis = self.basis_tree()
446
480
        basis.lock_read()
447
481
        try:
448
 
            changes = self.iter_changes(basis, True, [self.id2path(file_id)],
 
482
            changes = self._iter_changes(basis, True, [self.id2path(file_id)],
449
483
                require_versioned=True).next()
450
484
            changed_content, kind = changes[2], changes[6]
451
485
            if not changed_content:
487
521
        else:
488
522
            parents = [last_rev]
489
523
        try:
490
 
            merges_file = self._transport.get('pending-merges')
 
524
            merges_file = self._control_files.get('pending-merges')
491
525
        except errors.NoSuchFile:
492
526
            pass
493
527
        else:
494
528
            for l in merges_file.readlines():
495
 
                revision_id = l.rstrip('\n')
 
529
                revision_id = osutils.safe_revision_id(l.rstrip('\n'))
496
530
                parents.append(revision_id)
497
531
        return parents
498
532
 
503
537
        
504
538
    def _get_store_filename(self, file_id):
505
539
        ## XXX: badly named; this is not in the store at all
 
540
        file_id = osutils.safe_file_id(file_id)
506
541
        return self.abspath(self.id2path(file_id))
507
542
 
508
543
    @needs_read_lock
520
555
            and this one merged in.
521
556
        """
522
557
        # assumes the target bzr dir format is compatible.
523
 
        result = to_bzrdir.create_workingtree()
 
558
        result = self._format.initialize(to_bzrdir)
524
559
        self.copy_content_into(result, revision_id)
525
560
        return result
526
561
 
537
572
            tree.set_parent_ids([revision_id])
538
573
 
539
574
    def id2abspath(self, file_id):
 
575
        file_id = osutils.safe_file_id(file_id)
540
576
        return self.abspath(self.id2path(file_id))
541
577
 
542
578
    def has_id(self, file_id):
543
579
        # files that have been deleted are excluded
 
580
        file_id = osutils.safe_file_id(file_id)
544
581
        inv = self.inventory
545
582
        if not inv.has_id(file_id):
546
583
            return False
548
585
        return osutils.lexists(self.abspath(path))
549
586
 
550
587
    def has_or_had_id(self, file_id):
 
588
        file_id = osutils.safe_file_id(file_id)
551
589
        if file_id == self.inventory.root.file_id:
552
590
            return True
553
591
        return self.inventory.has_id(file_id)
555
593
    __contains__ = has_id
556
594
 
557
595
    def get_file_size(self, file_id):
558
 
        """See Tree.get_file_size"""
559
 
        try:
560
 
            return os.path.getsize(self.id2abspath(file_id))
561
 
        except OSError, e:
562
 
            if e.errno != errno.ENOENT:
563
 
                raise
564
 
            else:
565
 
                return None
 
596
        file_id = osutils.safe_file_id(file_id)
 
597
        return os.path.getsize(self.id2abspath(file_id))
566
598
 
567
599
    @needs_read_lock
568
600
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
601
        file_id = osutils.safe_file_id(file_id)
569
602
        if not path:
570
603
            path = self._inventory.id2path(file_id)
571
604
        return self._hashcache.get_sha1(path, stat_value)
572
605
 
573
606
    def get_file_mtime(self, file_id, path=None):
 
607
        file_id = osutils.safe_file_id(file_id)
574
608
        if not path:
575
609
            path = self.inventory.id2path(file_id)
576
610
        return os.lstat(self.abspath(path)).st_mtime
577
611
 
578
 
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
579
 
        file_id = self.path2id(path)
580
 
        return self._inventory[file_id].executable
581
 
 
582
 
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
583
 
        mode = stat_result.st_mode
584
 
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
585
 
 
586
612
    if not supports_executable():
587
613
        def is_executable(self, file_id, path=None):
 
614
            file_id = osutils.safe_file_id(file_id)
588
615
            return self._inventory[file_id].executable
589
 
 
590
 
        _is_executable_from_path_and_stat = \
591
 
            _is_executable_from_path_and_stat_from_basis
592
616
    else:
593
617
        def is_executable(self, file_id, path=None):
594
618
            if not path:
 
619
                file_id = osutils.safe_file_id(file_id)
595
620
                path = self.id2path(file_id)
596
621
            mode = os.lstat(self.abspath(path)).st_mode
597
622
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
598
623
 
599
 
        _is_executable_from_path_and_stat = \
600
 
            _is_executable_from_path_and_stat_from_stat
601
 
 
602
624
    @needs_tree_write_lock
603
625
    def _add(self, files, ids, kinds):
604
626
        """See MutableTree._add."""
608
630
        # function - they should be part of lock_write and unlock.
609
631
        inv = self.inventory
610
632
        for f, file_id, kind in zip(files, ids, kinds):
 
633
            assert kind is not None
611
634
            if file_id is None:
612
635
                inv.add_path(f, kind=kind)
613
636
            else:
 
637
                file_id = osutils.safe_file_id(file_id)
614
638
                inv.add_path(f, kind=kind, file_id=file_id)
615
639
            self._inventory_is_modified = True
616
640
 
688
712
            if getattr(e, 'errno', None) == errno.ENOENT:
689
713
                # no file.
690
714
                return ('missing', None, None, None)
691
 
            # propagate other errors
 
715
            # propogate other errors
692
716
            raise
693
717
        kind = _mapper(stat_result.st_mode)
694
718
        if kind == 'file':
695
719
            size = stat_result.st_size
696
720
            # try for a stat cache lookup
697
 
            executable = self._is_executable_from_path_and_stat(path, stat_result)
698
 
            return (kind, size, executable, self._sha_from_stat(
699
 
                path, stat_result))
 
721
            if not supports_executable():
 
722
                executable = None # caller can decide policy.
 
723
            else:
 
724
                mode = stat_result.st_mode
 
725
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
 
726
            sha1 = None # 'stat-hit-check' here
 
727
            return (kind, size, executable, sha1)
700
728
        elif kind == 'directory':
701
729
            # perhaps it looks like a plain directory, but it's really a
702
730
            # reference.
708
736
        else:
709
737
            return (kind, None, None, None)
710
738
 
 
739
    @deprecated_method(zero_eleven)
 
740
    @needs_read_lock
 
741
    def pending_merges(self):
 
742
        """Return a list of pending merges.
 
743
 
 
744
        These are revisions that have been merged into the working
 
745
        directory but not yet committed.
 
746
 
 
747
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
 
748
        instead - which is available on all tree objects.
 
749
        """
 
750
        return self.get_parent_ids()[1:]
 
751
 
711
752
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
712
753
        """Common ghost checking functionality from set_parent_*.
713
754
 
722
763
 
723
764
    def _set_merges_from_parent_ids(self, parent_ids):
724
765
        merges = parent_ids[1:]
725
 
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
726
 
            mode=self._control_files._file_mode)
727
 
 
728
 
    def _filter_parent_ids_by_ancestry(self, revision_ids):
729
 
        """Check that all merged revisions are proper 'heads'.
730
 
 
731
 
        This will always return the first revision_id, and any merged revisions
732
 
        which are 
733
 
        """
734
 
        if len(revision_ids) == 0:
735
 
            return revision_ids
736
 
        graph = self.branch.repository.get_graph()
737
 
        heads = graph.heads(revision_ids)
738
 
        new_revision_ids = revision_ids[:1]
739
 
        for revision_id in revision_ids[1:]:
740
 
            if revision_id in heads and revision_id not in new_revision_ids:
741
 
                new_revision_ids.append(revision_id)
742
 
        if new_revision_ids != revision_ids:
743
 
            trace.mutter('requested to set revision_ids = %s,'
744
 
                         ' but filtered to %s', revision_ids, new_revision_ids)
745
 
        return new_revision_ids
 
766
        self._control_files.put_bytes('pending-merges', '\n'.join(merges))
746
767
 
747
768
    @needs_tree_write_lock
748
769
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
757
778
        :param revision_ids: The revision_ids to set as the parent ids of this
758
779
            working tree. Any of these may be ghosts.
759
780
        """
 
781
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
760
782
        self._check_parents_for_ghosts(revision_ids,
761
783
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
762
784
        for revision_id in revision_ids:
763
785
            _mod_revision.check_not_reserved_id(revision_id)
764
786
 
765
 
        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
766
 
 
767
787
        if len(revision_ids) > 0:
768
788
            self.set_last_revision(revision_ids[0])
769
789
        else:
774
794
    @needs_tree_write_lock
775
795
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
776
796
        """See MutableTree.set_parent_trees."""
777
 
        parent_ids = [rev for (rev, tree) in parents_list]
 
797
        parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
778
798
        for revision_id in parent_ids:
779
799
            _mod_revision.check_not_reserved_id(revision_id)
780
800
 
781
801
        self._check_parents_for_ghosts(parent_ids,
782
802
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
783
803
 
784
 
        parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
785
 
 
786
804
        if len(parent_ids) == 0:
787
805
            leftmost_parent_id = _mod_revision.NULL_REVISION
788
806
            leftmost_parent_tree = None
815
833
                yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
816
834
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
817
835
 
818
 
    def _sha_from_stat(self, path, stat_result):
819
 
        """Get a sha digest from the tree's stat cache.
820
 
 
821
 
        The default implementation assumes no stat cache is present.
822
 
 
823
 
        :param path: The path.
824
 
        :param stat_result: The stat result being looked up.
825
 
        """
826
 
        return None
827
 
 
828
836
    def _put_rio(self, filename, stanzas, header):
829
837
        self._must_be_locked()
830
838
        my_file = rio_file(stanzas, header)
831
 
        self._transport.put_file(filename, my_file,
832
 
            mode=self._control_files._file_mode)
 
839
        self._control_files.put(filename, my_file)
833
840
 
834
841
    @needs_write_lock # because merge pulls data into the branch.
835
842
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
853
860
            merger.check_basis(check_clean=True, require_commits=False)
854
861
            if to_revision is None:
855
862
                to_revision = _mod_revision.ensure_null(branch.last_revision())
 
863
            else:
 
864
                to_revision = osutils.safe_revision_id(to_revision)
856
865
            merger.other_rev_id = to_revision
857
866
            if _mod_revision.is_null(merger.other_rev_id):
858
867
                raise errors.NoCommits(branch)
894
903
        still in the working inventory and have that text hash.
895
904
        """
896
905
        try:
897
 
            hashfile = self._transport.get('merge-hashes')
 
906
            hashfile = self._control_files.get('merge-hashes')
898
907
        except errors.NoSuchFile:
899
908
            return {}
900
909
        merge_hashes = {}
923
932
        return file_id
924
933
 
925
934
    def get_symlink_target(self, file_id):
 
935
        file_id = osutils.safe_file_id(file_id)
926
936
        return os.readlink(self.id2abspath(file_id))
927
937
 
928
938
    @needs_write_lock
967
977
            other_tree.unlock()
968
978
        other_tree.bzrdir.retire_bzrdir()
969
979
 
970
 
    def _setup_directory_is_tree_reference(self):
971
 
        if self._branch.repository._format.supports_tree_reference:
972
 
            self._directory_is_tree_reference = \
973
 
                self._directory_may_be_tree_reference
974
 
        else:
975
 
            self._directory_is_tree_reference = \
976
 
                self._directory_is_never_tree_reference
977
 
 
978
 
    def _directory_is_never_tree_reference(self, relpath):
979
 
        return False
980
 
 
981
 
    def _directory_may_be_tree_reference(self, relpath):
 
980
    def _directory_is_tree_reference(self, relpath):
982
981
        # as a special case, if a directory contains control files then 
983
982
        # it's a tree reference, except that the root of the tree is not
984
983
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1011
1010
        sub_path = self.id2path(file_id)
1012
1011
        branch_transport = mkdirs(sub_path)
1013
1012
        if format is None:
1014
 
            format = self.bzrdir.cloning_metadir()
 
1013
            format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
1015
1014
        branch_transport.ensure_base()
1016
1015
        branch_bzrdir = format.initialize_on_transport(branch_transport)
1017
1016
        try:
1018
1017
            repo = branch_bzrdir.find_repository()
1019
1018
        except errors.NoRepositoryPresent:
1020
1019
            repo = branch_bzrdir.create_repository()
1021
 
        if not repo.supports_rich_root():
1022
 
            raise errors.RootNotRich()
 
1020
            assert repo.supports_rich_root()
 
1021
        else:
 
1022
            if not repo.supports_rich_root():
 
1023
                raise errors.RootNotRich()
1023
1024
        new_branch = branch_bzrdir.create_branch()
1024
1025
        new_branch.pull(self.branch)
1025
1026
        for parent_id in self.get_parent_ids():
1043
1044
        return wt
1044
1045
 
1045
1046
    def _serialize(self, inventory, out_file):
1046
 
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1047
 
            working=True)
 
1047
        xml5.serializer_v5.write_inventory(self._inventory, out_file)
1048
1048
 
1049
1049
    def _deserialize(selt, in_file):
1050
1050
        return xml5.serializer_v5.read_inventory(in_file)
1057
1057
        sio = StringIO()
1058
1058
        self._serialize(self._inventory, sio)
1059
1059
        sio.seek(0)
1060
 
        self._transport.put_file('inventory', sio,
1061
 
            mode=self._control_files._file_mode)
 
1060
        self._control_files.put('inventory', sio)
1062
1061
        self._inventory_is_modified = False
1063
1062
 
1064
1063
    def _kind(self, relpath):
1225
1224
                                       DeprecationWarning)
1226
1225
 
1227
1226
        # check destination directory
1228
 
        if isinstance(from_paths, basestring):
1229
 
            raise ValueError()
 
1227
        assert not isinstance(from_paths, basestring)
1230
1228
        inv = self.inventory
1231
1229
        to_abs = self.abspath(to_dir)
1232
1230
        if not isdir(to_abs):
1316
1314
                only_change_inv = True
1317
1315
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1318
1316
                only_change_inv = False
1319
 
            elif (not self.case_sensitive
1320
 
                  and from_rel.lower() == to_rel.lower()
1321
 
                  and self.has_filename(from_rel)):
1322
 
                only_change_inv = False
1323
1317
            else:
1324
1318
                # something is wrong, so lets determine what exactly
1325
1319
                if not self.has_filename(from_rel) and \
1328
1322
                        errors.PathsDoNotExist(paths=(str(from_rel),
1329
1323
                        str(to_rel))))
1330
1324
                else:
1331
 
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
 
1325
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
 
1326
                        extra="(Use --after to update the Bazaar id)")
1332
1327
            rename_entry.only_change_inv = only_change_inv
1333
1328
        return rename_entries
1334
1329
 
1481
1476
        :raises: NoSuchId if any fileid is not currently versioned.
1482
1477
        """
1483
1478
        for file_id in file_ids:
 
1479
            file_id = osutils.safe_file_id(file_id)
1484
1480
            if self._inventory.has_id(file_id):
1485
1481
                self._inventory.remove_recursive_id(file_id)
1486
1482
            else:
1496
1492
            # - RBC 20060907
1497
1493
            self._write_inventory(self._inventory)
1498
1494
    
 
1495
    @deprecated_method(zero_eight)
 
1496
    def iter_conflicts(self):
 
1497
        """List all files in the tree that have text or content conflicts.
 
1498
        DEPRECATED.  Use conflicts instead."""
 
1499
        return self._iter_conflicts()
 
1500
 
1499
1501
    def _iter_conflicts(self):
1500
1502
        conflicted = set()
1501
1503
        for info in self.list_files():
1509
1511
 
1510
1512
    @needs_write_lock
1511
1513
    def pull(self, source, overwrite=False, stop_revision=None,
1512
 
             change_reporter=None, possible_transports=None):
 
1514
             change_reporter=None):
1513
1515
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1514
1516
        source.lock_read()
1515
1517
        try:
1517
1519
            pp.next_phase()
1518
1520
            old_revision_info = self.branch.last_revision_info()
1519
1521
            basis_tree = self.basis_tree()
1520
 
            count = self.branch.pull(source, overwrite, stop_revision,
1521
 
                                     possible_transports=possible_transports)
 
1522
            count = self.branch.pull(source, overwrite, stop_revision)
1522
1523
            new_revision_info = self.branch.last_revision_info()
1523
1524
            if new_revision_info != old_revision_info:
1524
1525
                pp.next_phase()
1536
1537
                                change_reporter=change_reporter)
1537
1538
                    if (basis_tree.inventory.root is None and
1538
1539
                        new_basis_tree.inventory.root is not None):
1539
 
                        self.set_root_id(new_basis_tree.get_root_id())
 
1540
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
1540
1541
                finally:
1541
1542
                    pb.finished()
1542
1543
                    basis_tree.unlock()
1562
1563
    @needs_write_lock
1563
1564
    def put_file_bytes_non_atomic(self, file_id, bytes):
1564
1565
        """See MutableTree.put_file_bytes_non_atomic."""
 
1566
        file_id = osutils.safe_file_id(file_id)
1565
1567
        stream = file(self.id2abspath(file_id), 'wb')
1566
1568
        try:
1567
1569
            stream.write(bytes)
1592
1594
                if subf == '.bzr':
1593
1595
                    continue
1594
1596
                if subf not in dir_entry.children:
1595
 
                    try:
1596
 
                        (subf_norm,
1597
 
                         can_access) = osutils.normalized_filename(subf)
1598
 
                    except UnicodeDecodeError:
1599
 
                        path_os_enc = path.encode(osutils._fs_enc)
1600
 
                        relpath = path_os_enc + '/' + subf
1601
 
                        raise errors.BadFilenameEncoding(relpath,
1602
 
                                                         osutils._fs_enc)
 
1597
                    subf_norm, can_access = osutils.normalized_filename(subf)
1603
1598
                    if subf_norm != subf and can_access:
1604
1599
                        if subf_norm not in dir_entry.children:
1605
1600
                            fl.append(subf_norm)
1660
1655
    def kind(self, file_id):
1661
1656
        return file_kind(self.id2abspath(file_id))
1662
1657
 
1663
 
    def stored_kind(self, file_id):
1664
 
        """See Tree.stored_kind"""
1665
 
        return self.inventory[file_id].kind
1666
 
 
1667
1658
    def _comparison_data(self, entry, path):
1668
1659
        abspath = self.abspath(path)
1669
1660
        try:
1751
1742
    def _reset_data(self):
1752
1743
        """Reset transient data that cannot be revalidated."""
1753
1744
        self._inventory_is_modified = False
1754
 
        result = self._deserialize(self._transport.get('inventory'))
 
1745
        result = self._deserialize(self._control_files.get('inventory'))
1755
1746
        self._set_inventory(result, dirty=False)
1756
1747
 
1757
1748
    @needs_tree_write_lock
1758
1749
    def set_last_revision(self, new_revision):
1759
1750
        """Change the last revision in the working tree."""
 
1751
        new_revision = osutils.safe_revision_id(new_revision)
1760
1752
        if self._change_last_revision(new_revision):
1761
1753
            self._cache_basis_inventory(new_revision)
1762
1754
 
1778
1770
 
1779
1771
    def _write_basis_inventory(self, xml):
1780
1772
        """Write the basis inventory XML to the basis-inventory file"""
 
1773
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
1781
1774
        path = self._basis_inventory_name()
1782
1775
        sio = StringIO(xml)
1783
 
        self._transport.put_file(path, sio,
1784
 
            mode=self._control_files._file_mode)
 
1776
        self._control_files.put(path, sio)
1785
1777
 
1786
1778
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1787
1779
        """Create the text that will be saved in basis-inventory"""
1788
 
        inventory.revision_id = revision_id
 
1780
        # TODO: jam 20070209 This should be redundant, as the revision_id
 
1781
        #       as all callers should have already converted the revision_id to
 
1782
        #       utf8
 
1783
        inventory.revision_id = osutils.safe_revision_id(revision_id)
1789
1784
        return xml7.serializer_v7.write_inventory_to_string(inventory)
1790
1785
 
1791
1786
    def _cache_basis_inventory(self, new_revision):
1818
1813
    def read_basis_inventory(self):
1819
1814
        """Read the cached basis inventory."""
1820
1815
        path = self._basis_inventory_name()
1821
 
        return self._transport.get_bytes(path)
 
1816
        return self._control_files.get(path).read()
1822
1817
        
1823
1818
    @needs_read_lock
1824
1819
    def read_working_inventory(self):
1833
1828
        # binary.
1834
1829
        if self._inventory_is_modified:
1835
1830
            raise errors.InventoryModified(self)
1836
 
        result = self._deserialize(self._transport.get('inventory'))
 
1831
        result = self._deserialize(self._control_files.get('inventory'))
1837
1832
        self._set_inventory(result, dirty=False)
1838
1833
        return result
1839
1834
 
1859
1854
            # Recurse directory and add all files
1860
1855
            # so we can check if they have changed.
1861
1856
            for parent_info, file_infos in\
1862
 
                self.walkdirs(directory):
1863
 
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
1857
                osutils.walkdirs(self.abspath(directory),
 
1858
                    directory):
 
1859
                for relpath, basename, kind, lstat, abspath in file_infos:
1864
1860
                    # Is it versioned or ignored?
1865
1861
                    if self.path2id(relpath) or self.is_ignored(relpath):
1866
1862
                        # Add nested content for deletion.
1876
1872
            filename = self.relpath(abspath)
1877
1873
            if len(filename) > 0:
1878
1874
                new_files.add(filename)
1879
 
                recurse_directory_to_add_files(filename)
 
1875
                if osutils.isdir(abspath):
 
1876
                    recurse_directory_to_add_files(filename)
1880
1877
 
1881
1878
        files = list(new_files)
1882
1879
 
1891
1888
            has_changed_files = len(unknown_nested_files) > 0
1892
1889
            if not has_changed_files:
1893
1890
                for (file_id, path, content_change, versioned, parent_id, name,
1894
 
                     kind, executable) in self.iter_changes(self.basis_tree(),
 
1891
                     kind, executable) in self._iter_changes(self.basis_tree(),
1895
1892
                         include_unchanged=True, require_versioned=False,
1896
1893
                         want_unversioned=True, specific_files=files):
1897
 
                    if versioned == (False, False):
1898
 
                        # The record is unknown ...
1899
 
                        if not self.is_ignored(path[1]):
1900
 
                            # ... but not ignored
1901
 
                            has_changed_files = True
1902
 
                            break
1903
 
                    elif content_change and (kind[1] is not None):
1904
 
                        # Versioned and changed, but not deleted
 
1894
                    # Check if it's an unknown (but not ignored) OR
 
1895
                    # changed (but not deleted) :
 
1896
                    if not self.is_ignored(path[1]) and (
 
1897
                        versioned == (False, False) or
 
1898
                        content_change and kind[1] != None):
1905
1899
                        has_changed_files = True
1906
1900
                        break
1907
1901
 
1958
1952
        self.apply_inventory_delta(inv_delta)
1959
1953
 
1960
1954
    @needs_tree_write_lock
1961
 
    def revert(self, filenames=None, old_tree=None, backups=True,
 
1955
    def revert(self, filenames, old_tree=None, backups=True, 
1962
1956
               pb=DummyProgress(), report_changes=False):
1963
1957
        from bzrlib.conflicts import resolve
1964
 
        if filenames == []:
1965
 
            filenames = None
1966
 
            symbol_versioning.warn('Using [] to revert all files is deprecated'
1967
 
                ' as of bzr 0.91.  Please use None (the default) instead.',
1968
 
                DeprecationWarning, stacklevel=2)
1969
1958
        if old_tree is None:
1970
 
            basis_tree = self.basis_tree()
1971
 
            basis_tree.lock_read()
1972
 
            old_tree = basis_tree
 
1959
            old_tree = self.basis_tree()
 
1960
        conflicts = transform.revert(self, old_tree, filenames, backups, pb,
 
1961
                                     report_changes)
 
1962
        if not len(filenames):
 
1963
            self.set_parent_ids(self.get_parent_ids()[:1])
 
1964
            resolve(self)
1973
1965
        else:
1974
 
            basis_tree = None
1975
 
        try:
1976
 
            conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1977
 
                                         report_changes)
1978
 
            if filenames is None and len(self.get_parent_ids()) > 1:
1979
 
                parent_trees = []
1980
 
                last_revision = self.last_revision()
1981
 
                if last_revision != NULL_REVISION:
1982
 
                    if basis_tree is None:
1983
 
                        basis_tree = self.basis_tree()
1984
 
                        basis_tree.lock_read()
1985
 
                    parent_trees.append((last_revision, basis_tree))
1986
 
                self.set_parent_trees(parent_trees)
1987
 
                resolve(self)
1988
 
            else:
1989
 
                resolve(self, filenames, ignore_misses=True, recursive=True)
1990
 
        finally:
1991
 
            if basis_tree is not None:
1992
 
                basis_tree.unlock()
 
1966
            resolve(self, filenames, ignore_misses=True)
1993
1967
        return conflicts
1994
1968
 
1995
1969
    def revision_tree(self, revision_id):
2046
2020
        """Set the root id for this tree."""
2047
2021
        # for compatability 
2048
2022
        if file_id is None:
2049
 
            raise ValueError(
2050
 
                'WorkingTree.set_root_id with fileid=None')
2051
 
        file_id = osutils.safe_file_id(file_id)
 
2023
            symbol_versioning.warn(symbol_versioning.zero_twelve
 
2024
                % 'WorkingTree.set_root_id with fileid=None',
 
2025
                DeprecationWarning,
 
2026
                stacklevel=3)
 
2027
            file_id = ROOT_ID
 
2028
        else:
 
2029
            file_id = osutils.safe_file_id(file_id)
2052
2030
        self._set_root_id(file_id)
2053
2031
 
2054
2032
    def _set_root_id(self, file_id):
2088
2066
        """
2089
2067
        raise NotImplementedError(self.unlock)
2090
2068
 
2091
 
    def update(self, change_reporter=None, possible_transports=None):
 
2069
    def update(self, change_reporter=None):
2092
2070
        """Update a working tree along its branch.
2093
2071
 
2094
2072
        This will update the branch if its bound too, which means we have
2113
2091
          basis.
2114
2092
        - Do a 'normal' merge of the old branch basis if it is relevant.
2115
2093
        """
2116
 
        if self.branch.get_bound_location() is not None:
 
2094
        if self.branch.get_master_branch() is not None:
2117
2095
            self.lock_write()
2118
2096
            update_branch = True
2119
2097
        else:
2121
2099
            update_branch = False
2122
2100
        try:
2123
2101
            if update_branch:
2124
 
                old_tip = self.branch.update(possible_transports)
 
2102
                old_tip = self.branch.update()
2125
2103
            else:
2126
2104
                old_tip = None
2127
2105
            return self._update_tree(old_tip, change_reporter)
2157
2135
            try:
2158
2136
                to_tree = self.branch.basis_tree()
2159
2137
                if basis.inventory.root is None:
2160
 
                    self.set_root_id(to_tree.get_root_id())
 
2138
                    self.set_root_id(to_tree.inventory.root.file_id)
2161
2139
                    self.flush()
2162
2140
                result += merge.merge_inner(
2163
2141
                                      self.branch,
2305
2283
            current_inv = None
2306
2284
            inv_finished = True
2307
2285
        while not inv_finished or not disk_finished:
2308
 
            if current_disk:
2309
 
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2310
 
                    cur_disk_dir_content) = current_disk
2311
 
            else:
2312
 
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2313
 
                    cur_disk_dir_content) = ((None, None), None)
2314
2286
            if not disk_finished:
2315
2287
                # strip out .bzr dirs
2316
 
                if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
2317
 
                    len(cur_disk_dir_content) > 0):
2318
 
                    # osutils.walkdirs can be made nicer -
 
2288
                if current_disk[0][1][top_strip_len:] == '':
 
2289
                    # osutils.walkdirs can be made nicer - 
2319
2290
                    # yield the path-from-prefix rather than the pathjoined
2320
2291
                    # value.
2321
 
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
2322
 
                        ('.bzr', '.bzr'))
2323
 
                    if cur_disk_dir_content[bzrdir_loc][0] == '.bzr':
 
2292
                    bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
 
2293
                    if current_disk[1][bzrdir_loc][0] == '.bzr':
2324
2294
                        # we dont yield the contents of, or, .bzr itself.
2325
 
                        del cur_disk_dir_content[bzrdir_loc]
 
2295
                        del current_disk[1][bzrdir_loc]
2326
2296
            if inv_finished:
2327
2297
                # everything is unknown
2328
2298
                direction = 1
2330
2300
                # everything is missing
2331
2301
                direction = -1
2332
2302
            else:
2333
 
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
 
2303
                direction = cmp(current_inv[0][0], current_disk[0][0])
2334
2304
            if direction > 0:
2335
2305
                # disk is before inventory - unknown
2336
2306
                dirblock = [(relpath, basename, kind, stat, None, None) for
2337
 
                    relpath, basename, kind, stat, top_path in
2338
 
                    cur_disk_dir_content]
2339
 
                yield (cur_disk_dir_relpath, None), dirblock
 
2307
                    relpath, basename, kind, stat, top_path in current_disk[1]]
 
2308
                yield (current_disk[0][0], None), dirblock
2340
2309
                try:
2341
2310
                    current_disk = disk_iterator.next()
2342
2311
                except StopIteration:
2344
2313
            elif direction < 0:
2345
2314
                # inventory is before disk - missing.
2346
2315
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2347
 
                    for relpath, basename, dkind, stat, fileid, kind in
 
2316
                    for relpath, basename, dkind, stat, fileid, kind in 
2348
2317
                    current_inv[1]]
2349
2318
                yield (current_inv[0][0], current_inv[0][1]), dirblock
2350
2319
                try:
2356
2325
                # merge the inventory and disk data together
2357
2326
                dirblock = []
2358
2327
                for relpath, subiterator in itertools.groupby(sorted(
2359
 
                    current_inv[1] + cur_disk_dir_content,
2360
 
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
 
2328
                    current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2361
2329
                    path_elements = list(subiterator)
2362
2330
                    if len(path_elements) == 2:
2363
2331
                        inv_row, disk_row = path_elements
2416
2384
                relroot = ""
2417
2385
            # FIXME: stash the node in pending
2418
2386
            entry = inv[top_id]
2419
 
            if entry.kind == 'directory':
2420
 
                for name, child in entry.sorted_children():
2421
 
                    dirblock.append((relroot + name, name, child.kind, None,
2422
 
                        child.file_id, child.kind
2423
 
                        ))
 
2387
            for name, child in entry.sorted_children():
 
2388
                dirblock.append((relroot + name, name, child.kind, None,
 
2389
                    child.file_id, child.kind
 
2390
                    ))
2424
2391
            yield (currentdir[0], entry.file_id), dirblock
2425
2392
            # push the user specified dirs from dirblock
2426
2393
            for dir in reversed(dirblock):
2459
2426
        self.set_conflicts(un_resolved)
2460
2427
        return un_resolved, resolved
2461
2428
 
2462
 
    @needs_read_lock
2463
 
    def _check(self):
2464
 
        tree_basis = self.basis_tree()
2465
 
        tree_basis.lock_read()
2466
 
        try:
2467
 
            repo_basis = self.branch.repository.revision_tree(
2468
 
                self.last_revision())
2469
 
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2470
 
                raise errors.BzrCheckError(
2471
 
                    "Mismatched basis inventory content.")
2472
 
            self._validate()
2473
 
        finally:
2474
 
            tree_basis.unlock()
2475
 
 
2476
2429
    def _validate(self):
2477
2430
        """Validate internal structures.
2478
2431
 
2484
2437
        """
2485
2438
        return
2486
2439
 
2487
 
    @needs_read_lock
2488
 
    def _get_rules_searcher(self, default_searcher):
2489
 
        """See Tree._get_rules_searcher."""
2490
 
        if self._rules_searcher is None:
2491
 
            self._rules_searcher = super(WorkingTree,
2492
 
                self)._get_rules_searcher(default_searcher)
2493
 
        return self._rules_searcher
2494
 
 
2495
2440
 
2496
2441
class WorkingTree2(WorkingTree):
2497
2442
    """This is the Format 2 working tree.
2557
2502
    def _last_revision(self):
2558
2503
        """See Mutable.last_revision."""
2559
2504
        try:
2560
 
            return self._transport.get_bytes('last-revision')
 
2505
            return osutils.safe_revision_id(
 
2506
                        self._control_files.get('last-revision').read())
2561
2507
        except errors.NoSuchFile:
2562
2508
            return _mod_revision.NULL_REVISION
2563
2509
 
2565
2511
        """See WorkingTree._change_last_revision."""
2566
2512
        if revision_id is None or revision_id == NULL_REVISION:
2567
2513
            try:
2568
 
                self._transport.delete('last-revision')
 
2514
                self._control_files._transport.delete('last-revision')
2569
2515
            except errors.NoSuchFile:
2570
2516
                pass
2571
2517
            return False
2572
2518
        else:
2573
 
            self._transport.put_bytes('last-revision', revision_id,
2574
 
                mode=self._control_files._file_mode)
 
2519
            self._control_files.put_bytes('last-revision', revision_id)
2575
2520
            return True
2576
2521
 
2577
2522
    @needs_tree_write_lock
2589
2534
    @needs_read_lock
2590
2535
    def conflicts(self):
2591
2536
        try:
2592
 
            confile = self._transport.get('conflicts')
 
2537
            confile = self._control_files.get('conflicts')
2593
2538
        except errors.NoSuchFile:
2594
2539
            return _mod_conflicts.ConflictList()
2595
2540
        try:
2620
2565
            return path[:-len(suffix)]
2621
2566
 
2622
2567
 
 
2568
@deprecated_function(zero_eight)
 
2569
def is_control_file(filename):
 
2570
    """See WorkingTree.is_control_filename(filename)."""
 
2571
    ## FIXME: better check
 
2572
    filename = normpath(filename)
 
2573
    while filename != '':
 
2574
        head, tail = os.path.split(filename)
 
2575
        ## mutter('check %r for control file' % ((head, tail),))
 
2576
        if tail == '.bzr':
 
2577
            return True
 
2578
        if filename == head:
 
2579
            break
 
2580
        filename = head
 
2581
    return False
 
2582
 
 
2583
 
2623
2584
class WorkingTreeFormat(object):
2624
2585
    """An encapsulation of the initialization and open routines for a format.
2625
2586
 
2658
2619
        except errors.NoSuchFile:
2659
2620
            raise errors.NoWorkingTree(base=transport.base)
2660
2621
        except KeyError:
2661
 
            raise errors.UnknownFormatError(format=format_string,
2662
 
                                            kind="working tree")
 
2622
            raise errors.UnknownFormatError(format=format_string)
2663
2623
 
2664
2624
    def __eq__(self, other):
2665
2625
        return self.__class__ is other.__class__
2699
2659
 
2700
2660
    @classmethod
2701
2661
    def unregister_format(klass, format):
 
2662
        assert klass._formats[format.get_format_string()] is format
2702
2663
        del klass._formats[format.get_format_string()]
2703
2664
 
2704
2665
 
2714
2675
        """See WorkingTreeFormat.get_format_description()."""
2715
2676
        return "Working tree format 2"
2716
2677
 
2717
 
    def _stub_initialize_on_transport(self, transport, file_mode):
2718
 
        """Workaround: create control files for a remote working tree.
2719
 
 
 
2678
    def stub_initialize_remote(self, control_files):
 
2679
        """As a special workaround create critical control files for a remote working tree
 
2680
        
2720
2681
        This ensures that it can later be updated and dealt with locally,
2721
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
 
2682
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
2722
2683
        no working tree.  (See bug #43064).
2723
2684
        """
2724
2685
        sio = StringIO()
2725
2686
        inv = Inventory()
2726
 
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
 
2687
        xml5.serializer_v5.write_inventory(inv, sio)
2727
2688
        sio.seek(0)
2728
 
        transport.put_file('inventory', sio, file_mode)
2729
 
        transport.put_bytes('pending-merges', '', file_mode)
2730
 
 
2731
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2732
 
                   accelerator_tree=None, hardlink=False):
 
2689
        control_files.put('inventory', sio)
 
2690
 
 
2691
        control_files.put_bytes('pending-merges', '')
 
2692
        
 
2693
 
 
2694
    def initialize(self, a_bzrdir, revision_id=None):
2733
2695
        """See WorkingTreeFormat.initialize()."""
2734
2696
        if not isinstance(a_bzrdir.transport, LocalTransport):
2735
2697
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2736
 
        if from_branch is not None:
2737
 
            branch = from_branch
2738
 
        else:
2739
 
            branch = a_bzrdir.open_branch()
 
2698
        branch = a_bzrdir.open_branch()
2740
2699
        if revision_id is None:
2741
2700
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2701
        else:
 
2702
            revision_id = osutils.safe_revision_id(revision_id)
2742
2703
        branch.lock_write()
2743
2704
        try:
2744
2705
            branch.generate_revision_history(revision_id)
2753
2714
                         _bzrdir=a_bzrdir)
2754
2715
        basis_tree = branch.repository.revision_tree(revision_id)
2755
2716
        if basis_tree.inventory.root is not None:
2756
 
            wt.set_root_id(basis_tree.get_root_id())
 
2717
            wt.set_root_id(basis_tree.inventory.root.file_id)
2757
2718
        # set the parent list and cache the basis tree.
2758
2719
        if _mod_revision.is_null(revision_id):
2759
2720
            parent_trees = []
2821
2782
        return LockableFiles(transport, self._lock_file_name, 
2822
2783
                             self._lock_class)
2823
2784
 
2824
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2825
 
                   accelerator_tree=None, hardlink=False):
 
2785
    def initialize(self, a_bzrdir, revision_id=None):
2826
2786
        """See WorkingTreeFormat.initialize().
2827
2787
        
2828
 
        :param revision_id: if supplied, create a working tree at a different
2829
 
            revision than the branch is at.
2830
 
        :param accelerator_tree: A tree which can be used for retrieving file
2831
 
            contents more quickly than the revision tree, i.e. a workingtree.
2832
 
            The revision tree will be used for cases where accelerator_tree's
2833
 
            content is different.
2834
 
        :param hardlink: If true, hard-link files from accelerator_tree,
2835
 
            where possible.
 
2788
        revision_id allows creating a working tree at a different
 
2789
        revision than the branch is at.
2836
2790
        """
2837
2791
        if not isinstance(a_bzrdir.transport, LocalTransport):
2838
2792
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2840
2794
        control_files = self._open_control_files(a_bzrdir)
2841
2795
        control_files.create_lock()
2842
2796
        control_files.lock_write()
2843
 
        transport.put_bytes('format', self.get_format_string(),
2844
 
            mode=control_files._file_mode)
2845
 
        if from_branch is not None:
2846
 
            branch = from_branch
2847
 
        else:
2848
 
            branch = a_bzrdir.open_branch()
 
2797
        control_files.put_utf8('format', self.get_format_string())
 
2798
        branch = a_bzrdir.open_branch()
2849
2799
        if revision_id is None:
2850
2800
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2801
        else:
 
2802
            revision_id = osutils.safe_revision_id(revision_id)
2851
2803
        # WorkingTree3 can handle an inventory which has a unique root id.
2852
2804
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2853
2805
        # those trees. And because there isn't a format bump inbetween, we
2866
2818
            basis_tree = branch.repository.revision_tree(revision_id)
2867
2819
            # only set an explicit root id if there is one to set.
2868
2820
            if basis_tree.inventory.root is not None:
2869
 
                wt.set_root_id(basis_tree.get_root_id())
 
2821
                wt.set_root_id(basis_tree.inventory.root.file_id)
2870
2822
            if revision_id == NULL_REVISION:
2871
2823
                wt.set_parent_trees([])
2872
2824
            else: