294
288
yield revision_id, str(start_revno - num), 0
297
def _iter_revisions(repository, view_revisions, generate_delta):
291
def make_log_rev_iterator(branch, view_revisions, generate_delta, search):
292
"""Create a revision iterator for log.
294
:param branch: The branch being logged.
295
:param view_revisions: The revisions being viewed.
296
:param generate_delta: Whether to generate a delta for each revision.
297
:param search: A user text search string.
298
:return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
301
# Convert view_revisions into (view, None, None) groups to fit with
302
# the standard interface here.
303
if type(view_revisions) == list:
304
# A single batch conversion is faster than many incremental ones.
305
# As we have all the data, do a batch conversion.
306
nones = [None] * len(view_revisions)
307
log_rev_iterator = iter([zip(view_revisions, nones, nones)])
310
for view in view_revisions:
311
yield (view, None, None)
312
log_rev_iterator = iter([_convert()])
313
for adapter in log_adapters:
314
log_rev_iterator = adapter(branch, generate_delta, search,
316
return log_rev_iterator
319
def _make_search_filter(branch, generate_delta, search, log_rev_iterator):
320
"""Create a filtered iterator of log_rev_iterator matching on a regex.
322
:param branch: The branch being logged.
323
:param generate_delta: Whether to generate a delta for each revision.
324
:param search: A user text search string.
325
:param log_rev_iterator: An input iterator containing all revisions that
326
could be displayed, in lists.
327
:return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
331
return log_rev_iterator
332
# Compile the search now to get early errors.
333
searchRE = re.compile(search, re.IGNORECASE)
334
return _filter_message_re(searchRE, log_rev_iterator)
337
def _filter_message_re(searchRE, log_rev_iterator):
338
for revs in log_rev_iterator:
340
for (rev_id, revno, merge_depth), rev, delta in revs:
341
if searchRE.search(rev.message):
342
new_revs.append(((rev_id, revno, merge_depth), rev, delta))
346
def _make_delta_filter(branch, generate_delta, search, log_rev_iterator):
347
"""Add revision deltas to a log iterator if needed.
349
:param branch: The branch being logged.
350
:param generate_delta: Whether to generate a delta for each revision.
351
:param search: A user text search string.
352
:param log_rev_iterator: An input iterator containing all revisions that
353
could be displayed, in lists.
354
:return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
357
if not generate_delta:
358
return log_rev_iterator
359
return _generate_deltas(branch.repository, log_rev_iterator)
362
def _generate_deltas(repository, log_rev_iterator):
363
"""Create deltas for each batch of revisions in log_rev_iterator."""
364
for revs in log_rev_iterator:
365
revisions = [rev[1] for rev in revs]
366
deltas = repository.get_deltas_for_revisions(revisions)
367
revs = [(rev[0], rev[1], delta) for rev, delta in izip(revs, deltas)]
371
def _make_revision_objects(branch, generate_delta, search, log_rev_iterator):
372
"""Extract revision objects from the repository
374
:param branch: The branch being logged.
375
:param generate_delta: Whether to generate a delta for each revision.
376
:param search: A user text search string.
377
:param log_rev_iterator: An input iterator containing all revisions that
378
could be displayed, in lists.
379
:return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
382
repository = branch.repository
383
for revs in log_rev_iterator:
384
# r = revision_id, n = revno, d = merge depth
385
revision_ids = [view[0] for view, _, _ in revs]
386
revisions = repository.get_revisions(revision_ids)
387
revs = [(rev[0], revision, rev[2]) for rev, revision in
388
izip(revs, revisions)]
392
def _make_batch_filter(branch, generate_delta, search, log_rev_iterator):
393
"""Group up a single large batch into smaller ones.
395
:param branch: The branch being logged.
396
:param generate_delta: Whether to generate a delta for each revision.
397
:param search: A user text search string.
398
:param log_rev_iterator: An input iterator containing all revisions that
399
could be displayed, in lists.
400
:return: An iterator over lists of ((rev_id, revno, merge_depth), rev, delta).
402
repository = branch.repository
299
view_revisions = iter(view_revisions)
301
cur_view_revisions = [d for x, d in zip(range(num), view_revisions)]
302
if len(cur_view_revisions) == 0:
305
# r = revision, n = revno, d = merge depth
306
revision_ids = [r for (r, n, d) in cur_view_revisions]
307
revisions = repository.get_revisions(revision_ids)
309
deltas = repository.get_deltas_for_revisions(revisions)
310
cur_deltas = dict(izip((r.revision_id for r in revisions),
312
for view_data, revision in izip(cur_view_revisions, revisions):
313
yield view_data, revision, cur_deltas.get(revision.revision_id)
314
num = min(int(num * 1.5), 200)
404
for batch in log_rev_iterator:
407
step = [detail for _, detail in zip(range(num), batch)]
411
num = min(int(num * 1.5), 200)
317
414
def _get_mainline_revs(branch, start_revision, end_revision):
355
452
end_rev_id = None
356
453
if end_revision is None:
357
end_revno = len(which_revs)
454
end_revno = branch_revno
359
456
if isinstance(end_revision, revisionspec.RevisionInfo):
360
457
end_rev_id = end_revision.rev_id
361
end_revno = end_revision.revno or len(which_revs)
458
end_revno = end_revision.revno or branch_revno
363
460
branch.check_real_revno(end_revision)
364
461
end_revno = end_revision
366
if ((start_rev_id == revision.NULL_REVISION)
367
or (end_rev_id == revision.NULL_REVISION)):
463
if ((start_rev_id == _mod_revision.NULL_REVISION)
464
or (end_rev_id == _mod_revision.NULL_REVISION)):
368
465
raise errors.BzrCommandError('Logging revision 0 is invalid.')
369
466
if start_revno > end_revno:
370
467
raise errors.BzrCommandError("Start revision must be older than "
371
468
"the end revision.")
373
# list indexes are 0-based; revisions are 1-based
374
cut_revs = which_revs[(start_revno-1):(end_revno)]
470
if end_revno < start_revno:
376
471
return None, None, None, None
472
cur_revno = branch_revno
475
for revision_id in branch.repository.iter_reverse_revision_history(
476
branch_last_revision):
477
if cur_revno < start_revno:
478
# We have gone far enough, but we always add 1 more revision
479
rev_nos[revision_id] = cur_revno
480
mainline_revs.append(revision_id)
482
if cur_revno <= end_revno:
483
rev_nos[revision_id] = cur_revno
484
mainline_revs.append(revision_id)
487
# We walked off the edge of all revisions, so we add a 'None' marker
488
mainline_revs.append(None)
378
# convert the revision history to a dictionary:
379
rev_nos = dict((k, v) for v, k in cut_revs)
490
mainline_revs.reverse()
381
492
# override the mainline to look like the revision history.
382
mainline_revs = [revision_id for index, revision_id in cut_revs]
383
if cut_revs[0][0] == 1:
384
mainline_revs.insert(0, None)
386
mainline_revs.insert(0, which_revs[start_revno-2][1])
387
493
return mainline_revs, rev_nos, start_rev_id, end_rev_id
451
557
:return: A list of (revision_id, dotted_revno, merge_depth) tuples.
453
559
# find all the revisions that change the specific file
454
file_weave = branch.repository.weave_store.get_weave(file_id,
455
branch.repository.get_transaction())
456
weave_modifed_revisions = set(file_weave.versions())
457
560
# build the ancestry of each revision in the graph
458
561
# - only listing the ancestors that change the specific file.
459
562
graph = branch.repository.get_graph()
460
563
# This asks for all mainline revisions, which means we only have to spider
461
564
# sideways, rather than depth history. That said, its still size-of-history
462
565
# and should be addressed.
566
# mainline_revisions always includes an extra revision at the beginning, so
463
568
parent_map = dict(((key, value) for key, value in
464
graph.iter_ancestry(mainline_revisions) if value is not None))
569
graph.iter_ancestry(mainline_revisions[1:]) if value is not None))
465
570
sorted_rev_list = tsort.topo_sort(parent_map.items())
571
text_keys = [(file_id, rev_id) for rev_id in sorted_rev_list]
572
modified_text_versions = branch.repository.texts.get_parent_map(text_keys)
467
574
for rev in sorted_rev_list:
575
text_key = (file_id, rev)
468
576
parents = parent_map[rev]
469
if rev not in weave_modifed_revisions and len(parents) == 1:
577
if text_key not in modified_text_versions and len(parents) == 1:
470
578
# We will not be adding anything new, so just use a reference to
471
579
# the parent ancestry.
472
580
rev_ancestry = ancestry[parents[0]]
474
582
rev_ancestry = set()
475
if rev in weave_modifed_revisions:
583
if text_key in modified_text_versions:
476
584
rev_ancestry.add(rev)
477
585
for parent in parents:
586
if parent not in ancestry:
587
# parent is a Ghost, which won't be present in
588
# sorted_rev_list, but we may access it later, so create an
590
ancestry[parent] = set()
478
591
rev_ancestry = rev_ancestry.union(ancestry[parent])
479
592
ancestry[rev] = rev_ancestry