107
111
for revision_id in branch.revision_history():
108
this_inv = branch.repository.get_revision_inventory(revision_id)
112
this_inv = branch.repository.get_inventory(revision_id)
109
113
if file_id in this_inv:
110
114
this_ie = this_inv[file_id]
111
115
this_path = this_inv.id2path(file_id)
306
310
class Logger(object):
307
"""An object the generates, formats and displays a log."""
311
"""An object that generates, formats and displays a log."""
309
313
def __init__(self, branch, rqst):
310
314
"""Create a Logger.
532
536
def _generate_all_revisions(branch, start_rev_id, end_rev_id, direction,
533
delayed_graph_generation):
537
delayed_graph_generation):
534
538
# On large trees, generating the merge graph can take 30-60 seconds
535
539
# so we delay doing it until a merge is detected, incrementally
536
540
# returning initial (non-merge) revisions while we can.
542
# The above is only true for old formats (<= 0.92), for newer formats, a
543
# couple of seconds only should be needed to load the whole graph and the
544
# other graph operations needed are even faster than that -- vila 100201
537
545
initial_revisions = []
538
546
if delayed_graph_generation:
540
for rev_id, revno, depth in \
541
_linear_view_revisions(branch, start_rev_id, end_rev_id):
548
for rev_id, revno, depth in _linear_view_revisions(
549
branch, start_rev_id, end_rev_id):
542
550
if _has_merges(branch, rev_id):
551
# The end_rev_id can be nested down somewhere. We need an
552
# explicit ancestry check. There is an ambiguity here as we
553
# may not raise _StartNotLinearAncestor for a revision that
554
# is an ancestor but not a *linear* one. But since we have
555
# loaded the graph to do the check (or calculate a dotted
556
# revno), we may as well accept to show the log... We need
557
# the check only if start_rev_id is not None as all
558
# revisions have _mod_revision.NULL_REVISION as an ancestor
560
graph = branch.repository.get_graph()
561
if (start_rev_id is not None
562
and not graph.is_ancestor(start_rev_id, end_rev_id)):
563
raise _StartNotLinearAncestor()
564
# Since we collected the revisions so far, we need to
543
566
end_rev_id = rev_id
558
581
raise errors.BzrCommandError('Start revision not found in'
559
582
' history of end revision.')
584
# We exit the loop above because we encounter a revision with merges, from
585
# this revision, we need to switch to _graph_view_revisions.
561
587
# A log including nested merges is required. If the direction is reverse,
562
588
# we rebase the initial merge depths so that the development line is
563
589
# shown naturally, i.e. just like it is for linear logging. We can easily
565
591
# indented at the end seems slightly nicer in that case.
566
592
view_revisions = chain(iter(initial_revisions),
567
593
_graph_view_revisions(branch, start_rev_id, end_rev_id,
568
rebase_initial_depths=direction == 'reverse'))
594
rebase_initial_depths=(direction == 'reverse')))
569
595
if direction == 'reverse':
570
596
return view_revisions
571
597
elif direction == 'forward':
637
665
def _graph_view_revisions(branch, start_rev_id, end_rev_id,
638
rebase_initial_depths=True):
666
rebase_initial_depths=True):
639
667
"""Calculate revisions to view including merges, newest to oldest.
641
669
:param branch: the branch
664
692
depth_adjustment = merge_depth
665
693
if depth_adjustment:
666
694
if merge_depth < depth_adjustment:
695
# From now on we reduce the depth adjustement, this can be
696
# surprising for users. The alternative requires two passes
697
# which breaks the fast display of the first revision
667
699
depth_adjustment = merge_depth
668
700
merge_depth -= depth_adjustment
669
701
yield rev_id, '.'.join(map(str, revno)), merge_depth
704
@deprecated_function(deprecated_in((2, 2, 0)))
672
705
def calculate_view_revisions(branch, start_revision, end_revision, direction,
673
706
specific_fileid, generate_merge_revisions):
674
707
"""Calculate the revisions to view.
676
709
:return: An iterator of (revision_id, dotted_revno, merge_depth) tuples OR
677
710
a list of the same tuples.
679
# This method is no longer called by the main code path.
680
# It is retained for API compatibility and may be deprecated
682
712
start_rev_id, end_rev_id = _get_revision_limits(branch, start_revision,
684
714
view_revisions = list(_calc_view_revisions(branch, start_rev_id, end_rev_id,
1034
1064
return mainline_revs, rev_nos, start_rev_id, end_rev_id
1067
@deprecated_function(deprecated_in((2, 2, 0)))
1037
1068
def _filter_revision_range(view_revisions, start_rev_id, end_rev_id):
1038
1069
"""Filter view_revisions based on revision ranges.
1049
1080
:return: The filtered view_revisions.
1051
# This method is no longer called by the main code path.
1052
# It may be removed soon. IGC 20090127
1053
1082
if start_rev_id or end_rev_id:
1054
1083
revision_ids = [r for r, n, d in view_revisions]
1055
1084
if start_rev_id:
1193
@deprecated_function(deprecated_in((2, 2, 0)))
1164
1194
def get_view_revisions(mainline_revs, rev_nos, branch, direction,
1165
1195
include_merges=True):
1166
1196
"""Produce an iterator of revisions to show
1167
1197
:return: an iterator of (revision_id, revno, merge_depth)
1168
1198
(if there is no revno for a revision, None is supplied)
1170
# This method is no longer called by the main code path.
1171
# It is retained for API compatibility and may be deprecated
1172
# soon. IGC 20090127
1173
1200
if not include_merges:
1174
1201
revision_ids = mainline_revs[1:]
1175
1202
if direction == 'reverse':
1293
1320
preferred_levels = 0
1295
1322
def __init__(self, to_file, show_ids=False, show_timezone='original',
1296
delta_format=None, levels=None, show_advice=False,
1297
to_exact_file=None):
1323
delta_format=None, levels=None, show_advice=False,
1324
to_exact_file=None):
1298
1325
"""Create a LogFormatter.
1300
1327
:param to_file: the file to output to
1406
1433
# Revision comes directly from a foreign repository
1407
1434
if isinstance(rev, foreign.ForeignRevision):
1408
return self._format_properties(rev.mapping.vcs.show_foreign_revid(rev.foreign_revid))
1435
return self._format_properties(
1436
rev.mapping.vcs.show_foreign_revid(rev.foreign_revid))
1410
1438
# Imported foreign revision revision ids always contain :
1411
1439
if not ":" in rev.revision_id:
1498
1526
to_file = self.to_file
1499
1527
to_file.write("%s%s\n" % (indent, ('\n' + indent).join(lines)))
1500
1528
if revision.delta is not None:
1501
# We don't respect delta_format for compatibility
1502
revision.delta.show(to_file, self.show_ids, indent=indent,
1529
# Use the standard status output to display changes
1530
from bzrlib.delta import report_delta
1531
report_delta(to_file, revision.delta, short_status=False,
1532
show_ids=self.show_ids, indent=indent)
1504
1533
if revision.diff is not None:
1505
1534
to_file.write(indent + 'diff:\n')
1506
1535
to_file.flush()
1569
1598
to_file.write(indent + offset + '%s\n' % (l,))
1571
1600
if revision.delta is not None:
1572
revision.delta.show(to_file, self.show_ids, indent=indent + offset,
1573
short_status=self.delta_format==1)
1601
# Use the standard status output to display changes
1602
from bzrlib.delta import report_delta
1603
report_delta(to_file, revision.delta,
1604
short_status=self.delta_format==1,
1605
show_ids=self.show_ids, indent=indent + offset)
1574
1606
if revision.diff is not None:
1575
1607
self.show_diff(self.to_exact_file, revision.diff, ' ')
1576
1608
to_file.write('\n')
1651
1683
self.show_timezone,
1652
1684
date_fmt='%Y-%m-%d',
1653
1685
show_offset=False)
1654
committer_str = revision.rev.committer.replace (' <', ' <')
1686
committer_str = revision.rev.get_apparent_authors()[0].replace (' <', ' <')
1655
1687
to_file.write('%s %s\n\n' % (date_str,committer_str))
1657
1689
if revision.delta is not None and revision.delta.has_changed():
1987
2019
bug_rows = [line.split(' ', 1) for line in bug_lines]
1988
2020
fixed_bug_urls = [row[0] for row in bug_rows if
1989
2021
len(row) > 1 and row[1] == 'fixed']
1991
2023
if fixed_bug_urls:
1992
2024
return {'fixes bug(s)': ' '.join(fixed_bug_urls)}