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
236
diff_type=None, _match_using_deltas=True,
234
237
exclude_common_ancestry=False, match=None,
238
signature=False, omit_merges=False,
237
240
"""Convenience function for making a logging request dictionary.
334
341
from bzrlib import gpg
336
343
gpg_strategy = gpg.GPGStrategy(None)
337
result = repo.verify_revision(rev_id, gpg_strategy)
344
result = repo.verify_revision_signature(rev_id, gpg_strategy)
338
345
if result[0] == gpg.SIGNATURE_VALID:
339
346
return "valid signature from {0}".format(result[1])
340
347
if result[0] == gpg.SIGNATURE_KEY_MISSING:
558
568
a list of the same tuples.
560
570
if (exclude_common_ancestry and start_rev_id == end_rev_id):
561
raise errors.BzrCommandError(
562
'--exclude-common-ancestry requires two different revisions')
571
raise errors.BzrCommandError(gettext(
572
'--exclude-common-ancestry requires two different revisions'))
563
573
if direction not in ('reverse', 'forward'):
564
raise ValueError('invalid direction %r' % direction)
574
raise ValueError(gettext('invalid direction %r') % direction)
565
575
br_revno, br_rev_id = branch.last_revision_info()
566
576
if br_revno == 0:
570
580
and (not generate_merge_revisions
571
581
or not _has_merges(branch, end_rev_id))):
572
582
# 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)))
583
return _generate_one_revision(branch, end_rev_id, br_rev_id,
585
if not generate_merge_revisions:
587
# If we only want to see linear revisions, we can iterate ...
588
iter_revs = _linear_view_revisions(
589
branch, start_rev_id, end_rev_id,
590
exclude_common_ancestry=exclude_common_ancestry)
591
# If a start limit was given and it's not obviously an
592
# ancestor of the end limit, check it before outputting anything
593
if (direction == 'forward'
594
or (start_rev_id and not _is_obvious_ancestor(
595
branch, start_rev_id, end_rev_id))):
596
iter_revs = list(iter_revs)
597
if direction == 'forward':
598
iter_revs = reversed(iter_revs)
600
except _StartNotLinearAncestor:
601
# Switch to the slower implementation that may be able to find a
602
# non-obvious ancestor out of the left-hand history.
604
iter_revs = _generate_all_revisions(branch, start_rev_id, end_rev_id,
605
direction, delayed_graph_generation,
606
exclude_common_ancestry)
607
if direction == 'forward':
608
iter_revs = _rebase_merge_depth(reverse_by_depth(list(iter_revs)))
596
618
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
621
def _generate_all_revisions(branch, start_rev_id, end_rev_id, direction,
617
622
delayed_graph_generation,
618
623
exclude_common_ancestry=False):
654
659
except _StartNotLinearAncestor:
655
660
# A merge was never detected so the lower revision limit can't
656
661
# be nested down somewhere
657
raise errors.BzrCommandError('Start revision not found in'
658
' history of end revision.')
662
raise errors.BzrCommandError(gettext('Start revision not found in'
663
' history of end revision.'))
660
665
# We exit the loop above because we encounter a revision with merges, from
661
666
# this revision, we need to switch to _graph_view_revisions.
1072
1077
if branch_revno != 0:
1073
1078
if (start_rev_id == _mod_revision.NULL_REVISION
1074
1079
or end_rev_id == _mod_revision.NULL_REVISION):
1075
raise errors.BzrCommandError('Logging revision 0 is invalid.')
1080
raise errors.BzrCommandError(gettext('Logging revision 0 is invalid.'))
1076
1081
if start_revno > end_revno:
1077
raise errors.BzrCommandError("Start revision must be older than "
1078
"the end revision.")
1082
raise errors.BzrCommandError(gettext("Start revision must be "
1083
"older than the end revision."))
1079
1084
return (start_rev_id, end_rev_id)
1131
1136
if ((start_rev_id == _mod_revision.NULL_REVISION)
1132
1137
or (end_rev_id == _mod_revision.NULL_REVISION)):
1133
raise errors.BzrCommandError('Logging revision 0 is invalid.')
1138
raise errors.BzrCommandError(gettext('Logging revision 0 is invalid.'))
1134
1139
if start_revno > end_revno:
1135
raise errors.BzrCommandError("Start revision must be older than "
1136
"the end revision.")
1140
raise errors.BzrCommandError(gettext("Start revision must be older "
1141
"than the end revision."))
1138
1143
if end_revno < start_revno:
1139
1144
return None, None, None, None
1789
1794
return self.get(name)(*args, **kwargs)
1791
1796
def get_default(self, branch):
1792
return self.get(branch.get_config().log_format())
1797
c = branch.get_config_stack()
1798
return self.get(c.get('log_format'))
1795
1801
log_formatter_registry = LogFormatterRegistry()
1798
1804
log_formatter_registry.register('short', ShortLogFormatter,
1799
'Moderately short log format')
1805
'Moderately short log format.')
1800
1806
log_formatter_registry.register('long', LongLogFormatter,
1801
'Detailed log format')
1807
'Detailed log format.')
1802
1808
log_formatter_registry.register('line', LineLogFormatter,
1803
'Log format with one line per revision')
1809
'Log format with one line per revision.')
1804
1810
log_formatter_registry.register('gnu-changelog', GnuChangelogLogFormatter,
1805
'Format used by GNU ChangeLog files')
1811
'Format used by GNU ChangeLog files.')
1808
1814
def register_formatter(name, formatter):
2017
2023
kind is one of values 'directory', 'file', 'symlink', 'tree-reference'.
2018
2024
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])
2026
from bzrlib.builtins import _get_revision_range
2027
tree, b, path = controldir.ControlDir.open_containing_tree_or_branch(
2022
2029
add_cleanup(b.lock_read().unlock)
2023
2030
# XXX: It's damn messy converting a list of paths to relative paths when
2024
2031
# those paths might be deleted ones, they might be on a case-insensitive
2113
2120
len(row) > 1 and row[1] == 'fixed']
2115
2122
if fixed_bug_urls:
2116
return {'fixes bug(s)': ' '.join(fixed_bug_urls)}
2123
return {ngettext('fixes bug', 'fixes bugs', len(fixed_bug_urls)):\
2124
' '.join(fixed_bug_urls)}
2119
2127
properties_handler_registry.register('bugs_properties_handler',