~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Martin Pool
  • Date: 2007-06-15 07:01:24 UTC
  • mfrom: (2528 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2530.
  • Revision ID: mbp@sourcefrog.net-20070615070124-clpwqh5gxc4wbf9l
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
54
54
from itertools import izip
55
55
import re
56
56
 
57
 
from bzrlib import(
 
57
from bzrlib import (
58
58
    registry,
59
59
    symbol_versioning,
60
60
    )
61
61
import bzrlib.errors as errors
62
 
from bzrlib.symbol_versioning import deprecated_method, zero_eleven
 
62
from bzrlib.symbol_versioning import (
 
63
    deprecated_method,
 
64
    zero_eleven,
 
65
    zero_seventeen,
 
66
    )
63
67
from bzrlib.trace import mutter
64
 
from bzrlib.tsort import(
 
68
from bzrlib.tsort import (
65
69
    merge_sort,
66
70
    topo_sort,
67
71
    )
128
132
             direction='reverse',
129
133
             start_revision=None,
130
134
             end_revision=None,
131
 
             search=None):
 
135
             search=None,
 
136
             limit=None):
132
137
    """Write out human-readable log of commits to this branch.
133
138
 
134
139
    lf
150
155
 
151
156
    end_revision
152
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
153
164
    """
154
165
    branch.lock_read()
155
166
    try:
 
167
        if getattr(lf, 'begin_log', None):
 
168
            lf.begin_log()
 
169
 
156
170
        _show_log(branch, lf, specific_fileid, verbose, direction,
157
 
                  start_revision, end_revision, search)
 
171
                  start_revision, end_revision, search, limit)
 
172
 
 
173
        if getattr(lf, 'end_log', None):
 
174
            lf.end_log()
158
175
    finally:
159
176
        branch.unlock()
160
 
    
 
177
 
161
178
def _show_log(branch,
162
179
             lf,
163
180
             specific_fileid=None,
165
182
             direction='reverse',
166
183
             start_revision=None,
167
184
             end_revision=None,
168
 
             search=None):
 
185
             search=None,
 
186
             limit=None):
169
187
    """Worker function for show_log - see show_log."""
170
188
    from bzrlib.osutils import format_date
171
189
    from bzrlib.errors import BzrCheckError
210
228
        mainline_revs.insert(0, None)
211
229
    else:
212
230
        mainline_revs.insert(0, which_revs[start_revision-2][1])
213
 
    # how should we show merged revisions ?
214
 
    # old api: show_merge. New api: show_merge_revno
215
 
    show_merge_revno = getattr(lf, 'show_merge_revno', None)
216
 
    show_merge = getattr(lf, 'show_merge', None)
217
 
    if show_merge is None and show_merge_revno is None:
218
 
        # no merged-revno support
219
 
        include_merges = False
220
 
    else:
221
 
        include_merges = True
222
 
    if show_merge is not None and show_merge_revno is None:
 
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
223
244
        # tell developers to update their code
224
 
        symbol_versioning.warn('LogFormatters should provide show_merge_revno '
225
 
            'instead of show_merge since bzr 0.11.',
 
245
        symbol_versioning.warn('LogFormatters should provide log_revision '
 
246
            'instead of show and show_merge_revno since bzr 0.17.',
226
247
            DeprecationWarning, stacklevel=3)
 
248
    else:
 
249
        generate_merge_revisions = getattr(lf, 'supports_merge_revisions', 
 
250
                                           False)
227
251
    view_revs_iter = get_view_revisions(mainline_revs, rev_nos, branch,
228
 
                          direction, include_merges=include_merges)
 
252
                          direction, include_merges=generate_merge_revisions)
229
253
    if specific_fileid:
230
254
        view_revisions = _get_revisions_touching_file_id(branch,
231
255
                                                         specific_fileid,
234
258
    else:
235
259
        view_revisions = list(view_revs_iter)
236
260
 
237
 
    use_tags = getattr(lf, 'supports_tags', False)
238
 
    if use_tags:
239
 
        rev_tag_dict = {}
 
261
    rev_tag_dict = {}
 
262
    generate_tags = getattr(lf, 'supports_tags', False)
 
263
    if generate_tags:
240
264
        if branch.supports_tags():
241
265
            rev_tag_dict = branch.tags.get_reverse_tag_dict()
242
266
 
 
267
    generate_delta = verbose and getattr(lf, 'supports_delta', False)
 
268
 
243
269
    def iter_revisions():
244
270
        # r = revision, n = revno, d = merge depth
245
271
        revision_ids = [r for r, n, d in view_revisions]
249
275
        while revision_ids:
250
276
            cur_deltas = {}
251
277
            revisions = repository.get_revisions(revision_ids[:num])
252
 
            if verbose:
 
278
            if generate_delta:
253
279
                delta_revisions = [r for r in revisions if
254
280
                                   r.revision_id in zeros]
255
281
                deltas = repository.get_deltas_for_revisions(delta_revisions)
262
288
                yield revision, cur_deltas.get(revision.revision_id)
263
289
            revision_ids  = revision_ids[num:]
264
290
            num = min(int(num * 1.5), 200)
265
 
            
 
291
 
266
292
    # now we just print all the revisions
 
293
    log_count = 0
267
294
    for ((rev_id, revno, merge_depth), (rev, delta)) in \
268
295
         izip(view_revisions, iter_revisions()):
269
296
 
271
298
            if not searchRE.search(rev.message):
272
299
                continue
273
300
 
274
 
        if merge_depth == 0:
275
 
            if use_tags:
276
 
                lf.show(revno, rev, delta, rev_tag_dict.get(rev_id))
277
 
            else:
278
 
                lf.show(revno, rev, delta)
 
301
        if not legacy_lf:
 
302
            lr = LogRevision(rev, revno, merge_depth, delta,
 
303
                             rev_tag_dict.get(rev_id))
 
304
            lf.log_revision(lr)
279
305
        else:
280
 
            if show_merge_revno is None:
281
 
                lf.show_merge(rev, merge_depth)
 
306
            # support for legacy (pre-0.17) LogFormatters
 
307
            if merge_depth == 0:
 
308
                if generate_tags:
 
309
                    lf.show(revno, rev, delta, rev_tag_dict.get(rev_id))
 
310
                else:
 
311
                    lf.show(revno, rev, delta)
282
312
            else:
283
 
                if use_tags:
284
 
                    lf.show_merge_revno(rev, merge_depth, revno,
285
 
                                        rev_tag_dict.get(rev_id))
 
313
                if show_merge_revno is None:
 
314
                    lf.show_merge(rev, merge_depth)
286
315
                else:
287
 
                    lf.show_merge_revno(rev, merge_depth, revno)
 
316
                    if generate_tags:
 
317
                        lf.show_merge_revno(rev, merge_depth, revno,
 
318
                                            rev_tag_dict.get(rev_id))
 
319
                    else:
 
320
                        lf.show_merge_revno(rev, merge_depth, revno)
 
321
        if limit:
 
322
            log_count += 1
 
323
            if log_count >= limit:
 
324
                break
288
325
 
289
326
 
290
327
def _get_revisions_touching_file_id(branch, file_id, mainline_revisions,
398
435
    return result
399
436
 
400
437
 
 
438
class LogRevision(object):
 
439
    """A revision to be logged (by LogFormatter.log_revision).
 
440
 
 
441
    A simple wrapper for the attributes of a revision to be logged.
 
442
    The attributes may or may not be populated, as determined by the 
 
443
    logging options and the log formatter capabilities.
 
444
    """
 
445
 
 
446
    def __init__(self, rev=None, revno=None, merge_depth=0, delta=None,
 
447
                 tags=None):
 
448
        self.rev = rev
 
449
        self.revno = revno
 
450
        self.merge_depth = merge_depth
 
451
        self.delta = delta
 
452
        self.tags = tags
 
453
 
 
454
 
401
455
class LogFormatter(object):
402
 
    """Abstract class to display log messages."""
 
456
    """Abstract class to display log messages.
 
457
 
 
458
    At a minimum, a derived class must implement the log_revision method.
 
459
 
 
460
    If the LogFormatter needs to be informed of the beginning or end of
 
461
    a log it should implement the begin_log and/or end_log hook methods.
 
462
 
 
463
    A LogFormatter should define the following supports_XXX flags 
 
464
    to indicate which LogRevision attributes it supports:
 
465
 
 
466
    - supports_delta must be True if this log formatter supports delta.
 
467
        Otherwise the delta attribute may not be populated.
 
468
    - supports_merge_revisions must be True if this log formatter supports 
 
469
        merge revisions.  If not, only revisions mainline revisions (those 
 
470
        with merge_depth == 0) will be passed to the formatter.
 
471
    - supports_tags must be True if this log formatter supports tags.
 
472
        Otherwise the tags attribute may not be populated.
 
473
    """
403
474
 
404
475
    def __init__(self, to_file, show_ids=False, show_timezone='original'):
405
476
        self.to_file = to_file
406
477
        self.show_ids = show_ids
407
478
        self.show_timezone = show_timezone
408
479
 
 
480
# TODO: uncomment this block after show() has been removed.
 
481
# Until then defining log_revision would prevent _show_log calling show() 
 
482
# in legacy formatters.
 
483
#    def log_revision(self, revision):
 
484
#        """Log a revision.
 
485
#
 
486
#        :param  revision:   The LogRevision to be logged.
 
487
#        """
 
488
#        raise NotImplementedError('not implemented in abstract base')
 
489
 
 
490
    @deprecated_method(zero_seventeen)
409
491
    def show(self, revno, rev, delta):
410
492
        raise NotImplementedError('not implemented in abstract base')
411
493
 
415
497
 
416
498
class LongLogFormatter(LogFormatter):
417
499
 
418
 
    supports_tags = True    # must exist and be True
419
 
                            # if this log formatter support tags.
420
 
                            # .show() and .show_merge_revno() must then accept
421
 
                            # the 'tags'-argument with list of tags
 
500
    supports_merge_revisions = True
 
501
    supports_delta = True
 
502
    supports_tags = True
422
503
 
 
504
    @deprecated_method(zero_seventeen)
423
505
    def show(self, revno, rev, delta, tags=None):
424
 
        return self._show_helper(revno=revno, rev=rev, delta=delta, tags=tags)
 
506
        lr = LogRevision(rev, revno, 0, delta, tags)
 
507
        return self.log_revision(lr)
425
508
 
426
509
    @deprecated_method(zero_eleven)
427
510
    def show_merge(self, rev, merge_depth):
428
 
        return self._show_helper(rev=rev, indent='    '*merge_depth,
429
 
                                 merged=True, delta=None)
 
511
        lr = LogRevision(rev, merge_depth=merge_depth)
 
512
        return self.log_revision(lr)
430
513
 
 
514
    @deprecated_method(zero_seventeen)
431
515
    def show_merge_revno(self, rev, merge_depth, revno, tags=None):
432
516
        """Show a merged revision rev, with merge_depth and a revno."""
433
 
        return self._show_helper(rev=rev, revno=revno,
434
 
            indent='    '*merge_depth, merged=True, delta=None, tags=tags)
 
517
        lr = LogRevision(rev, revno, merge_depth, tags=tags)
 
518
        return self.log_revision(lr)
435
519
 
436
 
    def _show_helper(self, rev=None, revno=None, indent='', merged=False,
437
 
                     delta=None, tags=None):
438
 
        """Show a revision, either merged or not."""
 
520
    def log_revision(self, revision):
 
521
        """Log a revision, either merged or not."""
439
522
        from bzrlib.osutils import format_date
 
523
        indent = '    '*revision.merge_depth
440
524
        to_file = self.to_file
441
525
        print >>to_file,  indent+'-' * 60
442
 
        if revno is not None:
443
 
            print >>to_file,  indent+'revno:', revno
444
 
        if tags:
445
 
            print >>to_file, indent+'tags: %s' % (', '.join(tags))
446
 
        if merged:
447
 
            print >>to_file,  indent+'merged:', rev.revision_id
448
 
        elif self.show_ids:
449
 
            print >>to_file,  indent+'revision-id:', rev.revision_id
 
526
        if revision.revno is not None:
 
527
            print >>to_file,  indent+'revno:', revision.revno
 
528
        if revision.tags:
 
529
            print >>to_file, indent+'tags: %s' % (', '.join(revision.tags))
450
530
        if self.show_ids:
451
 
            for parent_id in rev.parent_ids:
 
531
            print >>to_file, indent+'revision-id:', revision.rev.revision_id
 
532
            for parent_id in revision.rev.parent_ids:
452
533
                print >>to_file, indent+'parent:', parent_id
453
 
        print >>to_file,  indent+'committer:', rev.committer
 
534
        print >>to_file, indent+'committer:', revision.rev.committer
454
535
 
455
536
        try:
456
537
            print >>to_file, indent+'branch nick: %s' % \
457
 
                rev.properties['branch-nick']
 
538
                revision.rev.properties['branch-nick']
458
539
        except KeyError:
459
540
            pass
460
 
        date_str = format_date(rev.timestamp,
461
 
                               rev.timezone or 0,
 
541
        date_str = format_date(revision.rev.timestamp,
 
542
                               revision.rev.timezone or 0,
462
543
                               self.show_timezone)
463
544
        print >>to_file,  indent+'timestamp: %s' % date_str
464
545
 
465
546
        print >>to_file,  indent+'message:'
466
 
        if not rev.message:
 
547
        if not revision.rev.message:
467
548
            print >>to_file,  indent+'  (no message)'
468
549
        else:
469
 
            message = rev.message.rstrip('\r\n')
 
550
            message = revision.rev.message.rstrip('\r\n')
470
551
            for l in message.split('\n'):
471
552
                print >>to_file,  indent+'  ' + l
472
 
        if delta is not None:
473
 
            delta.show(to_file, self.show_ids)
 
553
        if revision.delta is not None:
 
554
            revision.delta.show(to_file, self.show_ids)
474
555
 
475
556
 
476
557
class ShortLogFormatter(LogFormatter):
 
558
 
 
559
    supports_delta = True
 
560
 
 
561
    @deprecated_method(zero_seventeen)
477
562
    def show(self, revno, rev, delta):
 
563
        lr = LogRevision(rev, revno, 0, delta)
 
564
        return self.log_revision(lr)
 
565
 
 
566
    def log_revision(self, revision):
478
567
        from bzrlib.osutils import format_date
479
568
 
480
569
        to_file = self.to_file
481
 
        date_str = format_date(rev.timestamp, rev.timezone or 0,
482
 
                            self.show_timezone)
483
 
        print >>to_file, "%5s %s\t%s" % (revno, self.short_committer(rev),
484
 
                format_date(rev.timestamp, rev.timezone or 0,
 
570
        date_str = format_date(revision.rev.timestamp,
 
571
                               revision.rev.timezone or 0,
 
572
                               self.show_timezone)
 
573
        is_merge = ''
 
574
        if len(revision.rev.parent_ids) > 1:
 
575
            is_merge = ' [merge]'
 
576
        print >>to_file, "%5s %s\t%s%s" % (revision.revno,
 
577
                self.short_committer(revision.rev),
 
578
                format_date(revision.rev.timestamp,
 
579
                            revision.rev.timezone or 0,
485
580
                            self.show_timezone, date_fmt="%Y-%m-%d",
486
 
                           show_offset=False))
 
581
                            show_offset=False),
 
582
                is_merge)
487
583
        if self.show_ids:
488
 
            print >>to_file,  '      revision-id:', rev.revision_id
489
 
        if not rev.message:
 
584
            print >>to_file,  '      revision-id:', revision.rev.revision_id
 
585
        if not revision.rev.message:
490
586
            print >>to_file,  '      (no message)'
491
587
        else:
492
 
            message = rev.message.rstrip('\r\n')
 
588
            message = revision.rev.message.rstrip('\r\n')
493
589
            for l in message.split('\n'):
494
590
                print >>to_file,  '      ' + l
495
591
 
496
592
        # TODO: Why not show the modified files in a shorter form as
497
593
        # well? rewrap them single lines of appropriate length
498
 
        if delta is not None:
499
 
            delta.show(to_file, self.show_ids)
 
594
        if revision.delta is not None:
 
595
            revision.delta.show(to_file, self.show_ids)
500
596
        print >>to_file, ''
501
597
 
502
598
 
503
599
class LineLogFormatter(LogFormatter):
 
600
 
 
601
    def __init__(self, *args, **kwargs):
 
602
        from bzrlib.osutils import terminal_width
 
603
        super(LineLogFormatter, self).__init__(*args, **kwargs)
 
604
        self._max_chars = terminal_width() - 1
 
605
 
504
606
    def truncate(self, str, max_len):
505
607
        if len(str) <= max_len:
506
608
            return str
518
620
        else:
519
621
            return rev.message
520
622
 
 
623
    @deprecated_method(zero_seventeen)
521
624
    def show(self, revno, rev, delta):
522
625
        from bzrlib.osutils import terminal_width
523
626
        print >> self.to_file, self.log_string(revno, rev, terminal_width()-1)
524
627
 
 
628
    def log_revision(self, revision):
 
629
        print >>self.to_file, self.log_string(revision.revno, revision.rev,
 
630
                                              self._max_chars)
 
631
 
525
632
    def log_string(self, revno, rev, max_chars):
526
633
        """Format log info into one string. Truncate tail of string
527
634
        :param  revno:      revision number (int) or None.
593
700
    lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
594
701
    lf.show(revno, rev, delta)
595
702
 
 
703
 
596
704
def show_changed_revisions(branch, old_rh, new_rh, to_file=None, log_format='long'):
597
705
    """Show the change in revision history comparing the old revision history to the new one.
598
706
 
633
741
        to_file.write('\nRemoved Revisions:\n')
634
742
        for i in range(base_idx, len(old_rh)):
635
743
            rev = branch.repository.get_revision(old_rh[i])
636
 
            lf.show(i+1, rev, None)
 
744
            lr = LogRevision(rev, i+1, 0, None)
 
745
            lf.log_revision(lr)
637
746
        to_file.write('*'*60)
638
747
        to_file.write('\n\n')
639
748
    if base_idx < len(new_rh):