14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
17
"""Code to show logs of changes.
21
19
Various flavors of log can be produced:
108
for revision_id in branch.revision_history():
108
graph = branch.repository.get_graph()
109
history = list(graph.iter_lefthand_ancestry(branch.last_revision(),
110
[_mod_revision.NULL_REVISION]))
111
for revision_id in reversed(history):
109
112
this_inv = branch.repository.get_inventory(revision_id)
110
113
if this_inv.has_id(file_id):
111
114
this_ie = this_inv[file_id]
233
227
diff_type=None, _match_using_deltas=True,
234
228
exclude_common_ancestry=False, match=None,
229
signature=False, omit_merges=False,
237
231
"""Convenience function for making a logging request dictionary.
334
332
from bzrlib import gpg
336
334
gpg_strategy = gpg.GPGStrategy(None)
337
result = repo.verify_revision(rev_id, gpg_strategy)
335
result = repo.verify_revision_signature(rev_id, gpg_strategy)
338
336
if result[0] == gpg.SIGNATURE_VALID:
339
337
return "valid signature from {0}".format(result[1])
340
338
if result[0] == gpg.SIGNATURE_KEY_MISSING:
558
559
a list of the same tuples.
560
561
if (exclude_common_ancestry and start_rev_id == end_rev_id):
561
raise errors.BzrCommandError(
562
'--exclude-common-ancestry requires two different revisions')
562
raise errors.BzrCommandError(gettext(
563
'--exclude-common-ancestry requires two different revisions'))
563
564
if direction not in ('reverse', 'forward'):
564
raise ValueError('invalid direction %r' % direction)
565
raise ValueError(gettext('invalid direction %r') % direction)
565
566
br_revno, br_rev_id = branch.last_revision_info()
566
567
if br_revno == 0:
570
571
and (not generate_merge_revisions
571
572
or not _has_merges(branch, end_rev_id))):
572
573
# If a single revision is requested, check we can handle it
573
iter_revs = _generate_one_revision(branch, end_rev_id, br_rev_id,
575
elif not generate_merge_revisions:
576
# If we only want to see linear revisions, we can iterate ...
577
iter_revs = _generate_flat_revisions(branch, start_rev_id, end_rev_id,
578
direction, exclude_common_ancestry)
579
if direction == 'forward':
580
iter_revs = reversed(iter_revs)
582
iter_revs = _generate_all_revisions(branch, start_rev_id, end_rev_id,
583
direction, delayed_graph_generation,
584
exclude_common_ancestry)
585
if direction == 'forward':
586
iter_revs = _rebase_merge_depth(reverse_by_depth(list(iter_revs)))
574
return _generate_one_revision(branch, end_rev_id, br_rev_id,
576
if not generate_merge_revisions:
578
# If we only want to see linear revisions, we can iterate ...
579
iter_revs = _linear_view_revisions(
580
branch, start_rev_id, end_rev_id,
581
exclude_common_ancestry=exclude_common_ancestry)
582
# If a start limit was given and it's not obviously an
583
# ancestor of the end limit, check it before outputting anything
584
if (direction == 'forward'
585
or (start_rev_id and not _is_obvious_ancestor(
586
branch, start_rev_id, end_rev_id))):
587
iter_revs = list(iter_revs)
588
if direction == 'forward':
589
iter_revs = reversed(iter_revs)
591
except _StartNotLinearAncestor:
592
# Switch to the slower implementation that may be able to find a
593
# non-obvious ancestor out of the left-hand history.
595
iter_revs = _generate_all_revisions(branch, start_rev_id, end_rev_id,
596
direction, delayed_graph_generation,
597
exclude_common_ancestry)
598
if direction == 'forward':
599
iter_revs = _rebase_merge_depth(reverse_by_depth(list(iter_revs)))
596
609
return [(rev_id, revno_str, 0)]
599
def _generate_flat_revisions(branch, start_rev_id, end_rev_id, direction,
600
exclude_common_ancestry=False):
601
result = _linear_view_revisions(
602
branch, start_rev_id, end_rev_id,
603
exclude_common_ancestry=exclude_common_ancestry)
604
# If a start limit was given and it's not obviously an
605
# ancestor of the end limit, check it before outputting anything
606
if direction == 'forward' or (start_rev_id
607
and not _is_obvious_ancestor(branch, start_rev_id, end_rev_id)):
609
result = list(result)
610
except _StartNotLinearAncestor:
611
raise errors.BzrCommandError('Start revision not found in'
612
' left-hand history of end revision.')
616
612
def _generate_all_revisions(branch, start_rev_id, end_rev_id, direction,
617
613
delayed_graph_generation,
618
614
exclude_common_ancestry=False):
654
650
except _StartNotLinearAncestor:
655
651
# A merge was never detected so the lower revision limit can't
656
652
# be nested down somewhere
657
raise errors.BzrCommandError('Start revision not found in'
658
' history of end revision.')
653
raise errors.BzrCommandError(gettext('Start revision not found in'
654
' history of end revision.'))
660
656
# We exit the loop above because we encounter a revision with merges, from
661
657
# this revision, we need to switch to _graph_view_revisions.
1072
1068
if branch_revno != 0:
1073
1069
if (start_rev_id == _mod_revision.NULL_REVISION
1074
1070
or end_rev_id == _mod_revision.NULL_REVISION):
1075
raise errors.BzrCommandError('Logging revision 0 is invalid.')
1071
raise errors.BzrCommandError(gettext('Logging revision 0 is invalid.'))
1076
1072
if start_revno > end_revno:
1077
raise errors.BzrCommandError("Start revision must be older than "
1078
"the end revision.")
1073
raise errors.BzrCommandError(gettext("Start revision must be "
1074
"older than the end revision."))
1079
1075
return (start_rev_id, end_rev_id)
1131
1127
if ((start_rev_id == _mod_revision.NULL_REVISION)
1132
1128
or (end_rev_id == _mod_revision.NULL_REVISION)):
1133
raise errors.BzrCommandError('Logging revision 0 is invalid.')
1129
raise errors.BzrCommandError(gettext('Logging revision 0 is invalid.'))
1134
1130
if start_revno > end_revno:
1135
raise errors.BzrCommandError("Start revision must be older than "
1136
"the end revision.")
1131
raise errors.BzrCommandError(gettext("Start revision must be older "
1132
"than the end revision."))
1138
1134
if end_revno < start_revno:
1139
1135
return None, None, None, None
1789
1785
return self.get(name)(*args, **kwargs)
1791
1787
def get_default(self, branch):
1792
return self.get(branch.get_config().log_format())
1788
c = branch.get_config_stack()
1789
return self.get(c.get('log_format'))
1795
1792
log_formatter_registry = LogFormatterRegistry()
1798
1795
log_formatter_registry.register('short', ShortLogFormatter,
1799
'Moderately short log format')
1796
'Moderately short log format.')
1800
1797
log_formatter_registry.register('long', LongLogFormatter,
1801
'Detailed log format')
1798
'Detailed log format.')
1802
1799
log_formatter_registry.register('line', LineLogFormatter,
1803
'Log format with one line per revision')
1800
'Log format with one line per revision.')
1804
1801
log_formatter_registry.register('gnu-changelog', GnuChangelogLogFormatter,
1805
'Format used by GNU ChangeLog files')
1802
'Format used by GNU ChangeLog files.')
1808
1805
def register_formatter(name, formatter):
2017
2014
kind is one of values 'directory', 'file', 'symlink', 'tree-reference'.
2018
2015
branch will be read-locked.
2020
from builtins import _get_revision_range
2021
tree, b, path = bzrdir.BzrDir.open_containing_tree_or_branch(file_list[0])
2017
from bzrlib.builtins import _get_revision_range
2018
tree, b, path = controldir.ControlDir.open_containing_tree_or_branch(
2022
2020
add_cleanup(b.lock_read().unlock)
2023
2021
# XXX: It's damn messy converting a list of paths to relative paths when
2024
2022
# those paths might be deleted ones, they might be on a case-insensitive
2113
2111
len(row) > 1 and row[1] == 'fixed']
2115
2113
if fixed_bug_urls:
2116
return {'fixes bug(s)': ' '.join(fixed_bug_urls)}
2114
return {ngettext('fixes bug', 'fixes bugs', len(fixed_bug_urls)):\
2115
' '.join(fixed_bug_urls)}
2119
2118
properties_handler_registry.register('bugs_properties_handler',