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
38
28
from bzrlib.trace import mutter, warning
39
from bzrlib import tree
41
import bzrlib.urlutils as urlutils
44
32
ROOT_PARENT = "root-parent"
187
147
"""Change the path that is assigned to a transaction id."""
188
148
if trans_id == self._new_root:
189
149
raise CantMoveRoot
190
previous_parent = self._new_parent.get(trans_id)
191
previous_name = self._new_name.get(trans_id)
192
150
self._new_name[trans_id] = name
193
151
self._new_parent[trans_id] = parent
194
if (trans_id in self._limbo_files and
195
trans_id not in self._needs_rename):
196
self._rename_in_limbo([trans_id])
197
self._limbo_children[previous_parent].remove(trans_id)
198
del self._limbo_children_names[previous_parent][previous_name]
200
def _rename_in_limbo(self, trans_ids):
201
"""Fix limbo names so that the right final path is produced.
203
This means we outsmarted ourselves-- we tried to avoid renaming
204
these files later by creating them with their final names in their
205
final parents. But now the previous name or parent is no longer
206
suitable, so we have to rename them.
208
Even for trans_ids that have no new contents, we must remove their
209
entries from _limbo_files, because they are now stale.
211
for trans_id in trans_ids:
212
old_path = self._limbo_files.pop(trans_id)
213
if trans_id not in self._new_contents:
215
new_path = self._limbo_name(trans_id)
216
os.rename(old_path, new_path)
218
153
def adjust_root_path(self, name, parent):
219
154
"""Emulate moving the root by moving all children, instead.
276
211
def canonical_path(self, path):
277
212
"""Get the canonical tree-relative path"""
278
213
# don't follow final symlinks
279
abs = self._tree.abspath(path)
280
if abs in self._relpaths:
281
return self._relpaths[abs]
282
dirname, basename = os.path.split(abs)
283
if dirname not in self._realpaths:
284
self._realpaths[dirname] = os.path.realpath(dirname)
285
dirname = self._realpaths[dirname]
286
abs = pathjoin(dirname, basename)
287
if dirname in self._relpaths:
288
relpath = pathjoin(self._relpaths[dirname], basename)
289
relpath = relpath.rstrip('/\\')
291
relpath = self._tree.relpath(abs)
292
self._relpaths[abs] = relpath
214
dirname, basename = os.path.split(self._tree.abspath(path))
215
dirname = os.path.realpath(dirname)
216
return self._tree.relpath(pathjoin(dirname, basename))
295
218
def trans_id_tree_path(self, path):
296
219
"""Determine (and maybe set) the transaction ID for a tree path."""
789
682
If filesystem or inventory conflicts are present, MalformedTransform
792
If apply succeeds, finalize is not necessary.
794
685
conflicts = self.find_conflicts()
795
686
if len(conflicts) != 0:
796
687
raise MalformedTransform(conflicts=conflicts)
797
689
inv = self._tree.inventory
799
690
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
801
692
child_pb.update('Apply phase', 0, 2)
802
self._apply_removals(inv, inventory_delta)
693
self._apply_removals(inv, limbo_inv)
803
694
child_pb.update('Apply phase', 1, 2)
804
modified_paths = self._apply_insertions(inv, inventory_delta)
695
modified_paths = self._apply_insertions(inv, limbo_inv)
806
697
child_pb.finished()
807
self._tree.apply_inventory_delta(inventory_delta)
698
self._tree._write_inventory(inv)
808
699
self.__done = True
810
return _TransformResults(modified_paths, self.rename_count)
701
return _TransformResults(modified_paths)
812
703
def _limbo_name(self, trans_id):
813
704
"""Generate the limbo name of a file"""
814
limbo_name = self._limbo_files.get(trans_id)
815
if limbo_name is not None:
817
parent = self._new_parent.get(trans_id)
818
# if the parent directory is already in limbo (e.g. when building a
819
# tree), choose a limbo name inside the parent, to reduce further
821
use_direct_path = False
822
if self._new_contents.get(parent) == 'directory':
823
filename = self._new_name.get(trans_id)
824
if filename is not None:
825
if parent not in self._limbo_children:
826
self._limbo_children[parent] = set()
827
self._limbo_children_names[parent] = {}
828
use_direct_path = True
829
# the direct path can only be used if no other file has
830
# already taken this pathname, i.e. if the name is unused, or
831
# if it is already associated with this trans_id.
832
elif (self._limbo_children_names[parent].get(filename)
833
in (trans_id, None)):
834
use_direct_path = True
836
limbo_name = pathjoin(self._limbo_files[parent], filename)
837
self._limbo_children[parent].add(trans_id)
838
self._limbo_children_names[parent][filename] = trans_id
840
limbo_name = pathjoin(self._limbodir, trans_id)
841
self._needs_rename.add(trans_id)
842
self._limbo_files[trans_id] = limbo_name
705
return pathjoin(self._limbodir, trans_id)
845
def _apply_removals(self, inv, inventory_delta):
707
def _apply_removals(self, inv, limbo_inv):
846
708
"""Perform tree operations that remove directory/inventory names.
848
710
That is, delete files that are to be deleted, and put any files that
915
775
if trans_id in self._new_id:
917
777
kind = file_kind(self._tree.abspath(path))
918
if trans_id in self._new_reference_revision:
919
new_entry = inventory.TreeReference(
920
self._new_id[trans_id],
921
self._new_name[trans_id],
922
self.final_file_id(self._new_parent[trans_id]),
923
None, self._new_reference_revision[trans_id])
925
new_entry = inventory.make_entry(kind,
926
self.final_name(trans_id),
927
self.final_file_id(self.final_parent(trans_id)),
928
self._new_id[trans_id])
930
if trans_id in self._new_name or trans_id in\
932
trans_id in self._new_executability:
933
file_id = self.final_file_id(trans_id)
934
if file_id is not None:
936
new_entry = entry.copy()
938
if trans_id in self._new_name or trans_id in\
940
if new_entry is not None:
941
new_entry.name = self.final_name(trans_id)
942
parent = self.final_parent(trans_id)
943
parent_id = self.final_file_id(parent)
944
new_entry.parent_id = parent_id
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
946
790
if trans_id in self._new_executability:
947
self._set_executability(path, new_entry, trans_id)
948
if new_entry is not None:
949
if new_entry.file_id in inv:
950
old_path = inv.id2path(new_entry.file_id)
953
inventory_delta.append((old_path, path,
791
self._set_executability(path, inv, trans_id)
957
793
child_pb.finished()
958
794
return modified_paths
960
def _set_executability(self, path, entry, trans_id):
796
def _set_executability(self, path, inv, trans_id):
961
797
"""Set the executability of versioned files """
798
file_id = inv.path2id(path)
962
799
new_executability = self._new_executability[trans_id]
963
entry.executable = new_executability
800
inv[file_id].executable = new_executability
964
801
if supports_executable():
965
802
abspath = self._tree.abspath(path)
966
803
current_mode = os.stat(abspath).st_mode
1027
861
self.create_symlink(target, trans_id)
1030
def _affected_ids(self):
1031
"""Return the set of transform ids affected by the transform"""
1032
trans_ids = set(self._removed_id)
1033
trans_ids.update(self._new_id.keys())
1034
trans_ids.update(self._removed_contents)
1035
trans_ids.update(self._new_contents.keys())
1036
trans_ids.update(self._new_executability.keys())
1037
trans_ids.update(self._new_name.keys())
1038
trans_ids.update(self._new_parent.keys())
1041
def _get_file_id_maps(self):
1042
"""Return mapping of file_ids to trans_ids in the to and from states"""
1043
trans_ids = self._affected_ids()
1046
# Build up two dicts: trans_ids associated with file ids in the
1047
# FROM state, vs the TO state.
1048
for trans_id in trans_ids:
1049
from_file_id = self.tree_file_id(trans_id)
1050
if from_file_id is not None:
1051
from_trans_ids[from_file_id] = trans_id
1052
to_file_id = self.final_file_id(trans_id)
1053
if to_file_id is not None:
1054
to_trans_ids[to_file_id] = trans_id
1055
return from_trans_ids, to_trans_ids
1057
def _from_file_data(self, from_trans_id, from_versioned, file_id):
1058
"""Get data about a file in the from (tree) state
1060
Return a (name, parent, kind, executable) tuple
1062
from_path = self._tree_id_paths.get(from_trans_id)
1064
# get data from working tree if versioned
1065
from_entry = self._tree.inventory[file_id]
1066
from_name = from_entry.name
1067
from_parent = from_entry.parent_id
1070
if from_path is None:
1071
# File does not exist in FROM state
1075
# File exists, but is not versioned. Have to use path-
1077
from_name = os.path.basename(from_path)
1078
tree_parent = self.get_tree_parent(from_trans_id)
1079
from_parent = self.tree_file_id(tree_parent)
1080
if from_path is not None:
1081
from_kind, from_executable, from_stats = \
1082
self._tree._comparison_data(from_entry, from_path)
1085
from_executable = False
1086
return from_name, from_parent, from_kind, from_executable
1088
def _to_file_data(self, to_trans_id, from_trans_id, from_executable):
1089
"""Get data about a file in the to (target) state
1091
Return a (name, parent, kind, executable) tuple
1093
to_name = self.final_name(to_trans_id)
1095
to_kind = self.final_kind(to_trans_id)
1098
to_parent = self.final_file_id(self.final_parent(to_trans_id))
1099
if to_trans_id in self._new_executability:
1100
to_executable = self._new_executability[to_trans_id]
1101
elif to_trans_id == from_trans_id:
1102
to_executable = from_executable
1104
to_executable = False
1105
return to_name, to_parent, to_kind, to_executable
1107
def _iter_changes(self):
1108
"""Produce output in the same format as Tree._iter_changes.
1110
Will produce nonsensical results if invoked while inventory/filesystem
1111
conflicts (as reported by TreeTransform.find_conflicts()) are present.
1113
This reads the Transform, but only reproduces changes involving a
1114
file_id. Files that are not versioned in either of the FROM or TO
1115
states are not reflected.
1117
final_paths = FinalPaths(self)
1118
from_trans_ids, to_trans_ids = self._get_file_id_maps()
1120
# Now iterate through all active file_ids
1121
for file_id in set(from_trans_ids.keys() + to_trans_ids.keys()):
1123
from_trans_id = from_trans_ids.get(file_id)
1124
# find file ids, and determine versioning state
1125
if from_trans_id is None:
1126
from_versioned = False
1127
from_trans_id = to_trans_ids[file_id]
1129
from_versioned = True
1130
to_trans_id = to_trans_ids.get(file_id)
1131
if to_trans_id is None:
1132
to_versioned = False
1133
to_trans_id = from_trans_id
1137
from_name, from_parent, from_kind, from_executable = \
1138
self._from_file_data(from_trans_id, from_versioned, file_id)
1140
to_name, to_parent, to_kind, to_executable = \
1141
self._to_file_data(to_trans_id, from_trans_id, from_executable)
1143
if not from_versioned:
1146
from_path = self._tree_id_paths.get(from_trans_id)
1147
if not to_versioned:
1150
to_path = final_paths.get_path(to_trans_id)
1151
if from_kind != to_kind:
1153
elif to_kind in ('file', 'symlink') and (
1154
to_trans_id != from_trans_id or
1155
to_trans_id in self._new_contents):
1157
if (not modified and from_versioned == to_versioned and
1158
from_parent==to_parent and from_name == to_name and
1159
from_executable == to_executable):
1161
results.append((file_id, (from_path, to_path), modified,
1162
(from_versioned, to_versioned),
1163
(from_parent, to_parent),
1164
(from_name, to_name),
1165
(from_kind, to_kind),
1166
(from_executable, to_executable)))
1167
return iter(sorted(results, key=lambda x:x[1]))
1170
864
def joinpath(parent, child):
1171
865
"""Join tree-relative paths, handling the tree root specially"""
1172
866
if parent is None or parent == "":
1208
902
file_ids.sort(key=tree.id2path)
1212
905
def build_tree(tree, wt):
1213
"""Create working tree for a branch, using a TreeTransform.
1215
This function should be used on empty trees, having a tree root at most.
1216
(see merge and revert functionality for working with existing trees)
1218
Existing files are handled like so:
1220
- Existing bzrdirs take precedence over creating new items. They are
1221
created as '%s.diverted' % name.
1222
- Otherwise, if the content on disk matches the content we are building,
1223
it is silently replaced.
1224
- Otherwise, conflict resolution will move the old file to 'oldname.moved'.
1226
wt.lock_tree_write()
1230
return _build_tree(tree, wt)
1236
def _build_tree(tree, wt):
1237
"""See build_tree."""
1238
if len(wt.inventory) > 1: # more than just a root
1239
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
906
"""Create working tree for a branch, using a Transaction."""
1240
907
file_trans_id = {}
1241
908
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1242
909
pp = ProgressPhase("Build phase", 2, top_pb)
1243
if tree.inventory.root is not None:
1244
# This is kind of a hack: we should be altering the root
1245
# as part of the regular tree shape diff logic.
1246
# The conditional test here is to avoid doing an
1247
# expensive operation (flush) every time the root id
1248
# is set within the tree, nor setting the root and thus
1249
# marking the tree as dirty, because we use two different
1250
# idioms here: tree interfaces and inventory interfaces.
1251
if wt.path2id('') != tree.inventory.root.file_id:
1252
wt.set_root_id(tree.inventory.root.file_id)
1254
910
tt = TreeTransform(wt)
1258
file_trans_id[wt.get_root_id()] = \
1259
tt.trans_id_tree_file_id(wt.get_root_id())
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)
1260
915
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1262
for num, (tree_path, entry) in \
1263
enumerate(tree.inventory.iter_entries_by_dir()):
1264
pb.update("Building tree", num, len(tree.inventory))
917
for num, file_id in enumerate(file_ids):
918
pb.update("Building tree", num, len(file_ids))
919
entry = tree.inventory[file_id]
1265
920
if entry.parent_id is None:
1268
file_id = entry.file_id
1269
target_path = wt.abspath(tree_path)
1271
kind = file_kind(target_path)
1275
if kind == "directory":
1277
bzrdir.BzrDir.open(target_path)
1278
except errors.NotBranchError:
1282
if (file_id not in divert and
1283
_content_match(tree, entry, file_id, kind,
1285
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1286
if kind == 'directory':
1288
922
if entry.parent_id not in file_trans_id:
1289
raise AssertionError(
1290
'entry %s parent id %r is not in file_trans_id %r'
1291
% (entry, entry.parent_id, file_trans_id))
923
raise repr(entry.parent_id)
1292
924
parent_id = file_trans_id[entry.parent_id]
1293
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
925
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1296
new_trans_id = file_trans_id[file_id]
1297
old_parent = tt.trans_id_tree_path(tree_path)
1298
_reparent_children(tt, old_parent, new_trans_id)
1302
divert_trans = set(file_trans_id[f] for f in divert)
1303
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1304
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1305
conflicts = cook_conflicts(raw_conflicts, tt)
1306
for conflict in conflicts:
1309
wt.add_conflicts(conflicts)
1310
except errors.UnsupportedOperation:
1315
933
top_pb.finished()
1319
def _reparent_children(tt, old_parent, new_parent):
1320
for child in tt.iter_tree_children(old_parent):
1321
tt.adjust_path(tt.final_name(child), new_parent, child)
1324
def _content_match(tree, entry, file_id, kind, target_path):
1325
if entry.kind != kind:
1327
if entry.kind == "directory":
1329
if entry.kind == "file":
1330
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1332
elif entry.kind == "symlink":
1333
if tree.get_symlink_target(file_id) == os.readlink(target_path):
1338
def resolve_checkout(tt, conflicts, divert):
1339
new_conflicts = set()
1340
for c_type, conflict in ((c[0], c) for c in conflicts):
1341
# Anything but a 'duplicate' would indicate programmer error
1342
assert c_type == 'duplicate', c_type
1343
# Now figure out which is new and which is old
1344
if tt.new_contents(conflict[1]):
1345
new_file = conflict[1]
1346
old_file = conflict[2]
1348
new_file = conflict[2]
1349
old_file = conflict[1]
1351
# We should only get here if the conflict wasn't completely
1353
final_parent = tt.final_parent(old_file)
1354
if new_file in divert:
1355
new_name = tt.final_name(old_file)+'.diverted'
1356
tt.adjust_path(new_name, final_parent, new_file)
1357
new_conflicts.add((c_type, 'Diverted to',
1358
new_file, old_file))
1360
new_name = tt.final_name(old_file)+'.moved'
1361
tt.adjust_path(new_name, final_parent, old_file)
1362
new_conflicts.add((c_type, 'Moved existing file to',
1363
old_file, new_file))
1364
return new_conflicts
1367
935
def new_by_entry(tt, entry, parent_id, tree):
1368
936
"""Create a new file according to its inventory entry"""
1373
941
executable = tree.is_executable(entry.file_id)
1374
942
return tt.new_file(name, parent_id, contents, entry.file_id,
1376
elif kind in ('directory', 'tree-reference'):
1377
trans_id = tt.new_directory(name, parent_id, entry.file_id)
1378
if kind == 'tree-reference':
1379
tt.set_tree_reference(entry.reference_revision, trans_id)
944
elif kind == 'directory':
945
return tt.new_directory(name, parent_id, entry.file_id)
1381
946
elif kind == 'symlink':
1382
947
target = tree.get_symlink_target(entry.file_id)
1383
948
return tt.new_symlink(name, parent_id, target, entry.file_id)
1385
raise errors.BadFileKindError(name, kind)
1387
950
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
1388
951
"""Create new file contents according to an inventory entry."""
1389
952
if entry.kind == "file":
1391
954
lines = tree.get_file(entry.file_id).readlines()
1392
955
tt.create_file(lines, trans_id, mode_id=mode_id)
1393
956
elif entry.kind == "symlink":
1501
1065
return has_contents, contents_mod, meta_mod
1504
def revert(working_tree, target_tree, filenames, backups=False,
1505
pb=DummyProgress(), change_reporter=None):
1068
def revert(working_tree, target_tree, filenames, backups=False,
1069
pb=DummyProgress()):
1506
1070
"""Revert a working tree's contents to those of a target tree."""
1507
target_tree.lock_read()
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
1508
1075
tt = TreeTransform(working_tree, pb)
1510
pp = ProgressPhase("Revert phase", 3, pb)
1512
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1514
merge_modified = _alter_files(working_tree, target_tree, tt,
1515
child_pb, filenames, backups)
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]
1517
1124
child_pb.finished()
1518
1125
pp.next_phase()
1523
1130
child_pb.finished()
1524
1131
conflicts = cook_conflicts(raw_conflicts, tt)
1526
change_reporter = delta._ChangeReporter(
1527
unversioned_filter=working_tree.is_ignored)
1528
delta.report_changes(tt._iter_changes(), change_reporter)
1529
1132
for conflict in conflicts:
1530
1133
warning(conflict)
1531
1134
pp.next_phase()
1533
working_tree.set_merge_modified(merge_modified)
1136
working_tree.set_merge_modified({})
1535
target_tree.unlock()
1538
1140
return conflicts
1541
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
1543
merge_modified = working_tree.merge_modified()
1544
change_list = target_tree._iter_changes(working_tree,
1545
specific_files=specific_files, pb=pb)
1546
if target_tree.inventory.root is None:
1552
for id_num, (file_id, path, changed_content, versioned, parent, name,
1553
kind, executable) in enumerate(change_list):
1554
if skip_root and file_id[0] is not None and parent[0] is None:
1556
trans_id = tt.trans_id_file_id(file_id)
1559
keep_content = False
1560
if kind[0] == 'file' and (backups or kind[1] is None):
1561
wt_sha1 = working_tree.get_file_sha1(file_id)
1562
if merge_modified.get(file_id) != wt_sha1:
1563
# acquire the basis tree lazily to prevent the
1564
# expense of accessing it when it's not needed ?
1565
# (Guessing, RBC, 200702)
1566
if basis_tree is None:
1567
basis_tree = working_tree.basis_tree()
1568
basis_tree.lock_read()
1569
if file_id in basis_tree:
1570
if wt_sha1 != basis_tree.get_file_sha1(file_id):
1572
elif kind[1] is None and not versioned[1]:
1574
if kind[0] is not None:
1575
if not keep_content:
1576
tt.delete_contents(trans_id)
1577
elif kind[1] is not None:
1578
parent_trans_id = tt.trans_id_file_id(parent[0])
1579
by_parent = tt.by_parent()
1580
backup_name = _get_backup_name(name[0], by_parent,
1581
parent_trans_id, tt)
1582
tt.adjust_path(backup_name, parent_trans_id, trans_id)
1583
new_trans_id = tt.create_path(name[0], parent_trans_id)
1584
if versioned == (True, True):
1585
tt.unversion_file(trans_id)
1586
tt.version_file(file_id, new_trans_id)
1587
# New contents should have the same unix perms as old
1590
trans_id = new_trans_id
1591
if kind[1] == 'directory':
1592
tt.create_directory(trans_id)
1593
elif kind[1] == 'symlink':
1594
tt.create_symlink(target_tree.get_symlink_target(file_id),
1596
elif kind[1] == 'file':
1597
tt.create_file(target_tree.get_file_lines(file_id),
1599
if basis_tree is None:
1600
basis_tree = working_tree.basis_tree()
1601
basis_tree.lock_read()
1602
new_sha1 = target_tree.get_file_sha1(file_id)
1603
if (file_id in basis_tree and new_sha1 ==
1604
basis_tree.get_file_sha1(file_id)):
1605
if file_id in merge_modified:
1606
del merge_modified[file_id]
1608
merge_modified[file_id] = new_sha1
1610
# preserve the execute bit when backing up
1611
if keep_content and executable[0] == executable[1]:
1612
tt.set_executability(executable[1], trans_id)
1614
assert kind[1] is None
1615
if versioned == (False, True):
1616
tt.version_file(file_id, trans_id)
1617
if versioned == (True, False):
1618
tt.unversion_file(trans_id)
1619
if (name[1] is not None and
1620
(name[0] != name[1] or parent[0] != parent[1])):
1622
name[1], tt.trans_id_file_id(parent[1]), trans_id)
1623
if executable[0] != executable[1] and kind[1] == "file":
1624
tt.set_executability(executable[1], trans_id)
1626
if basis_tree is not None:
1628
return merge_modified
1631
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
1143
def resolve_conflicts(tt, pb=DummyProgress()):
1632
1144
"""Make many conflict-resolution attempts, but die if they fail"""
1633
if pass_func is None:
1634
pass_func = conflict_pass
1635
1145
new_conflicts = set()
1637
1147
for n in range(10):