/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/git/workingtree.py

  • Committer: Jelmer Vernooij
  • Date: 2019-03-05 07:32:38 UTC
  • mto: (7290.1.21 work)
  • mto: This revision was merged to the branch mainline in revision 7311.
  • Revision ID: jelmer@jelmer.uk-20190305073238-zlqn981opwnqsmzi
Add appveyor configuration.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
"""An adapter between a Git index and a Bazaar Working Tree"""
19
19
 
 
20
from __future__ import absolute_import
 
21
 
20
22
import itertools
21
23
from collections import defaultdict
22
24
import errno
23
25
from dulwich.ignore import (
24
26
    IgnoreFilterManager,
25
27
    )
26
 
from dulwich.config import ConfigFile as GitConfigFile
27
28
from dulwich.file import GitFile, FileLocked
28
29
from dulwich.index import (
29
30
    Index,
44
45
    )
45
46
import os
46
47
import posixpath
47
 
import re
48
48
import stat
49
49
import sys
50
50
 
51
51
from .. import (
52
 
    branch as _mod_branch,
53
52
    conflicts as _mod_conflicts,
54
53
    errors,
55
54
    controldir as _mod_controldir,
56
55
    globbing,
 
56
    ignores,
57
57
    lock,
 
58
    merge,
58
59
    osutils,
59
60
    revision as _mod_revision,
60
61
    trace,
61
62
    transport as _mod_transport,
62
63
    tree,
63
 
    urlutils,
64
64
    workingtree,
65
65
    )
66
66
from ..decorators import (
79
79
    MutableGitIndexTree,
80
80
    )
81
81
from .mapping import (
82
 
    encode_git_path,
83
 
    decode_git_path,
 
82
    GitFileIdMap,
84
83
    mode_kind,
85
84
    )
86
85
 
87
86
 
88
 
CONFLICT_SUFFIXES = ['.BASE', '.OTHER', '.THIS']
89
 
 
90
 
 
91
 
# TODO: There should be a base revid attribute to better inform the user about
92
 
# how the conflicts were generated.
93
 
class TextConflict(_mod_conflicts.Conflict):
94
 
    """The merge algorithm could not resolve all differences encountered."""
95
 
 
96
 
    has_files = True
97
 
 
98
 
    typestring = 'text conflict'
99
 
 
100
 
    _conflict_re = re.compile(b'^(<{7}|={7}|>{7})')
101
 
 
102
 
    def associated_filenames(self):
103
 
        return [self.path + suffix for suffix in CONFLICT_SUFFIXES]
104
 
 
105
 
    def _resolve(self, tt, winner_suffix):
106
 
        """Resolve the conflict by copying one of .THIS or .OTHER into file.
107
 
 
108
 
        :param tt: The TreeTransform where the conflict is resolved.
109
 
        :param winner_suffix: Either 'THIS' or 'OTHER'
110
 
 
111
 
        The resolution is symmetric, when taking THIS, item.THIS is renamed
112
 
        into item and vice-versa. This takes one of the files as a whole
113
 
        ignoring every difference that could have been merged cleanly.
114
 
        """
115
 
        # To avoid useless copies, we switch item and item.winner_suffix, only
116
 
        # item will exist after the conflict has been resolved anyway.
117
 
        item_tid = tt.trans_id_tree_path(self.path)
118
 
        item_parent_tid = tt.get_tree_parent(item_tid)
119
 
        winner_path = self.path + '.' + winner_suffix
120
 
        winner_tid = tt.trans_id_tree_path(winner_path)
121
 
        winner_parent_tid = tt.get_tree_parent(winner_tid)
122
 
        # Switch the paths to preserve the content
123
 
        tt.adjust_path(osutils.basename(self.path),
124
 
                       winner_parent_tid, winner_tid)
125
 
        tt.adjust_path(osutils.basename(winner_path),
126
 
                       item_parent_tid, item_tid)
127
 
        tt.unversion_file(item_tid)
128
 
        tt.version_file(winner_tid)
129
 
        tt.apply()
130
 
 
131
 
    def action_auto(self, tree):
132
 
        # GZ 2012-07-27: Using NotImplementedError to signal that a conflict
133
 
        #                can't be auto resolved does not seem ideal.
134
 
        try:
135
 
            kind = tree.kind(self.path)
136
 
        except errors.NoSuchFile:
137
 
            return
138
 
        if kind != 'file':
139
 
            raise NotImplementedError("Conflict is not a file")
140
 
        conflict_markers_in_line = self._conflict_re.search
141
 
        with tree.get_file(self.path) as f:
142
 
            for line in f:
143
 
                if conflict_markers_in_line(line):
144
 
                    raise NotImplementedError("Conflict markers present")
145
 
 
146
 
    def _resolve_with_cleanups(self, tree, *args, **kwargs):
147
 
        with tree.transform() as tt:
148
 
            self._resolve(tt, *args, **kwargs)
149
 
 
150
 
    def action_take_this(self, tree):
151
 
        self._resolve_with_cleanups(tree, 'THIS')
152
 
 
153
 
    def action_take_other(self, tree):
154
 
        self._resolve_with_cleanups(tree, 'OTHER')
155
 
 
156
 
    def do(self, action, tree):
157
 
        """Apply the specified action to the conflict.
158
 
 
159
 
        :param action: The method name to call.
160
 
 
161
 
        :param tree: The tree passed as a parameter to the method.
162
 
        """
163
 
        meth = getattr(self, 'action_%s' % action, None)
164
 
        if meth is None:
165
 
            raise NotImplementedError(self.__class__.__name__ + '.' + action)
166
 
        meth(tree)
167
 
 
168
 
    def action_done(self, tree):
169
 
        """Mark the conflict as solved once it has been handled."""
170
 
        # This method does nothing but simplifies the design of upper levels.
171
 
        pass
172
 
 
173
 
    def describe(self):
174
 
        return 'Text conflict in %(path)s' % self.__dict__
175
 
 
176
 
    def __str__(self):
177
 
        return self.describe()
178
 
 
179
 
    def __repr__(self):
180
 
        return "%s(%r)" % (type(self).__name__, self.path)
181
 
 
182
 
 
183
87
class GitWorkingTree(MutableGitIndexTree, workingtree.WorkingTree):
184
88
    """A Git working tree."""
185
89
 
211
115
        self.index = Index(self.control_transport.local_abspath('index'))
212
116
        self._index_dirty = False
213
117
 
214
 
    def _get_submodule_index(self, relpath):
215
 
        if not isinstance(relpath, bytes):
216
 
            raise TypeError(relpath)
217
 
        try:
218
 
            info = self._submodule_info()[relpath]
219
 
        except KeyError:
220
 
            index_path = os.path.join(self.basedir, decode_git_path(relpath), '.git', 'index')
221
 
        else:
222
 
            index_path = self.control_transport.local_abspath(
223
 
                posixpath.join('modules', decode_git_path(info[1]), 'index'))
224
 
        return Index(index_path)
225
 
 
226
118
    def lock_read(self):
227
119
        """Lock the repository for read operations.
228
120
 
426
318
        def recurse_directory_to_add_files(directory):
427
319
            # Recurse directory and add all files
428
320
            # so we can check if they have changed.
429
 
            for parent_path, file_infos in self.walkdirs(directory):
430
 
                for relpath, basename, kind, lstat, kind in file_infos:
 
321
            for parent_info, file_infos in self.walkdirs(directory):
 
322
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
431
323
                    # Is it versioned or ignored?
432
324
                    if self.is_versioned(relpath):
433
325
                        # Add nested content for deletion.
458
350
 
459
351
            # Bail out if we are going to delete files we shouldn't
460
352
            if not keep_files and not force:
461
 
                for change in self.iter_changes(
462
 
                        self.basis_tree(), include_unchanged=True,
463
 
                        require_versioned=False, want_unversioned=True,
464
 
                        specific_files=files):
465
 
                    if change.versioned[0] is False:
 
353
                for (file_id, path, content_change, versioned, parent_id, name,
 
354
                     kind, executable) in self.iter_changes(
 
355
                         self.basis_tree(), include_unchanged=True,
 
356
                         require_versioned=False, want_unversioned=True,
 
357
                         specific_files=files):
 
358
                    if versioned[0] is False:
466
359
                        # The record is unknown or newly added
467
 
                        files_to_backup.append(change.path[1])
 
360
                        files_to_backup.append(path[1])
468
361
                        files_to_backup.extend(
469
 
                            osutils.parent_directories(change.path[1]))
470
 
                    elif (change.changed_content and (change.kind[1] is not None)
471
 
                            and osutils.is_inside_any(files, change.path[1])):
 
362
                            osutils.parent_directories(path[1]))
 
363
                    elif (content_change and (kind[1] is not None)
 
364
                            and osutils.is_inside_any(files, path[1])):
472
365
                        # Versioned and changed, but not deleted, and still
473
366
                        # in one of the dirs to be deleted.
474
 
                        files_to_backup.append(change.path[1])
 
367
                        files_to_backup.append(path[1])
475
368
                        files_to_backup.extend(
476
 
                            osutils.parent_directories(change.path[1]))
 
369
                            osutils.parent_directories(path[1]))
477
370
 
478
371
            for f in files:
479
372
                if f == '':
521
414
        # expand any symlinks in the directory part, while leaving the
522
415
        # filename alone
523
416
        # only expanding if symlinks are supported avoids windows path bugs
524
 
        if self.supports_symlinks():
 
417
        if osutils.has_symlinks():
525
418
            file_list = list(map(osutils.normalizepath, file_list))
526
419
 
527
420
        conflicts_related = set()
554
447
                kind = osutils.file_kind(abspath)
555
448
                if kind in ("file", "symlink"):
556
449
                    (index, subpath) = self._lookup_index(
557
 
                        encode_git_path(filepath))
 
450
                        filepath.encode('utf-8'))
558
451
                    if subpath in index:
559
452
                        # Already present
560
453
                        continue
564
457
                    added.append(filepath)
565
458
                elif kind == "directory":
566
459
                    (index, subpath) = self._lookup_index(
567
 
                        encode_git_path(filepath))
 
460
                        filepath.encode('utf-8'))
568
461
                    if subpath not in index:
569
462
                        call_action(filepath, kind)
570
463
                    if recurse:
604
497
                        user_dirs.append(subp)
605
498
                    else:
606
499
                        (index, subpath) = self._lookup_index(
607
 
                            encode_git_path(subp))
 
500
                            subp.encode('utf-8'))
608
501
                        if subpath in index:
609
502
                            # Already present
610
503
                            continue
619
512
    def has_filename(self, filename):
620
513
        return osutils.lexists(self.abspath(filename))
621
514
 
622
 
    def _iter_files_recursive(self, from_dir=None, include_dirs=False,
623
 
                              recurse_nested=False):
 
515
    def _iter_files_recursive(self, from_dir=None, include_dirs=False):
624
516
        if from_dir is None:
625
517
            from_dir = u""
626
 
        if not isinstance(from_dir, str):
627
 
            raise TypeError(from_dir)
628
518
        encoded_from_dir = self.abspath(from_dir).encode(osutils._fs_enc)
629
519
        for (dirpath, dirnames, filenames) in os.walk(encoded_from_dir):
630
520
            dir_relpath = dirpath[len(self.basedir):].strip(b"/")
637
527
                    dirnames.remove(name)
638
528
                    continue
639
529
                relpath = os.path.join(dir_relpath, name)
640
 
                if not recurse_nested and self._directory_is_tree_reference(relpath.decode(osutils._fs_enc)):
641
 
                    dirnames.remove(name)
642
530
                if include_dirs:
643
531
                    try:
644
532
                        yield relpath.decode(osutils._fs_enc)
645
533
                    except UnicodeDecodeError:
646
534
                        raise errors.BadFilenameEncoding(
647
535
                            relpath, osutils._fs_enc)
648
 
                    if not self.is_versioned(relpath.decode(osutils._fs_enc)):
 
536
                    if not self._has_dir(relpath):
649
537
                        dirnames.remove(name)
650
538
            for name in filenames:
651
539
                if self.mapping.is_special_file(name):
665
553
        """
666
554
        with self.lock_read():
667
555
            index_paths = set(
668
 
                [decode_git_path(p) for p, sha, mode in self.iter_git_objects()])
 
556
                [p.decode('utf-8') for p, i in self._recurse_index_entries()])
669
557
            all_paths = set(self._iter_files_recursive(include_dirs=False))
670
558
            return iter(all_paths - index_paths)
671
559
 
680
568
                    except OSError as e:
681
569
                        if e.errno == errno.ENOENT:
682
570
                            raise errors.NoSuchFile(fullpath)
683
 
                    if f != '' and self._directory_is_tree_reference(f):
 
571
                    if (kind == 'directory' and f != '' and
 
572
                            os.path.exists(os.path.join(fullpath, '.git'))):
684
573
                        kind = 'tree-reference'
685
574
                    kinds[pos] = kind
686
575
 
704
593
            raise
705
594
        self._index_dirty = False
706
595
 
 
596
    def has_or_had_id(self, file_id):
 
597
        if self.has_id(file_id):
 
598
            return True
 
599
        if self.had_id(file_id):
 
600
            return True
 
601
        return False
 
602
 
 
603
    def had_id(self, file_id):
 
604
        path = self._basis_fileid_map.lookup_path(file_id)
 
605
        try:
 
606
            head = self.repository._git.head()
 
607
        except KeyError:
 
608
            # Assume no if basis is not accessible
 
609
            return False
 
610
        try:
 
611
            root_tree = self.store[head].tree
 
612
        except KeyError:
 
613
            return False
 
614
        try:
 
615
            tree_lookup_path(self.store.__getitem__,
 
616
                             root_tree, path.encode('utf-8'))
 
617
        except KeyError:
 
618
            return False
 
619
        else:
 
620
            return True
 
621
 
707
622
    def get_file_mtime(self, path):
708
623
        """See Tree.get_file_mtime."""
709
624
        try:
720
635
        be ignored, otherwise None.  So this can simply be used as a
721
636
        boolean if desired."""
722
637
        if getattr(self, '_global_ignoreglobster', None) is None:
723
 
            from breezy import ignores
724
638
            ignore_globs = set()
725
639
            ignore_globs.update(ignores.get_runtime_ignores())
726
640
            ignore_globs.update(ignores.get_user_ignores())
766
680
            raise errors.GhostRevisionUnusableHere(revid)
767
681
 
768
682
    def _reset_data(self):
769
 
        pass
 
683
        try:
 
684
            head = self.repository._git.head()
 
685
        except KeyError:
 
686
            self._basis_fileid_map = GitFileIdMap({}, self.mapping)
 
687
        else:
 
688
            self._basis_fileid_map = self.mapping.get_fileid_map(
 
689
                self.store.__getitem__, self.store[head].tree)
 
690
        self._fileid_map = self._basis_fileid_map.copy()
770
691
 
771
692
    def get_file_verifier(self, path, stat_value=None):
772
693
        with self.lock_read():
773
 
            (index, subpath) = self._lookup_index(encode_git_path(path))
 
694
            (index, subpath) = self._lookup_index(path.encode('utf-8'))
774
695
            try:
775
696
                return ("GIT", index[subpath].sha)
776
697
            except KeyError:
802
723
 
803
724
    def stored_kind(self, path):
804
725
        with self.lock_read():
805
 
            encoded_path = encode_git_path(path)
 
726
            encoded_path = path.encode('utf-8')
806
727
            (index, subpath) = self._lookup_index(encoded_path)
807
728
            try:
808
729
                return mode_kind(index[subpath].mode)
816
737
        return os.lstat(self.abspath(path))
817
738
 
818
739
    def _live_entry(self, path):
819
 
        encoded_path = self.abspath(decode_git_path(path)).encode(
 
740
        encoded_path = self.abspath(path.decode('utf-8')).encode(
820
741
            osutils._fs_enc)
821
742
        return index_entry_from_path(encoded_path)
822
743
 
823
744
    def is_executable(self, path):
824
745
        with self.lock_read():
825
 
            if self._supports_executable():
 
746
            if getattr(self, "_supports_executable",
 
747
                       osutils.supports_executable)():
826
748
                mode = self._lstat(path).st_mode
827
749
            else:
828
 
                (index, subpath) = self._lookup_index(encode_git_path(path))
 
750
                (index, subpath) = self._lookup_index(path.encode('utf-8'))
829
751
                try:
830
752
                    mode = index[subpath].mode
831
753
                except KeyError:
833
755
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
834
756
 
835
757
    def _is_executable_from_path_and_stat(self, path, stat_result):
836
 
        if self._supports_executable():
837
 
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
 
758
        if getattr(self, "_supports_executable",
 
759
                   osutils.supports_executable)():
 
760
            return self._is_executable_from_path_and_stat_from_stat(
 
761
                path, stat_result)
838
762
        else:
839
763
            return self._is_executable_from_path_and_stat_from_basis(
840
764
                path, stat_result)
841
765
 
842
 
    def list_files(self, include_root=False, from_dir=None, recursive=True,
843
 
                   recurse_nested=False):
 
766
    def list_files(self, include_root=False, from_dir=None, recursive=True):
844
767
        if from_dir is None or from_dir == '.':
845
768
            from_dir = u""
846
769
        dir_ids = {}
855
778
            dir_ids[u""] = root_ie.file_id
856
779
            if recursive:
857
780
                path_iterator = sorted(
858
 
                    self._iter_files_recursive(
859
 
                        from_dir, include_dirs=True,
860
 
                        recurse_nested=recurse_nested))
 
781
                    self._iter_files_recursive(from_dir, include_dirs=True))
861
782
            else:
862
783
                encoded_from_dir = self.abspath(from_dir).encode(
863
784
                    osutils._fs_enc)
870
791
                         name.decode(osutils._fs_enc))])
871
792
            for path in path_iterator:
872
793
                try:
873
 
                    encoded_path = encode_git_path(path)
 
794
                    encoded_path = path.encode("utf-8")
874
795
                except UnicodeEncodeError:
875
796
                    raise errors.BadFilenameEncoding(
876
797
                        path, osutils._fs_enc)
884
805
                for dir_path, dir_ie in self._add_missing_parent_ids(
885
806
                        parent, dir_ids):
886
807
                    pass
887
 
                if kind == 'tree-reference' and recurse_nested:
888
 
                    ie = self._get_dir_ie(path, self.path2id(path))
889
 
                    yield (posixpath.relpath(path, from_dir), 'V', 'directory',
890
 
                           ie)
891
 
                    continue
892
 
                if kind == 'directory':
 
808
                if kind in ('directory', 'tree-reference'):
893
809
                    if path != from_dir:
894
810
                        if self._has_dir(encoded_path):
895
811
                            ie = self._get_dir_ie(path, self.path2id(path))
907
823
                    ie = self._get_file_ie(name, path, value, dir_ids[parent])
908
824
                    yield (posixpath.relpath(path, from_dir), "V", ie.kind, ie)
909
825
                else:
910
 
                    try:
911
 
                        ie = fk_entries[kind]()
912
 
                    except KeyError:
913
 
                        # unsupported kind
914
 
                        continue
 
826
                    ie = fk_entries[kind]()
915
827
                    yield (posixpath.relpath(path, from_dir),
916
828
                           ("I" if self.is_ignored(path) else "?"), kind, ie)
917
829
 
924
836
            for path in self.index:
925
837
                if self.mapping.is_special_file(path):
926
838
                    continue
927
 
                path = decode_git_path(path)
 
839
                path = path.decode("utf-8")
928
840
                paths.add(path)
929
841
                while path != "":
930
842
                    path = posixpath.dirname(path).strip("/")
934
846
            return paths
935
847
 
936
848
    def iter_child_entries(self, path):
937
 
        encoded_path = encode_git_path(path)
 
849
        encoded_path = path.encode('utf-8')
938
850
        with self.lock_read():
939
851
            parent_id = self.path2id(path)
940
852
            found_any = False
941
853
            for item_path, value in self.index.iteritems():
942
 
                decoded_item_path = decode_git_path(item_path)
 
854
                decoded_item_path = item_path.decode('utf-8')
943
855
                if self.mapping.is_special_file(item_path):
944
856
                    continue
945
857
                if not osutils.is_inside(path, decoded_item_path):
963
875
            conflicts = _mod_conflicts.ConflictList()
964
876
            for item_path, value in self.index.iteritems():
965
877
                if value.flags & FLAG_STAGEMASK:
966
 
                    conflicts.append(TextConflict(decode_git_path(item_path)))
 
878
                    conflicts.append(_mod_conflicts.TextConflict(
 
879
                        item_path.decode('utf-8')))
967
880
            return conflicts
968
881
 
969
882
    def set_conflicts(self, conflicts):
970
883
        by_path = set()
971
884
        for conflict in conflicts:
972
885
            if conflict.typestring in ('text conflict', 'contents conflict'):
973
 
                by_path.add(encode_git_path(conflict.path))
 
886
                by_path.add(conflict.path.encode('utf-8'))
974
887
            else:
975
888
                raise errors.UnsupportedOperation(self.set_conflicts, self)
976
889
        with self.lock_tree_write():
978
891
                self._set_conflicted(path, path in by_path)
979
892
 
980
893
    def _set_conflicted(self, path, conflicted):
 
894
        trace.mutter('change conflict: %r -> %r', path, conflicted)
981
895
        value = self.index[path]
982
896
        self._index_dirty = True
983
897
        if conflicted:
992
906
                                           'contents conflict'):
993
907
                    try:
994
908
                        self._set_conflicted(
995
 
                            encode_git_path(conflict.path), True)
 
909
                            conflict.path.encode('utf-8'), True)
996
910
                    except KeyError:
997
911
                        raise errors.UnsupportedOperation(
998
912
                            self.add_conflicts, self)
1003
917
        """Walk the directories of this tree.
1004
918
 
1005
919
        returns a generator which yields items in the form:
1006
 
                (current_directory_path,
1007
 
                 [(file1_path, file1_name, file1_kind, (lstat),
 
920
                ((curren_directory_path, fileid),
 
921
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
1008
922
                   file1_kind), ... ])
1009
923
 
1010
924
        This API returns a generator, which is only valid during the current
1068
982
                             - (current_inv[0][0] < cur_disk_dir_relpath))
1069
983
            if direction > 0:
1070
984
                # disk is before inventory - unknown
1071
 
                dirblock = [(relpath, basename, kind, stat, None) for
 
985
                dirblock = [(relpath, basename, kind, stat, None, None) for
1072
986
                            relpath, basename, kind, stat, top_path in
1073
987
                            cur_disk_dir_content]
1074
 
                yield cur_disk_dir_relpath, dirblock
 
988
                yield (cur_disk_dir_relpath, None), dirblock
1075
989
                try:
1076
990
                    current_disk = next(disk_iterator)
1077
991
                except StopIteration:
1078
992
                    disk_finished = True
1079
993
            elif direction < 0:
1080
994
                # inventory is before disk - missing.
1081
 
                dirblock = [(relpath, basename, 'unknown', None, kind)
 
995
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1082
996
                            for relpath, basename, dkind, stat, fileid, kind in
1083
997
                            current_inv[1]]
1084
 
                yield current_inv[0][0], dirblock
 
998
                yield (current_inv[0][0], current_inv[0][1]), dirblock
1085
999
                try:
1086
1000
                    current_inv = next(inventory_iterator)
1087
1001
                except StopIteration:
1099
1013
                        # versioned, present file
1100
1014
                        dirblock.append((inv_row[0],
1101
1015
                                         inv_row[1], disk_row[2],
1102
 
                                         disk_row[3], inv_row[5]))
 
1016
                                         disk_row[3], inv_row[4],
 
1017
                                         inv_row[5]))
1103
1018
                    elif len(path_elements[0]) == 5:
1104
1019
                        # unknown disk file
1105
1020
                        dirblock.append(
1106
1021
                            (path_elements[0][0], path_elements[0][1],
1107
1022
                                path_elements[0][2], path_elements[0][3],
1108
 
                                None))
 
1023
                                None, None))
1109
1024
                    elif len(path_elements[0]) == 6:
1110
1025
                        # versioned, absent file.
1111
1026
                        dirblock.append(
1112
1027
                            (path_elements[0][0], path_elements[0][1],
1113
 
                                'unknown', None,
 
1028
                                'unknown', None, path_elements[0][4],
1114
1029
                                path_elements[0][5]))
1115
1030
                    else:
1116
1031
                        raise NotImplementedError('unreachable code')
1117
 
                yield current_inv[0][0], dirblock
 
1032
                yield current_inv[0], dirblock
1118
1033
                try:
1119
1034
                    current_inv = next(inventory_iterator)
1120
1035
                except StopIteration:
1127
1042
    def _walkdirs(self, prefix=u""):
1128
1043
        if prefix != u"":
1129
1044
            prefix += u"/"
1130
 
        prefix = encode_git_path(prefix)
 
1045
        prefix = prefix.encode('utf-8')
1131
1046
        per_dir = defaultdict(set)
1132
1047
        if prefix == b"":
1133
 
            per_dir[(u'', self.path2id(''))] = set()
 
1048
            per_dir[(u'', self.get_root_id())] = set()
1134
1049
 
1135
1050
        def add_entry(path, kind):
1136
1051
            if path == b'' or not path.startswith(prefix):
1137
1052
                return
1138
1053
            (dirname, child_name) = posixpath.split(path)
1139
1054
            add_entry(dirname, 'directory')
1140
 
            dirname = decode_git_path(dirname)
 
1055
            dirname = dirname.decode("utf-8")
1141
1056
            dir_file_id = self.path2id(dirname)
1142
1057
            if not isinstance(value, tuple) or len(value) != 10:
1143
1058
                raise ValueError(value)
1144
1059
            per_dir[(dirname, dir_file_id)].add(
1145
 
                (decode_git_path(path), decode_git_path(child_name),
 
1060
                (path.decode("utf-8"), child_name.decode("utf-8"),
1146
1061
                 kind, None,
1147
 
                 self.path2id(decode_git_path(path)),
 
1062
                 self.path2id(path.decode("utf-8")),
1148
1063
                 kind))
1149
1064
        with self.lock_read():
1150
1065
            for path, value in self.index.iteritems():
1161
1076
    def store_uncommitted(self):
1162
1077
        raise errors.StoringUncommittedNotSupported(self)
1163
1078
 
 
1079
    def apply_inventory_delta(self, changes):
 
1080
        for (old_path, new_path, file_id, ie) in changes:
 
1081
            if old_path is not None:
 
1082
                (index, old_subpath) = self._lookup_index(
 
1083
                    old_path.encode('utf-8'))
 
1084
                try:
 
1085
                    self._index_del_entry(index, old_subpath)
 
1086
                except KeyError:
 
1087
                    pass
 
1088
                else:
 
1089
                    self._versioned_dirs = None
 
1090
            if new_path is not None and ie.kind != 'directory':
 
1091
                if ie.kind == 'tree-reference':
 
1092
                    self._index_add_entry(
 
1093
                        new_path, ie.kind,
 
1094
                        reference_revision=ie.reference_revision)
 
1095
                else:
 
1096
                    self._index_add_entry(new_path, ie.kind)
 
1097
        self.flush()
 
1098
 
1164
1099
    def annotate_iter(self, path,
1165
1100
                      default_revision=_mod_revision.CURRENT_REVISION):
1166
1101
        """See Tree.annotate_iter
1231
1166
            self.store,
1232
1167
            None
1233
1168
            if self.branch.head is None
1234
 
            else self.store[self.branch.head].tree,
1235
 
            honor_filemode=self._supports_executable())
 
1169
            else self.store[self.branch.head].tree)
1236
1170
 
1237
1171
    def reset_state(self, revision_ids=None):
1238
1172
        """Reset the state of the working tree.
1257
1191
                        # Let's at least try to use the working tree file:
1258
1192
                        try:
1259
1193
                            st = self._lstat(self.abspath(
1260
 
                                decode_git_path(entry.path)))
 
1194
                                entry.path.decode('utf-8')))
1261
1195
                        except OSError:
1262
1196
                            # But if it doesn't exist, we'll make something up.
1263
1197
                            obj = self.store[entry.sha]
1268
1202
                    (index, subpath) = self._lookup_index(entry.path)
1269
1203
                    index[subpath] = index_entry_from_stat(st, entry.sha, 0)
1270
1204
 
1271
 
    def _update_git_tree(
1272
 
            self, old_revision, new_revision, change_reporter=None,
1273
 
            show_base=False):
1274
 
        basis_tree = self.revision_tree(old_revision)
1275
 
        if new_revision != old_revision:
1276
 
            from .. import merge
1277
 
            with basis_tree.lock_read():
1278
 
                new_basis_tree = self.branch.basis_tree()
1279
 
                merge.merge_inner(
1280
 
                    self.branch,
1281
 
                    new_basis_tree,
1282
 
                    basis_tree,
1283
 
                    this_tree=self,
1284
 
                    change_reporter=change_reporter,
1285
 
                    show_base=show_base)
1286
 
 
1287
1205
    def pull(self, source, overwrite=False, stop_revision=None,
1288
1206
             change_reporter=None, possible_transports=None, local=False,
1289
 
             show_base=False, tag_selector=None):
 
1207
             show_base=False):
1290
1208
        with self.lock_write(), source.lock_read():
1291
1209
            old_revision = self.branch.last_revision()
 
1210
            basis_tree = self.basis_tree()
1292
1211
            count = self.branch.pull(source, overwrite, stop_revision,
1293
1212
                                     possible_transports=possible_transports,
1294
 
                                     local=local, tag_selector=tag_selector)
1295
 
            self._update_git_tree(
1296
 
                old_revision=old_revision,
1297
 
                new_revision=self.branch.last_revision(),
1298
 
                change_reporter=change_reporter,
1299
 
                show_base=show_base)
 
1213
                                     local=local)
 
1214
            new_revision = self.branch.last_revision()
 
1215
            if new_revision != old_revision:
 
1216
                with basis_tree.lock_read():
 
1217
                    new_basis_tree = self.branch.basis_tree()
 
1218
                    merge.merge_inner(
 
1219
                        self.branch,
 
1220
                        new_basis_tree,
 
1221
                        basis_tree,
 
1222
                        this_tree=self,
 
1223
                        change_reporter=change_reporter,
 
1224
                        show_base=show_base)
1300
1225
            return count
1301
1226
 
1302
1227
    def add_reference(self, sub_tree):
1316
1241
    def _read_submodule_head(self, path):
1317
1242
        return read_submodule_head(self.abspath(path))
1318
1243
 
1319
 
    def get_reference_revision(self, path, branch=None):
 
1244
    def get_reference_revision(self, path):
1320
1245
        hexsha = self._read_submodule_head(path)
1321
1246
        if hexsha is None:
1322
 
            (index, subpath) = self._lookup_index(
1323
 
                encode_git_path(path))
1324
 
            if subpath is None:
1325
 
                raise errors.NoSuchFile(path)
1326
 
            hexsha = index[subpath].sha
 
1247
            return _mod_revision.NULL_REVISION
1327
1248
        return self.branch.lookup_foreign_revision_id(hexsha)
1328
1249
 
1329
1250
    def get_nested_tree(self, path):
1386
1307
 
1387
1308
    def copy_content_into(self, tree, revision_id=None):
1388
1309
        """Copy the current content and user files of this tree into tree."""
1389
 
        from .. import merge
1390
1310
        with self.lock_read():
1391
1311
            if revision_id is None:
1392
1312
                merge.transform_tree(tree, self)
1406
1326
                    new_parents = [revision_id]
1407
1327
                tree.set_parent_ids(new_parents)
1408
1328
 
1409
 
    def reference_parent(self, path, possible_transports=None):
1410
 
        remote_url = self.get_reference_info(path)
1411
 
        if remote_url is None:
1412
 
            trace.warning("Unable to find submodule info for %s", path)
1413
 
            return None
1414
 
        return _mod_branch.Branch.open(remote_url, possible_transports=possible_transports)
1415
 
 
1416
 
    def get_reference_info(self, path):
1417
 
        submodule_info = self._submodule_info()
1418
 
        info = submodule_info.get(encode_git_path(path))
1419
 
        if info is None:
1420
 
            return None
1421
 
        return decode_git_path(info[0])
1422
 
 
1423
 
    def set_reference_info(self, tree_path, branch_location):
1424
 
        path = self.abspath('.gitmodules')
1425
 
        try:
1426
 
            config = GitConfigFile.from_path(path)
1427
 
        except EnvironmentError as e:
1428
 
            if e.errno == errno.ENOENT:
1429
 
                config = GitConfigFile()
1430
 
            else:
1431
 
                raise
1432
 
        section = (b'submodule', encode_git_path(tree_path))
1433
 
        if branch_location is None:
1434
 
            try:
1435
 
                del config[section]
1436
 
            except KeyError:
1437
 
                pass
1438
 
        else:
1439
 
            branch_location = urlutils.join(
1440
 
                urlutils.strip_segment_parameters(self.branch.user_url),
1441
 
                branch_location)
1442
 
            config.set(
1443
 
                section,
1444
 
                b'path', encode_git_path(tree_path))
1445
 
            config.set(
1446
 
                section,
1447
 
                b'url', branch_location.encode('utf-8'))
1448
 
        config.write_to_path(path)
1449
 
        self.add('.gitmodules')
1450
 
 
1451
1329
 
1452
1330
class GitWorkingTreeFormat(workingtree.WorkingTreeFormat):
1453
1331