180
122
warn("not a LogFormatter instance: %r" % lf)
182
124
if specific_fileid:
183
mutter('get log for file_id %r', specific_fileid)
185
if search is not None:
187
searchRE = re.compile(search, re.IGNORECASE)
191
which_revs = _enumerate_history(branch)
193
if start_revision is None:
196
branch.check_real_revno(start_revision)
198
if end_revision is None:
199
end_revision = len(which_revs)
201
branch.check_real_revno(end_revision)
203
# list indexes are 0-based; revisions are 1-based
204
cut_revs = which_revs[(start_revision-1):(end_revision)]
206
if direction == 'reverse':
208
elif direction == 'forward':
211
raise ValueError('invalid direction %r' % direction)
213
revision_history = branch.revision_history()
214
for revno, rev_id in cut_revs:
215
if verbose or specific_fileid:
216
delta = _get_revision_delta(branch, revno)
125
mutter('get log for file_id %r' % specific_fileid)
127
which_revs = branch.enum_history(direction)
128
which_revs = [x for x in which_revs if (
129
(start_revision is None or x[0] >= start_revision)
130
and (end_revision is None or x[0] <= end_revision))]
132
if not (verbose or specific_fileid):
133
# no need to know what changed between revisions
134
with_deltas = deltas_for_log_dummy(branch, which_revs)
135
elif direction == 'reverse':
136
with_deltas = deltas_for_log_reverse(branch, which_revs)
138
with_deltas = deltas_for_log_forward(branch, which_revs)
140
for revno, rev, delta in with_deltas:
218
141
if specific_fileid:
219
142
if not delta.touches_file_id(specific_fileid):
223
146
# although we calculated it, throw it away without display
226
rev = branch.get_revision(rev_id)
229
if not searchRE.search(rev.message):
232
149
lf.show(revno, rev, delta)
233
if hasattr(lf, 'show_merge'):
237
# revno is 1 based, so -2 to get back 1 less.
238
excludes = set(branch.get_ancestry(revision_history[revno - 2]))
239
pending = list(rev.parent_ids)
241
rev_id = pending.pop()
242
if rev_id in excludes:
244
# prevent showing merged revs twice if they multi-path.
247
rev = branch.get_revision(rev_id)
248
except errors.NoSuchRevision:
250
pending.extend(rev.parent_ids)
254
153
def deltas_for_log_dummy(branch, which_revs):
255
"""Return all the revisions without intermediate deltas.
257
Useful for log commands that won't need the delta information.
260
154
for revno, revision_id in which_revs:
261
155
yield revno, branch.get_revision(revision_id), None
264
158
def deltas_for_log_reverse(branch, which_revs):
265
"""Compute deltas for display in latest-to-earliest order.
271
Sequence of (revno, revision_id) for the subset of history to examine
274
Sequence of (revno, rev, delta)
159
"""Compute deltas for display in reverse log.
161
Given a sequence of (revno, revision_id) pairs, return
276
164
The delta is from the given revision to the next one in the
277
165
sequence, which makes sense if the log is being displayed from
278
166
newest to oldest.
168
from tree import EmptyTree
169
from diff import compare_trees
280
171
last_revno = last_revision_id = last_tree = None
281
172
for revno, revision_id in which_revs:
282
173
this_tree = branch.revision_tree(revision_id)
336
226
class LogFormatter(object):
337
227
"""Abstract class to display log messages."""
338
def __init__(self, to_file, show_ids=False, show_timezone='original'):
228
def __init__(self, to_file, show_ids=False, show_timezone=False):
339
229
self.to_file = to_file
340
230
self.show_ids = show_ids
341
231
self.show_timezone = show_timezone
344
def show(self, revno, rev, delta):
345
raise NotImplementedError('not implemented in abstract base')
347
def short_committer(self, rev):
348
return re.sub('<.*@.*>', '', rev.committer).strip(' ')
351
238
class LongLogFormatter(LogFormatter):
352
239
def show(self, revno, rev, delta):
353
return self._show_helper(revno=revno, rev=rev, delta=delta)
355
def show_merge(self, rev):
356
return self._show_helper(rev=rev, indent=' ', merged=True, delta=None)
358
def _show_helper(self, rev=None, revno=None, indent='', merged=False, delta=None):
359
"""Show a revision, either merged or not."""
360
from bzrlib.osutils import format_date
240
from osutils import format_date
361
242
to_file = self.to_file
362
print >>to_file, indent+'-' * 60
363
if revno is not None:
364
print >>to_file, 'revno:', revno
366
print >>to_file, indent+'merged:', rev.revision_id
368
print >>to_file, indent+'revision-id:', rev.revision_id
244
print >>to_file, '-' * 60
245
print >>to_file, 'revno:', revno
369
246
if self.show_ids:
370
for parent_id in rev.parent_ids:
371
print >>to_file, indent+'parent:', parent_id
372
print >>to_file, indent+'committer:', rev.committer
374
print >>to_file, indent+'branch nick: %s' % \
375
rev.properties['branch-nick']
378
date_str = format_date(rev.timestamp,
381
print >>to_file, indent+'timestamp: %s' % date_str
247
print >>to_file, 'revision-id:', rev.revision_id
248
print >>to_file, 'committer:', rev.committer
249
print >>to_file, 'timestamp: %s' % (format_date(rev.timestamp, rev.timezone or 0,
383
print >>to_file, indent+'message:'
252
print >>to_file, 'message:'
384
253
if not rev.message:
385
print >>to_file, indent+' (no message)'
254
print >>to_file, ' (no message)'
387
message = rev.message.rstrip('\r\n')
388
for l in message.split('\n'):
389
print >>to_file, indent+' ' + l
256
for l in rev.message.split('\n'):
257
print >>to_file, ' ' + l
390
259
if delta != None:
391
260
delta.show(to_file, self.show_ids)
394
264
class ShortLogFormatter(LogFormatter):
395
265
def show(self, revno, rev, delta):
396
266
from bzrlib.osutils import format_date
398
268
to_file = self.to_file
399
date_str = format_date(rev.timestamp, rev.timezone or 0,
401
print >>to_file, "%5d %s\t%s" % (revno, self.short_committer(rev),
270
print >>to_file, "%5d %s\t%s" % (revno, rev.committer,
402
271
format_date(rev.timestamp, rev.timezone or 0,
403
self.show_timezone, date_fmt="%Y-%m-%d",
405
273
if self.show_ids:
406
274
print >>to_file, ' revision-id:', rev.revision_id
407
275
if not rev.message:
408
276
print >>to_file, ' (no message)'
410
message = rev.message.rstrip('\r\n')
411
for l in message.split('\n'):
278
for l in rev.message.split('\n'):
412
279
print >>to_file, ' ' + l
414
# TODO: Why not show the modified files in a shorter form as
415
# well? rewrap them single lines of appropriate length
416
281
if delta != None:
417
282
delta.show(to_file, self.show_ids)
420
class LineLogFormatter(LogFormatter):
421
def truncate(self, str, max_len):
422
if len(str) <= max_len:
424
return str[:max_len-3]+'...'
426
def date_string(self, rev):
427
from bzrlib.osutils import format_date
428
return format_date(rev.timestamp, rev.timezone or 0,
429
self.show_timezone, date_fmt="%Y-%m-%d",
432
def message(self, rev):
434
return '(no message)'
438
def show(self, revno, rev, delta):
439
print >> self.to_file, self.log_string(rev, 79)
441
def log_string(self, rev, max_chars):
442
out = [self.truncate(self.short_committer(rev), 20)]
443
out.append(self.date_string(rev))
444
out.append(self.message(rev).replace('\n', ' '))
445
return self.truncate(" ".join(out).rstrip('\n'), max_chars)
447
def line_log(rev, max_chars):
448
lf = LineLogFormatter(None)
449
return lf.log_string(rev, max_chars)
451
287
FORMATTERS = {'long': LongLogFormatter,
452
288
'short': ShortLogFormatter,
453
'line': LineLogFormatter,
457
292
def log_formatter(name, *args, **kwargs):
458
"""Construct a formatter from arguments.
460
name -- Name of the formatter to construct; currently 'long', 'short' and
461
'line' are supported.
463
293
from bzrlib.errors import BzrCommandError
465
296
return FORMATTERS[name](*args, **kwargs)
466
297
except IndexError:
467
298
raise BzrCommandError("unknown log formatter: %r" % name)
469
def show_one_log(revno, rev, delta, verbose, to_file, show_timezone):
470
# deprecated; for compatability
471
lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
472
lf.show(revno, rev, delta)
474
def show_changed_revisions(branch, old_rh, new_rh, to_file=None, log_format='long'):
475
"""Show the change in revision history comparing the old revision history to the new one.
477
:param branch: The branch where the revisions exist
478
:param old_rh: The old revision history
479
:param new_rh: The new revision history
480
:param to_file: A file to write the results to. If None, stdout will be used
486
to_file = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
487
lf = log_formatter(log_format,
490
show_timezone='original')
492
# This is the first index which is different between
495
for i in xrange(max(len(new_rh),
499
or new_rh[i] != old_rh[i]):
504
to_file.write('Nothing seems to have changed\n')
506
## TODO: It might be nice to do something like show_log
507
## and show the merged entries. But since this is the
508
## removed revisions, it shouldn't be as important
509
if base_idx < len(old_rh):
510
to_file.write('*'*60)
511
to_file.write('\nRemoved Revisions:\n')
512
for i in range(base_idx, len(old_rh)):
513
rev = branch.get_revision(old_rh[i])
514
lf.show(i+1, rev, None)
515
to_file.write('*'*60)
516
to_file.write('\n\n')
517
if base_idx < len(new_rh):
518
to_file.write('Added Revisions:\n')
524
start_revision=base_idx+1,
525
end_revision=len(new_rh),