comparison hgext/graphlog.py @ 16405:17deb6bbfbab

graphlog: refactor revset() to return revisions When --follow and --rev are passed, --follow actual behaviour depends on the input revision sequence defined by --rev. If --rev is not passed, the default revision sequence depends on the presence of --follow. It means the revision sequence generation is part of log logic and must be wrapped. The issue described above is fixed in following patches.
author Patrick Mezard <patrick@mezard.eu>
date Wed, 11 Apr 2012 11:07:30 +0200
parents 0f1e621d3d3b
children 4aa4f50c52b9
comparison
equal deleted inserted replaced
16404:9fca5b056c0a 16405:17deb6bbfbab
269 populate() 269 populate()
270 return scmutil.match(wctx, fcache.get(rev, []), default='path') 270 return scmutil.match(wctx, fcache.get(rev, []), default='path')
271 271
272 return filematcher 272 return filematcher
273 273
274 def revset(repo, pats, opts): 274 def _makelogrevset(repo, pats, opts, revs):
275 """Return (expr, filematcher) where expr is a revset string built 275 """Return (expr, filematcher) where expr is a revset string built
276 log options and file patterns, or None. Note that --rev options 276 from log options and file patterns or None. If --stat or --patch
277 are ignored when building expr because we do not know if they are 277 are not passed filematcher is None. Otherwise it is a callable
278 proper revsets or legacy expressions like a 'foo-bar' tags. If 278 taking a revision number and returning a match objects filtering
279 --stat or --patch are not passed filematcher is None. Otherwise it 279 the files to be detailed when displaying the revision.
280 a a callable taking a revision number and returning a match
281 objects filtering the files to be detailed when displaying the
282 revision.
283 """ 280 """
284 opt2revset = { 281 opt2revset = {
285 'follow': ('follow()', None), 282 'follow': ('follow()', None),
286 'follow_first': ('_followfirst()', None), 283 'follow_first': ('_followfirst()', None),
287 'no_merges': ('not merge()', None), 284 'no_merges': ('not merge()', None),
296 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '), 293 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
297 'user': ('user(%(val)r)', ' or '), 294 'user': ('user(%(val)r)', ' or '),
298 } 295 }
299 296
300 opts = dict(opts) 297 opts = dict(opts)
301 # branch and only_branch are really aliases and must be handled at 298 # follow or not follow?
302 # the same time
303 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
304 follow = opts.get('follow') or opts.get('follow_first') 299 follow = opts.get('follow') or opts.get('follow_first')
305 followfirst = opts.get('follow_first') 300 followfirst = opts.get('follow_first')
306 if 'follow' in opts: 301 if 'follow' in opts:
307 del opts['follow'] 302 del opts['follow']
308 if 'follow_first' in opts: 303 if 'follow_first' in opts:
309 del opts['follow_first'] 304 del opts['follow_first']
305
306 # branch and only_branch are really aliases and must be handled at
307 # the same time
308 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
310 # pats/include/exclude are passed to match.match() directly in 309 # pats/include/exclude are passed to match.match() directly in
311 # _matchfile() revset but walkchangerevs() builds its matcher with 310 # _matchfile() revset but walkchangerevs() builds its matcher with
312 # scmutil.match(). The difference is input pats are globbed on 311 # scmutil.match(). The difference is input pats are globbed on
313 # platforms without shell expansion (windows). 312 # platforms without shell expansion (windows).
314 pctx = repo[None] 313 pctx = repo[None]
390 revset = '(' + ' and '.join(revset) + ')' 389 revset = '(' + ' and '.join(revset) + ')'
391 else: 390 else:
392 revset = None 391 revset = None
393 return revset, filematcher 392 return revset, filematcher
394 393
394 def getlogrevs(repo, pats, opts):
395 """Return (revs, expr, filematcher) where revs is a list of
396 revision numbers, expr is a revset string built from log options
397 and file patterns or None, and used to filter 'revs'. If --stat or
398 --patch are not passed filematcher is None. Otherwise it is a
399 callable taking a revision number and returning a match objects
400 filtering the files to be detailed when displaying the revision.
401 """
402 if not len(repo):
403 return [], None, None
404 if opts.get('rev'):
405 revs = scmutil.revrange(repo, opts['rev'])
406 else:
407 revs = range(len(repo))
408 if not revs:
409 return [], None, None
410 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
411 if expr:
412 revs = revsetmod.match(repo.ui, expr)(repo, revs)
413 return revs, expr, filematcher
414
395 def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None, 415 def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None,
396 filematcher=None): 416 filematcher=None):
397 seen, state = [], asciistate() 417 seen, state = [], asciistate()
398 for rev, type, ctx, parents in dag: 418 for rev, type, ctx, parents in dag:
399 char = ctx.node() in showparents and '@' or 'o' 419 char = ctx.node() in showparents and '@' or 'o'
432 directory. 452 directory.
433 """ 453 """
434 454
435 check_unsupported_flags(pats, opts) 455 check_unsupported_flags(pats, opts)
436 456
437 expr, filematcher = revset(repo, pats, opts) 457 revs, expr, filematcher = getlogrevs(repo, pats, opts)
438 if opts.get('rev'):
439 revs = scmutil.revrange(repo, opts['rev'])
440 else:
441 revs = range(len(repo))
442 if expr:
443 revs = revsetmod.match(repo.ui, expr)(repo, revs)
444 revs = sorted(revs, reverse=1) 458 revs = sorted(revs, reverse=1)
445 limit = cmdutil.loglimit(opts) 459 limit = cmdutil.loglimit(opts)
446 if limit is not None: 460 if limit is not None:
447 revs = revs[:limit] 461 revs = revs[:limit]
448 revdag = graphmod.dagwalker(repo, revs) 462 revdag = graphmod.dagwalker(repo, revs)