/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/transform.py

Factor out resolvers in conflict resolution handling.

Merged from https://code.launchpad.net/~jelmer/brz/split-conflict-pass/+merge/388933

Show diffs side-by-side

added added

removed removed

Lines of Context:
1146
1146
        raise MalformedTransform(conflicts=conflicts)
1147
1147
 
1148
1148
 
 
1149
def resolve_duplicate_id(tt, path_tree, c_type, old_trans_id, trans_id):
 
1150
    tt.unversion_file(old_trans_id)
 
1151
    yield (c_type, 'Unversioned existing file', old_trans_id, trans_id)
 
1152
 
 
1153
 
 
1154
def resolve_duplicate(tt, path_tree, c_type, last_trans_id, trans_id, name):
 
1155
    # files that were renamed take precedence
 
1156
    final_parent = tt.final_parent(last_trans_id)
 
1157
    if tt.path_changed(last_trans_id):
 
1158
        existing_file, new_file = trans_id, last_trans_id
 
1159
    else:
 
1160
        existing_file, new_file = last_trans_id, trans_id
 
1161
    new_name = tt.final_name(existing_file) + '.moved'
 
1162
    tt.adjust_path(new_name, final_parent, existing_file)
 
1163
    yield (c_type, 'Moved existing file to', existing_file, new_file)
 
1164
 
 
1165
 
 
1166
def resolve_parent_loop(tt, path_tree, c_type, cur):
 
1167
    # break the loop by undoing one of the ops that caused the loop
 
1168
    while not tt.path_changed(cur):
 
1169
        cur = tt.final_parent(cur)
 
1170
    yield (c_type, 'Cancelled move', cur, tt.final_parent(cur),)
 
1171
    tt.adjust_path(tt.final_name(cur), tt.get_tree_parent(cur), cur)
 
1172
 
 
1173
 
 
1174
def resolve_missing_parent(tt, path_tree, c_type, trans_id):
 
1175
    if trans_id in tt._removed_contents:
 
1176
        cancel_deletion = True
 
1177
        orphans = tt._get_potential_orphans(trans_id)
 
1178
        if orphans:
 
1179
            cancel_deletion = False
 
1180
            # All children are orphans
 
1181
            for o in orphans:
 
1182
                try:
 
1183
                    tt.new_orphan(o, trans_id)
 
1184
                except OrphaningError:
 
1185
                    # Something bad happened so we cancel the directory
 
1186
                    # deletion which will leave it in place with a
 
1187
                    # conflict. The user can deal with it from there.
 
1188
                    # Note that this also catch the case where we don't
 
1189
                    # want to create orphans and leave the directory in
 
1190
                    # place.
 
1191
                    cancel_deletion = True
 
1192
                    break
 
1193
        if cancel_deletion:
 
1194
            # Cancel the directory deletion
 
1195
            tt.cancel_deletion(trans_id)
 
1196
            yield ('deleting parent', 'Not deleting', trans_id)
 
1197
    else:
 
1198
        create = True
 
1199
        try:
 
1200
            tt.final_name(trans_id)
 
1201
        except NoFinalPath:
 
1202
            if path_tree is not None:
 
1203
                file_id = tt.final_file_id(trans_id)
 
1204
                if file_id is None:
 
1205
                    file_id = tt.inactive_file_id(trans_id)
 
1206
                _, entry = next(path_tree.iter_entries_by_dir(
 
1207
                    specific_files=[path_tree.id2path(file_id)]))
 
1208
                # special-case the other tree root (move its
 
1209
                # children to current root)
 
1210
                if entry.parent_id is None:
 
1211
                    create = False
 
1212
                    moved = _reparent_transform_children(
 
1213
                        tt, trans_id, tt.root)
 
1214
                    for child in moved:
 
1215
                        yield (c_type, 'Moved to root', child)
 
1216
                else:
 
1217
                    parent_trans_id = tt.trans_id_file_id(
 
1218
                        entry.parent_id)
 
1219
                    tt.adjust_path(entry.name, parent_trans_id,
 
1220
                                   trans_id)
 
1221
        if create:
 
1222
            tt.create_directory(trans_id)
 
1223
            yield (c_type, 'Created directory', trans_id)
 
1224
 
 
1225
 
 
1226
def resolve_unversioned_parent(tt, path_tree, c_type, trans_id):
 
1227
    file_id = tt.inactive_file_id(trans_id)
 
1228
    # special-case the other tree root (move its children instead)
 
1229
    if path_tree and path_tree.path2id('') == file_id:
 
1230
        # This is the root entry, skip it
 
1231
        return
 
1232
    tt.version_file(trans_id, file_id=file_id)
 
1233
    yield (c_type, 'Versioned directory', trans_id)
 
1234
 
 
1235
 
 
1236
def resolve_non_directory_parent(tt, path_tree, c_type, parent_id):
 
1237
    parent_parent = tt.final_parent(parent_id)
 
1238
    parent_name = tt.final_name(parent_id)
 
1239
    parent_file_id = tt.final_file_id(parent_id)
 
1240
    new_parent_id = tt.new_directory(parent_name + '.new',
 
1241
                                     parent_parent, parent_file_id)
 
1242
    _reparent_transform_children(tt, parent_id, new_parent_id)
 
1243
    if parent_file_id is not None:
 
1244
        tt.unversion_file(parent_id)
 
1245
    yield (c_type, 'Created directory', new_parent_id)
 
1246
 
 
1247
 
 
1248
def resolve_versioning_no_contents(tt, path_tree, c_type, trans_id):
 
1249
    tt.cancel_versioning(trans_id)
 
1250
    return []
 
1251
 
 
1252
 
 
1253
CONFLICT_RESOLVERS = {
 
1254
    'duplicate id': resolve_duplicate_id,
 
1255
    'duplicate': resolve_duplicate,
 
1256
    'parent loop': resolve_parent_loop,
 
1257
    'missing parent': resolve_missing_parent,
 
1258
    'unversioned parent': resolve_unversioned_parent,
 
1259
    'non-directory parent': resolve_non_directory_parent,
 
1260
    'versioning no contents': resolve_versioning_no_contents,
 
1261
}
 
1262
 
 
1263
 
1149
1264
def conflict_pass(tt, conflicts, path_tree=None):
1150
1265
    """Resolve some classes of conflicts.
1151
1266
 
1154
1269
    :param path_tree: A Tree to get supplemental paths from
1155
1270
    """
1156
1271
    new_conflicts = set()
1157
 
    for c_type, conflict in ((c[0], c) for c in conflicts):
1158
 
        if c_type == 'duplicate id':
1159
 
            tt.unversion_file(conflict[1])
1160
 
            new_conflicts.add((c_type, 'Unversioned existing file',
1161
 
                               conflict[1], conflict[2], ))
1162
 
        elif c_type == 'duplicate':
1163
 
            # files that were renamed take precedence
1164
 
            final_parent = tt.final_parent(conflict[1])
1165
 
            if tt.path_changed(conflict[1]):
1166
 
                existing_file, new_file = conflict[2], conflict[1]
1167
 
            else:
1168
 
                existing_file, new_file = conflict[1], conflict[2]
1169
 
            new_name = tt.final_name(existing_file) + '.moved'
1170
 
            tt.adjust_path(new_name, final_parent, existing_file)
1171
 
            new_conflicts.add((c_type, 'Moved existing file to',
1172
 
                               existing_file, new_file))
1173
 
        elif c_type == 'parent loop':
1174
 
            # break the loop by undoing one of the ops that caused the loop
1175
 
            cur = conflict[1]
1176
 
            while not tt.path_changed(cur):
1177
 
                cur = tt.final_parent(cur)
1178
 
            new_conflicts.add((c_type, 'Cancelled move', cur,
1179
 
                               tt.final_parent(cur),))
1180
 
            tt.adjust_path(tt.final_name(cur), tt.get_tree_parent(cur), cur)
1181
 
 
1182
 
        elif c_type == 'missing parent':
1183
 
            trans_id = conflict[1]
1184
 
            if trans_id in tt._removed_contents:
1185
 
                cancel_deletion = True
1186
 
                orphans = tt._get_potential_orphans(trans_id)
1187
 
                if orphans:
1188
 
                    cancel_deletion = False
1189
 
                    # All children are orphans
1190
 
                    for o in orphans:
1191
 
                        try:
1192
 
                            tt.new_orphan(o, trans_id)
1193
 
                        except OrphaningError:
1194
 
                            # Something bad happened so we cancel the directory
1195
 
                            # deletion which will leave it in place with a
1196
 
                            # conflict. The user can deal with it from there.
1197
 
                            # Note that this also catch the case where we don't
1198
 
                            # want to create orphans and leave the directory in
1199
 
                            # place.
1200
 
                            cancel_deletion = True
1201
 
                            break
1202
 
                if cancel_deletion:
1203
 
                    # Cancel the directory deletion
1204
 
                    tt.cancel_deletion(trans_id)
1205
 
                    new_conflicts.add(('deleting parent', 'Not deleting',
1206
 
                                       trans_id))
1207
 
            else:
1208
 
                create = True
1209
 
                try:
1210
 
                    tt.final_name(trans_id)
1211
 
                except NoFinalPath:
1212
 
                    if path_tree is not None:
1213
 
                        file_id = tt.final_file_id(trans_id)
1214
 
                        if file_id is None:
1215
 
                            file_id = tt.inactive_file_id(trans_id)
1216
 
                        _, entry = next(path_tree.iter_entries_by_dir(
1217
 
                            specific_files=[path_tree.id2path(file_id)]))
1218
 
                        # special-case the other tree root (move its
1219
 
                        # children to current root)
1220
 
                        if entry.parent_id is None:
1221
 
                            create = False
1222
 
                            moved = _reparent_transform_children(
1223
 
                                tt, trans_id, tt.root)
1224
 
                            for child in moved:
1225
 
                                new_conflicts.add((c_type, 'Moved to root',
1226
 
                                                   child))
1227
 
                        else:
1228
 
                            parent_trans_id = tt.trans_id_file_id(
1229
 
                                entry.parent_id)
1230
 
                            tt.adjust_path(entry.name, parent_trans_id,
1231
 
                                           trans_id)
1232
 
                if create:
1233
 
                    tt.create_directory(trans_id)
1234
 
                    new_conflicts.add((c_type, 'Created directory', trans_id))
1235
 
        elif c_type == 'unversioned parent':
1236
 
            file_id = tt.inactive_file_id(conflict[1])
1237
 
            # special-case the other tree root (move its children instead)
1238
 
            if path_tree and path_tree.path2id('') == file_id:
1239
 
                # This is the root entry, skip it
1240
 
                continue
1241
 
            tt.version_file(conflict[1], file_id=file_id)
1242
 
            new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
1243
 
        elif c_type == 'non-directory parent':
1244
 
            parent_id = conflict[1]
1245
 
            parent_parent = tt.final_parent(parent_id)
1246
 
            parent_name = tt.final_name(parent_id)
1247
 
            parent_file_id = tt.final_file_id(parent_id)
1248
 
            new_parent_id = tt.new_directory(parent_name + '.new',
1249
 
                                             parent_parent, parent_file_id)
1250
 
            _reparent_transform_children(tt, parent_id, new_parent_id)
1251
 
            if parent_file_id is not None:
1252
 
                tt.unversion_file(parent_id)
1253
 
            new_conflicts.add((c_type, 'Created directory', new_parent_id))
1254
 
        elif c_type == 'versioning no contents':
1255
 
            tt.cancel_versioning(conflict[1])
 
1272
    for conflict in conflicts:
 
1273
        resolver = CONFLICT_RESOLVERS.get(conflict[0])
 
1274
        if resolver is None:
 
1275
            continue
 
1276
        new_conflicts.update(resolver(tt, path_tree, *conflict))
1256
1277
    return new_conflicts
1257
1278
 
1258
1279