~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Robert Collins
  • Date: 2007-07-04 08:08:13 UTC
  • mfrom: (2572 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2587.
  • Revision ID: robertc@robertcollins.net-20070704080813-wzebx0r88fvwj5rq
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
54
54
from itertools import izip
55
55
import re
56
56
 
 
57
from bzrlib import (
 
58
    registry,
 
59
    symbol_versioning,
 
60
    )
57
61
import bzrlib.errors as errors
 
62
from bzrlib.symbol_versioning import (
 
63
    deprecated_method,
 
64
    zero_eleven,
 
65
    zero_seventeen,
 
66
    )
58
67
from bzrlib.trace import mutter
59
 
from bzrlib.tsort import merge_sort
 
68
from bzrlib.tsort import (
 
69
    merge_sort,
 
70
    topo_sort,
 
71
    )
60
72
 
61
73
 
62
74
def find_touching_revisions(branch, file_id):
120
132
             direction='reverse',
121
133
             start_revision=None,
122
134
             end_revision=None,
123
 
             search=None):
 
135
             search=None,
 
136
             limit=None):
124
137
    """Write out human-readable log of commits to this branch.
125
138
 
126
139
    lf
142
155
 
143
156
    end_revision
144
157
        If not None, only show revisions <= end_revision
 
158
 
 
159
    search
 
160
        If not None, only show revisions with matching commit messages
 
161
 
 
162
    limit
 
163
        If not None or 0, only show limit revisions
145
164
    """
146
165
    branch.lock_read()
147
166
    try:
 
167
        if getattr(lf, 'begin_log', None):
 
168
            lf.begin_log()
 
169
 
148
170
        _show_log(branch, lf, specific_fileid, verbose, direction,
149
 
                  start_revision, end_revision, search)
 
171
                  start_revision, end_revision, search, limit)
 
172
 
 
173
        if getattr(lf, 'end_log', None):
 
174
            lf.end_log()
150
175
    finally:
151
176
        branch.unlock()
152
 
    
 
177
 
153
178
def _show_log(branch,
154
179
             lf,
155
180
             specific_fileid=None,
157
182
             direction='reverse',
158
183
             start_revision=None,
159
184
             end_revision=None,
160
 
             search=None):
 
185
             search=None,
 
186
             limit=None):
161
187
    """Worker function for show_log - see show_log."""
162
188
    from bzrlib.osutils import format_date
163
189
    from bzrlib.errors import BzrCheckError
202
228
        mainline_revs.insert(0, None)
203
229
    else:
204
230
        mainline_revs.insert(0, which_revs[start_revision-2][1])
205
 
    if getattr(lf, 'show_merge', None) is not None:
206
 
        include_merges = True 
207
 
    else:
208
 
        include_merges = False 
209
 
    view_revisions = list(get_view_revisions(mainline_revs, rev_nos, branch,
210
 
                          direction, include_merges=include_merges))
 
231
    legacy_lf = getattr(lf, 'log_revision', None) is None
 
232
    if legacy_lf:
 
233
        # pre-0.17 formatters use show for mainline revisions.
 
234
        # how should we show merged revisions ?
 
235
        #   pre-0.11 api: show_merge
 
236
        #   0.11-0.16 api: show_merge_revno
 
237
        show_merge_revno = getattr(lf, 'show_merge_revno', None)
 
238
        show_merge = getattr(lf, 'show_merge', None)
 
239
        if show_merge is None and show_merge_revno is None:
 
240
            # no merged-revno support
 
241
            generate_merge_revisions = False
 
242
        else:
 
243
            generate_merge_revisions = True
 
244
        # tell developers to update their code
 
245
        symbol_versioning.warn('LogFormatters should provide log_revision '
 
246
            'instead of show and show_merge_revno since bzr 0.17.',
 
247
            DeprecationWarning, stacklevel=3)
 
248
    else:
 
249
        generate_merge_revisions = getattr(lf, 'supports_merge_revisions', 
 
250
                                           False)
 
251
    view_revs_iter = get_view_revisions(mainline_revs, rev_nos, branch,
 
252
                          direction, include_merges=generate_merge_revisions)
 
253
    if specific_fileid:
 
254
        view_revisions = _get_revisions_touching_file_id(branch,
 
255
                                                         specific_fileid,
 
256
                                                         mainline_revs,
 
257
                                                         view_revs_iter)
 
258
    else:
 
259
        view_revisions = list(view_revs_iter)
 
260
 
 
261
    rev_tag_dict = {}
 
262
    generate_tags = getattr(lf, 'supports_tags', False)
 
263
    if generate_tags:
 
264
        if branch.supports_tags():
 
265
            rev_tag_dict = branch.tags.get_reverse_tag_dict()
 
266
 
 
267
    generate_delta = verbose and getattr(lf, 'supports_delta', False)
211
268
 
212
269
    def iter_revisions():
213
270
        # r = revision, n = revno, d = merge depth
214
271
        revision_ids = [r for r, n, d in view_revisions]
215
 
        zeros = set(r for r, n, d in view_revisions if d == 0)
216
272
        num = 9
217
273
        repository = branch.repository
218
274
        while revision_ids:
219
275
            cur_deltas = {}
220
276
            revisions = repository.get_revisions(revision_ids[:num])
221
 
            if verbose or specific_fileid:
222
 
                delta_revisions = [r for r in revisions if
223
 
                                   r.revision_id in zeros]
224
 
                deltas = repository.get_deltas_for_revisions(delta_revisions)
225
 
                cur_deltas = dict(izip((r.revision_id for r in 
226
 
                                        delta_revisions), deltas))
 
277
            if generate_delta:
 
278
                deltas = repository.get_deltas_for_revisions(revisions)
 
279
                cur_deltas = dict(izip((r.revision_id for r in revisions),
 
280
                                       deltas))
227
281
            for revision in revisions:
228
 
                # The delta value will be None unless
229
 
                # 1. verbose or specific_fileid is specified, and
230
 
                # 2. the revision is a mainline revision
231
282
                yield revision, cur_deltas.get(revision.revision_id)
232
283
            revision_ids  = revision_ids[num:]
233
 
            num = int(num * 1.5)
234
 
            
 
284
            num = min(int(num * 1.5), 200)
 
285
 
235
286
    # now we just print all the revisions
 
287
    log_count = 0
236
288
    for ((rev_id, revno, merge_depth), (rev, delta)) in \
237
289
         izip(view_revisions, iter_revisions()):
238
290
 
240
292
            if not searchRE.search(rev.message):
241
293
                continue
242
294
 
243
 
        if merge_depth == 0:
244
 
            # a mainline revision.
245
 
                
246
 
            if specific_fileid:
247
 
                if not delta.touches_file_id(specific_fileid):
248
 
                    continue
249
 
    
250
 
            if not verbose:
251
 
                # although we calculated it, throw it away without display
252
 
                delta = None
253
 
 
254
 
            lf.show(revno, rev, delta)
255
 
        else:
256
 
            lf.show_merge(rev, merge_depth)
 
295
        if not legacy_lf:
 
296
            lr = LogRevision(rev, revno, merge_depth, delta,
 
297
                             rev_tag_dict.get(rev_id))
 
298
            lf.log_revision(lr)
 
299
        else:
 
300
            # support for legacy (pre-0.17) LogFormatters
 
301
            if merge_depth == 0:
 
302
                if generate_tags:
 
303
                    lf.show(revno, rev, delta, rev_tag_dict.get(rev_id))
 
304
                else:
 
305
                    lf.show(revno, rev, delta)
 
306
            else:
 
307
                if show_merge_revno is None:
 
308
                    lf.show_merge(rev, merge_depth)
 
309
                else:
 
310
                    if generate_tags:
 
311
                        lf.show_merge_revno(rev, merge_depth, revno,
 
312
                                            rev_tag_dict.get(rev_id))
 
313
                    else:
 
314
                        lf.show_merge_revno(rev, merge_depth, revno)
 
315
        if limit:
 
316
            log_count += 1
 
317
            if log_count >= limit:
 
318
                break
 
319
 
 
320
 
 
321
def _get_revisions_touching_file_id(branch, file_id, mainline_revisions,
 
322
                                    view_revs_iter):
 
323
    """Return the list of revision ids which touch a given file id.
 
324
 
 
325
    This includes the revisions which directly change the file id,
 
326
    and the revisions which merge these changes. So if the
 
327
    revision graph is::
 
328
        A
 
329
        |\
 
330
        B C
 
331
        |/
 
332
        D
 
333
 
 
334
    And 'C' changes a file, then both C and D will be returned.
 
335
 
 
336
    This will also can be restricted based on a subset of the mainline.
 
337
 
 
338
    :return: A list of (revision_id, dotted_revno, merge_depth) tuples.
 
339
    """
 
340
    # find all the revisions that change the specific file
 
341
    file_weave = branch.repository.weave_store.get_weave(file_id,
 
342
                branch.repository.get_transaction())
 
343
    weave_modifed_revisions = set(file_weave.versions())
 
344
    # build the ancestry of each revision in the graph
 
345
    # - only listing the ancestors that change the specific file.
 
346
    rev_graph = branch.repository.get_revision_graph(mainline_revisions[-1])
 
347
    sorted_rev_list = topo_sort(rev_graph)
 
348
    ancestry = {}
 
349
    for rev in sorted_rev_list:
 
350
        parents = rev_graph[rev]
 
351
        if rev not in weave_modifed_revisions and len(parents) == 1:
 
352
            # We will not be adding anything new, so just use a reference to
 
353
            # the parent ancestry.
 
354
            rev_ancestry = ancestry[parents[0]]
 
355
        else:
 
356
            rev_ancestry = set()
 
357
            if rev in weave_modifed_revisions:
 
358
                rev_ancestry.add(rev)
 
359
            for parent in parents:
 
360
                rev_ancestry = rev_ancestry.union(ancestry[parent])
 
361
        ancestry[rev] = rev_ancestry
 
362
 
 
363
    def is_merging_rev(r):
 
364
        parents = rev_graph[r]
 
365
        if len(parents) > 1:
 
366
            leftparent = parents[0]
 
367
            for rightparent in parents[1:]:
 
368
                if not ancestry[leftparent].issuperset(
 
369
                        ancestry[rightparent]):
 
370
                    return True
 
371
        return False
 
372
 
 
373
    # filter from the view the revisions that did not change or merge 
 
374
    # the specific file
 
375
    return [(r, n, d) for r, n, d in view_revs_iter
 
376
            if r in weave_modifed_revisions or is_merging_rev(r)]
257
377
 
258
378
 
259
379
def get_view_revisions(mainline_revs, rev_nos, branch, direction,
267
387
        if direction == 'reverse':
268
388
            revision_ids.reverse()
269
389
        for revision_id in revision_ids:
270
 
            yield revision_id, rev_nos[revision_id], 0
 
390
            yield revision_id, str(rev_nos[revision_id]), 0
271
391
        return
272
392
    merge_sorted_revisions = merge_sort(
273
393
        branch.repository.get_revision_graph(mainline_revs[-1]),
274
394
        mainline_revs[-1],
275
 
        mainline_revs)
 
395
        mainline_revs,
 
396
        generate_revno=True)
276
397
 
277
398
    if direction == 'forward':
278
399
        # forward means oldest first.
280
401
    elif direction != 'reverse':
281
402
        raise ValueError('invalid direction %r' % direction)
282
403
 
283
 
    revision_history = branch.revision_history()
284
 
 
285
 
    for sequence, rev_id, merge_depth, end_of_merge in merge_sorted_revisions:
286
 
        yield rev_id, rev_nos.get(rev_id), merge_depth
 
404
    for sequence, rev_id, merge_depth, revno, end_of_merge in merge_sorted_revisions:
 
405
        yield rev_id, '.'.join(map(str, revno)), merge_depth
287
406
 
288
407
 
289
408
def reverse_by_depth(merge_sorted_revisions, _depth=0):
310
429
    return result
311
430
 
312
431
 
 
432
class LogRevision(object):
 
433
    """A revision to be logged (by LogFormatter.log_revision).
 
434
 
 
435
    A simple wrapper for the attributes of a revision to be logged.
 
436
    The attributes may or may not be populated, as determined by the 
 
437
    logging options and the log formatter capabilities.
 
438
    """
 
439
 
 
440
    def __init__(self, rev=None, revno=None, merge_depth=0, delta=None,
 
441
                 tags=None):
 
442
        self.rev = rev
 
443
        self.revno = revno
 
444
        self.merge_depth = merge_depth
 
445
        self.delta = delta
 
446
        self.tags = tags
 
447
 
 
448
 
313
449
class LogFormatter(object):
314
 
    """Abstract class to display log messages."""
 
450
    """Abstract class to display log messages.
 
451
 
 
452
    At a minimum, a derived class must implement the log_revision method.
 
453
 
 
454
    If the LogFormatter needs to be informed of the beginning or end of
 
455
    a log it should implement the begin_log and/or end_log hook methods.
 
456
 
 
457
    A LogFormatter should define the following supports_XXX flags 
 
458
    to indicate which LogRevision attributes it supports:
 
459
 
 
460
    - supports_delta must be True if this log formatter supports delta.
 
461
        Otherwise the delta attribute may not be populated.
 
462
    - supports_merge_revisions must be True if this log formatter supports 
 
463
        merge revisions.  If not, only revisions mainline revisions (those 
 
464
        with merge_depth == 0) will be passed to the formatter.
 
465
    - supports_tags must be True if this log formatter supports tags.
 
466
        Otherwise the tags attribute may not be populated.
 
467
    """
315
468
 
316
469
    def __init__(self, to_file, show_ids=False, show_timezone='original'):
317
470
        self.to_file = to_file
318
471
        self.show_ids = show_ids
319
472
        self.show_timezone = show_timezone
320
473
 
 
474
# TODO: uncomment this block after show() has been removed.
 
475
# Until then defining log_revision would prevent _show_log calling show() 
 
476
# in legacy formatters.
 
477
#    def log_revision(self, revision):
 
478
#        """Log a revision.
 
479
#
 
480
#        :param  revision:   The LogRevision to be logged.
 
481
#        """
 
482
#        raise NotImplementedError('not implemented in abstract base')
 
483
 
 
484
    @deprecated_method(zero_seventeen)
321
485
    def show(self, revno, rev, delta):
322
486
        raise NotImplementedError('not implemented in abstract base')
323
487
 
324
488
    def short_committer(self, rev):
325
489
        return re.sub('<.*@.*>', '', rev.committer).strip(' ')
326
 
    
327
 
    
 
490
 
 
491
 
328
492
class LongLogFormatter(LogFormatter):
329
 
    def show(self, revno, rev, delta):
330
 
        return self._show_helper(revno=revno, rev=rev, delta=delta)
331
 
 
 
493
 
 
494
    supports_merge_revisions = True
 
495
    supports_delta = True
 
496
    supports_tags = True
 
497
 
 
498
    @deprecated_method(zero_seventeen)
 
499
    def show(self, revno, rev, delta, tags=None):
 
500
        lr = LogRevision(rev, revno, 0, delta, tags)
 
501
        return self.log_revision(lr)
 
502
 
 
503
    @deprecated_method(zero_eleven)
332
504
    def show_merge(self, rev, merge_depth):
333
 
        return self._show_helper(rev=rev, indent='    '*merge_depth, merged=True, delta=None)
334
 
 
335
 
    def _show_helper(self, rev=None, revno=None, indent='', merged=False, delta=None):
336
 
        """Show a revision, either merged or not."""
 
505
        lr = LogRevision(rev, merge_depth=merge_depth)
 
506
        return self.log_revision(lr)
 
507
 
 
508
    @deprecated_method(zero_seventeen)
 
509
    def show_merge_revno(self, rev, merge_depth, revno, tags=None):
 
510
        """Show a merged revision rev, with merge_depth and a revno."""
 
511
        lr = LogRevision(rev, revno, merge_depth, tags=tags)
 
512
        return self.log_revision(lr)
 
513
 
 
514
    def log_revision(self, revision):
 
515
        """Log a revision, either merged or not."""
337
516
        from bzrlib.osutils import format_date
 
517
        indent = '    '*revision.merge_depth
338
518
        to_file = self.to_file
339
519
        print >>to_file,  indent+'-' * 60
340
 
        if revno is not None:
341
 
            print >>to_file,  'revno:', revno
342
 
        if merged:
343
 
            print >>to_file,  indent+'merged:', rev.revision_id
344
 
        elif self.show_ids:
345
 
            print >>to_file,  indent+'revision-id:', rev.revision_id
 
520
        if revision.revno is not None:
 
521
            print >>to_file,  indent+'revno:', revision.revno
 
522
        if revision.tags:
 
523
            print >>to_file, indent+'tags: %s' % (', '.join(revision.tags))
346
524
        if self.show_ids:
347
 
            for parent_id in rev.parent_ids:
 
525
            print >>to_file, indent+'revision-id:', revision.rev.revision_id
 
526
            for parent_id in revision.rev.parent_ids:
348
527
                print >>to_file, indent+'parent:', parent_id
349
 
        print >>to_file,  indent+'committer:', rev.committer
 
528
        print >>to_file, indent+'committer:', revision.rev.committer
 
529
 
350
530
        try:
351
531
            print >>to_file, indent+'branch nick: %s' % \
352
 
                rev.properties['branch-nick']
 
532
                revision.rev.properties['branch-nick']
353
533
        except KeyError:
354
534
            pass
355
 
        date_str = format_date(rev.timestamp,
356
 
                               rev.timezone or 0,
 
535
        date_str = format_date(revision.rev.timestamp,
 
536
                               revision.rev.timezone or 0,
357
537
                               self.show_timezone)
358
538
        print >>to_file,  indent+'timestamp: %s' % date_str
359
539
 
360
540
        print >>to_file,  indent+'message:'
361
 
        if not rev.message:
 
541
        if not revision.rev.message:
362
542
            print >>to_file,  indent+'  (no message)'
363
543
        else:
364
 
            message = rev.message.rstrip('\r\n')
 
544
            message = revision.rev.message.rstrip('\r\n')
365
545
            for l in message.split('\n'):
366
546
                print >>to_file,  indent+'  ' + l
367
 
        if delta is not None:
368
 
            delta.show(to_file, self.show_ids)
 
547
        if revision.delta is not None:
 
548
            revision.delta.show(to_file, self.show_ids, indent=indent)
369
549
 
370
550
 
371
551
class ShortLogFormatter(LogFormatter):
 
552
 
 
553
    supports_delta = True
 
554
 
 
555
    @deprecated_method(zero_seventeen)
372
556
    def show(self, revno, rev, delta):
 
557
        lr = LogRevision(rev, revno, 0, delta)
 
558
        return self.log_revision(lr)
 
559
 
 
560
    def log_revision(self, revision):
373
561
        from bzrlib.osutils import format_date
374
562
 
375
563
        to_file = self.to_file
376
 
        date_str = format_date(rev.timestamp, rev.timezone or 0,
377
 
                            self.show_timezone)
378
 
        print >>to_file, "%5d %s\t%s" % (revno, self.short_committer(rev),
379
 
                format_date(rev.timestamp, rev.timezone or 0,
 
564
        date_str = format_date(revision.rev.timestamp,
 
565
                               revision.rev.timezone or 0,
 
566
                               self.show_timezone)
 
567
        is_merge = ''
 
568
        if len(revision.rev.parent_ids) > 1:
 
569
            is_merge = ' [merge]'
 
570
        print >>to_file, "%5s %s\t%s%s" % (revision.revno,
 
571
                self.short_committer(revision.rev),
 
572
                format_date(revision.rev.timestamp,
 
573
                            revision.rev.timezone or 0,
380
574
                            self.show_timezone, date_fmt="%Y-%m-%d",
381
 
                           show_offset=False))
 
575
                            show_offset=False),
 
576
                is_merge)
382
577
        if self.show_ids:
383
 
            print >>to_file,  '      revision-id:', rev.revision_id
384
 
        if not rev.message:
 
578
            print >>to_file,  '      revision-id:', revision.rev.revision_id
 
579
        if not revision.rev.message:
385
580
            print >>to_file,  '      (no message)'
386
581
        else:
387
 
            message = rev.message.rstrip('\r\n')
 
582
            message = revision.rev.message.rstrip('\r\n')
388
583
            for l in message.split('\n'):
389
584
                print >>to_file,  '      ' + l
390
585
 
391
586
        # TODO: Why not show the modified files in a shorter form as
392
587
        # well? rewrap them single lines of appropriate length
393
 
        if delta is not None:
394
 
            delta.show(to_file, self.show_ids)
 
588
        if revision.delta is not None:
 
589
            revision.delta.show(to_file, self.show_ids)
395
590
        print >>to_file, ''
396
591
 
397
592
 
398
593
class LineLogFormatter(LogFormatter):
 
594
 
 
595
    def __init__(self, *args, **kwargs):
 
596
        from bzrlib.osutils import terminal_width
 
597
        super(LineLogFormatter, self).__init__(*args, **kwargs)
 
598
        self._max_chars = terminal_width() - 1
 
599
 
399
600
    def truncate(self, str, max_len):
400
601
        if len(str) <= max_len:
401
602
            return str
413
614
        else:
414
615
            return rev.message
415
616
 
 
617
    @deprecated_method(zero_seventeen)
416
618
    def show(self, revno, rev, delta):
417
619
        from bzrlib.osutils import terminal_width
418
620
        print >> self.to_file, self.log_string(revno, rev, terminal_width()-1)
419
621
 
 
622
    def log_revision(self, revision):
 
623
        print >>self.to_file, self.log_string(revision.revno, revision.rev,
 
624
                                              self._max_chars)
 
625
 
420
626
    def log_string(self, revno, rev, max_chars):
421
627
        """Format log info into one string. Truncate tail of string
422
628
        :param  revno:      revision number (int) or None.
428
634
        out = []
429
635
        if revno:
430
636
            # show revno only when is not None
431
 
            out.append("%d:" % revno)
 
637
            out.append("%s:" % revno)
432
638
        out.append(self.truncate(self.short_committer(rev), 20))
433
639
        out.append(self.date_string(rev))
434
640
        out.append(rev.get_summary())
439
645
    lf = LineLogFormatter(None)
440
646
    return lf.log_string(None, rev, max_chars)
441
647
 
442
 
FORMATTERS = {
443
 
              'long': LongLogFormatter,
444
 
              'short': ShortLogFormatter,
445
 
              'line': LineLogFormatter,
446
 
              }
 
648
 
 
649
class LogFormatterRegistry(registry.Registry):
 
650
    """Registry for log formatters"""
 
651
 
 
652
    def make_formatter(self, name, *args, **kwargs):
 
653
        """Construct a formatter from arguments.
 
654
 
 
655
        :param name: Name of the formatter to construct.  'short', 'long' and
 
656
            'line' are built-in.
 
657
        """
 
658
        return self.get(name)(*args, **kwargs)
 
659
 
 
660
    def get_default(self, branch):
 
661
        return self.get(branch.get_config().log_format())
 
662
 
 
663
 
 
664
log_formatter_registry = LogFormatterRegistry()
 
665
 
 
666
 
 
667
log_formatter_registry.register('short', ShortLogFormatter,
 
668
                                'Moderately short log format')
 
669
log_formatter_registry.register('long', LongLogFormatter,
 
670
                                'Detailed log format')
 
671
log_formatter_registry.register('line', LineLogFormatter,
 
672
                                'Log format with one line per revision')
 
673
 
447
674
 
448
675
def register_formatter(name, formatter):
449
 
    FORMATTERS[name] = formatter
 
676
    log_formatter_registry.register(name, formatter)
 
677
 
450
678
 
451
679
def log_formatter(name, *args, **kwargs):
452
680
    """Construct a formatter from arguments.
456
684
    """
457
685
    from bzrlib.errors import BzrCommandError
458
686
    try:
459
 
        return FORMATTERS[name](*args, **kwargs)
 
687
        return log_formatter_registry.make_formatter(name, *args, **kwargs)
460
688
    except KeyError:
461
689
        raise BzrCommandError("unknown log formatter: %r" % name)
462
690
 
 
691
 
463
692
def show_one_log(revno, rev, delta, verbose, to_file, show_timezone):
464
693
    # deprecated; for compatibility
465
694
    lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
466
695
    lf.show(revno, rev, delta)
467
696
 
 
697
 
468
698
def show_changed_revisions(branch, old_rh, new_rh, to_file=None, log_format='long'):
469
699
    """Show the change in revision history comparing the old revision history to the new one.
470
700
 
505
735
        to_file.write('\nRemoved Revisions:\n')
506
736
        for i in range(base_idx, len(old_rh)):
507
737
            rev = branch.repository.get_revision(old_rh[i])
508
 
            lf.show(i+1, rev, None)
 
738
            lr = LogRevision(rev, i+1, 0, None)
 
739
            lf.log_revision(lr)
509
740
        to_file.write('*'*60)
510
741
        to_file.write('\n\n')
511
742
    if base_idx < len(new_rh):