54
54
from itertools import izip
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(
59
from bzrlib.tsort import merge_sort
70
62
def find_touching_revisions(branch, file_id):
210
202
mainline_revs.insert(0, None)
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
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)
230
view_revisions = _get_revisions_touching_file_id(branch,
235
view_revisions = list(view_revs_iter)
237
use_tags = getattr(lf, 'supports_tags', False)
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
208
include_merges = False
209
view_revisions = list(get_view_revisions(mainline_revs, rev_nos, branch,
210
direction, include_merges=include_merges))
243
212
def iter_revisions():
244
213
# r = revision, n = revno, d = merge depth
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)
266
235
# now we just print all the revisions
267
236
for ((rev_id, revno, merge_depth), (rev, delta)) in \
274
243
if merge_depth == 0:
276
lf.show(revno, rev, delta, rev_tag_dict.get(rev_id))
278
lf.show(revno, rev, delta)
280
if show_merge_revno is None:
281
lf.show_merge(rev, merge_depth)
284
lf.show_merge_revno(rev, merge_depth, revno,
285
rev_tag_dict.get(rev_id))
287
lf.show_merge_revno(rev, merge_depth, revno)
290
def _get_revisions_touching_file_id(branch, file_id, mainline_revisions,
292
"""Return the list of revision ids which touch a given file id.
294
This includes the revisions which directly change the file id,
295
and the revisions which merge these changes. So if the
303
And 'C' changes a file, then both C and D will be returned.
305
This will also can be restricted based on a subset of the mainline.
307
:return: A list of (revision_id, dotted_revno, merge_depth) tuples.
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)
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]]
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
332
def is_merging_rev(r):
333
parents = rev_graph[r]
335
leftparent = parents[0]
336
for rightparent in parents[1:]:
337
if not ancestry[leftparent].issuperset(
338
ancestry[rightparent]):
342
# filter from the view the revisions that did not change or merge
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.
247
if not delta.touches_file_id(specific_fileid):
251
# although we calculated it, throw it away without display
254
lf.show(revno, rev, delta)
256
lf.show_merge(rev, merge_depth)
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
361
272
merge_sorted_revisions = merge_sort(
362
273
branch.repository.get_revision_graph(mainline_revs[-1]),
363
274
mainline_revs[-1],
367
277
if direction == 'forward':
368
278
# forward means oldest first.
370
280
elif direction != 'reverse':
371
281
raise ValueError('invalid direction %r' % direction)
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()
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
377
289
def reverse_by_depth(merge_sorted_revisions, _depth=0):
412
324
def short_committer(self, rev):
413
325
return re.sub('<.*@.*>', '', rev.committer).strip(' ')
416
328
class LongLogFormatter(LogFormatter):
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
423
def show(self, revno, rev, delta, tags=None):
424
return self._show_helper(revno=revno, rev=rev, delta=delta, tags=tags)
426
@deprecated_method(zero_eleven)
329
def show(self, revno, rev, delta):
330
return self._show_helper(revno=revno, rev=rev, delta=delta)
427
332
def show_merge(self, rev, merge_depth):
428
return self._show_helper(rev=rev, indent=' '*merge_depth,
429
merged=True, delta=None)
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)
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)
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
445
print >>to_file, indent+'tags: %s' % (', '.join(tags))
341
print >>to_file, 'revno:', revno
447
343
print >>to_file, indent+'merged:', rev.revision_id
448
344
elif self.show_ids:
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))
544
439
lf = LineLogFormatter(None)
545
440
return lf.log_string(None, rev, max_chars)
548
class LogFormatterRegistry(registry.Registry):
549
"""Registry for log formatters"""
551
def make_formatter(self, name, *args, **kwargs):
552
"""Construct a formatter from arguments.
554
:param name: Name of the formatter to construct. 'short', 'long' and
557
return self.get(name)(*args, **kwargs)
559
def get_default(self, branch):
560
return self.get(branch.get_config().log_format())
563
log_formatter_registry = LogFormatterRegistry()
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')
443
'long': LongLogFormatter,
444
'short': ShortLogFormatter,
445
'line': LineLogFormatter,
574
448
def register_formatter(name, formatter):
575
log_formatter_registry.register(name, formatter)
449
FORMATTERS[name] = formatter
578
451
def log_formatter(name, *args, **kwargs):
579
452
"""Construct a formatter from arguments.
584
457
from bzrlib.errors import BzrCommandError
586
return log_formatter_registry.make_formatter(name, *args, **kwargs)
459
return FORMATTERS[name](*args, **kwargs)
588
461
raise BzrCommandError("unknown log formatter: %r" % name)
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)