mercurial/cmdutil.py
changeset 45652 0356b41fe01d
parent 45650 3a024d7cd08e
child 45653 c7413ffe0402
equal deleted inserted replaced
45651:bba730d7a6f4 45652:0356b41fe01d
  2426                 return True
  2426                 return True
  2427 
  2427 
  2428         return False
  2428         return False
  2429 
  2429 
  2430 
  2430 
  2431 def walkchangerevs(repo, match, opts, prepare):
  2431 def walkchangerevs(repo, revs, makefilematcher, prepare):
  2432     '''Iterate over files and the revs in which they changed.
  2432     '''Iterate over files and the revs in a "windowed" way.
  2433 
  2433 
  2434     Callers most commonly need to iterate backwards over the history
  2434     Callers most commonly need to iterate backwards over the history
  2435     in which they are interested. Doing so has awful (quadratic-looking)
  2435     in which they are interested. Doing so has awful (quadratic-looking)
  2436     performance, so we use iterators in a "windowed" way.
  2436     performance, so we use iterators in a "windowed" way.
  2437 
  2437 
  2441 
  2441 
  2442     This function returns an iterator yielding contexts. Before
  2442     This function returns an iterator yielding contexts. Before
  2443     yielding each context, the iterator will first call the prepare
  2443     yielding each context, the iterator will first call the prepare
  2444     function on each context in the window in forward order.'''
  2444     function on each context in the window in forward order.'''
  2445 
  2445 
  2446     allfiles = opts.get(b'all_files')
       
  2447     follow = opts.get(b'follow') or opts.get(b'follow_first')
       
  2448     revs = _walkrevs(repo, opts)
       
  2449     if not revs:
  2446     if not revs:
  2450         return []
  2447         return []
  2451     wanted = set()
       
  2452     slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
       
  2453     fncache = {}
       
  2454     change = repo.__getitem__
  2448     change = repo.__getitem__
  2455 
  2449 
  2456     # First step is to fill wanted, the set of revisions that we want to yield.
       
  2457     # When it does not induce extra cost, we also fill fncache for revisions in
       
  2458     # wanted: a cache of filenames that were changed (ctx.files()) and that
       
  2459     # match the file filtering conditions.
       
  2460 
       
  2461     if match.always() or allfiles:
       
  2462         # No files, no patterns.  Display all revs.
       
  2463         wanted = revs
       
  2464     elif not slowpath:
       
  2465         # We only have to read through the filelog to find wanted revisions
       
  2466 
       
  2467         try:
       
  2468             wanted = walkfilerevs(repo, match, follow, revs, fncache)
       
  2469         except FileWalkError:
       
  2470             slowpath = True
       
  2471 
       
  2472             # We decided to fall back to the slowpath because at least one
       
  2473             # of the paths was not a file. Check to see if at least one of them
       
  2474             # existed in history, otherwise simply return
       
  2475             for path in match.files():
       
  2476                 if path == b'.' or path in repo.store:
       
  2477                     break
       
  2478             else:
       
  2479                 return []
       
  2480 
       
  2481     if slowpath:
       
  2482         # We have to read the changelog to match filenames against
       
  2483         # changed files
       
  2484 
       
  2485         if follow:
       
  2486             raise error.Abort(
       
  2487                 _(b'can only follow copies/renames for explicit filenames')
       
  2488             )
       
  2489 
       
  2490         # The slow path checks files modified in every changeset.
       
  2491         # This is really slow on large repos, so compute the set lazily.
       
  2492         class lazywantedset(object):
       
  2493             def __init__(self):
       
  2494                 self.set = set()
       
  2495                 self.revs = set(revs)
       
  2496 
       
  2497             # No need to worry about locality here because it will be accessed
       
  2498             # in the same order as the increasing window below.
       
  2499             def __contains__(self, value):
       
  2500                 if value in self.set:
       
  2501                     return True
       
  2502                 elif not value in self.revs:
       
  2503                     return False
       
  2504                 else:
       
  2505                     self.revs.discard(value)
       
  2506                     ctx = change(value)
       
  2507                     if allfiles:
       
  2508                         matches = list(ctx.manifest().walk(match))
       
  2509                     else:
       
  2510                         matches = [f for f in ctx.files() if match(f)]
       
  2511                     if matches:
       
  2512                         fncache[value] = matches
       
  2513                         self.set.add(value)
       
  2514                         return True
       
  2515                     return False
       
  2516 
       
  2517             def discard(self, value):
       
  2518                 self.revs.discard(value)
       
  2519                 self.set.discard(value)
       
  2520 
       
  2521         wanted = lazywantedset()
       
  2522 
       
  2523     # it might be worthwhile to do this in the iterator if the rev range
       
  2524     # is descending and the prune args are all within that range
       
  2525     for rev in opts.get(b'prune', ()):
       
  2526         rev = repo[rev].rev()
       
  2527         ff = _followfilter(repo)
       
  2528         stop = min(revs[0], revs[-1])
       
  2529         for x in pycompat.xrange(rev, stop - 1, -1):
       
  2530             if ff.match(x):
       
  2531                 wanted = wanted - [x]
       
  2532 
       
  2533     # Now that wanted is correctly initialized, we can iterate over the
       
  2534     # revision range, yielding only revisions in wanted.
       
  2535     def iterate():
  2450     def iterate():
  2536         if follow and match.always():
       
  2537             ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
       
  2538 
       
  2539             def want(rev):
       
  2540                 return ff.match(rev) and rev in wanted
       
  2541 
       
  2542         else:
       
  2543 
       
  2544             def want(rev):
       
  2545                 return rev in wanted
       
  2546 
       
  2547         it = iter(revs)
  2451         it = iter(revs)
  2548         stopiteration = False
  2452         stopiteration = False
  2549         for windowsize in increasingwindows():
  2453         for windowsize in increasingwindows():
  2550             nrevs = []
  2454             nrevs = []
  2551             for i in pycompat.xrange(windowsize):
  2455             for i in pycompat.xrange(windowsize):
  2552                 rev = next(it, None)
  2456                 rev = next(it, None)
  2553                 if rev is None:
  2457                 if rev is None:
  2554                     stopiteration = True
  2458                     stopiteration = True
  2555                     break
  2459                     break
  2556                 elif want(rev):
  2460                 nrevs.append(rev)
  2557                     nrevs.append(rev)
       
  2558             for rev in sorted(nrevs):
  2461             for rev in sorted(nrevs):
  2559                 fns = fncache.get(rev)
       
  2560                 ctx = change(rev)
  2462                 ctx = change(rev)
  2561                 if not fns:
  2463                 prepare(ctx, makefilematcher(ctx))
  2562 
       
  2563                     def fns_generator():
       
  2564                         if allfiles:
       
  2565 
       
  2566                             def bad(f, msg):
       
  2567                                 pass
       
  2568 
       
  2569                             for f in ctx.matches(matchmod.badmatch(match, bad)):
       
  2570                                 yield f
       
  2571                         else:
       
  2572                             for f in ctx.files():
       
  2573                                 if match(f):
       
  2574                                     yield f
       
  2575 
       
  2576                     fns = fns_generator()
       
  2577                 prepare(ctx, scmutil.matchfiles(repo, fns))
       
  2578             for rev in nrevs:
  2464             for rev in nrevs:
  2579                 yield change(rev)
  2465                 yield change(rev)
  2580 
  2466 
  2581             if stopiteration:
  2467             if stopiteration:
  2582                 break
  2468                 break