1314
1354
raise RevisionNotPresent(version_id, self._filename)
1357
class KnitGraphIndex(object):
1358
"""A knit index that builds on GraphIndex."""
1360
def __init__(self, graph_index, deltas=False, parents=True, add_callback=None):
1361
"""Construct a KnitGraphIndex on a graph_index.
1363
:param graph_index: An implementation of bzrlib.index.GraphIndex.
1364
:param deltas: Allow delta-compressed records.
1365
:param add_callback: If not None, allow additions to the index and call
1366
this callback with a list of added GraphIndex nodes:
1367
[(node, value, node_refs), ...]
1368
:param parents: If True, record knits parents, if not do not record
1371
self._graph_index = graph_index
1372
self._deltas = deltas
1373
self._add_callback = add_callback
1374
self._parents = parents
1375
if deltas and not parents:
1376
raise KnitCorrupt(self, "Cannot do delta compression without "
1379
def _get_entries(self, keys, check_present=False):
1380
"""Get the entries for keys.
1382
:param keys: An iterable of index keys, - 1-tuples.
1387
for node in self._graph_index.iter_entries(keys):
1389
found_keys.add(node[0])
1391
# adapt parentless index to the rest of the code.
1392
for node in self._graph_index.iter_entries(keys):
1393
yield node[0], node[1], ()
1394
found_keys.add(node[0])
1396
missing_keys = keys.difference(found_keys)
1398
raise RevisionNotPresent(missing_keys.pop(), self)
1400
def _present_keys(self, version_ids):
1402
node[0] for node in self._get_entries(version_ids)])
1404
def _parentless_ancestry(self, versions):
1405
"""Honour the get_ancestry API for parentless knit indices."""
1406
wanted_keys = self._version_ids_to_keys(versions)
1407
present_keys = self._present_keys(wanted_keys)
1408
missing = set(wanted_keys).difference(present_keys)
1410
raise RevisionNotPresent(missing.pop(), self)
1411
return list(self._keys_to_version_ids(present_keys))
1413
def get_ancestry(self, versions, topo_sorted=True):
1414
"""See VersionedFile.get_ancestry."""
1415
if not self._parents:
1416
return self._parentless_ancestry(versions)
1417
# XXX: This will do len(history) index calls - perhaps
1418
# it should be altered to be a index core feature?
1419
# get a graph of all the mentioned versions:
1422
versions = self._version_ids_to_keys(versions)
1423
pending = set(versions)
1425
# get all pending nodes
1426
this_iteration = pending
1427
new_nodes = self._get_entries(this_iteration)
1430
for (key, value, node_refs) in new_nodes:
1431
# dont ask for ghosties - otherwise
1432
# we we can end up looping with pending
1433
# being entirely ghosted.
1434
graph[key] = [parent for parent in node_refs[0]
1435
if parent not in ghosts]
1437
for parent in graph[key]:
1438
# dont examine known nodes again
1443
ghosts.update(this_iteration.difference(found))
1444
if versions.difference(graph):
1445
raise RevisionNotPresent(versions.difference(graph).pop(), self)
1447
result_keys = topo_sort(graph.items())
1449
result_keys = graph.iterkeys()
1450
return [key[0] for key in result_keys]
1452
def get_ancestry_with_ghosts(self, versions):
1453
"""See VersionedFile.get_ancestry."""
1454
if not self._parents:
1455
return self._parentless_ancestry(versions)
1456
# XXX: This will do len(history) index calls - perhaps
1457
# it should be altered to be a index core feature?
1458
# get a graph of all the mentioned versions:
1460
versions = self._version_ids_to_keys(versions)
1461
pending = set(versions)
1463
# get all pending nodes
1464
this_iteration = pending
1465
new_nodes = self._get_entries(this_iteration)
1467
for (key, value, node_refs) in new_nodes:
1468
graph[key] = node_refs[0]
1470
for parent in graph[key]:
1471
# dont examine known nodes again
1475
missing_versions = this_iteration.difference(graph)
1476
missing_needed = versions.intersection(missing_versions)
1478
raise RevisionNotPresent(missing_needed.pop(), self)
1479
for missing_version in missing_versions:
1480
# add a key, no parents
1481
graph[missing_version] = []
1482
pending.discard(missing_version) # don't look for it
1483
result_keys = topo_sort(graph.items())
1484
return [key[0] for key in result_keys]
1486
def get_graph(self):
1487
"""Return a list of the node:parents lists from this knit index."""
1488
if not self._parents:
1489
return [(key, ()) for key in self.get_versions()]
1491
for key, value, refs in self._graph_index.iter_all_entries():
1492
result.append((key[0], tuple([ref[0] for ref in refs[0]])))
1495
def iter_parents(self, version_ids):
1496
"""Iterate through the parents for many version ids.
1498
:param version_ids: An iterable yielding version_ids.
1499
:return: An iterator that yields (version_id, parents). Requested
1500
version_ids not present in the versioned file are simply skipped.
1501
The order is undefined, allowing for different optimisations in
1502
the underlying implementation.
1505
all_nodes = set(self._get_entries(self._version_ids_to_keys(version_ids)))
1507
present_parents = set()
1508
for node in all_nodes:
1509
all_parents.update(node[2][0])
1510
# any node we are querying must be present
1511
present_parents.add(node[0])
1512
unknown_parents = all_parents.difference(present_parents)
1513
present_parents.update(self._present_keys(unknown_parents))
1514
for node in all_nodes:
1516
for parent in node[2][0]:
1517
if parent in present_parents:
1518
parents.append(parent[0])
1519
yield node[0][0], tuple(parents)
1521
for node in self._get_entries(self._version_ids_to_keys(version_ids)):
1522
yield node[0][0], ()
1524
def num_versions(self):
1525
return len(list(self._graph_index.iter_all_entries()))
1527
__len__ = num_versions
1529
def get_versions(self):
1530
"""Get all the versions in the file. not topologically sorted."""
1531
return [node[0][0] for node in self._graph_index.iter_all_entries()]
1533
def has_version(self, version_id):
1534
"""True if the version is in the index."""
1535
return len(self._present_keys(self._version_ids_to_keys([version_id]))) == 1
1537
def _keys_to_version_ids(self, keys):
1538
return tuple(key[0] for key in keys)
1540
def get_position(self, version_id):
1541
"""Return data position and size of specified version."""
1542
bits = self._get_node(version_id)[1][1:].split(' ')
1543
return int(bits[0]), int(bits[1])
1545
def get_method(self, version_id):
1546
"""Return compression method of specified version."""
1547
if not self._deltas:
1549
return self._parent_compression(self._get_node(version_id)[2][1])
1551
def _parent_compression(self, reference_list):
1552
# use the second reference list to decide if this is delta'd or not.
1553
if len(reference_list):
1558
def _get_node(self, version_id):
1559
return list(self._get_entries(self._version_ids_to_keys([version_id])))[0]
1561
def get_options(self, version_id):
1562
"""Return a string represention options.
1566
node = self._get_node(version_id)
1567
if not self._deltas:
1568
options = ['fulltext']
1570
options = [self._parent_compression(node[2][1])]
1571
if node[1][0] == 'N':
1572
options.append('no-eol')
1575
def get_parents(self, version_id):
1576
"""Return parents of specified version ignoring ghosts."""
1577
parents = list(self.iter_parents([version_id]))
1580
raise errors.RevisionNotPresent(version_id, self)
1581
return parents[0][1]
1583
def get_parents_with_ghosts(self, version_id):
1584
"""Return parents of specified version with ghosts."""
1585
nodes = list(self._get_entries(self._version_ids_to_keys([version_id]),
1586
check_present=True))
1587
if not self._parents:
1589
return self._keys_to_version_ids(nodes[0][2][0])
1591
def check_versions_present(self, version_ids):
1592
"""Check that all specified versions are present."""
1593
keys = self._version_ids_to_keys(version_ids)
1594
present = self._present_keys(keys)
1595
missing = keys.difference(present)
1597
raise RevisionNotPresent(missing.pop(), self)
1599
def add_version(self, version_id, options, pos, size, parents):
1600
"""Add a version record to the index."""
1601
return self.add_versions(((version_id, options, pos, size, parents),))
1603
def add_versions(self, versions):
1604
"""Add multiple versions to the index.
1606
This function does not insert data into the Immutable GraphIndex
1607
backing the KnitGraphIndex, instead it prepares data for insertion by
1608
the caller and checks that it is safe to insert then calls
1609
self._add_callback with the prepared GraphIndex nodes.
1611
:param versions: a list of tuples:
1612
(version_id, options, pos, size, parents).
1614
if not self._add_callback:
1615
raise errors.ReadOnlyError(self)
1616
# we hope there are no repositories with inconsistent parentage
1621
for (version_id, options, pos, size, parents) in versions:
1622
# index keys are tuples:
1623
key = (version_id, )
1624
parents = tuple((parent, ) for parent in parents)
1625
if 'no-eol' in options:
1629
value += "%d %d" % (pos, size)
1630
if not self._deltas:
1631
if 'line-delta' in options:
1632
raise KnitCorrupt(self, "attempt to add line-delta in non-delta knit")
1635
if 'line-delta' in options:
1636
node_refs = (parents, (parents[0],))
1638
node_refs = (parents, ())
1640
node_refs = (parents, )
1643
raise KnitCorrupt(self, "attempt to add node with parents "
1644
"in parentless index.")
1646
keys[key] = (value, node_refs)
1647
present_nodes = self._get_entries(keys)
1648
for (key, value, node_refs) in present_nodes:
1649
if (value, node_refs) != keys[key]:
1650
raise KnitCorrupt(self, "inconsistent details in add_versions"
1651
": %s %s" % ((value, node_refs), keys[key]))
1655
for key, (value, node_refs) in keys.iteritems():
1656
result.append((key, value, node_refs))
1658
for key, (value, node_refs) in keys.iteritems():
1659
result.append((key, value))
1660
self._add_callback(result)
1662
def _version_ids_to_keys(self, version_ids):
1663
return set((version_id, ) for version_id in version_ids)
1317
1666
class _KnitData(_KnitComponentFile):
1318
1667
"""Contents of the knit data file"""