mercurial/cmdutil.py
changeset 34857 84c6b9384d6a
parent 34856 890afefa7296
child 34893 068e0e531584
equal deleted inserted replaced
34856:890afefa7296 34857:84c6b9384d6a
    24 from . import (
    24 from . import (
    25     bookmarks,
    25     bookmarks,
    26     changelog,
    26     changelog,
    27     copies,
    27     copies,
    28     crecord as crecordmod,
    28     crecord as crecordmod,
       
    29     dagop,
    29     dirstateguard,
    30     dirstateguard,
    30     encoding,
    31     encoding,
    31     error,
    32     error,
    32     formatter,
    33     formatter,
    33     graphmod,
    34     graphmod,
    34     match as matchmod,
    35     match as matchmod,
       
    36     mdiff,
    35     obsolete,
    37     obsolete,
    36     patch,
    38     patch,
    37     pathutil,
    39     pathutil,
    38     pycompat,
    40     pycompat,
    39     registrar,
    41     registrar,
  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