/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-08-10 15:00:17 UTC
  • mfrom: (7490.40.99 work)
  • mto: This revision was merged to the branch mainline in revision 7521.
  • Revision ID: jelmer@jelmer.uk-20200810150017-vs7xnrd1vat4iktg
Merge lp:brz/3.1.

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
 
87
177
class GitWorkingTree(MutableGitIndexTree, workingtree.WorkingTree):
88
178
    """A Git working tree."""
89
179
 
330
420
        def recurse_directory_to_add_files(directory):
331
421
            # Recurse directory and add all files
332
422
            # 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:
 
423
            for parent_path, file_infos in self.walkdirs(directory):
 
424
                for relpath, basename, kind, lstat, kind in file_infos:
335
425
                    # Is it versioned or ignored?
336
426
                    if self.is_versioned(relpath):
337
427
                        # Add nested content for deletion.
569
659
        """
570
660
        with self.lock_read():
571
661
            index_paths = set(
572
 
                [decode_git_path(p) for p, i in self._recurse_index_entries()])
 
662
                [decode_git_path(p) for p, sha, mode in self.iter_git_objects()])
573
663
            all_paths = set(self._iter_files_recursive(include_dirs=False))
574
664
            return iter(all_paths - index_paths)
575
665
 
867
957
            conflicts = _mod_conflicts.ConflictList()
868
958
            for item_path, value in self.index.iteritems():
869
959
                if value.flags & FLAG_STAGEMASK:
870
 
                    conflicts.append(_mod_conflicts.TextConflict(
871
 
                        decode_git_path(item_path)))
 
960
                    conflicts.append(TextConflict(decode_git_path(item_path)))
872
961
            return conflicts
873
962
 
874
963
    def set_conflicts(self, conflicts):
909
998
        """Walk the directories of this tree.
910
999
 
911
1000
        returns a generator which yields items in the form:
912
 
                ((curren_directory_path, fileid),
913
 
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
 
1001
                (current_directory_path,
 
1002
                 [(file1_path, file1_name, file1_kind, (lstat),
914
1003
                   file1_kind), ... ])
915
1004
 
916
1005
        This API returns a generator, which is only valid during the current
974
1063
                             - (current_inv[0][0] < cur_disk_dir_relpath))
975
1064
            if direction > 0:
976
1065
                # disk is before inventory - unknown
977
 
                dirblock = [(relpath, basename, kind, stat, None, None) for
 
1066
                dirblock = [(relpath, basename, kind, stat, None) for
978
1067
                            relpath, basename, kind, stat, top_path in
979
1068
                            cur_disk_dir_content]
980
 
                yield (cur_disk_dir_relpath, None), dirblock
 
1069
                yield cur_disk_dir_relpath, dirblock
981
1070
                try:
982
1071
                    current_disk = next(disk_iterator)
983
1072
                except StopIteration:
984
1073
                    disk_finished = True
985
1074
            elif direction < 0:
986
1075
                # inventory is before disk - missing.
987
 
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
 
1076
                dirblock = [(relpath, basename, 'unknown', None, kind)
988
1077
                            for relpath, basename, dkind, stat, fileid, kind in
989
1078
                            current_inv[1]]
990
 
                yield (current_inv[0][0], current_inv[0][1]), dirblock
 
1079
                yield current_inv[0][0], dirblock
991
1080
                try:
992
1081
                    current_inv = next(inventory_iterator)
993
1082
                except StopIteration:
1005
1094
                        # versioned, present file
1006
1095
                        dirblock.append((inv_row[0],
1007
1096
                                         inv_row[1], disk_row[2],
1008
 
                                         disk_row[3], inv_row[4],
1009
 
                                         inv_row[5]))
 
1097
                                         disk_row[3], inv_row[5]))
1010
1098
                    elif len(path_elements[0]) == 5:
1011
1099
                        # unknown disk file
1012
1100
                        dirblock.append(
1013
1101
                            (path_elements[0][0], path_elements[0][1],
1014
1102
                                path_elements[0][2], path_elements[0][3],
1015
 
                                None, None))
 
1103
                                None))
1016
1104
                    elif len(path_elements[0]) == 6:
1017
1105
                        # versioned, absent file.
1018
1106
                        dirblock.append(
1019
1107
                            (path_elements[0][0], path_elements[0][1],
1020
 
                                'unknown', None, path_elements[0][4],
 
1108
                                'unknown', None,
1021
1109
                                path_elements[0][5]))
1022
1110
                    else:
1023
1111
                        raise NotImplementedError('unreachable code')
1024
 
                yield current_inv[0], dirblock
 
1112
                yield current_inv[0][0], dirblock
1025
1113
                try:
1026
1114
                    current_inv = next(inventory_iterator)
1027
1115
                except StopIteration: