/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:
17
17
 
18
18
"""An adapter between a Git index and a Bazaar Working Tree"""
19
19
 
20
 
from __future__ import absolute_import
21
 
 
22
20
import itertools
23
21
from collections import defaultdict
24
22
import errno
46
44
    )
47
45
import os
48
46
import posixpath
 
47
import re
49
48
import stat
50
49
import sys
51
50
 
71
70
    BadReferenceTarget,
72
71
    MutableTree,
73
72
    )
74
 
from ..sixish import text_type
75
73
 
76
74
 
77
75
from .dir import (
87
85
    )
88
86
 
89
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
 
90
177
class GitWorkingTree(MutableGitIndexTree, workingtree.WorkingTree):
91
178
    """A Git working tree."""
92
179
 
224
311
        else:
225
312
            self.case_sensitive = False
226
313
 
227
 
    def transform(self, pb=None):
228
 
        from ..transform import TreeTransform
229
 
        return TreeTransform(self, pb=pb)
230
 
 
231
314
    def merge_modified(self):
232
315
        return {}
233
316
 
337
420
        def recurse_directory_to_add_files(directory):
338
421
            # Recurse directory and add all files
339
422
            # so we can check if they have changed.
340
 
            for parent_info, file_infos in self.walkdirs(directory):
341
 
                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:
342
425
                    # Is it versioned or ignored?
343
426
                    if self.is_versioned(relpath):
344
427
                        # Add nested content for deletion.
534
617
                              recurse_nested=False):
535
618
        if from_dir is None:
536
619
            from_dir = u""
537
 
        if not isinstance(from_dir, text_type):
 
620
        if not isinstance(from_dir, str):
538
621
            raise TypeError(from_dir)
539
622
        encoded_from_dir = self.abspath(from_dir).encode(osutils._fs_enc)
540
623
        for (dirpath, dirnames, filenames) in os.walk(encoded_from_dir):
576
659
        """
577
660
        with self.lock_read():
578
661
            index_paths = set(
579
 
                [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()])
580
663
            all_paths = set(self._iter_files_recursive(include_dirs=False))
581
664
            return iter(all_paths - index_paths)
582
665
 
874
957
            conflicts = _mod_conflicts.ConflictList()
875
958
            for item_path, value in self.index.iteritems():
876
959
                if value.flags & FLAG_STAGEMASK:
877
 
                    conflicts.append(_mod_conflicts.TextConflict(
878
 
                        decode_git_path(item_path)))
 
960
                    conflicts.append(TextConflict(decode_git_path(item_path)))
879
961
            return conflicts
880
962
 
881
963
    def set_conflicts(self, conflicts):
916
998
        """Walk the directories of this tree.
917
999
 
918
1000
        returns a generator which yields items in the form:
919
 
                ((curren_directory_path, fileid),
920
 
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
 
1001
                (current_directory_path,
 
1002
                 [(file1_path, file1_name, file1_kind, (lstat),
921
1003
                   file1_kind), ... ])
922
1004
 
923
1005
        This API returns a generator, which is only valid during the current
981
1063
                             - (current_inv[0][0] < cur_disk_dir_relpath))
982
1064
            if direction > 0:
983
1065
                # disk is before inventory - unknown
984
 
                dirblock = [(relpath, basename, kind, stat, None, None) for
 
1066
                dirblock = [(relpath, basename, kind, stat, None) for
985
1067
                            relpath, basename, kind, stat, top_path in
986
1068
                            cur_disk_dir_content]
987
 
                yield (cur_disk_dir_relpath, None), dirblock
 
1069
                yield cur_disk_dir_relpath, dirblock
988
1070
                try:
989
1071
                    current_disk = next(disk_iterator)
990
1072
                except StopIteration:
991
1073
                    disk_finished = True
992
1074
            elif direction < 0:
993
1075
                # inventory is before disk - missing.
994
 
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
 
1076
                dirblock = [(relpath, basename, 'unknown', None, kind)
995
1077
                            for relpath, basename, dkind, stat, fileid, kind in
996
1078
                            current_inv[1]]
997
 
                yield (current_inv[0][0], current_inv[0][1]), dirblock
 
1079
                yield current_inv[0][0], dirblock
998
1080
                try:
999
1081
                    current_inv = next(inventory_iterator)
1000
1082
                except StopIteration:
1012
1094
                        # versioned, present file
1013
1095
                        dirblock.append((inv_row[0],
1014
1096
                                         inv_row[1], disk_row[2],
1015
 
                                         disk_row[3], inv_row[4],
1016
 
                                         inv_row[5]))
 
1097
                                         disk_row[3], inv_row[5]))
1017
1098
                    elif len(path_elements[0]) == 5:
1018
1099
                        # unknown disk file
1019
1100
                        dirblock.append(
1020
1101
                            (path_elements[0][0], path_elements[0][1],
1021
1102
                                path_elements[0][2], path_elements[0][3],
1022
 
                                None, None))
 
1103
                                None))
1023
1104
                    elif len(path_elements[0]) == 6:
1024
1105
                        # versioned, absent file.
1025
1106
                        dirblock.append(
1026
1107
                            (path_elements[0][0], path_elements[0][1],
1027
 
                                'unknown', None, path_elements[0][4],
 
1108
                                'unknown', None,
1028
1109
                                path_elements[0][5]))
1029
1110
                    else:
1030
1111
                        raise NotImplementedError('unreachable code')
1031
 
                yield current_inv[0], dirblock
 
1112
                yield current_inv[0][0], dirblock
1032
1113
                try:
1033
1114
                    current_inv = next(inventory_iterator)
1034
1115
                except StopIteration:
1075
1156
    def store_uncommitted(self):
1076
1157
        raise errors.StoringUncommittedNotSupported(self)
1077
1158
 
1078
 
    def apply_inventory_delta(self, changes):
1079
 
        for (old_path, new_path, file_id, ie) in changes:
 
1159
    def _apply_transform_delta(self, changes):
 
1160
        for (old_path, new_path, ie) in changes:
1080
1161
            if old_path is not None:
1081
1162
                (index, old_subpath) = self._lookup_index(
1082
1163
                    encode_git_path(old_path))