944
935
file_ids.sort(key=tree.id2path)
948
938
def build_tree(tree, wt):
949
"""Create working tree for a branch, using a TreeTransform.
951
This function should be used on empty trees, having a tree root at most.
952
(see merge and revert functionality for working with existing trees)
954
Existing files are handled like so:
956
- Existing bzrdirs take precedence over creating new items. They are
957
created as '%s.diverted' % name.
958
- Otherwise, if the content on disk matches the content we are building,
959
it is silently replaced.
960
- Otherwise, conflict resolution will move the old file to 'oldname.moved'.
962
if len(wt.inventory) > 1: # more than just a root
963
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
939
"""Create working tree for a branch, using a Transaction."""
964
940
file_trans_id = {}
965
941
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
966
942
pp = ProgressPhase("Build phase", 2, top_pb)
967
if tree.inventory.root is not None:
968
wt.set_root_id(tree.inventory.root.file_id)
969
943
tt = TreeTransform(wt)
973
file_trans_id[wt.get_root_id()] = \
974
tt.trans_id_tree_file_id(wt.get_root_id())
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)
975
948
pb = bzrlib.ui.ui_factory.nested_progress_bar()
977
for num, (tree_path, entry) in \
978
enumerate(tree.inventory.iter_entries_by_dir()):
979
pb.update("Building tree", num, len(tree.inventory))
950
for num, file_id in enumerate(file_ids):
951
pb.update("Building tree", num, len(file_ids))
952
entry = tree.inventory[file_id]
980
953
if entry.parent_id is None:
983
file_id = entry.file_id
984
target_path = wt.abspath(tree_path)
986
kind = file_kind(target_path)
990
if kind == "directory":
992
bzrdir.BzrDir.open(target_path)
993
except errors.NotBranchError:
997
if (file_id not in divert and
998
_content_match(tree, entry, file_id, kind,
1000
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1001
if kind == 'directory':
1003
955
if entry.parent_id not in file_trans_id:
1004
956
raise repr(entry.parent_id)
1005
957
parent_id = file_trans_id[entry.parent_id]
1006
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
958
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1009
new_trans_id = file_trans_id[file_id]
1010
old_parent = tt.trans_id_tree_path(tree_path)
1011
_reparent_children(tt, old_parent, new_trans_id)
1015
divert_trans = set(file_trans_id[f] for f in divert)
1016
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1017
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1018
conflicts = cook_conflicts(raw_conflicts, tt)
1019
for conflict in conflicts:
1022
wt.add_conflicts(conflicts)
1023
except errors.UnsupportedOperation:
1028
966
top_pb.finished()
1031
def _reparent_children(tt, old_parent, new_parent):
1032
for child in tt.iter_tree_children(old_parent):
1033
tt.adjust_path(tt.final_name(child), new_parent, child)
1036
def _content_match(tree, entry, file_id, kind, target_path):
1037
if entry.kind != kind:
1039
if entry.kind == "directory":
1041
if entry.kind == "file":
1042
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1044
elif entry.kind == "symlink":
1045
if tree.get_symlink_target(file_id) == os.readlink(target_path):
1050
def resolve_checkout(tt, conflicts, divert):
1051
new_conflicts = set()
1052
for c_type, conflict in ((c[0], c) for c in conflicts):
1053
# Anything but a 'duplicate' would indicate programmer error
1054
assert c_type == 'duplicate', c_type
1055
# Now figure out which is new and which is old
1056
if tt.new_contents(conflict[1]):
1057
new_file = conflict[1]
1058
old_file = conflict[2]
1060
new_file = conflict[2]
1061
old_file = conflict[1]
1063
# We should only get here if the conflict wasn't completely
1065
final_parent = tt.final_parent(old_file)
1066
if new_file in divert:
1067
new_name = tt.final_name(old_file)+'.diverted'
1068
tt.adjust_path(new_name, final_parent, new_file)
1069
new_conflicts.add((c_type, 'Diverted to',
1070
new_file, old_file))
1072
new_name = tt.final_name(old_file)+'.moved'
1073
tt.adjust_path(new_name, final_parent, old_file)
1074
new_conflicts.add((c_type, 'Moved existing file to',
1075
old_file, new_file))
1076
return new_conflicts
1079
968
def new_by_entry(tt, entry, parent_id, tree):
1080
969
"""Create a new file according to its inventory entry"""
1081
970
name = entry.name
1157
1046
def get_backup_name(entry, by_parent, parent_trans_id, tt):
1158
return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
1161
def _get_backup_name(name, by_parent, parent_trans_id, tt):
1162
1047
"""Produce a backup-style name that appears to be available"""
1163
1048
def name_gen():
1166
yield "%s.~%d~" % (name, counter)
1051
yield "%s.~%d~" % (entry.name, counter)
1168
for new_name in name_gen():
1169
if not tt.has_named_child(by_parent, parent_trans_id, new_name):
1053
for name in name_gen():
1054
if not tt.has_named_child(by_parent, parent_trans_id, name):
1173
1057
def _entry_changes(file_id, entry, working_tree):
1174
1058
"""Determine in which ways the inventory entry has changed.
1200
1087
def revert(working_tree, target_tree, filenames, backups=False,
1201
pb=DummyProgress(), change_reporter=None):
1088
pb=DummyProgress()):
1202
1089
"""Revert a working tree's contents to those of a target tree."""
1203
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
1204
1094
tt = TreeTransform(working_tree, pb)
1206
pp = ProgressPhase("Revert phase", 3, pb)
1208
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1210
_alter_files(working_tree, target_tree, tt, child_pb,
1211
interesting_ids, backups, change_reporter)
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]
1213
1143
child_pb.finished()
1214
1144
pp.next_phase()
1229
1159
return conflicts
1232
def _alter_files(working_tree, target_tree, tt, pb, interesting_ids, backups,
1234
from bzrlib import delta
1235
merge_modified = working_tree.merge_modified()
1236
change_list = list(target_tree._iter_changes(working_tree,
1237
specific_file_ids=interesting_ids, pb=pb))
1238
if target_tree.inventory.root is None:
1244
change_reporter = delta.ChangeReporter(working_tree.inventory)
1245
delta.report_changes(change_list, change_reporter)
1246
for id_num, (file_id, path, changed_content, versioned, parent, name, kind,
1247
executable) in enumerate(change_list):
1248
if skip_root and file_id[0] is not None and parent[0] is None:
1250
trans_id = tt.trans_id_file_id(file_id)
1253
keep_content = False
1254
if kind[0] == 'file' and (backups or kind[1] is None):
1255
wt_sha1 = working_tree.get_file_sha1(file_id)
1256
if merge_modified.get(file_id) != wt_sha1:
1257
if basis_tree is None:
1258
basis_tree = working_tree.basis_tree()
1259
if file_id in basis_tree:
1260
if wt_sha1 != basis_tree.get_file_sha1(file_id):
1262
elif kind[1] is None and not versioned[1]:
1264
if kind[0] is not None:
1265
if not keep_content:
1266
tt.delete_contents(trans_id)
1267
elif kind[1] is not None:
1268
parent_trans_id = tt.trans_id_file_id(parent[0])
1269
by_parent = tt.by_parent()
1270
backup_name = _get_backup_name(name[0], by_parent,
1271
parent_trans_id, tt)
1272
tt.adjust_path(backup_name, parent_trans_id, trans_id)
1273
new_trans_id = tt.create_path(name[0], parent_trans_id)
1274
if versioned == (True, True):
1275
tt.unversion_file(trans_id)
1276
tt.version_file(file_id, new_trans_id)
1277
# New contents should have the same unix perms as old
1280
trans_id = new_trans_id
1281
if kind[1] == 'directory':
1282
tt.create_directory(trans_id)
1283
elif kind[1] == 'symlink':
1284
tt.create_symlink(target_tree.get_symlink_target(file_id),
1286
elif kind[1] == 'file':
1287
tt.create_file(target_tree.get_file_lines(file_id),
1289
# preserve the execute bit when backing up
1290
if keep_content and executable[0] == executable[1]:
1291
tt.set_executability(executable[1], trans_id)
1293
assert kind[1] is None
1294
if versioned == (False, True):
1295
tt.version_file(file_id, trans_id)
1296
if versioned == (True, False):
1297
tt.unversion_file(trans_id)
1298
if (name[1] is not None and
1299
(name[0] != name[1] or parent[0] != parent[1])):
1300
tt.adjust_path(name[1], tt.trans_id_file_id(parent[1]), trans_id)
1301
if executable[0] != executable[1] and kind[1] == "file":
1302
tt.set_executability(executable[1], trans_id)
1305
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
1162
def resolve_conflicts(tt, pb=DummyProgress()):
1306
1163
"""Make many conflict-resolution attempts, but die if they fail"""
1307
if pass_func is None:
1308
pass_func = conflict_pass
1309
1164
new_conflicts = set()
1311
1166
for n in range(10):