19
19
from stat import S_ISREG
21
from bzrlib.lazy_import import lazy_import
22
lazy_import(globals(), """
30
21
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
31
22
ReusingTransform, NotVersionedError, CantMoveRoot,
32
ExistingLimbo, ImmortalLimbo, NoFinalPath)
23
ExistingLimbo, ImmortalLimbo)
33
24
from bzrlib.inventory import InventoryEntry
34
25
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
36
27
from bzrlib.progress import DummyProgress, ProgressPhase
37
from bzrlib.symbol_versioning import deprecated_function, zero_fifteen, \
39
28
from bzrlib.trace import mutter, warning
40
29
from bzrlib import tree
42
31
import bzrlib.urlutils as urlutils
188
154
"""Change the path that is assigned to a transaction id."""
189
155
if trans_id == self._new_root:
190
156
raise CantMoveRoot
191
previous_parent = self._new_parent.get(trans_id)
192
previous_name = self._new_name.get(trans_id)
193
157
self._new_name[trans_id] = name
194
158
self._new_parent[trans_id] = parent
195
if (trans_id in self._limbo_files and
196
trans_id not in self._needs_rename):
197
self._rename_in_limbo([trans_id])
198
self._limbo_children[previous_parent].remove(trans_id)
199
del self._limbo_children_names[previous_parent][previous_name]
201
def _rename_in_limbo(self, trans_ids):
202
"""Fix limbo names so that the right final path is produced.
204
This means we outsmarted ourselves-- we tried to avoid renaming
205
these files later by creating them with their final names in their
206
final parents. But now the previous name or parent is no longer
207
suitable, so we have to rename them.
209
Even for trans_ids that have no new contents, we must remove their
210
entries from _limbo_files, because they are now stale.
212
for trans_id in trans_ids:
213
old_path = self._limbo_files.pop(trans_id)
214
if trans_id not in self._new_contents:
216
new_path = self._limbo_name(trans_id)
217
os.rename(old_path, new_path)
219
160
def adjust_root_path(self, name, parent):
220
161
"""Emulate moving the root by moving all children, instead.
789
def apply(self, no_conflicts=False):
790
710
"""Apply all changes to the inventory and filesystem.
792
712
If filesystem or inventory conflicts are present, MalformedTransform
795
If apply succeeds, finalize is not necessary.
797
:param no_conflicts: if True, the caller guarantees there are no
798
conflicts, so no check is made.
801
conflicts = self.find_conflicts()
802
if len(conflicts) != 0:
803
raise MalformedTransform(conflicts=conflicts)
715
conflicts = self.find_conflicts()
716
if len(conflicts) != 0:
717
raise MalformedTransform(conflicts=conflicts)
804
719
inv = self._tree.inventory
806
720
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
808
722
child_pb.update('Apply phase', 0, 2)
809
self._apply_removals(inv, inventory_delta)
723
self._apply_removals(inv, limbo_inv)
810
724
child_pb.update('Apply phase', 1, 2)
811
modified_paths = self._apply_insertions(inv, inventory_delta)
725
modified_paths = self._apply_insertions(inv, limbo_inv)
813
727
child_pb.finished()
814
self._tree.apply_inventory_delta(inventory_delta)
728
self._tree._write_inventory(inv)
815
729
self.__done = True
817
return _TransformResults(modified_paths, self.rename_count)
731
return _TransformResults(modified_paths)
819
733
def _limbo_name(self, trans_id):
820
734
"""Generate the limbo name of a file"""
821
limbo_name = self._limbo_files.get(trans_id)
822
if limbo_name is not None:
824
parent = self._new_parent.get(trans_id)
825
# if the parent directory is already in limbo (e.g. when building a
826
# tree), choose a limbo name inside the parent, to reduce further
828
use_direct_path = False
829
if self._new_contents.get(parent) == 'directory':
830
filename = self._new_name.get(trans_id)
831
if filename is not None:
832
if parent not in self._limbo_children:
833
self._limbo_children[parent] = set()
834
self._limbo_children_names[parent] = {}
835
use_direct_path = True
836
# the direct path can only be used if no other file has
837
# already taken this pathname, i.e. if the name is unused, or
838
# if it is already associated with this trans_id.
839
elif (self._limbo_children_names[parent].get(filename)
840
in (trans_id, None)):
841
use_direct_path = True
843
limbo_name = pathjoin(self._limbo_files[parent], filename)
844
self._limbo_children[parent].add(trans_id)
845
self._limbo_children_names[parent][filename] = trans_id
847
limbo_name = pathjoin(self._limbodir, trans_id)
848
self._needs_rename.add(trans_id)
849
self._limbo_files[trans_id] = limbo_name
735
return pathjoin(self._limbodir, trans_id)
852
def _apply_removals(self, inv, inventory_delta):
737
def _apply_removals(self, inv, limbo_inv):
853
738
"""Perform tree operations that remove directory/inventory names.
855
740
That is, delete files that are to be deleted, and put any files that
922
805
if trans_id in self._new_id:
924
807
kind = file_kind(self._tree.abspath(path))
925
if trans_id in self._new_reference_revision:
926
new_entry = inventory.TreeReference(
927
self._new_id[trans_id],
928
self._new_name[trans_id],
929
self.final_file_id(self._new_parent[trans_id]),
930
None, self._new_reference_revision[trans_id])
932
new_entry = inventory.make_entry(kind,
933
self.final_name(trans_id),
934
self.final_file_id(self.final_parent(trans_id)),
935
self._new_id[trans_id])
937
if trans_id in self._new_name or trans_id in\
939
trans_id in self._new_executability:
940
file_id = self.final_file_id(trans_id)
941
if file_id is not None:
943
new_entry = entry.copy()
945
if trans_id in self._new_name or trans_id in\
947
if new_entry is not None:
948
new_entry.name = self.final_name(trans_id)
949
parent = self.final_parent(trans_id)
950
parent_id = self.final_file_id(parent)
951
new_entry.parent_id = parent_id
808
inv.add_path(path, kind, self._new_id[trans_id])
809
elif trans_id in self._new_name or trans_id in\
811
entry = limbo_inv.get(trans_id)
812
if entry is not None:
813
entry.name = self.final_name(trans_id)
814
parent_path = os.path.dirname(path)
816
self._tree.inventory.path2id(parent_path)
819
# requires files and inventory entries to be in place
953
820
if trans_id in self._new_executability:
954
self._set_executability(path, new_entry, trans_id)
955
if new_entry is not None:
956
if new_entry.file_id in inv:
957
old_path = inv.id2path(new_entry.file_id)
960
inventory_delta.append((old_path, path,
821
self._set_executability(path, inv, trans_id)
964
823
child_pb.finished()
965
824
return modified_paths
967
def _set_executability(self, path, entry, trans_id):
826
def _set_executability(self, path, inv, trans_id):
968
827
"""Set the executability of versioned files """
828
file_id = inv.path2id(path)
969
829
new_executability = self._new_executability[trans_id]
970
entry.executable = new_executability
830
inv[file_id].executable = new_executability
971
831
if supports_executable():
972
832
abspath = self._tree.abspath(path)
973
833
current_mode = os.stat(abspath).st_mode
1034
894
self.create_symlink(target, trans_id)
1037
def _affected_ids(self):
1038
"""Return the set of transform ids affected by the transform"""
1039
trans_ids = set(self._removed_id)
1040
trans_ids.update(self._new_id.keys())
1041
trans_ids.update(self._removed_contents)
1042
trans_ids.update(self._new_contents.keys())
1043
trans_ids.update(self._new_executability.keys())
1044
trans_ids.update(self._new_name.keys())
1045
trans_ids.update(self._new_parent.keys())
1048
def _get_file_id_maps(self):
1049
"""Return mapping of file_ids to trans_ids in the to and from states"""
1050
trans_ids = self._affected_ids()
1053
# Build up two dicts: trans_ids associated with file ids in the
1054
# FROM state, vs the TO state.
1055
for trans_id in trans_ids:
1056
from_file_id = self.tree_file_id(trans_id)
1057
if from_file_id is not None:
1058
from_trans_ids[from_file_id] = trans_id
1059
to_file_id = self.final_file_id(trans_id)
1060
if to_file_id is not None:
1061
to_trans_ids[to_file_id] = trans_id
1062
return from_trans_ids, to_trans_ids
1064
def _from_file_data(self, from_trans_id, from_versioned, file_id):
1065
"""Get data about a file in the from (tree) state
1067
Return a (name, parent, kind, executable) tuple
1069
from_path = self._tree_id_paths.get(from_trans_id)
1071
# get data from working tree if versioned
1072
from_entry = self._tree.inventory[file_id]
1073
from_name = from_entry.name
1074
from_parent = from_entry.parent_id
1077
if from_path is None:
1078
# File does not exist in FROM state
1082
# File exists, but is not versioned. Have to use path-
1084
from_name = os.path.basename(from_path)
1085
tree_parent = self.get_tree_parent(from_trans_id)
1086
from_parent = self.tree_file_id(tree_parent)
1087
if from_path is not None:
1088
from_kind, from_executable, from_stats = \
1089
self._tree._comparison_data(from_entry, from_path)
1092
from_executable = False
1093
return from_name, from_parent, from_kind, from_executable
1095
def _to_file_data(self, to_trans_id, from_trans_id, from_executable):
1096
"""Get data about a file in the to (target) state
1098
Return a (name, parent, kind, executable) tuple
1100
to_name = self.final_name(to_trans_id)
1102
to_kind = self.final_kind(to_trans_id)
1105
to_parent = self.final_file_id(self.final_parent(to_trans_id))
1106
if to_trans_id in self._new_executability:
1107
to_executable = self._new_executability[to_trans_id]
1108
elif to_trans_id == from_trans_id:
1109
to_executable = from_executable
1111
to_executable = False
1112
return to_name, to_parent, to_kind, to_executable
1114
def _iter_changes(self):
1115
"""Produce output in the same format as Tree._iter_changes.
1117
Will produce nonsensical results if invoked while inventory/filesystem
1118
conflicts (as reported by TreeTransform.find_conflicts()) are present.
1120
This reads the Transform, but only reproduces changes involving a
1121
file_id. Files that are not versioned in either of the FROM or TO
1122
states are not reflected.
1124
final_paths = FinalPaths(self)
1125
from_trans_ids, to_trans_ids = self._get_file_id_maps()
1127
# Now iterate through all active file_ids
1128
for file_id in set(from_trans_ids.keys() + to_trans_ids.keys()):
1130
from_trans_id = from_trans_ids.get(file_id)
1131
# find file ids, and determine versioning state
1132
if from_trans_id is None:
1133
from_versioned = False
1134
from_trans_id = to_trans_ids[file_id]
1136
from_versioned = True
1137
to_trans_id = to_trans_ids.get(file_id)
1138
if to_trans_id is None:
1139
to_versioned = False
1140
to_trans_id = from_trans_id
1144
from_name, from_parent, from_kind, from_executable = \
1145
self._from_file_data(from_trans_id, from_versioned, file_id)
1147
to_name, to_parent, to_kind, to_executable = \
1148
self._to_file_data(to_trans_id, from_trans_id, from_executable)
1150
if not from_versioned:
1153
from_path = self._tree_id_paths.get(from_trans_id)
1154
if not to_versioned:
1157
to_path = final_paths.get_path(to_trans_id)
1158
if from_kind != to_kind:
1160
elif to_kind in ('file', 'symlink') and (
1161
to_trans_id != from_trans_id or
1162
to_trans_id in self._new_contents):
1164
if (not modified and from_versioned == to_versioned and
1165
from_parent==to_parent and from_name == to_name and
1166
from_executable == to_executable):
1168
results.append((file_id, (from_path, to_path), modified,
1169
(from_versioned, to_versioned),
1170
(from_parent, to_parent),
1171
(from_name, to_name),
1172
(from_kind, to_kind),
1173
(from_executable, to_executable)))
1174
return iter(sorted(results, key=lambda x:x[1]))
1177
897
def joinpath(parent, child):
1178
898
"""Join tree-relative paths, handling the tree root specially"""
1179
899
if parent is None or parent == "":
1215
935
file_ids.sort(key=tree.id2path)
1219
938
def build_tree(tree, wt):
1220
"""Create working tree for a branch, using a TreeTransform.
1222
This function should be used on empty trees, having a tree root at most.
1223
(see merge and revert functionality for working with existing trees)
1225
Existing files are handled like so:
1227
- Existing bzrdirs take precedence over creating new items. They are
1228
created as '%s.diverted' % name.
1229
- Otherwise, if the content on disk matches the content we are building,
1230
it is silently replaced.
1231
- Otherwise, conflict resolution will move the old file to 'oldname.moved'.
1233
wt.lock_tree_write()
1237
return _build_tree(tree, wt)
1243
def _build_tree(tree, wt):
1244
"""See build_tree."""
1245
if len(wt.inventory) > 1: # more than just a root
1246
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
939
"""Create working tree for a branch, using a Transaction."""
1247
940
file_trans_id = {}
1248
941
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1249
942
pp = ProgressPhase("Build phase", 2, top_pb)
1250
if tree.inventory.root is not None:
1251
# This is kind of a hack: we should be altering the root
1252
# as part of the regular tree shape diff logic.
1253
# The conditional test here is to avoid doing an
1254
# expensive operation (flush) every time the root id
1255
# is set within the tree, nor setting the root and thus
1256
# marking the tree as dirty, because we use two different
1257
# idioms here: tree interfaces and inventory interfaces.
1258
if wt.path2id('') != tree.inventory.root.file_id:
1259
wt.set_root_id(tree.inventory.root.file_id)
1261
943
tt = TreeTransform(wt)
1265
file_trans_id[wt.get_root_id()] = \
1266
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)
1267
948
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1269
for num, (tree_path, entry) in \
1270
enumerate(tree.inventory.iter_entries_by_dir()):
1271
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]
1272
953
if entry.parent_id is None:
1275
file_id = entry.file_id
1276
target_path = wt.abspath(tree_path)
1278
kind = file_kind(target_path)
1282
if kind == "directory":
1284
bzrdir.BzrDir.open(target_path)
1285
except errors.NotBranchError:
1289
if (file_id not in divert and
1290
_content_match(tree, entry, file_id, kind,
1292
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1293
if kind == 'directory':
1295
955
if entry.parent_id not in file_trans_id:
1296
raise AssertionError(
1297
'entry %s parent id %r is not in file_trans_id %r'
1298
% (entry, entry.parent_id, file_trans_id))
956
raise repr(entry.parent_id)
1299
957
parent_id = file_trans_id[entry.parent_id]
1300
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,
1303
new_trans_id = file_trans_id[file_id]
1304
old_parent = tt.trans_id_tree_path(tree_path)
1305
_reparent_children(tt, old_parent, new_trans_id)
1309
divert_trans = set(file_trans_id[f] for f in divert)
1310
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1311
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1312
conflicts = cook_conflicts(raw_conflicts, tt)
1313
for conflict in conflicts:
1316
wt.add_conflicts(conflicts)
1317
except errors.UnsupportedOperation:
1322
966
top_pb.finished()
1326
def _reparent_children(tt, old_parent, new_parent):
1327
for child in tt.iter_tree_children(old_parent):
1328
tt.adjust_path(tt.final_name(child), new_parent, child)
1331
def _content_match(tree, entry, file_id, kind, target_path):
1332
if entry.kind != kind:
1334
if entry.kind == "directory":
1336
if entry.kind == "file":
1337
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1339
elif entry.kind == "symlink":
1340
if tree.get_symlink_target(file_id) == os.readlink(target_path):
1345
def resolve_checkout(tt, conflicts, divert):
1346
new_conflicts = set()
1347
for c_type, conflict in ((c[0], c) for c in conflicts):
1348
# Anything but a 'duplicate' would indicate programmer error
1349
assert c_type == 'duplicate', c_type
1350
# Now figure out which is new and which is old
1351
if tt.new_contents(conflict[1]):
1352
new_file = conflict[1]
1353
old_file = conflict[2]
1355
new_file = conflict[2]
1356
old_file = conflict[1]
1358
# We should only get here if the conflict wasn't completely
1360
final_parent = tt.final_parent(old_file)
1361
if new_file in divert:
1362
new_name = tt.final_name(old_file)+'.diverted'
1363
tt.adjust_path(new_name, final_parent, new_file)
1364
new_conflicts.add((c_type, 'Diverted to',
1365
new_file, old_file))
1367
new_name = tt.final_name(old_file)+'.moved'
1368
tt.adjust_path(new_name, final_parent, old_file)
1369
new_conflicts.add((c_type, 'Moved existing file to',
1370
old_file, new_file))
1371
return new_conflicts
1374
968
def new_by_entry(tt, entry, parent_id, tree):
1375
969
"""Create a new file according to its inventory entry"""
1380
974
executable = tree.is_executable(entry.file_id)
1381
975
return tt.new_file(name, parent_id, contents, entry.file_id,
1383
elif kind in ('directory', 'tree-reference'):
1384
trans_id = tt.new_directory(name, parent_id, entry.file_id)
1385
if kind == 'tree-reference':
1386
tt.set_tree_reference(entry.reference_revision, trans_id)
977
elif kind == 'directory':
978
return tt.new_directory(name, parent_id, entry.file_id)
1388
979
elif kind == 'symlink':
1389
980
target = tree.get_symlink_target(entry.file_id)
1390
981
return tt.new_symlink(name, parent_id, target, entry.file_id)
1392
raise errors.BadFileKindError(name, kind)
1394
983
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
1395
984
"""Create new file contents according to an inventory entry."""
1396
985
if entry.kind == "file":
1398
987
lines = tree.get_file(entry.file_id).readlines()
1399
988
tt.create_file(lines, trans_id, mode_id=mode_id)
1400
989
elif entry.kind == "symlink":
1408
997
tt.set_executability(entry.executable, trans_id)
1411
@deprecated_function(zero_fifteen)
1412
1000
def find_interesting(working_tree, target_tree, filenames):
1413
"""Find the ids corresponding to specified filenames.
1415
Deprecated: Please use tree1.paths2ids(filenames, [tree2]).
1417
working_tree.lock_read()
1419
target_tree.lock_read()
1421
return working_tree.paths2ids(filenames, [target_tree])
1423
target_tree.unlock()
1425
working_tree.unlock()
1428
@deprecated_function(zero_nineteen)
1001
"""Find the ids corresponding to specified filenames."""
1002
trees = (working_tree, target_tree)
1003
return tree.find_ids_across_trees(filenames, trees)
1429
1006
def change_entry(tt, file_id, working_tree, target_tree,
1430
1007
trans_id_file_id, backups, trans_id, by_parent):
1431
1008
"""Replace a file_id's contents with those from a target tree."""
1432
if file_id is None and target_tree is None:
1433
# skip the logic altogether in the deprecation test
1435
1009
e_trans_id = trans_id_file_id(file_id)
1436
1010
entry = target_tree.inventory[file_id]
1437
1011
has_contents, contents_mod, meta_mod, = _entry_changes(file_id, entry,
1512
1084
return has_contents, contents_mod, meta_mod
1515
def revert(working_tree, target_tree, filenames, backups=False,
1516
pb=DummyProgress(), change_reporter=None):
1087
def revert(working_tree, target_tree, filenames, backups=False,
1088
pb=DummyProgress()):
1517
1089
"""Revert a working tree's contents to those of a target tree."""
1518
target_tree.lock_read()
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
1519
1094
tt = TreeTransform(working_tree, pb)
1521
pp = ProgressPhase("Revert phase", 3, pb)
1523
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1525
merge_modified = _alter_files(working_tree, target_tree, tt,
1526
child_pb, filenames, backups)
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]
1528
1143
child_pb.finished()
1529
1144
pp.next_phase()
1534
1149
child_pb.finished()
1535
1150
conflicts = cook_conflicts(raw_conflicts, tt)
1537
change_reporter = delta._ChangeReporter(
1538
unversioned_filter=working_tree.is_ignored)
1539
delta.report_changes(tt._iter_changes(), change_reporter)
1540
1151
for conflict in conflicts:
1541
1152
warning(conflict)
1542
1153
pp.next_phase()
1544
working_tree.set_merge_modified(merge_modified)
1155
working_tree.set_merge_modified({})
1546
target_tree.unlock()
1549
1159
return conflicts
1552
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
1554
merge_modified = working_tree.merge_modified()
1555
change_list = target_tree._iter_changes(working_tree,
1556
specific_files=specific_files, pb=pb)
1557
if target_tree.inventory.root is None:
1563
for id_num, (file_id, path, changed_content, versioned, parent, name,
1564
kind, executable) in enumerate(change_list):
1565
if skip_root and file_id[0] is not None and parent[0] is None:
1567
trans_id = tt.trans_id_file_id(file_id)
1570
keep_content = False
1571
if kind[0] == 'file' and (backups or kind[1] is None):
1572
wt_sha1 = working_tree.get_file_sha1(file_id)
1573
if merge_modified.get(file_id) != wt_sha1:
1574
# acquire the basis tree lazily to prevent the
1575
# expense of accessing it when it's not needed ?
1576
# (Guessing, RBC, 200702)
1577
if basis_tree is None:
1578
basis_tree = working_tree.basis_tree()
1579
basis_tree.lock_read()
1580
if file_id in basis_tree:
1581
if wt_sha1 != basis_tree.get_file_sha1(file_id):
1583
elif kind[1] is None and not versioned[1]:
1585
if kind[0] is not None:
1586
if not keep_content:
1587
tt.delete_contents(trans_id)
1588
elif kind[1] is not None:
1589
parent_trans_id = tt.trans_id_file_id(parent[0])
1590
by_parent = tt.by_parent()
1591
backup_name = _get_backup_name(name[0], by_parent,
1592
parent_trans_id, tt)
1593
tt.adjust_path(backup_name, parent_trans_id, trans_id)
1594
new_trans_id = tt.create_path(name[0], parent_trans_id)
1595
if versioned == (True, True):
1596
tt.unversion_file(trans_id)
1597
tt.version_file(file_id, new_trans_id)
1598
# New contents should have the same unix perms as old
1601
trans_id = new_trans_id
1602
if kind[1] == 'directory':
1603
tt.create_directory(trans_id)
1604
elif kind[1] == 'symlink':
1605
tt.create_symlink(target_tree.get_symlink_target(file_id),
1607
elif kind[1] == 'file':
1608
tt.create_file(target_tree.get_file_lines(file_id),
1610
if basis_tree is None:
1611
basis_tree = working_tree.basis_tree()
1612
basis_tree.lock_read()
1613
new_sha1 = target_tree.get_file_sha1(file_id)
1614
if (file_id in basis_tree and new_sha1 ==
1615
basis_tree.get_file_sha1(file_id)):
1616
if file_id in merge_modified:
1617
del merge_modified[file_id]
1619
merge_modified[file_id] = new_sha1
1621
# preserve the execute bit when backing up
1622
if keep_content and executable[0] == executable[1]:
1623
tt.set_executability(executable[1], trans_id)
1625
assert kind[1] is None
1626
if versioned == (False, True):
1627
tt.version_file(file_id, trans_id)
1628
if versioned == (True, False):
1629
tt.unversion_file(trans_id)
1630
if (name[1] is not None and
1631
(name[0] != name[1] or parent[0] != parent[1])):
1633
name[1], tt.trans_id_file_id(parent[1]), trans_id)
1634
if executable[0] != executable[1] and kind[1] == "file":
1635
tt.set_executability(executable[1], trans_id)
1637
if basis_tree is not None:
1639
return merge_modified
1642
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
1162
def resolve_conflicts(tt, pb=DummyProgress()):
1643
1163
"""Make many conflict-resolution attempts, but die if they fail"""
1644
if pass_func is None:
1645
pass_func = conflict_pass
1646
1164
new_conflicts = set()
1648
1166
for n in range(10):