~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

- rules for using destructors

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
 
import re
55
 
 
 
53
import bzrlib.errors as errors
 
54
from bzrlib.tree import EmptyTree
56
55
from bzrlib.delta import compare_trees
57
 
import bzrlib.errors as errors
58
56
from bzrlib.trace import mutter
59
 
from bzrlib.tree import EmptyTree
60
 
from bzrlib.tsort import merge_sort
 
57
import re
61
58
 
62
59
 
63
60
def find_touching_revisions(branch, file_id):
75
72
    last_path = None
76
73
    revno = 1
77
74
    for revision_id in branch.revision_history():
78
 
        this_inv = branch.repository.get_revision_inventory(revision_id)
 
75
        this_inv = branch.get_revision_inventory(revision_id)
79
76
        if file_id in this_inv:
80
77
            this_ie = this_inv[file_id]
81
78
            this_path = this_inv.id2path(file_id)
181
178
        warn("not a LogFormatter instance: %r" % lf)
182
179
 
183
180
    if specific_fileid:
184
 
        mutter('get log for file_id %r', specific_fileid)
 
181
        mutter('get log for file_id %r' % specific_fileid)
185
182
 
186
183
    if search is not None:
187
184
        import re
203
200
 
204
201
    # list indexes are 0-based; revisions are 1-based
205
202
    cut_revs = which_revs[(start_revision-1):(end_revision)]
206
 
    if not cut_revs:
207
 
        return
208
 
    # override the mainline to look like the revision history.
209
 
    mainline_revs = [revision_id for index, revision_id in cut_revs]
210
 
    if cut_revs[0][0] == 1:
211
 
        mainline_revs.insert(0, None)
212
 
    else:
213
 
        mainline_revs.insert(0, which_revs[start_revision-2][1])
214
 
 
215
 
    merge_sorted_revisions = merge_sort(
216
 
        branch.repository.get_revision_graph(mainline_revs[-1]),
217
 
        mainline_revs[-1],
218
 
        mainline_revs)
219
203
 
220
204
    if direction == 'reverse':
221
205
        cut_revs.reverse()
222
206
    elif direction == 'forward':
223
 
        # forward means oldest first.
224
 
        merge_sorted_revisions.reverse()
 
207
        pass
225
208
    else:
226
209
        raise ValueError('invalid direction %r' % direction)
227
210
 
228
211
    revision_history = branch.revision_history()
229
 
 
230
 
    # convert the revision history to a dictionary:
231
 
    rev_nos = {}
232
 
    for index, rev_id in cut_revs:
233
 
        rev_nos[rev_id] = index
234
 
 
235
 
    # now we just print all the revisions
236
 
    for sequence, rev_id, merge_depth, end_of_merge in merge_sorted_revisions:
237
 
        rev = branch.repository.get_revision(rev_id)
 
212
    for revno, rev_id in cut_revs:
 
213
        if verbose or specific_fileid:
 
214
            delta = _get_revision_delta(branch, revno)
 
215
            
 
216
        if specific_fileid:
 
217
            if not delta.touches_file_id(specific_fileid):
 
218
                continue
 
219
 
 
220
        if not verbose:
 
221
            # although we calculated it, throw it away without display
 
222
            delta = None
 
223
 
 
224
        rev = branch.get_revision(rev_id)
238
225
 
239
226
        if searchRE:
240
227
            if not searchRE.search(rev.message):
241
228
                continue
242
229
 
243
 
        if merge_depth == 0:
244
 
            # a mainline revision.
245
 
            if verbose or specific_fileid:
246
 
                delta = _get_revision_delta(branch, rev_nos[rev_id])
247
 
                
248
 
            if specific_fileid:
249
 
                if not delta.touches_file_id(specific_fileid):
250
 
                    continue
251
 
    
252
 
            if not verbose:
253
 
                # although we calculated it, throw it away without display
254
 
                delta = None
255
 
 
256
 
            lf.show(rev_nos[rev_id], rev, delta)
257
 
        elif hasattr(lf, 'show_merge'):
258
 
            lf.show_merge(rev, merge_depth)
 
230
        lf.show(revno, rev, delta)
 
231
        if hasattr(lf, 'show_merge'):
 
232
            if revno == 1:
 
233
                excludes = set()
 
234
            else:
 
235
                # revno is 1 based, so -2 to get back 1 less.
 
236
                excludes = set(branch.get_ancestry(revision_history[revno - 2]))
 
237
            pending = list(rev.parent_ids)
 
238
            while pending:
 
239
                rev_id = pending.pop()
 
240
                if rev_id in excludes:
 
241
                    continue
 
242
                # prevent showing merged revs twice if they multi-path.
 
243
                excludes.add(rev_id)
 
244
                try:
 
245
                    rev = branch.get_revision(rev_id)
 
246
                except errors.NoSuchRevision:
 
247
                    continue
 
248
                pending.extend(rev.parent_ids)
 
249
                lf.show_merge(rev)
259
250
 
260
251
 
261
252
def deltas_for_log_dummy(branch, which_revs):
351
342
    def show(self, revno, rev, delta):
352
343
        raise NotImplementedError('not implemented in abstract base')
353
344
 
354
 
    def short_committer(self, rev):
355
 
        return re.sub('<.*@.*>', '', rev.committer).strip(' ')
356
 
    
357
345
    
358
346
class LongLogFormatter(LogFormatter):
359
347
    def show(self, revno, rev, delta):
360
 
        return self._show_helper(revno=revno, rev=rev, delta=delta)
361
 
 
362
 
    def show_merge(self, rev, merge_depth):
363
 
        return self._show_helper(rev=rev, indent='    '*merge_depth, merged=True, delta=None)
364
 
 
365
 
    def _show_helper(self, rev=None, revno=None, indent='', merged=False, delta=None):
366
 
        """Show a revision, either merged or not."""
367
 
        from bzrlib.osutils import format_date
368
 
        to_file = self.to_file
 
348
        from osutils import format_date
 
349
 
 
350
        to_file = self.to_file
 
351
 
 
352
        print >>to_file,  '-' * 60
 
353
        print >>to_file,  'revno:', revno
 
354
        if self.show_ids:
 
355
            print >>to_file,  'revision-id:', rev.revision_id
 
356
 
 
357
            for parent_id in rev.parent_ids:
 
358
                print >>to_file, 'parent:', parent_id
 
359
            
 
360
        print >>to_file,  'committer:', rev.committer
 
361
 
 
362
        date_str = format_date(rev.timestamp,
 
363
                               rev.timezone or 0,
 
364
                               self.show_timezone)
 
365
        print >>to_file,  'timestamp: %s' % date_str
 
366
 
 
367
        print >>to_file,  'message:'
 
368
        if not rev.message:
 
369
            print >>to_file,  '  (no message)'
 
370
        else:
 
371
            for l in rev.message.split('\n'):
 
372
                print >>to_file,  '  ' + l
 
373
 
 
374
        if delta != None:
 
375
            delta.show(to_file, self.show_ids)
 
376
 
 
377
    def show_merge(self, rev):
 
378
        from osutils import format_date
 
379
 
 
380
        to_file = self.to_file
 
381
 
 
382
        indent = '    '
 
383
 
369
384
        print >>to_file,  indent+'-' * 60
370
 
        if revno is not None:
371
 
            print >>to_file,  'revno:', revno
372
 
        if merged:
373
 
            print >>to_file,  indent+'merged:', rev.revision_id
374
 
        elif self.show_ids:
375
 
            print >>to_file,  indent+'revision-id:', rev.revision_id
 
385
        print >>to_file,  indent+'merged:', rev.revision_id
376
386
        if self.show_ids:
377
387
            for parent_id in rev.parent_ids:
378
388
                print >>to_file, indent+'parent:', parent_id
 
389
            
379
390
        print >>to_file,  indent+'committer:', rev.committer
380
 
        try:
381
 
            print >>to_file, indent+'branch nick: %s' % \
382
 
                rev.properties['branch-nick']
383
 
        except KeyError:
384
 
            pass
 
391
 
385
392
        date_str = format_date(rev.timestamp,
386
393
                               rev.timezone or 0,
387
394
                               self.show_timezone)
391
398
        if not rev.message:
392
399
            print >>to_file,  indent+'  (no message)'
393
400
        else:
394
 
            message = rev.message.rstrip('\r\n')
395
 
            for l in message.split('\n'):
 
401
            for l in rev.message.split('\n'):
396
402
                print >>to_file,  indent+'  ' + l
397
 
        if delta != None:
398
 
            delta.show(to_file, self.show_ids)
399
403
 
400
404
 
401
405
class ShortLogFormatter(LogFormatter):
405
409
        to_file = self.to_file
406
410
        date_str = format_date(rev.timestamp, rev.timezone or 0,
407
411
                            self.show_timezone)
408
 
        print >>to_file, "%5d %s\t%s" % (revno, self.short_committer(rev),
 
412
        print >>to_file, "%5d %s\t%s" % (revno, rev.committer,
409
413
                format_date(rev.timestamp, rev.timezone or 0,
410
 
                            self.show_timezone, date_fmt="%Y-%m-%d",
411
 
                           show_offset=False))
 
414
                            self.show_timezone))
412
415
        if self.show_ids:
413
416
            print >>to_file,  '      revision-id:', rev.revision_id
414
417
        if not rev.message:
415
418
            print >>to_file,  '      (no message)'
416
419
        else:
417
 
            message = rev.message.rstrip('\r\n')
418
 
            for l in message.split('\n'):
 
420
            for l in rev.message.split('\n'):
419
421
                print >>to_file,  '      ' + l
420
422
 
421
423
        # TODO: Why not show the modified files in a shorter form as
422
424
        # well? rewrap them single lines of appropriate length
423
425
        if delta != None:
424
426
            delta.show(to_file, self.show_ids)
425
 
        print >>to_file, ''
 
427
        print
426
428
 
427
429
class LineLogFormatter(LogFormatter):
428
430
    def truncate(self, str, max_len):
442
444
        else:
443
445
            return rev.message
444
446
 
 
447
    def short_committer(self, rev):
 
448
        return re.sub('<.*@.*>', '', rev.committer).strip(' ')
 
449
    
445
450
    def show(self, revno, rev, delta):
446
451
        print >> self.to_file, self.log_string(rev, 79) 
447
452
 
455
460
    lf = LineLogFormatter(None)
456
461
    return lf.log_string(rev, max_chars)
457
462
 
458
 
FORMATTERS = {
459
 
              'long': LongLogFormatter,
 
463
FORMATTERS = {'long': LongLogFormatter,
460
464
              'short': ShortLogFormatter,
461
465
              'line': LineLogFormatter,
462
466
              }
463
467
 
464
 
def register_formatter(name, formatter):
465
 
    FORMATTERS[name] = formatter
466
468
 
467
469
def log_formatter(name, *args, **kwargs):
468
470
    """Construct a formatter from arguments.
473
475
    from bzrlib.errors import BzrCommandError
474
476
    try:
475
477
        return FORMATTERS[name](*args, **kwargs)
476
 
    except KeyError:
 
478
    except IndexError:
477
479
        raise BzrCommandError("unknown log formatter: %r" % name)
478
480
 
479
481
def show_one_log(revno, rev, delta, verbose, to_file, show_timezone):
480
482
    # deprecated; for compatability
481
483
    lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
482
484
    lf.show(revno, rev, delta)
483
 
 
484
 
def show_changed_revisions(branch, old_rh, new_rh, to_file=None, log_format='long'):
485
 
    """Show the change in revision history comparing the old revision history to the new one.
486
 
 
487
 
    :param branch: The branch where the revisions exist
488
 
    :param old_rh: The old revision history
489
 
    :param new_rh: The new revision history
490
 
    :param to_file: A file to write the results to. If None, stdout will be used
491
 
    """
492
 
    if to_file is None:
493
 
        import sys
494
 
        import codecs
495
 
        import bzrlib
496
 
        to_file = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
497
 
    lf = log_formatter(log_format,
498
 
                       show_ids=False,
499
 
                       to_file=to_file,
500
 
                       show_timezone='original')
501
 
 
502
 
    # This is the first index which is different between
503
 
    # old and new
504
 
    base_idx = None
505
 
    for i in xrange(max(len(new_rh),
506
 
                        len(old_rh))):
507
 
        if (len(new_rh) <= i
508
 
            or len(old_rh) <= i
509
 
            or new_rh[i] != old_rh[i]):
510
 
            base_idx = i
511
 
            break
512
 
 
513
 
    if base_idx is None:
514
 
        to_file.write('Nothing seems to have changed\n')
515
 
        return
516
 
    ## TODO: It might be nice to do something like show_log
517
 
    ##       and show the merged entries. But since this is the
518
 
    ##       removed revisions, it shouldn't be as important
519
 
    if base_idx < len(old_rh):
520
 
        to_file.write('*'*60)
521
 
        to_file.write('\nRemoved Revisions:\n')
522
 
        for i in range(base_idx, len(old_rh)):
523
 
            rev = branch.repository.get_revision(old_rh[i])
524
 
            lf.show(i+1, rev, None)
525
 
        to_file.write('*'*60)
526
 
        to_file.write('\n\n')
527
 
    if base_idx < len(new_rh):
528
 
        to_file.write('Added Revisions:\n')
529
 
        show_log(branch,
530
 
                 lf,
531
 
                 None,
532
 
                 verbose=True,
533
 
                 direction='forward',
534
 
                 start_revision=base_idx+1,
535
 
                 end_revision=len(new_rh),
536
 
                 search=None)
537