comparison mercurial/cmdutil.py @ 35686:b25fa5da4ca2

log: resolve --follow thoroughly in getlogrevs() This makes sense because --follow isn't really an option to filter revisions, but an option to extend revisions to be filtered. _fileancestors() is a minimal copy of revset._follow(). They are slightly different in that which revision the matcher sees. _fileancestors() also uses ctx.walk() instead of ctx.manifest().walk() to show a better warning on bad match, which will be tested later.
author Yuya Nishihara <yuya@tcha.org>
date Wed, 03 Jan 2018 15:46:15 +0900
parents 659dfbd852e2
children 67893a516272
comparison
equal deleted inserted replaced
35685:659dfbd852e2 35686:b25fa5da4ca2
2369 else: 2369 else:
2370 slowpath = False 2370 slowpath = False
2371 2371
2372 return match, pats, slowpath 2372 return match, pats, slowpath
2373 2373
2374 def _fileancestors(repo, revs, match, followfirst):
2375 fctxs = []
2376 for r in revs:
2377 ctx = repo[r]
2378 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
2379 return dagop.filerevancestors(fctxs, followfirst=followfirst)
2380
2374 def _makefollowlogfilematcher(repo, files, followfirst): 2381 def _makefollowlogfilematcher(repo, files, followfirst):
2375 # When displaying a revision with --patch --follow FILE, we have 2382 # When displaying a revision with --patch --follow FILE, we have
2376 # to know which file of the revision must be diffed. With 2383 # to know which file of the revision must be diffed. With
2377 # --follow, we want the names of the ancestors of FILE in the 2384 # --follow, we want the names of the ancestors of FILE in the
2378 # revision, stored in "fcache". "fcache" is populated by 2385 # revision, stored in "fcache". "fcache" is populated by
2404 return None 2411 return None
2405 2412
2406 _opt2logrevset = { 2413 _opt2logrevset = {
2407 'no_merges': ('not merge()', None), 2414 'no_merges': ('not merge()', None),
2408 'only_merges': ('merge()', None), 2415 'only_merges': ('merge()', None),
2409 '_ancestors': ('ancestors(%r)', None),
2410 '_fancestors': ('_firstancestors(%r)', None),
2411 '_matchfiles': (None, '_matchfiles(%ps)'), 2416 '_matchfiles': (None, '_matchfiles(%ps)'),
2412 'date': ('date(%s)', None), 2417 'date': ('date(%s)', None),
2413 'branch': ('branch(%s)', '%lr'), 2418 'branch': ('branch(%s)', '%lr'),
2414 '_patslog': ('filelog(%s)', '%lr'), 2419 '_patslog': ('filelog(%s)', '%lr'),
2415 '_patsfollow': ('follow(%s)', '%lr'),
2416 '_patsfollowfirst': ('_followfirst(%s)', '%lr'),
2417 'keyword': ('keyword(%s)', '%lr'), 2420 'keyword': ('keyword(%s)', '%lr'),
2418 'prune': ('ancestors(%s)', 'not %lr'), 2421 'prune': ('ancestors(%s)', 'not %lr'),
2419 'user': ('user(%s)', '%lr'), 2422 'user': ('user(%s)', '%lr'),
2420 } 2423 }
2421 2424
2427 the files to be detailed when displaying the revision. 2430 the files to be detailed when displaying the revision.
2428 """ 2431 """
2429 opts = dict(opts) 2432 opts = dict(opts)
2430 # follow or not follow? 2433 # follow or not follow?
2431 follow = opts.get('follow') or opts.get('follow_first') 2434 follow = opts.get('follow') or opts.get('follow_first')
2432 if opts.get('follow_first'):
2433 followfirst = 1
2434 else:
2435 followfirst = 0
2436 2435
2437 # branch and only_branch are really aliases and must be handled at 2436 # branch and only_branch are really aliases and must be handled at
2438 # the same time 2437 # the same time
2439 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', []) 2438 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2440 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']] 2439 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2441
2442 fpats = ('_patsfollow', '_patsfollowfirst')
2443 fnopats = ('_ancestors', '_fancestors')
2444 2440
2445 if slowpath: 2441 if slowpath:
2446 # See walkchangerevs() slow path. 2442 # See walkchangerevs() slow path.
2447 # 2443 #
2448 # pats/include/exclude cannot be represented as separate 2444 # pats/include/exclude cannot be represented as separate
2457 for p in opts.get('include', []): 2453 for p in opts.get('include', []):
2458 matchargs.append('i:' + p) 2454 matchargs.append('i:' + p)
2459 for p in opts.get('exclude', []): 2455 for p in opts.get('exclude', []):
2460 matchargs.append('x:' + p) 2456 matchargs.append('x:' + p)
2461 opts['_matchfiles'] = matchargs 2457 opts['_matchfiles'] = matchargs
2462 if follow: 2458 elif not follow:
2463 opts[fnopats[followfirst]] = '.' 2459 opts['_patslog'] = list(pats)
2464 else:
2465 if follow:
2466 if pats:
2467 # follow() revset interprets its file argument as a
2468 # manifest entry, so use match.files(), not pats.
2469 opts[fpats[followfirst]] = list(match.files())
2470 else:
2471 op = fnopats[followfirst]
2472 opts[op] = '.'
2473 else:
2474 opts['_patslog'] = list(pats)
2475 2460
2476 filematcher = None 2461 filematcher = None
2477 if opts.get('patch') or opts.get('stat'): 2462 if opts.get('patch') or opts.get('stat'):
2478 # When following files, track renames via a special matcher. 2463 # When following files, track renames via a special matcher.
2479 # If we're forced to take the slowpath it means we're following 2464 # If we're forced to take the slowpath it means we're following
2480 # at least one pattern/directory, so don't bother with rename tracking. 2465 # at least one pattern/directory, so don't bother with rename tracking.
2481 if follow and not match.always() and not slowpath: 2466 if follow and not match.always() and not slowpath:
2482 # _makefollowlogfilematcher expects its files argument to be 2467 # _makefollowlogfilematcher expects its files argument to be
2483 # relative to the repo root, so use match.files(), not pats. 2468 # relative to the repo root, so use match.files(), not pats.
2484 filematcher = _makefollowlogfilematcher(repo, match.files(), 2469 filematcher = _makefollowlogfilematcher(repo, match.files(),
2485 followfirst) 2470 opts.get('follow_first'))
2486 else: 2471 else:
2487 filematcher = _makenofollowlogfilematcher(repo, pats, opts) 2472 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2488 if filematcher is None: 2473 if filematcher is None:
2489 filematcher = lambda rev: match 2474 filematcher = lambda rev: match
2490 2475
2509 else: 2494 else:
2510 expr = None 2495 expr = None
2511 return expr, filematcher 2496 return expr, filematcher
2512 2497
2513 def _logrevs(repo, opts): 2498 def _logrevs(repo, opts):
2499 """Return the initial set of revisions to be filtered or followed"""
2514 follow = opts.get('follow') or opts.get('follow_first') 2500 follow = opts.get('follow') or opts.get('follow_first')
2515 if opts.get('rev'): 2501 if opts.get('rev'):
2516 revs = scmutil.revrange(repo, opts['rev']) 2502 revs = scmutil.revrange(repo, opts['rev'])
2517 elif follow and repo.dirstate.p1() == nullid: 2503 elif follow and repo.dirstate.p1() == nullid:
2518 revs = smartset.baseset() 2504 revs = smartset.baseset()
2519 elif follow: 2505 elif follow:
2520 revs = repo.revs('reverse(:.)') 2506 revs = repo.revs('.')
2521 else: 2507 else:
2522 revs = smartset.spanset(repo) 2508 revs = smartset.spanset(repo)
2523 revs.reverse() 2509 revs.reverse()
2524 return revs 2510 return revs
2525 2511
2539 limit = loglimit(opts) 2525 limit = loglimit(opts)
2540 revs = _logrevs(repo, opts) 2526 revs = _logrevs(repo, opts)
2541 if not revs: 2527 if not revs:
2542 return smartset.baseset(), None 2528 return smartset.baseset(), None
2543 match, pats, slowpath = _makelogmatcher(repo, pats, opts) 2529 match, pats, slowpath = _makelogmatcher(repo, pats, opts)
2544 if opts.get('rev') and follow: 2530 if follow:
2545 revs = dagop.revancestors(repo, revs, followfirst=followfirst) 2531 if opts.get('rev') or slowpath or not pats:
2532 revs = dagop.revancestors(repo, revs, followfirst=followfirst)
2533 else:
2534 revs = _fileancestors(repo, revs, match, followfirst)
2546 revs.reverse() 2535 revs.reverse()
2547 expr, filematcher = _makelogrevset(repo, match, pats, slowpath, opts) 2536 expr, filematcher = _makelogrevset(repo, match, pats, slowpath, opts)
2548 if opts.get('graph') and opts.get('rev'): 2537 if opts.get('graph') and opts.get('rev'):
2549 # User-specified revs might be unsorted, but don't sort before 2538 # User-specified revs might be unsorted, but don't sort before
2550 # _makelogrevset because it might depend on the order of revs 2539 # _makelogrevset because it might depend on the order of revs