204
172
# list indexes are 0-based; revisions are 1-based
205
173
cut_revs = which_revs[(start_revision-1):(end_revision)]
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)
213
mainline_revs.insert(0, which_revs[start_revision-2][1])
215
merge_sorted_revisions = merge_sort(
216
branch.repository.get_revision_graph(mainline_revs[-1]),
220
175
if direction == 'reverse':
221
176
cut_revs.reverse()
222
177
elif direction == 'forward':
223
# forward means oldest first.
224
merge_sorted_revisions.reverse()
226
180
raise ValueError('invalid direction %r' % direction)
228
revision_history = branch.revision_history()
230
# convert the revision history to a dictionary:
232
for index, rev_id in cut_revs:
233
rev_nos[rev_id] = index
235
revisions = branch.repository.get_revisions([r for s, r, m, e in
236
merge_sorted_revisions])
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):
182
for revno, rev_id in cut_revs:
183
if verbose or specific_fileid:
184
delta = branch.get_revision_delta(revno)
187
if not delta.touches_file_id(specific_fileid):
191
# although we calculated it, throw it away without display
194
rev = branch.get_revision(rev_id)
243
197
if not searchRE.search(rev.message):
247
# a mainline revision.
248
if verbose or specific_fileid:
249
delta = _get_revision_delta(branch, rev_nos[rev_id])
252
if not delta.touches_file_id(specific_fileid):
256
# although we calculated it, throw it away without display
200
lf.show(revno, rev, delta)
259
lf.show(rev_nos[rev_id], rev, delta)
260
elif hasattr(lf, 'show_merge'):
261
lf.show_merge(rev, merge_depth)
264
204
def deltas_for_log_dummy(branch, which_revs):
346
286
class LogFormatter(object):
347
287
"""Abstract class to display log messages."""
349
288
def __init__(self, to_file, show_ids=False, show_timezone='original'):
350
289
self.to_file = to_file
351
290
self.show_ids = show_ids
352
291
self.show_timezone = show_timezone
354
294
def show(self, revno, rev, delta):
355
295
raise NotImplementedError('not implemented in abstract base')
357
def short_committer(self, rev):
358
return re.sub('<.*@.*>', '', rev.committer).strip(' ')
361
302
class LongLogFormatter(LogFormatter):
362
303
def show(self, revno, rev, delta):
363
return self._show_helper(revno=revno, rev=rev, delta=delta)
365
def show_merge(self, rev, merge_depth):
366
return self._show_helper(rev=rev, indent=' '*merge_depth, merged=True, delta=None)
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
304
from osutils import format_date
371
306
to_file = self.to_file
372
print >>to_file, indent+'-' * 60
373
if revno is not None:
374
print >>to_file, 'revno:', revno
376
print >>to_file, indent+'merged:', rev.revision_id
378
print >>to_file, indent+'revision-id:', rev.revision_id
308
print >>to_file, '-' * 60
309
print >>to_file, 'revno:', revno
379
310
if self.show_ids:
380
for parent_id in rev.parent_ids:
381
print >>to_file, indent+'parent:', parent_id
382
print >>to_file, indent+'committer:', rev.committer
384
print >>to_file, indent+'branch nick: %s' % \
385
rev.properties['branch-nick']
311
print >>to_file, 'revision-id:', rev.revision_id
313
for parent in rev.parents:
314
print >>to_file, 'parent:', parent.revision_id
316
print >>to_file, 'committer:', rev.committer
388
318
date_str = format_date(rev.timestamp,
389
319
rev.timezone or 0,
390
320
self.show_timezone)
391
print >>to_file, indent+'timestamp: %s' % date_str
321
print >>to_file, 'timestamp: %s' % date_str
393
print >>to_file, indent+'message:'
323
print >>to_file, 'message:'
394
324
if not rev.message:
395
print >>to_file, indent+' (no message)'
325
print >>to_file, ' (no message)'
397
message = rev.message.rstrip('\r\n')
398
for l in message.split('\n'):
399
print >>to_file, indent+' ' + l
327
for l in rev.message.split('\n'):
328
print >>to_file, ' ' + l
400
330
if delta != None:
401
331
delta.show(to_file, self.show_ids)
404
335
class ShortLogFormatter(LogFormatter):
405
336
def show(self, revno, rev, delta):
406
337
from bzrlib.osutils import format_date
408
339
to_file = self.to_file
409
date_str = format_date(rev.timestamp, rev.timezone or 0,
411
print >>to_file, "%5d %s\t%s" % (revno, self.short_committer(rev),
341
print >>to_file, "%5d %s\t%s" % (revno, rev.committer,
412
342
format_date(rev.timestamp, rev.timezone or 0,
413
self.show_timezone, date_fmt="%Y-%m-%d",
415
344
if self.show_ids:
416
345
print >>to_file, ' revision-id:', rev.revision_id
417
346
if not rev.message:
418
347
print >>to_file, ' (no message)'
420
message = rev.message.rstrip('\r\n')
421
for l in message.split('\n'):
349
for l in rev.message.split('\n'):
422
350
print >>to_file, ' ' + l
424
352
# TODO: Why not show the modified files in a shorter form as
425
353
# well? rewrap them single lines of appropriate length
426
354
if delta != None:
427
355
delta.show(to_file, self.show_ids)
431
class LineLogFormatter(LogFormatter):
432
def truncate(self, str, max_len):
433
if len(str) <= max_len:
435
return str[:max_len-3]+'...'
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",
443
def message(self, rev):
445
return '(no message)'
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)
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
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)
471
def line_log(rev, max_chars):
472
lf = LineLogFormatter(None)
473
return lf.log_string(None, rev, max_chars)
476
'long': LongLogFormatter,
360
FORMATTERS = {'long': LongLogFormatter,
477
361
'short': ShortLogFormatter,
478
'line': LineLogFormatter,
481
def register_formatter(name, formatter):
482
FORMATTERS[name] = formatter
484
365
def log_formatter(name, *args, **kwargs):
485
"""Construct a formatter from arguments.
487
name -- Name of the formatter to construct; currently 'long', 'short' and
488
'line' are supported.
490
366
from bzrlib.errors import BzrCommandError
492
369
return FORMATTERS[name](*args, **kwargs)
494
371
raise BzrCommandError("unknown log formatter: %r" % name)
496
373
def show_one_log(revno, rev, delta, verbose, to_file, show_timezone):
497
# deprecated; for compatibility
374
# deprecated; for compatability
498
375
lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
499
376
lf.show(revno, rev, delta)
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.
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
513
to_file = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
514
lf = log_formatter(log_format,
517
show_timezone='original')
519
# This is the first index which is different between
522
for i in xrange(max(len(new_rh),
526
or new_rh[i] != old_rh[i]):
531
to_file.write('Nothing seems to have changed\n')
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')
551
start_revision=base_idx+1,
552
end_revision=len(new_rh),