53
53
# TODO: option to show delta summaries for merged-in revisions
55
import bzrlib.errors as errors
56
from bzrlib.tree import EmptyTree
56
57
from bzrlib.delta import compare_trees
57
import bzrlib.errors as errors
58
58
from bzrlib.trace import mutter
59
from bzrlib.tree import EmptyTree
60
from bzrlib.tsort import merge_sort
63
62
def find_touching_revisions(branch, file_id):
204
203
# list indexes are 0-based; revisions are 1-based
205
204
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
206
if direction == 'reverse':
221
207
cut_revs.reverse()
222
208
elif direction == 'forward':
223
# forward means oldest first.
224
merge_sorted_revisions.reverse()
226
211
raise ValueError('invalid direction %r' % direction)
228
213
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
# now we just print all the revisions
236
for sequence, rev_id, merge_depth, end_of_merge in merge_sorted_revisions:
214
for revno, rev_id in cut_revs:
215
if verbose or specific_fileid:
216
delta = _get_revision_delta(branch, revno)
219
if not delta.touches_file_id(specific_fileid):
223
# although we calculated it, throw it away without display
237
226
rev = branch.repository.get_revision(rev_id)
240
229
if not searchRE.search(rev.message):
244
# a mainline revision.
245
if verbose or specific_fileid:
246
delta = _get_revision_delta(branch, rev_nos[rev_id])
249
if not delta.touches_file_id(specific_fileid):
253
# although we calculated it, throw it away without display
256
lf.show(rev_nos[rev_id], rev, delta)
257
elif hasattr(lf, 'show_merge'):
258
lf.show_merge(rev, merge_depth)
232
lf.show(revno, rev, delta)
233
if hasattr(lf, 'show_merge'):
237
# revno is 1 based, so -2 to get back 1 less.
238
repository = branch.repository
239
excludes = repository.get_ancestry(revision_history[revno - 2])
240
excludes = set(excludes)
241
pending = list(rev.parent_ids)
243
rev_id = pending.pop()
244
if rev_id in excludes:
246
# prevent showing merged revs twice if they multi-path.
249
rev = branch.repository.get_revision(rev_id)
250
except errors.NoSuchRevision:
252
pending.extend(rev.parent_ids)
261
256
def deltas_for_log_dummy(branch, which_revs):
343
338
class LogFormatter(object):
344
339
"""Abstract class to display log messages."""
346
340
def __init__(self, to_file, show_ids=False, show_timezone='original'):
347
341
self.to_file = to_file
348
342
self.show_ids = show_ids
349
343
self.show_timezone = show_timezone
351
346
def show(self, revno, rev, delta):
352
347
raise NotImplementedError('not implemented in abstract base')
359
354
def show(self, revno, rev, delta):
360
355
return self._show_helper(revno=revno, rev=rev, delta=delta)
362
def show_merge(self, rev, merge_depth):
363
return self._show_helper(rev=rev, indent=' '*merge_depth, merged=True, delta=None)
357
def show_merge(self, rev):
358
return self._show_helper(rev=rev, indent=' ', merged=True, delta=None)
365
360
def _show_helper(self, rev=None, revno=None, indent='', merged=False, delta=None):
366
"""Show a revision, either merged or not."""
361
"""Show a revision, either merged or not."""
367
362
from bzrlib.osutils import format_date
368
363
to_file = self.to_file
369
364
print >>to_file, indent+'-' * 60
444
438
return rev.message
446
440
def show(self, revno, rev, delta):
447
from bzrlib.osutils import terminal_width
448
print >> self.to_file, self.log_string(revno, rev, terminal_width()-1)
441
print >> self.to_file, self.log_string(rev, 79)
450
def log_string(self, revno, rev, max_chars):
451
"""Format log info into one string. Truncate tail of string
452
:param revno: revision number (int) or None.
453
Revision numbers counts from 1.
454
:param rev: revision info object
455
:param max_chars: maximum length of resulting string
456
:return: formatted truncated string
460
# show revno only when is not None
461
out.append("%d:" % revno)
462
out.append(self.truncate(self.short_committer(rev), 20))
443
def log_string(self, rev, max_chars):
444
out = [self.truncate(self.short_committer(rev), 20)]
463
445
out.append(self.date_string(rev))
464
446
out.append(self.message(rev).replace('\n', ' '))
465
447
return self.truncate(" ".join(out).rstrip('\n'), max_chars)
468
449
def line_log(rev, max_chars):
469
450
lf = LineLogFormatter(None)
470
return lf.log_string(None, rev, max_chars)
451
return lf.log_string(rev, max_chars)
473
'long': LongLogFormatter,
453
FORMATTERS = {'long': LongLogFormatter,
474
454
'short': ShortLogFormatter,
475
455
'line': LineLogFormatter,
478
def register_formatter(name, formatter):
479
FORMATTERS[name] = formatter
481
459
def log_formatter(name, *args, **kwargs):
482
460
"""Construct a formatter from arguments.