/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: 2020-05-24 00:39:50 UTC
  • mto: This revision was merged to the branch mainline in revision 7504.
  • Revision ID: jelmer@jelmer.uk-20200524003950-bbc545r76vc5yajg
Add github action.

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
    )
45
45
import os
46
46
import posixpath
47
 
import re
48
47
import stat
49
48
import sys
50
49
 
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,
79
80
    MutableGitIndexTree,
80
81
    )
81
82
from .mapping import (
82
 
    encode_git_path,
83
 
    decode_git_path,
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
 
217
121
        try:
218
122
            info = self._submodule_info()[relpath]
219
123
        except KeyError:
220
 
            index_path = os.path.join(self.basedir, decode_git_path(relpath), '.git', 'index')
 
124
            index_path = os.path.join(self.basedir, relpath.decode('utf-8'), '.git', 'index')
221
125
        else:
222
126
            index_path = self.control_transport.local_abspath(
223
 
                posixpath.join('modules', decode_git_path(info[1]), 'index'))
 
127
                posixpath.join('modules', info[1].decode('utf-8'), 'index'))
224
128
        return Index(index_path)
225
129
 
226
130
    def lock_read(self):
317
221
        else:
318
222
            self.case_sensitive = False
319
223
 
 
224
    def get_transform(self, pb=None):
 
225
        from ..transform import TreeTransform
 
226
        return TreeTransform(self, pb=pb)
 
227
 
320
228
    def merge_modified(self):
321
229
        return {}
322
230
 
426
334
        def recurse_directory_to_add_files(directory):
427
335
            # Recurse directory and add all files
428
336
            # 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:
 
337
            for parent_info, file_infos in self.walkdirs(directory):
 
338
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
431
339
                    # Is it versioned or ignored?
432
340
                    if self.is_versioned(relpath):
433
341
                        # Add nested content for deletion.
554
462
                kind = osutils.file_kind(abspath)
555
463
                if kind in ("file", "symlink"):
556
464
                    (index, subpath) = self._lookup_index(
557
 
                        encode_git_path(filepath))
 
465
                        filepath.encode('utf-8'))
558
466
                    if subpath in index:
559
467
                        # Already present
560
468
                        continue
564
472
                    added.append(filepath)
565
473
                elif kind == "directory":
566
474
                    (index, subpath) = self._lookup_index(
567
 
                        encode_git_path(filepath))
 
475
                        filepath.encode('utf-8'))
568
476
                    if subpath not in index:
569
477
                        call_action(filepath, kind)
570
478
                    if recurse:
604
512
                        user_dirs.append(subp)
605
513
                    else:
606
514
                        (index, subpath) = self._lookup_index(
607
 
                            encode_git_path(subp))
 
515
                            subp.encode('utf-8'))
608
516
                        if subpath in index:
609
517
                            # Already present
610
518
                            continue
665
573
        """
666
574
        with self.lock_read():
667
575
            index_paths = set(
668
 
                [decode_git_path(p) for p, sha, mode in self.iter_git_objects()])
 
576
                [p.decode('utf-8') for p, i in self._recurse_index_entries()])
669
577
            all_paths = set(self._iter_files_recursive(include_dirs=False))
670
578
            return iter(all_paths - index_paths)
671
579
 
720
628
        be ignored, otherwise None.  So this can simply be used as a
721
629
        boolean if desired."""
722
630
        if getattr(self, '_global_ignoreglobster', None) is None:
723
 
            from breezy import ignores
724
631
            ignore_globs = set()
725
632
            ignore_globs.update(ignores.get_runtime_ignores())
726
633
            ignore_globs.update(ignores.get_user_ignores())
770
677
 
771
678
    def get_file_verifier(self, path, stat_value=None):
772
679
        with self.lock_read():
773
 
            (index, subpath) = self._lookup_index(encode_git_path(path))
 
680
            (index, subpath) = self._lookup_index(path.encode('utf-8'))
774
681
            try:
775
682
                return ("GIT", index[subpath].sha)
776
683
            except KeyError:
802
709
 
803
710
    def stored_kind(self, path):
804
711
        with self.lock_read():
805
 
            encoded_path = encode_git_path(path)
 
712
            encoded_path = path.encode('utf-8')
806
713
            (index, subpath) = self._lookup_index(encoded_path)
807
714
            try:
808
715
                return mode_kind(index[subpath].mode)
816
723
        return os.lstat(self.abspath(path))
817
724
 
818
725
    def _live_entry(self, path):
819
 
        encoded_path = self.abspath(decode_git_path(path)).encode(
 
726
        encoded_path = self.abspath(path.decode('utf-8')).encode(
820
727
            osutils._fs_enc)
821
728
        return index_entry_from_path(encoded_path)
822
729
 
825
732
            if self._supports_executable():
826
733
                mode = self._lstat(path).st_mode
827
734
            else:
828
 
                (index, subpath) = self._lookup_index(encode_git_path(path))
 
735
                (index, subpath) = self._lookup_index(path.encode('utf-8'))
829
736
                try:
830
737
                    mode = index[subpath].mode
831
738
                except KeyError:
870
777
                         name.decode(osutils._fs_enc))])
871
778
            for path in path_iterator:
872
779
                try:
873
 
                    encoded_path = encode_git_path(path)
 
780
                    encoded_path = path.encode("utf-8")
874
781
                except UnicodeEncodeError:
875
782
                    raise errors.BadFilenameEncoding(
876
783
                        path, osutils._fs_enc)
924
831
            for path in self.index:
925
832
                if self.mapping.is_special_file(path):
926
833
                    continue
927
 
                path = decode_git_path(path)
 
834
                path = path.decode("utf-8")
928
835
                paths.add(path)
929
836
                while path != "":
930
837
                    path = posixpath.dirname(path).strip("/")
934
841
            return paths
935
842
 
936
843
    def iter_child_entries(self, path):
937
 
        encoded_path = encode_git_path(path)
 
844
        encoded_path = path.encode('utf-8')
938
845
        with self.lock_read():
939
846
            parent_id = self.path2id(path)
940
847
            found_any = False
941
848
            for item_path, value in self.index.iteritems():
942
 
                decoded_item_path = decode_git_path(item_path)
 
849
                decoded_item_path = item_path.decode('utf-8')
943
850
                if self.mapping.is_special_file(item_path):
944
851
                    continue
945
852
                if not osutils.is_inside(path, decoded_item_path):
963
870
            conflicts = _mod_conflicts.ConflictList()
964
871
            for item_path, value in self.index.iteritems():
965
872
                if value.flags & FLAG_STAGEMASK:
966
 
                    conflicts.append(TextConflict(decode_git_path(item_path)))
 
873
                    conflicts.append(_mod_conflicts.TextConflict(
 
874
                        item_path.decode('utf-8')))
967
875
            return conflicts
968
876
 
969
877
    def set_conflicts(self, conflicts):
970
878
        by_path = set()
971
879
        for conflict in conflicts:
972
880
            if conflict.typestring in ('text conflict', 'contents conflict'):
973
 
                by_path.add(encode_git_path(conflict.path))
 
881
                by_path.add(conflict.path.encode('utf-8'))
974
882
            else:
975
883
                raise errors.UnsupportedOperation(self.set_conflicts, self)
976
884
        with self.lock_tree_write():
978
886
                self._set_conflicted(path, path in by_path)
979
887
 
980
888
    def _set_conflicted(self, path, conflicted):
 
889
        trace.mutter('change conflict: %r -> %r', path, conflicted)
981
890
        value = self.index[path]
982
891
        self._index_dirty = True
983
892
        if conflicted:
992
901
                                           'contents conflict'):
993
902
                    try:
994
903
                        self._set_conflicted(
995
 
                            encode_git_path(conflict.path), True)
 
904
                            conflict.path.encode('utf-8'), True)
996
905
                    except KeyError:
997
906
                        raise errors.UnsupportedOperation(
998
907
                            self.add_conflicts, self)
1003
912
        """Walk the directories of this tree.
1004
913
 
1005
914
        returns a generator which yields items in the form:
1006
 
                (current_directory_path,
1007
 
                 [(file1_path, file1_name, file1_kind, (lstat),
 
915
                ((curren_directory_path, fileid),
 
916
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
1008
917
                   file1_kind), ... ])
1009
918
 
1010
919
        This API returns a generator, which is only valid during the current
1068
977
                             - (current_inv[0][0] < cur_disk_dir_relpath))
1069
978
            if direction > 0:
1070
979
                # disk is before inventory - unknown
1071
 
                dirblock = [(relpath, basename, kind, stat, None) for
 
980
                dirblock = [(relpath, basename, kind, stat, None, None) for
1072
981
                            relpath, basename, kind, stat, top_path in
1073
982
                            cur_disk_dir_content]
1074
 
                yield cur_disk_dir_relpath, dirblock
 
983
                yield (cur_disk_dir_relpath, None), dirblock
1075
984
                try:
1076
985
                    current_disk = next(disk_iterator)
1077
986
                except StopIteration:
1078
987
                    disk_finished = True
1079
988
            elif direction < 0:
1080
989
                # inventory is before disk - missing.
1081
 
                dirblock = [(relpath, basename, 'unknown', None, kind)
 
990
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1082
991
                            for relpath, basename, dkind, stat, fileid, kind in
1083
992
                            current_inv[1]]
1084
 
                yield current_inv[0][0], dirblock
 
993
                yield (current_inv[0][0], current_inv[0][1]), dirblock
1085
994
                try:
1086
995
                    current_inv = next(inventory_iterator)
1087
996
                except StopIteration:
1099
1008
                        # versioned, present file
1100
1009
                        dirblock.append((inv_row[0],
1101
1010
                                         inv_row[1], disk_row[2],
1102
 
                                         disk_row[3], inv_row[5]))
 
1011
                                         disk_row[3], inv_row[4],
 
1012
                                         inv_row[5]))
1103
1013
                    elif len(path_elements[0]) == 5:
1104
1014
                        # unknown disk file
1105
1015
                        dirblock.append(
1106
1016
                            (path_elements[0][0], path_elements[0][1],
1107
1017
                                path_elements[0][2], path_elements[0][3],
1108
 
                                None))
 
1018
                                None, None))
1109
1019
                    elif len(path_elements[0]) == 6:
1110
1020
                        # versioned, absent file.
1111
1021
                        dirblock.append(
1112
1022
                            (path_elements[0][0], path_elements[0][1],
1113
 
                                'unknown', None,
 
1023
                                'unknown', None, path_elements[0][4],
1114
1024
                                path_elements[0][5]))
1115
1025
                    else:
1116
1026
                        raise NotImplementedError('unreachable code')
1117
 
                yield current_inv[0][0], dirblock
 
1027
                yield current_inv[0], dirblock
1118
1028
                try:
1119
1029
                    current_inv = next(inventory_iterator)
1120
1030
                except StopIteration:
1127
1037
    def _walkdirs(self, prefix=u""):
1128
1038
        if prefix != u"":
1129
1039
            prefix += u"/"
1130
 
        prefix = encode_git_path(prefix)
 
1040
        prefix = prefix.encode('utf-8')
1131
1041
        per_dir = defaultdict(set)
1132
1042
        if prefix == b"":
1133
1043
            per_dir[(u'', self.path2id(''))] = set()
1137
1047
                return
1138
1048
            (dirname, child_name) = posixpath.split(path)
1139
1049
            add_entry(dirname, 'directory')
1140
 
            dirname = decode_git_path(dirname)
 
1050
            dirname = dirname.decode("utf-8")
1141
1051
            dir_file_id = self.path2id(dirname)
1142
1052
            if not isinstance(value, tuple) or len(value) != 10:
1143
1053
                raise ValueError(value)
1144
1054
            per_dir[(dirname, dir_file_id)].add(
1145
 
                (decode_git_path(path), decode_git_path(child_name),
 
1055
                (path.decode("utf-8"), child_name.decode("utf-8"),
1146
1056
                 kind, None,
1147
 
                 self.path2id(decode_git_path(path)),
 
1057
                 self.path2id(path.decode("utf-8")),
1148
1058
                 kind))
1149
1059
        with self.lock_read():
1150
1060
            for path, value in self.index.iteritems():
1161
1071
    def store_uncommitted(self):
1162
1072
        raise errors.StoringUncommittedNotSupported(self)
1163
1073
 
 
1074
    def apply_inventory_delta(self, changes):
 
1075
        for (old_path, new_path, file_id, ie) in changes:
 
1076
            if old_path is not None:
 
1077
                (index, old_subpath) = self._lookup_index(
 
1078
                    old_path.encode('utf-8'))
 
1079
                try:
 
1080
                    self._index_del_entry(index, old_subpath)
 
1081
                except KeyError:
 
1082
                    pass
 
1083
                else:
 
1084
                    self._versioned_dirs = None
 
1085
            if new_path is not None and ie.kind != 'directory':
 
1086
                if ie.kind == 'tree-reference':
 
1087
                    self._index_add_entry(
 
1088
                        new_path, ie.kind,
 
1089
                        reference_revision=ie.reference_revision)
 
1090
                else:
 
1091
                    self._index_add_entry(new_path, ie.kind)
 
1092
        self.flush()
 
1093
 
1164
1094
    def annotate_iter(self, path,
1165
1095
                      default_revision=_mod_revision.CURRENT_REVISION):
1166
1096
        """See Tree.annotate_iter
1257
1187
                        # Let's at least try to use the working tree file:
1258
1188
                        try:
1259
1189
                            st = self._lstat(self.abspath(
1260
 
                                decode_git_path(entry.path)))
 
1190
                                entry.path.decode('utf-8')))
1261
1191
                        except OSError:
1262
1192
                            # But if it doesn't exist, we'll make something up.
1263
1193
                            obj = self.store[entry.sha]
1268
1198
                    (index, subpath) = self._lookup_index(entry.path)
1269
1199
                    index[subpath] = index_entry_from_stat(st, entry.sha, 0)
1270
1200
 
1271
 
    def _update_git_tree(
1272
 
            self, old_revision, new_revision, change_reporter=None,
1273
 
            show_base=False):
 
1201
    def _update_git_tree(self, old_revision, new_revision, change_reporter=None,
 
1202
                         show_base=False):
1274
1203
        basis_tree = self.revision_tree(old_revision)
1275
1204
        if new_revision != old_revision:
1276
 
            from .. import merge
1277
1205
            with basis_tree.lock_read():
1278
1206
                new_basis_tree = self.branch.basis_tree()
1279
1207
                merge.merge_inner(
1319
1247
    def get_reference_revision(self, path, branch=None):
1320
1248
        hexsha = self._read_submodule_head(path)
1321
1249
        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
 
1250
            return _mod_revision.NULL_REVISION
1327
1251
        return self.branch.lookup_foreign_revision_id(hexsha)
1328
1252
 
1329
1253
    def get_nested_tree(self, path):
1386
1310
 
1387
1311
    def copy_content_into(self, tree, revision_id=None):
1388
1312
        """Copy the current content and user files of this tree into tree."""
1389
 
        from .. import merge
1390
1313
        with self.lock_read():
1391
1314
            if revision_id is None:
1392
1315
                merge.transform_tree(tree, self)
1415
1338
 
1416
1339
    def get_reference_info(self, path):
1417
1340
        submodule_info = self._submodule_info()
1418
 
        info = submodule_info.get(encode_git_path(path))
 
1341
        info = submodule_info.get(path.encode('utf-8'))
1419
1342
        if info is None:
1420
1343
            return None
1421
 
        return decode_git_path(info[0])
 
1344
        return info[0].decode('utf-8')
1422
1345
 
1423
1346
    def set_reference_info(self, tree_path, branch_location):
1424
1347
        path = self.abspath('.gitmodules')
1429
1352
                config = GitConfigFile()
1430
1353
            else:
1431
1354
                raise
1432
 
        section = (b'submodule', encode_git_path(tree_path))
 
1355
        section = (b'submodule', tree_path.encode('utf-8'))
1433
1356
        if branch_location is None:
1434
1357
            try:
1435
1358
                del config[section]
1441
1364
                branch_location)
1442
1365
            config.set(
1443
1366
                section,
1444
 
                b'path', encode_git_path(tree_path))
 
1367
                b'path', tree_path.encode('utf-8'))
1445
1368
            config.set(
1446
1369
                section,
1447
1370
                b'url', branch_location.encode('utf-8'))