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, lexists,
25
from bzrlib.osutils import file_kind, supports_executable, pathjoin
27
26
from bzrlib.progress import DummyProgress, ProgressPhase
28
27
from bzrlib.trace import mutter, warning
287
286
os.symlink(target, self._limbo_name(trans_id))
288
287
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):
290
300
def cancel_creation(self, trans_id):
291
301
"""Cancel the creation of new file contents."""
292
302
del self._new_contents[trans_id]
293
delete_any(self._limbo_name(trans_id))
303
self.delete_any(self._limbo_name(trans_id))
295
305
def delete_contents(self, trans_id):
296
306
"""Schedule the contents of a path entry for deletion"""
455
465
# ensure all children of all existent parents are known
456
466
# all children of non-existent parents are known, by definition.
457
467
self._add_tree_children()
458
by_parent = self.by_parent()
468
by_parent = self._by_parent()
459
469
conflicts.extend(self._unversioned_parents(by_parent))
460
470
conflicts.extend(self._parent_loops())
461
471
conflicts.extend(self._duplicate_entries(by_parent))
472
482
Active parents are those which gain children, and those which are
473
483
removed. This is a necessary first step in detecting conflicts.
475
parents = self.by_parent().keys()
485
parents = self._by_parent().keys()
476
486
parents.extend([t for t in self._removed_contents if
477
487
self.tree_kind(t) == 'directory'])
478
488
for trans_id in self._removed_id:
505
515
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?
532
517
def _parent_loops(self):
533
518
"""No entry should be its own ancestor"""
620
605
if name == last_name:
621
606
conflicts.append(('duplicate', 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
609
last_trans_id = trans_id
633
612
def _duplicate_ids(self):
905
884
def build_tree(tree, wt):
906
885
"""Create working tree for a branch, using a Transaction."""
907
886
file_trans_id = {}
908
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
909
pp = ProgressPhase("Build phase", 2, top_pb)
910
887
tt = TreeTransform(wt)
913
889
file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
914
890
file_ids = topology_sorted_ids(tree)
915
pb = bzrlib.ui.ui_factory.nested_progress_bar()
917
for num, file_id in enumerate(file_ids):
918
pb.update("Building tree", num, len(file_ids))
919
entry = tree.inventory[file_id]
920
if entry.parent_id is None:
922
if entry.parent_id not in file_trans_id:
923
raise repr(entry.parent_id)
924
parent_id = file_trans_id[entry.parent_id]
925
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
891
for file_id in file_ids:
892
entry = tree.inventory[file_id]
893
if entry.parent_id is None:
895
if entry.parent_id not in file_trans_id:
896
raise repr(entry.parent_id)
897
parent_id = file_trans_id[entry.parent_id]
898
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id, tree)
935
903
def new_by_entry(tt, entry, parent_id, tree):
936
904
"""Create a new file according to its inventory entry"""
972
940
interesting_ids = set()
973
941
for tree_path in filenames:
975
942
for tree in (working_tree, target_tree):
976
944
file_id = tree.inventory.path2id(tree_path)
977
945
if file_id is not None:
978
946
interesting_ids.add(file_id)
979
947
not_found = False
981
raise NotVersionedError(path=tree_path)
949
raise NotVersionedError(path=tree_path)
982
950
return interesting_ids
985
953
def change_entry(tt, file_id, working_tree, target_tree,
986
trans_id_file_id, backups, trans_id, by_parent):
954
trans_id_file_id, backups, trans_id):
987
955
"""Replace a file_id's contents with those from a target tree."""
988
956
e_trans_id = trans_id_file_id(file_id)
989
957
entry = target_tree.inventory[file_id]
996
964
tt.delete_contents(e_trans_id)
998
966
parent_trans_id = trans_id_file_id(entry.parent_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)
967
tt.adjust_path(entry.name+"~", parent_trans_id, e_trans_id)
1002
968
tt.unversion_file(e_trans_id)
1003
969
e_trans_id = tt.create_path(entry.name, parent_trans_id)
1004
970
tt.version_file(file_id, e_trans_id)
1022
988
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):
1036
991
def _entry_changes(file_id, entry, working_tree):
1037
992
"""Determine in which ways the inventory entry has changed.
1103
1057
backup_this = False
1104
1058
del merge_modified[file_id]
1105
1059
change_entry(tt, file_id, working_tree, target_tree,
1106
trans_id_file_id, backup_this, trans_id,
1060
trans_id_file_id, backup_this, trans_id)
1109
1062
child_pb.finished()
1110
1063
pp.next_phase()
1128
1081
raw_conflicts = resolve_conflicts(tt, child_pb)
1130
1083
child_pb.finished()
1131
conflicts = cook_conflicts(raw_conflicts, tt)
1132
for conflict in conflicts:
1084
for line in conflicts_strings(cook_conflicts(raw_conflicts, tt)):
1134
1086
pp.next_phase()
1136
1088
working_tree.set_merge_modified({})
1143
1094
def resolve_conflicts(tt, pb=DummyProgress()):
1200
1151
def cook_conflicts(raw_conflicts, tt):
1201
1152
"""Generate a list of cooked conflicts, sorted by file path"""
1202
1153
def key(conflict):
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
1154
if conflict[2] is not None:
1155
return conflict[2], conflict[0]
1156
elif len(conflict) == 6:
1157
return conflict[4], conflict[0]
1208
return None, conflict.typestring
1159
return None, conflict[0]
1210
1161
return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1212
1163
def iter_cook_conflicts(raw_conflicts, tt):
1213
from bzrlib.conflicts import Conflict
1164
cooked_conflicts = []
1214
1165
fp = FinalPaths(tt)
1215
1166
for conflict in raw_conflicts:
1216
1167
c_type = conflict[0]
1218
1169
modified_path = fp.get_path(conflict[2])
1219
1170
modified_id = tt.final_file_id(conflict[2])
1220
1171
if len(conflict) == 3:
1221
yield Conflict.factory(c_type, action=action, path=modified_path,
1222
file_id=modified_id)
1172
yield c_type, action, modified_path, modified_id
1225
1174
conflicting_path = fp.get_path(conflict[3])
1226
1175
conflicting_id = tt.final_file_id(conflict[3])
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)
1176
yield (c_type, action, modified_path, modified_id,
1177
conflicting_path, conflicting_id)
1180
def conflicts_strings(conflicts):
1181
"""Generate strings for the provided conflicts"""
1182
for conflict in conflicts:
1183
conflict_type = conflict[0]
1184
if conflict_type == 'text conflict':
1185
yield 'Text conflict in %s' % conflict[2]
1186
elif conflict_type == 'contents conflict':
1187
yield 'Contents conflict in %s' % conflict[2]
1188
elif conflict_type == 'path conflict':
1189
yield 'Path conflict: %s / %s' % conflict[2:]
1190
elif conflict_type == 'duplicate id':
1191
vals = (conflict[4], conflict[1], conflict[2])
1192
yield 'Conflict adding id to %s. %s %s.' % vals
1193
elif conflict_type == 'duplicate':
1194
vals = (conflict[4], conflict[1], conflict[2])
1195
yield 'Conflict adding file %s. %s %s.' % vals
1196
elif conflict_type == 'parent loop':
1197
vals = (conflict[4], conflict[2], conflict[1])
1198
yield 'Conflict moving %s into %s. %s.' % vals
1199
elif conflict_type == 'unversioned parent':
1200
vals = (conflict[2], conflict[1])
1201
yield 'Conflict adding versioned files to %s. %s.' % vals
1202
elif conflict_type == 'missing parent':
1203
vals = (conflict[2], conflict[1])
1204
yield 'Conflict adding files to %s. %s.' % vals