/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/workingtree_4.py

  • Committer: Martin
  • Date: 2017-06-11 14:36:07 UTC
  • mto: This revision was merged to the branch mainline in revision 6688.
  • Revision ID: gzlist@googlemail.com-20170611143607-iuusrtbtuvgzrcho
Make test__rio pass on Python 3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007-2012 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
22
22
WorkingTree.open(dir).
23
23
"""
24
24
 
25
 
from cStringIO import StringIO
 
25
from __future__ import absolute_import
 
26
 
26
27
import os
27
28
import sys
28
29
 
29
 
from bzrlib.lazy_import import lazy_import
 
30
from .lazy_import import lazy_import
30
31
lazy_import(globals(), """
31
32
import errno
32
33
import stat
33
34
 
34
 
import bzrlib
35
 
from bzrlib import (
 
35
from breezy import (
36
36
    bzrdir,
37
37
    cache_utf8,
 
38
    cleanup,
 
39
    config,
 
40
    conflicts as _mod_conflicts,
 
41
    controldir,
38
42
    debug,
39
43
    dirstate,
40
44
    errors,
 
45
    filters as _mod_filters,
41
46
    generate_ids,
42
47
    osutils,
43
48
    revision as _mod_revision,
46
51
    transform,
47
52
    views,
48
53
    )
49
 
import bzrlib.branch
50
 
import bzrlib.ui
51
54
""")
52
55
 
53
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
 
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
 
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
 
from bzrlib.mutabletree import needs_tree_write_lock
57
 
from bzrlib.osutils import (
 
56
from .decorators import needs_read_lock, needs_write_lock
 
57
from .inventory import Inventory, ROOT_ID, entry_factory
 
58
from .inventorytree import (
 
59
    InventoryTree,
 
60
    InventoryRevisionTree,
 
61
    )
 
62
from .lock import LogicalLockResult
 
63
from .lockable_files import LockableFiles
 
64
from .lockdir import LockDir
 
65
from .mutabletree import (
 
66
    MutableTree,
 
67
    needs_tree_write_lock,
 
68
    )
 
69
from .osutils import (
58
70
    file_kind,
59
71
    isdir,
60
72
    pathjoin,
61
73
    realpath,
62
74
    safe_unicode,
63
75
    )
64
 
from bzrlib.trace import mutter
65
 
from bzrlib.transport.local import LocalTransport
66
 
from bzrlib.tree import InterTree
67
 
from bzrlib.tree import Tree
68
 
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
69
 
 
70
 
 
71
 
class DirStateWorkingTree(WorkingTree3):
 
76
from .sixish import (
 
77
    BytesIO,
 
78
    viewitems,
 
79
    )
 
80
from .transport.local import LocalTransport
 
81
from .tree import (
 
82
    InterTree,
 
83
    )
 
84
from .workingtree import (
 
85
    WorkingTree,
 
86
    )
 
87
from .bzrworkingtree import (
 
88
    InventoryWorkingTree,
 
89
    WorkingTreeFormatMetaDir,
 
90
    )
 
91
 
 
92
 
 
93
class DirStateWorkingTree(InventoryWorkingTree):
 
94
 
72
95
    def __init__(self, basedir,
73
96
                 branch,
74
97
                 _control_files=None,
82
105
        would be meaningless).
83
106
        """
84
107
        self._format = _format
85
 
        self.bzrdir = _bzrdir
 
108
        self.controldir = _bzrdir
86
109
        basedir = safe_unicode(basedir)
87
 
        mutter("opening working tree %r", basedir)
 
110
        trace.mutter("opening working tree %r", basedir)
88
111
        self._branch = branch
89
112
        self.basedir = realpath(basedir)
90
113
        # if branch is at our basedir and is a format 6 or less
110
133
        """See MutableTree._add."""
111
134
        state = self.current_dirstate()
112
135
        for f, file_id, kind in zip(files, ids, kinds):
113
 
            f = f.strip('/')
 
136
            f = f.strip(b'/')
114
137
            if self.path2id(f):
115
138
                # special case tree root handling.
116
 
                if f == '' and self.path2id(f) == ROOT_ID:
117
 
                    state.set_path_id('', generate_ids.gen_file_id(f))
 
139
                if f == b'' and self.path2id(f) == ROOT_ID:
 
140
                    state.set_path_id(b'', generate_ids.gen_file_id(f))
118
141
                continue
119
142
            if file_id is None:
120
143
                file_id = generate_ids.gen_file_id(f)
121
144
            # deliberately add the file with no cached stat or sha1
122
145
            # - on the first access it will be gathered, and we can
123
146
            # always change this once tests are all passing.
124
 
            state.add(f, file_id, kind, None, '')
 
147
            state.add(f, file_id, kind, None, b'')
125
148
        self._make_dirty(reset_inventory=True)
126
149
 
 
150
    def _get_check_refs(self):
 
151
        """Return the references needed to perform a check of this tree."""
 
152
        return [('trees', self.last_revision())]
 
153
 
127
154
    def _make_dirty(self, reset_inventory):
128
155
        """Make the tree state dirty.
129
156
 
181
208
 
182
209
    def _comparison_data(self, entry, path):
183
210
        kind, executable, stat_value = \
184
 
            WorkingTree3._comparison_data(self, entry, path)
 
211
            WorkingTree._comparison_data(self, entry, path)
185
212
        # it looks like a plain directory, but it's really a reference -- see
186
213
        # also kind()
187
214
        if (self._repo_supports_tree_reference and kind == 'directory'
193
220
    def commit(self, message=None, revprops=None, *args, **kwargs):
194
221
        # mark the tree as dirty post commit - commit
195
222
        # can change the current versioned list by doing deletes.
196
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
223
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
197
224
        self._make_dirty(reset_inventory=True)
198
225
        return result
199
226
 
215
242
        """
216
243
        if self._dirstate is not None:
217
244
            return self._dirstate
218
 
        local_path = self.bzrdir.get_workingtree_transport(None
 
245
        local_path = self.controldir.get_workingtree_transport(None
219
246
            ).local_abspath('dirstate')
220
247
        self._dirstate = dirstate.DirState.on_file(local_path,
221
 
            self._sha1_provider())
 
248
            self._sha1_provider(), self._worth_saving_limit())
222
249
        return self._dirstate
223
250
 
224
251
    def _sha1_provider(self):
233
260
        else:
234
261
            return None
235
262
 
 
263
    def _worth_saving_limit(self):
 
264
        """How many hash changes are ok before we must save the dirstate.
 
265
 
 
266
        :return: an integer. -1 means never save.
 
267
        """
 
268
        conf = self.get_config_stack()
 
269
        return conf.get('bzr.workingtree.worth_saving_limit')
 
270
 
236
271
    def filter_unversioned_files(self, paths):
237
272
        """Filter out paths that are versioned.
238
273
 
280
315
        state._read_dirblocks_if_needed()
281
316
        root_key, current_entry = self._get_entry(path='')
282
317
        current_id = root_key[2]
283
 
        if not (current_entry[0][0] == 'd'): # directory
 
318
        if not (current_entry[0][0] == b'd'): # directory
284
319
            raise AssertionError(current_entry)
285
320
        inv = Inventory(root_id=current_id)
286
321
        # Turn some things into local variables
300
335
                continue
301
336
            for key, entry in block[1]:
302
337
                minikind, link_or_sha1, size, executable, stat = entry[0]
303
 
                if minikind in ('a', 'r'): # absent, relocated
 
338
                if minikind in (b'a', b'r'): # absent, relocated
304
339
                    # a parent tree only entry
305
340
                    continue
306
341
                name = key[1]
318
353
                    #inv_entry.text_sha1 = sha1
319
354
                elif kind == 'directory':
320
355
                    # add this entry to the parent map.
321
 
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
 
356
                    parent_ies[(dirname + b'/' + name).strip(b'/')] = inv_entry
322
357
                elif kind == 'tree-reference':
323
358
                    if not self._repo_supports_tree_reference:
324
359
                        raise errors.UnsupportedOperation(
368
403
        state = self.current_dirstate()
369
404
        if stat_value is None:
370
405
            try:
371
 
                stat_value = os.lstat(file_abspath)
372
 
            except OSError, e:
 
406
                stat_value = osutils.lstat(file_abspath)
 
407
            except OSError as e:
373
408
                if e.errno == errno.ENOENT:
374
409
                    return None
375
410
                else:
376
411
                    raise
377
412
        link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
378
413
            stat_value=stat_value)
379
 
        if entry[1][0][0] == 'f':
 
414
        if entry[1][0][0] == b'f':
380
415
            if link_or_sha1 is None:
381
416
                file_obj, statvalue = self.get_file_with_stat(file_id, path)
382
417
                try:
389
424
                return link_or_sha1
390
425
        return None
391
426
 
392
 
    def _get_inventory(self):
 
427
    def _get_root_inventory(self):
393
428
        """Get the inventory for the tree. This is only valid within a lock."""
394
429
        if 'evil' in debug.debug_flags:
395
430
            trace.mutter_callsite(2,
400
435
        self._generate_inventory()
401
436
        return self._inventory
402
437
 
403
 
    inventory = property(_get_inventory,
404
 
                         doc="Inventory of this Tree")
 
438
    root_inventory = property(_get_root_inventory,
 
439
        "Root inventory of this tree")
405
440
 
406
441
    @needs_read_lock
407
442
    def get_parent_ids(self):
455
490
            return False # Missing entries are not executable
456
491
        return entry[1][0][3] # Executable?
457
492
 
458
 
    if not osutils.supports_executable():
459
 
        def is_executable(self, file_id, path=None):
460
 
            """Test if a file is executable or not.
 
493
    def is_executable(self, file_id, path=None):
 
494
        """Test if a file is executable or not.
461
495
 
462
 
            Note: The caller is expected to take a read-lock before calling this.
463
 
            """
 
496
        Note: The caller is expected to take a read-lock before calling this.
 
497
        """
 
498
        if not self._supports_executable():
464
499
            entry = self._get_entry(file_id=file_id, path=path)
465
500
            if entry == (None, None):
466
501
                return False
467
502
            return entry[1][0][3]
468
 
 
469
 
        _is_executable_from_path_and_stat = \
470
 
            _is_executable_from_path_and_stat_from_basis
471
 
    else:
472
 
        def is_executable(self, file_id, path=None):
473
 
            """Test if a file is executable or not.
474
 
 
475
 
            Note: The caller is expected to take a read-lock before calling this.
476
 
            """
 
503
        else:
477
504
            self._must_be_locked()
478
505
            if not path:
479
506
                path = self.id2path(file_id)
480
 
            mode = os.lstat(self.abspath(path)).st_mode
 
507
            mode = osutils.lstat(self.abspath(path)).st_mode
481
508
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
482
509
 
483
510
    def all_file_ids(self):
485
512
        self._must_be_locked()
486
513
        result = set()
487
514
        for key, tree_details in self.current_dirstate()._iter_entries():
488
 
            if tree_details[0][0] in ('a', 'r'): # relocated
 
515
            if tree_details[0][0] in (b'a', b'r'): # relocated
489
516
                continue
490
517
            result.add(key[2])
491
518
        return result
499
526
        """
500
527
        result = []
501
528
        for key, tree_details in self.current_dirstate()._iter_entries():
502
 
            if tree_details[0][0] in ('a', 'r'): # absent, relocated
 
529
            if tree_details[0][0] in (b'a', b'r'): # absent, relocated
503
530
                # not relevant to the working tree
504
531
                continue
505
532
            path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
513
540
            # return
514
541
            return
515
542
        for key, tree_details in self.current_dirstate()._iter_entries():
516
 
            if tree_details[0][0] in ('a', 'r'): # absent, relocated
 
543
            if tree_details[0][0] in (b'a', b'r'): # absent, relocated
517
544
                # not relevant to the working tree
518
545
                continue
519
546
            if not key[1]:
527
554
                # path is missing on disk.
528
555
                continue
529
556
 
530
 
    def _observed_sha1(self, file_id, path, (sha1, statvalue)):
 
557
    def _observed_sha1(self, file_id, path, sha_and_stat):
531
558
        """See MutableTree._observed_sha1."""
532
559
        state = self.current_dirstate()
533
560
        entry = self._get_entry(file_id=file_id, path=path)
534
 
        state._observed_sha1(entry, sha1, statvalue)
 
561
        state._observed_sha1(entry, *sha_and_stat)
535
562
 
536
563
    def kind(self, file_id):
537
564
        """Return the kind of a file.
553
580
        if (self._repo_supports_tree_reference and kind == 'directory'):
554
581
            entry = self._get_entry(path=relpath)
555
582
            if entry[1] is not None:
556
 
                if entry[1][0][0] == 't':
 
583
                if entry[1][0][0] == b't':
557
584
                    kind = 'tree-reference'
558
585
        return kind
559
586
 
567
594
            return _mod_revision.NULL_REVISION
568
595
 
569
596
    def lock_read(self):
570
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
597
        """See Branch.lock_read, and WorkingTree.unlock.
 
598
 
 
599
        :return: A breezy.lock.LogicalLockResult.
 
600
        """
571
601
        self.branch.lock_read()
572
602
        try:
573
603
            self._control_files.lock_read()
586
616
        except:
587
617
            self.branch.unlock()
588
618
            raise
 
619
        return LogicalLockResult(self.unlock)
589
620
 
590
621
    def _lock_self_write(self):
591
622
        """This should be called after the branch is locked."""
606
637
        except:
607
638
            self.branch.unlock()
608
639
            raise
 
640
        return LogicalLockResult(self.unlock)
609
641
 
610
642
    def lock_tree_write(self):
611
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
643
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
644
 
 
645
        :return: A breezy.lock.LogicalLockResult.
 
646
        """
612
647
        self.branch.lock_read()
613
 
        self._lock_self_write()
 
648
        return self._lock_self_write()
614
649
 
615
650
    def lock_write(self):
616
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
651
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
652
 
 
653
        :return: A breezy.lock.LogicalLockResult.
 
654
        """
617
655
        self.branch.lock_write()
618
 
        self._lock_self_write()
 
656
        return self._lock_self_write()
619
657
 
620
658
    @needs_tree_write_lock
621
659
    def move(self, from_paths, to_dir, after=False):
652
690
 
653
691
        if self._inventory is not None:
654
692
            update_inventory = True
655
 
            inv = self.inventory
 
693
            inv = self.root_inventory
656
694
            to_dir_id = to_entry[0][2]
657
695
            to_dir_ie = inv[to_dir_id]
658
696
        else:
659
697
            update_inventory = False
660
698
 
661
 
        rollbacks = []
 
699
        # GZ 2017-03-28: The rollbacks variable was shadowed in the loop below
 
700
        # missing those added here, but there's also no test coverage for this.
 
701
        rollbacks = cleanup.ObjectWithCleanups()
662
702
        def move_one(old_entry, from_path_utf8, minikind, executable,
663
703
                     fingerprint, packed_stat, size,
664
704
                     to_block, to_key, to_path_utf8):
665
705
            state._make_absent(old_entry)
666
706
            from_key = old_entry[0]
667
 
            rollbacks.append(
668
 
                lambda:state.update_minimal(from_key,
669
 
                    minikind,
670
 
                    executable=executable,
671
 
                    fingerprint=fingerprint,
672
 
                    packed_stat=packed_stat,
673
 
                    size=size,
674
 
                    path_utf8=from_path_utf8))
 
707
            rollbacks.add_cleanup(
 
708
                state.update_minimal,
 
709
                from_key,
 
710
                minikind,
 
711
                executable=executable,
 
712
                fingerprint=fingerprint,
 
713
                packed_stat=packed_stat,
 
714
                size=size,
 
715
                path_utf8=from_path_utf8)
675
716
            state.update_minimal(to_key,
676
717
                    minikind,
677
718
                    executable=executable,
681
722
                    path_utf8=to_path_utf8)
682
723
            added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
683
724
            new_entry = to_block[1][added_entry_index]
684
 
            rollbacks.append(lambda:state._make_absent(new_entry))
 
725
            rollbacks.add_cleanup(state._make_absent, new_entry)
685
726
 
686
727
        for from_rel in from_paths:
687
728
            # from_rel is 'pathinroot/foo/bar'
726
767
                elif not after:
727
768
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
728
769
 
729
 
            rollbacks = []
730
 
            def rollback_rename():
731
 
                """A single rename has failed, roll it back."""
732
 
                # roll back everything, even if we encounter trouble doing one
733
 
                # of them.
734
 
                #
735
 
                # TODO: at least log the other exceptions rather than just
736
 
                # losing them mbp 20070307
737
 
                exc_info = None
738
 
                for rollback in reversed(rollbacks):
739
 
                    try:
740
 
                        rollback()
741
 
                    except Exception, e:
742
 
                        exc_info = sys.exc_info()
743
 
                if exc_info:
744
 
                    raise exc_info[0], exc_info[1], exc_info[2]
745
 
 
746
770
            # perform the disk move first - its the most likely failure point.
747
771
            if move_file:
748
772
                from_rel_abs = self.abspath(from_rel)
749
773
                to_rel_abs = self.abspath(to_rel)
750
774
                try:
751
775
                    osutils.rename(from_rel_abs, to_rel_abs)
752
 
                except OSError, e:
 
776
                except OSError as e:
753
777
                    raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
754
 
                rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
 
778
                rollbacks.add_cleanup(osutils.rename, to_rel_abs, from_rel_abs)
755
779
            try:
756
780
                # perform the rename in the inventory next if needed: its easy
757
781
                # to rollback
760
784
                    from_entry = inv[from_id]
761
785
                    current_parent = from_entry.parent_id
762
786
                    inv.rename(from_id, to_dir_id, from_tail)
763
 
                    rollbacks.append(
764
 
                        lambda: inv.rename(from_id, current_parent, from_tail))
 
787
                    rollbacks.add_cleanup(
 
788
                        inv.rename, from_id, current_parent, from_tail)
765
789
                # finally do the rename in the dirstate, which is a little
766
790
                # tricky to rollback, but least likely to need it.
767
791
                old_block_index, old_entry_index, dir_present, file_present = \
783
807
                         to_key=to_key,
784
808
                         to_path_utf8=to_rel_utf8)
785
809
 
786
 
                if minikind == 'd':
 
810
                if minikind == b'd':
787
811
                    def update_dirblock(from_dir, to_key, to_dir_utf8):
788
812
                        """Recursively update all entries in this dirblock."""
789
 
                        if from_dir == '':
 
813
                        if from_dir == b'':
790
814
                            raise AssertionError("renaming root not supported")
791
815
                        from_key = (from_dir, '')
792
816
                        from_block_idx, present = \
813
837
                            from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
814
838
                            to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
815
839
                            minikind = cur_details[0]
816
 
                            if minikind in 'ar':
 
840
                            if minikind in (b'a', b'r'):
817
841
                                # Deleted children of a renamed directory
818
842
                                # Do not need to be updated.
819
843
                                # Children that have been renamed out of this
828
852
                                     to_block=to_block,
829
853
                                     to_key=to_key,
830
854
                                     to_path_utf8=to_path_utf8)
831
 
                            if minikind == 'd':
 
855
                            if minikind == b'd':
832
856
                                # We need to move all the children of this
833
857
                                # entry
834
858
                                update_dirblock(from_path_utf8, to_key,
835
859
                                                to_path_utf8)
836
860
                    update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
837
861
            except:
838
 
                rollback_rename()
 
862
                rollbacks.cleanup_now()
839
863
                raise
840
864
            result.append((from_rel, to_rel))
841
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
865
            state._mark_modified()
842
866
            self._make_dirty(reset_inventory=False)
843
867
 
844
868
        return result
854
878
    @needs_read_lock
855
879
    def path2id(self, path):
856
880
        """Return the id for path in this tree."""
 
881
        if isinstance(path, list):
 
882
            if path == []:
 
883
                path = [""]
 
884
            path = osutils.pathjoin(*path)
857
885
        path = path.strip('/')
858
886
        entry = self._get_entry(path=path)
859
887
        if entry == (None, None):
884
912
        # -- get the state object and prepare it.
885
913
        state = self.current_dirstate()
886
914
        if False and (state._dirblock_state == dirstate.DirState.NOT_IN_MEMORY
887
 
            and '' not in paths):
 
915
            and b'' not in paths):
888
916
            paths2ids = self._paths2ids_using_bisect
889
917
        else:
890
918
            paths2ids = self._paths2ids_in_memory
899
927
            """Return a list with all the entries that match path for all ids.
900
928
            """
901
929
            dirname, basename = os.path.split(path)
902
 
            key = (dirname, basename, '')
 
930
            key = (dirname, basename, b'')
903
931
            block_index, present = state._find_block_index_from_key(key)
904
932
            if not present:
905
933
                # the block which should contain path is absent.
927
955
                for entry in path_entries:
928
956
                    # for each tree.
929
957
                    for index in search_indexes:
930
 
                        if entry[1][index][0] != 'a': # absent
 
958
                        if entry[1][index][0] != b'a': # absent
931
959
                            found_versioned = True
932
960
                            # all good: found a versioned cell
933
961
                            break
937
965
                    all_versioned = False
938
966
                    break
939
967
            if not all_versioned:
940
 
                raise errors.PathsNotVersionedError(paths)
 
968
                raise errors.PathsNotVersionedError(
 
969
                    [p.decode('utf-8') for p in paths])
941
970
        # -- remove redundancy in supplied paths to prevent over-scanning --
942
971
        search_paths = osutils.minimum_path_selection(paths)
943
972
        # sketch:
955
984
            nothing. Otherwise add the id to found_ids.
956
985
            """
957
986
            for index in search_indexes:
958
 
                if entry[1][index][0] == 'r': # relocated
 
987
                if entry[1][index][0] == b'r': # relocated
959
988
                    if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
960
989
                        search_paths.add(entry[1][index][1])
961
 
                elif entry[1][index][0] != 'a': # absent
 
990
                elif entry[1][index][0] != b'a': # absent
962
991
                    found_ids.add(entry[0][2])
963
992
        while search_paths:
964
993
            current_root = search_paths.pop()
971
1000
                continue
972
1001
            for entry in root_entries:
973
1002
                _process_entry(entry)
974
 
            initial_key = (current_root, '', '')
 
1003
            initial_key = (current_root, b'', b'')
975
1004
            block_index, _ = state._find_block_index_from_key(initial_key)
976
1005
            while (block_index < len(state._dirblocks) and
977
1006
                osutils.is_inside(current_root, state._dirblocks[block_index][0])):
992
1021
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
993
1022
            for dir_name in split_paths:
994
1023
                if dir_name not in found_dir_names:
995
 
                    raise errors.PathsNotVersionedError(paths)
 
1024
                    raise errors.PathsNotVersionedError(
 
1025
                        [p.decode('utf-8') for p in paths])
996
1026
 
997
 
        for dir_name_id, trees_info in found.iteritems():
 
1027
        for dir_name_id, trees_info in viewitems(found):
998
1028
            for index in search_indexes:
999
 
                if trees_info[index][0] not in ('r', 'a'):
 
1029
                if trees_info[index][0] not in (b'r', b'a'):
1000
1030
                    found_ids.add(dir_name_id[2])
1001
1031
        return found_ids
1002
1032
 
1005
1035
 
1006
1036
        This is a meaningless operation for dirstate, but we obey it anyhow.
1007
1037
        """
1008
 
        return self.inventory
 
1038
        return self.root_inventory
1009
1039
 
1010
1040
    @needs_read_lock
1011
1041
    def revision_tree(self, revision_id):
1101
1131
                        _mod_revision.NULL_REVISION)))
1102
1132
                ghosts.append(rev_id)
1103
1133
            accepted_revisions.add(rev_id)
1104
 
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1134
        updated = False
 
1135
        if (len(real_trees) == 1
 
1136
            and not ghosts
 
1137
            and self.branch.repository._format.fast_deltas
 
1138
            and isinstance(real_trees[0][1], InventoryRevisionTree)
 
1139
            and self.get_parent_ids()):
 
1140
            rev_id, rev_tree = real_trees[0]
 
1141
            basis_id = self.get_parent_ids()[0]
 
1142
            # There are times when basis_tree won't be in
 
1143
            # self.branch.repository, (switch, for example)
 
1144
            try:
 
1145
                basis_tree = self.branch.repository.revision_tree(basis_id)
 
1146
            except errors.NoSuchRevision:
 
1147
                # Fall back to the set_parent_trees(), since we can't use
 
1148
                # _make_delta if we can't get the RevisionTree
 
1149
                pass
 
1150
            else:
 
1151
                delta = rev_tree.root_inventory._make_delta(
 
1152
                    basis_tree.root_inventory)
 
1153
                dirstate.update_basis_by_delta(delta, rev_id)
 
1154
                updated = True
 
1155
        if not updated:
 
1156
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1105
1157
        self._make_dirty(reset_inventory=False)
1106
1158
 
1107
1159
    def _set_root_id(self, file_id):
1108
1160
        """See WorkingTree.set_root_id."""
1109
1161
        state = self.current_dirstate()
1110
 
        state.set_path_id('', file_id)
 
1162
        state.set_path_id(b'', file_id)
1111
1163
        if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1112
1164
            self._make_dirty(reset_inventory=True)
1113
1165
 
1127
1179
 
1128
1180
    def unlock(self):
1129
1181
        """Unlock in format 4 trees needs to write the entire dirstate."""
1130
 
        # do non-implementation specific cleanup
1131
 
        self._cleanup()
1132
 
 
1133
1182
        if self._control_files._lock_count == 1:
 
1183
            # do non-implementation specific cleanup
 
1184
            self._cleanup()
 
1185
 
1134
1186
            # eventually we should do signature checking during read locks for
1135
1187
            # dirstate updates.
1136
1188
            if self._control_files._lock_mode == 'w':
1230
1282
                ids_to_unversion.remove(entry[0][2])
1231
1283
            block_index += 1
1232
1284
        if ids_to_unversion:
1233
 
            raise errors.NoSuchId(self, iter(ids_to_unversion).next())
 
1285
            raise errors.NoSuchId(self, next(iter(ids_to_unversion)))
1234
1286
        self._make_dirty(reset_inventory=False)
1235
1287
        # have to change the legacy inventory too.
1236
1288
        if self._inventory is not None:
1237
1289
            for file_id in file_ids:
1238
 
                self._inventory.remove_recursive_id(file_id)
 
1290
                if self._inventory.has_id(file_id):
 
1291
                    self._inventory.remove_recursive_id(file_id)
1239
1292
 
1240
1293
    @needs_tree_write_lock
1241
1294
    def rename_one(self, from_rel, to_rel, after=False):
1242
1295
        """See WorkingTree.rename_one"""
1243
1296
        self.flush()
1244
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1297
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1245
1298
 
1246
1299
    @needs_tree_write_lock
1247
1300
    def apply_inventory_delta(self, changes):
1273
1326
        # being created.
1274
1327
        self._inventory = None
1275
1328
        # generate a delta,
1276
 
        delta = inv._make_delta(self.inventory)
 
1329
        delta = inv._make_delta(self.root_inventory)
1277
1330
        # and apply it.
1278
1331
        self.apply_inventory_delta(delta)
1279
1332
        if had_inventory:
1280
1333
            self._inventory = inv
1281
1334
        self.flush()
1282
1335
 
 
1336
    @needs_tree_write_lock
 
1337
    def reset_state(self, revision_ids=None):
 
1338
        """Reset the state of the working tree.
 
1339
 
 
1340
        This does a hard-reset to a last-known-good state. This is a way to
 
1341
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
 
1342
        """
 
1343
        if revision_ids is None:
 
1344
            revision_ids = self.get_parent_ids()
 
1345
        if not revision_ids:
 
1346
            base_tree = self.branch.repository.revision_tree(
 
1347
                _mod_revision.NULL_REVISION)
 
1348
            trees = []
 
1349
        else:
 
1350
            trees = list(zip(revision_ids,
 
1351
                        self.branch.repository.revision_trees(revision_ids)))
 
1352
            base_tree = trees[0][1]
 
1353
        state = self.current_dirstate()
 
1354
        # We don't support ghosts yet
 
1355
        state.set_state_from_scratch(base_tree.root_inventory, trees, [])
 
1356
 
1283
1357
 
1284
1358
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1285
1359
 
1290
1364
        """See dirstate.SHA1Provider.sha1()."""
1291
1365
        filters = self.tree._content_filter_stack(
1292
1366
            self.tree.relpath(osutils.safe_unicode(abspath)))
1293
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1367
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1294
1368
 
1295
1369
    def stat_and_sha1(self, abspath):
1296
1370
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1300
1374
        try:
1301
1375
            statvalue = os.fstat(file_obj.fileno())
1302
1376
            if filters:
1303
 
                file_obj = filtered_input_file(file_obj, filters)
 
1377
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1304
1378
            sha1 = osutils.size_sha_file(file_obj)[1]
1305
1379
        finally:
1306
1380
            file_obj.close()
1317
1391
    def _file_content_summary(self, path, stat_result):
1318
1392
        # This is to support the somewhat obsolete path_content_summary method
1319
1393
        # with content filtering: see
1320
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1394
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1321
1395
        #
1322
1396
        # If the dirstate cache is up to date and knows the hash and size,
1323
1397
        # return that.
1336
1410
class WorkingTree4(DirStateWorkingTree):
1337
1411
    """This is the Format 4 working tree.
1338
1412
 
1339
 
    This differs from WorkingTree3 by:
 
1413
    This differs from WorkingTree by:
1340
1414
     - Having a consolidated internal dirstate, stored in a
1341
1415
       randomly-accessible sorted file on disk.
1342
1416
     - Not having a regular inventory attribute.  One can be synthesized
1370
1444
        return views.PathBasedViews(self)
1371
1445
 
1372
1446
 
1373
 
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1374
 
 
1375
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
1447
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
 
1448
 
 
1449
    missing_parent_conflicts = True
 
1450
 
 
1451
    supports_versioned_directories = True
 
1452
 
 
1453
    _lock_class = LockDir
 
1454
    _lock_file_name = 'lock'
 
1455
 
 
1456
    def _open_control_files(self, a_controldir):
 
1457
        transport = a_controldir.get_workingtree_transport(None)
 
1458
        return LockableFiles(transport, self._lock_file_name,
 
1459
                             self._lock_class)
 
1460
 
 
1461
    def initialize(self, a_controldir, revision_id=None, from_branch=None,
1376
1462
                   accelerator_tree=None, hardlink=False):
1377
1463
        """See WorkingTreeFormat.initialize().
1378
1464
 
1379
1465
        :param revision_id: allows creating a working tree at a different
1380
 
        revision than the branch is at.
 
1466
            revision than the branch is at.
1381
1467
        :param accelerator_tree: A tree which can be used for retrieving file
1382
1468
            contents more quickly than the revision tree, i.e. a workingtree.
1383
1469
            The revision tree will be used for cases where accelerator_tree's
1388
1474
        These trees get an initial random root id, if their repository supports
1389
1475
        rich root data, TREE_ROOT otherwise.
1390
1476
        """
1391
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
1392
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1393
 
        transport = a_bzrdir.get_workingtree_transport(self)
1394
 
        control_files = self._open_control_files(a_bzrdir)
 
1477
        if not isinstance(a_controldir.transport, LocalTransport):
 
1478
            raise errors.NotLocalUrl(a_controldir.transport.base)
 
1479
        transport = a_controldir.get_workingtree_transport(self)
 
1480
        control_files = self._open_control_files(a_controldir)
1395
1481
        control_files.create_lock()
1396
1482
        control_files.lock_write()
1397
 
        transport.put_bytes('format', self.get_format_string(),
1398
 
            mode=a_bzrdir._get_file_mode())
 
1483
        transport.put_bytes('format', self.as_string(),
 
1484
            mode=a_controldir._get_file_mode())
1399
1485
        if from_branch is not None:
1400
1486
            branch = from_branch
1401
1487
        else:
1402
 
            branch = a_bzrdir.open_branch()
 
1488
            branch = a_controldir.open_branch()
1403
1489
        if revision_id is None:
1404
1490
            revision_id = branch.last_revision()
1405
1491
        local_path = transport.local_abspath('dirstate')
1407
1493
        state = dirstate.DirState.initialize(local_path)
1408
1494
        state.unlock()
1409
1495
        del state
1410
 
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
1496
        wt = self._tree_class(a_controldir.root_transport.local_abspath('.'),
1411
1497
                         branch,
1412
1498
                         _format=self,
1413
 
                         _bzrdir=a_bzrdir,
 
1499
                         _bzrdir=a_controldir,
1414
1500
                         _control_files=control_files)
1415
1501
        wt._new_tree()
1416
1502
        wt.lock_tree_write()
1460
1546
                transform.build_tree(basis, wt, accelerator_tree,
1461
1547
                                     hardlink=hardlink,
1462
1548
                                     delta_from_tree=delta_from_tree)
 
1549
                for hook in MutableTree.hooks['post_build_tree']:
 
1550
                    hook(wt)
1463
1551
            finally:
1464
1552
                basis.unlock()
1465
1553
        finally:
1476
1564
        :param wt: the WorkingTree object
1477
1565
        """
1478
1566
 
1479
 
    def _open(self, a_bzrdir, control_files):
 
1567
    def open(self, a_controldir, _found=False):
 
1568
        """Return the WorkingTree object for a_controldir
 
1569
 
 
1570
        _found is a private parameter, do not use it. It is used to indicate
 
1571
               if format probing has already been done.
 
1572
        """
 
1573
        if not _found:
 
1574
            # we are being called directly and must probe.
 
1575
            raise NotImplementedError
 
1576
        if not isinstance(a_controldir.transport, LocalTransport):
 
1577
            raise errors.NotLocalUrl(a_controldir.transport.base)
 
1578
        wt = self._open(a_controldir, self._open_control_files(a_controldir))
 
1579
        return wt
 
1580
 
 
1581
    def _open(self, a_controldir, control_files):
1480
1582
        """Open the tree itself.
1481
1583
 
1482
 
        :param a_bzrdir: the dir for the tree.
 
1584
        :param a_controldir: the dir for the tree.
1483
1585
        :param control_files: the control files for the tree.
1484
1586
        """
1485
 
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1486
 
                           branch=a_bzrdir.open_branch(),
 
1587
        return self._tree_class(a_controldir.root_transport.local_abspath('.'),
 
1588
                           branch=a_controldir.open_branch(),
1487
1589
                           _format=self,
1488
 
                           _bzrdir=a_bzrdir,
 
1590
                           _bzrdir=a_controldir,
1489
1591
                           _control_files=control_files)
1490
1592
 
1491
1593
    def __get_matchingbzrdir(self):
1494
1596
    def _get_matchingbzrdir(self):
1495
1597
        """Overrideable method to get a bzrdir for testing."""
1496
1598
        # please test against something that will let us do tree references
1497
 
        return bzrdir.format_registry.make_bzrdir(
1498
 
            'dirstate-with-subtree')
 
1599
        return controldir.format_registry.make_controldir(
 
1600
            'development-subtree')
1499
1601
 
1500
1602
    _matchingbzrdir = property(__get_matchingbzrdir)
1501
1603
 
1506
1608
    This format:
1507
1609
        - exists within a metadir controlling .bzr
1508
1610
        - includes an explicit version marker for the workingtree control
1509
 
          files, separate from the BzrDir format
 
1611
          files, separate from the ControlDir format
1510
1612
        - modifies the hash cache format
1511
1613
        - is new in bzr 0.15
1512
1614
        - uses a LockDir to guard access to it.
1516
1618
 
1517
1619
    _tree_class = WorkingTree4
1518
1620
 
1519
 
    def get_format_string(self):
 
1621
    @classmethod
 
1622
    def get_format_string(cls):
1520
1623
        """See WorkingTreeFormat.get_format_string()."""
1521
1624
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1522
1625
 
1533
1636
 
1534
1637
    _tree_class = WorkingTree5
1535
1638
 
1536
 
    def get_format_string(self):
 
1639
    @classmethod
 
1640
    def get_format_string(cls):
1537
1641
        """See WorkingTreeFormat.get_format_string()."""
1538
1642
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1539
1643
 
1553
1657
 
1554
1658
    _tree_class = WorkingTree6
1555
1659
 
1556
 
    def get_format_string(self):
 
1660
    @classmethod
 
1661
    def get_format_string(cls):
1557
1662
        """See WorkingTreeFormat.get_format_string()."""
1558
1663
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1559
1664
 
1563
1668
 
1564
1669
    def _init_custom_control_files(self, wt):
1565
1670
        """Subclasses with custom control files should override this method."""
1566
 
        wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
 
1671
        wt._transport.put_bytes('views', b'',
 
1672
            mode=wt.controldir._get_file_mode())
1567
1673
 
1568
1674
    def supports_content_filtering(self):
1569
1675
        return True
1571
1677
    def supports_views(self):
1572
1678
        return True
1573
1679
 
1574
 
 
1575
 
class DirStateRevisionTree(Tree):
 
1680
    def _get_matchingbzrdir(self):
 
1681
        """Overrideable method to get a bzrdir for testing."""
 
1682
        # We use 'development-subtree' instead of '2a', because we have a
 
1683
        # few tests that want to test tree references
 
1684
        return controldir.format_registry.make_controldir('development-subtree')
 
1685
 
 
1686
 
 
1687
class DirStateRevisionTree(InventoryTree):
1576
1688
    """A revision tree pulling the inventory from a dirstate.
1577
1689
    
1578
1690
    Note that this is one of the historical (ie revision) trees cached in the
1597
1709
    def annotate_iter(self, file_id,
1598
1710
                      default_revision=_mod_revision.CURRENT_REVISION):
1599
1711
        """See Tree.annotate_iter"""
1600
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1712
        text_key = (file_id, self.get_file_revision(file_id))
1601
1713
        annotations = self._repository.texts.annotate(text_key)
1602
1714
        return [(key[-1], line) for (key, line) in annotations]
1603
1715
 
1604
 
    def _get_ancestors(self, default_revision):
1605
 
        return set(self._repository.get_ancestry(self._revision_id,
1606
 
                                                 topo_sorted=False))
1607
1716
    def _comparison_data(self, entry, path):
1608
1717
        """See Tree._comparison_data."""
1609
1718
        if entry is None:
1662
1771
        if path is not None:
1663
1772
            path = path.encode('utf8')
1664
1773
        parent_index = self._get_parent_index()
1665
 
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
 
1774
        return self._dirstate._get_entry(parent_index, fileid_utf8=file_id,
 
1775
            path_utf8=path)
1666
1776
 
1667
1777
    def _generate_inventory(self):
1668
1778
        """Create and set self.inventory from the dirstate object.
1687
1797
        # for the tree index use.
1688
1798
        root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1689
1799
        current_id = root_key[2]
1690
 
        if current_entry[parent_index][0] != 'd':
 
1800
        if current_entry[parent_index][0] != b'd':
1691
1801
            raise AssertionError()
1692
1802
        inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1693
1803
        inv.root.revision = current_entry[parent_index][4]
1708
1818
                continue
1709
1819
            for key, entry in block[1]:
1710
1820
                minikind, fingerprint, size, executable, revid = entry[parent_index]
1711
 
                if minikind in ('a', 'r'): # absent, relocated
 
1821
                if minikind in (b'a', b'r'): # absent, relocated
1712
1822
                    # not this tree
1713
1823
                    continue
1714
1824
                name = key[1]
1723
1833
                    inv_entry.text_size = size
1724
1834
                    inv_entry.text_sha1 = fingerprint
1725
1835
                elif kind == 'directory':
1726
 
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
 
1836
                    parent_ies[(dirname + b'/' + name).strip(b'/')] = inv_entry
1727
1837
                elif kind == 'symlink':
1728
 
                    inv_entry.executable = False
1729
 
                    inv_entry.text_size = None
1730
1838
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1731
1839
                elif kind == 'tree-reference':
1732
1840
                    inv_entry.reference_revision = fingerprint or None
1752
1860
        # Make sure the file exists
1753
1861
        entry = self._get_entry(file_id, path=path)
1754
1862
        if entry == (None, None): # do we raise?
1755
 
            return None
 
1863
            raise errors.NoSuchId(self, file_id)
1756
1864
        parent_index = self._get_parent_index()
1757
1865
        last_changed_revision = entry[1][parent_index][4]
1758
1866
        try:
1765
1873
        entry = self._get_entry(file_id=file_id, path=path)
1766
1874
        parent_index = self._get_parent_index()
1767
1875
        parent_details = entry[1][parent_index]
1768
 
        if parent_details[0] == 'f':
 
1876
        if parent_details[0] == b'f':
1769
1877
            return parent_details[1]
1770
1878
        return None
1771
1879
 
 
1880
    @needs_read_lock
 
1881
    def get_file_revision(self, file_id):
 
1882
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1883
        return inv[inv_file_id].revision
 
1884
 
1772
1885
    def get_file(self, file_id, path=None):
1773
 
        return StringIO(self.get_file_text(file_id))
 
1886
        return BytesIO(self.get_file_text(file_id))
1774
1887
 
1775
1888
    def get_file_size(self, file_id):
1776
1889
        """See Tree.get_file_size"""
1777
 
        return self.inventory[file_id].text_size
 
1890
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1891
        return inv[inv_file_id].text_size
1778
1892
 
1779
1893
    def get_file_text(self, file_id, path=None):
1780
 
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1781
 
        return ''.join(content)
 
1894
        content = None
 
1895
        for _, content_iter in self.iter_files_bytes([(file_id, None)]):
 
1896
            if content is not None:
 
1897
                raise AssertionError('iter_files_bytes returned'
 
1898
                    ' too many entries')
 
1899
            # For each entry returned by iter_files_bytes, we must consume the
 
1900
            # content_iter before we step the files iterator.
 
1901
            content = ''.join(content_iter)
 
1902
        if content is None:
 
1903
            raise AssertionError('iter_files_bytes did not return'
 
1904
                ' the requested data')
 
1905
        return content
1782
1906
 
1783
1907
    def get_reference_revision(self, file_id, path=None):
1784
 
        return self.inventory[file_id].reference_revision
 
1908
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1909
        return inv[inv_file_id].reference_revision
1785
1910
 
1786
1911
    def iter_files_bytes(self, desired_files):
1787
1912
        """See Tree.iter_files_bytes.
1797
1922
                                       identifier))
1798
1923
        return self._repository.iter_files_bytes(repo_desired_files)
1799
1924
 
1800
 
    def get_symlink_target(self, file_id):
 
1925
    def get_symlink_target(self, file_id, path=None):
1801
1926
        entry = self._get_entry(file_id=file_id)
1802
1927
        parent_index = self._get_parent_index()
1803
 
        if entry[1][parent_index][0] != 'l':
 
1928
        if entry[1][parent_index][0] != b'l':
1804
1929
            return None
1805
1930
        else:
1806
1931
            target = entry[1][parent_index][1]
1811
1936
        """Return the revision id for this tree."""
1812
1937
        return self._revision_id
1813
1938
 
1814
 
    def _get_inventory(self):
 
1939
    def _get_root_inventory(self):
1815
1940
        if self._inventory is not None:
1816
1941
            return self._inventory
1817
1942
        self._must_be_locked()
1818
1943
        self._generate_inventory()
1819
1944
        return self._inventory
1820
1945
 
1821
 
    inventory = property(_get_inventory,
 
1946
    root_inventory = property(_get_root_inventory,
1822
1947
                         doc="Inventory of this Tree")
1823
1948
 
1824
1949
    def get_parent_ids(self):
1841
1966
 
1842
1967
    def path_content_summary(self, path):
1843
1968
        """See Tree.path_content_summary."""
1844
 
        id = self.inventory.path2id(path)
1845
 
        if id is None:
 
1969
        inv, inv_file_id = self._path2inv_file_id(path)
 
1970
        if inv_file_id is None:
1846
1971
            return ('missing', None, None, None)
1847
 
        entry = self._inventory[id]
 
1972
        entry = inv[inv_file_id]
1848
1973
        kind = entry.kind
1849
1974
        if kind == 'file':
1850
1975
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
1854
1979
            return (kind, None, None, None)
1855
1980
 
1856
1981
    def is_executable(self, file_id, path=None):
1857
 
        ie = self.inventory[file_id]
 
1982
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1983
        ie = inv[inv_file_id]
1858
1984
        if ie.kind != "file":
1859
 
            return None
 
1985
            return False
1860
1986
        return ie.executable
1861
1987
 
 
1988
    def is_locked(self):
 
1989
        return self._locked
 
1990
 
1862
1991
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1863
1992
        # We use a standard implementation, because DirStateRevisionTree is
1864
1993
        # dealing with one of the parents of the current state
1865
 
        inv = self._get_inventory()
1866
1994
        if from_dir is None:
 
1995
            inv = self.root_inventory
1867
1996
            from_dir_id = None
1868
1997
        else:
1869
 
            from_dir_id = inv.path2id(from_dir)
 
1998
            inv, from_dir_id = self._path2inv_file_id(from_dir)
1870
1999
            if from_dir_id is None:
1871
2000
                # Directory not versioned
1872
2001
                return
 
2002
        # FIXME: Support nested trees
1873
2003
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1874
2004
        if inv.root is not None and not include_root and from_dir is None:
1875
 
            entries.next()
 
2005
            next(entries)
1876
2006
        for path, entry in entries:
1877
2007
            yield path, 'V', entry.kind, entry.file_id, entry
1878
2008
 
1879
2009
    def lock_read(self):
1880
 
        """Lock the tree for a set of operations."""
 
2010
        """Lock the tree for a set of operations.
 
2011
 
 
2012
        :return: A breezy.lock.LogicalLockResult.
 
2013
        """
1881
2014
        if not self._locked:
1882
2015
            self._repository.lock_read()
1883
2016
            if self._dirstate._lock_token is None:
1884
2017
                self._dirstate.lock_read()
1885
2018
                self._dirstate_locked = True
1886
2019
        self._locked += 1
 
2020
        return LogicalLockResult(self.unlock)
1887
2021
 
1888
2022
    def _must_be_locked(self):
1889
2023
        if not self._locked:
1893
2027
    def path2id(self, path):
1894
2028
        """Return the id for path in this tree."""
1895
2029
        # lookup by path: faster than splitting and walking the ivnentory.
 
2030
        if isinstance(path, list):
 
2031
            if path == []:
 
2032
                path = [""]
 
2033
            path = osutils.pathjoin(*path)
1896
2034
        entry = self._get_entry(path=path)
1897
2035
        if entry == (None, None):
1898
2036
            return None
1921
2059
        # So for now, we just build up the parent inventory, and extract
1922
2060
        # it the same way RevisionTree does.
1923
2061
        _directory = 'directory'
1924
 
        inv = self._get_inventory()
 
2062
        inv = self._get_root_inventory()
1925
2063
        top_id = inv.path2id(prefix)
1926
2064
        if top_id is None:
1927
2065
            pending = []
1962
2100
    def __init__(self, source, target):
1963
2101
        super(InterDirStateTree, self).__init__(source, target)
1964
2102
        if not InterDirStateTree.is_compatible(source, target):
1965
 
            raise Exception, "invalid source %r and target %r" % (source, target)
 
2103
            raise Exception("invalid source %r and target %r" % (source, target))
1966
2104
 
1967
2105
    @staticmethod
1968
2106
    def make_source_parent_tree(source, target):
1969
2107
        """Change the source tree into a parent of the target."""
1970
2108
        revid = source.commit('record tree')
1971
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2109
        target.branch.fetch(source.branch, revid)
1972
2110
        target.set_parent_ids([revid])
1973
2111
        return target.basis_tree(), target
1974
2112
 
1981
2119
    @classmethod
1982
2120
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1983
2121
                                                  target):
1984
 
        from bzrlib.tests.test__dirstate_helpers import \
 
2122
        from .tests.test__dirstate_helpers import \
1985
2123
            compiled_dirstate_helpers_feature
1986
2124
        test_case.requireFeature(compiled_dirstate_helpers_feature)
1987
 
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
 
2125
        from ._dirstate_helpers_pyx import ProcessEntryC
1988
2126
        result = klass.make_source_parent_tree(source, target)
1989
2127
        result[1]._iter_changes = ProcessEntryC
1990
2128
        return result
2053
2191
                specific_files_utf8.add(path.encode('utf8'))
2054
2192
            specific_files = specific_files_utf8
2055
2193
        else:
2056
 
            specific_files = set([''])
 
2194
            specific_files = {b''}
2057
2195
        # -- specific_files is now a utf8 path set --
2058
2196
 
2059
2197
        # -- get the state object and prepare it.
2066
2204
                path_entries = state._entries_for_path(path)
2067
2205
                if not path_entries:
2068
2206
                    # this specified path is not present at all: error
2069
 
                    not_versioned.append(path)
 
2207
                    not_versioned.append(path.decode('utf-8'))
2070
2208
                    continue
2071
2209
                found_versioned = False
2072
2210
                # for each id at this path
2073
2211
                for entry in path_entries:
2074
2212
                    # for each tree.
2075
2213
                    for index in indices:
2076
 
                        if entry[1][index][0] != 'a': # absent
 
2214
                        if entry[1][index][0] != b'a': # absent
2077
2215
                            found_versioned = True
2078
2216
                            # all good: found a versioned cell
2079
2217
                            break
2080
2218
                if not found_versioned:
2081
2219
                    # none of the indexes was not 'absent' at all ids for this
2082
2220
                    # path.
2083
 
                    not_versioned.append(path)
 
2221
                    not_versioned.append(path.decode('utf-8'))
2084
2222
            if len(not_versioned) > 0:
2085
2223
                raise errors.PathsNotVersionedError(not_versioned)
2086
2224
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2133
2271
 
2134
2272
    def create_dirstate_data(self, tree):
2135
2273
        """Create the dirstate based data for tree."""
2136
 
        local_path = tree.bzrdir.get_workingtree_transport(None
 
2274
        local_path = tree.controldir.get_workingtree_transport(None
2137
2275
            ).local_abspath('dirstate')
2138
2276
        state = dirstate.DirState.from_tree(tree, local_path)
2139
2277
        state.save()
2141
2279
 
2142
2280
    def remove_xml_files(self, tree):
2143
2281
        """Remove the oldformat 3 data."""
2144
 
        transport = tree.bzrdir.get_workingtree_transport(None)
 
2282
        transport = tree.controldir.get_workingtree_transport(None)
2145
2283
        for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2146
2284
            'pending-merges', 'stat-cache']:
2147
2285
            try:
2153
2291
    def update_format(self, tree):
2154
2292
        """Change the format marker."""
2155
2293
        tree._transport.put_bytes('format',
2156
 
            self.target_format.get_format_string(),
2157
 
            mode=tree.bzrdir._get_file_mode())
 
2294
            self.target_format.as_string(),
 
2295
            mode=tree.controldir._get_file_mode())
2158
2296
 
2159
2297
 
2160
2298
class Converter4to5(object):
2176
2314
    def update_format(self, tree):
2177
2315
        """Change the format marker."""
2178
2316
        tree._transport.put_bytes('format',
2179
 
            self.target_format.get_format_string(),
2180
 
            mode=tree.bzrdir._get_file_mode())
 
2317
            self.target_format.as_string(),
 
2318
            mode=tree.controldir._get_file_mode())
2181
2319
 
2182
2320
 
2183
2321
class Converter4or5to6(object):
2199
2337
 
2200
2338
    def init_custom_control_files(self, tree):
2201
2339
        """Initialize custom control files."""
2202
 
        tree._transport.put_bytes('views', '',
2203
 
            mode=tree.bzrdir._get_file_mode())
 
2340
        tree._transport.put_bytes('views', b'',
 
2341
            mode=tree.controldir._get_file_mode())
2204
2342
 
2205
2343
    def update_format(self, tree):
2206
2344
        """Change the format marker."""
2207
2345
        tree._transport.put_bytes('format',
2208
 
            self.target_format.get_format_string(),
2209
 
            mode=tree.bzrdir._get_file_mode())
 
2346
            self.target_format.as_string(),
 
2347
            mode=tree.controldir._get_file_mode())