2583 limitedrevs.append(r) |
2585 limitedrevs.append(r) |
2584 revs = smartset.baseset(limitedrevs) |
2586 revs = smartset.baseset(limitedrevs) |
2585 |
2587 |
2586 return revs, expr, filematcher |
2588 return revs, expr, filematcher |
2587 |
2589 |
|
2590 def _parselinerangelogopt(repo, opts): |
|
2591 """Parse --line-range log option and return a list of tuples (filename, |
|
2592 (fromline, toline)). |
|
2593 """ |
|
2594 linerangebyfname = [] |
|
2595 for pat in opts.get('line_range', []): |
|
2596 try: |
|
2597 pat, linerange = pat.rsplit(',', 1) |
|
2598 except ValueError: |
|
2599 raise error.Abort(_('malformatted line-range pattern %s') % pat) |
|
2600 try: |
|
2601 fromline, toline = map(int, linerange.split('-')) |
|
2602 except ValueError: |
|
2603 raise error.Abort(_("invalid line range for %s") % pat) |
|
2604 msg = _("line range pattern '%s' must match exactly one file") % pat |
|
2605 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg) |
|
2606 linerangebyfname.append( |
|
2607 (fname, util.processlinerange(fromline, toline))) |
|
2608 return linerangebyfname |
|
2609 |
|
2610 def getloglinerangerevs(repo, userrevs, opts): |
|
2611 """Return (revs, filematcher, hunksfilter). |
|
2612 |
|
2613 "revs" are revisions obtained by processing "line-range" log options and |
|
2614 walking block ancestors of each specified file/line-range. |
|
2615 |
|
2616 "filematcher(rev) -> match" is a factory function returning a match object |
|
2617 for a given revision for file patterns specified in --line-range option. |
|
2618 If neither --stat nor --patch options are passed, "filematcher" is None. |
|
2619 |
|
2620 "hunksfilter(rev) -> filterfn(fctx, hunks)" is a factory function |
|
2621 returning a hunks filtering function. |
|
2622 If neither --stat nor --patch options are passed, "filterhunks" is None. |
|
2623 """ |
|
2624 wctx = repo[None] |
|
2625 |
|
2626 # Two-levels map of "rev -> file ctx -> [line range]". |
|
2627 linerangesbyrev = {} |
|
2628 for fname, (fromline, toline) in _parselinerangelogopt(repo, opts): |
|
2629 fctx = wctx.filectx(fname) |
|
2630 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline): |
|
2631 rev = fctx.introrev() |
|
2632 if rev not in userrevs: |
|
2633 continue |
|
2634 linerangesbyrev.setdefault( |
|
2635 rev, {}).setdefault( |
|
2636 fctx.path(), []).append(linerange) |
|
2637 |
|
2638 filematcher = None |
|
2639 hunksfilter = None |
|
2640 if opts.get('patch') or opts.get('stat'): |
|
2641 |
|
2642 def nofilterhunksfn(fctx, hunks): |
|
2643 return hunks |
|
2644 |
|
2645 def hunksfilter(rev): |
|
2646 fctxlineranges = linerangesbyrev.get(rev) |
|
2647 if fctxlineranges is None: |
|
2648 return nofilterhunksfn |
|
2649 |
|
2650 def filterfn(fctx, hunks): |
|
2651 lineranges = fctxlineranges.get(fctx.path()) |
|
2652 if lineranges is not None: |
|
2653 for hr, lines in hunks: |
|
2654 if any(mdiff.hunkinrange(hr[2:], lr) |
|
2655 for lr in lineranges): |
|
2656 yield hr, lines |
|
2657 else: |
|
2658 for hunk in hunks: |
|
2659 yield hunk |
|
2660 |
|
2661 return filterfn |
|
2662 |
|
2663 def filematcher(rev): |
|
2664 files = list(linerangesbyrev.get(rev, [])) |
|
2665 return scmutil.matchfiles(repo, files) |
|
2666 |
|
2667 revs = sorted(linerangesbyrev, reverse=True) |
|
2668 |
|
2669 return revs, filematcher, hunksfilter |
|
2670 |
2588 def _graphnodeformatter(ui, displayer): |
2671 def _graphnodeformatter(ui, displayer): |
2589 spec = ui.config('ui', 'graphnodetemplate') |
2672 spec = ui.config('ui', 'graphnodetemplate') |
2590 if not spec: |
2673 if not spec: |
2591 return templatekw.showgraphnode # fast path for "{graphnode}" |
2674 return templatekw.showgraphnode # fast path for "{graphnode}" |
2592 |
2675 |