/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: 2008-07-08 14:55:19 UTC
  • mfrom: (3530 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3532.
  • Revision ID: john@arbash-meinel.com-20080708145519-paqg4kjwbpgs2xmq
Merge bzr.dev 3530

Show diffs side-by-side

added added

removed removed

Lines of Context:
63
63
    config,
64
64
    lazy_regex,
65
65
    registry,
66
 
    symbol_versioning,
67
66
    )
68
67
from bzrlib.errors import (
69
68
    BzrCommandError,
73
72
    get_terminal_encoding,
74
73
    terminal_width,
75
74
    )
 
75
from bzrlib.repository import _strip_NULL_ghosts
76
76
from bzrlib.revision import (
77
77
    NULL_REVISION,
78
78
    )
79
79
from bzrlib.revisionspec import (
80
80
    RevisionInfo,
81
81
    )
82
 
from bzrlib.symbol_versioning import (
83
 
    deprecated_method,
84
 
    zero_seventeen,
85
 
    )
86
82
from bzrlib.trace import mutter
87
83
from bzrlib.tsort import (
88
84
    merge_sort,
193
189
    finally:
194
190
        branch.unlock()
195
191
 
 
192
 
196
193
def _show_log(branch,
197
194
             lf,
198
195
             specific_fileid=None,
208
205
 
209
206
    if specific_fileid:
210
207
        mutter('get log for file_id %r', specific_fileid)
211
 
 
 
208
    generate_merge_revisions = getattr(lf, 'supports_merge_revisions', False)
 
209
    allow_single_merge_revision = getattr(lf,
 
210
        'supports_single_merge_revision', False)
 
211
    view_revisions = calculate_view_revisions(branch, start_revision,
 
212
                                              end_revision, direction,
 
213
                                              specific_fileid,
 
214
                                              generate_merge_revisions,
 
215
                                              allow_single_merge_revision)
212
216
    if search is not None:
213
217
        searchRE = re.compile(search, re.IGNORECASE)
214
218
    else:
215
219
        searchRE = None
216
220
 
 
221
    rev_tag_dict = {}
 
222
    generate_tags = getattr(lf, 'supports_tags', False)
 
223
    if generate_tags:
 
224
        if branch.supports_tags():
 
225
            rev_tag_dict = branch.tags.get_reverse_tag_dict()
 
226
 
 
227
    generate_delta = verbose and getattr(lf, 'supports_delta', False)
 
228
 
 
229
    # now we just print all the revisions
 
230
    log_count = 0
 
231
    for (rev_id, revno, merge_depth), rev, delta in _iter_revisions(
 
232
        branch.repository, view_revisions, generate_delta):
 
233
        if searchRE:
 
234
            if not searchRE.search(rev.message):
 
235
                continue
 
236
 
 
237
        lr = LogRevision(rev, revno, merge_depth, delta,
 
238
                         rev_tag_dict.get(rev_id))
 
239
        lf.log_revision(lr)
 
240
        if limit:
 
241
            log_count += 1
 
242
            if log_count >= limit:
 
243
                break
 
244
 
 
245
 
 
246
def calculate_view_revisions(branch, start_revision, end_revision, direction,
 
247
                             specific_fileid, generate_merge_revisions,
 
248
                             allow_single_merge_revision):
 
249
    if (not generate_merge_revisions and start_revision is end_revision is
 
250
        None and direction == 'reverse' and specific_fileid is None):
 
251
        return _linear_view_revisions(branch)
 
252
 
217
253
    mainline_revs, rev_nos, start_rev_id, end_rev_id = \
218
254
        _get_mainline_revs(branch, start_revision, end_revision)
219
255
    if not mainline_revs:
220
 
        return
 
256
        return []
221
257
 
222
258
    if direction == 'reverse':
223
259
        start_rev_id, end_rev_id = end_rev_id, start_rev_id
224
 
        
225
 
    legacy_lf = getattr(lf, 'log_revision', None) is None
226
 
    if legacy_lf:
227
 
        # pre-0.17 formatters use show for mainline revisions.
228
 
        # how should we show merged revisions ?
229
 
        #   pre-0.11 api: show_merge
230
 
        #   0.11-0.16 api: show_merge_revno
231
 
        show_merge_revno = getattr(lf, 'show_merge_revno', None)
232
 
        show_merge = getattr(lf, 'show_merge', None)
233
 
        if show_merge is None and show_merge_revno is None:
234
 
            # no merged-revno support
235
 
            generate_merge_revisions = False
236
 
        else:
237
 
            generate_merge_revisions = True
238
 
        # tell developers to update their code
239
 
        symbol_versioning.warn('LogFormatters should provide log_revision '
240
 
            'instead of show and show_merge_revno since bzr 0.17.',
241
 
            DeprecationWarning, stacklevel=3)
242
 
    else:
243
 
        generate_merge_revisions = getattr(lf, 'supports_merge_revisions', 
244
 
                                           False)
 
260
 
245
261
    generate_single_revision = False
246
262
    if ((not generate_merge_revisions)
247
263
        and ((start_rev_id and (start_rev_id not in rev_nos))
248
264
            or (end_rev_id and (end_rev_id not in rev_nos)))):
249
265
        generate_single_revision = ((start_rev_id == end_rev_id)
250
 
            and getattr(lf, 'supports_single_merge_revision', False))
 
266
            and allow_single_merge_revision)
251
267
        if not generate_single_revision:
252
268
            raise BzrCommandError('Selected log formatter only supports '
253
269
                'mainline revisions.')
271
287
        min_depth = min([d for r,n,d in view_revisions])
272
288
        if min_depth != 0:
273
289
            view_revisions = [(r,n,d-min_depth) for r,n,d in view_revisions]
274
 
        
275
 
    rev_tag_dict = {}
276
 
    generate_tags = getattr(lf, 'supports_tags', False)
277
 
    if generate_tags:
278
 
        if branch.supports_tags():
279
 
            rev_tag_dict = branch.tags.get_reverse_tag_dict()
280
 
 
281
 
    generate_delta = verbose and getattr(lf, 'supports_delta', False)
282
 
 
283
 
    def iter_revisions():
 
290
    return view_revisions
 
291
 
 
292
 
 
293
def _linear_view_revisions(branch):
 
294
    start_revno, start_revision_id = branch.last_revision_info()
 
295
    repo = branch.repository
 
296
    revision_ids = repo.iter_reverse_revision_history(start_revision_id)
 
297
    for num, revision_id in enumerate(revision_ids):
 
298
        yield revision_id, str(start_revno - num), 0
 
299
 
 
300
 
 
301
def _iter_revisions(repository, view_revisions, generate_delta):
 
302
    num = 9
 
303
    view_revisions = iter(view_revisions)
 
304
    while True:
 
305
        cur_view_revisions = [d for x, d in zip(range(num), view_revisions)]
 
306
        if len(cur_view_revisions) == 0:
 
307
            break
 
308
        cur_deltas = {}
284
309
        # r = revision, n = revno, d = merge depth
285
 
        revision_ids = [r for r, n, d in view_revisions]
286
 
        num = 9
287
 
        repository = branch.repository
288
 
        while revision_ids:
289
 
            cur_deltas = {}
290
 
            revisions = repository.get_revisions(revision_ids[:num])
291
 
            if generate_delta:
292
 
                deltas = repository.get_deltas_for_revisions(revisions)
293
 
                cur_deltas = dict(izip((r.revision_id for r in revisions),
294
 
                                       deltas))
295
 
            for revision in revisions:
296
 
                yield revision, cur_deltas.get(revision.revision_id)
297
 
            revision_ids  = revision_ids[num:]
298
 
            num = min(int(num * 1.5), 200)
299
 
 
300
 
    # now we just print all the revisions
301
 
    log_count = 0
302
 
    for ((rev_id, revno, merge_depth), (rev, delta)) in \
303
 
         izip(view_revisions, iter_revisions()):
304
 
 
305
 
        if searchRE:
306
 
            if not searchRE.search(rev.message):
307
 
                continue
308
 
 
309
 
        if not legacy_lf:
310
 
            lr = LogRevision(rev, revno, merge_depth, delta,
311
 
                             rev_tag_dict.get(rev_id))
312
 
            lf.log_revision(lr)
313
 
        else:
314
 
            # support for legacy (pre-0.17) LogFormatters
315
 
            if merge_depth == 0:
316
 
                if generate_tags:
317
 
                    lf.show(revno, rev, delta, rev_tag_dict.get(rev_id))
318
 
                else:
319
 
                    lf.show(revno, rev, delta)
320
 
            else:
321
 
                if show_merge_revno is None:
322
 
                    lf.show_merge(rev, merge_depth)
323
 
                else:
324
 
                    if generate_tags:
325
 
                        lf.show_merge_revno(rev, merge_depth, revno,
326
 
                                            rev_tag_dict.get(rev_id))
327
 
                    else:
328
 
                        lf.show_merge_revno(rev, merge_depth, revno)
329
 
        if limit:
330
 
            log_count += 1
331
 
            if log_count >= limit:
332
 
                break
 
310
        revision_ids = [r for (r, n, d) in cur_view_revisions]
 
311
        revisions = repository.get_revisions(revision_ids)
 
312
        if generate_delta:
 
313
            deltas = repository.get_deltas_for_revisions(revisions)
 
314
            cur_deltas = dict(izip((r.revision_id for r in revisions),
 
315
                                   deltas))
 
316
        for view_data, revision in izip(cur_view_revisions, revisions):
 
317
            yield view_data, revision, cur_deltas.get(revision.revision_id)
 
318
        num = min(int(num * 1.5), 200)
333
319
 
334
320
 
335
321
def _get_mainline_revs(branch, start_revision, end_revision):
349
335
 
350
336
    :return: A (mainline_revs, rev_nos, start_rev_id, end_rev_id) tuple.
351
337
    """
352
 
    which_revs = _enumerate_history(branch)
353
 
    if not which_revs:
 
338
    branch_revno, branch_last_revision = branch.last_revision_info()
 
339
    if branch_revno == 0:
354
340
        return None, None, None, None
355
341
 
356
342
    # For mainline generation, map start_revision and end_revision to 
363
349
    if start_revision is None:
364
350
        start_revno = 1
365
351
    else:
366
 
        if isinstance(start_revision,RevisionInfo):
 
352
        if isinstance(start_revision, RevisionInfo):
367
353
            start_rev_id = start_revision.rev_id
368
354
            start_revno = start_revision.revno or 1
369
355
        else:
372
358
    
373
359
    end_rev_id = None
374
360
    if end_revision is None:
375
 
        end_revno = len(which_revs)
 
361
        end_revno = branch_revno
376
362
    else:
377
 
        if isinstance(end_revision,RevisionInfo):
 
363
        if isinstance(end_revision, RevisionInfo):
378
364
            end_rev_id = end_revision.rev_id
379
 
            end_revno = end_revision.revno or len(which_revs)
 
365
            end_revno = end_revision.revno or branch_revno
380
366
        else:
381
367
            branch.check_real_revno(end_revision)
382
368
            end_revno = end_revision
388
374
        raise BzrCommandError("Start revision must be older than "
389
375
                              "the end revision.")
390
376
 
391
 
    # list indexes are 0-based; revisions are 1-based
392
 
    cut_revs = which_revs[(start_revno-1):(end_revno)]
393
 
    if not cut_revs:
 
377
    if end_revno < start_revno:
394
378
        return None, None, None, None
 
379
    cur_revno = branch_revno
 
380
    rev_nos = {}
 
381
    mainline_revs = []
 
382
    for revision_id in branch.repository.iter_reverse_revision_history(
 
383
                        branch_last_revision):
 
384
        if cur_revno < start_revno:
 
385
            # We have gone far enough, but we always add 1 more revision
 
386
            rev_nos[revision_id] = cur_revno
 
387
            mainline_revs.append(revision_id)
 
388
            break
 
389
        if cur_revno <= end_revno:
 
390
            rev_nos[revision_id] = cur_revno
 
391
            mainline_revs.append(revision_id)
 
392
        cur_revno -= 1
 
393
    else:
 
394
        # We walked off the edge of all revisions, so we add a 'None' marker
 
395
        mainline_revs.append(None)
395
396
 
396
 
    # convert the revision history to a dictionary:
397
 
    rev_nos = dict((k, v) for v, k in cut_revs)
 
397
    mainline_revs.reverse()
398
398
 
399
399
    # override the mainline to look like the revision history.
400
 
    mainline_revs = [revision_id for index, revision_id in cut_revs]
401
 
    if cut_revs[0][0] == 1:
402
 
        mainline_revs.insert(0, None)
403
 
    else:
404
 
        mainline_revs.insert(0, which_revs[start_revno-2][1])
405
400
    return mainline_revs, rev_nos, start_rev_id, end_rev_id
406
401
 
407
402
 
469
464
    :return: A list of (revision_id, dotted_revno, merge_depth) tuples.
470
465
    """
471
466
    # find all the revisions that change the specific file
472
 
    file_weave = branch.repository.weave_store.get_weave(file_id,
473
 
                branch.repository.get_transaction())
474
 
    weave_modifed_revisions = set(file_weave.versions())
475
467
    # build the ancestry of each revision in the graph
476
468
    # - only listing the ancestors that change the specific file.
477
 
    rev_graph = branch.repository.get_revision_graph(mainline_revisions[-1])
478
 
    sorted_rev_list = topo_sort(rev_graph)
 
469
    graph = branch.repository.get_graph()
 
470
    # This asks for all mainline revisions, which means we only have to spider
 
471
    # sideways, rather than depth history. That said, its still size-of-history
 
472
    # and should be addressed.
 
473
    # mainline_revisions always includes an extra revision at the beginning, so
 
474
    # don't request it.
 
475
    parent_map = dict(((key, value) for key, value in
 
476
        graph.iter_ancestry(mainline_revisions[1:]) if value is not None))
 
477
    sorted_rev_list = topo_sort(parent_map.items())
 
478
    text_keys = [(file_id, rev_id) for rev_id in sorted_rev_list]
 
479
    modified_text_versions = branch.repository.texts.get_parent_map(text_keys)
479
480
    ancestry = {}
480
481
    for rev in sorted_rev_list:
481
 
        parents = rev_graph[rev]
482
 
        if rev not in weave_modifed_revisions and len(parents) == 1:
 
482
        text_key = (file_id, rev)
 
483
        parents = parent_map[rev]
 
484
        if text_key not in modified_text_versions and len(parents) == 1:
483
485
            # We will not be adding anything new, so just use a reference to
484
486
            # the parent ancestry.
485
487
            rev_ancestry = ancestry[parents[0]]
486
488
        else:
487
489
            rev_ancestry = set()
488
 
            if rev in weave_modifed_revisions:
 
490
            if text_key in modified_text_versions:
489
491
                rev_ancestry.add(rev)
490
492
            for parent in parents:
 
493
                if parent not in ancestry:
 
494
                    # parent is a Ghost, which won't be present in
 
495
                    # sorted_rev_list, but we may access it later, so create an
 
496
                    # empty node for it
 
497
                    ancestry[parent] = set()
491
498
                rev_ancestry = rev_ancestry.union(ancestry[parent])
492
499
        ancestry[rev] = rev_ancestry
493
500
 
494
501
    def is_merging_rev(r):
495
 
        parents = rev_graph[r]
 
502
        parents = parent_map[r]
496
503
        if len(parents) > 1:
497
504
            leftparent = parents[0]
498
505
            for rightparent in parents[1:]:
504
511
    # filter from the view the revisions that did not change or merge 
505
512
    # the specific file
506
513
    return [(r, n, d) for r, n, d in view_revs_iter
507
 
            if r in weave_modifed_revisions or is_merging_rev(r)]
 
514
            if (file_id, r) in modified_text_versions or is_merging_rev(r)]
508
515
 
509
516
 
510
517
def get_view_revisions(mainline_revs, rev_nos, branch, direction,
520
527
        for revision_id in revision_ids:
521
528
            yield revision_id, str(rev_nos[revision_id]), 0
522
529
        return
 
530
    graph = branch.repository.get_graph()
 
531
    # This asks for all mainline revisions, which means we only have to spider
 
532
    # sideways, rather than depth history. That said, its still size-of-history
 
533
    # and should be addressed.
 
534
    # mainline_revisions always includes an extra revision at the beginning, so
 
535
    # don't request it.
 
536
    parent_map = dict(((key, value) for key, value in
 
537
        graph.iter_ancestry(mainline_revs[1:]) if value is not None))
 
538
    # filter out ghosts; merge_sort errors on ghosts.
 
539
    rev_graph = _strip_NULL_ghosts(parent_map)
523
540
    merge_sorted_revisions = merge_sort(
524
 
        branch.repository.get_revision_graph(mainline_revs[-1]),
 
541
        rev_graph,
525
542
        mainline_revs[-1],
526
543
        mainline_revs,
527
544
        generate_revno=True)
548
565
        if val[2] == _depth:
549
566
            zd_revisions.append([val])
550
567
        else:
551
 
            assert val[2] > _depth
552
568
            zd_revisions[-1].append(val)
553
569
    for revisions in zd_revisions:
554
570
        if len(revisions) > 1:
616
632
#        """
617
633
#        raise NotImplementedError('not implemented in abstract base')
618
634
 
619
 
    @deprecated_method(zero_seventeen)
620
 
    def show(self, revno, rev, delta):
621
 
        raise NotImplementedError('not implemented in abstract base')
622
 
 
623
635
    def short_committer(self, rev):
624
636
        name, address = config.parse_username(rev.committer)
625
637
        if name:
639
651
    supports_delta = True
640
652
    supports_tags = True
641
653
 
642
 
    @deprecated_method(zero_seventeen)
643
 
    def show(self, revno, rev, delta, tags=None):
644
 
        lr = LogRevision(rev, revno, 0, delta, tags)
645
 
        return self.log_revision(lr)
646
 
 
647
 
    @deprecated_method(zero_seventeen)
648
 
    def show_merge_revno(self, rev, merge_depth, revno, tags=None):
649
 
        """Show a merged revision rev, with merge_depth and a revno."""
650
 
        lr = LogRevision(rev, revno, merge_depth, tags=tags)
651
 
        return self.log_revision(lr)
652
 
 
653
654
    def log_revision(self, revision):
654
655
        """Log a revision, either merged or not."""
655
656
        indent = '    ' * revision.merge_depth
695
696
    supports_delta = True
696
697
    supports_single_merge_revision = True
697
698
 
698
 
    @deprecated_method(zero_seventeen)
699
 
    def show(self, revno, rev, delta):
700
 
        lr = LogRevision(rev, revno, 0, delta)
701
 
        return self.log_revision(lr)
702
 
 
703
699
    def log_revision(self, revision):
704
700
        to_file = self.to_file
705
 
        date_str = format_date(revision.rev.timestamp,
706
 
                               revision.rev.timezone or 0,
707
 
                               self.show_timezone)
708
701
        is_merge = ''
709
702
        if len(revision.rev.parent_ids) > 1:
710
703
            is_merge = ' [merge]'
755
748
        else:
756
749
            return rev.message
757
750
 
758
 
    @deprecated_method(zero_seventeen)
759
 
    def show(self, revno, rev, delta):
760
 
        self.to_file.write(self.log_string(revno, rev, terminal_width()-1))
761
 
        self.to_file.write('\n')
762
 
 
763
751
    def log_revision(self, revision):
764
752
        self.to_file.write(self.log_string(revision.revno, revision.rev,
765
753
                                              self._max_chars))