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"
147
187
"""Change the path that is assigned to a transaction id."""
148
188
if trans_id == self._new_root:
149
189
raise CantMoveRoot
190
previous_parent = self._new_parent.get(trans_id)
191
previous_name = self._new_name.get(trans_id)
150
192
self._new_name[trans_id] = name
151
193
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)
153
218
def adjust_root_path(self, name, parent):
154
219
"""Emulate moving the root by moving all children, instead.
211
276
def canonical_path(self, path):
212
277
"""Get the canonical tree-relative path"""
213
278
# 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))
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
218
295
def trans_id_tree_path(self, path):
219
296
"""Determine (and maybe set) the transaction ID for a tree path."""
682
789
If filesystem or inventory conflicts are present, MalformedTransform
792
If apply succeeds, finalize is not necessary.
685
794
conflicts = self.find_conflicts()
686
795
if len(conflicts) != 0:
687
796
raise MalformedTransform(conflicts=conflicts)
689
797
inv = self._tree.inventory
690
799
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
692
801
child_pb.update('Apply phase', 0, 2)
693
self._apply_removals(inv, limbo_inv)
802
self._apply_removals(inv, inventory_delta)
694
803
child_pb.update('Apply phase', 1, 2)
695
modified_paths = self._apply_insertions(inv, limbo_inv)
804
modified_paths = self._apply_insertions(inv, inventory_delta)
697
806
child_pb.finished()
698
self._tree._write_inventory(inv)
807
self._tree.apply_inventory_delta(inventory_delta)
699
808
self.__done = True
701
return _TransformResults(modified_paths)
810
return _TransformResults(modified_paths, self.rename_count)
703
812
def _limbo_name(self, trans_id):
704
813
"""Generate the limbo name of a file"""
705
return pathjoin(self._limbodir, trans_id)
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
707
def _apply_removals(self, inv, limbo_inv):
845
def _apply_removals(self, inv, inventory_delta):
708
846
"""Perform tree operations that remove directory/inventory names.
710
848
That is, delete files that are to be deleted, and put any files that
775
915
if trans_id in self._new_id:
777
917
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
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
790
946
if trans_id in self._new_executability:
791
self._set_executability(path, inv, trans_id)
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,
793
957
child_pb.finished()
794
958
return modified_paths
796
def _set_executability(self, path, inv, trans_id):
960
def _set_executability(self, path, entry, trans_id):
797
961
"""Set the executability of versioned files """
798
file_id = inv.path2id(path)
799
962
new_executability = self._new_executability[trans_id]
800
inv[file_id].executable = new_executability
963
entry.executable = new_executability
801
964
if supports_executable():
802
965
abspath = self._tree.abspath(path)
803
966
current_mode = os.stat(abspath).st_mode
861
1027
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]))
864
1170
def joinpath(parent, child):
865
1171
"""Join tree-relative paths, handling the tree root specially"""
866
1172
if parent is None or parent == "":
902
1208
file_ids.sort(key=tree.id2path)
905
1212
def build_tree(tree, wt):
906
"""Create working tree for a branch, using a Transaction."""
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)
907
1240
file_trans_id = {}
908
1241
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
909
1242
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)
910
1254
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)
1258
file_trans_id[wt.get_root_id()] = \
1259
tt.trans_id_tree_file_id(wt.get_root_id())
915
1260
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]
1262
for num, (tree_path, entry) in \
1263
enumerate(tree.inventory.iter_entries_by_dir()):
1264
pb.update("Building tree", num, len(tree.inventory))
920
1265
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':
922
1288
if entry.parent_id not in file_trans_id:
923
raise repr(entry.parent_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))
924
1292
parent_id = file_trans_id[entry.parent_id]
925
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1293
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:
933
1315
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
935
1367
def new_by_entry(tt, entry, parent_id, tree):
936
1368
"""Create a new file according to its inventory entry"""
941
1373
executable = tree.is_executable(entry.file_id)
942
1374
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)
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)
946
1381
elif kind == 'symlink':
947
1382
target = tree.get_symlink_target(entry.file_id)
948
1383
return tt.new_symlink(name, parent_id, target, entry.file_id)
1385
raise errors.BadFileKindError(name, kind)
950
1387
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
951
1388
"""Create new file contents according to an inventory entry."""
952
1389
if entry.kind == "file":
954
1391
lines = tree.get_file(entry.file_id).readlines()
955
1392
tt.create_file(lines, trans_id, mode_id=mode_id)
956
1393
elif entry.kind == "symlink":
1065
1501
return has_contents, contents_mod, meta_mod
1068
def revert(working_tree, target_tree, filenames, backups=False,
1069
pb=DummyProgress()):
1504
def revert(working_tree, target_tree, filenames, backups=False,
1505
pb=DummyProgress(), change_reporter=None):
1070
1506
"""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
1507
target_tree.lock_read()
1075
1508
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]
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)
1124
1517
child_pb.finished()
1125
1518
pp.next_phase()
1130
1523
child_pb.finished()
1131
1524
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)
1132
1529
for conflict in conflicts:
1133
1530
warning(conflict)
1134
1531
pp.next_phase()
1136
working_tree.set_merge_modified({})
1533
working_tree.set_merge_modified(merge_modified)
1535
target_tree.unlock()
1140
1538
return conflicts
1143
def resolve_conflicts(tt, pb=DummyProgress()):
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):
1144
1632
"""Make many conflict-resolution attempts, but die if they fail"""
1633
if pass_func is None:
1634
pass_func = conflict_pass
1145
1635
new_conflicts = set()
1147
1637
for n in range(10):
1188
1678
trans_id = conflict[1]
1190
1680
tt.cancel_deletion(trans_id)
1191
new_conflicts.add((c_type, 'Not deleting', trans_id))
1681
new_conflicts.add(('deleting parent', 'Not deleting',
1192
1683
except KeyError:
1193
1684
tt.create_directory(trans_id)
1194
new_conflicts.add((c_type, 'Created directory.', trans_id))
1685
new_conflicts.add((c_type, 'Created directory', trans_id))
1195
1686
elif c_type == 'unversioned parent':
1196
1687
tt.version_file(tt.inactive_file_id(conflict[1]), conflict[1])
1197
1688
new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
1198
1689
return new_conflicts
1200
1692
def cook_conflicts(raw_conflicts, tt):
1201
1693
"""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
1694
from bzrlib.conflicts import Conflict
1695
conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
1696
return sorted(conflict_iter, key=Conflict.sort_key)
1210
return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1212
1699
def iter_cook_conflicts(raw_conflicts, tt):
1213
1700
from bzrlib.conflicts import Conflict