433
450
del self._new_id[trans_id]
434
451
del self._r_new_id[file_id]
437
"""Determine the paths of all new and changed files"""
453
def new_paths(self, filesystem_only=False):
454
"""Determine the paths of all new and changed files.
456
:param filesystem_only: if True, only calculate values for files
457
that require renames or execute bit changes.
439
fp = FinalPaths(self)
440
for id_set in (self._new_name, self._new_parent, self._new_contents,
441
self._new_id, self._new_executability):
461
id_sets = (self._needs_rename, self._new_executability)
463
id_sets = (self._new_name, self._new_parent, self._new_contents,
464
self._new_id, self._new_executability)
465
for id_set in id_sets:
442
466
new_ids.update(id_set)
443
new_paths = [(fp.get_path(t), t) for t in new_ids]
467
return sorted(FinalPaths(self).get_paths(new_ids))
447
469
def tree_kind(self, trans_id):
448
470
"""Determine the file kind in the working tree.
1224
1252
self.rename_count += 1
1225
if trans_id in self._removed_id:
1253
if (trans_id in self._removed_id
1254
and inventory_delta is not None):
1226
1255
if trans_id == self._new_root:
1227
1256
file_id = self._tree.get_root_id()
1229
1258
file_id = self.tree_file_id(trans_id)
1230
if file_id is not None:
1231
inventory_delta.append((path, None, file_id, None))
1259
# File-id isn't really being deleted, just moved
1260
if file_id in self._r_new_id:
1262
inventory_delta.append((path, None, file_id, None))
1233
1264
child_pb.finished()
1235
def _apply_insertions(self, inv, inventory_delta, mover):
1266
def _apply_insertions(self, inventory_delta, mover):
1236
1267
"""Perform tree operations that insert directory/inventory names.
1238
1269
That is, create any files that need to be created, and restore from
1239
1270
limbo any files that needed renaming. This must be done in strict
1240
1271
parent-to-child order.
1273
If inventory_delta is None, no inventory delta is calculated, and
1274
no list of modified paths is returned.
1242
new_paths = self.new_paths()
1276
new_paths = self.new_paths(filesystem_only=(inventory_delta is None))
1243
1277
modified_paths = []
1244
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1245
1278
completed_new = []
1279
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1281
if inventory_delta is not None:
1282
entries = self._tree.iter_entries_by_dir(
1283
new_path_file_ids.values())
1284
old_paths = dict((e.file_id, p) for p, e in entries)
1285
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1247
1287
for num, (path, trans_id) in enumerate(new_paths):
1248
1288
new_entry = None
1249
child_pb.update('adding file', num, len(new_paths))
1251
kind = self._new_contents[trans_id]
1253
kind = contents = None
1254
if trans_id in self._new_contents or \
1255
self.path_changed(trans_id):
1256
full_path = self._tree.abspath(path)
1257
if trans_id in self._needs_rename:
1290
child_pb.update('adding file', num, len(new_paths))
1291
full_path = self._tree.abspath(path)
1292
if trans_id in self._needs_rename:
1294
mover.rename(self._limbo_name(trans_id), full_path)
1296
# We may be renaming a dangling inventory id
1297
if e.errno != errno.ENOENT:
1300
self.rename_count += 1
1301
if inventory_delta is not None:
1302
if (trans_id in self._new_contents or
1303
self.path_changed(trans_id)):
1304
if trans_id in self._new_contents:
1305
modified_paths.append(full_path)
1306
completed_new.append(trans_id)
1307
file_id = new_path_file_ids[trans_id]
1308
if file_id is not None and (trans_id in self._new_id or
1309
trans_id in self._new_name or
1310
trans_id in self._new_parent
1311
or trans_id in self._new_executability):
1259
mover.rename(self._limbo_name(trans_id), full_path)
1261
# We may be renaming a dangling inventory id
1262
if e.errno != errno.ENOENT:
1313
kind = self.final_kind(trans_id)
1315
kind = self._tree.stored_kind(file_id)
1316
parent_trans_id = self.final_parent(trans_id)
1317
parent_file_id = new_path_file_ids.get(parent_trans_id)
1318
if parent_file_id is None:
1319
parent_file_id = self.final_file_id(
1321
if trans_id in self._new_reference_revision:
1322
new_entry = inventory.TreeReference(
1324
self._new_name[trans_id],
1325
self.final_file_id(self._new_parent[trans_id]),
1326
None, self._new_reference_revision[trans_id])
1265
self.rename_count += 1
1266
if trans_id in self._new_contents:
1267
modified_paths.append(full_path)
1268
completed_new.append(trans_id)
1270
if trans_id in self._new_id:
1272
kind = file_kind(self._tree.abspath(path))
1273
if trans_id in self._new_reference_revision:
1274
new_entry = inventory.TreeReference(
1275
self._new_id[trans_id],
1276
self._new_name[trans_id],
1277
self.final_file_id(self._new_parent[trans_id]),
1278
None, self._new_reference_revision[trans_id])
1280
new_entry = inventory.make_entry(kind,
1281
self.final_name(trans_id),
1282
self.final_file_id(self.final_parent(trans_id)),
1283
self._new_id[trans_id])
1285
if trans_id in self._new_name or trans_id in\
1286
self._new_parent or\
1287
trans_id in self._new_executability:
1288
file_id = self.final_file_id(trans_id)
1289
if file_id is not None:
1290
entry = inv[file_id]
1291
new_entry = entry.copy()
1293
if trans_id in self._new_name or trans_id in\
1295
if new_entry is not None:
1296
new_entry.name = self.final_name(trans_id)
1297
parent = self.final_parent(trans_id)
1298
parent_id = self.final_file_id(parent)
1299
new_entry.parent_id = parent_id
1328
new_entry = inventory.make_entry(kind,
1329
self.final_name(trans_id),
1330
parent_file_id, file_id)
1331
old_path = old_paths.get(new_entry.file_id)
1332
inventory_delta.append(
1333
(old_path, path, new_entry.file_id, new_entry))
1301
1335
if trans_id in self._new_executability:
1302
1336
self._set_executability(path, new_entry, trans_id)
1303
if new_entry is not None:
1304
if new_entry.file_id in inv:
1305
old_path = inv.id2path(new_entry.file_id)
1308
inventory_delta.append((old_path, path,
1312
1338
child_pb.finished()
1313
for trans_id in completed_new:
1314
del self._new_contents[trans_id]
1339
if inventory_delta is None:
1340
self._new_contents.clear()
1342
for trans_id in completed_new:
1343
del self._new_contents[trans_id]
1315
1344
return modified_paths
1359
1388
yield self.trans_id_tree_path(childpath)
1362
class _PreviewTree(object):
1391
class _PreviewTree(tree.Tree):
1363
1392
"""Partial implementation of Tree to support show_diff_trees"""
1365
1394
def __init__(self, transform):
1366
1395
self._transform = transform
1396
self._final_paths = FinalPaths(transform)
1398
def _changes(self, file_id):
1399
for changes in self._transform.iter_changes():
1400
if changes[0] == file_id:
1403
def _content_change(self, file_id):
1404
"""Return True if the content of this file changed"""
1405
changes = self._changes(file_id)
1406
# changes[2] is true if the file content changed. See
1407
# InterTree.iter_changes.
1408
return (changes is not None and changes[2])
1410
def _get_file_revision(self, file_id, vf, tree_revision):
1411
return self._transform._tree._get_file_revision(file_id, vf,
1414
def _stat_limbo_file(self, file_id):
1415
trans_id = self._transform.trans_id_file_id(file_id)
1416
name = self._transform._limbo_name(trans_id)
1417
return os.lstat(name)
1368
1419
def lock_read(self):
1369
1420
# Perhaps in theory, this should lock the TreeTransform?
1372
1423
def unlock(self):
1375
def _iter_changes(self, from_tree, include_unchanged=False,
1427
def inventory(self):
1428
"""This Tree does not use inventory as its backing data."""
1429
raise NotImplementedError(_PreviewTree.inventory)
1431
def get_root_id(self):
1432
return self._transform.final_file_id(self._transform.root)
1434
def all_file_ids(self):
1435
return self._transform._tree.all_file_ids()
1438
return iter(self.all_file_ids())
1440
def paths2ids(self, specific_files, trees=None, require_versioned=False):
1441
"""See Tree.paths2ids"""
1442
to_find = set(specific_files)
1444
for (file_id, paths, changed, versioned, parent, name, kind,
1445
executable) in self._transform.iter_changes():
1446
if paths[1] in to_find:
1447
result.append(file_id)
1448
to_find.remove(paths[1])
1449
result.update(self._transform._tree.paths2ids(to_find,
1450
trees=[], require_versioned=require_versioned))
1453
def path2id(self, path):
1454
return self._transform._tree.path2id(path)
1456
def id2path(self, file_id):
1457
trans_id = self._transform.trans_id_file_id(file_id)
1459
return self._final_paths._determine_path(trans_id)
1461
raise errors.NoSuchId(self, file_id)
1463
def iter_entries_by_dir(self, specific_file_ids=None):
1464
return self._transform._tree.iter_entries_by_dir(specific_file_ids)
1466
def kind(self, file_id):
1467
trans_id = self._transform.trans_id_file_id(file_id)
1468
return self._transform.final_kind(trans_id)
1470
def stored_kind(self, file_id):
1471
return self._transform._tree.stored_kind(file_id)
1473
def get_file_mtime(self, file_id, path=None):
1474
"""See Tree.get_file_mtime"""
1475
if not self._content_change(file_id):
1476
return self._transform._tree.get_file_mtime(file_id, path)
1477
return self._stat_limbo_file(file_id).st_mtime
1479
def get_file_size(self, file_id):
1480
"""See Tree.get_file_size"""
1481
if self.kind(file_id) == 'file':
1482
return self._transform._tree.get_file_size(file_id)
1486
def get_file_sha1(self, file_id, path=None, stat_value=None):
1487
return self._transform._tree.get_file_sha1(file_id)
1489
def is_executable(self, file_id, path=None):
1490
return self._transform._tree.is_executable(file_id, path)
1492
def path_content_summary(self, path):
1493
return self._transform._tree.path_content_summary(path)
1495
def iter_changes(self, from_tree, include_unchanged=False,
1376
1496
specific_files=None, pb=None, extra_trees=None,
1377
1497
require_versioned=True, want_unversioned=False):
1378
"""See InterTree._iter_changes.
1498
"""See InterTree.iter_changes.
1380
1500
This implementation does not support include_unchanged, specific_files,
1381
1501
or want_unversioned. extra_trees, require_versioned, and pb are
1389
1509
raise ValueError('specific_files is not supported')
1390
1510
if want_unversioned:
1391
1511
raise ValueError('want_unversioned is not supported')
1392
return self._transform._iter_changes()
1394
def kind(self, file_id):
1395
trans_id = self._transform.trans_id_file_id(file_id)
1396
return self._transform.final_kind(trans_id)
1398
def get_file_mtime(self, file_id, path=None):
1399
"""See Tree.get_file_mtime"""
1400
trans_id = self._transform.trans_id_file_id(file_id)
1401
name = self._transform._limbo_name(trans_id)
1402
return os.stat(name).st_mtime
1404
def get_file(self, file_id):
1512
return self._transform.iter_changes()
1514
def get_file(self, file_id, path=None):
1405
1515
"""See Tree.get_file"""
1516
if not self._content_change(file_id):
1517
return self._transform._tree.get_file(file_id, path)
1406
1518
trans_id = self._transform.trans_id_file_id(file_id)
1407
1519
name = self._transform._limbo_name(trans_id)
1408
1520
return open(name, 'rb')
1410
def paths2ids(self, specific_files, trees=None, require_versioned=False):
1411
"""See Tree.paths2ids"""
1522
def get_file_text(self, file_id):
1523
text_file = self.get_file(file_id)
1525
return text_file.read()
1529
def annotate_iter(self, file_id,
1530
default_revision=_mod_revision.CURRENT_REVISION):
1531
return self._transform._tree.annotate_iter(file_id,
1532
default_revision=default_revision)
1534
def get_symlink_target(self, file_id):
1535
"""See Tree.get_symlink_target"""
1536
if not self._content_change(file_id):
1537
return self._transform._tree.get_symlink_target(file_id)
1538
trans_id = self._transform.trans_id_file_id(file_id)
1539
name = self._transform._limbo_name(trans_id)
1540
return os.readlink(name)
1542
def list_files(self, include_root=False):
1543
return self._transform._tree.list_files(include_root)
1545
def walkdirs(self, prefix=""):
1546
return self._transform._tree.walkdirs(prefix)
1548
def get_parent_ids(self):
1549
return self._transform._tree.get_parent_ids()
1551
def get_revision_tree(self, revision_id):
1552
return self._transform._tree.get_revision_tree(revision_id)
1415
1555
def joinpath(parent, child):
1546
1705
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1547
1706
if kind == 'directory':
1548
1707
reparent = True
1549
if entry.parent_id not in file_trans_id:
1550
raise AssertionError(
1551
'entry %s parent id %r is not in file_trans_id %r'
1552
% (entry, entry.parent_id, file_trans_id))
1553
1708
parent_id = file_trans_id[entry.parent_id]
1554
1709
if entry.kind == 'file':
1555
1710
# We *almost* replicate new_by_entry, so that we can defer
1556
1711
# getting the file text, and get them all at once.
1557
1712
trans_id = tt.create_path(entry.name, parent_id)
1558
1713
file_trans_id[file_id] = trans_id
1559
tt.version_file(entry.file_id, trans_id)
1560
executable = tree.is_executable(entry.file_id, tree_path)
1561
if executable is not None:
1714
tt.version_file(file_id, trans_id)
1715
executable = tree.is_executable(file_id, tree_path)
1562
1717
tt.set_executability(executable, trans_id)
1563
deferred_contents.append((entry.file_id, trans_id))
1718
deferred_contents.append((file_id, trans_id))
1565
1720
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1568
1723
new_trans_id = file_trans_id[file_id]
1569
1724
old_parent = tt.trans_id_tree_path(tree_path)
1570
1725
_reparent_children(tt, old_parent, new_trans_id)
1571
for num, (trans_id, bytes) in enumerate(
1572
_iter_files_bytes_accelerated(tree, accelerator_tree,
1573
deferred_contents)):
1574
tt.create_file(bytes, trans_id)
1575
pb.update('Adding file contents',
1576
(num + len(tree.inventory) - len(deferred_contents)),
1577
len(tree.inventory))
1726
offset = num + 1 - len(deferred_contents)
1727
_create_files(tt, tree, deferred_contents, pb, offset,
1728
accelerator_tree, hardlink)
1580
1731
pp.next_phase()
1581
1732
divert_trans = set(file_trans_id[f] for f in divert)
1582
1733
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1583
1734
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1735
if len(raw_conflicts) > 0:
1736
precomputed_delta = None
1584
1737
conflicts = cook_conflicts(raw_conflicts, tt)
1585
1738
for conflict in conflicts:
1586
1739
warning(conflict)
1588
1741
wt.add_conflicts(conflicts)
1589
1742
except errors.UnsupportedOperation:
1591
result = tt.apply(no_conflicts=True)
1744
result = tt.apply(no_conflicts=True,
1745
precomputed_delta=precomputed_delta)
1594
1748
top_pb.finished()
1598
def _iter_files_bytes_accelerated(tree, accelerator_tree, desired_files):
1752
def _create_files(tt, tree, desired_files, pb, offset, accelerator_tree,
1754
total = len(desired_files) + offset
1599
1755
if accelerator_tree is None:
1600
1756
new_desired_files = desired_files
1602
iter = accelerator_tree._iter_changes(tree, include_unchanged=True)
1758
iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
1603
1759
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
1760
in iter if not (c or e[0] != e[1]))
1605
1761
new_desired_files = []
1606
for file_id, identifier in desired_files:
1763
for file_id, trans_id in desired_files:
1607
1764
accelerator_path = unchanged.get(file_id)
1608
1765
if accelerator_path is None:
1609
new_desired_files.append((file_id, identifier))
1766
new_desired_files.append((file_id, trans_id))
1611
contents = accelerator_tree.get_file(file_id, accelerator_path)
1614
contents_bytes = (contents.read(),)
1617
yield identifier, contents_bytes
1618
for result in tree.iter_files_bytes(new_desired_files):
1768
pb.update('Adding file contents', count + offset, total)
1770
tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
1773
contents = accelerator_tree.get_file(file_id, accelerator_path)
1775
tt.create_file(contents, trans_id)
1780
for count, (trans_id, contents) in enumerate(tree.iter_files_bytes(
1781
new_desired_files)):
1782
tt.create_file(contents, trans_id)
1783
pb.update('Adding file contents', count + offset, total)
1622
1786
def _reparent_children(tt, old_parent, new_parent):
1710
1876
tt.set_executability(entry.executable, trans_id)
1713
@deprecated_function(zero_fifteen)
1714
def find_interesting(working_tree, target_tree, filenames):
1715
"""Find the ids corresponding to specified filenames.
1717
Deprecated: Please use tree1.paths2ids(filenames, [tree2]).
1719
working_tree.lock_read()
1721
target_tree.lock_read()
1723
return working_tree.paths2ids(filenames, [target_tree])
1725
target_tree.unlock()
1727
working_tree.unlock()
1730
@deprecated_function(zero_ninety)
1731
def change_entry(tt, file_id, working_tree, target_tree,
1732
trans_id_file_id, backups, trans_id, by_parent):
1733
"""Replace a file_id's contents with those from a target tree."""
1734
if file_id is None and target_tree is None:
1735
# skip the logic altogether in the deprecation test
1737
e_trans_id = trans_id_file_id(file_id)
1738
entry = target_tree.inventory[file_id]
1739
has_contents, contents_mod, meta_mod, = _entry_changes(file_id, entry,
1742
mode_id = e_trans_id
1745
tt.delete_contents(e_trans_id)
1747
parent_trans_id = trans_id_file_id(entry.parent_id)
1748
backup_name = get_backup_name(entry, by_parent,
1749
parent_trans_id, tt)
1750
tt.adjust_path(backup_name, parent_trans_id, e_trans_id)
1751
tt.unversion_file(e_trans_id)
1752
e_trans_id = tt.create_path(entry.name, parent_trans_id)
1753
tt.version_file(file_id, e_trans_id)
1754
trans_id[file_id] = e_trans_id
1755
create_by_entry(tt, entry, target_tree, e_trans_id, mode_id=mode_id)
1756
create_entry_executability(tt, entry, e_trans_id)
1759
tt.set_executability(entry.executable, e_trans_id)
1760
if tt.final_name(e_trans_id) != entry.name:
1763
parent_id = tt.final_parent(e_trans_id)
1764
parent_file_id = tt.final_file_id(parent_id)
1765
if parent_file_id != entry.parent_id:
1770
parent_trans_id = trans_id_file_id(entry.parent_id)
1771
tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
1774
1879
def get_backup_name(entry, by_parent, parent_trans_id, tt):
1775
1880
return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
2002
2107
new_conflicts.add(('deleting parent', 'Not deleting',
2004
2109
except KeyError:
2005
tt.create_directory(trans_id)
2006
new_conflicts.add((c_type, 'Created directory', trans_id))
2008
2112
tt.final_name(trans_id)
2009
2113
except NoFinalPath:
2010
2114
if path_tree is not None:
2011
2115
file_id = tt.final_file_id(trans_id)
2117
file_id = tt.inactive_file_id(trans_id)
2012
2118
entry = path_tree.inventory[file_id]
2013
parent_trans_id = tt.trans_id_file_id(entry.parent_id)
2014
tt.adjust_path(entry.name, parent_trans_id, trans_id)
2119
# special-case the other tree root (move its
2120
# children to current root)
2121
if entry.parent_id is None:
2123
moved = _reparent_transform_children(
2124
tt, trans_id, tt.root)
2126
new_conflicts.add((c_type, 'Moved to root',
2129
parent_trans_id = tt.trans_id_file_id(
2131
tt.adjust_path(entry.name, parent_trans_id,
2134
tt.create_directory(trans_id)
2135
new_conflicts.add((c_type, 'Created directory', trans_id))
2015
2136
elif c_type == 'unversioned parent':
2016
tt.version_file(tt.inactive_file_id(conflict[1]), conflict[1])
2137
file_id = tt.inactive_file_id(conflict[1])
2138
# special-case the other tree root (move its children instead)
2139
if path_tree and file_id in path_tree:
2140
if path_tree.inventory[file_id].parent_id is None:
2142
tt.version_file(file_id, conflict[1])
2017
2143
new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
2018
2144
elif c_type == 'non-directory parent':
2019
2145
parent_id = conflict[1]