~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Andrew Starr-Bochicchio
  • Date: 2014-03-30 17:59:29 UTC
  • mto: This revision was merged to the branch mainline in revision 6592.
  • Revision ID: a.starr.b@gmail.com-20140330175929-rd97jstcbau2j1gy
Use LooseVersion from distutils to check Cython version in order to handle non-integers in the version string.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
16
16
 
17
 
 
18
 
 
19
17
"""Code to show logs of changes.
20
18
 
21
19
Various flavors of log can be produced:
49
47
all the changes since the previous revision that touched hello.c.
50
48
"""
51
49
 
 
50
from __future__ import absolute_import
 
51
 
52
52
import codecs
53
53
from cStringIO import StringIO
54
54
from itertools import (
65
65
lazy_import(globals(), """
66
66
 
67
67
from bzrlib import (
68
 
    bzrdir,
69
68
    config,
 
69
    controldir,
70
70
    diff,
71
71
    errors,
72
72
    foreign,
74
74
    revision as _mod_revision,
75
75
    revisionspec,
76
76
    tsort,
77
 
    i18n,
78
77
    )
 
78
from bzrlib.i18n import gettext, ngettext
79
79
""")
80
80
 
81
81
from bzrlib import (
105
105
    last_ie = None
106
106
    last_path = None
107
107
    revno = 1
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]
135
138
        revno += 1
136
139
 
137
140
 
138
 
def _enumerate_history(branch):
139
 
    rh = []
140
 
    revno = 1
141
 
    for rev_id in branch.revision_history():
142
 
        rh.append((revno, rev_id))
143
 
        revno += 1
144
 
    return rh
145
 
 
146
 
 
147
141
def show_log(branch,
148
142
             lf,
149
143
             specific_fileid=None,
215
209
    Logger(branch, rqst).show(lf)
216
210
 
217
211
 
218
 
# Note: This needs to be kept this in sync with the defaults in
 
212
# Note: This needs to be kept in sync with the defaults in
219
213
# make_log_request_dict() below
220
214
_DEFAULT_REQUEST_PARAMS = {
221
215
    'direction': 'reverse',
232
226
                          delta_type=None,
233
227
                          diff_type=None, _match_using_deltas=True,
234
228
                          exclude_common_ancestry=False, match=None,
235
 
                          signature=False,
 
229
                          signature=False, omit_merges=False,
236
230
                          ):
237
231
    """Convenience function for making a logging request dictionary.
238
232
 
288
282
      revisions. Keys can be 'message', 'author', 'committer', 'bugs' or
289
283
      the empty string to match any of the preceding properties.
290
284
 
 
285
    :param omit_merges: If True, commits with more than one parent are
 
286
      omitted.
 
287
 
291
288
    """
292
289
    # Take care of old style message_search parameter
293
290
    if message_search:
311
308
        'exclude_common_ancestry': exclude_common_ancestry,
312
309
        'signature': signature,
313
310
        'match': match,
 
311
        'omit_merges': omit_merges,
314
312
        # Add 'private' attributes for features that may be deprecated
315
313
        '_match_using_deltas': _match_using_deltas,
316
314
    }
334
332
    from bzrlib import gpg
335
333
 
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:
447
445
        limit = rqst.get('limit')
448
446
        diff_type = rqst.get('diff_type')
449
447
        show_signature = rqst.get('signature')
 
448
        omit_merges = rqst.get('omit_merges')
450
449
        log_count = 0
451
450
        revision_iterator = self._create_log_revision_iterator()
452
451
        for revs in revision_iterator:
454
453
                # 0 levels means show everything; merge_depth counts from 0
455
454
                if levels != 0 and merge_depth >= levels:
456
455
                    continue
 
456
                if omit_merges and len(rev.parent_ids) > 1:
 
457
                    continue
457
458
                if diff_type is None:
458
459
                    diff = None
459
460
                else:
558
559
             a list of the same tuples.
559
560
    """
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:
567
568
        return []
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,
574
 
                                           br_revno)
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)
581
 
    else:
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,
 
575
                                       br_revno)
 
576
    if not generate_merge_revisions:
 
577
        try:
 
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)
 
590
            return 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.
 
594
            pass
 
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)))
587
600
    return iter_revs
588
601
 
589
602
 
596
609
        return [(rev_id, revno_str, 0)]
597
610
 
598
611
 
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)):
608
 
        try:
609
 
            result = list(result)
610
 
        except _StartNotLinearAncestor:
611
 
            raise errors.BzrCommandError('Start revision not found in'
612
 
                ' left-hand history of end revision.')
613
 
    return result
614
 
 
615
 
 
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.'))
659
655
 
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)
1080
1076
 
1081
1077
 
1130
1126
 
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."))
1137
1133
 
1138
1134
    if end_revno < start_revno:
1139
1135
        return None, None, None, None
1405
1401
            if advice_sep:
1406
1402
                self.to_file.write(advice_sep)
1407
1403
            self.to_file.write(
1408
 
                "Use --include-merges or -n0 to see merged revisions.\n")
 
1404
                "Use --include-merged or -n0 to see merged revisions.\n")
1409
1405
 
1410
1406
    def get_advice_separator(self):
1411
1407
        """Get the text separating the log from the closing advice."""
1789
1785
        return self.get(name)(*args, **kwargs)
1790
1786
 
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'))
1793
1790
 
1794
1791
 
1795
1792
log_formatter_registry = LogFormatterRegistry()
1796
1793
 
1797
1794
 
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.')
1806
1803
 
1807
1804
 
1808
1805
def register_formatter(name, formatter):
1818
1815
    try:
1819
1816
        return log_formatter_registry.make_formatter(name, *args, **kwargs)
1820
1817
    except KeyError:
1821
 
        raise errors.BzrCommandError("unknown log formatter: %r" % name)
 
1818
        raise errors.BzrCommandError(gettext("unknown log formatter: %r") % name)
1822
1819
 
1823
1820
 
1824
1821
def author_list_all(rev):
2017
2014
      kind is one of values 'directory', 'file', 'symlink', 'tree-reference'.
2018
2015
      branch will be read-locked.
2019
2016
    """
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(
 
2019
        file_list[0])
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']
2114
2112
 
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)}
2117
2116
    return {}
2118
2117
 
2119
2118
properties_handler_registry.register('bugs_properties_handler',