~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Robert Collins
  • Date: 2005-12-24 02:20:45 UTC
  • mto: (1185.50.57 bzr-jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1550.
  • Revision ID: robertc@robertcollins.net-20051224022045-14efc8dfa0e1a4e9
Start tests for api usage.

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
"""
51
51
 
52
52
 
 
53
# TODO: option to show delta summaries for merged-in revisions
 
54
 
 
55
import bzrlib.errors as errors
53
56
from bzrlib.tree import EmptyTree
54
57
from bzrlib.delta import compare_trees
55
58
from bzrlib.trace import mutter
56
 
from bzrlib.errors import InvalidRevisionNumber
 
59
import re
57
60
 
58
61
 
59
62
def find_touching_revisions(branch, file_id):
110
113
    return rh
111
114
 
112
115
 
 
116
def _get_revision_delta(branch, revno):
 
117
    """Return the delta for a mainline revision.
 
118
    
 
119
    This is used to show summaries in verbose logs, and also for finding 
 
120
    revisions which touch a given file."""
 
121
    # XXX: What are we supposed to do when showing a summary for something 
 
122
    # other than a mainline revision.  The delta to it's first parent, or
 
123
    # (more useful) the delta to a nominated other revision.
 
124
    return branch.get_revision_delta(revno)
 
125
 
 
126
 
113
127
def show_log(branch,
114
128
             lf,
115
129
             specific_fileid=None,
140
154
    end_revision
141
155
        If not None, only show revisions <= end_revision
142
156
    """
 
157
    branch.lock_read()
 
158
    try:
 
159
        _show_log(branch, lf, specific_fileid, verbose, direction,
 
160
                  start_revision, end_revision, search)
 
161
    finally:
 
162
        branch.unlock()
 
163
    
 
164
def _show_log(branch,
 
165
             lf,
 
166
             specific_fileid=None,
 
167
             verbose=False,
 
168
             direction='reverse',
 
169
             start_revision=None,
 
170
             end_revision=None,
 
171
             search=None):
 
172
    """Worker function for show_log - see show_log."""
143
173
    from bzrlib.osutils import format_date
144
174
    from bzrlib.errors import BzrCheckError
145
175
    from bzrlib.textui import show_status
150
180
        warn("not a LogFormatter instance: %r" % lf)
151
181
 
152
182
    if specific_fileid:
153
 
        mutter('get log for file_id %r' % specific_fileid)
 
183
        mutter('get log for file_id %r', specific_fileid)
154
184
 
155
185
    if search is not None:
156
186
        import re
162
192
    
163
193
    if start_revision is None:
164
194
        start_revision = 1
165
 
    elif start_revision < 1 or start_revision >= len(which_revs):
166
 
        raise InvalidRevisionNumber(start_revision)
 
195
    else:
 
196
        branch.check_real_revno(start_revision)
167
197
    
168
198
    if end_revision is None:
169
199
        end_revision = len(which_revs)
170
 
    elif end_revision < 1 or end_revision >= len(which_revs):
171
 
        raise InvalidRevisionNumber(end_revision)
 
200
    else:
 
201
        branch.check_real_revno(end_revision)
172
202
 
173
203
    # list indexes are 0-based; revisions are 1-based
174
204
    cut_revs = which_revs[(start_revision-1):(end_revision)]
180
210
    else:
181
211
        raise ValueError('invalid direction %r' % direction)
182
212
 
 
213
    revision_history = branch.revision_history()
183
214
    for revno, rev_id in cut_revs:
184
215
        if verbose or specific_fileid:
185
 
            delta = branch.get_revision_delta(revno)
 
216
            delta = _get_revision_delta(branch, revno)
186
217
            
187
218
        if specific_fileid:
188
219
            if not delta.touches_file_id(specific_fileid):
199
230
                continue
200
231
 
201
232
        lf.show(revno, rev, delta)
202
 
 
 
233
        if hasattr(lf, 'show_merge'):
 
234
            if revno == 1:
 
235
                excludes = set()
 
236
            else:
 
237
                # revno is 1 based, so -2 to get back 1 less.
 
238
                excludes = set(branch.get_ancestry(revision_history[revno - 2]))
 
239
            pending = list(rev.parent_ids)
 
240
            while pending:
 
241
                rev_id = pending.pop()
 
242
                if rev_id in excludes:
 
243
                    continue
 
244
                # prevent showing merged revs twice if they multi-path.
 
245
                excludes.add(rev_id)
 
246
                try:
 
247
                    rev = branch.get_revision(rev_id)
 
248
                except errors.NoSuchRevision:
 
249
                    continue
 
250
                pending.extend(rev.parent_ids)
 
251
                lf.show_merge(rev)
203
252
 
204
253
 
205
254
def deltas_for_log_dummy(branch, which_revs):
294
343
 
295
344
    def show(self, revno, rev, delta):
296
345
        raise NotImplementedError('not implemented in abstract base')
297
 
        
298
 
 
299
 
 
300
 
 
301
 
 
302
 
 
 
346
 
 
347
    def short_committer(self, rev):
 
348
        return re.sub('<.*@.*>', '', rev.committer).strip(' ')
 
349
    
 
350
    
303
351
class LongLogFormatter(LogFormatter):
304
352
    def show(self, revno, rev, delta):
305
 
        from osutils import format_date
306
 
 
 
353
        return self._show_helper(revno=revno, rev=rev, delta=delta)
 
354
 
 
355
    def show_merge(self, rev):
 
356
        return self._show_helper(rev=rev, indent='    ', merged=True, delta=None)
 
357
 
 
358
    def _show_helper(self, rev=None, revno=None, indent='', merged=False, delta=None):
 
359
        """Show a revision, either merged or not."""
 
360
        from bzrlib.osutils import format_date
307
361
        to_file = self.to_file
308
 
 
309
 
        print >>to_file,  '-' * 60
310
 
        print >>to_file,  'revno:', revno
 
362
        print >>to_file,  indent+'-' * 60
 
363
        if revno is not None:
 
364
            print >>to_file,  'revno:', revno
 
365
        if merged:
 
366
            print >>to_file,  indent+'merged:', rev.revision_id
 
367
        elif self.show_ids:
 
368
            print >>to_file,  indent+'revision-id:', rev.revision_id
311
369
        if self.show_ids:
312
 
            print >>to_file,  'revision-id:', rev.revision_id
313
 
 
314
 
            for parent in rev.parents:
315
 
                print >>to_file, 'parent:', parent.revision_id
316
 
            
317
 
        print >>to_file,  'committer:', rev.committer
318
 
 
 
370
            for parent_id in rev.parent_ids:
 
371
                print >>to_file, indent+'parent:', parent_id
 
372
        print >>to_file,  indent+'committer:', rev.committer
 
373
        try:
 
374
            print >>to_file, indent+'branch nick: %s' % \
 
375
                rev.properties['branch-nick']
 
376
        except KeyError:
 
377
            pass
319
378
        date_str = format_date(rev.timestamp,
320
379
                               rev.timezone or 0,
321
380
                               self.show_timezone)
322
 
        print >>to_file,  'timestamp: %s' % date_str
 
381
        print >>to_file,  indent+'timestamp: %s' % date_str
323
382
 
324
 
        print >>to_file,  'message:'
 
383
        print >>to_file,  indent+'message:'
325
384
        if not rev.message:
326
 
            print >>to_file,  '  (no message)'
 
385
            print >>to_file,  indent+'  (no message)'
327
386
        else:
328
 
            for l in rev.message.split('\n'):
329
 
                print >>to_file,  '  ' + l
330
 
 
 
387
            message = rev.message.rstrip('\r\n')
 
388
            for l in message.split('\n'):
 
389
                print >>to_file,  indent+'  ' + l
331
390
        if delta != None:
332
391
            delta.show(to_file, self.show_ids)
333
392
 
334
393
 
335
 
 
336
394
class ShortLogFormatter(LogFormatter):
337
395
    def show(self, revno, rev, delta):
338
396
        from bzrlib.osutils import format_date
339
397
 
340
398
        to_file = self.to_file
341
 
 
342
 
        print >>to_file, "%5d %s\t%s" % (revno, rev.committer,
 
399
        date_str = format_date(rev.timestamp, rev.timezone or 0,
 
400
                            self.show_timezone)
 
401
        print >>to_file, "%5d %s\t%s" % (revno, self.short_committer(rev),
343
402
                format_date(rev.timestamp, rev.timezone or 0,
344
 
                            self.show_timezone))
 
403
                            self.show_timezone, date_fmt="%Y-%m-%d",
 
404
                           show_offset=False))
345
405
        if self.show_ids:
346
406
            print >>to_file,  '      revision-id:', rev.revision_id
347
407
        if not rev.message:
348
408
            print >>to_file,  '      (no message)'
349
409
        else:
350
 
            for l in rev.message.split('\n'):
 
410
            message = rev.message.rstrip('\r\n')
 
411
            for l in message.split('\n'):
351
412
                print >>to_file,  '      ' + l
352
413
 
353
414
        # TODO: Why not show the modified files in a shorter form as
354
415
        # well? rewrap them single lines of appropriate length
355
416
        if delta != None:
356
417
            delta.show(to_file, self.show_ids)
357
 
        print
358
 
 
359
 
 
 
418
        print >>to_file, ''
 
419
 
 
420
class LineLogFormatter(LogFormatter):
 
421
    def truncate(self, str, max_len):
 
422
        if len(str) <= max_len:
 
423
            return str
 
424
        return str[:max_len-3]+'...'
 
425
 
 
426
    def date_string(self, rev):
 
427
        from bzrlib.osutils import format_date
 
428
        return format_date(rev.timestamp, rev.timezone or 0, 
 
429
                           self.show_timezone, date_fmt="%Y-%m-%d",
 
430
                           show_offset=False)
 
431
 
 
432
    def message(self, rev):
 
433
        if not rev.message:
 
434
            return '(no message)'
 
435
        else:
 
436
            return rev.message
 
437
 
 
438
    def show(self, revno, rev, delta):
 
439
        print >> self.to_file, self.log_string(rev, 79) 
 
440
 
 
441
    def log_string(self, rev, max_chars):
 
442
        out = [self.truncate(self.short_committer(rev), 20)]
 
443
        out.append(self.date_string(rev))
 
444
        out.append(self.message(rev).replace('\n', ' '))
 
445
        return self.truncate(" ".join(out).rstrip('\n'), max_chars)
 
446
 
 
447
def line_log(rev, max_chars):
 
448
    lf = LineLogFormatter(None)
 
449
    return lf.log_string(rev, max_chars)
360
450
 
361
451
FORMATTERS = {'long': LongLogFormatter,
362
452
              'short': ShortLogFormatter,
 
453
              'line': LineLogFormatter,
363
454
              }
364
455
 
365
456
 
366
457
def log_formatter(name, *args, **kwargs):
 
458
    """Construct a formatter from arguments.
 
459
 
 
460
    name -- Name of the formatter to construct; currently 'long', 'short' and
 
461
        'line' are supported.
 
462
    """
367
463
    from bzrlib.errors import BzrCommandError
368
 
    
369
464
    try:
370
465
        return FORMATTERS[name](*args, **kwargs)
371
466
    except IndexError:
375
470
    # deprecated; for compatability
376
471
    lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
377
472
    lf.show(revno, rev, delta)
 
473
 
 
474
def show_changed_revisions(branch, old_rh, new_rh, to_file=None, log_format='long'):
 
475
    """Show the change in revision history comparing the old revision history to the new one.
 
476
 
 
477
    :param branch: The branch where the revisions exist
 
478
    :param old_rh: The old revision history
 
479
    :param new_rh: The new revision history
 
480
    :param to_file: A file to write the results to. If None, stdout will be used
 
481
    """
 
482
    if to_file is None:
 
483
        import sys
 
484
        import codecs
 
485
        import bzrlib
 
486
        to_file = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
 
487
    lf = log_formatter(log_format,
 
488
                       show_ids=False,
 
489
                       to_file=to_file,
 
490
                       show_timezone='original')
 
491
 
 
492
    # This is the first index which is different between
 
493
    # old and new
 
494
    base_idx = None
 
495
    for i in xrange(max(len(new_rh),
 
496
                        len(old_rh))):
 
497
        if (len(new_rh) <= i
 
498
            or len(old_rh) <= i
 
499
            or new_rh[i] != old_rh[i]):
 
500
            base_idx = i
 
501
            break
 
502
 
 
503
    if base_idx is None:
 
504
        to_file.write('Nothing seems to have changed\n')
 
505
        return
 
506
    ## TODO: It might be nice to do something like show_log
 
507
    ##       and show the merged entries. But since this is the
 
508
    ##       removed revisions, it shouldn't be as important
 
509
    if base_idx < len(old_rh):
 
510
        to_file.write('*'*60)
 
511
        to_file.write('\nRemoved Revisions:\n')
 
512
        for i in range(base_idx, len(old_rh)):
 
513
            rev = branch.get_revision(old_rh[i])
 
514
            lf.show(i+1, rev, None)
 
515
        to_file.write('*'*60)
 
516
        to_file.write('\n\n')
 
517
    if base_idx < len(new_rh):
 
518
        to_file.write('Added Revisions:\n')
 
519
        show_log(branch,
 
520
                 lf,
 
521
                 None,
 
522
                 verbose=True,
 
523
                 direction='forward',
 
524
                 start_revision=base_idx+1,
 
525
                 end_revision=len(new_rh),
 
526
                 search=None)
 
527