904
924
self.create_symlink(target, trans_id)
927
def _affected_ids(self):
928
"""Return the set of transform ids affected by the transform"""
929
trans_ids = set(self._removed_id)
930
trans_ids.update(self._new_id.keys())
931
trans_ids.update(self._removed_contents)
932
trans_ids.update(self._new_contents.keys())
933
trans_ids.update(self._new_executability.keys())
934
trans_ids.update(self._new_name.keys())
935
trans_ids.update(self._new_parent.keys())
938
def _get_file_id_maps(self):
939
"""Return mapping of file_ids to trans_ids in the to and from states"""
940
trans_ids = self._affected_ids()
943
# Build up two dicts: trans_ids associated with file ids in the
944
# FROM state, vs the TO state.
945
for trans_id in trans_ids:
946
from_file_id = self.tree_file_id(trans_id)
947
if from_file_id is not None:
948
from_trans_ids[from_file_id] = trans_id
949
to_file_id = self.final_file_id(trans_id)
950
if to_file_id is not None:
951
to_trans_ids[to_file_id] = trans_id
952
return from_trans_ids, to_trans_ids
954
def _from_file_data(self, from_trans_id, from_versioned, file_id):
955
"""Get data about a file in the from (tree) state
957
Return a (name, parent, kind, executable) tuple
959
from_path = self._tree_id_paths.get(from_trans_id)
961
# get data from working tree if versioned
962
from_entry = self._tree.inventory[file_id]
963
from_name = from_entry.name
964
from_parent = from_entry.parent_id
967
if from_path is None:
968
# File does not exist in FROM state
972
# File exists, but is not versioned. Have to use path-
974
from_name = os.path.basename(from_path)
975
tree_parent = self.get_tree_parent(from_trans_id)
976
from_parent = self.tree_file_id(tree_parent)
977
if from_path is not None:
978
from_kind, from_executable, from_stats = \
979
self._tree._comparison_data(from_entry, from_path)
982
from_executable = False
983
return from_name, from_parent, from_kind, from_executable
985
def _to_file_data(self, to_trans_id, from_trans_id, from_executable):
986
"""Get data about a file in the to (target) state
988
Return a (name, parent, kind, executable) tuple
990
to_name = self.final_name(to_trans_id)
992
to_kind = self.final_kind(to_trans_id)
995
to_parent = self.final_file_id(self.final_parent(to_trans_id))
996
if to_trans_id in self._new_executability:
997
to_executable = self._new_executability[to_trans_id]
998
elif to_trans_id == from_trans_id:
999
to_executable = from_executable
1001
to_executable = False
1002
return to_name, to_parent, to_kind, to_executable
1004
def _iter_changes(self):
1005
"""Produce output in the same format as Tree._iter_changes.
1007
Will produce nonsensical results if invoked while inventory/filesystem
1008
conflicts (as reported by TreeTransform.find_conflicts()) are present.
1010
This reads the Transform, but only reproduces changes involving a
1011
file_id. Files that are not versioned in either of the FROM or TO
1012
states are not reflected.
1014
final_paths = FinalPaths(self)
1015
from_trans_ids, to_trans_ids = self._get_file_id_maps()
1017
# Now iterate through all active file_ids
1018
for file_id in set(from_trans_ids.keys() + to_trans_ids.keys()):
1020
from_trans_id = from_trans_ids.get(file_id)
1021
# find file ids, and determine versioning state
1022
if from_trans_id is None:
1023
from_versioned = False
1024
from_trans_id = to_trans_ids[file_id]
1026
from_versioned = True
1027
to_trans_id = to_trans_ids.get(file_id)
1028
if to_trans_id is None:
1029
to_versioned = False
1030
to_trans_id = from_trans_id
1034
from_name, from_parent, from_kind, from_executable = \
1035
self._from_file_data(from_trans_id, from_versioned, file_id)
1037
to_name, to_parent, to_kind, to_executable = \
1038
self._to_file_data(to_trans_id, from_trans_id, from_executable)
1040
if not from_versioned:
1043
from_path = self._tree_id_paths.get(from_trans_id)
1044
if not to_versioned:
1047
to_path = final_paths.get_path(to_trans_id)
1048
if from_kind != to_kind:
1050
elif to_kind in ('file' or 'symlink') and (
1051
to_trans_id != from_trans_id or
1052
to_trans_id in self._new_contents):
1054
if (not modified and from_versioned == to_versioned and
1055
from_parent==to_parent and from_name == to_name and
1056
from_executable == to_executable):
1058
results.append((file_id, (from_path, to_path), modified,
1059
(from_versioned, to_versioned),
1060
(from_parent, to_parent),
1061
(from_name, to_name),
1062
(from_kind, to_kind),
1063
(from_executable, to_executable)))
1064
return iter(sorted(results, key=lambda x:x[1]))
907
1067
def joinpath(parent, child):
908
1068
"""Join tree-relative paths, handling the tree root specially"""
909
1069
if parent is None or parent == "":
1198
1397
return has_contents, contents_mod, meta_mod
1201
def revert(working_tree, target_tree, filenames, backups=False,
1202
pb=DummyProgress()):
1400
def revert(working_tree, target_tree, filenames, backups=False,
1401
pb=DummyProgress(), change_reporter=None):
1203
1402
"""Revert a working tree's contents to those of a target tree."""
1204
interesting_ids = find_interesting(working_tree, target_tree, filenames)
1403
target_tree.lock_read()
1205
1404
tt = TreeTransform(working_tree, pb)
1207
1406
pp = ProgressPhase("Revert phase", 3, pb)
1208
1407
pp.next_phase()
1209
1408
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1211
_alter_files(working_tree, target_tree, tt, child_pb,
1212
interesting_ids, backups)
1410
_alter_files(working_tree, target_tree, tt, child_pb,
1214
1413
child_pb.finished()
1215
1414
pp.next_phase()
1220
1419
child_pb.finished()
1221
1420
conflicts = cook_conflicts(raw_conflicts, tt)
1422
change_reporter = delta.ChangeReporter(
1423
unversioned_filter=working_tree.is_ignored)
1424
delta.report_changes(tt._iter_changes(), change_reporter)
1222
1425
for conflict in conflicts:
1223
1426
warning(conflict)
1224
1427
pp.next_phase()
1226
1429
working_tree.set_merge_modified({})
1431
target_tree.unlock()
1230
1434
return conflicts
1233
def _alter_files(working_tree, target_tree, tt, pb, interesting_ids, backups):
1437
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
1234
1439
merge_modified = working_tree.merge_modified()
1235
iterator = target_tree._iter_changes(working_tree,
1236
specific_file_ids=interesting_ids,
1440
change_list = target_tree._iter_changes(working_tree,
1441
specific_files=specific_files, pb=pb)
1238
1442
if target_tree.inventory.root is None:
1239
1443
skip_root = True
1241
1445
skip_root = False
1242
1446
basis_tree = None
1243
for id_num, (file_id, path, changed_content, versioned, parent, name, kind,
1244
executable) in enumerate(iterator):
1245
if skip_root and file_id[0] is not None and parent[0] is None:
1247
trans_id = tt.trans_id_file_id(file_id)
1250
keep_content = False
1251
if kind[0] == 'file' and (backups or kind[1] is None):
1252
wt_sha1 = working_tree.get_file_sha1(file_id)
1253
if merge_modified.get(file_id) != wt_sha1:
1254
if basis_tree is None:
1255
basis_tree = working_tree.basis_tree()
1256
if file_id in basis_tree:
1257
if wt_sha1 != basis_tree.get_file_sha1(file_id):
1448
for id_num, (file_id, path, changed_content, versioned, parent, name,
1449
kind, executable) in enumerate(change_list):
1450
if skip_root and file_id[0] is not None and parent[0] is None:
1452
trans_id = tt.trans_id_file_id(file_id)
1455
keep_content = False
1456
if kind[0] == 'file' and (backups or kind[1] is None):
1457
wt_sha1 = working_tree.get_file_sha1(file_id)
1458
if merge_modified.get(file_id) != wt_sha1:
1459
# acquire the basis tree lazyily to prevent the expense
1460
# of accessing it when its not needed ? (Guessing, RBC,
1462
if basis_tree is None:
1463
basis_tree = working_tree.basis_tree()
1464
basis_tree.lock_read()
1465
if file_id in basis_tree:
1466
if wt_sha1 != basis_tree.get_file_sha1(file_id):
1468
elif kind[1] is None and not versioned[1]:
1258
1469
keep_content = True
1259
elif kind[1] is None and not versioned[1]:
1261
if kind[0] is not None:
1262
if not keep_content:
1263
tt.delete_contents(trans_id)
1264
elif kind[1] is not None:
1265
parent_trans_id = tt.trans_id_file_id(parent[0])
1266
by_parent = tt.by_parent()
1267
backup_name = _get_backup_name(name[0], by_parent,
1268
parent_trans_id, tt)
1269
tt.adjust_path(backup_name, parent_trans_id, trans_id)
1270
new_trans_id = tt.create_path(name[0], parent_trans_id)
1271
if versioned == (True, True):
1272
tt.unversion_file(trans_id)
1273
tt.version_file(file_id, new_trans_id)
1274
# New contents should have the same unix perms as old
1277
trans_id = new_trans_id
1278
if kind[1] == 'directory':
1279
tt.create_directory(trans_id)
1280
elif kind[1] == 'symlink':
1281
tt.create_symlink(target_tree.get_symlink_target(file_id),
1283
elif kind[1] == 'file':
1284
tt.create_file(target_tree.get_file_lines(file_id),
1286
# preserve the execute bit when backing up
1287
if keep_content and executable[0] == executable[1]:
1288
tt.set_executability(executable[1], trans_id)
1290
assert kind[1] is None
1291
if versioned == (False, True):
1292
tt.version_file(file_id, trans_id)
1293
if versioned == (True, False):
1294
tt.unversion_file(trans_id)
1295
if (name[1] is not None and
1296
(name[0] != name[1] or parent[0] != parent[1])):
1297
tt.adjust_path(name[1], tt.trans_id_file_id(parent[1]), trans_id)
1298
if executable[0] != executable[1] and kind[1] == "file":
1299
tt.set_executability(executable[1], trans_id)
1470
if kind[0] is not None:
1471
if not keep_content:
1472
tt.delete_contents(trans_id)
1473
elif kind[1] is not None:
1474
parent_trans_id = tt.trans_id_file_id(parent[0])
1475
by_parent = tt.by_parent()
1476
backup_name = _get_backup_name(name[0], by_parent,
1477
parent_trans_id, tt)
1478
tt.adjust_path(backup_name, parent_trans_id, trans_id)
1479
new_trans_id = tt.create_path(name[0], parent_trans_id)
1480
if versioned == (True, True):
1481
tt.unversion_file(trans_id)
1482
tt.version_file(file_id, new_trans_id)
1483
# New contents should have the same unix perms as old
1486
trans_id = new_trans_id
1487
if kind[1] == 'directory':
1488
tt.create_directory(trans_id)
1489
elif kind[1] == 'symlink':
1490
tt.create_symlink(target_tree.get_symlink_target(file_id),
1492
elif kind[1] == 'file':
1493
tt.create_file(target_tree.get_file_lines(file_id),
1495
# preserve the execute bit when backing up
1496
if keep_content and executable[0] == executable[1]:
1497
tt.set_executability(executable[1], trans_id)
1499
assert kind[1] is None
1500
if versioned == (False, True):
1501
tt.version_file(file_id, trans_id)
1502
if versioned == (True, False):
1503
tt.unversion_file(trans_id)
1504
if (name[1] is not None and
1505
(name[0] != name[1] or parent[0] != parent[1])):
1507
name[1], tt.trans_id_file_id(parent[1]), trans_id)
1508
if executable[0] != executable[1] and kind[1] == "file":
1509
tt.set_executability(executable[1], trans_id)
1511
if basis_tree is not None:
1302
1515
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):