/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: John Arbash Meinel
  • Date: 2006-06-19 14:40:19 UTC
  • mto: This revision was merged to the branch mainline in revision 1794.
  • Revision ID: john@arbash-meinel.com-20060619144019-873a4a8d252f7896
Refactor import stuff into separate functions. Update news

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
"""
51
51
 
52
52
 
53
 
from bzrlib.tree import EmptyTree
 
53
# TODO: option to show delta summaries for merged-in revisions
 
54
import re
 
55
 
54
56
from bzrlib.delta import compare_trees
 
57
import bzrlib.errors as errors
55
58
from bzrlib.trace import mutter
56
 
from bzrlib.errors import InvalidRevisionNumber
 
59
from bzrlib.tree import EmptyTree
 
60
from bzrlib.tsort import merge_sort
57
61
 
58
62
 
59
63
def find_touching_revisions(branch, file_id):
71
75
    last_path = None
72
76
    revno = 1
73
77
    for revision_id in branch.revision_history():
74
 
        this_inv = branch.get_revision_inventory(revision_id)
 
78
        this_inv = branch.repository.get_revision_inventory(revision_id)
75
79
        if file_id in this_inv:
76
80
            this_ie = this_inv[file_id]
77
81
            this_path = this_inv.id2path(file_id)
110
114
    return rh
111
115
 
112
116
 
 
117
def _get_revision_delta(branch, revno):
 
118
    """Return the delta for a mainline revision.
 
119
    
 
120
    This is used to show summaries in verbose logs, and also for finding 
 
121
    revisions which touch a given file."""
 
122
    # XXX: What are we supposed to do when showing a summary for something 
 
123
    # other than a mainline revision.  The delta to it's first parent, or
 
124
    # (more useful) the delta to a nominated other revision.
 
125
    return branch.get_revision_delta(revno)
 
126
 
 
127
 
113
128
def show_log(branch,
114
129
             lf,
115
130
             specific_fileid=None,
140
155
    end_revision
141
156
        If not None, only show revisions <= end_revision
142
157
    """
 
158
    branch.lock_read()
 
159
    try:
 
160
        _show_log(branch, lf, specific_fileid, verbose, direction,
 
161
                  start_revision, end_revision, search)
 
162
    finally:
 
163
        branch.unlock()
 
164
    
 
165
def _show_log(branch,
 
166
             lf,
 
167
             specific_fileid=None,
 
168
             verbose=False,
 
169
             direction='reverse',
 
170
             start_revision=None,
 
171
             end_revision=None,
 
172
             search=None):
 
173
    """Worker function for show_log - see show_log."""
143
174
    from bzrlib.osutils import format_date
144
175
    from bzrlib.errors import BzrCheckError
145
176
    from bzrlib.textui import show_status
150
181
        warn("not a LogFormatter instance: %r" % lf)
151
182
 
152
183
    if specific_fileid:
153
 
        mutter('get log for file_id %r' % specific_fileid)
 
184
        mutter('get log for file_id %r', specific_fileid)
154
185
 
155
186
    if search is not None:
156
187
        import re
162
193
    
163
194
    if start_revision is None:
164
195
        start_revision = 1
165
 
    elif start_revision < 1 or start_revision >= len(which_revs):
166
 
        raise InvalidRevisionNumber(start_revision)
 
196
    else:
 
197
        branch.check_real_revno(start_revision)
167
198
    
168
199
    if end_revision is None:
169
200
        end_revision = len(which_revs)
170
 
    elif end_revision < 1 or end_revision >= len(which_revs):
171
 
        raise InvalidRevisionNumber(end_revision)
 
201
    else:
 
202
        branch.check_real_revno(end_revision)
172
203
 
173
204
    # list indexes are 0-based; revisions are 1-based
174
205
    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)
175
219
 
176
220
    if direction == 'reverse':
177
221
        cut_revs.reverse()
178
222
    elif direction == 'forward':
179
 
        pass
 
223
        # forward means oldest first.
 
224
        merge_sorted_revisions.reverse()
180
225
    else:
181
226
        raise ValueError('invalid direction %r' % direction)
182
227
 
183
 
    for revno, rev_id in cut_revs:
184
 
        if verbose or specific_fileid:
185
 
            delta = branch.get_revision_delta(revno)
186
 
            
187
 
        if specific_fileid:
188
 
            if not delta.touches_file_id(specific_fileid):
189
 
                continue
190
 
 
191
 
        if not verbose:
192
 
            # although we calculated it, throw it away without display
193
 
            delta = None
194
 
 
195
 
        rev = branch.get_revision(rev_id)
 
228
    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
    revisions = branch.repository.get_revisions([r for s, r, m, e in
 
236
                                                 merge_sorted_revisions])
 
237
 
 
238
    # now we just print all the revisions
 
239
    for ((sequence, rev_id, merge_depth, end_of_merge), rev) in \
 
240
        zip(merge_sorted_revisions, revisions):
196
241
 
197
242
        if searchRE:
198
243
            if not searchRE.search(rev.message):
199
244
                continue
200
245
 
201
 
        lf.show(revno, rev, delta)
 
246
        if merge_depth == 0:
 
247
            # a mainline revision.
 
248
            if verbose or specific_fileid:
 
249
                delta = _get_revision_delta(branch, rev_nos[rev_id])
 
250
                
 
251
            if specific_fileid:
 
252
                if not delta.touches_file_id(specific_fileid):
 
253
                    continue
 
254
    
 
255
            if not verbose:
 
256
                # although we calculated it, throw it away without display
 
257
                delta = None
202
258
 
 
259
            lf.show(rev_nos[rev_id], rev, delta)
 
260
        elif hasattr(lf, 'show_merge'):
 
261
            lf.show_merge(rev, merge_depth)
203
262
 
204
263
 
205
264
def deltas_for_log_dummy(branch, which_revs):
286
345
 
287
346
class LogFormatter(object):
288
347
    """Abstract class to display log messages."""
 
348
 
289
349
    def __init__(self, to_file, show_ids=False, show_timezone='original'):
290
350
        self.to_file = to_file
291
351
        self.show_ids = show_ids
292
352
        self.show_timezone = show_timezone
293
353
 
294
 
 
295
354
    def show(self, revno, rev, delta):
296
355
        raise NotImplementedError('not implemented in abstract base')
297
 
        
298
 
 
299
 
 
300
 
 
301
 
 
302
 
 
 
356
 
 
357
    def short_committer(self, rev):
 
358
        return re.sub('<.*@.*>', '', rev.committer).strip(' ')
 
359
    
 
360
    
303
361
class LongLogFormatter(LogFormatter):
304
362
    def show(self, revno, rev, delta):
305
 
        from osutils import format_date
306
 
 
 
363
        return self._show_helper(revno=revno, rev=rev, delta=delta)
 
364
 
 
365
    def show_merge(self, rev, merge_depth):
 
366
        return self._show_helper(rev=rev, indent='    '*merge_depth, merged=True, delta=None)
 
367
 
 
368
    def _show_helper(self, rev=None, revno=None, indent='', merged=False, delta=None):
 
369
        """Show a revision, either merged or not."""
 
370
        from bzrlib.osutils import format_date
307
371
        to_file = self.to_file
308
 
 
309
 
        print >>to_file,  '-' * 60
310
 
        print >>to_file,  'revno:', revno
 
372
        print >>to_file,  indent+'-' * 60
 
373
        if revno is not None:
 
374
            print >>to_file,  'revno:', revno
 
375
        if merged:
 
376
            print >>to_file,  indent+'merged:', rev.revision_id
 
377
        elif self.show_ids:
 
378
            print >>to_file,  indent+'revision-id:', rev.revision_id
311
379
        if self.show_ids:
312
 
            print >>to_file,  'revision-id:', rev.revision_id
313
 
        print >>to_file,  'committer:', rev.committer
314
 
 
 
380
            for parent_id in rev.parent_ids:
 
381
                print >>to_file, indent+'parent:', parent_id
 
382
        print >>to_file,  indent+'committer:', rev.committer
 
383
        try:
 
384
            print >>to_file, indent+'branch nick: %s' % \
 
385
                rev.properties['branch-nick']
 
386
        except KeyError:
 
387
            pass
315
388
        date_str = format_date(rev.timestamp,
316
389
                               rev.timezone or 0,
317
390
                               self.show_timezone)
318
 
        print >>to_file,  'timestamp: %s' % date_str
 
391
        print >>to_file,  indent+'timestamp: %s' % date_str
319
392
 
320
 
        print >>to_file,  'message:'
 
393
        print >>to_file,  indent+'message:'
321
394
        if not rev.message:
322
 
            print >>to_file,  '  (no message)'
 
395
            print >>to_file,  indent+'  (no message)'
323
396
        else:
324
 
            for l in rev.message.split('\n'):
325
 
                print >>to_file,  '  ' + l
326
 
 
 
397
            message = rev.message.rstrip('\r\n')
 
398
            for l in message.split('\n'):
 
399
                print >>to_file,  indent+'  ' + l
327
400
        if delta != None:
328
401
            delta.show(to_file, self.show_ids)
329
402
 
330
403
 
331
 
 
332
404
class ShortLogFormatter(LogFormatter):
333
405
    def show(self, revno, rev, delta):
334
406
        from bzrlib.osutils import format_date
335
407
 
336
408
        to_file = self.to_file
337
 
 
338
 
        print >>to_file, "%5d %s\t%s" % (revno, rev.committer,
 
409
        date_str = format_date(rev.timestamp, rev.timezone or 0,
 
410
                            self.show_timezone)
 
411
        print >>to_file, "%5d %s\t%s" % (revno, self.short_committer(rev),
339
412
                format_date(rev.timestamp, rev.timezone or 0,
340
 
                            self.show_timezone))
 
413
                            self.show_timezone, date_fmt="%Y-%m-%d",
 
414
                           show_offset=False))
341
415
        if self.show_ids:
342
416
            print >>to_file,  '      revision-id:', rev.revision_id
343
417
        if not rev.message:
344
418
            print >>to_file,  '      (no message)'
345
419
        else:
346
 
            for l in rev.message.split('\n'):
 
420
            message = rev.message.rstrip('\r\n')
 
421
            for l in message.split('\n'):
347
422
                print >>to_file,  '      ' + l
348
423
 
349
424
        # TODO: Why not show the modified files in a shorter form as
350
425
        # well? rewrap them single lines of appropriate length
351
426
        if delta != None:
352
427
            delta.show(to_file, self.show_ids)
353
 
        print
354
 
 
355
 
 
356
 
 
357
 
FORMATTERS = {'long': LongLogFormatter,
 
428
        print >>to_file, ''
 
429
 
 
430
 
 
431
class LineLogFormatter(LogFormatter):
 
432
    def truncate(self, str, max_len):
 
433
        if len(str) <= max_len:
 
434
            return str
 
435
        return str[:max_len-3]+'...'
 
436
 
 
437
    def date_string(self, rev):
 
438
        from bzrlib.osutils import format_date
 
439
        return format_date(rev.timestamp, rev.timezone or 0, 
 
440
                           self.show_timezone, date_fmt="%Y-%m-%d",
 
441
                           show_offset=False)
 
442
 
 
443
    def message(self, rev):
 
444
        if not rev.message:
 
445
            return '(no message)'
 
446
        else:
 
447
            return rev.message
 
448
 
 
449
    def show(self, revno, rev, delta):
 
450
        from bzrlib.osutils import terminal_width
 
451
        print >> self.to_file, self.log_string(revno, rev, terminal_width()-1)
 
452
 
 
453
    def log_string(self, revno, rev, max_chars):
 
454
        """Format log info into one string. Truncate tail of string
 
455
        :param  revno:      revision number (int) or None.
 
456
                            Revision numbers counts from 1.
 
457
        :param  rev:        revision info object
 
458
        :param  max_chars:  maximum length of resulting string
 
459
        :return:            formatted truncated string
 
460
        """
 
461
        out = []
 
462
        if revno:
 
463
            # show revno only when is not None
 
464
            out.append("%d:" % revno)
 
465
        out.append(self.truncate(self.short_committer(rev), 20))
 
466
        out.append(self.date_string(rev))
 
467
        out.append(rev.get_summary())
 
468
        return self.truncate(" ".join(out).rstrip('\n'), max_chars)
 
469
 
 
470
 
 
471
def line_log(rev, max_chars):
 
472
    lf = LineLogFormatter(None)
 
473
    return lf.log_string(None, rev, max_chars)
 
474
 
 
475
FORMATTERS = {
 
476
              'long': LongLogFormatter,
358
477
              'short': ShortLogFormatter,
 
478
              'line': LineLogFormatter,
359
479
              }
360
480
 
 
481
def register_formatter(name, formatter):
 
482
    FORMATTERS[name] = formatter
361
483
 
362
484
def log_formatter(name, *args, **kwargs):
 
485
    """Construct a formatter from arguments.
 
486
 
 
487
    name -- Name of the formatter to construct; currently 'long', 'short' and
 
488
        'line' are supported.
 
489
    """
363
490
    from bzrlib.errors import BzrCommandError
364
 
    
365
491
    try:
366
492
        return FORMATTERS[name](*args, **kwargs)
367
 
    except IndexError:
 
493
    except KeyError:
368
494
        raise BzrCommandError("unknown log formatter: %r" % name)
369
495
 
370
496
def show_one_log(revno, rev, delta, verbose, to_file, show_timezone):
371
 
    # deprecated; for compatability
 
497
    # deprecated; for compatibility
372
498
    lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
373
499
    lf.show(revno, rev, delta)
 
500
 
 
501
def show_changed_revisions(branch, old_rh, new_rh, to_file=None, log_format='long'):
 
502
    """Show the change in revision history comparing the old revision history to the new one.
 
503
 
 
504
    :param branch: The branch where the revisions exist
 
505
    :param old_rh: The old revision history
 
506
    :param new_rh: The new revision history
 
507
    :param to_file: A file to write the results to. If None, stdout will be used
 
508
    """
 
509
    if to_file is None:
 
510
        import sys
 
511
        import codecs
 
512
        import bzrlib
 
513
        to_file = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
 
514
    lf = log_formatter(log_format,
 
515
                       show_ids=False,
 
516
                       to_file=to_file,
 
517
                       show_timezone='original')
 
518
 
 
519
    # This is the first index which is different between
 
520
    # old and new
 
521
    base_idx = None
 
522
    for i in xrange(max(len(new_rh),
 
523
                        len(old_rh))):
 
524
        if (len(new_rh) <= i
 
525
            or len(old_rh) <= i
 
526
            or new_rh[i] != old_rh[i]):
 
527
            base_idx = i
 
528
            break
 
529
 
 
530
    if base_idx is None:
 
531
        to_file.write('Nothing seems to have changed\n')
 
532
        return
 
533
    ## TODO: It might be nice to do something like show_log
 
534
    ##       and show the merged entries. But since this is the
 
535
    ##       removed revisions, it shouldn't be as important
 
536
    if base_idx < len(old_rh):
 
537
        to_file.write('*'*60)
 
538
        to_file.write('\nRemoved Revisions:\n')
 
539
        for i in range(base_idx, len(old_rh)):
 
540
            rev = branch.repository.get_revision(old_rh[i])
 
541
            lf.show(i+1, rev, None)
 
542
        to_file.write('*'*60)
 
543
        to_file.write('\n\n')
 
544
    if base_idx < len(new_rh):
 
545
        to_file.write('Added Revisions:\n')
 
546
        show_log(branch,
 
547
                 lf,
 
548
                 None,
 
549
                 verbose=True,
 
550
                 direction='forward',
 
551
                 start_revision=base_idx+1,
 
552
                 end_revision=len(new_rh),
 
553
                 search=None)
 
554