~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-30 16:43:12 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060730164312-b025fd3ff0cee59e
rename  gpl.txt => COPYING.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005 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
 
    )
61
57
import bzrlib.errors as errors
62
 
from bzrlib.symbol_versioning import deprecated_method, zero_eleven
63
58
from bzrlib.trace import mutter
64
 
from bzrlib.tsort import(
65
 
    merge_sort,
66
 
    topo_sort,
67
 
    )
 
59
from bzrlib.tsort import merge_sort
68
60
 
69
61
 
70
62
def find_touching_revisions(branch, file_id):
210
202
        mainline_revs.insert(0, None)
211
203
    else:
212
204
        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:
223
 
        # 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.',
226
 
            DeprecationWarning, stacklevel=3)
227
 
    view_revs_iter = get_view_revisions(mainline_revs, rev_nos, branch,
228
 
                          direction, include_merges=include_merges)
229
 
    if specific_fileid:
230
 
        view_revisions = _get_revisions_touching_file_id(branch,
231
 
                                                         specific_fileid,
232
 
                                                         mainline_revs,
233
 
                                                         view_revs_iter)
234
 
    else:
235
 
        view_revisions = list(view_revs_iter)
236
 
 
237
 
    use_tags = getattr(lf, 'supports_tags', False)
238
 
    if use_tags:
239
 
        rev_tag_dict = {}
240
 
        if branch.supports_tags():
241
 
            rev_tag_dict = branch.tags.get_reverse_tag_dict()
 
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))
242
211
 
243
212
    def iter_revisions():
244
213
        # r = revision, n = revno, d = merge depth
249
218
        while revision_ids:
250
219
            cur_deltas = {}
251
220
            revisions = repository.get_revisions(revision_ids[:num])
252
 
            if verbose:
 
221
            if verbose or specific_fileid:
253
222
                delta_revisions = [r for r in revisions if
254
223
                                   r.revision_id in zeros]
255
224
                deltas = repository.get_deltas_for_revisions(delta_revisions)
257
226
                                        delta_revisions), deltas))
258
227
            for revision in revisions:
259
228
                # The delta value will be None unless
260
 
                # 1. verbose is specified, and
 
229
                # 1. verbose or specific_fileid is specified, and
261
230
                # 2. the revision is a mainline revision
262
231
                yield revision, cur_deltas.get(revision.revision_id)
263
232
            revision_ids  = revision_ids[num:]
264
 
            num = min(int(num * 1.5), 200)
 
233
            num = int(num * 1.5)
265
234
            
266
235
    # now we just print all the revisions
267
236
    for ((rev_id, revno, merge_depth), (rev, delta)) in \
272
241
                continue
273
242
 
274
243
        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)
279
 
        else:
280
 
            if show_merge_revno is None:
281
 
                lf.show_merge(rev, merge_depth)
282
 
            else:
283
 
                if use_tags:
284
 
                    lf.show_merge_revno(rev, merge_depth, revno,
285
 
                                        rev_tag_dict.get(rev_id))
286
 
                else:
287
 
                    lf.show_merge_revno(rev, merge_depth, revno)
288
 
 
289
 
 
290
 
def _get_revisions_touching_file_id(branch, file_id, mainline_revisions,
291
 
                                    view_revs_iter):
292
 
    """Return the list of revision ids which touch a given file id.
293
 
 
294
 
    This includes the revisions which directly change the file id,
295
 
    and the revisions which merge these changes. So if the
296
 
    revision graph is::
297
 
        A
298
 
        |\
299
 
        B C
300
 
        |/
301
 
        D
302
 
 
303
 
    And 'C' changes a file, then both C and D will be returned.
304
 
 
305
 
    This will also can be restricted based on a subset of the mainline.
306
 
 
307
 
    :return: A list of (revision_id, dotted_revno, merge_depth) tuples.
308
 
    """
309
 
    # find all the revisions that change the specific file
310
 
    file_weave = branch.repository.weave_store.get_weave(file_id,
311
 
                branch.repository.get_transaction())
312
 
    weave_modifed_revisions = set(file_weave.versions())
313
 
    # build the ancestry of each revision in the graph
314
 
    # - only listing the ancestors that change the specific file.
315
 
    rev_graph = branch.repository.get_revision_graph(mainline_revisions[-1])
316
 
    sorted_rev_list = topo_sort(rev_graph)
317
 
    ancestry = {}
318
 
    for rev in sorted_rev_list:
319
 
        parents = rev_graph[rev]
320
 
        if rev not in weave_modifed_revisions and len(parents) == 1:
321
 
            # We will not be adding anything new, so just use a reference to
322
 
            # the parent ancestry.
323
 
            rev_ancestry = ancestry[parents[0]]
324
 
        else:
325
 
            rev_ancestry = set()
326
 
            if rev in weave_modifed_revisions:
327
 
                rev_ancestry.add(rev)
328
 
            for parent in parents:
329
 
                rev_ancestry = rev_ancestry.union(ancestry[parent])
330
 
        ancestry[rev] = rev_ancestry
331
 
 
332
 
    def is_merging_rev(r):
333
 
        parents = rev_graph[r]
334
 
        if len(parents) > 1:
335
 
            leftparent = parents[0]
336
 
            for rightparent in parents[1:]:
337
 
                if not ancestry[leftparent].issuperset(
338
 
                        ancestry[rightparent]):
339
 
                    return True
340
 
        return False
341
 
 
342
 
    # filter from the view the revisions that did not change or merge 
343
 
    # the specific file
344
 
    return [(r, n, d) for r, n, d in view_revs_iter
345
 
            if r in weave_modifed_revisions or is_merging_rev(r)]
 
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)
346
257
 
347
258
 
348
259
def get_view_revisions(mainline_revs, rev_nos, branch, direction,
356
267
        if direction == 'reverse':
357
268
            revision_ids.reverse()
358
269
        for revision_id in revision_ids:
359
 
            yield revision_id, str(rev_nos[revision_id]), 0
 
270
            yield revision_id, rev_nos[revision_id], 0
360
271
        return
361
272
    merge_sorted_revisions = merge_sort(
362
273
        branch.repository.get_revision_graph(mainline_revs[-1]),
363
274
        mainline_revs[-1],
364
 
        mainline_revs,
365
 
        generate_revno=True)
 
275
        mainline_revs)
366
276
 
367
277
    if direction == 'forward':
368
278
        # forward means oldest first.
370
280
    elif direction != 'reverse':
371
281
        raise ValueError('invalid direction %r' % direction)
372
282
 
373
 
    for sequence, rev_id, merge_depth, revno, end_of_merge in merge_sorted_revisions:
374
 
        yield rev_id, '.'.join(map(str, revno)), merge_depth
 
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
375
287
 
376
288
 
377
289
def reverse_by_depth(merge_sorted_revisions, _depth=0):
411
323
 
412
324
    def short_committer(self, rev):
413
325
        return re.sub('<.*@.*>', '', rev.committer).strip(' ')
414
 
 
415
 
 
 
326
    
 
327
    
416
328
class LongLogFormatter(LogFormatter):
417
 
 
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
422
 
 
423
 
    def show(self, revno, rev, delta, tags=None):
424
 
        return self._show_helper(revno=revno, rev=rev, delta=delta, tags=tags)
425
 
 
426
 
    @deprecated_method(zero_eleven)
 
329
    def show(self, revno, rev, delta):
 
330
        return self._show_helper(revno=revno, rev=rev, delta=delta)
 
331
 
427
332
    def show_merge(self, rev, merge_depth):
428
 
        return self._show_helper(rev=rev, indent='    '*merge_depth,
429
 
                                 merged=True, delta=None)
430
 
 
431
 
    def show_merge_revno(self, rev, merge_depth, revno, tags=None):
432
 
        """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)
435
 
 
436
 
    def _show_helper(self, rev=None, revno=None, indent='', merged=False,
437
 
                     delta=None, tags=None):
 
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):
438
336
        """Show a revision, either merged or not."""
439
337
        from bzrlib.osutils import format_date
440
338
        to_file = self.to_file
441
339
        print >>to_file,  indent+'-' * 60
442
340
        if revno is not None:
443
 
            print >>to_file,  indent+'revno:', revno
444
 
        if tags:
445
 
            print >>to_file, indent+'tags: %s' % (', '.join(tags))
 
341
            print >>to_file,  'revno:', revno
446
342
        if merged:
447
343
            print >>to_file,  indent+'merged:', rev.revision_id
448
344
        elif self.show_ids:
451
347
            for parent_id in rev.parent_ids:
452
348
                print >>to_file, indent+'parent:', parent_id
453
349
        print >>to_file,  indent+'committer:', rev.committer
454
 
 
455
350
        try:
456
351
            print >>to_file, indent+'branch nick: %s' % \
457
352
                rev.properties['branch-nick']
469
364
            message = rev.message.rstrip('\r\n')
470
365
            for l in message.split('\n'):
471
366
                print >>to_file,  indent+'  ' + l
472
 
        if delta is not None:
 
367
        if delta != None:
473
368
            delta.show(to_file, self.show_ids)
474
369
 
475
370
 
480
375
        to_file = self.to_file
481
376
        date_str = format_date(rev.timestamp, rev.timezone or 0,
482
377
                            self.show_timezone)
483
 
        print >>to_file, "%5s %s\t%s" % (revno, self.short_committer(rev),
 
378
        print >>to_file, "%5d %s\t%s" % (revno, self.short_committer(rev),
484
379
                format_date(rev.timestamp, rev.timezone or 0,
485
380
                            self.show_timezone, date_fmt="%Y-%m-%d",
486
381
                           show_offset=False))
495
390
 
496
391
        # TODO: Why not show the modified files in a shorter form as
497
392
        # well? rewrap them single lines of appropriate length
498
 
        if delta is not None:
 
393
        if delta != None:
499
394
            delta.show(to_file, self.show_ids)
500
395
        print >>to_file, ''
501
396
 
533
428
        out = []
534
429
        if revno:
535
430
            # show revno only when is not None
536
 
            out.append("%s:" % revno)
 
431
            out.append("%d:" % revno)
537
432
        out.append(self.truncate(self.short_committer(rev), 20))
538
433
        out.append(self.date_string(rev))
539
434
        out.append(rev.get_summary())
544
439
    lf = LineLogFormatter(None)
545
440
    return lf.log_string(None, rev, max_chars)
546
441
 
547
 
 
548
 
class LogFormatterRegistry(registry.Registry):
549
 
    """Registry for log formatters"""
550
 
 
551
 
    def make_formatter(self, name, *args, **kwargs):
552
 
        """Construct a formatter from arguments.
553
 
 
554
 
        :param name: Name of the formatter to construct.  'short', 'long' and
555
 
            'line' are built-in.
556
 
        """
557
 
        return self.get(name)(*args, **kwargs)
558
 
 
559
 
    def get_default(self, branch):
560
 
        return self.get(branch.get_config().log_format())
561
 
 
562
 
 
563
 
log_formatter_registry = LogFormatterRegistry()
564
 
 
565
 
 
566
 
log_formatter_registry.register('short', ShortLogFormatter,
567
 
                                'Moderately short log format')
568
 
log_formatter_registry.register('long', LongLogFormatter,
569
 
                                'Detailed log format')
570
 
log_formatter_registry.register('line', LineLogFormatter,
571
 
                                'Log format with one line per revision')
572
 
 
 
442
FORMATTERS = {
 
443
              'long': LongLogFormatter,
 
444
              'short': ShortLogFormatter,
 
445
              'line': LineLogFormatter,
 
446
              }
573
447
 
574
448
def register_formatter(name, formatter):
575
 
    log_formatter_registry.register(name, formatter)
576
 
 
 
449
    FORMATTERS[name] = formatter
577
450
 
578
451
def log_formatter(name, *args, **kwargs):
579
452
    """Construct a formatter from arguments.
583
456
    """
584
457
    from bzrlib.errors import BzrCommandError
585
458
    try:
586
 
        return log_formatter_registry.make_formatter(name, *args, **kwargs)
 
459
        return FORMATTERS[name](*args, **kwargs)
587
460
    except KeyError:
588
461
        raise BzrCommandError("unknown log formatter: %r" % name)
589
462
 
590
 
 
591
463
def show_one_log(revno, rev, delta, verbose, to_file, show_timezone):
592
464
    # deprecated; for compatibility
593
465
    lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)