/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/log.py

  • Committer: Jelmer Vernooij
  • Date: 2019-03-04 00:16:27 UTC
  • mfrom: (7293 work)
  • mto: This revision was merged to the branch mainline in revision 7318.
  • Revision ID: jelmer@jelmer.uk-20190304001627-v6u7o6pf97tukhek
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
65
65
    controldir,
66
66
    diff,
67
67
    foreign,
68
 
    repository as _mod_repository,
 
68
    lazy_regex,
69
69
    revision as _mod_revision,
70
 
    tsort,
71
70
    )
72
71
from breezy.i18n import gettext, ngettext
73
72
""")
74
73
 
75
74
from . import (
76
75
    errors,
77
 
    lazy_regex,
78
76
    registry,
79
77
    revisionspec,
 
78
    trace,
80
79
    )
81
80
from .osutils import (
82
81
    format_date,
215
214
 
216
215
    # Build the request and execute it
217
216
    rqst = make_log_request_dict(direction=direction, specific_fileids=file_ids,
218
 
        start_revision=start_revision, end_revision=end_revision,
219
 
        limit=limit, message_search=search,
220
 
        delta_type=delta_type, diff_type=diff_type)
 
217
                                 start_revision=start_revision, end_revision=end_revision,
 
218
                                 limit=limit, message_search=search,
 
219
                                 delta_type=delta_type, diff_type=diff_type)
221
220
    Logger(branch, rqst).show(lf)
222
221
 
223
222
 
306
305
            else:
307
306
                match['message'] = [message_search]
308
307
        else:
309
 
            match= {'message': [message_search]}
 
308
            match = {'message': [message_search]}
310
309
    return {
311
310
        'direction': direction,
312
311
        'specific_fileids': specific_fileids,
422
421
                lf.log_revision(lr)
423
422
        except errors.GhostRevisionUnusableHere:
424
423
            raise errors.BzrCommandError(
425
 
                    gettext('Further revision history missing.'))
 
424
                gettext('Further revision history missing.'))
426
425
        lf.show_advice()
427
426
 
428
427
    def _generator_factory(self, branch, rqst):
464
463
        for revs in revision_iterator:
465
464
            for (rev_id, revno, merge_depth), rev, delta in revs:
466
465
                # 0 levels means show everything; merge_depth counts from 0
467
 
                if levels != 0 and merge_depth is not None and merge_depth >= levels:
 
466
                if (levels != 0 and merge_depth is not None and
 
467
                        merge_depth >= levels):
468
468
                    continue
469
469
                if omit_merges and len(rev.parent_ids) > 1:
470
470
                    continue
478
478
                    signature = format_signature_validity(rev_id, self.branch)
479
479
                else:
480
480
                    signature = None
481
 
                yield LogRevision(rev, revno, merge_depth, delta,
 
481
                yield LogRevision(
 
482
                    rev, revno, merge_depth, delta,
482
483
                    self.rev_tag_dict.get(rev_id), diff, signature)
483
484
                if limit:
484
485
                    log_count += 1
501
502
        s = BytesIO()
502
503
        path_encoding = get_diff_header_encoding()
503
504
        diff.show_diff_trees(tree_1, tree_2, s, specific_files, old_label='',
504
 
            new_label='', path_encoding=path_encoding)
 
505
                             new_label='', path_encoding=path_encoding)
505
506
        return s.getvalue()
506
507
 
507
508
    def _create_log_revision_iterator(self):
521
522
            # not a directory
522
523
            file_count = len(self.rqst.get('specific_fileids'))
523
524
            if file_count != 1:
524
 
                raise BzrError("illegal LogRequest: must match-using-deltas "
 
525
                raise errors.BzrError(
 
526
                    "illegal LogRequest: must match-using-deltas "
525
527
                    "when logging %d files" % file_count)
526
528
            return self._log_revision_iterator_using_per_file_graph()
527
529
 
530
532
        rqst = self.rqst
531
533
        generate_merge_revisions = rqst.get('levels') != 1
532
534
        delayed_graph_generation = not rqst.get('specific_fileids') and (
533
 
                rqst.get('limit') or self.start_rev_id or self.end_rev_id)
 
535
            rqst.get('limit') or self.start_rev_id or self.end_rev_id)
534
536
        view_revisions = _calc_view_revisions(
535
537
            self.branch, self.start_rev_id, self.end_rev_id,
536
538
            rqst.get('direction'),
540
542
 
541
543
        # Apply the other filters
542
544
        return make_log_rev_iterator(self.branch, view_revisions,
543
 
            rqst.get('delta_type'), rqst.get('match'),
544
 
            file_ids=rqst.get('specific_fileids'),
545
 
            direction=rqst.get('direction'))
 
545
                                     rqst.get('delta_type'), rqst.get('match'),
 
546
                                     file_ids=rqst.get('specific_fileids'),
 
547
                                     direction=rqst.get('direction'))
546
548
 
547
549
    def _log_revision_iterator_using_per_file_graph(self):
548
550
        # Get the base revisions, filtering by the revision range.
556
558
        if not isinstance(view_revisions, list):
557
559
            view_revisions = list(view_revisions)
558
560
        view_revisions = _filter_revisions_touching_file_id(self.branch,
559
 
            rqst.get('specific_fileids')[0], view_revisions,
560
 
            include_merges=rqst.get('levels') != 1)
 
561
                                                            rqst.get('specific_fileids')[
 
562
                                                                0], view_revisions,
 
563
                                                            include_merges=rqst.get('levels') != 1)
561
564
        return make_log_rev_iterator(self.branch, view_revisions,
562
 
            rqst.get('delta_type'), rqst.get('match'))
 
565
                                     rqst.get('delta_type'), rqst.get('match'))
563
566
 
564
567
 
565
568
def _calc_view_revisions(branch, start_rev_id, end_rev_id, direction,
585
588
        and (not generate_merge_revisions
586
589
             or not _has_merges(branch, end_rev_id))):
587
590
        # If a single revision is requested, check we can handle it
588
 
        return  _generate_one_revision(branch, end_rev_id, br_rev_id,
589
 
                                       branch.revno())
 
591
        return _generate_one_revision(branch, end_rev_id, br_rev_id,
 
592
                                      branch.revno())
590
593
    if not generate_merge_revisions:
591
594
        try:
592
595
            # If we only want to see linear revisions, we can iterate ...
597
600
            # ancestor of the end limit, check it before outputting anything
598
601
            if (direction == 'forward'
599
602
                or (start_rev_id and not _is_obvious_ancestor(
600
 
                        branch, start_rev_id, end_rev_id))):
601
 
                    iter_revs = list(iter_revs)
 
603
                    branch, start_rev_id, end_rev_id))):
 
604
                iter_revs = list(iter_revs)
602
605
            if direction == 'forward':
603
606
                iter_revs = reversed(iter_revs)
604
607
            return iter_revs
636
639
    initial_revisions = []
637
640
    if delayed_graph_generation:
638
641
        try:
639
 
            for rev_id, revno, depth in  _linear_view_revisions(
640
 
                branch, start_rev_id, end_rev_id, exclude_common_ancestry):
 
642
            for rev_id, revno, depth in _linear_view_revisions(
 
643
                    branch, start_rev_id, end_rev_id, exclude_common_ancestry):
641
644
                if _has_merges(branch, rev_id):
642
645
                    # The end_rev_id can be nested down somewhere. We need an
643
646
                    # explicit ancestry check. There is an ambiguity here as we
650
653
                    # -- vila 20100319
651
654
                    graph = branch.repository.get_graph()
652
655
                    if (start_rev_id is not None
653
 
                        and not graph.is_ancestor(start_rev_id, end_rev_id)):
 
656
                            and not graph.is_ancestor(start_rev_id, end_rev_id)):
654
657
                        raise _StartNotLinearAncestor()
655
658
                    # Since we collected the revisions so far, we need to
656
659
                    # adjust end_rev_id.
665
668
            # A merge was never detected so the lower revision limit can't
666
669
            # be nested down somewhere
667
670
            raise errors.BzrCommandError(gettext('Start revision not found in'
668
 
                ' history of end revision.'))
 
671
                                                 ' history of end revision.'))
669
672
 
670
673
    # We exit the loop above because we encounter a revision with merges, from
671
674
    # this revision, we need to switch to _graph_view_revisions.
676
679
    # make forward the exact opposite display, but showing the merge revisions
677
680
    # indented at the end seems slightly nicer in that case.
678
681
    view_revisions = itertools.chain(iter(initial_revisions),
679
 
        _graph_view_revisions(branch, start_rev_id, end_rev_id,
680
 
                              rebase_initial_depths=(direction == 'reverse'),
681
 
                              exclude_common_ancestry=exclude_common_ancestry))
 
682
                                     _graph_view_revisions(branch, start_rev_id, end_rev_id,
 
683
                                                           rebase_initial_depths=(
 
684
                                                               direction == 'reverse'),
 
685
                                                           exclude_common_ancestry=exclude_common_ancestry))
682
686
    return view_revisions
683
687
 
684
688
 
716
720
            # both on mainline
717
721
            return start_dotted[0] <= end_dotted[0]
718
722
        elif (len(start_dotted) == 3 and len(end_dotted) == 3 and
719
 
            start_dotted[0:1] == end_dotted[0:1]):
 
723
              start_dotted[0:1] == end_dotted[0:1]):
720
724
            # both on same development line
721
725
            return start_dotted[2] <= end_dotted[2]
722
726
        else:
751
755
        else:
752
756
            cur_revno = br_revno
753
757
        graph_iter = graph.iter_lefthand_ancestry(br_rev_id,
754
 
            (_mod_revision.NULL_REVISION,))
 
758
                                                  (_mod_revision.NULL_REVISION,))
755
759
        while True:
756
760
            try:
757
761
                revision_id = next(graph_iter)
771
775
            end_rev_id = br_rev_id
772
776
        found_start = start_rev_id is None
773
777
        graph_iter = graph.iter_lefthand_ancestry(end_rev_id,
774
 
            (_mod_revision.NULL_REVISION,))
 
778
                                                  (_mod_revision.NULL_REVISION,))
775
779
        while True:
776
780
            try:
777
781
                revision_id = next(graph_iter)
844
848
    if view_revisions and view_revisions[0][2] and view_revisions[-1][2]:
845
849
        min_depth = min([d for r, n, d in view_revisions])
846
850
        if min_depth != 0:
847
 
            view_revisions = [(r, n, d-min_depth) for r, n, d in view_revisions]
 
851
            view_revisions = [(r, n, d - min_depth)
 
852
                              for r, n, d in view_revisions]
848
853
    return view_revisions
849
854
 
850
855
 
851
856
def make_log_rev_iterator(branch, view_revisions, generate_delta, search,
852
 
        file_ids=None, direction='reverse'):
 
857
                          file_ids=None, direction='reverse'):
853
858
    """Create a revision iterator for log.
854
859
 
855
860
    :param branch: The branch being logged.
879
884
        # It would be nicer if log adapters were first class objects
880
885
        # with custom parameters. This will do for now. IGC 20090127
881
886
        if adapter == _make_delta_filter:
882
 
            log_rev_iterator = adapter(branch, generate_delta,
883
 
                search, log_rev_iterator, file_ids, direction)
 
887
            log_rev_iterator = adapter(
 
888
                branch, generate_delta, search, log_rev_iterator, file_ids,
 
889
                direction)
884
890
        else:
885
 
            log_rev_iterator = adapter(branch, generate_delta,
886
 
                search, log_rev_iterator)
 
891
            log_rev_iterator = adapter(
 
892
                branch, generate_delta, search, log_rev_iterator)
887
893
    return log_rev_iterator
888
894
 
889
895
 
903
909
    """
904
910
    if not match:
905
911
        return log_rev_iterator
906
 
    searchRE = [(k, [re.compile(x, re.IGNORECASE) for x in v])
 
912
    # Use lazy_compile so mapping to InvalidPattern error occurs.
 
913
    searchRE = [(k, [lazy_regex.lazy_compile(x, re.IGNORECASE) for x in v])
907
914
                for k, v in match.items()]
908
915
    return _filter_re(searchRE, log_rev_iterator)
909
916
 
914
921
        if new_revs:
915
922
            yield new_revs
916
923
 
 
924
 
917
925
def _match_filter(searchRE, rev):
918
926
    strings = {
919
 
               'message': (rev.message,),
920
 
               'committer': (rev.committer,),
921
 
               'author': (rev.get_apparent_authors()),
922
 
               'bugs': list(rev.iter_bugs())
923
 
               }
 
927
        'message': (rev.message,),
 
928
        'committer': (rev.committer,),
 
929
        'author': (rev.get_apparent_authors()),
 
930
        'bugs': list(rev.iter_bugs())
 
931
        }
924
932
    strings[''] = [item for inner_list in strings.values()
925
933
                   for item in inner_list]
926
 
    for (k, v) in searchRE:
 
934
    for k, v in searchRE:
927
935
        if k in strings and not _match_any_filter(strings[k], v):
928
936
            return False
929
937
    return True
930
938
 
 
939
 
931
940
def _match_any_filter(strings, res):
932
 
    return any(re.search(s) for re in res for s in strings)
 
941
    return any(r.search(s) for r in res for s in strings)
 
942
 
933
943
 
934
944
def _make_delta_filter(branch, generate_delta, search, log_rev_iterator,
935
 
    fileids=None, direction='reverse'):
 
945
                       fileids=None, direction='reverse'):
936
946
    """Add revision deltas to a log iterator if needed.
937
947
 
938
948
    :param branch: The branch being logged.
950
960
    if not generate_delta and not fileids:
951
961
        return log_rev_iterator
952
962
    return _generate_deltas(branch.repository, log_rev_iterator,
953
 
        generate_delta, fileids, direction)
 
963
                            generate_delta, fileids, direction)
954
964
 
955
965
 
956
966
def _generate_deltas(repository, log_rev_iterator, delta_type, fileids,
957
 
    direction):
 
967
                     direction):
958
968
    """Create deltas for each batch of revisions in log_rev_iterator.
959
969
 
960
970
    If we're only generating deltas for the sake of filtering against
1103
1113
 
1104
1114
    if branch.last_revision() != _mod_revision.NULL_REVISION:
1105
1115
        if (start_rev_id == _mod_revision.NULL_REVISION
1106
 
            or end_rev_id == _mod_revision.NULL_REVISION):
1107
 
            raise errors.BzrCommandError(gettext('Logging revision 0 is invalid.'))
 
1116
                or end_rev_id == _mod_revision.NULL_REVISION):
 
1117
            raise errors.BzrCommandError(
 
1118
                gettext('Logging revision 0 is invalid.'))
1108
1119
        if end_revno is not None and start_revno > end_revno:
1109
 
            raise errors.BzrCommandError(gettext("Start revision must be "
1110
 
                                         "older than the end revision."))
 
1120
            raise errors.BzrCommandError(
 
1121
                gettext("Start revision must be older than the end revision."))
1111
1122
    return (start_rev_id, end_rev_id)
1112
1123
 
1113
1124
 
1161
1172
            end_revno = end_revision
1162
1173
 
1163
1174
    if ((start_rev_id == _mod_revision.NULL_REVISION)
1164
 
        or (end_rev_id == _mod_revision.NULL_REVISION)):
 
1175
            or (end_rev_id == _mod_revision.NULL_REVISION)):
1165
1176
        raise errors.BzrCommandError(gettext('Logging revision 0 is invalid.'))
1166
1177
    if start_revno > end_revno:
1167
1178
        raise errors.BzrCommandError(gettext("Start revision must be older "
1168
 
                                     "than the end revision."))
 
1179
                                             "than the end revision."))
1169
1180
 
1170
1181
    if end_revno < start_revno:
1171
1182
        return None, None, None, None
1195
1206
 
1196
1207
 
1197
1208
def _filter_revisions_touching_file_id(branch, file_id, view_revisions,
1198
 
    include_merges=True):
 
1209
                                       include_merges=True):
1199
1210
    r"""Return the list of revision ids which touch a given file id.
1200
1211
 
1201
1212
    The function filters view_revisions and returns a subset.
1282
1293
    """Reverse revisions by depth.
1283
1294
 
1284
1295
    Revisions with a different depth are sorted as a group with the previous
1285
 
    revision of that depth.  There may be no topological justification for this,
 
1296
    revision of that depth.  There may be no topological justification for this
1286
1297
    but it looks much nicer.
1287
1298
    """
1288
1299
    # Add a fake revision at start so that we can always attach sub revisions
1395
1406
        """
1396
1407
        self.to_file = to_file
1397
1408
        # 'exact' stream used to show diff, it should print content 'as is'
1398
 
        # and should not try to decode/encode it to unicode to avoid bug #328007
 
1409
        # and should not try to decode/encode it to unicode to avoid bug
 
1410
        # #328007
1399
1411
        if to_exact_file is not None:
1400
1412
            self.to_exact_file = to_exact_file
1401
1413
        else:
1402
 
            # XXX: somewhat hacky; this assumes it's a codec writer; it's better
1403
 
            # for code that expects to get diffs to pass in the exact file
1404
 
            # stream
 
1414
            # XXX: somewhat hacky; this assumes it's a codec writer; it's
 
1415
            # better for code that expects to get diffs to pass in the exact
 
1416
            # file stream
1405
1417
            self.to_exact_file = getattr(to_file, 'stream', to_file)
1406
1418
        self.show_ids = show_ids
1407
1419
        self.show_timezone = show_timezone
1408
1420
        if delta_format is None:
1409
1421
            # Ensures backward compatibility
1410
 
            delta_format = 2 # long format
 
1422
            delta_format = 2  # long format
1411
1423
        self.delta_format = delta_format
1412
1424
        self.levels = levels
1413
1425
        self._show_advice = show_advice
1511
1523
        """
1512
1524
        lines = self._foreign_info_properties(revision)
1513
1525
        for key, handler in properties_handler_registry.iteritems():
1514
 
            lines.extend(self._format_properties(handler(revision)))
 
1526
            try:
 
1527
                lines.extend(self._format_properties(handler(revision)))
 
1528
            except Exception:
 
1529
                trace.log_exception_quietly()
 
1530
                trace.print_exception(sys.exc_info(), self.to_file)
1515
1531
        return lines
1516
1532
 
1517
1533
    def _foreign_info_properties(self, rev):
1525
1541
                rev.mapping.vcs.show_foreign_revid(rev.foreign_revid))
1526
1542
 
1527
1543
        # Imported foreign revision revision ids always contain :
1528
 
        if not b":" in rev.revision_id:
 
1544
        if b":" not in rev.revision_id:
1529
1545
            return []
1530
1546
 
1531
1547
        # Revision was once imported from a foreign repository
1576
1592
 
1577
1593
    def _date_string_original_timezone(self, rev):
1578
1594
        return format_date_with_offset_in_original_timezone(rev.timestamp,
1579
 
            rev.timezone or 0)
 
1595
                                                            rev.timezone or 0)
1580
1596
 
1581
1597
    def log_revision(self, revision):
1582
1598
        """Log a revision, either merged or not."""
1584
1600
        lines = [_LONG_SEP]
1585
1601
        if revision.revno is not None:
1586
1602
            lines.append('revno: %s%s' % (revision.revno,
1587
 
                self.merge_marker(revision)))
 
1603
                                          self.merge_marker(revision)))
1588
1604
        if revision.tags:
1589
1605
            lines.append('tags: %s' % (', '.join(sorted(revision.tags))))
1590
1606
        if self.show_ids or revision.revno is None:
1591
 
            lines.append('revision-id: %s' % (revision.rev.revision_id.decode('utf-8'),))
 
1607
            lines.append('revision-id: %s' %
 
1608
                         (revision.rev.revision_id.decode('utf-8'),))
1592
1609
        if self.show_ids:
1593
1610
            for parent_id in revision.rev.parent_ids:
1594
1611
                lines.append('parent: %s' % (parent_id.decode('utf-8'),))
1675
1692
        if revision.tags:
1676
1693
            tags = ' {%s}' % (', '.join(sorted(revision.tags)))
1677
1694
        to_file.write(indent + "%*s %s\t%s%s%s\n" % (revno_width,
1678
 
                revision.revno or "", self.short_author(revision.rev),
1679
 
                format_date(revision.rev.timestamp,
1680
 
                            revision.rev.timezone or 0,
1681
 
                            self.show_timezone, date_fmt="%Y-%m-%d",
1682
 
                            show_offset=False),
1683
 
                tags, self.merge_marker(revision)))
1684
 
        self.show_properties(revision.rev, indent+offset)
 
1695
                                                     revision.revno or "", self.short_author(
 
1696
                                                         revision.rev),
 
1697
                                                     format_date(revision.rev.timestamp,
 
1698
                                                                 revision.rev.timezone or 0,
 
1699
                                                                 self.show_timezone, date_fmt="%Y-%m-%d",
 
1700
                                                                 show_offset=False),
 
1701
                                                     tags, self.merge_marker(revision)))
 
1702
        self.show_properties(revision.rev, indent + offset)
1685
1703
        if self.show_ids or revision.revno is None:
1686
1704
            to_file.write(indent + offset + 'revision-id:%s\n'
1687
1705
                          % (revision.rev.revision_id.decode('utf-8'),))
1696
1714
            # Use the standard status output to display changes
1697
1715
            from breezy.delta import report_delta
1698
1716
            report_delta(to_file, revision.delta,
1699
 
                         short_status=self.delta_format==1,
 
1717
                         short_status=self.delta_format == 1,
1700
1718
                         show_ids=self.show_ids, indent=indent + offset)
1701
1719
        if revision.diff is not None:
1702
1720
            self.show_diff(self.to_exact_file, revision.diff, '      ')
1720
1738
    def truncate(self, str, max_len):
1721
1739
        if max_len is None or len(str) <= max_len:
1722
1740
            return str
1723
 
        return str[:max_len-3] + '...'
 
1741
        return str[:max_len - 3] + '...'
1724
1742
 
1725
1743
    def date_string(self, rev):
1726
1744
        return format_date(rev.timestamp, rev.timezone or 0,
1736
1754
    def log_revision(self, revision):
1737
1755
        indent = '  ' * revision.merge_depth
1738
1756
        self.to_file.write(self.log_string(revision.revno, revision.rev,
1739
 
            self._max_chars, revision.tags, indent))
 
1757
                                           self._max_chars, revision.tags, indent))
1740
1758
        self.to_file.write('\n')
1741
1759
 
1742
1760
    def log_string(self, revno, rev, max_chars, tags=None, prefix=''):
1755
1773
            # show revno only when is not None
1756
1774
            out.append("%s:" % revno)
1757
1775
        if max_chars is not None:
1758
 
            out.append(self.truncate(self.short_author(rev), (max_chars+3)//4))
 
1776
            out.append(self.truncate(
 
1777
                self.short_author(rev), (max_chars + 3) // 4))
1759
1778
        else:
1760
1779
            out.append(self.short_author(rev))
1761
1780
        out.append(self.date_string(rev))
1852
1871
    try:
1853
1872
        return log_formatter_registry.make_formatter(name, *args, **kwargs)
1854
1873
    except KeyError:
1855
 
        raise errors.BzrCommandError(gettext("unknown log formatter: %r") % name)
 
1874
        raise errors.BzrCommandError(
 
1875
            gettext("unknown log formatter: %r") % name)
1856
1876
 
1857
1877
 
1858
1878
def author_list_all(rev):
1894
1914
    """
1895
1915
    if to_file is None:
1896
1916
        to_file = codecs.getwriter(get_terminal_encoding())(sys.stdout,
1897
 
            errors='replace')
 
1917
                                                            errors='replace')
1898
1918
    lf = log_formatter(log_format,
1899
1919
                       show_ids=False,
1900
1920
                       to_file=to_file,
1906
1926
    for i in range(max(len(new_rh), len(old_rh))):
1907
1927
        if (len(new_rh) <= i
1908
1928
            or len(old_rh) <= i
1909
 
            or new_rh[i] != old_rh[i]):
 
1929
                or new_rh[i] != old_rh[i]):
1910
1930
            base_idx = i
1911
1931
            break
1912
1932
 
1913
1933
    if base_idx is None:
1914
1934
        to_file.write('Nothing seems to have changed\n')
1915
1935
        return
1916
 
    ## TODO: It might be nice to do something like show_log
1917
 
    ##       and show the merged entries. But since this is the
1918
 
    ##       removed revisions, it shouldn't be as important
 
1936
    # TODO: It might be nice to do something like show_log
 
1937
    # and show the merged entries. But since this is the
 
1938
    # removed revisions, it shouldn't be as important
1919
1939
    if base_idx < len(old_rh):
1920
 
        to_file.write('*'*60)
 
1940
        to_file.write('*' * 60)
1921
1941
        to_file.write('\nRemoved Revisions:\n')
1922
1942
        for i in range(base_idx, len(old_rh)):
1923
1943
            rev = branch.repository.get_revision(old_rh[i])
1924
 
            lr = LogRevision(rev, i+1, 0, None)
 
1944
            lr = LogRevision(rev, i + 1, 0, None)
1925
1945
            lf.log_revision(lr)
1926
 
        to_file.write('*'*60)
 
1946
        to_file.write('*' * 60)
1927
1947
        to_file.write('\n\n')
1928
1948
    if base_idx < len(new_rh):
1929
1949
        to_file.write('Added Revisions:\n')
1932
1952
                 None,
1933
1953
                 verbose=False,
1934
1954
                 direction='forward',
1935
 
                 start_revision=base_idx+1,
 
1955
                 start_revision=base_idx + 1,
1936
1956
                 end_revision=len(new_rh),
1937
1957
                 search=None)
1938
1958
 
2006
2026
    log_format = log_formatter_registry.get_default(branch)
2007
2027
    lf = log_format(show_ids=False, to_file=output, show_timezone='original')
2008
2028
    if old_history != []:
2009
 
        output.write('*'*60)
 
2029
        output.write('*' * 60)
2010
2030
        output.write('\nRemoved Revisions:\n')
2011
2031
        show_flat_log(branch.repository, old_history, old_revno, lf)
2012
 
        output.write('*'*60)
 
2032
        output.write('*' * 60)
2013
2033
        output.write('\n\n')
2014
2034
    if new_history != []:
2015
2035
        output.write('Added Revisions:\n')
2026
2046
    :param last_revno: The revno of the last revision_id in the history.
2027
2047
    :param lf: The log formatter to use.
2028
2048
    """
2029
 
    start_revno = last_revno - len(history) + 1
2030
2049
    revisions = repository.get_revisions(history)
2031
2050
    for i, rev in enumerate(revisions):
2032
2051
        lr = LogRevision(rev, i + last_revno, 0, None)
2068
2087
        relpaths = [path] + file_list[1:]
2069
2088
    info_list = []
2070
2089
    start_rev_info, end_rev_info = _get_revision_range(revisionspec_list, b,
2071
 
        "log")
 
2090
                                                       "log")
2072
2091
    if relpaths in ([], [u'']):
2073
2092
        return b, [], start_rev_info, end_rev_info
2074
2093
    if start_rev_info is None and end_rev_info is None:
2131
2150
def _get_kind_for_file_id(tree, path, file_id):
2132
2151
    """Return the kind of a file-id or None if it doesn't exist."""
2133
2152
    if file_id is not None:
2134
 
        return tree.kind(path, file_id)
 
2153
        return tree.kind(path)
2135
2154
    else:
2136
2155
        return None
2137
2156
 
2139
2158
properties_handler_registry = registry.Registry()
2140
2159
 
2141
2160
# Use the properties handlers to print out bug information if available
 
2161
 
 
2162
 
2142
2163
def _bugs_properties_handler(revision):
2143
 
    if 'bugs' in revision.properties:
2144
 
        bug_lines = revision.properties['bugs'].split('\n')
2145
 
        bug_rows = [line.split(' ', 1) for line in bug_lines]
2146
 
        fixed_bug_urls = [row[0] for row in bug_rows if
2147
 
                          len(row) > 1 and row[1] == 'fixed']
 
2164
    fixed_bug_urls = []
 
2165
    related_bug_urls = []
 
2166
    for bug_url, status in revision.iter_bugs():
 
2167
        if status == 'fixed':
 
2168
            fixed_bug_urls.append(bug_url)
 
2169
        elif status == 'related':
 
2170
            related_bug_urls.append(bug_url)
 
2171
    ret = {}
 
2172
    if fixed_bug_urls:
 
2173
        text = ngettext('fixes bug', 'fixes bugs', len(fixed_bug_urls))
 
2174
        ret[text] = ' '.join(fixed_bug_urls)
 
2175
    if related_bug_urls:
 
2176
        text = ngettext('related bug', 'related bugs',
 
2177
                        len(related_bug_urls))
 
2178
        ret[text] = ' '.join(related_bug_urls)
 
2179
    return ret
2148
2180
 
2149
 
        if fixed_bug_urls:
2150
 
            return {ngettext('fixes bug', 'fixes bugs', len(fixed_bug_urls)):\
2151
 
                    ' '.join(fixed_bug_urls)}
2152
 
    return {}
2153
2181
 
2154
2182
properties_handler_registry.register('bugs_properties_handler',
2155
2183
                                     _bugs_properties_handler)