450
458
del self._new_id[trans_id]
451
459
del self._r_new_id[file_id]
454
"""Determine the paths of all new and changed files"""
461
def new_paths(self, filesystem_only=False):
462
"""Determine the paths of all new and changed files.
464
:param filesystem_only: if True, only calculate values for files
465
that require renames or execute bit changes.
456
fp = FinalPaths(self)
457
for id_set in (self._new_name, self._new_parent, self._new_contents,
458
self._new_id, self._new_executability):
469
id_sets = (self._needs_rename, self._new_executability)
471
id_sets = (self._new_name, self._new_parent, self._new_contents,
472
self._new_id, self._new_executability)
473
for id_set in id_sets:
459
474
new_ids.update(id_set)
460
new_paths = [(fp.get_path(t), t) for t in new_ids]
475
return sorted(FinalPaths(self).get_paths(new_ids))
464
477
def tree_kind(self, trans_id):
465
478
"""Determine the file kind in the working tree.
1256
1277
That is, create any files that need to be created, and restore from
1257
1278
limbo any files that needed renaming. This must be done in strict
1258
1279
parent-to-child order.
1281
If inventory_delta is None, no inventory delta is calculated, and
1282
no list of modified paths is returned.
1260
new_paths = self.new_paths()
1284
new_paths = self.new_paths(filesystem_only=(inventory_delta is None))
1261
1285
modified_paths = []
1262
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1263
1286
completed_new = []
1287
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1289
if inventory_delta is not None:
1290
entries = self._tree.iter_entries_by_dir(
1291
new_path_file_ids.values())
1292
old_paths = dict((e.file_id, p) for p, e in entries)
1293
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1265
1295
for num, (path, trans_id) in enumerate(new_paths):
1266
1296
new_entry = None
1267
child_pb.update('adding file', num, len(new_paths))
1268
if trans_id in self._new_contents or \
1269
self.path_changed(trans_id):
1270
full_path = self._tree.abspath(path)
1271
if trans_id in self._needs_rename:
1298
child_pb.update('adding file', num, len(new_paths))
1299
full_path = self._tree.abspath(path)
1300
if trans_id in self._needs_rename:
1302
mover.rename(self._limbo_name(trans_id), full_path)
1304
# We may be renaming a dangling inventory id
1305
if e.errno != errno.ENOENT:
1308
self.rename_count += 1
1309
if inventory_delta is not None:
1310
if (trans_id in self._new_contents or
1311
self.path_changed(trans_id)):
1312
if trans_id in self._new_contents:
1313
modified_paths.append(full_path)
1314
completed_new.append(trans_id)
1315
file_id = new_path_file_ids[trans_id]
1316
if file_id is not None and (trans_id in self._new_id or
1317
trans_id in self._new_name or
1318
trans_id in self._new_parent
1319
or trans_id in self._new_executability):
1273
mover.rename(self._limbo_name(trans_id), full_path)
1275
# We may be renaming a dangling inventory id
1276
if e.errno != errno.ENOENT:
1321
kind = self.final_kind(trans_id)
1323
kind = self._tree.stored_kind(file_id)
1324
parent_trans_id = self.final_parent(trans_id)
1325
parent_file_id = new_path_file_ids.get(parent_trans_id)
1326
if parent_file_id is None:
1327
parent_file_id = self.final_file_id(
1329
if trans_id in self._new_reference_revision:
1330
new_entry = inventory.TreeReference(
1332
self._new_name[trans_id],
1333
self.final_file_id(self._new_parent[trans_id]),
1334
None, self._new_reference_revision[trans_id])
1279
self.rename_count += 1
1280
if trans_id in self._new_contents:
1281
modified_paths.append(full_path)
1282
completed_new.append(trans_id)
1283
file_id = self.final_file_id(trans_id)
1284
if file_id is not None and (trans_id in self._new_id or
1285
trans_id in self._new_name or trans_id in self._new_parent
1286
or trans_id in self._new_executability):
1288
kind = self.final_kind(trans_id)
1290
kind = self._tree.stored_kind(file_id)
1291
if trans_id in self._new_reference_revision:
1292
new_entry = inventory.TreeReference(
1293
self.final_file_id(trans_id),
1294
self._new_name[trans_id],
1295
self.final_file_id(self._new_parent[trans_id]),
1296
None, self._new_reference_revision[trans_id])
1298
new_entry = inventory.make_entry(kind,
1299
self.final_name(trans_id),
1300
self.final_file_id(self.final_parent(trans_id)),
1301
self.final_file_id(trans_id))
1303
old_path = self._tree.id2path(new_entry.file_id)
1304
except errors.NoSuchId:
1306
inventory_delta.append((old_path, path, new_entry.file_id,
1336
new_entry = inventory.make_entry(kind,
1337
self.final_name(trans_id),
1338
parent_file_id, file_id)
1339
old_path = old_paths.get(new_entry.file_id)
1340
inventory_delta.append(
1341
(old_path, path, new_entry.file_id, new_entry))
1309
1343
if trans_id in self._new_executability:
1310
1344
self._set_executability(path, new_entry, trans_id)
1312
1346
child_pb.finished()
1313
for trans_id in completed_new:
1314
del self._new_contents[trans_id]
1347
if inventory_delta is None:
1348
self._new_contents.clear()
1350
for trans_id in completed_new:
1351
del self._new_contents[trans_id]
1315
1352
return modified_paths
1354
1391
except KeyError:
1356
1393
file_id = self.tree_file_id(parent_id)
1357
for child in self._tree.inventory[file_id].children.iterkeys():
1396
children = getattr(self._tree.inventory[file_id], 'children', {})
1397
for child in children:
1358
1398
childpath = joinpath(path, child)
1359
1399
yield self.trans_id_tree_path(childpath)
1362
class _PreviewTree(object):
1402
class _PreviewTree(tree.Tree):
1363
1403
"""Partial implementation of Tree to support show_diff_trees"""
1365
1405
def __init__(self, transform):
1366
1406
self._transform = transform
1407
self._final_paths = FinalPaths(transform)
1408
self.__by_parent = None
1410
def _changes(self, file_id):
1411
for changes in self._transform.iter_changes():
1412
if changes[0] == file_id:
1415
def _content_change(self, file_id):
1416
"""Return True if the content of this file changed"""
1417
changes = self._changes(file_id)
1418
# changes[2] is true if the file content changed. See
1419
# InterTree.iter_changes.
1420
return (changes is not None and changes[2])
1422
def _get_file_revision(self, file_id, vf, tree_revision):
1423
return self._transform._tree._get_file_revision(file_id, vf,
1426
def _stat_limbo_file(self, file_id):
1427
trans_id = self._transform.trans_id_file_id(file_id)
1428
name = self._transform._limbo_name(trans_id)
1429
return os.lstat(name)
1432
def _by_parent(self):
1433
if self.__by_parent is None:
1434
self.__by_parent = self._transform.by_parent()
1435
return self.__by_parent
1368
1437
def lock_read(self):
1369
1438
# Perhaps in theory, this should lock the TreeTransform?
1372
1441
def unlock(self):
1445
def inventory(self):
1446
"""This Tree does not use inventory as its backing data."""
1447
raise NotImplementedError(_PreviewTree.inventory)
1449
def get_root_id(self):
1450
return self._transform.final_file_id(self._transform.root)
1452
def all_file_ids(self):
1453
tree_ids = set(self._transform._tree.all_file_ids())
1454
tree_ids.difference_update(self._transform.tree_file_id(t)
1455
for t in self._transform._removed_id)
1456
tree_ids.update(self._transform._new_id.values())
1460
return iter(self.all_file_ids())
1462
def paths2ids(self, specific_files, trees=None, require_versioned=False):
1463
"""See Tree.paths2ids"""
1464
to_find = set(specific_files)
1466
for (file_id, paths, changed, versioned, parent, name, kind,
1467
executable) in self._transform.iter_changes():
1468
if paths[1] in to_find:
1469
result.append(file_id)
1470
to_find.remove(paths[1])
1471
result.update(self._transform._tree.paths2ids(to_find,
1472
trees=[], require_versioned=require_versioned))
1475
def _path2trans_id(self, path):
1476
segments = splitpath(path)
1477
cur_parent = self._transform.root
1478
for cur_segment in segments:
1479
for child in self._all_children(cur_parent):
1480
if self._transform.final_name(child) == cur_segment:
1487
def path2id(self, path):
1488
return self._transform.final_file_id(self._path2trans_id(path))
1490
def id2path(self, file_id):
1491
trans_id = self._transform.trans_id_file_id(file_id)
1493
return self._final_paths._determine_path(trans_id)
1495
raise errors.NoSuchId(self, file_id)
1497
def _all_children(self, trans_id):
1498
children = set(self._transform.iter_tree_children(trans_id))
1499
# children in the _new_parent set are provided by _by_parent.
1500
children.difference_update(self._transform._new_parent.keys())
1501
children.update(self._by_parent.get(trans_id, []))
1504
def _make_inv_entries(self, ordered_entries, specific_file_ids):
1505
for trans_id, parent_file_id in ordered_entries:
1506
file_id = self._transform.final_file_id(trans_id)
1509
if (specific_file_ids is not None
1510
and file_id not in specific_file_ids):
1513
kind = self._transform.final_kind(trans_id)
1515
kind = self._transform._tree.stored_kind(file_id)
1516
new_entry = inventory.make_entry(
1518
self._transform.final_name(trans_id),
1519
parent_file_id, file_id)
1520
yield new_entry, trans_id
1522
def iter_entries_by_dir(self, specific_file_ids=None):
1523
# This may not be a maximally efficient implementation, but it is
1524
# reasonably straightforward. An implementation that grafts the
1525
# TreeTransform changes onto the tree's iter_entries_by_dir results
1526
# might be more efficient, but requires tricky inferences about stack
1528
todo = [ROOT_PARENT]
1530
while len(todo) > 0:
1532
parent_file_id = self._transform.final_file_id(parent)
1533
children = list(self._all_children(parent))
1534
paths = dict(zip(children, self._final_paths.get_paths(children)))
1535
children.sort(key=paths.get)
1536
todo.extend(reversed(children))
1537
for trans_id in children:
1538
ordered_ids.append((trans_id, parent_file_id))
1539
for entry, trans_id in self._make_inv_entries(ordered_ids,
1541
yield unicode(self._final_paths.get_path(trans_id)), entry
1543
def kind(self, file_id):
1544
trans_id = self._transform.trans_id_file_id(file_id)
1545
return self._transform.final_kind(trans_id)
1547
def stored_kind(self, file_id):
1548
trans_id = self._transform.trans_id_file_id(file_id)
1550
return self._transform._new_contents[trans_id]
1552
return self._transform._tree.stored_kind(file_id)
1554
def get_file_mtime(self, file_id, path=None):
1555
"""See Tree.get_file_mtime"""
1556
if not self._content_change(file_id):
1557
return self._transform._tree.get_file_mtime(file_id, path)
1558
return self._stat_limbo_file(file_id).st_mtime
1560
def get_file_size(self, file_id):
1561
"""See Tree.get_file_size"""
1562
if self.kind(file_id) == 'file':
1563
return self._transform._tree.get_file_size(file_id)
1567
def get_file_sha1(self, file_id, path=None, stat_value=None):
1568
return self._transform._tree.get_file_sha1(file_id)
1570
def is_executable(self, file_id, path=None):
1571
trans_id = self._transform.trans_id_file_id(file_id)
1573
return self._transform._new_executability[trans_id]
1575
return self._transform._tree.is_executable(file_id, path)
1577
def path_content_summary(self, path):
1578
trans_id = self._path2trans_id(path)
1579
tt = self._transform
1580
tree_path = tt._tree_id_paths.get(trans_id)
1581
kind = tt._new_contents.get(trans_id)
1583
if tree_path is None or trans_id in tt._removed_contents:
1584
return 'missing', None, None, None
1585
summary = tt._tree.path_content_summary(tree_path)
1586
kind, size, executable, link_or_sha1 = summary
1589
limbo_name = tt._limbo_name(trans_id)
1590
if trans_id in tt._new_reference_revision:
1591
kind = 'tree-reference'
1593
statval = os.lstat(limbo_name)
1594
size = statval.st_size
1595
if not supports_executable():
1598
executable = statval.st_mode & S_IEXEC
1602
if kind == 'symlink':
1603
link_or_sha1 = os.readlink(limbo_name)
1604
if supports_executable():
1605
executable = tt._new_executability.get(trans_id, executable)
1606
return kind, size, executable, link_or_sha1
1375
1608
def iter_changes(self, from_tree, include_unchanged=False,
1376
1609
specific_files=None, pb=None, extra_trees=None,
1377
1610
require_versioned=True, want_unversioned=False):
1391
1624
raise ValueError('want_unversioned is not supported')
1392
1625
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):
1627
def get_file(self, file_id, path=None):
1405
1628
"""See Tree.get_file"""
1629
if not self._content_change(file_id):
1630
return self._transform._tree.get_file(file_id, path)
1406
1631
trans_id = self._transform.trans_id_file_id(file_id)
1407
1632
name = self._transform._limbo_name(trans_id)
1408
1633
return open(name, 'rb')
1635
def get_file_text(self, file_id):
1636
text_file = self.get_file(file_id)
1638
return text_file.read()
1642
def annotate_iter(self, file_id,
1643
default_revision=_mod_revision.CURRENT_REVISION):
1644
changes = self._changes(file_id)
1648
changed_content, versioned, kind = (changes[2], changes[3],
1652
get_old = (kind[0] == 'file' and versioned[0])
1654
old_annotation = self._transform._tree.annotate_iter(file_id,
1655
default_revision=default_revision)
1659
return old_annotation
1660
if not changed_content:
1661
return old_annotation
1662
return annotate.reannotate([old_annotation],
1663
self.get_file(file_id).readlines(),
1410
1666
def get_symlink_target(self, file_id):
1411
1667
"""See Tree.get_symlink_target"""
1668
if not self._content_change(file_id):
1669
return self._transform._tree.get_symlink_target(file_id)
1412
1670
trans_id = self._transform.trans_id_file_id(file_id)
1413
1671
name = self._transform._limbo_name(trans_id)
1414
1672
return os.readlink(name)
1416
def paths2ids(self, specific_files, trees=None, require_versioned=False):
1417
"""See Tree.paths2ids"""
1674
def list_files(self, include_root=False):
1675
return self._transform._tree.list_files(include_root)
1677
def walkdirs(self, prefix=""):
1678
return self._transform._tree.walkdirs(prefix)
1680
def get_parent_ids(self):
1681
return self._transform._tree.get_parent_ids()
1683
def get_revision_tree(self, revision_id):
1684
return self._transform._tree.get_revision_tree(revision_id)
1421
1687
def joinpath(parent, child):
1557
1837
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1558
1838
if kind == 'directory':
1559
1839
reparent = True
1560
if entry.parent_id not in file_trans_id:
1561
raise AssertionError(
1562
'entry %s parent id %r is not in file_trans_id %r'
1563
% (entry, entry.parent_id, file_trans_id))
1564
1840
parent_id = file_trans_id[entry.parent_id]
1565
1841
if entry.kind == 'file':
1566
1842
# We *almost* replicate new_by_entry, so that we can defer
1567
1843
# getting the file text, and get them all at once.
1568
1844
trans_id = tt.create_path(entry.name, parent_id)
1569
1845
file_trans_id[file_id] = trans_id
1570
tt.version_file(entry.file_id, trans_id)
1571
executable = tree.is_executable(entry.file_id, tree_path)
1572
if executable is not None:
1846
tt.version_file(file_id, trans_id)
1847
executable = tree.is_executable(file_id, tree_path)
1573
1849
tt.set_executability(executable, trans_id)
1574
deferred_contents.append((entry.file_id, trans_id))
1850
deferred_contents.append((file_id, trans_id))
1576
1852
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1727
2008
tt.set_executability(entry.executable, trans_id)
1730
@deprecated_function(zero_fifteen)
1731
def find_interesting(working_tree, target_tree, filenames):
1732
"""Find the ids corresponding to specified filenames.
1734
Deprecated: Please use tree1.paths2ids(filenames, [tree2]).
1736
working_tree.lock_read()
1738
target_tree.lock_read()
1740
return working_tree.paths2ids(filenames, [target_tree])
1742
target_tree.unlock()
1744
working_tree.unlock()
1747
@deprecated_function(zero_ninety)
1748
def change_entry(tt, file_id, working_tree, target_tree,
1749
trans_id_file_id, backups, trans_id, by_parent):
1750
"""Replace a file_id's contents with those from a target tree."""
1751
if file_id is None and target_tree is None:
1752
# skip the logic altogether in the deprecation test
1754
e_trans_id = trans_id_file_id(file_id)
1755
entry = target_tree.inventory[file_id]
1756
has_contents, contents_mod, meta_mod, = _entry_changes(file_id, entry,
1759
mode_id = e_trans_id
1762
tt.delete_contents(e_trans_id)
1764
parent_trans_id = trans_id_file_id(entry.parent_id)
1765
backup_name = get_backup_name(entry, by_parent,
1766
parent_trans_id, tt)
1767
tt.adjust_path(backup_name, parent_trans_id, e_trans_id)
1768
tt.unversion_file(e_trans_id)
1769
e_trans_id = tt.create_path(entry.name, parent_trans_id)
1770
tt.version_file(file_id, e_trans_id)
1771
trans_id[file_id] = e_trans_id
1772
create_by_entry(tt, entry, target_tree, e_trans_id, mode_id=mode_id)
1773
create_entry_executability(tt, entry, e_trans_id)
1776
tt.set_executability(entry.executable, e_trans_id)
1777
if tt.final_name(e_trans_id) != entry.name:
1780
parent_id = tt.final_parent(e_trans_id)
1781
parent_file_id = tt.final_file_id(parent_id)
1782
if parent_file_id != entry.parent_id:
1787
parent_trans_id = trans_id_file_id(entry.parent_id)
1788
tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
1791
2011
def get_backup_name(entry, by_parent, parent_trans_id, tt):
1792
2012
return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
2019
2239
new_conflicts.add(('deleting parent', 'Not deleting',
2021
2241
except KeyError:
2022
tt.create_directory(trans_id)
2023
new_conflicts.add((c_type, 'Created directory', trans_id))
2025
2244
tt.final_name(trans_id)
2026
2245
except NoFinalPath:
2027
2246
if path_tree is not None:
2028
2247
file_id = tt.final_file_id(trans_id)
2249
file_id = tt.inactive_file_id(trans_id)
2029
2250
entry = path_tree.inventory[file_id]
2030
parent_trans_id = tt.trans_id_file_id(entry.parent_id)
2031
tt.adjust_path(entry.name, parent_trans_id, trans_id)
2251
# special-case the other tree root (move its
2252
# children to current root)
2253
if entry.parent_id is None:
2255
moved = _reparent_transform_children(
2256
tt, trans_id, tt.root)
2258
new_conflicts.add((c_type, 'Moved to root',
2261
parent_trans_id = tt.trans_id_file_id(
2263
tt.adjust_path(entry.name, parent_trans_id,
2266
tt.create_directory(trans_id)
2267
new_conflicts.add((c_type, 'Created directory', trans_id))
2032
2268
elif c_type == 'unversioned parent':
2033
tt.version_file(tt.inactive_file_id(conflict[1]), conflict[1])
2269
file_id = tt.inactive_file_id(conflict[1])
2270
# special-case the other tree root (move its children instead)
2271
if path_tree and file_id in path_tree:
2272
if path_tree.inventory[file_id].parent_id is None:
2274
tt.version_file(file_id, conflict[1])
2034
2275
new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
2035
2276
elif c_type == 'non-directory parent':
2036
2277
parent_id = conflict[1]