~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-08-17 18:13:57 UTC
  • mfrom: (5268.7.29 transport-segments)
  • Revision ID: pqm@pqm.ubuntu.com-20110817181357-y5q5eth1hk8bl3om
(jelmer) Allow specifying the colocated branch to use in the branch URL,
 and retrieving the branch name using ControlDir._get_selected_branch.
 (Jelmer Vernooij)

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
 
17
19
"""Code to show logs of changes.
18
20
 
19
21
Various flavors of log can be produced:
47
49
all the changes since the previous revision that touched hello.c.
48
50
"""
49
51
 
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,
68
69
    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,
77
78
    )
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
 
    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):
 
108
    for revision_id in branch.revision_history():
112
109
        this_inv = branch.repository.get_inventory(revision_id)
113
110
        if this_inv.has_id(file_id):
114
111
            this_ie = this_inv[file_id]
138
135
        revno += 1
139
136
 
140
137
 
 
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
 
141
147
def show_log(branch,
142
148
             lf,
143
149
             specific_fileid=None,
209
215
    Logger(branch, rqst).show(lf)
210
216
 
211
217
 
212
 
# Note: This needs to be kept in sync with the defaults in
 
218
# Note: This needs to be kept this in sync with the defaults in
213
219
# make_log_request_dict() below
214
220
_DEFAULT_REQUEST_PARAMS = {
215
221
    'direction': 'reverse',
226
232
                          delta_type=None,
227
233
                          diff_type=None, _match_using_deltas=True,
228
234
                          exclude_common_ancestry=False, match=None,
229
 
                          signature=False, omit_merges=False,
 
235
                          signature=False,
230
236
                          ):
231
237
    """Convenience function for making a logging request dictionary.
232
238
 
282
288
      revisions. Keys can be 'message', 'author', 'committer', 'bugs' or
283
289
      the empty string to match any of the preceding properties.
284
290
 
285
 
    :param omit_merges: If True, commits with more than one parent are
286
 
      omitted.
287
 
 
288
291
    """
289
292
    # Take care of old style message_search parameter
290
293
    if message_search:
308
311
        'exclude_common_ancestry': exclude_common_ancestry,
309
312
        'signature': signature,
310
313
        'match': match,
311
 
        'omit_merges': omit_merges,
312
314
        # Add 'private' attributes for features that may be deprecated
313
315
        '_match_using_deltas': _match_using_deltas,
314
316
    }
332
334
    from bzrlib import gpg
333
335
 
334
336
    gpg_strategy = gpg.GPGStrategy(None)
335
 
    result = repo.verify_revision_signature(rev_id, gpg_strategy)
 
337
    result = repo.verify_revision(rev_id, gpg_strategy)
336
338
    if result[0] == gpg.SIGNATURE_VALID:
337
 
        return u"valid signature from {0}".format(result[1])
 
339
        return "valid signature from {0}".format(result[1])
338
340
    if result[0] == gpg.SIGNATURE_KEY_MISSING:
339
341
        return "unknown key {0}".format(result[1])
340
342
    if result[0] == gpg.SIGNATURE_NOT_VALID:
445
447
        limit = rqst.get('limit')
446
448
        diff_type = rqst.get('diff_type')
447
449
        show_signature = rqst.get('signature')
448
 
        omit_merges = rqst.get('omit_merges')
449
450
        log_count = 0
450
451
        revision_iterator = self._create_log_revision_iterator()
451
452
        for revs in revision_iterator:
453
454
                # 0 levels means show everything; merge_depth counts from 0
454
455
                if levels != 0 and merge_depth >= levels:
455
456
                    continue
456
 
                if omit_merges and len(rev.parent_ids) > 1:
457
 
                    continue
458
457
                if diff_type is None:
459
458
                    diff = None
460
459
                else:
559
558
             a list of the same tuples.
560
559
    """
561
560
    if (exclude_common_ancestry and start_rev_id == end_rev_id):
562
 
        raise errors.BzrCommandError(gettext(
563
 
            '--exclude-common-ancestry requires two different revisions'))
 
561
        raise errors.BzrCommandError(
 
562
            '--exclude-common-ancestry requires two different revisions')
564
563
    if direction not in ('reverse', 'forward'):
565
 
        raise ValueError(gettext('invalid direction %r') % direction)
 
564
        raise ValueError('invalid direction %r' % direction)
566
565
    br_revno, br_rev_id = branch.last_revision_info()
567
566
    if br_revno == 0:
568
567
        return []
571
570
        and (not generate_merge_revisions
572
571
             or not _has_merges(branch, end_rev_id))):
573
572
        # If a single revision is requested, check we can handle it
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)))
 
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)))
600
587
    return iter_revs
601
588
 
602
589
 
609
596
        return [(rev_id, revno_str, 0)]
610
597
 
611
598
 
 
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
 
612
616
def _generate_all_revisions(branch, start_rev_id, end_rev_id, direction,
613
617
                            delayed_graph_generation,
614
618
                            exclude_common_ancestry=False):
650
654
        except _StartNotLinearAncestor:
651
655
            # A merge was never detected so the lower revision limit can't
652
656
            # be nested down somewhere
653
 
            raise errors.BzrCommandError(gettext('Start revision not found in'
654
 
                ' history of end revision.'))
 
657
            raise errors.BzrCommandError('Start revision not found in'
 
658
                ' history of end revision.')
655
659
 
656
660
    # We exit the loop above because we encounter a revision with merges, from
657
661
    # this revision, we need to switch to _graph_view_revisions.
1068
1072
    if branch_revno != 0:
1069
1073
        if (start_rev_id == _mod_revision.NULL_REVISION
1070
1074
            or end_rev_id == _mod_revision.NULL_REVISION):
1071
 
            raise errors.BzrCommandError(gettext('Logging revision 0 is invalid.'))
 
1075
            raise errors.BzrCommandError('Logging revision 0 is invalid.')
1072
1076
        if start_revno > end_revno:
1073
 
            raise errors.BzrCommandError(gettext("Start revision must be "
1074
 
                                         "older than the end revision."))
 
1077
            raise errors.BzrCommandError("Start revision must be older than "
 
1078
                                         "the end revision.")
1075
1079
    return (start_rev_id, end_rev_id)
1076
1080
 
1077
1081
 
1126
1130
 
1127
1131
    if ((start_rev_id == _mod_revision.NULL_REVISION)
1128
1132
        or (end_rev_id == _mod_revision.NULL_REVISION)):
1129
 
        raise errors.BzrCommandError(gettext('Logging revision 0 is invalid.'))
 
1133
        raise errors.BzrCommandError('Logging revision 0 is invalid.')
1130
1134
    if start_revno > end_revno:
1131
 
        raise errors.BzrCommandError(gettext("Start revision must be older "
1132
 
                                     "than the end revision."))
 
1135
        raise errors.BzrCommandError("Start revision must be older than "
 
1136
                                     "the end revision.")
1133
1137
 
1134
1138
    if end_revno < start_revno:
1135
1139
        return None, None, None, None
1401
1405
            if advice_sep:
1402
1406
                self.to_file.write(advice_sep)
1403
1407
            self.to_file.write(
1404
 
                "Use --include-merged or -n0 to see merged revisions.\n")
 
1408
                "Use --include-merges or -n0 to see merged revisions.\n")
1405
1409
 
1406
1410
    def get_advice_separator(self):
1407
1411
        """Get the text separating the log from the closing advice."""
1785
1789
        return self.get(name)(*args, **kwargs)
1786
1790
 
1787
1791
    def get_default(self, branch):
1788
 
        c = branch.get_config_stack()
1789
 
        return self.get(c.get('log_format'))
 
1792
        return self.get(branch.get_config().log_format())
1790
1793
 
1791
1794
 
1792
1795
log_formatter_registry = LogFormatterRegistry()
1793
1796
 
1794
1797
 
1795
1798
log_formatter_registry.register('short', ShortLogFormatter,
1796
 
                                'Moderately short log format.')
 
1799
                                'Moderately short log format')
1797
1800
log_formatter_registry.register('long', LongLogFormatter,
1798
 
                                'Detailed log format.')
 
1801
                                'Detailed log format')
1799
1802
log_formatter_registry.register('line', LineLogFormatter,
1800
 
                                'Log format with one line per revision.')
 
1803
                                'Log format with one line per revision')
1801
1804
log_formatter_registry.register('gnu-changelog', GnuChangelogLogFormatter,
1802
 
                                'Format used by GNU ChangeLog files.')
 
1805
                                'Format used by GNU ChangeLog files')
1803
1806
 
1804
1807
 
1805
1808
def register_formatter(name, formatter):
1815
1818
    try:
1816
1819
        return log_formatter_registry.make_formatter(name, *args, **kwargs)
1817
1820
    except KeyError:
1818
 
        raise errors.BzrCommandError(gettext("unknown log formatter: %r") % name)
 
1821
        raise errors.BzrCommandError("unknown log formatter: %r" % name)
1819
1822
 
1820
1823
 
1821
1824
def author_list_all(rev):
2014
2017
      kind is one of values 'directory', 'file', 'symlink', 'tree-reference'.
2015
2018
      branch will be read-locked.
2016
2019
    """
2017
 
    from bzrlib.builtins import _get_revision_range
2018
 
    tree, b, path = controldir.ControlDir.open_containing_tree_or_branch(
2019
 
        file_list[0])
 
2020
    from builtins import _get_revision_range
 
2021
    tree, b, path = bzrdir.BzrDir.open_containing_tree_or_branch(file_list[0])
2020
2022
    add_cleanup(b.lock_read().unlock)
2021
2023
    # XXX: It's damn messy converting a list of paths to relative paths when
2022
2024
    # those paths might be deleted ones, they might be on a case-insensitive
2111
2113
                          len(row) > 1 and row[1] == 'fixed']
2112
2114
 
2113
2115
        if fixed_bug_urls:
2114
 
            return {ngettext('fixes bug', 'fixes bugs', len(fixed_bug_urls)):\
2115
 
                    ' '.join(fixed_bug_urls)}
 
2116
            return {'fixes bug(s)': ' '.join(fixed_bug_urls)}
2116
2117
    return {}
2117
2118
 
2118
2119
properties_handler_registry.register('bugs_properties_handler',