195
199
previous_name = self._new_name.get(trans_id)
196
200
self._new_name[trans_id] = name
197
201
self._new_parent[trans_id] = parent
202
if parent == ROOT_PARENT:
203
if self._new_root is not None:
204
raise ValueError("Cannot have multiple roots.")
205
self._new_root = trans_id
198
206
if (trans_id in self._limbo_files and
199
207
trans_id not in self._needs_rename):
200
208
self._rename_in_limbo([trans_id])
472
484
if filesystem_only:
473
id_sets = (self._needs_rename, self._new_executability)
485
stale_ids = self._needs_rename.difference(self._new_name)
486
stale_ids.difference_update(self._new_parent)
487
stale_ids.difference_update(self._new_contents)
488
stale_ids.difference_update(self._new_id)
489
needs_rename = self._needs_rename.difference(stale_ids)
490
id_sets = (needs_rename, self._new_executability)
475
492
id_sets = (self._new_name, self._new_parent, self._new_contents,
476
493
self._new_id, self._new_executability)
478
495
new_ids.update(id_set)
479
496
return sorted(FinalPaths(self).get_paths(new_ids))
498
def _inventory_altered(self):
499
"""Get the trans_ids and paths of files needing new inv entries."""
501
for id_set in [self._new_name, self._new_parent, self._new_id,
502
self._new_executability]:
503
new_ids.update(id_set)
504
changed_kind = set(self._removed_contents)
505
changed_kind.intersection_update(self._new_contents)
506
changed_kind.difference_update(new_ids)
507
changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
509
new_ids.update(changed_kind)
510
return sorted(FinalPaths(self).get_paths(new_ids))
481
512
def tree_kind(self, trans_id):
482
513
"""Determine the file kind in the working tree.
870
902
self._limbo_files[trans_id] = limbo_name
871
903
return limbo_name
873
def _set_executability(self, path, entry, trans_id):
905
def _set_executability(self, path, trans_id):
874
906
"""Set the executability of versioned files """
875
new_executability = self._new_executability[trans_id]
876
if entry is not None:
877
entry.executable = new_executability
878
907
if supports_executable():
908
new_executability = self._new_executability[trans_id]
879
909
abspath = self._tree.abspath(path)
880
910
current_mode = os.stat(abspath).st_mode
881
911
if new_executability:
1203
1233
conflicts = self.find_conflicts()
1204
1234
if len(conflicts) != 0:
1205
1235
raise MalformedTransform(conflicts=conflicts)
1206
if precomputed_delta is None:
1207
new_inventory_delta = []
1208
inventory_delta = new_inventory_delta
1210
new_inventory_delta = None
1211
inventory_delta = precomputed_delta
1212
1236
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1238
if precomputed_delta is None:
1239
child_pb.update('Apply phase', 0, 2)
1240
inventory_delta = self._generate_inventory_delta()
1243
inventory_delta = precomputed_delta
1214
1245
if _mover is None:
1215
1246
mover = _FileMover()
1219
child_pb.update('Apply phase', 0, 2)
1220
self._apply_removals(new_inventory_delta, mover)
1221
child_pb.update('Apply phase', 1, 2)
1222
modified_paths = self._apply_insertions(new_inventory_delta,
1250
child_pb.update('Apply phase', 0 + offset, 2 + offset)
1251
self._apply_removals(mover)
1252
child_pb.update('Apply phase', 1 + offset, 2 + offset)
1253
modified_paths = self._apply_insertions(mover)
1225
1255
mover.rollback()
1233
1263
self.finalize()
1234
1264
return _TransformResults(modified_paths, self.rename_count)
1236
def _apply_removals(self, inventory_delta, mover):
1266
def _generate_inventory_delta(self):
1267
"""Generate an inventory delta for the current transform."""
1268
inventory_delta = []
1269
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1270
new_paths = self._inventory_altered()
1271
total_entries = len(new_paths) + len(self._removed_id)
1273
for num, trans_id in enumerate(self._removed_id):
1275
child_pb.update('removing file', num, total_entries)
1276
if trans_id == self._new_root:
1277
file_id = self._tree.get_root_id()
1279
file_id = self.tree_file_id(trans_id)
1280
# File-id isn't really being deleted, just moved
1281
if file_id in self._r_new_id:
1283
path = self._tree_id_paths[trans_id]
1284
inventory_delta.append((path, None, file_id, None))
1285
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1287
entries = self._tree.iter_entries_by_dir(
1288
new_path_file_ids.values())
1289
old_paths = dict((e.file_id, p) for p, e in entries)
1291
for num, (path, trans_id) in enumerate(new_paths):
1293
child_pb.update('adding file',
1294
num + len(self._removed_id), total_entries)
1295
file_id = new_path_file_ids[trans_id]
1300
kind = self.final_kind(trans_id)
1302
kind = self._tree.stored_kind(file_id)
1303
parent_trans_id = self.final_parent(trans_id)
1304
parent_file_id = new_path_file_ids.get(parent_trans_id)
1305
if parent_file_id is None:
1306
parent_file_id = self.final_file_id(parent_trans_id)
1307
if trans_id in self._new_reference_revision:
1308
new_entry = inventory.TreeReference(
1310
self._new_name[trans_id],
1311
self.final_file_id(self._new_parent[trans_id]),
1312
None, self._new_reference_revision[trans_id])
1314
new_entry = inventory.make_entry(kind,
1315
self.final_name(trans_id),
1316
parent_file_id, file_id)
1317
old_path = old_paths.get(new_entry.file_id)
1318
new_executability = self._new_executability.get(trans_id)
1319
if new_executability is not None:
1320
new_entry.executable = new_executability
1321
inventory_delta.append(
1322
(old_path, path, new_entry.file_id, new_entry))
1325
return inventory_delta
1327
def _apply_removals(self, mover):
1237
1328
"""Perform tree operations that remove directory/inventory names.
1239
1330
That is, delete files that are to be deleted, and put any files that
1264
1355
self.rename_count += 1
1265
if (trans_id in self._removed_id
1266
and inventory_delta is not None):
1267
if trans_id == self._new_root:
1268
file_id = self._tree.get_root_id()
1270
file_id = self.tree_file_id(trans_id)
1271
# File-id isn't really being deleted, just moved
1272
if file_id in self._r_new_id:
1274
inventory_delta.append((path, None, file_id, None))
1276
1357
child_pb.finished()
1278
def _apply_insertions(self, inventory_delta, mover):
1359
def _apply_insertions(self, mover):
1279
1360
"""Perform tree operations that insert directory/inventory names.
1281
1362
That is, create any files that need to be created, and restore from
1285
1366
If inventory_delta is None, no inventory delta is calculated, and
1286
1367
no list of modified paths is returned.
1288
new_paths = self.new_paths(filesystem_only=(inventory_delta is None))
1369
new_paths = self.new_paths(filesystem_only=True)
1289
1370
modified_paths = []
1291
1371
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1293
if inventory_delta is not None:
1294
entries = self._tree.iter_entries_by_dir(
1295
new_path_file_ids.values())
1296
old_paths = dict((e.file_id, p) for p, e in entries)
1297
1373
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1299
1375
for num, (path, trans_id) in enumerate(new_paths):
1301
1376
if (num % 10) == 0:
1302
1377
child_pb.update('adding file', num, len(new_paths))
1303
1378
full_path = self._tree.abspath(path)
1312
1387
self.rename_count += 1
1313
if inventory_delta is not None:
1314
if (trans_id in self._new_contents or
1315
self.path_changed(trans_id)):
1316
if trans_id in self._new_contents:
1317
modified_paths.append(full_path)
1318
completed_new.append(trans_id)
1319
file_id = new_path_file_ids[trans_id]
1320
if file_id is not None and (trans_id in self._new_id or
1321
trans_id in self._new_name or
1322
trans_id in self._new_parent
1323
or trans_id in self._new_executability):
1325
kind = self.final_kind(trans_id)
1327
kind = self._tree.stored_kind(file_id)
1328
parent_trans_id = self.final_parent(trans_id)
1329
parent_file_id = new_path_file_ids.get(parent_trans_id)
1330
if parent_file_id is None:
1331
parent_file_id = self.final_file_id(
1333
if trans_id in self._new_reference_revision:
1334
new_entry = inventory.TreeReference(
1336
self._new_name[trans_id],
1337
self.final_file_id(self._new_parent[trans_id]),
1338
None, self._new_reference_revision[trans_id])
1340
new_entry = inventory.make_entry(kind,
1341
self.final_name(trans_id),
1342
parent_file_id, file_id)
1343
old_path = old_paths.get(new_entry.file_id)
1344
inventory_delta.append(
1345
(old_path, path, new_entry.file_id, new_entry))
1388
if (trans_id in self._new_contents or
1389
self.path_changed(trans_id)):
1390
if trans_id in self._new_contents:
1391
modified_paths.append(full_path)
1347
1392
if trans_id in self._new_executability:
1348
self._set_executability(path, new_entry, trans_id)
1393
self._set_executability(path, trans_id)
1350
1395
child_pb.finished()
1351
if inventory_delta is None:
1352
self._new_contents.clear()
1354
for trans_id in completed_new:
1355
del self._new_contents[trans_id]
1396
self._new_contents.clear()
1356
1397
return modified_paths
1367
1408
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1368
1409
tree.lock_read()
1369
limbodir = tempfile.mkdtemp(prefix='bzr-limbo-')
1410
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1370
1411
TreeTransformBase.__init__(self, tree, limbodir, pb, case_sensitive)
1372
1413
def canonical_path(self, path):
1423
1465
# InterTree.iter_changes.
1424
1466
return (changes is not None and changes[2])
1468
def _get_repository(self):
1469
repo = getattr(self._transform._tree, '_repository', None)
1471
repo = self._transform._tree.branch.repository
1474
def _iter_parent_trees(self):
1475
for revision_id in self.get_parent_ids():
1477
yield self.revision_tree(revision_id)
1478
except errors.NoSuchRevisionInTree:
1479
yield self._get_repository().revision_tree(revision_id)
1426
1481
def _get_file_revision(self, file_id, vf, tree_revision):
1427
return self._transform._tree._get_file_revision(file_id, vf,
1482
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1483
self._iter_parent_trees()]
1484
vf.add_lines((file_id, tree_revision), parent_keys,
1485
self.get_file(file_id).readlines())
1486
repo = self._get_repository()
1487
base_vf = repo.texts
1488
if base_vf not in vf.fallback_versionedfiles:
1489
vf.fallback_versionedfiles.append(base_vf)
1490
return tree_revision
1430
1492
def _stat_limbo_file(self, file_id):
1431
1493
trans_id = self._transform.trans_id_file_id(file_id)
1523
1585
parent_file_id, file_id)
1524
1586
yield new_entry, trans_id
1526
def iter_entries_by_dir(self, specific_file_ids=None):
1527
# This may not be a maximally efficient implementation, but it is
1528
# reasonably straightforward. An implementation that grafts the
1529
# TreeTransform changes onto the tree's iter_entries_by_dir results
1530
# might be more efficient, but requires tricky inferences about stack
1588
def _list_files_by_dir(self):
1532
1589
todo = [ROOT_PARENT]
1533
1590
ordered_ids = []
1534
1591
while len(todo) > 0:
1540
1597
todo.extend(reversed(children))
1541
1598
for trans_id in children:
1542
1599
ordered_ids.append((trans_id, parent_file_id))
1602
def iter_entries_by_dir(self, specific_file_ids=None):
1603
# This may not be a maximally efficient implementation, but it is
1604
# reasonably straightforward. An implementation that grafts the
1605
# TreeTransform changes onto the tree's iter_entries_by_dir results
1606
# might be more efficient, but requires tricky inferences about stack
1608
ordered_ids = self._list_files_by_dir()
1543
1609
for entry, trans_id in self._make_inv_entries(ordered_ids,
1544
1610
specific_file_ids):
1545
1611
yield unicode(self._final_paths.get_path(trans_id)), entry
1613
def list_files(self, include_root=False):
1614
"""See Tree.list_files."""
1615
# XXX This should behave like WorkingTree.list_files, but is really
1616
# more like RevisionTree.list_files.
1617
for path, entry in self.iter_entries_by_dir():
1618
if entry.name == '' and not include_root:
1620
yield path, 'V', entry.kind, entry.file_id, entry
1547
1622
def kind(self, file_id):
1548
1623
trans_id = self._transform.trans_id_file_id(file_id)
1549
1624
return self._transform.final_kind(trans_id)
1675
1750
name = self._transform._limbo_name(trans_id)
1676
1751
return os.readlink(name)
1678
def list_files(self, include_root=False):
1679
return self._transform._tree.list_files(include_root)
1681
def walkdirs(self, prefix=""):
1682
return self._transform._tree.walkdirs(prefix)
1753
def walkdirs(self, prefix=''):
1754
pending = [self._transform.root]
1755
while len(pending) > 0:
1756
parent_id = pending.pop()
1759
prefix = prefix.rstrip('/')
1760
parent_path = self._final_paths.get_path(parent_id)
1761
parent_file_id = self._transform.final_file_id(parent_id)
1762
for child_id in self._all_children(parent_id):
1763
path_from_root = self._final_paths.get_path(child_id)
1764
basename = self._transform.final_name(child_id)
1765
file_id = self._transform.final_file_id(child_id)
1767
kind = self._transform.final_kind(child_id)
1768
versioned_kind = kind
1771
versioned_kind = self._transform._tree.stored_kind(file_id)
1772
if versioned_kind == 'directory':
1773
subdirs.append(child_id)
1774
children.append((path_from_root, basename, kind, None,
1775
file_id, versioned_kind))
1777
if parent_path.startswith(prefix):
1778
yield (parent_path, parent_file_id), children
1779
pending.extend(sorted(subdirs, key=self._final_paths.get_path,
1684
1782
def get_parent_ids(self):
1685
return self._transform._tree.get_parent_ids()
1783
return self._parent_ids
1785
def set_parent_ids(self, parent_ids):
1786
self._parent_ids = parent_ids
1687
1788
def get_revision_tree(self, revision_id):
1688
1789
return self._transform._tree.get_revision_tree(revision_id)
2062
2163
tt = TreeTransform(working_tree, pb)
2064
2165
pp = ProgressPhase("Revert phase", 3, pb)
2066
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2068
merge_modified = _alter_files(working_tree, target_tree, tt,
2069
child_pb, filenames, backups)
2073
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2075
raw_conflicts = resolve_conflicts(tt, child_pb,
2076
lambda t, c: conflict_pass(t, c, target_tree))
2079
conflicts = cook_conflicts(raw_conflicts, tt)
2166
conflicts, merge_modified = _prepare_revert_transform(
2167
working_tree, target_tree, tt, filenames, backups, pp)
2080
2168
if change_reporter:
2081
2169
change_reporter = delta._ChangeReporter(
2082
2170
unversioned_filter=working_tree.is_ignored)
2093
2181
return conflicts
2184
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2185
backups, pp, basis_tree=None,
2186
merge_modified=None):
2188
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2190
if merge_modified is None:
2191
merge_modified = working_tree.merge_modified()
2192
merge_modified = _alter_files(working_tree, target_tree, tt,
2193
child_pb, filenames, backups,
2194
merge_modified, basis_tree)
2198
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2200
raw_conflicts = resolve_conflicts(tt, child_pb,
2201
lambda t, c: conflict_pass(t, c, target_tree))
2204
conflicts = cook_conflicts(raw_conflicts, tt)
2205
return conflicts, merge_modified
2096
2208
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
2098
merge_modified = working_tree.merge_modified()
2209
backups, merge_modified, basis_tree=None):
2210
if basis_tree is not None:
2211
basis_tree.lock_read()
2099
2212
change_list = target_tree.iter_changes(working_tree,
2100
2213
specific_files=specific_files, pb=pb)
2101
if target_tree.inventory.root is None:
2214
if target_tree.get_root_id() is None:
2102
2215
skip_root = True
2104
2217
skip_root = False
2107
2219
deferred_files = []
2108
2220
for id_num, (file_id, path, changed_content, versioned, parent, name,
2145
2257
mode_id = trans_id
2146
2258
trans_id = new_trans_id
2147
if kind[1] == 'directory':
2259
if kind[1] in ('directory', 'tree-reference'):
2148
2260
tt.create_directory(trans_id)
2261
if kind[1] == 'tree-reference':
2262
revision = target_tree.get_reference_revision(file_id,
2264
tt.set_tree_reference(revision, trans_id)
2149
2265
elif kind[1] == 'symlink':
2150
2266
tt.create_symlink(target_tree.get_symlink_target(file_id),
2171
2287
tt.version_file(file_id, trans_id)
2172
2288
if versioned == (True, False):
2173
2289
tt.unversion_file(trans_id)
2174
if (name[1] is not None and
2290
if (name[1] is not None and
2175
2291
(name[0] != name[1] or parent[0] != parent[1])):
2177
name[1], tt.trans_id_file_id(parent[1]), trans_id)
2292
if name[1] == '' and parent[1] is None:
2293
parent_trans = ROOT_PARENT
2295
parent_trans = tt.trans_id_file_id(parent[1])
2296
tt.adjust_path(name[1], parent_trans, trans_id)
2178
2297
if executable[0] != executable[1] and kind[1] == "file":
2179
2298
tt.set_executability(executable[1], trans_id)
2180
2299
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(