Mercurial > public > mercurial-scm > hg-stable
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 |