230
225
# There may be duplicates, but I don't think it is worth worrying
232
227
self._nodes[reference] = ('a', (), '')
233
self._absent_keys.update(absent_references)
234
self._absent_keys.discard(key)
235
228
self._nodes[key] = ('', node_refs, value)
236
230
if self._nodes_by_key is not None and self._key_length > 1:
237
231
self._update_nodes_by_key(key, value, node_refs)
239
def clear_cache(self):
240
"""See GraphIndex.clear_cache()
242
This is a no-op, but we need the api to conform to a generic 'Index'
246
233
def finish(self):
247
234
lines = [_SIGNATURE]
248
235
lines.append(_OPTION_NODE_REFS + str(self.reference_lists) + '\n')
249
236
lines.append(_OPTION_KEY_ELEMENTS + str(self._key_length) + '\n')
250
key_count = len(self._nodes) - len(self._absent_keys)
251
lines.append(_OPTION_LEN + str(key_count) + '\n')
237
lines.append(_OPTION_LEN + str(len(self._keys)) + '\n')
252
238
prefix_length = sum(len(x) for x in lines)
253
239
# references are byte offsets. To avoid having to do nasty
254
240
# polynomial work to resolve offsets (references to later in the
754
703
# the last thing looked up was a terminal element
755
704
yield (self, ) + key_dict
757
def _find_ancestors(self, keys, ref_list_num, parent_map, missing_keys):
758
"""See BTreeIndex._find_ancestors."""
759
# The api can be implemented as a trivial overlay on top of
760
# iter_entries, it is not an efficient implementation, but it at least
764
for index, key, value, refs in self.iter_entries(keys):
765
parent_keys = refs[ref_list_num]
767
parent_map[key] = parent_keys
768
search_keys.update(parent_keys)
769
# Figure out what, if anything, was missing
770
missing_keys.update(set(keys).difference(found_keys))
771
search_keys = search_keys.difference(parent_map)
774
706
def key_count(self):
775
707
"""Return an estimate of the number of keys in this index.
1270
1186
self._indices = indices
1271
1187
self._reload_func = reload_func
1272
# Sibling indices are other CombinedGraphIndex that we should call
1273
# _move_to_front_by_name on when we auto-reorder ourself.
1274
self._sibling_indices = []
1275
# A list of names that corresponds to the instances in self._indices,
1276
# so _index_names[0] is always the name for _indices[0], etc. Sibling
1277
# indices must all use the same set of names as each other.
1278
self._index_names = [None] * len(self._indices)
1280
1189
def __repr__(self):
1281
1190
return "%s(%s)" % (
1282
1191
self.__class__.__name__,
1283
1192
', '.join(map(repr, self._indices)))
1285
def clear_cache(self):
1286
"""See GraphIndex.clear_cache()"""
1287
for index in self._indices:
1290
1194
def get_parent_map(self, keys):
1291
"""See graph.StackedParentsProvider.get_parent_map"""
1195
"""See graph._StackedParentsProvider.get_parent_map"""
1292
1196
search_keys = set(keys)
1293
if _mod_revision.NULL_REVISION in search_keys:
1294
search_keys.discard(_mod_revision.NULL_REVISION)
1295
found_parents = {_mod_revision.NULL_REVISION:[]}
1197
if NULL_REVISION in search_keys:
1198
search_keys.discard(NULL_REVISION)
1199
found_parents = {NULL_REVISION:[]}
1297
1201
found_parents = {}
1298
1202
for index, key, value, refs in self.iter_entries(search_keys):
1299
1203
parents = refs[0]
1300
1204
if not parents:
1301
parents = (_mod_revision.NULL_REVISION,)
1205
parents = (NULL_REVISION,)
1302
1206
found_parents[key] = parents
1303
1207
return found_parents
1305
1209
has_key = _has_key_from_parent_map
1307
def insert_index(self, pos, index, name=None):
1211
def insert_index(self, pos, index):
1308
1212
"""Insert a new index in the list of indices to query.
1310
1214
:param pos: The position to insert the index.
1311
1215
:param index: The index to insert.
1312
:param name: a name for this index, e.g. a pack name. These names can
1313
be used to reflect index reorderings to related CombinedGraphIndex
1314
instances that use the same names. (see set_sibling_indices)
1316
1217
self._indices.insert(pos, index)
1317
self._index_names.insert(pos, name)
1319
1219
def iter_all_entries(self):
1320
1220
"""Iterate over all keys within the index
1394
1288
seen_keys = set()
1398
1291
for index in self._indices:
1400
1292
for node in index.iter_entries_prefix(keys):
1401
1293
if node[1] in seen_keys:
1403
1295
seen_keys.add(node[1])
1407
hit_indices.append(index)
1409
1298
except errors.NoSuchFile:
1410
1299
self._reload_or_raise()
1411
self._move_to_front(hit_indices)
1413
def _move_to_front(self, hit_indices):
1414
"""Rearrange self._indices so that hit_indices are first.
1416
Order is maintained as much as possible, e.g. the first unhit index
1417
will be the first index in _indices after the hit_indices, and the
1418
hit_indices will be present in exactly the order they are passed to
1421
_move_to_front propagates to all objects in self._sibling_indices by
1422
calling _move_to_front_by_name.
1424
if self._indices[:len(hit_indices)] == hit_indices:
1425
# The 'hit_indices' are already at the front (and in the same
1426
# order), no need to re-order
1428
hit_names = self._move_to_front_by_index(hit_indices)
1429
for sibling_idx in self._sibling_indices:
1430
sibling_idx._move_to_front_by_name(hit_names)
1432
def _move_to_front_by_index(self, hit_indices):
1433
"""Core logic for _move_to_front.
1435
Returns a list of names corresponding to the hit_indices param.
1437
indices_info = zip(self._index_names, self._indices)
1438
if 'index' in debug.debug_flags:
1439
trace.mutter('CombinedGraphIndex reordering: currently %r, '
1440
'promoting %r', indices_info, hit_indices)
1443
new_hit_indices = []
1446
for offset, (name, idx) in enumerate(indices_info):
1447
if idx in hit_indices:
1448
hit_names.append(name)
1449
new_hit_indices.append(idx)
1450
if len(new_hit_indices) == len(hit_indices):
1451
# We've found all of the hit entries, everything else is
1453
unhit_names.extend(self._index_names[offset+1:])
1454
unhit_indices.extend(self._indices[offset+1:])
1457
unhit_names.append(name)
1458
unhit_indices.append(idx)
1460
self._indices = new_hit_indices + unhit_indices
1461
self._index_names = hit_names + unhit_names
1462
if 'index' in debug.debug_flags:
1463
trace.mutter('CombinedGraphIndex reordered: %r', self._indices)
1466
def _move_to_front_by_name(self, hit_names):
1467
"""Moves indices named by 'hit_names' to front of the search order, as
1468
described in _move_to_front.
1470
# Translate names to index instances, and then call
1471
# _move_to_front_by_index.
1472
indices_info = zip(self._index_names, self._indices)
1474
for name, idx in indices_info:
1475
if name in hit_names:
1476
hit_indices.append(idx)
1477
self._move_to_front_by_index(hit_indices)
1479
def find_ancestry(self, keys, ref_list_num):
1480
"""Find the complete ancestry for the given set of keys.
1482
Note that this is a whole-ancestry request, so it should be used
1485
:param keys: An iterable of keys to look for
1486
:param ref_list_num: The reference list which references the parents
1488
:return: (parent_map, missing_keys)
1490
# XXX: make this call _move_to_front?
1491
missing_keys = set()
1493
keys_to_lookup = set(keys)
1495
while keys_to_lookup:
1496
# keys that *all* indexes claim are missing, stop searching them
1498
all_index_missing = None
1499
# print 'gen\tidx\tsub\tn_keys\tn_pmap\tn_miss'
1500
# print '%4d\t\t\t%4d\t%5d\t%5d' % (generation, len(keys_to_lookup),
1502
# len(missing_keys))
1503
for index_idx, index in enumerate(self._indices):
1504
# TODO: we should probably be doing something with
1505
# 'missing_keys' since we've already determined that
1506
# those revisions have not been found anywhere
1507
index_missing_keys = set()
1508
# Find all of the ancestry we can from this index
1509
# keep looking until the search_keys set is empty, which means
1510
# things we didn't find should be in index_missing_keys
1511
search_keys = keys_to_lookup
1513
# print ' \t%2d\t\t%4d\t%5d\t%5d' % (
1514
# index_idx, len(search_keys),
1515
# len(parent_map), len(index_missing_keys))
1518
# TODO: ref_list_num should really be a parameter, since
1519
# CombinedGraphIndex does not know what the ref lists
1521
search_keys = index._find_ancestors(search_keys,
1522
ref_list_num, parent_map, index_missing_keys)
1523
# print ' \t \t%2d\t%4d\t%5d\t%5d' % (
1524
# sub_generation, len(search_keys),
1525
# len(parent_map), len(index_missing_keys))
1526
# Now set whatever was missing to be searched in the next index
1527
keys_to_lookup = index_missing_keys
1528
if all_index_missing is None:
1529
all_index_missing = set(index_missing_keys)
1531
all_index_missing.intersection_update(index_missing_keys)
1532
if not keys_to_lookup:
1534
if all_index_missing is None:
1535
# There were no indexes, so all search keys are 'missing'
1536
missing_keys.update(keys_to_lookup)
1537
keys_to_lookup = None
1539
missing_keys.update(all_index_missing)
1540
keys_to_lookup.difference_update(all_index_missing)
1541
return parent_map, missing_keys
1543
1301
def key_count(self):
1544
1302
"""Return an estimate of the number of keys in this index.