19
19
from stat import S_ISREG
21
from bzrlib.lazy_import import lazy_import
22
lazy_import(globals(), """
21
30
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
22
31
ReusingTransform, NotVersionedError, CantMoveRoot,
23
ExistingLimbo, ImmortalLimbo)
32
ExistingLimbo, ImmortalLimbo, NoFinalPath)
24
33
from bzrlib.inventory import InventoryEntry
25
34
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
27
36
from bzrlib.progress import DummyProgress, ProgressPhase
37
from bzrlib.symbol_versioning import deprecated_function, zero_fifteen
28
38
from bzrlib.trace import mutter, warning
39
from bzrlib import tree
41
import bzrlib.urlutils as urlutils
32
44
ROOT_PARENT = "root-parent"
211
229
def canonical_path(self, path):
212
230
"""Get the canonical tree-relative path"""
213
231
# don't follow final symlinks
214
dirname, basename = os.path.split(self._tree.abspath(path))
215
dirname = os.path.realpath(dirname)
216
return self._tree.relpath(pathjoin(dirname, basename))
232
abs = self._tree.abspath(path)
233
if abs in self._relpaths:
234
return self._relpaths[abs]
235
dirname, basename = os.path.split(abs)
236
if dirname not in self._realpaths:
237
self._realpaths[dirname] = os.path.realpath(dirname)
238
dirname = self._realpaths[dirname]
239
abs = pathjoin(dirname, basename)
240
if dirname in self._relpaths:
241
relpath = pathjoin(self._relpaths[dirname], basename)
242
relpath = relpath.rstrip('/\\')
244
relpath = self._tree.relpath(abs)
245
self._relpaths[abs] = relpath
218
248
def trans_id_tree_path(self, path):
219
249
"""Determine (and maybe set) the transaction ID for a tree path."""
775
825
if trans_id in self._new_id:
777
827
kind = file_kind(self._tree.abspath(path))
778
inv.add_path(path, kind, self._new_id[trans_id])
779
elif trans_id in self._new_name or trans_id in\
781
entry = limbo_inv.get(trans_id)
782
if entry is not None:
783
entry.name = self.final_name(trans_id)
784
parent_path = os.path.dirname(path)
786
self._tree.inventory.path2id(parent_path)
789
# requires files and inventory entries to be in place
828
if trans_id in self._new_reference_revision:
829
new_entry = inventory.TreeReference(
830
self._new_id[trans_id],
831
self._new_name[trans_id],
832
self.final_file_id(self._new_parent[trans_id]),
833
None, self._new_reference_revision[trans_id])
835
new_entry = inventory.make_entry(kind,
836
self.final_name(trans_id),
837
self.final_file_id(self.final_parent(trans_id)),
838
self._new_id[trans_id])
840
if trans_id in self._new_name or trans_id in\
842
trans_id in self._new_executability:
843
file_id = self.final_file_id(trans_id)
844
if file_id is not None:
846
new_entry = entry.copy()
848
if trans_id in self._new_name or trans_id in\
850
if new_entry is not None:
851
new_entry.name = self.final_name(trans_id)
852
parent = self.final_parent(trans_id)
853
parent_id = self.final_file_id(parent)
854
new_entry.parent_id = parent_id
790
856
if trans_id in self._new_executability:
791
self._set_executability(path, inv, trans_id)
857
self._set_executability(path, new_entry, trans_id)
858
if new_entry is not None:
859
if new_entry.file_id in inv:
860
old_path = inv.id2path(new_entry.file_id)
863
inventory_delta.append((old_path, path,
793
867
child_pb.finished()
794
868
return modified_paths
796
def _set_executability(self, path, inv, trans_id):
870
def _set_executability(self, path, entry, trans_id):
797
871
"""Set the executability of versioned files """
798
file_id = inv.path2id(path)
799
872
new_executability = self._new_executability[trans_id]
800
inv[file_id].executable = new_executability
873
entry.executable = new_executability
801
874
if supports_executable():
802
875
abspath = self._tree.abspath(path)
803
876
current_mode = os.stat(abspath).st_mode
861
937
self.create_symlink(target, trans_id)
940
def _affected_ids(self):
941
"""Return the set of transform ids affected by the transform"""
942
trans_ids = set(self._removed_id)
943
trans_ids.update(self._new_id.keys())
944
trans_ids.update(self._removed_contents)
945
trans_ids.update(self._new_contents.keys())
946
trans_ids.update(self._new_executability.keys())
947
trans_ids.update(self._new_name.keys())
948
trans_ids.update(self._new_parent.keys())
951
def _get_file_id_maps(self):
952
"""Return mapping of file_ids to trans_ids in the to and from states"""
953
trans_ids = self._affected_ids()
956
# Build up two dicts: trans_ids associated with file ids in the
957
# FROM state, vs the TO state.
958
for trans_id in trans_ids:
959
from_file_id = self.tree_file_id(trans_id)
960
if from_file_id is not None:
961
from_trans_ids[from_file_id] = trans_id
962
to_file_id = self.final_file_id(trans_id)
963
if to_file_id is not None:
964
to_trans_ids[to_file_id] = trans_id
965
return from_trans_ids, to_trans_ids
967
def _from_file_data(self, from_trans_id, from_versioned, file_id):
968
"""Get data about a file in the from (tree) state
970
Return a (name, parent, kind, executable) tuple
972
from_path = self._tree_id_paths.get(from_trans_id)
974
# get data from working tree if versioned
975
from_entry = self._tree.inventory[file_id]
976
from_name = from_entry.name
977
from_parent = from_entry.parent_id
980
if from_path is None:
981
# File does not exist in FROM state
985
# File exists, but is not versioned. Have to use path-
987
from_name = os.path.basename(from_path)
988
tree_parent = self.get_tree_parent(from_trans_id)
989
from_parent = self.tree_file_id(tree_parent)
990
if from_path is not None:
991
from_kind, from_executable, from_stats = \
992
self._tree._comparison_data(from_entry, from_path)
995
from_executable = False
996
return from_name, from_parent, from_kind, from_executable
998
def _to_file_data(self, to_trans_id, from_trans_id, from_executable):
999
"""Get data about a file in the to (target) state
1001
Return a (name, parent, kind, executable) tuple
1003
to_name = self.final_name(to_trans_id)
1005
to_kind = self.final_kind(to_trans_id)
1008
to_parent = self.final_file_id(self.final_parent(to_trans_id))
1009
if to_trans_id in self._new_executability:
1010
to_executable = self._new_executability[to_trans_id]
1011
elif to_trans_id == from_trans_id:
1012
to_executable = from_executable
1014
to_executable = False
1015
return to_name, to_parent, to_kind, to_executable
1017
def _iter_changes(self):
1018
"""Produce output in the same format as Tree._iter_changes.
1020
Will produce nonsensical results if invoked while inventory/filesystem
1021
conflicts (as reported by TreeTransform.find_conflicts()) are present.
1023
This reads the Transform, but only reproduces changes involving a
1024
file_id. Files that are not versioned in either of the FROM or TO
1025
states are not reflected.
1027
final_paths = FinalPaths(self)
1028
from_trans_ids, to_trans_ids = self._get_file_id_maps()
1030
# Now iterate through all active file_ids
1031
for file_id in set(from_trans_ids.keys() + to_trans_ids.keys()):
1033
from_trans_id = from_trans_ids.get(file_id)
1034
# find file ids, and determine versioning state
1035
if from_trans_id is None:
1036
from_versioned = False
1037
from_trans_id = to_trans_ids[file_id]
1039
from_versioned = True
1040
to_trans_id = to_trans_ids.get(file_id)
1041
if to_trans_id is None:
1042
to_versioned = False
1043
to_trans_id = from_trans_id
1047
from_name, from_parent, from_kind, from_executable = \
1048
self._from_file_data(from_trans_id, from_versioned, file_id)
1050
to_name, to_parent, to_kind, to_executable = \
1051
self._to_file_data(to_trans_id, from_trans_id, from_executable)
1053
if not from_versioned:
1056
from_path = self._tree_id_paths.get(from_trans_id)
1057
if not to_versioned:
1060
to_path = final_paths.get_path(to_trans_id)
1061
if from_kind != to_kind:
1063
elif to_kind in ('file', 'symlink') and (
1064
to_trans_id != from_trans_id or
1065
to_trans_id in self._new_contents):
1067
if (not modified and from_versioned == to_versioned and
1068
from_parent==to_parent and from_name == to_name and
1069
from_executable == to_executable):
1071
results.append((file_id, (from_path, to_path), modified,
1072
(from_versioned, to_versioned),
1073
(from_parent, to_parent),
1074
(from_name, to_name),
1075
(from_kind, to_kind),
1076
(from_executable, to_executable)))
1077
return iter(sorted(results, key=lambda x:x[1]))
864
1080
def joinpath(parent, child):
865
1081
"""Join tree-relative paths, handling the tree root specially"""
866
1082
if parent is None or parent == "":
902
1118
file_ids.sort(key=tree.id2path)
905
1122
def build_tree(tree, wt):
906
"""Create working tree for a branch, using a Transaction."""
1123
"""Create working tree for a branch, using a TreeTransform.
1125
This function should be used on empty trees, having a tree root at most.
1126
(see merge and revert functionality for working with existing trees)
1128
Existing files are handled like so:
1130
- Existing bzrdirs take precedence over creating new items. They are
1131
created as '%s.diverted' % name.
1132
- Otherwise, if the content on disk matches the content we are building,
1133
it is silently replaced.
1134
- Otherwise, conflict resolution will move the old file to 'oldname.moved'.
1136
wt.lock_tree_write()
1140
return _build_tree(tree, wt)
1146
def _build_tree(tree, wt):
1147
"""See build_tree."""
1148
if len(wt.inventory) > 1: # more than just a root
1149
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
907
1150
file_trans_id = {}
908
1151
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
909
1152
pp = ProgressPhase("Build phase", 2, top_pb)
1153
if tree.inventory.root is not None:
1154
# this is kindof a hack: we should be altering the root
1155
# as partof the regular tree shape diff logic.
1156
# the conditional test hereis to avoid doing an
1157
# expensive operation (flush) every time the root id
1158
# is set within the tree, nor setting the root and thus
1159
# marking the tree as dirty, because we use two different
1160
# idioms here: tree interfaces and inventory interfaces.
1161
if wt.path2id('') != tree.inventory.root.file_id:
1162
wt.set_root_id(tree.inventory.root.file_id)
910
1164
tt = TreeTransform(wt)
913
file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
914
file_ids = topology_sorted_ids(tree)
1168
file_trans_id[wt.get_root_id()] = \
1169
tt.trans_id_tree_file_id(wt.get_root_id())
915
1170
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]
1172
for num, (tree_path, entry) in \
1173
enumerate(tree.inventory.iter_entries_by_dir()):
1174
pb.update("Building tree", num, len(tree.inventory))
920
1175
if entry.parent_id is None:
1178
file_id = entry.file_id
1179
target_path = wt.abspath(tree_path)
1181
kind = file_kind(target_path)
1185
if kind == "directory":
1187
bzrdir.BzrDir.open(target_path)
1188
except errors.NotBranchError:
1192
if (file_id not in divert and
1193
_content_match(tree, entry, file_id, kind,
1195
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1196
if kind == 'directory':
922
1198
if entry.parent_id not in file_trans_id:
923
raise repr(entry.parent_id)
1199
raise AssertionError(
1200
'entry %s parent id %r is not in file_trans_id %r'
1201
% (entry, entry.parent_id, file_trans_id))
924
1202
parent_id = file_trans_id[entry.parent_id]
925
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1203
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1206
new_trans_id = file_trans_id[file_id]
1207
old_parent = tt.trans_id_tree_path(tree_path)
1208
_reparent_children(tt, old_parent, new_trans_id)
1212
divert_trans = set(file_trans_id[f] for f in divert)
1213
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1214
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1215
conflicts = cook_conflicts(raw_conflicts, tt)
1216
for conflict in conflicts:
1219
wt.add_conflicts(conflicts)
1220
except errors.UnsupportedOperation:
933
1225
top_pb.finished()
1228
def _reparent_children(tt, old_parent, new_parent):
1229
for child in tt.iter_tree_children(old_parent):
1230
tt.adjust_path(tt.final_name(child), new_parent, child)
1233
def _content_match(tree, entry, file_id, kind, target_path):
1234
if entry.kind != kind:
1236
if entry.kind == "directory":
1238
if entry.kind == "file":
1239
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1241
elif entry.kind == "symlink":
1242
if tree.get_symlink_target(file_id) == os.readlink(target_path):
1247
def resolve_checkout(tt, conflicts, divert):
1248
new_conflicts = set()
1249
for c_type, conflict in ((c[0], c) for c in conflicts):
1250
# Anything but a 'duplicate' would indicate programmer error
1251
assert c_type == 'duplicate', c_type
1252
# Now figure out which is new and which is old
1253
if tt.new_contents(conflict[1]):
1254
new_file = conflict[1]
1255
old_file = conflict[2]
1257
new_file = conflict[2]
1258
old_file = conflict[1]
1260
# We should only get here if the conflict wasn't completely
1262
final_parent = tt.final_parent(old_file)
1263
if new_file in divert:
1264
new_name = tt.final_name(old_file)+'.diverted'
1265
tt.adjust_path(new_name, final_parent, new_file)
1266
new_conflicts.add((c_type, 'Diverted to',
1267
new_file, old_file))
1269
new_name = tt.final_name(old_file)+'.moved'
1270
tt.adjust_path(new_name, final_parent, old_file)
1271
new_conflicts.add((c_type, 'Moved existing file to',
1272
old_file, new_file))
1273
return new_conflicts
935
1276
def new_by_entry(tt, entry, parent_id, tree):
936
1277
"""Create a new file according to its inventory entry"""
937
1278
name = entry.name
941
1282
executable = tree.is_executable(entry.file_id)
942
1283
return tt.new_file(name, parent_id, contents, entry.file_id,
944
elif kind == 'directory':
945
return tt.new_directory(name, parent_id, entry.file_id)
1285
elif kind in ('directory', 'tree-reference'):
1286
trans_id = tt.new_directory(name, parent_id, entry.file_id)
1287
if kind == 'tree-reference':
1288
tt.set_tree_reference(entry.reference_revision, trans_id)
946
1290
elif kind == 'symlink':
947
1291
target = tree.get_symlink_target(entry.file_id)
948
1292
return tt.new_symlink(name, parent_id, target, entry.file_id)
1294
raise errors.BadFileKindError(name, kind)
950
1296
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
951
1297
"""Create new file contents according to an inventory entry."""
952
1298
if entry.kind == "file":
954
1300
lines = tree.get_file(entry.file_id).readlines()
955
1301
tt.create_file(lines, trans_id, mode_id=mode_id)
956
1302
elif entry.kind == "symlink":
1065
1410
return has_contents, contents_mod, meta_mod
1068
def revert(working_tree, target_tree, filenames, backups=False,
1069
pb=DummyProgress()):
1413
def revert(working_tree, target_tree, filenames, backups=False,
1414
pb=DummyProgress(), change_reporter=None):
1070
1415
"""Revert a working tree's contents to those of a target tree."""
1071
interesting_ids = find_interesting(working_tree, target_tree, filenames)
1072
def interesting(file_id):
1073
return interesting_ids is None or file_id in interesting_ids
1416
target_tree.lock_read()
1075
1417
tt = TreeTransform(working_tree, pb)
1077
merge_modified = working_tree.merge_modified()
1079
def trans_id_file_id(file_id):
1081
return trans_id[file_id]
1083
return tt.trans_id_tree_file_id(file_id)
1085
pp = ProgressPhase("Revert phase", 4, pb)
1087
sorted_interesting = [i for i in topology_sorted_ids(target_tree) if
1089
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1091
by_parent = tt.by_parent()
1092
for id_num, file_id in enumerate(sorted_interesting):
1093
child_pb.update("Reverting file", id_num+1,
1094
len(sorted_interesting))
1095
if file_id not in working_tree.inventory:
1096
entry = target_tree.inventory[file_id]
1097
parent_id = trans_id_file_id(entry.parent_id)
1098
e_trans_id = new_by_entry(tt, entry, parent_id, target_tree)
1099
trans_id[file_id] = e_trans_id
1101
backup_this = backups
1102
if file_id in merge_modified:
1104
del merge_modified[file_id]
1105
change_entry(tt, file_id, working_tree, target_tree,
1106
trans_id_file_id, backup_this, trans_id,
1111
wt_interesting = [i for i in working_tree.inventory if interesting(i)]
1112
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1114
for id_num, file_id in enumerate(wt_interesting):
1115
child_pb.update("New file check", id_num+1,
1116
len(sorted_interesting))
1117
if file_id not in target_tree:
1118
trans_id = tt.trans_id_tree_file_id(file_id)
1119
tt.unversion_file(trans_id)
1120
if file_id in merge_modified:
1121
tt.delete_contents(trans_id)
1122
del merge_modified[file_id]
1419
pp = ProgressPhase("Revert phase", 3, pb)
1421
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1423
_alter_files(working_tree, target_tree, tt, child_pb,
1124
1426
child_pb.finished()
1125
1427
pp.next_phase()
1130
1432
child_pb.finished()
1131
1433
conflicts = cook_conflicts(raw_conflicts, tt)
1435
change_reporter = delta._ChangeReporter(
1436
unversioned_filter=working_tree.is_ignored)
1437
delta.report_changes(tt._iter_changes(), change_reporter)
1132
1438
for conflict in conflicts:
1133
1439
warning(conflict)
1134
1440
pp.next_phase()
1136
1442
working_tree.set_merge_modified({})
1444
target_tree.unlock()
1140
1447
return conflicts
1143
def resolve_conflicts(tt, pb=DummyProgress()):
1450
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
1452
merge_modified = working_tree.merge_modified()
1453
change_list = target_tree._iter_changes(working_tree,
1454
specific_files=specific_files, pb=pb)
1455
if target_tree.inventory.root is None:
1461
for id_num, (file_id, path, changed_content, versioned, parent, name,
1462
kind, executable) in enumerate(change_list):
1463
if skip_root and file_id[0] is not None and parent[0] is None:
1465
trans_id = tt.trans_id_file_id(file_id)
1468
keep_content = False
1469
if kind[0] == 'file' and (backups or kind[1] is None):
1470
wt_sha1 = working_tree.get_file_sha1(file_id)
1471
if merge_modified.get(file_id) != wt_sha1:
1472
# acquire the basis tree lazyily to prevent the expense
1473
# of accessing it when its not needed ? (Guessing, RBC,
1475
if basis_tree is None:
1476
basis_tree = working_tree.basis_tree()
1477
basis_tree.lock_read()
1478
if file_id in basis_tree:
1479
if wt_sha1 != basis_tree.get_file_sha1(file_id):
1481
elif kind[1] is None and not versioned[1]:
1483
if kind[0] is not None:
1484
if not keep_content:
1485
tt.delete_contents(trans_id)
1486
elif kind[1] is not None:
1487
parent_trans_id = tt.trans_id_file_id(parent[0])
1488
by_parent = tt.by_parent()
1489
backup_name = _get_backup_name(name[0], by_parent,
1490
parent_trans_id, tt)
1491
tt.adjust_path(backup_name, parent_trans_id, trans_id)
1492
new_trans_id = tt.create_path(name[0], parent_trans_id)
1493
if versioned == (True, True):
1494
tt.unversion_file(trans_id)
1495
tt.version_file(file_id, new_trans_id)
1496
# New contents should have the same unix perms as old
1499
trans_id = new_trans_id
1500
if kind[1] == 'directory':
1501
tt.create_directory(trans_id)
1502
elif kind[1] == 'symlink':
1503
tt.create_symlink(target_tree.get_symlink_target(file_id),
1505
elif kind[1] == 'file':
1506
tt.create_file(target_tree.get_file_lines(file_id),
1508
# preserve the execute bit when backing up
1509
if keep_content and executable[0] == executable[1]:
1510
tt.set_executability(executable[1], trans_id)
1512
assert kind[1] is None
1513
if versioned == (False, True):
1514
tt.version_file(file_id, trans_id)
1515
if versioned == (True, False):
1516
tt.unversion_file(trans_id)
1517
if (name[1] is not None and
1518
(name[0] != name[1] or parent[0] != parent[1])):
1520
name[1], tt.trans_id_file_id(parent[1]), trans_id)
1521
if executable[0] != executable[1] and kind[1] == "file":
1522
tt.set_executability(executable[1], trans_id)
1524
if basis_tree is not None:
1528
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
1144
1529
"""Make many conflict-resolution attempts, but die if they fail"""
1530
if pass_func is None:
1531
pass_func = conflict_pass
1145
1532
new_conflicts = set()
1147
1534
for n in range(10):
1188
1575
trans_id = conflict[1]
1190
1577
tt.cancel_deletion(trans_id)
1191
new_conflicts.add((c_type, 'Not deleting', trans_id))
1578
new_conflicts.add(('deleting parent', 'Not deleting',
1192
1580
except KeyError:
1193
1581
tt.create_directory(trans_id)
1194
new_conflicts.add((c_type, 'Created directory.', trans_id))
1582
new_conflicts.add((c_type, 'Created directory', trans_id))
1195
1583
elif c_type == 'unversioned parent':
1196
1584
tt.version_file(tt.inactive_file_id(conflict[1]), conflict[1])
1197
1585
new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
1198
1586
return new_conflicts
1200
1589
def cook_conflicts(raw_conflicts, tt):
1201
1590
"""Generate a list of cooked conflicts, sorted by file path"""
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
1208
return None, conflict.typestring
1591
from bzrlib.conflicts import Conflict
1592
conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
1593
return sorted(conflict_iter, key=Conflict.sort_key)
1210
return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1212
1596
def iter_cook_conflicts(raw_conflicts, tt):
1213
1597
from bzrlib.conflicts import Conflict