mercurial/cmdutil.py
changeset 35685 659dfbd852e2
parent 35684 1c929b4942a3
child 35686 b25fa5da4ca2
equal deleted inserted replaced
35684:1c929b4942a3 35685:659dfbd852e2
  2320             if stopiteration:
  2320             if stopiteration:
  2321                 break
  2321                 break
  2322 
  2322 
  2323     return iterate()
  2323     return iterate()
  2324 
  2324 
       
  2325 def _makelogmatcher(repo, pats, opts):
       
  2326     """Build matcher and expanded patterns from log options
       
  2327 
       
  2328     Returns (match, pats, slowpath) where
       
  2329     - match: a matcher built from the given pats and -I/-X opts
       
  2330     - pats: patterns used (globs are expanded on Windows)
       
  2331     - slowpath: True if patterns aren't as simple as scanning filelogs
       
  2332     """
       
  2333     # pats/include/exclude are passed to match.match() directly in
       
  2334     # _matchfiles() revset but walkchangerevs() builds its matcher with
       
  2335     # scmutil.match(). The difference is input pats are globbed on
       
  2336     # platforms without shell expansion (windows).
       
  2337     wctx = repo[None]
       
  2338     match, pats = scmutil.matchandpats(wctx, pats, opts)
       
  2339     slowpath = match.anypats() or (not match.always() and opts.get('removed'))
       
  2340     if not slowpath:
       
  2341         follow = opts.get('follow') or opts.get('follow_first')
       
  2342         for f in match.files():
       
  2343             if follow and f not in wctx:
       
  2344                 # If the file exists, it may be a directory, so let it
       
  2345                 # take the slow path.
       
  2346                 if os.path.exists(repo.wjoin(f)):
       
  2347                     slowpath = True
       
  2348                     continue
       
  2349                 else:
       
  2350                     raise error.Abort(_('cannot follow file not in parent '
       
  2351                                         'revision: "%s"') % f)
       
  2352             filelog = repo.file(f)
       
  2353             if not filelog:
       
  2354                 # A zero count may be a directory or deleted file, so
       
  2355                 # try to find matching entries on the slow path.
       
  2356                 if follow:
       
  2357                     raise error.Abort(
       
  2358                         _('cannot follow nonexistent file: "%s"') % f)
       
  2359                 slowpath = True
       
  2360 
       
  2361         # We decided to fall back to the slowpath because at least one
       
  2362         # of the paths was not a file. Check to see if at least one of them
       
  2363         # existed in history - in that case, we'll continue down the
       
  2364         # slowpath; otherwise, we can turn off the slowpath
       
  2365         if slowpath:
       
  2366             for path in match.files():
       
  2367                 if path == '.' or path in repo.store:
       
  2368                     break
       
  2369             else:
       
  2370                 slowpath = False
       
  2371 
       
  2372     return match, pats, slowpath
       
  2373 
  2325 def _makefollowlogfilematcher(repo, files, followfirst):
  2374 def _makefollowlogfilematcher(repo, files, followfirst):
  2326     # When displaying a revision with --patch --follow FILE, we have
  2375     # When displaying a revision with --patch --follow FILE, we have
  2327     # to know which file of the revision must be diffed. With
  2376     # to know which file of the revision must be diffed. With
  2328     # --follow, we want the names of the ancestors of FILE in the
  2377     # --follow, we want the names of the ancestors of FILE in the
  2329     # revision, stored in "fcache". "fcache" is populated by
  2378     # revision, stored in "fcache". "fcache" is populated by
  2368     'keyword':          ('keyword(%s)', '%lr'),
  2417     'keyword':          ('keyword(%s)', '%lr'),
  2369     'prune':            ('ancestors(%s)', 'not %lr'),
  2418     'prune':            ('ancestors(%s)', 'not %lr'),
  2370     'user':             ('user(%s)', '%lr'),
  2419     'user':             ('user(%s)', '%lr'),
  2371 }
  2420 }
  2372 
  2421 
  2373 def _makelogrevset(repo, pats, opts):
  2422 def _makelogrevset(repo, match, pats, slowpath, opts):
  2374     """Return (expr, filematcher) where expr is a revset string built
  2423     """Return (expr, filematcher) where expr is a revset string built
  2375     from log options and file patterns or None. If --stat or --patch
  2424     from log options and file patterns or None. If --stat or --patch
  2376     are not passed filematcher is None. Otherwise it is a callable
  2425     are not passed filematcher is None. Otherwise it is a callable
  2377     taking a revision number and returning a match objects filtering
  2426     taking a revision number and returning a match objects filtering
  2378     the files to be detailed when displaying the revision.
  2427     the files to be detailed when displaying the revision.
  2387 
  2436 
  2388     # branch and only_branch are really aliases and must be handled at
  2437     # branch and only_branch are really aliases and must be handled at
  2389     # the same time
  2438     # the same time
  2390     opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
  2439     opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
  2391     opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
  2440     opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
  2392     # pats/include/exclude are passed to match.match() directly in
       
  2393     # _matchfiles() revset but walkchangerevs() builds its matcher with
       
  2394     # scmutil.match(). The difference is input pats are globbed on
       
  2395     # platforms without shell expansion (windows).
       
  2396     wctx = repo[None]
       
  2397     match, pats = scmutil.matchandpats(wctx, pats, opts)
       
  2398     slowpath = match.anypats() or (not match.always() and opts.get('removed'))
       
  2399     if not slowpath:
       
  2400         for f in match.files():
       
  2401             if follow and f not in wctx:
       
  2402                 # If the file exists, it may be a directory, so let it
       
  2403                 # take the slow path.
       
  2404                 if os.path.exists(repo.wjoin(f)):
       
  2405                     slowpath = True
       
  2406                     continue
       
  2407                 else:
       
  2408                     raise error.Abort(_('cannot follow file not in parent '
       
  2409                                        'revision: "%s"') % f)
       
  2410             filelog = repo.file(f)
       
  2411             if not filelog:
       
  2412                 # A zero count may be a directory or deleted file, so
       
  2413                 # try to find matching entries on the slow path.
       
  2414                 if follow:
       
  2415                     raise error.Abort(
       
  2416                         _('cannot follow nonexistent file: "%s"') % f)
       
  2417                 slowpath = True
       
  2418 
       
  2419         # We decided to fall back to the slowpath because at least one
       
  2420         # of the paths was not a file. Check to see if at least one of them
       
  2421         # existed in history - in that case, we'll continue down the
       
  2422         # slowpath; otherwise, we can turn off the slowpath
       
  2423         if slowpath:
       
  2424             for path in match.files():
       
  2425                 if path == '.' or path in repo.store:
       
  2426                     break
       
  2427             else:
       
  2428                 slowpath = False
       
  2429 
  2441 
  2430     fpats = ('_patsfollow', '_patsfollowfirst')
  2442     fpats = ('_patsfollow', '_patsfollowfirst')
  2431     fnopats = ('_ancestors', '_fancestors')
  2443     fnopats = ('_ancestors', '_fancestors')
  2432 
  2444 
  2433     if slowpath:
  2445     if slowpath:
  2526         opts.pop('follow_first', None)
  2538         opts.pop('follow_first', None)
  2527     limit = loglimit(opts)
  2539     limit = loglimit(opts)
  2528     revs = _logrevs(repo, opts)
  2540     revs = _logrevs(repo, opts)
  2529     if not revs:
  2541     if not revs:
  2530         return smartset.baseset(), None
  2542         return smartset.baseset(), None
       
  2543     match, pats, slowpath = _makelogmatcher(repo, pats, opts)
  2531     if opts.get('rev') and follow:
  2544     if opts.get('rev') and follow:
  2532         revs = dagop.revancestors(repo, revs, followfirst=followfirst)
  2545         revs = dagop.revancestors(repo, revs, followfirst=followfirst)
  2533         revs.reverse()
  2546         revs.reverse()
  2534     expr, filematcher = _makelogrevset(repo, pats, opts)
  2547     expr, filematcher = _makelogrevset(repo, match, pats, slowpath, opts)
  2535     if opts.get('graph') and opts.get('rev'):
  2548     if opts.get('graph') and opts.get('rev'):
  2536         # User-specified revs might be unsorted, but don't sort before
  2549         # User-specified revs might be unsorted, but don't sort before
  2537         # _makelogrevset because it might depend on the order of revs
  2550         # _makelogrevset because it might depend on the order of revs
  2538         if not (revs.isdescending() or revs.istopo()):
  2551         if not (revs.isdescending() or revs.istopo()):
  2539             revs.sort(reverse=True)
  2552             revs.sort(reverse=True)