/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: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-08-23 01:15:41 UTC
  • mfrom: (7520.1.4 merge-3.1)
  • Revision ID: breezy.the.bot@gmail.com-20200823011541-nv0oh7nzaganx2qy
Merge lp:brz/3.1.

Merged from https://code.launchpad.net/~jelmer/brz/merge-3.1/+merge/389690

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
47
48
import stat
48
49
import sys
49
50
 
84
85
    )
85
86
 
86
87
 
 
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
 
87
183
class GitWorkingTree(MutableGitIndexTree, workingtree.WorkingTree):
88
184
    """A Git working tree."""
89
185
 
330
426
        def recurse_directory_to_add_files(directory):
331
427
            # Recurse directory and add all files
332
428
            # so we can check if they have changed.
333
 
            for parent_info, file_infos in self.walkdirs(directory):
334
 
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
429
            for parent_path, file_infos in self.walkdirs(directory):
 
430
                for relpath, basename, kind, lstat, kind in file_infos:
335
431
                    # Is it versioned or ignored?
336
432
                    if self.is_versioned(relpath):
337
433
                        # Add nested content for deletion.
569
665
        """
570
666
        with self.lock_read():
571
667
            index_paths = set(
572
 
                [decode_git_path(p) for p, i in self._recurse_index_entries()])
 
668
                [decode_git_path(p) for p, sha, mode in self.iter_git_objects()])
573
669
            all_paths = set(self._iter_files_recursive(include_dirs=False))
574
670
            return iter(all_paths - index_paths)
575
671
 
867
963
            conflicts = _mod_conflicts.ConflictList()
868
964
            for item_path, value in self.index.iteritems():
869
965
                if value.flags & FLAG_STAGEMASK:
870
 
                    conflicts.append(_mod_conflicts.TextConflict(
871
 
                        decode_git_path(item_path)))
 
966
                    conflicts.append(TextConflict(decode_git_path(item_path)))
872
967
            return conflicts
873
968
 
874
969
    def set_conflicts(self, conflicts):
883
978
                self._set_conflicted(path, path in by_path)
884
979
 
885
980
    def _set_conflicted(self, path, conflicted):
886
 
        trace.mutter('change conflict: %r -> %r', path, conflicted)
887
981
        value = self.index[path]
888
982
        self._index_dirty = True
889
983
        if conflicted:
909
1003
        """Walk the directories of this tree.
910
1004
 
911
1005
        returns a generator which yields items in the form:
912
 
                ((curren_directory_path, fileid),
913
 
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
 
1006
                (current_directory_path,
 
1007
                 [(file1_path, file1_name, file1_kind, (lstat),
914
1008
                   file1_kind), ... ])
915
1009
 
916
1010
        This API returns a generator, which is only valid during the current
974
1068
                             - (current_inv[0][0] < cur_disk_dir_relpath))
975
1069
            if direction > 0:
976
1070
                # disk is before inventory - unknown
977
 
                dirblock = [(relpath, basename, kind, stat, None, None) for
 
1071
                dirblock = [(relpath, basename, kind, stat, None) for
978
1072
                            relpath, basename, kind, stat, top_path in
979
1073
                            cur_disk_dir_content]
980
 
                yield (cur_disk_dir_relpath, None), dirblock
 
1074
                yield cur_disk_dir_relpath, dirblock
981
1075
                try:
982
1076
                    current_disk = next(disk_iterator)
983
1077
                except StopIteration:
984
1078
                    disk_finished = True
985
1079
            elif direction < 0:
986
1080
                # inventory is before disk - missing.
987
 
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
 
1081
                dirblock = [(relpath, basename, 'unknown', None, kind)
988
1082
                            for relpath, basename, dkind, stat, fileid, kind in
989
1083
                            current_inv[1]]
990
 
                yield (current_inv[0][0], current_inv[0][1]), dirblock
 
1084
                yield current_inv[0][0], dirblock
991
1085
                try:
992
1086
                    current_inv = next(inventory_iterator)
993
1087
                except StopIteration:
1005
1099
                        # versioned, present file
1006
1100
                        dirblock.append((inv_row[0],
1007
1101
                                         inv_row[1], disk_row[2],
1008
 
                                         disk_row[3], inv_row[4],
1009
 
                                         inv_row[5]))
 
1102
                                         disk_row[3], inv_row[5]))
1010
1103
                    elif len(path_elements[0]) == 5:
1011
1104
                        # unknown disk file
1012
1105
                        dirblock.append(
1013
1106
                            (path_elements[0][0], path_elements[0][1],
1014
1107
                                path_elements[0][2], path_elements[0][3],
1015
 
                                None, None))
 
1108
                                None))
1016
1109
                    elif len(path_elements[0]) == 6:
1017
1110
                        # versioned, absent file.
1018
1111
                        dirblock.append(
1019
1112
                            (path_elements[0][0], path_elements[0][1],
1020
 
                                'unknown', None, path_elements[0][4],
 
1113
                                'unknown', None,
1021
1114
                                path_elements[0][5]))
1022
1115
                    else:
1023
1116
                        raise NotImplementedError('unreachable code')
1024
 
                yield current_inv[0], dirblock
 
1117
                yield current_inv[0][0], dirblock
1025
1118
                try:
1026
1119
                    current_inv = next(inventory_iterator)
1027
1120
                except StopIteration:
1068
1161
    def store_uncommitted(self):
1069
1162
        raise errors.StoringUncommittedNotSupported(self)
1070
1163
 
1071
 
    def _apply_transform_delta(self, changes):
1072
 
        for (old_path, new_path, ie) in changes:
1073
 
            if old_path is not None:
1074
 
                (index, old_subpath) = self._lookup_index(
1075
 
                    encode_git_path(old_path))
1076
 
                try:
1077
 
                    self._index_del_entry(index, old_subpath)
1078
 
                except KeyError:
1079
 
                    pass
1080
 
                else:
1081
 
                    self._versioned_dirs = None
1082
 
            if new_path is not None and ie.kind != 'directory':
1083
 
                if ie.kind == 'tree-reference':
1084
 
                    self._index_add_entry(
1085
 
                        new_path, ie.kind,
1086
 
                        reference_revision=ie.reference_revision)
1087
 
                else:
1088
 
                    self._index_add_entry(new_path, ie.kind)
1089
 
        self.flush()
1090
 
 
1091
1164
    def annotate_iter(self, path,
1092
1165
                      default_revision=_mod_revision.CURRENT_REVISION):
1093
1166
        """See Tree.annotate_iter
1246
1319
    def get_reference_revision(self, path, branch=None):
1247
1320
        hexsha = self._read_submodule_head(path)
1248
1321
        if hexsha is None:
1249
 
            return _mod_revision.NULL_REVISION
 
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
1327
        return self.branch.lookup_foreign_revision_id(hexsha)
1251
1328
 
1252
1329
    def get_nested_tree(self, path):