22
22
ReusingTransform, NotVersionedError, CantMoveRoot,
23
23
ExistingLimbo, ImmortalLimbo)
24
24
from bzrlib.inventory import InventoryEntry
25
from bzrlib.osutils import file_kind, supports_executable, pathjoin
25
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
26
27
from bzrlib.progress import DummyProgress, ProgressPhase
27
28
from bzrlib.trace import mutter, warning
286
287
os.symlink(target, self._limbo_name(trans_id))
287
288
unique_add(self._new_contents, trans_id, 'symlink')
290
def delete_any(full_path):
291
"""Delete a file or directory."""
295
# We may be renaming a dangling inventory id
296
if e.errno not in (errno.EISDIR, errno.EACCES, errno.EPERM):
300
290
def cancel_creation(self, trans_id):
301
291
"""Cancel the creation of new file contents."""
302
292
del self._new_contents[trans_id]
303
self.delete_any(self._limbo_name(trans_id))
293
delete_any(self._limbo_name(trans_id))
305
295
def delete_contents(self, trans_id):
306
296
"""Schedule the contents of a path entry for deletion"""
465
455
# ensure all children of all existent parents are known
466
456
# all children of non-existent parents are known, by definition.
467
457
self._add_tree_children()
468
by_parent = self._by_parent()
458
by_parent = self.by_parent()
469
459
conflicts.extend(self._unversioned_parents(by_parent))
470
460
conflicts.extend(self._parent_loops())
471
461
conflicts.extend(self._duplicate_entries(by_parent))
482
472
Active parents are those which gain children, and those which are
483
473
removed. This is a necessary first step in detecting conflicts.
485
parents = self._by_parent().keys()
475
parents = self.by_parent().keys()
486
476
parents.extend([t for t in self._removed_contents if
487
477
self.tree_kind(t) == 'directory'])
488
478
for trans_id in self._removed_id:
515
505
yield self.trans_id_tree_path(childpath)
507
def has_named_child(self, by_parent, parent_id, name):
509
children = by_parent[parent_id]
512
for child in children:
513
if self.final_name(child) == name:
516
path = self._tree_id_paths[parent_id]
519
childpath = joinpath(path, name)
520
child_id = self._tree_path_ids.get(childpath)
522
return lexists(self._tree.abspath(childpath))
524
if tt.final_parent(child_id) != parent_id:
526
if child_id in tt._removed_contents:
527
# XXX What about dangling file-ids?
517
532
def _parent_loops(self):
518
533
"""No entry should be its own ancestor"""
605
620
if name == last_name:
606
621
conflicts.append(('duplicate', last_trans_id, trans_id,
609
last_trans_id = trans_id
624
kind = self.final_kind(trans_id)
627
file_id = self.final_file_id(trans_id)
628
if kind is not None or file_id is not None:
630
last_trans_id = trans_id
612
633
def _duplicate_ids(self):
951
972
interesting_ids = set()
952
973
for tree_path in filenames:
953
975
for tree in (working_tree, target_tree):
955
976
file_id = tree.inventory.path2id(tree_path)
956
977
if file_id is not None:
957
978
interesting_ids.add(file_id)
958
979
not_found = False
960
raise NotVersionedError(path=tree_path)
981
raise NotVersionedError(path=tree_path)
961
982
return interesting_ids
964
985
def change_entry(tt, file_id, working_tree, target_tree,
965
trans_id_file_id, backups, trans_id):
986
trans_id_file_id, backups, trans_id, by_parent):
966
987
"""Replace a file_id's contents with those from a target tree."""
967
988
e_trans_id = trans_id_file_id(file_id)
968
989
entry = target_tree.inventory[file_id]
975
996
tt.delete_contents(e_trans_id)
977
998
parent_trans_id = trans_id_file_id(entry.parent_id)
978
tt.adjust_path(entry.name+"~", parent_trans_id, e_trans_id)
999
backup_name = get_backup_name(entry, by_parent,
1000
parent_trans_id, tt)
1001
tt.adjust_path(backup_name, parent_trans_id, e_trans_id)
979
1002
tt.unversion_file(e_trans_id)
980
1003
e_trans_id = tt.create_path(entry.name, parent_trans_id)
981
1004
tt.version_file(file_id, e_trans_id)
999
1022
tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
1025
def get_backup_name(entry, by_parent, parent_trans_id, tt):
1026
"""Produce a backup-style name that appears to be available"""
1030
yield "%s.~%d~" % (entry.name, counter)
1032
for name in name_gen():
1033
if not tt.has_named_child(by_parent, parent_trans_id, name):
1002
1036
def _entry_changes(file_id, entry, working_tree):
1003
1037
"""Determine in which ways the inventory entry has changed.
1068
1103
backup_this = False
1069
1104
del merge_modified[file_id]
1070
1105
change_entry(tt, file_id, working_tree, target_tree,
1071
trans_id_file_id, backup_this, trans_id)
1106
trans_id_file_id, backup_this, trans_id,
1073
1109
child_pb.finished()
1074
1110
pp.next_phase()
1092
1128
raw_conflicts = resolve_conflicts(tt, child_pb)
1094
1130
child_pb.finished()
1095
for line in conflicts_strings(cook_conflicts(raw_conflicts, tt)):
1131
conflicts = cook_conflicts(raw_conflicts, tt)
1132
for conflict in conflicts:
1097
1134
pp.next_phase()
1099
1136
working_tree.set_merge_modified({})
1105
1143
def resolve_conflicts(tt, pb=DummyProgress()):
1162
1200
def cook_conflicts(raw_conflicts, tt):
1163
1201
"""Generate a list of cooked conflicts, sorted by file path"""
1164
1202
def key(conflict):
1165
if conflict[2] is not None:
1166
return conflict[2], conflict[0]
1167
elif len(conflict) == 6:
1168
return conflict[4], conflict[0]
1203
if conflict.path is not None:
1204
return conflict.path, conflict.typestring
1205
elif getattr(conflict, "conflict_path", None) is not None:
1206
return conflict.conflict_path, conflict.typestring
1170
return None, conflict[0]
1208
return None, conflict.typestring
1172
1210
return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1174
1212
def iter_cook_conflicts(raw_conflicts, tt):
1175
cooked_conflicts = []
1213
from bzrlib.conflicts import Conflict
1176
1214
fp = FinalPaths(tt)
1177
1215
for conflict in raw_conflicts:
1178
1216
c_type = conflict[0]
1180
1218
modified_path = fp.get_path(conflict[2])
1181
1219
modified_id = tt.final_file_id(conflict[2])
1182
1220
if len(conflict) == 3:
1183
yield c_type, action, modified_path, modified_id
1221
yield Conflict.factory(c_type, action=action, path=modified_path,
1222
file_id=modified_id)
1185
1225
conflicting_path = fp.get_path(conflict[3])
1186
1226
conflicting_id = tt.final_file_id(conflict[3])
1187
yield (c_type, action, modified_path, modified_id,
1188
conflicting_path, conflicting_id)
1191
def conflicts_strings(conflicts):
1192
"""Generate strings for the provided conflicts"""
1193
for conflict in conflicts:
1194
conflict_type = conflict[0]
1195
if conflict_type == 'text conflict':
1196
yield 'Text conflict in %s' % conflict[2]
1197
elif conflict_type == 'contents conflict':
1198
yield 'Contents conflict in %s' % conflict[2]
1199
elif conflict_type == 'path conflict':
1200
yield 'Path conflict: %s / %s' % conflict[2:]
1201
elif conflict_type == 'duplicate id':
1202
vals = (conflict[4], conflict[1], conflict[2])
1203
yield 'Conflict adding id to %s. %s %s.' % vals
1204
elif conflict_type == 'duplicate':
1205
vals = (conflict[4], conflict[1], conflict[2])
1206
yield 'Conflict adding file %s. %s %s.' % vals
1207
elif conflict_type == 'parent loop':
1208
vals = (conflict[4], conflict[2], conflict[1])
1209
yield 'Conflict moving %s into %s. %s.' % vals
1210
elif conflict_type == 'unversioned parent':
1211
vals = (conflict[2], conflict[1])
1212
yield 'Conflict adding versioned files to %s. %s.' % vals
1213
elif conflict_type == 'missing parent':
1214
vals = (conflict[2], conflict[1])
1215
yield 'Conflict adding files to %s. %s.' % vals
1227
yield Conflict.factory(c_type, action=action, path=modified_path,
1228
file_id=modified_id,
1229
conflict_path=conflicting_path,
1230
conflict_file_id=conflicting_id)