/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 01:12:29 UTC
  • mto: This revision was merged to the branch mainline in revision 6685.
  • Revision ID: gzlist@googlemail.com-20170611011229-somdjbalby8m7vlw
Make _chunks_to_lines pass for 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.lock import LogicalLockResult
57
 
from bzrlib.mutabletree import needs_tree_write_lock
58
 
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 (
59
70
    file_kind,
60
71
    isdir,
61
72
    pathjoin,
62
73
    realpath,
63
74
    safe_unicode,
64
75
    )
65
 
from bzrlib.trace import mutter
66
 
from bzrlib.transport.local import LocalTransport
67
 
from bzrlib.tree import InterTree
68
 
from bzrlib.tree import Tree
69
 
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
70
 
 
71
 
 
72
 
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
 
73
95
    def __init__(self, basedir,
74
96
                 branch,
75
97
                 _control_files=None,
83
105
        would be meaningless).
84
106
        """
85
107
        self._format = _format
86
 
        self.bzrdir = _bzrdir
 
108
        self.controldir = _bzrdir
87
109
        basedir = safe_unicode(basedir)
88
 
        mutter("opening working tree %r", basedir)
 
110
        trace.mutter("opening working tree %r", basedir)
89
111
        self._branch = branch
90
112
        self.basedir = realpath(basedir)
91
113
        # if branch is at our basedir and is a format 6 or less
111
133
        """See MutableTree._add."""
112
134
        state = self.current_dirstate()
113
135
        for f, file_id, kind in zip(files, ids, kinds):
114
 
            f = f.strip('/')
 
136
            f = f.strip(b'/')
115
137
            if self.path2id(f):
116
138
                # special case tree root handling.
117
 
                if f == '' and self.path2id(f) == ROOT_ID:
118
 
                    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))
119
141
                continue
120
142
            if file_id is None:
121
143
                file_id = generate_ids.gen_file_id(f)
122
144
            # deliberately add the file with no cached stat or sha1
123
145
            # - on the first access it will be gathered, and we can
124
146
            # always change this once tests are all passing.
125
 
            state.add(f, file_id, kind, None, '')
 
147
            state.add(f, file_id, kind, None, b'')
126
148
        self._make_dirty(reset_inventory=True)
127
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
 
128
154
    def _make_dirty(self, reset_inventory):
129
155
        """Make the tree state dirty.
130
156
 
182
208
 
183
209
    def _comparison_data(self, entry, path):
184
210
        kind, executable, stat_value = \
185
 
            WorkingTree3._comparison_data(self, entry, path)
 
211
            WorkingTree._comparison_data(self, entry, path)
186
212
        # it looks like a plain directory, but it's really a reference -- see
187
213
        # also kind()
188
214
        if (self._repo_supports_tree_reference and kind == 'directory'
194
220
    def commit(self, message=None, revprops=None, *args, **kwargs):
195
221
        # mark the tree as dirty post commit - commit
196
222
        # can change the current versioned list by doing deletes.
197
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
223
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
198
224
        self._make_dirty(reset_inventory=True)
199
225
        return result
200
226
 
216
242
        """
217
243
        if self._dirstate is not None:
218
244
            return self._dirstate
219
 
        local_path = self.bzrdir.get_workingtree_transport(None
 
245
        local_path = self.controldir.get_workingtree_transport(None
220
246
            ).local_abspath('dirstate')
221
247
        self._dirstate = dirstate.DirState.on_file(local_path,
222
 
            self._sha1_provider())
 
248
            self._sha1_provider(), self._worth_saving_limit())
223
249
        return self._dirstate
224
250
 
225
251
    def _sha1_provider(self):
234
260
        else:
235
261
            return None
236
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
 
237
271
    def filter_unversioned_files(self, paths):
238
272
        """Filter out paths that are versioned.
239
273
 
281
315
        state._read_dirblocks_if_needed()
282
316
        root_key, current_entry = self._get_entry(path='')
283
317
        current_id = root_key[2]
284
 
        if not (current_entry[0][0] == 'd'): # directory
 
318
        if not (current_entry[0][0] == b'd'): # directory
285
319
            raise AssertionError(current_entry)
286
320
        inv = Inventory(root_id=current_id)
287
321
        # Turn some things into local variables
301
335
                continue
302
336
            for key, entry in block[1]:
303
337
                minikind, link_or_sha1, size, executable, stat = entry[0]
304
 
                if minikind in ('a', 'r'): # absent, relocated
 
338
                if minikind in (b'a', b'r'): # absent, relocated
305
339
                    # a parent tree only entry
306
340
                    continue
307
341
                name = key[1]
319
353
                    #inv_entry.text_sha1 = sha1
320
354
                elif kind == 'directory':
321
355
                    # add this entry to the parent map.
322
 
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
 
356
                    parent_ies[(dirname + b'/' + name).strip(b'/')] = inv_entry
323
357
                elif kind == 'tree-reference':
324
358
                    if not self._repo_supports_tree_reference:
325
359
                        raise errors.UnsupportedOperation(
369
403
        state = self.current_dirstate()
370
404
        if stat_value is None:
371
405
            try:
372
 
                stat_value = os.lstat(file_abspath)
373
 
            except OSError, e:
 
406
                stat_value = osutils.lstat(file_abspath)
 
407
            except OSError as e:
374
408
                if e.errno == errno.ENOENT:
375
409
                    return None
376
410
                else:
377
411
                    raise
378
412
        link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
379
413
            stat_value=stat_value)
380
 
        if entry[1][0][0] == 'f':
 
414
        if entry[1][0][0] == b'f':
381
415
            if link_or_sha1 is None:
382
416
                file_obj, statvalue = self.get_file_with_stat(file_id, path)
383
417
                try:
390
424
                return link_or_sha1
391
425
        return None
392
426
 
393
 
    def _get_inventory(self):
 
427
    def _get_root_inventory(self):
394
428
        """Get the inventory for the tree. This is only valid within a lock."""
395
429
        if 'evil' in debug.debug_flags:
396
430
            trace.mutter_callsite(2,
401
435
        self._generate_inventory()
402
436
        return self._inventory
403
437
 
404
 
    inventory = property(_get_inventory,
405
 
                         doc="Inventory of this Tree")
 
438
    root_inventory = property(_get_root_inventory,
 
439
        "Root inventory of this tree")
406
440
 
407
441
    @needs_read_lock
408
442
    def get_parent_ids(self):
456
490
            return False # Missing entries are not executable
457
491
        return entry[1][0][3] # Executable?
458
492
 
459
 
    if not osutils.supports_executable():
460
 
        def is_executable(self, file_id, path=None):
461
 
            """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.
462
495
 
463
 
            Note: The caller is expected to take a read-lock before calling this.
464
 
            """
 
496
        Note: The caller is expected to take a read-lock before calling this.
 
497
        """
 
498
        if not self._supports_executable():
465
499
            entry = self._get_entry(file_id=file_id, path=path)
466
500
            if entry == (None, None):
467
501
                return False
468
502
            return entry[1][0][3]
469
 
 
470
 
        _is_executable_from_path_and_stat = \
471
 
            _is_executable_from_path_and_stat_from_basis
472
 
    else:
473
 
        def is_executable(self, file_id, path=None):
474
 
            """Test if a file is executable or not.
475
 
 
476
 
            Note: The caller is expected to take a read-lock before calling this.
477
 
            """
 
503
        else:
478
504
            self._must_be_locked()
479
505
            if not path:
480
506
                path = self.id2path(file_id)
481
 
            mode = os.lstat(self.abspath(path)).st_mode
 
507
            mode = osutils.lstat(self.abspath(path)).st_mode
482
508
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
483
509
 
484
510
    def all_file_ids(self):
486
512
        self._must_be_locked()
487
513
        result = set()
488
514
        for key, tree_details in self.current_dirstate()._iter_entries():
489
 
            if tree_details[0][0] in ('a', 'r'): # relocated
 
515
            if tree_details[0][0] in (b'a', b'r'): # relocated
490
516
                continue
491
517
            result.add(key[2])
492
518
        return result
500
526
        """
501
527
        result = []
502
528
        for key, tree_details in self.current_dirstate()._iter_entries():
503
 
            if tree_details[0][0] in ('a', 'r'): # absent, relocated
 
529
            if tree_details[0][0] in (b'a', b'r'): # absent, relocated
504
530
                # not relevant to the working tree
505
531
                continue
506
532
            path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
514
540
            # return
515
541
            return
516
542
        for key, tree_details in self.current_dirstate()._iter_entries():
517
 
            if tree_details[0][0] in ('a', 'r'): # absent, relocated
 
543
            if tree_details[0][0] in (b'a', b'r'): # absent, relocated
518
544
                # not relevant to the working tree
519
545
                continue
520
546
            if not key[1]:
528
554
                # path is missing on disk.
529
555
                continue
530
556
 
531
 
    def _observed_sha1(self, file_id, path, (sha1, statvalue)):
 
557
    def _observed_sha1(self, file_id, path, sha_and_stat):
532
558
        """See MutableTree._observed_sha1."""
533
559
        state = self.current_dirstate()
534
560
        entry = self._get_entry(file_id=file_id, path=path)
535
 
        state._observed_sha1(entry, sha1, statvalue)
 
561
        state._observed_sha1(entry, *sha_and_stat)
536
562
 
537
563
    def kind(self, file_id):
538
564
        """Return the kind of a file.
554
580
        if (self._repo_supports_tree_reference and kind == 'directory'):
555
581
            entry = self._get_entry(path=relpath)
556
582
            if entry[1] is not None:
557
 
                if entry[1][0][0] == 't':
 
583
                if entry[1][0][0] == b't':
558
584
                    kind = 'tree-reference'
559
585
        return kind
560
586
 
570
596
    def lock_read(self):
571
597
        """See Branch.lock_read, and WorkingTree.unlock.
572
598
 
573
 
        :return: A bzrlib.lock.LogicalLockResult.
 
599
        :return: A breezy.lock.LogicalLockResult.
574
600
        """
575
601
        self.branch.lock_read()
576
602
        try:
616
642
    def lock_tree_write(self):
617
643
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
618
644
 
619
 
        :return: A bzrlib.lock.LogicalLockResult.
 
645
        :return: A breezy.lock.LogicalLockResult.
620
646
        """
621
647
        self.branch.lock_read()
622
648
        return self._lock_self_write()
624
650
    def lock_write(self):
625
651
        """See MutableTree.lock_write, and WorkingTree.unlock.
626
652
 
627
 
        :return: A bzrlib.lock.LogicalLockResult.
 
653
        :return: A breezy.lock.LogicalLockResult.
628
654
        """
629
655
        self.branch.lock_write()
630
656
        return self._lock_self_write()
664
690
 
665
691
        if self._inventory is not None:
666
692
            update_inventory = True
667
 
            inv = self.inventory
 
693
            inv = self.root_inventory
668
694
            to_dir_id = to_entry[0][2]
669
695
            to_dir_ie = inv[to_dir_id]
670
696
        else:
671
697
            update_inventory = False
672
698
 
673
 
        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()
674
702
        def move_one(old_entry, from_path_utf8, minikind, executable,
675
703
                     fingerprint, packed_stat, size,
676
704
                     to_block, to_key, to_path_utf8):
677
705
            state._make_absent(old_entry)
678
706
            from_key = old_entry[0]
679
 
            rollbacks.append(
680
 
                lambda:state.update_minimal(from_key,
681
 
                    minikind,
682
 
                    executable=executable,
683
 
                    fingerprint=fingerprint,
684
 
                    packed_stat=packed_stat,
685
 
                    size=size,
686
 
                    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)
687
716
            state.update_minimal(to_key,
688
717
                    minikind,
689
718
                    executable=executable,
693
722
                    path_utf8=to_path_utf8)
694
723
            added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
695
724
            new_entry = to_block[1][added_entry_index]
696
 
            rollbacks.append(lambda:state._make_absent(new_entry))
 
725
            rollbacks.add_cleanup(state._make_absent, new_entry)
697
726
 
698
727
        for from_rel in from_paths:
699
728
            # from_rel is 'pathinroot/foo/bar'
738
767
                elif not after:
739
768
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
740
769
 
741
 
            rollbacks = []
742
 
            def rollback_rename():
743
 
                """A single rename has failed, roll it back."""
744
 
                # roll back everything, even if we encounter trouble doing one
745
 
                # of them.
746
 
                #
747
 
                # TODO: at least log the other exceptions rather than just
748
 
                # losing them mbp 20070307
749
 
                exc_info = None
750
 
                for rollback in reversed(rollbacks):
751
 
                    try:
752
 
                        rollback()
753
 
                    except Exception, e:
754
 
                        exc_info = sys.exc_info()
755
 
                if exc_info:
756
 
                    raise exc_info[0], exc_info[1], exc_info[2]
757
 
 
758
770
            # perform the disk move first - its the most likely failure point.
759
771
            if move_file:
760
772
                from_rel_abs = self.abspath(from_rel)
761
773
                to_rel_abs = self.abspath(to_rel)
762
774
                try:
763
775
                    osutils.rename(from_rel_abs, to_rel_abs)
764
 
                except OSError, e:
 
776
                except OSError as e:
765
777
                    raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
766
 
                rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
 
778
                rollbacks.add_cleanup(osutils.rename, to_rel_abs, from_rel_abs)
767
779
            try:
768
780
                # perform the rename in the inventory next if needed: its easy
769
781
                # to rollback
772
784
                    from_entry = inv[from_id]
773
785
                    current_parent = from_entry.parent_id
774
786
                    inv.rename(from_id, to_dir_id, from_tail)
775
 
                    rollbacks.append(
776
 
                        lambda: inv.rename(from_id, current_parent, from_tail))
 
787
                    rollbacks.add_cleanup(
 
788
                        inv.rename, from_id, current_parent, from_tail)
777
789
                # finally do the rename in the dirstate, which is a little
778
790
                # tricky to rollback, but least likely to need it.
779
791
                old_block_index, old_entry_index, dir_present, file_present = \
795
807
                         to_key=to_key,
796
808
                         to_path_utf8=to_rel_utf8)
797
809
 
798
 
                if minikind == 'd':
 
810
                if minikind == b'd':
799
811
                    def update_dirblock(from_dir, to_key, to_dir_utf8):
800
812
                        """Recursively update all entries in this dirblock."""
801
 
                        if from_dir == '':
 
813
                        if from_dir == b'':
802
814
                            raise AssertionError("renaming root not supported")
803
815
                        from_key = (from_dir, '')
804
816
                        from_block_idx, present = \
825
837
                            from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
826
838
                            to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
827
839
                            minikind = cur_details[0]
828
 
                            if minikind in 'ar':
 
840
                            if minikind in (b'a', b'r'):
829
841
                                # Deleted children of a renamed directory
830
842
                                # Do not need to be updated.
831
843
                                # Children that have been renamed out of this
840
852
                                     to_block=to_block,
841
853
                                     to_key=to_key,
842
854
                                     to_path_utf8=to_path_utf8)
843
 
                            if minikind == 'd':
 
855
                            if minikind == b'd':
844
856
                                # We need to move all the children of this
845
857
                                # entry
846
858
                                update_dirblock(from_path_utf8, to_key,
847
859
                                                to_path_utf8)
848
860
                    update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
849
861
            except:
850
 
                rollback_rename()
 
862
                rollbacks.cleanup_now()
851
863
                raise
852
864
            result.append((from_rel, to_rel))
853
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
865
            state._mark_modified()
854
866
            self._make_dirty(reset_inventory=False)
855
867
 
856
868
        return result
866
878
    @needs_read_lock
867
879
    def path2id(self, path):
868
880
        """Return the id for path in this tree."""
 
881
        if isinstance(path, list):
 
882
            if path == []:
 
883
                path = [""]
 
884
            path = osutils.pathjoin(*path)
869
885
        path = path.strip('/')
870
886
        entry = self._get_entry(path=path)
871
887
        if entry == (None, None):
896
912
        # -- get the state object and prepare it.
897
913
        state = self.current_dirstate()
898
914
        if False and (state._dirblock_state == dirstate.DirState.NOT_IN_MEMORY
899
 
            and '' not in paths):
 
915
            and b'' not in paths):
900
916
            paths2ids = self._paths2ids_using_bisect
901
917
        else:
902
918
            paths2ids = self._paths2ids_in_memory
911
927
            """Return a list with all the entries that match path for all ids.
912
928
            """
913
929
            dirname, basename = os.path.split(path)
914
 
            key = (dirname, basename, '')
 
930
            key = (dirname, basename, b'')
915
931
            block_index, present = state._find_block_index_from_key(key)
916
932
            if not present:
917
933
                # the block which should contain path is absent.
939
955
                for entry in path_entries:
940
956
                    # for each tree.
941
957
                    for index in search_indexes:
942
 
                        if entry[1][index][0] != 'a': # absent
 
958
                        if entry[1][index][0] != b'a': # absent
943
959
                            found_versioned = True
944
960
                            # all good: found a versioned cell
945
961
                            break
949
965
                    all_versioned = False
950
966
                    break
951
967
            if not all_versioned:
952
 
                raise errors.PathsNotVersionedError(paths)
 
968
                raise errors.PathsNotVersionedError(
 
969
                    [p.decode('utf-8') for p in paths])
953
970
        # -- remove redundancy in supplied paths to prevent over-scanning --
954
971
        search_paths = osutils.minimum_path_selection(paths)
955
972
        # sketch:
967
984
            nothing. Otherwise add the id to found_ids.
968
985
            """
969
986
            for index in search_indexes:
970
 
                if entry[1][index][0] == 'r': # relocated
 
987
                if entry[1][index][0] == b'r': # relocated
971
988
                    if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
972
989
                        search_paths.add(entry[1][index][1])
973
 
                elif entry[1][index][0] != 'a': # absent
 
990
                elif entry[1][index][0] != b'a': # absent
974
991
                    found_ids.add(entry[0][2])
975
992
        while search_paths:
976
993
            current_root = search_paths.pop()
983
1000
                continue
984
1001
            for entry in root_entries:
985
1002
                _process_entry(entry)
986
 
            initial_key = (current_root, '', '')
 
1003
            initial_key = (current_root, b'', b'')
987
1004
            block_index, _ = state._find_block_index_from_key(initial_key)
988
1005
            while (block_index < len(state._dirblocks) and
989
1006
                osutils.is_inside(current_root, state._dirblocks[block_index][0])):
1004
1021
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1005
1022
            for dir_name in split_paths:
1006
1023
                if dir_name not in found_dir_names:
1007
 
                    raise errors.PathsNotVersionedError(paths)
 
1024
                    raise errors.PathsNotVersionedError(
 
1025
                        [p.decode('utf-8') for p in paths])
1008
1026
 
1009
 
        for dir_name_id, trees_info in found.iteritems():
 
1027
        for dir_name_id, trees_info in viewitems(found):
1010
1028
            for index in search_indexes:
1011
 
                if trees_info[index][0] not in ('r', 'a'):
 
1029
                if trees_info[index][0] not in (b'r', b'a'):
1012
1030
                    found_ids.add(dir_name_id[2])
1013
1031
        return found_ids
1014
1032
 
1017
1035
 
1018
1036
        This is a meaningless operation for dirstate, but we obey it anyhow.
1019
1037
        """
1020
 
        return self.inventory
 
1038
        return self.root_inventory
1021
1039
 
1022
1040
    @needs_read_lock
1023
1041
    def revision_tree(self, revision_id):
1113
1131
                        _mod_revision.NULL_REVISION)))
1114
1132
                ghosts.append(rev_id)
1115
1133
            accepted_revisions.add(rev_id)
1116
 
        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)
1117
1157
        self._make_dirty(reset_inventory=False)
1118
1158
 
1119
1159
    def _set_root_id(self, file_id):
1120
1160
        """See WorkingTree.set_root_id."""
1121
1161
        state = self.current_dirstate()
1122
 
        state.set_path_id('', file_id)
 
1162
        state.set_path_id(b'', file_id)
1123
1163
        if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1124
1164
            self._make_dirty(reset_inventory=True)
1125
1165
 
1139
1179
 
1140
1180
    def unlock(self):
1141
1181
        """Unlock in format 4 trees needs to write the entire dirstate."""
1142
 
        # do non-implementation specific cleanup
1143
 
        self._cleanup()
1144
 
 
1145
1182
        if self._control_files._lock_count == 1:
 
1183
            # do non-implementation specific cleanup
 
1184
            self._cleanup()
 
1185
 
1146
1186
            # eventually we should do signature checking during read locks for
1147
1187
            # dirstate updates.
1148
1188
            if self._control_files._lock_mode == 'w':
1242
1282
                ids_to_unversion.remove(entry[0][2])
1243
1283
            block_index += 1
1244
1284
        if ids_to_unversion:
1245
 
            raise errors.NoSuchId(self, iter(ids_to_unversion).next())
 
1285
            raise errors.NoSuchId(self, next(iter(ids_to_unversion)))
1246
1286
        self._make_dirty(reset_inventory=False)
1247
1287
        # have to change the legacy inventory too.
1248
1288
        if self._inventory is not None:
1249
1289
            for file_id in file_ids:
1250
 
                self._inventory.remove_recursive_id(file_id)
 
1290
                if self._inventory.has_id(file_id):
 
1291
                    self._inventory.remove_recursive_id(file_id)
1251
1292
 
1252
1293
    @needs_tree_write_lock
1253
1294
    def rename_one(self, from_rel, to_rel, after=False):
1254
1295
        """See WorkingTree.rename_one"""
1255
1296
        self.flush()
1256
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1297
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1257
1298
 
1258
1299
    @needs_tree_write_lock
1259
1300
    def apply_inventory_delta(self, changes):
1285
1326
        # being created.
1286
1327
        self._inventory = None
1287
1328
        # generate a delta,
1288
 
        delta = inv._make_delta(self.inventory)
 
1329
        delta = inv._make_delta(self.root_inventory)
1289
1330
        # and apply it.
1290
1331
        self.apply_inventory_delta(delta)
1291
1332
        if had_inventory:
1292
1333
            self._inventory = inv
1293
1334
        self.flush()
1294
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
 
1295
1357
 
1296
1358
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1297
1359
 
1302
1364
        """See dirstate.SHA1Provider.sha1()."""
1303
1365
        filters = self.tree._content_filter_stack(
1304
1366
            self.tree.relpath(osutils.safe_unicode(abspath)))
1305
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1367
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1306
1368
 
1307
1369
    def stat_and_sha1(self, abspath):
1308
1370
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1312
1374
        try:
1313
1375
            statvalue = os.fstat(file_obj.fileno())
1314
1376
            if filters:
1315
 
                file_obj = filtered_input_file(file_obj, filters)
 
1377
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1316
1378
            sha1 = osutils.size_sha_file(file_obj)[1]
1317
1379
        finally:
1318
1380
            file_obj.close()
1329
1391
    def _file_content_summary(self, path, stat_result):
1330
1392
        # This is to support the somewhat obsolete path_content_summary method
1331
1393
        # with content filtering: see
1332
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1394
        # <https://bugs.launchpad.net/bzr/+bug/415508>.
1333
1395
        #
1334
1396
        # If the dirstate cache is up to date and knows the hash and size,
1335
1397
        # return that.
1348
1410
class WorkingTree4(DirStateWorkingTree):
1349
1411
    """This is the Format 4 working tree.
1350
1412
 
1351
 
    This differs from WorkingTree3 by:
 
1413
    This differs from WorkingTree by:
1352
1414
     - Having a consolidated internal dirstate, stored in a
1353
1415
       randomly-accessible sorted file on disk.
1354
1416
     - Not having a regular inventory attribute.  One can be synthesized
1382
1444
        return views.PathBasedViews(self)
1383
1445
 
1384
1446
 
1385
 
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1386
 
 
1387
 
    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,
1388
1462
                   accelerator_tree=None, hardlink=False):
1389
1463
        """See WorkingTreeFormat.initialize().
1390
1464
 
1391
1465
        :param revision_id: allows creating a working tree at a different
1392
 
        revision than the branch is at.
 
1466
            revision than the branch is at.
1393
1467
        :param accelerator_tree: A tree which can be used for retrieving file
1394
1468
            contents more quickly than the revision tree, i.e. a workingtree.
1395
1469
            The revision tree will be used for cases where accelerator_tree's
1400
1474
        These trees get an initial random root id, if their repository supports
1401
1475
        rich root data, TREE_ROOT otherwise.
1402
1476
        """
1403
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
1404
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1405
 
        transport = a_bzrdir.get_workingtree_transport(self)
1406
 
        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)
1407
1481
        control_files.create_lock()
1408
1482
        control_files.lock_write()
1409
 
        transport.put_bytes('format', self.get_format_string(),
1410
 
            mode=a_bzrdir._get_file_mode())
 
1483
        transport.put_bytes('format', self.as_string(),
 
1484
            mode=a_controldir._get_file_mode())
1411
1485
        if from_branch is not None:
1412
1486
            branch = from_branch
1413
1487
        else:
1414
 
            branch = a_bzrdir.open_branch()
 
1488
            branch = a_controldir.open_branch()
1415
1489
        if revision_id is None:
1416
1490
            revision_id = branch.last_revision()
1417
1491
        local_path = transport.local_abspath('dirstate')
1419
1493
        state = dirstate.DirState.initialize(local_path)
1420
1494
        state.unlock()
1421
1495
        del state
1422
 
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
1496
        wt = self._tree_class(a_controldir.root_transport.local_abspath('.'),
1423
1497
                         branch,
1424
1498
                         _format=self,
1425
 
                         _bzrdir=a_bzrdir,
 
1499
                         _bzrdir=a_controldir,
1426
1500
                         _control_files=control_files)
1427
1501
        wt._new_tree()
1428
1502
        wt.lock_tree_write()
1472
1546
                transform.build_tree(basis, wt, accelerator_tree,
1473
1547
                                     hardlink=hardlink,
1474
1548
                                     delta_from_tree=delta_from_tree)
 
1549
                for hook in MutableTree.hooks['post_build_tree']:
 
1550
                    hook(wt)
1475
1551
            finally:
1476
1552
                basis.unlock()
1477
1553
        finally:
1488
1564
        :param wt: the WorkingTree object
1489
1565
        """
1490
1566
 
1491
 
    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):
1492
1582
        """Open the tree itself.
1493
1583
 
1494
 
        :param a_bzrdir: the dir for the tree.
 
1584
        :param a_controldir: the dir for the tree.
1495
1585
        :param control_files: the control files for the tree.
1496
1586
        """
1497
 
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1498
 
                           branch=a_bzrdir.open_branch(),
 
1587
        return self._tree_class(a_controldir.root_transport.local_abspath('.'),
 
1588
                           branch=a_controldir.open_branch(),
1499
1589
                           _format=self,
1500
 
                           _bzrdir=a_bzrdir,
 
1590
                           _bzrdir=a_controldir,
1501
1591
                           _control_files=control_files)
1502
1592
 
1503
1593
    def __get_matchingbzrdir(self):
1506
1596
    def _get_matchingbzrdir(self):
1507
1597
        """Overrideable method to get a bzrdir for testing."""
1508
1598
        # please test against something that will let us do tree references
1509
 
        return bzrdir.format_registry.make_bzrdir(
1510
 
            'dirstate-with-subtree')
 
1599
        return controldir.format_registry.make_controldir(
 
1600
            'development-subtree')
1511
1601
 
1512
1602
    _matchingbzrdir = property(__get_matchingbzrdir)
1513
1603
 
1518
1608
    This format:
1519
1609
        - exists within a metadir controlling .bzr
1520
1610
        - includes an explicit version marker for the workingtree control
1521
 
          files, separate from the BzrDir format
 
1611
          files, separate from the ControlDir format
1522
1612
        - modifies the hash cache format
1523
1613
        - is new in bzr 0.15
1524
1614
        - uses a LockDir to guard access to it.
1528
1618
 
1529
1619
    _tree_class = WorkingTree4
1530
1620
 
1531
 
    def get_format_string(self):
 
1621
    @classmethod
 
1622
    def get_format_string(cls):
1532
1623
        """See WorkingTreeFormat.get_format_string()."""
1533
1624
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1534
1625
 
1545
1636
 
1546
1637
    _tree_class = WorkingTree5
1547
1638
 
1548
 
    def get_format_string(self):
 
1639
    @classmethod
 
1640
    def get_format_string(cls):
1549
1641
        """See WorkingTreeFormat.get_format_string()."""
1550
1642
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1551
1643
 
1565
1657
 
1566
1658
    _tree_class = WorkingTree6
1567
1659
 
1568
 
    def get_format_string(self):
 
1660
    @classmethod
 
1661
    def get_format_string(cls):
1569
1662
        """See WorkingTreeFormat.get_format_string()."""
1570
1663
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1571
1664
 
1575
1668
 
1576
1669
    def _init_custom_control_files(self, wt):
1577
1670
        """Subclasses with custom control files should override this method."""
1578
 
        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())
1579
1673
 
1580
1674
    def supports_content_filtering(self):
1581
1675
        return True
1583
1677
    def supports_views(self):
1584
1678
        return True
1585
1679
 
1586
 
 
1587
 
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):
1588
1688
    """A revision tree pulling the inventory from a dirstate.
1589
1689
    
1590
1690
    Note that this is one of the historical (ie revision) trees cached in the
1609
1709
    def annotate_iter(self, file_id,
1610
1710
                      default_revision=_mod_revision.CURRENT_REVISION):
1611
1711
        """See Tree.annotate_iter"""
1612
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1712
        text_key = (file_id, self.get_file_revision(file_id))
1613
1713
        annotations = self._repository.texts.annotate(text_key)
1614
1714
        return [(key[-1], line) for (key, line) in annotations]
1615
1715
 
1616
 
    def _get_ancestors(self, default_revision):
1617
 
        return set(self._repository.get_ancestry(self._revision_id,
1618
 
                                                 topo_sorted=False))
1619
1716
    def _comparison_data(self, entry, path):
1620
1717
        """See Tree._comparison_data."""
1621
1718
        if entry is None:
1674
1771
        if path is not None:
1675
1772
            path = path.encode('utf8')
1676
1773
        parent_index = self._get_parent_index()
1677
 
        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)
1678
1776
 
1679
1777
    def _generate_inventory(self):
1680
1778
        """Create and set self.inventory from the dirstate object.
1699
1797
        # for the tree index use.
1700
1798
        root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1701
1799
        current_id = root_key[2]
1702
 
        if current_entry[parent_index][0] != 'd':
 
1800
        if current_entry[parent_index][0] != b'd':
1703
1801
            raise AssertionError()
1704
1802
        inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1705
1803
        inv.root.revision = current_entry[parent_index][4]
1720
1818
                continue
1721
1819
            for key, entry in block[1]:
1722
1820
                minikind, fingerprint, size, executable, revid = entry[parent_index]
1723
 
                if minikind in ('a', 'r'): # absent, relocated
 
1821
                if minikind in (b'a', b'r'): # absent, relocated
1724
1822
                    # not this tree
1725
1823
                    continue
1726
1824
                name = key[1]
1735
1833
                    inv_entry.text_size = size
1736
1834
                    inv_entry.text_sha1 = fingerprint
1737
1835
                elif kind == 'directory':
1738
 
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
 
1836
                    parent_ies[(dirname + b'/' + name).strip(b'/')] = inv_entry
1739
1837
                elif kind == 'symlink':
1740
 
                    inv_entry.executable = False
1741
 
                    inv_entry.text_size = None
1742
1838
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1743
1839
                elif kind == 'tree-reference':
1744
1840
                    inv_entry.reference_revision = fingerprint or None
1764
1860
        # Make sure the file exists
1765
1861
        entry = self._get_entry(file_id, path=path)
1766
1862
        if entry == (None, None): # do we raise?
1767
 
            return None
 
1863
            raise errors.NoSuchId(self, file_id)
1768
1864
        parent_index = self._get_parent_index()
1769
1865
        last_changed_revision = entry[1][parent_index][4]
1770
1866
        try:
1777
1873
        entry = self._get_entry(file_id=file_id, path=path)
1778
1874
        parent_index = self._get_parent_index()
1779
1875
        parent_details = entry[1][parent_index]
1780
 
        if parent_details[0] == 'f':
 
1876
        if parent_details[0] == b'f':
1781
1877
            return parent_details[1]
1782
1878
        return None
1783
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
 
1784
1885
    def get_file(self, file_id, path=None):
1785
 
        return StringIO(self.get_file_text(file_id))
 
1886
        return BytesIO(self.get_file_text(file_id))
1786
1887
 
1787
1888
    def get_file_size(self, file_id):
1788
1889
        """See Tree.get_file_size"""
1789
 
        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
1790
1892
 
1791
1893
    def get_file_text(self, file_id, path=None):
1792
 
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1793
 
        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
1794
1906
 
1795
1907
    def get_reference_revision(self, file_id, path=None):
1796
 
        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
1797
1910
 
1798
1911
    def iter_files_bytes(self, desired_files):
1799
1912
        """See Tree.iter_files_bytes.
1809
1922
                                       identifier))
1810
1923
        return self._repository.iter_files_bytes(repo_desired_files)
1811
1924
 
1812
 
    def get_symlink_target(self, file_id):
 
1925
    def get_symlink_target(self, file_id, path=None):
1813
1926
        entry = self._get_entry(file_id=file_id)
1814
1927
        parent_index = self._get_parent_index()
1815
 
        if entry[1][parent_index][0] != 'l':
 
1928
        if entry[1][parent_index][0] != b'l':
1816
1929
            return None
1817
1930
        else:
1818
1931
            target = entry[1][parent_index][1]
1823
1936
        """Return the revision id for this tree."""
1824
1937
        return self._revision_id
1825
1938
 
1826
 
    def _get_inventory(self):
 
1939
    def _get_root_inventory(self):
1827
1940
        if self._inventory is not None:
1828
1941
            return self._inventory
1829
1942
        self._must_be_locked()
1830
1943
        self._generate_inventory()
1831
1944
        return self._inventory
1832
1945
 
1833
 
    inventory = property(_get_inventory,
 
1946
    root_inventory = property(_get_root_inventory,
1834
1947
                         doc="Inventory of this Tree")
1835
1948
 
1836
1949
    def get_parent_ids(self):
1853
1966
 
1854
1967
    def path_content_summary(self, path):
1855
1968
        """See Tree.path_content_summary."""
1856
 
        id = self.inventory.path2id(path)
1857
 
        if id is None:
 
1969
        inv, inv_file_id = self._path2inv_file_id(path)
 
1970
        if inv_file_id is None:
1858
1971
            return ('missing', None, None, None)
1859
 
        entry = self._inventory[id]
 
1972
        entry = inv[inv_file_id]
1860
1973
        kind = entry.kind
1861
1974
        if kind == 'file':
1862
1975
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
1866
1979
            return (kind, None, None, None)
1867
1980
 
1868
1981
    def is_executable(self, file_id, path=None):
1869
 
        ie = self.inventory[file_id]
 
1982
        inv, inv_file_id = self._unpack_file_id(file_id)
 
1983
        ie = inv[inv_file_id]
1870
1984
        if ie.kind != "file":
1871
 
            return None
 
1985
            return False
1872
1986
        return ie.executable
1873
1987
 
1874
1988
    def is_locked(self):
1877
1991
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1878
1992
        # We use a standard implementation, because DirStateRevisionTree is
1879
1993
        # dealing with one of the parents of the current state
1880
 
        inv = self._get_inventory()
1881
1994
        if from_dir is None:
 
1995
            inv = self.root_inventory
1882
1996
            from_dir_id = None
1883
1997
        else:
1884
 
            from_dir_id = inv.path2id(from_dir)
 
1998
            inv, from_dir_id = self._path2inv_file_id(from_dir)
1885
1999
            if from_dir_id is None:
1886
2000
                # Directory not versioned
1887
2001
                return
 
2002
        # FIXME: Support nested trees
1888
2003
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1889
2004
        if inv.root is not None and not include_root and from_dir is None:
1890
 
            entries.next()
 
2005
            next(entries)
1891
2006
        for path, entry in entries:
1892
2007
            yield path, 'V', entry.kind, entry.file_id, entry
1893
2008
 
1894
2009
    def lock_read(self):
1895
2010
        """Lock the tree for a set of operations.
1896
2011
 
1897
 
        :return: A bzrlib.lock.LogicalLockResult.
 
2012
        :return: A breezy.lock.LogicalLockResult.
1898
2013
        """
1899
2014
        if not self._locked:
1900
2015
            self._repository.lock_read()
1912
2027
    def path2id(self, path):
1913
2028
        """Return the id for path in this tree."""
1914
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)
1915
2034
        entry = self._get_entry(path=path)
1916
2035
        if entry == (None, None):
1917
2036
            return None
1940
2059
        # So for now, we just build up the parent inventory, and extract
1941
2060
        # it the same way RevisionTree does.
1942
2061
        _directory = 'directory'
1943
 
        inv = self._get_inventory()
 
2062
        inv = self._get_root_inventory()
1944
2063
        top_id = inv.path2id(prefix)
1945
2064
        if top_id is None:
1946
2065
            pending = []
1981
2100
    def __init__(self, source, target):
1982
2101
        super(InterDirStateTree, self).__init__(source, target)
1983
2102
        if not InterDirStateTree.is_compatible(source, target):
1984
 
            raise Exception, "invalid source %r and target %r" % (source, target)
 
2103
            raise Exception("invalid source %r and target %r" % (source, target))
1985
2104
 
1986
2105
    @staticmethod
1987
2106
    def make_source_parent_tree(source, target):
1988
2107
        """Change the source tree into a parent of the target."""
1989
2108
        revid = source.commit('record tree')
1990
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2109
        target.branch.fetch(source.branch, revid)
1991
2110
        target.set_parent_ids([revid])
1992
2111
        return target.basis_tree(), target
1993
2112
 
2000
2119
    @classmethod
2001
2120
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2002
2121
                                                  target):
2003
 
        from bzrlib.tests.test__dirstate_helpers import \
 
2122
        from .tests.test__dirstate_helpers import \
2004
2123
            compiled_dirstate_helpers_feature
2005
2124
        test_case.requireFeature(compiled_dirstate_helpers_feature)
2006
 
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
 
2125
        from ._dirstate_helpers_pyx import ProcessEntryC
2007
2126
        result = klass.make_source_parent_tree(source, target)
2008
2127
        result[1]._iter_changes = ProcessEntryC
2009
2128
        return result
2072
2191
                specific_files_utf8.add(path.encode('utf8'))
2073
2192
            specific_files = specific_files_utf8
2074
2193
        else:
2075
 
            specific_files = set([''])
 
2194
            specific_files = {b''}
2076
2195
        # -- specific_files is now a utf8 path set --
2077
2196
 
2078
2197
        # -- get the state object and prepare it.
2085
2204
                path_entries = state._entries_for_path(path)
2086
2205
                if not path_entries:
2087
2206
                    # this specified path is not present at all: error
2088
 
                    not_versioned.append(path)
 
2207
                    not_versioned.append(path.decode('utf-8'))
2089
2208
                    continue
2090
2209
                found_versioned = False
2091
2210
                # for each id at this path
2092
2211
                for entry in path_entries:
2093
2212
                    # for each tree.
2094
2213
                    for index in indices:
2095
 
                        if entry[1][index][0] != 'a': # absent
 
2214
                        if entry[1][index][0] != b'a': # absent
2096
2215
                            found_versioned = True
2097
2216
                            # all good: found a versioned cell
2098
2217
                            break
2099
2218
                if not found_versioned:
2100
2219
                    # none of the indexes was not 'absent' at all ids for this
2101
2220
                    # path.
2102
 
                    not_versioned.append(path)
 
2221
                    not_versioned.append(path.decode('utf-8'))
2103
2222
            if len(not_versioned) > 0:
2104
2223
                raise errors.PathsNotVersionedError(not_versioned)
2105
2224
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2152
2271
 
2153
2272
    def create_dirstate_data(self, tree):
2154
2273
        """Create the dirstate based data for tree."""
2155
 
        local_path = tree.bzrdir.get_workingtree_transport(None
 
2274
        local_path = tree.controldir.get_workingtree_transport(None
2156
2275
            ).local_abspath('dirstate')
2157
2276
        state = dirstate.DirState.from_tree(tree, local_path)
2158
2277
        state.save()
2160
2279
 
2161
2280
    def remove_xml_files(self, tree):
2162
2281
        """Remove the oldformat 3 data."""
2163
 
        transport = tree.bzrdir.get_workingtree_transport(None)
 
2282
        transport = tree.controldir.get_workingtree_transport(None)
2164
2283
        for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2165
2284
            'pending-merges', 'stat-cache']:
2166
2285
            try:
2172
2291
    def update_format(self, tree):
2173
2292
        """Change the format marker."""
2174
2293
        tree._transport.put_bytes('format',
2175
 
            self.target_format.get_format_string(),
2176
 
            mode=tree.bzrdir._get_file_mode())
 
2294
            self.target_format.as_string(),
 
2295
            mode=tree.controldir._get_file_mode())
2177
2296
 
2178
2297
 
2179
2298
class Converter4to5(object):
2195
2314
    def update_format(self, tree):
2196
2315
        """Change the format marker."""
2197
2316
        tree._transport.put_bytes('format',
2198
 
            self.target_format.get_format_string(),
2199
 
            mode=tree.bzrdir._get_file_mode())
 
2317
            self.target_format.as_string(),
 
2318
            mode=tree.controldir._get_file_mode())
2200
2319
 
2201
2320
 
2202
2321
class Converter4or5to6(object):
2218
2337
 
2219
2338
    def init_custom_control_files(self, tree):
2220
2339
        """Initialize custom control files."""
2221
 
        tree._transport.put_bytes('views', '',
2222
 
            mode=tree.bzrdir._get_file_mode())
 
2340
        tree._transport.put_bytes('views', b'',
 
2341
            mode=tree.controldir._get_file_mode())
2223
2342
 
2224
2343
    def update_format(self, tree):
2225
2344
        """Change the format marker."""
2226
2345
        tree._transport.put_bytes('format',
2227
 
            self.target_format.get_format_string(),
2228
 
            mode=tree.bzrdir._get_file_mode())
 
2346
            self.target_format.as_string(),
 
2347
            mode=tree.controldir._get_file_mode())