240 for op in ["newest_first"]: |
240 for op in ["newest_first"]: |
241 if op in opts and opts[op]: |
241 if op in opts and opts[op]: |
242 raise util.Abort(_("-G/--graph option is incompatible with --%s") |
242 raise util.Abort(_("-G/--graph option is incompatible with --%s") |
243 % op.replace("_", "-")) |
243 % op.replace("_", "-")) |
244 |
244 |
|
245 def makefilematcher(repo, pats, followfirst): |
|
246 # When displaying a revision with --patch --follow FILE, we have |
|
247 # to know which file of the revision must be diffed. With |
|
248 # --follow, we want the names of the ancestors of FILE in the |
|
249 # revision, stored in "fcache". "fcache" is populated by |
|
250 # reproducing the graph traversal already done by --follow revset |
|
251 # and relating linkrevs to file names (which is not "correct" but |
|
252 # good enough). |
|
253 fcache = {} |
|
254 fcacheready = [False] |
|
255 pctx = repo['.'] |
|
256 wctx = repo[None] |
|
257 |
|
258 def populate(): |
|
259 for fn in pats: |
|
260 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)): |
|
261 for c in i: |
|
262 fcache.setdefault(c.linkrev(), set()).add(c.path()) |
|
263 |
|
264 def filematcher(rev): |
|
265 if not fcacheready[0]: |
|
266 # Lazy initialization |
|
267 fcacheready[0] = True |
|
268 populate() |
|
269 return scmutil.match(wctx, fcache.get(rev, []), default='path') |
|
270 |
|
271 return filematcher |
|
272 |
245 def revset(repo, pats, opts): |
273 def revset(repo, pats, opts): |
246 """Return revset str built of revisions, log options and file patterns. |
274 """Return (expr, filematcher) where expr is a revset string built |
|
275 of revisions, log options and file patterns. If --stat or --patch |
|
276 are not passed filematcher is None. Otherwise it a a callable |
|
277 taking a revision number and returning a match objects filtering |
|
278 the files to be detailed when displaying the revision. |
247 """ |
279 """ |
248 opt2revset = { |
280 opt2revset = { |
249 'follow': ('follow()', None), |
281 'follow': ('follow()', None), |
250 'follow_first': ('_followfirst()', None), |
282 'follow_first': ('_followfirst()', None), |
251 'no_merges': ('not merge()', None), |
283 'no_merges': ('not merge()', None), |
327 else: |
359 else: |
328 opts['follow'] = True |
360 opts['follow'] = True |
329 else: |
361 else: |
330 opts['_patslog'] = list(pats) |
362 opts['_patslog'] = list(pats) |
331 |
363 |
|
364 filematcher = None |
|
365 if opts.get('patch') or opts.get('stat'): |
|
366 if follow: |
|
367 filematcher = makefilematcher(repo, pats, followfirst) |
|
368 else: |
|
369 filematcher = lambda rev: match |
|
370 |
332 revset = [] |
371 revset = [] |
333 for op, val in opts.iteritems(): |
372 for op, val in opts.iteritems(): |
334 if not val: |
373 if not val: |
335 continue |
374 continue |
336 if op not in opt2revset: |
375 if op not in opt2revset: |
347 |
386 |
348 if revset: |
387 if revset: |
349 revset = '(' + ' and '.join(revset) + ')' |
388 revset = '(' + ' and '.join(revset) + ')' |
350 else: |
389 else: |
351 revset = 'all()' |
390 revset = 'all()' |
352 return revset |
391 return revset, filematcher |
353 |
392 |
354 def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None): |
393 def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None, |
|
394 filematcher=None): |
355 seen, state = [], asciistate() |
395 seen, state = [], asciistate() |
356 for rev, type, ctx, parents in dag: |
396 for rev, type, ctx, parents in dag: |
357 char = ctx.node() in showparents and '@' or 'o' |
397 char = ctx.node() in showparents and '@' or 'o' |
358 copies = None |
398 copies = None |
359 if getrenamed and ctx.rev(): |
399 if getrenamed and ctx.rev(): |
360 copies = [] |
400 copies = [] |
361 for fn in ctx.files(): |
401 for fn in ctx.files(): |
362 rename = getrenamed(fn, ctx.rev()) |
402 rename = getrenamed(fn, ctx.rev()) |
363 if rename: |
403 if rename: |
364 copies.append((fn, rename[0])) |
404 copies.append((fn, rename[0])) |
365 displayer.show(ctx, copies=copies) |
405 revmatchfn = None |
|
406 if filematcher is not None: |
|
407 revmatchfn = filematcher(ctx.rev()) |
|
408 displayer.show(ctx, copies=copies, matchfn=revmatchfn) |
366 lines = displayer.hunk.pop(rev).split('\n')[:-1] |
409 lines = displayer.hunk.pop(rev).split('\n')[:-1] |
367 displayer.flush(rev) |
410 displayer.flush(rev) |
368 edges = edgefn(type, char, lines, seen, rev, parents) |
411 edges = edgefn(type, char, lines, seen, rev, parents) |
369 for type, char, lines, coldata in edges: |
412 for type, char, lines, coldata in edges: |
370 ascii(ui, state, type, char, lines, coldata) |
413 ascii(ui, state, type, char, lines, coldata) |
387 directory. |
430 directory. |
388 """ |
431 """ |
389 |
432 |
390 check_unsupported_flags(pats, opts) |
433 check_unsupported_flags(pats, opts) |
391 |
434 |
392 revs = sorted(scmutil.revrange(repo, [revset(repo, pats, opts)]), reverse=1) |
435 expr, filematcher = revset(repo, pats, opts) |
|
436 revs = sorted(scmutil.revrange(repo, [expr]), reverse=1) |
393 limit = cmdutil.loglimit(opts) |
437 limit = cmdutil.loglimit(opts) |
394 if limit is not None: |
438 if limit is not None: |
395 revs = revs[:limit] |
439 revs = revs[:limit] |
396 revdag = graphmod.dagwalker(repo, revs) |
440 revdag = graphmod.dagwalker(repo, revs) |
397 |
441 |
401 if opts.get('rev'): |
445 if opts.get('rev'): |
402 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1 |
446 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1 |
403 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) |
447 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) |
404 displayer = show_changeset(ui, repo, opts, buffered=True) |
448 displayer = show_changeset(ui, repo, opts, buffered=True) |
405 showparents = [ctx.node() for ctx in repo[None].parents()] |
449 showparents = [ctx.node() for ctx in repo[None].parents()] |
406 generate(ui, revdag, displayer, showparents, asciiedges, getrenamed) |
450 generate(ui, revdag, displayer, showparents, asciiedges, getrenamed, |
|
451 filematcher) |
407 |
452 |
408 def graphrevs(repo, nodes, opts): |
453 def graphrevs(repo, nodes, opts): |
409 limit = cmdutil.loglimit(opts) |
454 limit = cmdutil.loglimit(opts) |
410 nodes.reverse() |
455 nodes.reverse() |
411 if limit is not None: |
456 if limit is not None: |