154
187
"""Change the path that is assigned to a transaction id."""
155
188
if trans_id == self._new_root:
156
189
raise CantMoveRoot
190
previous_parent = self._new_parent.get(trans_id)
191
previous_name = self._new_name.get(trans_id)
157
192
self._new_name[trans_id] = name
158
193
self._new_parent[trans_id] = parent
194
if (trans_id in self._limbo_files and
195
trans_id not in self._needs_rename):
196
self._rename_in_limbo([trans_id])
197
self._limbo_children[previous_parent].remove(trans_id)
198
del self._limbo_children_names[previous_parent][previous_name]
200
def _rename_in_limbo(self, trans_ids):
201
"""Fix limbo names so that the right final path is produced.
203
This means we outsmarted ourselves-- we tried to avoid renaming
204
these files later by creating them with their final names in their
205
final parents. But now the previous name or parent is no longer
206
suitable, so we have to rename them.
208
Even for trans_ids that have no new contents, we must remove their
209
entries from _limbo_files, because they are now stale.
211
for trans_id in trans_ids:
212
old_path = self._limbo_files.pop(trans_id)
213
if trans_id not in self._new_contents:
215
new_path = self._limbo_name(trans_id)
216
os.rename(old_path, new_path)
160
218
def adjust_root_path(self, name, parent):
161
219
"""Emulate moving the root by moving all children, instead.
788
def apply(self, no_conflicts=False):
710
789
"""Apply all changes to the inventory and filesystem.
712
791
If filesystem or inventory conflicts are present, MalformedTransform
794
If apply succeeds, finalize is not necessary.
796
:param no_conflicts: if True, the caller guarantees there are no
797
conflicts, so no check is made.
715
conflicts = self.find_conflicts()
716
if len(conflicts) != 0:
717
raise MalformedTransform(conflicts=conflicts)
800
conflicts = self.find_conflicts()
801
if len(conflicts) != 0:
802
raise MalformedTransform(conflicts=conflicts)
719
803
inv = self._tree.inventory
720
805
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
722
807
child_pb.update('Apply phase', 0, 2)
723
self._apply_removals(inv, limbo_inv)
808
self._apply_removals(inv, inventory_delta)
724
809
child_pb.update('Apply phase', 1, 2)
725
modified_paths = self._apply_insertions(inv, limbo_inv)
810
modified_paths = self._apply_insertions(inv, inventory_delta)
727
812
child_pb.finished()
728
self._tree._write_inventory(inv)
813
self._tree.apply_inventory_delta(inventory_delta)
729
814
self.__done = True
731
return _TransformResults(modified_paths)
816
return _TransformResults(modified_paths, self.rename_count)
733
818
def _limbo_name(self, trans_id):
734
819
"""Generate the limbo name of a file"""
735
return pathjoin(self._limbodir, trans_id)
820
limbo_name = self._limbo_files.get(trans_id)
821
if limbo_name is not None:
823
parent = self._new_parent.get(trans_id)
824
# if the parent directory is already in limbo (e.g. when building a
825
# tree), choose a limbo name inside the parent, to reduce further
827
use_direct_path = False
828
if self._new_contents.get(parent) == 'directory':
829
filename = self._new_name.get(trans_id)
830
if filename is not None:
831
if parent not in self._limbo_children:
832
self._limbo_children[parent] = set()
833
self._limbo_children_names[parent] = {}
834
use_direct_path = True
835
# the direct path can only be used if no other file has
836
# already taken this pathname, i.e. if the name is unused, or
837
# if it is already associated with this trans_id.
838
elif (self._limbo_children_names[parent].get(filename)
839
in (trans_id, None)):
840
use_direct_path = True
842
limbo_name = pathjoin(self._limbo_files[parent], filename)
843
self._limbo_children[parent].add(trans_id)
844
self._limbo_children_names[parent][filename] = trans_id
846
limbo_name = pathjoin(self._limbodir, trans_id)
847
self._needs_rename.add(trans_id)
848
self._limbo_files[trans_id] = limbo_name
737
def _apply_removals(self, inv, limbo_inv):
851
def _apply_removals(self, inv, inventory_delta):
738
852
"""Perform tree operations that remove directory/inventory names.
740
854
That is, delete files that are to be deleted, and put any files that
805
921
if trans_id in self._new_id:
807
923
kind = file_kind(self._tree.abspath(path))
808
inv.add_path(path, kind, self._new_id[trans_id])
809
elif trans_id in self._new_name or trans_id in\
811
entry = limbo_inv.get(trans_id)
812
if entry is not None:
813
entry.name = self.final_name(trans_id)
814
parent_path = os.path.dirname(path)
816
self._tree.inventory.path2id(parent_path)
819
# requires files and inventory entries to be in place
924
if trans_id in self._new_reference_revision:
925
new_entry = inventory.TreeReference(
926
self._new_id[trans_id],
927
self._new_name[trans_id],
928
self.final_file_id(self._new_parent[trans_id]),
929
None, self._new_reference_revision[trans_id])
931
new_entry = inventory.make_entry(kind,
932
self.final_name(trans_id),
933
self.final_file_id(self.final_parent(trans_id)),
934
self._new_id[trans_id])
936
if trans_id in self._new_name or trans_id in\
938
trans_id in self._new_executability:
939
file_id = self.final_file_id(trans_id)
940
if file_id is not None:
942
new_entry = entry.copy()
944
if trans_id in self._new_name or trans_id in\
946
if new_entry is not None:
947
new_entry.name = self.final_name(trans_id)
948
parent = self.final_parent(trans_id)
949
parent_id = self.final_file_id(parent)
950
new_entry.parent_id = parent_id
820
952
if trans_id in self._new_executability:
821
self._set_executability(path, inv, trans_id)
953
self._set_executability(path, new_entry, trans_id)
954
if new_entry is not None:
955
if new_entry.file_id in inv:
956
old_path = inv.id2path(new_entry.file_id)
959
inventory_delta.append((old_path, path,
823
963
child_pb.finished()
824
964
return modified_paths
826
def _set_executability(self, path, inv, trans_id):
966
def _set_executability(self, path, entry, trans_id):
827
967
"""Set the executability of versioned files """
828
file_id = inv.path2id(path)
829
968
new_executability = self._new_executability[trans_id]
830
inv[file_id].executable = new_executability
969
entry.executable = new_executability
831
970
if supports_executable():
832
971
abspath = self._tree.abspath(path)
833
972
current_mode = os.stat(abspath).st_mode
894
1033
self.create_symlink(target, trans_id)
1036
def _affected_ids(self):
1037
"""Return the set of transform ids affected by the transform"""
1038
trans_ids = set(self._removed_id)
1039
trans_ids.update(self._new_id.keys())
1040
trans_ids.update(self._removed_contents)
1041
trans_ids.update(self._new_contents.keys())
1042
trans_ids.update(self._new_executability.keys())
1043
trans_ids.update(self._new_name.keys())
1044
trans_ids.update(self._new_parent.keys())
1047
def _get_file_id_maps(self):
1048
"""Return mapping of file_ids to trans_ids in the to and from states"""
1049
trans_ids = self._affected_ids()
1052
# Build up two dicts: trans_ids associated with file ids in the
1053
# FROM state, vs the TO state.
1054
for trans_id in trans_ids:
1055
from_file_id = self.tree_file_id(trans_id)
1056
if from_file_id is not None:
1057
from_trans_ids[from_file_id] = trans_id
1058
to_file_id = self.final_file_id(trans_id)
1059
if to_file_id is not None:
1060
to_trans_ids[to_file_id] = trans_id
1061
return from_trans_ids, to_trans_ids
1063
def _from_file_data(self, from_trans_id, from_versioned, file_id):
1064
"""Get data about a file in the from (tree) state
1066
Return a (name, parent, kind, executable) tuple
1068
from_path = self._tree_id_paths.get(from_trans_id)
1070
# get data from working tree if versioned
1071
from_entry = self._tree.inventory[file_id]
1072
from_name = from_entry.name
1073
from_parent = from_entry.parent_id
1076
if from_path is None:
1077
# File does not exist in FROM state
1081
# File exists, but is not versioned. Have to use path-
1083
from_name = os.path.basename(from_path)
1084
tree_parent = self.get_tree_parent(from_trans_id)
1085
from_parent = self.tree_file_id(tree_parent)
1086
if from_path is not None:
1087
from_kind, from_executable, from_stats = \
1088
self._tree._comparison_data(from_entry, from_path)
1091
from_executable = False
1092
return from_name, from_parent, from_kind, from_executable
1094
def _to_file_data(self, to_trans_id, from_trans_id, from_executable):
1095
"""Get data about a file in the to (target) state
1097
Return a (name, parent, kind, executable) tuple
1099
to_name = self.final_name(to_trans_id)
1101
to_kind = self.final_kind(to_trans_id)
1104
to_parent = self.final_file_id(self.final_parent(to_trans_id))
1105
if to_trans_id in self._new_executability:
1106
to_executable = self._new_executability[to_trans_id]
1107
elif to_trans_id == from_trans_id:
1108
to_executable = from_executable
1110
to_executable = False
1111
return to_name, to_parent, to_kind, to_executable
1113
def _iter_changes(self):
1114
"""Produce output in the same format as Tree._iter_changes.
1116
Will produce nonsensical results if invoked while inventory/filesystem
1117
conflicts (as reported by TreeTransform.find_conflicts()) are present.
1119
This reads the Transform, but only reproduces changes involving a
1120
file_id. Files that are not versioned in either of the FROM or TO
1121
states are not reflected.
1123
final_paths = FinalPaths(self)
1124
from_trans_ids, to_trans_ids = self._get_file_id_maps()
1126
# Now iterate through all active file_ids
1127
for file_id in set(from_trans_ids.keys() + to_trans_ids.keys()):
1129
from_trans_id = from_trans_ids.get(file_id)
1130
# find file ids, and determine versioning state
1131
if from_trans_id is None:
1132
from_versioned = False
1133
from_trans_id = to_trans_ids[file_id]
1135
from_versioned = True
1136
to_trans_id = to_trans_ids.get(file_id)
1137
if to_trans_id is None:
1138
to_versioned = False
1139
to_trans_id = from_trans_id
1143
from_name, from_parent, from_kind, from_executable = \
1144
self._from_file_data(from_trans_id, from_versioned, file_id)
1146
to_name, to_parent, to_kind, to_executable = \
1147
self._to_file_data(to_trans_id, from_trans_id, from_executable)
1149
if not from_versioned:
1152
from_path = self._tree_id_paths.get(from_trans_id)
1153
if not to_versioned:
1156
to_path = final_paths.get_path(to_trans_id)
1157
if from_kind != to_kind:
1159
elif to_kind in ('file', 'symlink') and (
1160
to_trans_id != from_trans_id or
1161
to_trans_id in self._new_contents):
1163
if (not modified and from_versioned == to_versioned and
1164
from_parent==to_parent and from_name == to_name and
1165
from_executable == to_executable):
1167
results.append((file_id, (from_path, to_path), modified,
1168
(from_versioned, to_versioned),
1169
(from_parent, to_parent),
1170
(from_name, to_name),
1171
(from_kind, to_kind),
1172
(from_executable, to_executable)))
1173
return iter(sorted(results, key=lambda x:x[1]))
897
1176
def joinpath(parent, child):
898
1177
"""Join tree-relative paths, handling the tree root specially"""
899
1178
if parent is None or parent == "":
935
1214
file_ids.sort(key=tree.id2path)
938
1218
def build_tree(tree, wt):
939
"""Create working tree for a branch, using a Transaction."""
1219
"""Create working tree for a branch, using a TreeTransform.
1221
This function should be used on empty trees, having a tree root at most.
1222
(see merge and revert functionality for working with existing trees)
1224
Existing files are handled like so:
1226
- Existing bzrdirs take precedence over creating new items. They are
1227
created as '%s.diverted' % name.
1228
- Otherwise, if the content on disk matches the content we are building,
1229
it is silently replaced.
1230
- Otherwise, conflict resolution will move the old file to 'oldname.moved'.
1232
wt.lock_tree_write()
1236
return _build_tree(tree, wt)
1242
def _build_tree(tree, wt):
1243
"""See build_tree."""
1244
if len(wt.inventory) > 1: # more than just a root
1245
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
940
1246
file_trans_id = {}
941
1247
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
942
1248
pp = ProgressPhase("Build phase", 2, top_pb)
1249
if tree.inventory.root is not None:
1250
# This is kind of a hack: we should be altering the root
1251
# as part of the regular tree shape diff logic.
1252
# The conditional test here is to avoid doing an
1253
# expensive operation (flush) every time the root id
1254
# is set within the tree, nor setting the root and thus
1255
# marking the tree as dirty, because we use two different
1256
# idioms here: tree interfaces and inventory interfaces.
1257
if wt.path2id('') != tree.inventory.root.file_id:
1258
wt.set_root_id(tree.inventory.root.file_id)
943
1260
tt = TreeTransform(wt)
946
file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
947
file_ids = topology_sorted_ids(tree)
1264
file_trans_id[wt.get_root_id()] = \
1265
tt.trans_id_tree_file_id(wt.get_root_id())
948
1266
pb = bzrlib.ui.ui_factory.nested_progress_bar()
950
for num, file_id in enumerate(file_ids):
951
pb.update("Building tree", num, len(file_ids))
952
entry = tree.inventory[file_id]
1268
for num, (tree_path, entry) in \
1269
enumerate(tree.inventory.iter_entries_by_dir()):
1270
pb.update("Building tree", num, len(tree.inventory))
953
1271
if entry.parent_id is None:
1274
file_id = entry.file_id
1275
target_path = wt.abspath(tree_path)
1277
kind = file_kind(target_path)
1281
if kind == "directory":
1283
bzrdir.BzrDir.open(target_path)
1284
except errors.NotBranchError:
1288
if (file_id not in divert and
1289
_content_match(tree, entry, file_id, kind,
1291
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1292
if kind == 'directory':
955
1294
if entry.parent_id not in file_trans_id:
956
raise repr(entry.parent_id)
1295
raise AssertionError(
1296
'entry %s parent id %r is not in file_trans_id %r'
1297
% (entry, entry.parent_id, file_trans_id))
957
1298
parent_id = file_trans_id[entry.parent_id]
958
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1299
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1302
new_trans_id = file_trans_id[file_id]
1303
old_parent = tt.trans_id_tree_path(tree_path)
1304
_reparent_children(tt, old_parent, new_trans_id)
1308
divert_trans = set(file_trans_id[f] for f in divert)
1309
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1310
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1311
conflicts = cook_conflicts(raw_conflicts, tt)
1312
for conflict in conflicts:
1315
wt.add_conflicts(conflicts)
1316
except errors.UnsupportedOperation:
966
1321
top_pb.finished()
1325
def _reparent_children(tt, old_parent, new_parent):
1326
for child in tt.iter_tree_children(old_parent):
1327
tt.adjust_path(tt.final_name(child), new_parent, child)
1330
def _content_match(tree, entry, file_id, kind, target_path):
1331
if entry.kind != kind:
1333
if entry.kind == "directory":
1335
if entry.kind == "file":
1336
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1338
elif entry.kind == "symlink":
1339
if tree.get_symlink_target(file_id) == os.readlink(target_path):
1344
def resolve_checkout(tt, conflicts, divert):
1345
new_conflicts = set()
1346
for c_type, conflict in ((c[0], c) for c in conflicts):
1347
# Anything but a 'duplicate' would indicate programmer error
1348
assert c_type == 'duplicate', c_type
1349
# Now figure out which is new and which is old
1350
if tt.new_contents(conflict[1]):
1351
new_file = conflict[1]
1352
old_file = conflict[2]
1354
new_file = conflict[2]
1355
old_file = conflict[1]
1357
# We should only get here if the conflict wasn't completely
1359
final_parent = tt.final_parent(old_file)
1360
if new_file in divert:
1361
new_name = tt.final_name(old_file)+'.diverted'
1362
tt.adjust_path(new_name, final_parent, new_file)
1363
new_conflicts.add((c_type, 'Diverted to',
1364
new_file, old_file))
1366
new_name = tt.final_name(old_file)+'.moved'
1367
tt.adjust_path(new_name, final_parent, old_file)
1368
new_conflicts.add((c_type, 'Moved existing file to',
1369
old_file, new_file))
1370
return new_conflicts
968
1373
def new_by_entry(tt, entry, parent_id, tree):
969
1374
"""Create a new file according to its inventory entry"""
974
1379
executable = tree.is_executable(entry.file_id)
975
1380
return tt.new_file(name, parent_id, contents, entry.file_id,
977
elif kind == 'directory':
978
return tt.new_directory(name, parent_id, entry.file_id)
1382
elif kind in ('directory', 'tree-reference'):
1383
trans_id = tt.new_directory(name, parent_id, entry.file_id)
1384
if kind == 'tree-reference':
1385
tt.set_tree_reference(entry.reference_revision, trans_id)
979
1387
elif kind == 'symlink':
980
1388
target = tree.get_symlink_target(entry.file_id)
981
1389
return tt.new_symlink(name, parent_id, target, entry.file_id)
1391
raise errors.BadFileKindError(name, kind)
983
1393
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
984
1394
"""Create new file contents according to an inventory entry."""
985
1395
if entry.kind == "file":
987
1397
lines = tree.get_file(entry.file_id).readlines()
988
1398
tt.create_file(lines, trans_id, mode_id=mode_id)
989
1399
elif entry.kind == "symlink":
1084
1507
return has_contents, contents_mod, meta_mod
1087
def revert(working_tree, target_tree, filenames, backups=False,
1088
pb=DummyProgress()):
1510
def revert(working_tree, target_tree, filenames, backups=False,
1511
pb=DummyProgress(), change_reporter=None):
1089
1512
"""Revert a working tree's contents to those of a target tree."""
1090
interesting_ids = find_interesting(working_tree, target_tree, filenames)
1091
def interesting(file_id):
1092
return interesting_ids is None or file_id in interesting_ids
1513
target_tree.lock_read()
1094
1514
tt = TreeTransform(working_tree, pb)
1096
merge_modified = working_tree.merge_modified()
1098
def trans_id_file_id(file_id):
1100
return trans_id[file_id]
1102
return tt.trans_id_tree_file_id(file_id)
1104
pp = ProgressPhase("Revert phase", 4, pb)
1106
sorted_interesting = [i for i in topology_sorted_ids(target_tree) if
1108
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1110
by_parent = tt.by_parent()
1111
for id_num, file_id in enumerate(sorted_interesting):
1112
child_pb.update("Reverting file", id_num+1,
1113
len(sorted_interesting))
1114
if file_id not in working_tree.inventory:
1115
entry = target_tree.inventory[file_id]
1116
parent_id = trans_id_file_id(entry.parent_id)
1117
e_trans_id = new_by_entry(tt, entry, parent_id, target_tree)
1118
trans_id[file_id] = e_trans_id
1120
backup_this = backups
1121
if file_id in merge_modified:
1123
del merge_modified[file_id]
1124
change_entry(tt, file_id, working_tree, target_tree,
1125
trans_id_file_id, backup_this, trans_id,
1130
wt_interesting = [i for i in working_tree.inventory if interesting(i)]
1131
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1133
for id_num, file_id in enumerate(wt_interesting):
1134
child_pb.update("New file check", id_num+1,
1135
len(sorted_interesting))
1136
if file_id not in target_tree:
1137
trans_id = tt.trans_id_tree_file_id(file_id)
1138
tt.unversion_file(trans_id)
1139
if file_id in merge_modified:
1140
tt.delete_contents(trans_id)
1141
del merge_modified[file_id]
1516
pp = ProgressPhase("Revert phase", 3, pb)
1518
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1520
merge_modified = _alter_files(working_tree, target_tree, tt,
1521
child_pb, filenames, backups)
1143
1523
child_pb.finished()
1144
1524
pp.next_phase()
1149
1529
child_pb.finished()
1150
1530
conflicts = cook_conflicts(raw_conflicts, tt)
1532
change_reporter = delta._ChangeReporter(
1533
unversioned_filter=working_tree.is_ignored)
1534
delta.report_changes(tt._iter_changes(), change_reporter)
1151
1535
for conflict in conflicts:
1152
1536
warning(conflict)
1153
1537
pp.next_phase()
1155
working_tree.set_merge_modified({})
1539
working_tree.set_merge_modified(merge_modified)
1541
target_tree.unlock()
1159
1544
return conflicts
1162
def resolve_conflicts(tt, pb=DummyProgress()):
1547
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
1549
merge_modified = working_tree.merge_modified()
1550
change_list = target_tree._iter_changes(working_tree,
1551
specific_files=specific_files, pb=pb)
1552
if target_tree.inventory.root is None:
1558
for id_num, (file_id, path, changed_content, versioned, parent, name,
1559
kind, executable) in enumerate(change_list):
1560
if skip_root and file_id[0] is not None and parent[0] is None:
1562
trans_id = tt.trans_id_file_id(file_id)
1565
keep_content = False
1566
if kind[0] == 'file' and (backups or kind[1] is None):
1567
wt_sha1 = working_tree.get_file_sha1(file_id)
1568
if merge_modified.get(file_id) != wt_sha1:
1569
# acquire the basis tree lazily to prevent the
1570
# expense of accessing it when it's not needed ?
1571
# (Guessing, RBC, 200702)
1572
if basis_tree is None:
1573
basis_tree = working_tree.basis_tree()
1574
basis_tree.lock_read()
1575
if file_id in basis_tree:
1576
if wt_sha1 != basis_tree.get_file_sha1(file_id):
1578
elif kind[1] is None and not versioned[1]:
1580
if kind[0] is not None:
1581
if not keep_content:
1582
tt.delete_contents(trans_id)
1583
elif kind[1] is not None:
1584
parent_trans_id = tt.trans_id_file_id(parent[0])
1585
by_parent = tt.by_parent()
1586
backup_name = _get_backup_name(name[0], by_parent,
1587
parent_trans_id, tt)
1588
tt.adjust_path(backup_name, parent_trans_id, trans_id)
1589
new_trans_id = tt.create_path(name[0], parent_trans_id)
1590
if versioned == (True, True):
1591
tt.unversion_file(trans_id)
1592
tt.version_file(file_id, new_trans_id)
1593
# New contents should have the same unix perms as old
1596
trans_id = new_trans_id
1597
if kind[1] == 'directory':
1598
tt.create_directory(trans_id)
1599
elif kind[1] == 'symlink':
1600
tt.create_symlink(target_tree.get_symlink_target(file_id),
1602
elif kind[1] == 'file':
1603
tt.create_file(target_tree.get_file_lines(file_id),
1605
if basis_tree is None:
1606
basis_tree = working_tree.basis_tree()
1607
basis_tree.lock_read()
1608
new_sha1 = target_tree.get_file_sha1(file_id)
1609
if (file_id in basis_tree and new_sha1 ==
1610
basis_tree.get_file_sha1(file_id)):
1611
if file_id in merge_modified:
1612
del merge_modified[file_id]
1614
merge_modified[file_id] = new_sha1
1616
# preserve the execute bit when backing up
1617
if keep_content and executable[0] == executable[1]:
1618
tt.set_executability(executable[1], trans_id)
1620
assert kind[1] is None
1621
if versioned == (False, True):
1622
tt.version_file(file_id, trans_id)
1623
if versioned == (True, False):
1624
tt.unversion_file(trans_id)
1625
if (name[1] is not None and
1626
(name[0] != name[1] or parent[0] != parent[1])):
1628
name[1], tt.trans_id_file_id(parent[1]), trans_id)
1629
if executable[0] != executable[1] and kind[1] == "file":
1630
tt.set_executability(executable[1], trans_id)
1632
if basis_tree is not None:
1634
return merge_modified
1637
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
1163
1638
"""Make many conflict-resolution attempts, but die if they fail"""
1639
if pass_func is None:
1640
pass_func = conflict_pass
1164
1641
new_conflicts = set()
1166
1643
for n in range(10):