##// END OF EJS Templates
context: don't count deleted files as candidates for path conflicts in IMM...
context: don't count deleted files as candidates for path conflicts in IMM This patch makes sure we don't consider the deleted files in our IMM wctx as potential conflicts while calculating paths conflicts. This fixes the bug demonstrated in previous patch. Differential Revision: https://phab.mercurial-scm.org/D4543

File last commit:

r38589:2523c020 default
r39612:fa4d16da default
Show More
logcmdutil.py
912 lines | 33.0 KiB | text/x-python | PythonLexer
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 # logcmdutil.py - utility for log-like commands
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
import itertools
import os
from .i18n import _
from .node import (
nullid,
)
from . import (
dagop,
error,
formatter,
graphmod,
match as matchmod,
mdiff,
patch,
pathutil,
pycompat,
revset,
revsetlang,
scmutil,
smartset,
templatekw,
templater,
util,
)
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 from .utils import (
dateutil,
stringutil,
)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def getlimit(opts):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """get the log limit according to option -l/--limit"""
limit = opts.get('limit')
if limit:
try:
limit = int(limit)
except ValueError:
raise error.Abort(_('limit must be a positive integer'))
if limit <= 0:
raise error.Abort(_('limit must be positive'))
else:
limit = None
return limit
def diffordiffstat(ui, repo, diffopts, node1, node2, match,
changes=None, stat=False, fp=None, prefix='',
root='', listsubrepos=False, hunksfilterfn=None):
'''show diff or diffstat.'''
if root:
relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
else:
relroot = ''
if relroot != '':
# XXX relative roots currently don't work if the root is within a
# subrepo
uirelroot = match.uipath(relroot)
relroot += '/'
for matchroot in match.files():
if not matchroot.startswith(relroot):
ui.warn(_('warning: %s not inside relative root %s\n') % (
match.uipath(matchroot), uirelroot))
if stat:
diffopts = diffopts.copy(context=0, noprefix=False)
width = 80
if not ui.plain():
width = ui.termwidth()
Joerg Sonnenberger
diff: improve ui.write performance when not coloring on Windows...
r35979
Boris Feld
diff: use `context.diff` to produce diff...
r38589 chunks = repo[node2].diff(repo[node1], match, changes, opts=diffopts,
prefix=prefix, relroot=relroot,
hunksfilterfn=hunksfilterfn)
Joerg Sonnenberger
diff: improve ui.write performance when not coloring on Windows...
r35979
if fp is not None or ui.canwritewithoutlabels():
Yuya Nishihara
diff: remove fp.write() wrapper which drops label argument...
r36025 out = fp or ui
Joerg Sonnenberger
diff: improve ui.write performance when not coloring on Windows...
r35979 if stat:
Gregory Szorc
py3: preserve chunks as an iterable of bytes...
r36132 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
Joerg Sonnenberger
diff: improve ui.write performance when not coloring on Windows...
r35979 for chunk in util.filechunkiter(util.chunkbuffer(chunks)):
Yuya Nishihara
diff: remove fp.write() wrapper which drops label argument...
r36025 out.write(chunk)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 else:
Joerg Sonnenberger
diff: improve ui.write performance when not coloring on Windows...
r35979 if stat:
chunks = patch.diffstatui(util.iterlines(chunks), width=width)
else:
chunks = patch.difflabel(lambda chunks, **kwargs: chunks, chunks,
opts=diffopts)
if ui.canbatchlabeledwrites():
def gen():
for chunk, label in chunks:
yield ui.label(chunk, label=label)
for chunk in util.filechunkiter(util.chunkbuffer(gen())):
Yuya Nishihara
diff: remove fp.write() wrapper which drops label argument...
r36025 ui.write(chunk)
Joerg Sonnenberger
diff: improve ui.write performance when not coloring on Windows...
r35979 else:
for chunk, label in chunks:
Yuya Nishihara
diff: remove fp.write() wrapper which drops label argument...
r36025 ui.write(chunk, label=label)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
if listsubrepos:
ctx1 = repo[node1]
ctx2 = repo[node2]
for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
tempnode2 = node2
try:
if node2 is not None:
tempnode2 = ctx2.substate[subpath][1]
except KeyError:
# A subrepo that existed in node1 was deleted between node1 and
# node2 (inclusive). Thus, ctx2's substate won't contain that
# subpath. The best we can do is to ignore it.
tempnode2 = None
submatch = matchmod.subdirmatcher(subpath, match)
sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
stat=stat, fp=fp, prefix=prefix)
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 class changesetdiffer(object):
"""Generate diff of changeset with pre-configured filtering functions"""
def _makefilematcher(self, ctx):
return scmutil.matchall(ctx.repo())
def _makehunksfilter(self, ctx):
return None
def showdiff(self, ui, ctx, diffopts, stat=False):
repo = ctx.repo()
node = ctx.node()
prev = ctx.p1().node()
diffordiffstat(ui, repo, diffopts, prev, node,
match=self._makefilematcher(ctx), stat=stat,
hunksfilterfn=self._makehunksfilter(ctx))
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 def changesetlabels(ctx):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
if ctx.obsolete():
labels.append('changeset.obsolete')
if ctx.isunstable():
labels.append('changeset.unstable')
for instability in ctx.instabilities():
labels.append('instability.%s' % instability)
return ' '.join(labels)
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 class changesetprinter(object):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 '''show changeset information when templating not requested.'''
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 self.ui = ui
self.repo = repo
self.buffered = buffered
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 self._differ = differ or changesetdiffer()
Yuya Nishihara
log: cache diffopts instance...
r37858 self._diffopts = patch.diffallopts(ui, diffopts)
Yuya Nishihara
log: consume --stat/patch options at constructor of changesetprinter...
r37859 self._includestat = diffopts and diffopts.get('stat')
self._includediff = diffopts and diffopts.get('patch')
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 self.header = {}
self.hunk = {}
self.lastheader = None
self.footer = None
self._columns = templatekw.getlogcolumns()
def flush(self, ctx):
rev = ctx.rev()
if rev in self.header:
h = self.header[rev]
if h != self.lastheader:
self.lastheader = h
self.ui.write(h)
del self.header[rev]
if rev in self.hunk:
self.ui.write(self.hunk[rev])
del self.hunk[rev]
def close(self):
if self.footer:
self.ui.write(self.footer)
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 def show(self, ctx, copies=None, **props):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 props = pycompat.byteskwargs(props)
if self.buffered:
self.ui.pushbuffer(labeled=True)
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 self._show(ctx, copies, props)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 self.hunk[ctx.rev()] = self.ui.popbuffer()
else:
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 self._show(ctx, copies, props)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 def _show(self, ctx, copies, props):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 '''show a single changeset or file revision'''
changenode = ctx.node()
rev = ctx.rev()
if self.ui.quiet:
self.ui.write("%s\n" % scmutil.formatchangeid(ctx),
label='log.node')
return
columns = self._columns
self.ui.write(columns['changeset'] % scmutil.formatchangeid(ctx),
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 label=changesetlabels(ctx))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
# branches are shown first before any other names due to backwards
# compatibility
branch = ctx.branch()
# don't show the default branch name
if branch != 'default':
self.ui.write(columns['branch'] % branch, label='log.branch')
for nsname, ns in self.repo.names.iteritems():
# branches has special logic already handled above, so here we just
# skip it
if nsname == 'branches':
continue
# we will use the templatename as the color name since those two
# should be the same
for name in ns.names(self.repo, changenode):
self.ui.write(ns.logfmt % name,
label='log.%s' % ns.colorname)
if self.ui.debugflag:
self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase')
for pctx in scmutil.meaningfulparents(self.repo, ctx):
label = 'log.parent changeset.%s' % pctx.phasestr()
self.ui.write(columns['parent'] % scmutil.formatchangeid(pctx),
label=label)
if self.ui.debugflag and rev is not None:
mnode = ctx.manifestnode()
Gregory Szorc
manifest: define and implement rev() on manifestlog...
r38573 mrev = self.repo.manifestlog.rev(mnode)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 self.ui.write(columns['manifest']
% scmutil.formatrevnode(self.ui, mrev, mnode),
label='ui.debug log.manifest')
self.ui.write(columns['user'] % ctx.user(), label='log.user')
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 self.ui.write(columns['date'] % dateutil.datestr(ctx.date()),
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 label='log.date')
if ctx.isunstable():
instabilities = ctx.instabilities()
self.ui.write(columns['instability'] % ', '.join(instabilities),
label='log.instability')
elif ctx.obsolete():
self._showobsfate(ctx)
self._exthook(ctx)
if self.ui.debugflag:
files = ctx.p1().status(ctx)[:3]
for key, value in zip(['files', 'files+', 'files-'], files):
if value:
self.ui.write(columns[key] % " ".join(value),
label='ui.debug log.files')
elif ctx.files() and self.ui.verbose:
self.ui.write(columns['files'] % " ".join(ctx.files()),
label='ui.note log.files')
if copies and self.ui.verbose:
copies = ['%s (%s)' % c for c in copies]
self.ui.write(columns['copies'] % ' '.join(copies),
label='ui.note log.copies')
extra = ctx.extra()
if extra and self.ui.debugflag:
for key, value in sorted(extra.items()):
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 self.ui.write(columns['extra']
% (key, stringutil.escapestr(value)),
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 label='ui.debug log.extra')
description = ctx.description().strip()
if description:
if self.ui.verbose:
self.ui.write(_("description:\n"),
label='ui.note log.description')
self.ui.write(description,
label='ui.note log.description')
self.ui.write("\n\n")
else:
self.ui.write(columns['summary'] % description.splitlines()[0],
label='log.summary')
self.ui.write("\n")
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 self._showpatch(ctx)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
def _showobsfate(self, ctx):
Yuya Nishihara
log: do not invoke templatekw.showobsfate() as a function...
r36534 # TODO: do not depend on templater
tres = formatter.templateresources(self.repo.ui, self.repo)
t = formatter.maketemplater(self.repo.ui, '{join(obsfate, "\n")}',
defaults=templatekw.keywords,
resources=tres)
Yuya Nishihara
templater: switch 'revcache' based on new mapping items...
r37121 obsfate = t.renderdefault({'ctx': ctx}).splitlines()
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
if obsfate:
for obsfateline in obsfate:
self.ui.write(self._columns['obsolete'] % obsfateline,
label='log.obsfate')
def _exthook(self, ctx):
'''empty method used by extension as a hook point
'''
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 def _showpatch(self, ctx):
Yuya Nishihara
log: consume --stat/patch options at constructor of changesetprinter...
r37859 if self._includestat:
Yuya Nishihara
log: cache diffopts instance...
r37858 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
Yuya Nishihara
log: consume --stat/patch options at constructor of changesetprinter...
r37859 if self._includestat and self._includediff:
Yuya Nishihara
logcmdutil: unindent diff generator of changesetprinter...
r36021 self.ui.write("\n")
Yuya Nishihara
log: consume --stat/patch options at constructor of changesetprinter...
r37859 if self._includediff:
Yuya Nishihara
log: cache diffopts instance...
r37858 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
Yuya Nishihara
log: consume --stat/patch options at constructor of changesetprinter...
r37859 if self._includestat or self._includediff:
Yuya Nishihara
logcmdutil: unindent diff generator of changesetprinter...
r36021 self.ui.write("\n")
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: pass formatter to jsonchangeset as argument...
r37791 class changesetformatter(changesetprinter):
"""Format changeset information by generic formatter"""
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: pass formatter to jsonchangeset as argument...
r37791 def __init__(self, ui, repo, fm, differ=None, diffopts=None,
buffered=False):
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
Yuya Nishihara
log: cache diffopts instance...
r37858 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True)
Yuya Nishihara
logcmdutil: pass formatter to jsonchangeset as argument...
r37791 self._fm = fm
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
def close(self):
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 self._fm.end()
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 def _show(self, ctx, copies, props):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 '''show a single changeset or file revision'''
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 fm = self._fm
fm.startitem()
# TODO: maybe this should be wdirrev/wdirnode?
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 rev = ctx.rev()
if rev is None:
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 hexnode = None
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 else:
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 hexnode = fm.hexfunc(ctx.node())
fm.data(rev=rev,
node=hexnode)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
if self.ui.quiet:
return
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 fm.data(branch=ctx.branch(),
phase=ctx.phasestr(),
user=ctx.user(),
date=fm.formatdate(ctx.date()),
desc=ctx.description(),
bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'),
tags=fm.formatlist(ctx.tags(), name='tag'),
parents=fm.formatlist([fm.hexfunc(c.node())
for c in ctx.parents()], name='node'))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
if self.ui.debugflag:
if rev is None:
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 hexnode = None
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 else:
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 hexnode = fm.hexfunc(ctx.manifestnode())
fm.data(manifest=hexnode,
extra=fm.formatdict(ctx.extra()))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
files = ctx.p1().status(ctx)
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 fm.data(modified=fm.formatlist(files[0], name='file'),
added=fm.formatlist(files[1], name='file'),
removed=fm.formatlist(files[2], name='file'))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
elif self.ui.verbose:
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 fm.data(files=fm.formatlist(ctx.files(), name='file'))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if copies:
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 fm.data(copies=fm.formatdict(copies,
key='name', value='source'))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
log: consume --stat/patch options at constructor of changesetprinter...
r37859 if self._includestat:
Yuya Nishihara
logcmdutil: unindent diff generator of changesetprinter...
r36021 self.ui.pushbuffer()
Yuya Nishihara
log: cache diffopts instance...
r37858 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 fm.data(diffstat=self.ui.popbuffer())
Yuya Nishihara
log: consume --stat/patch options at constructor of changesetprinter...
r37859 if self._includediff:
Yuya Nishihara
logcmdutil: unindent diff generator of changesetprinter...
r36021 self.ui.pushbuffer()
Yuya Nishihara
log: cache diffopts instance...
r37858 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
Yuya Nishihara
logcmdutil: rewrite jsonchangeset printer to be backed by jsonformatter...
r37790 fm.data(diff=self.ui.popbuffer())
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 class changesettemplater(changesetprinter):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 '''format changeset information.
Note: there are a variety of convenience functions to build a
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 changesettemplater for common cases. See functions such as:
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 maketemplater, changesetdisplayer, buildcommittemplate, or other
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 functions that use changesest_templater.
'''
# Arguments before "buffered" used to be positional. Consider not
# adding/removing arguments before "buffered" to not break callers.
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 def __init__(self, ui, repo, tmplspec, differ=None, diffopts=None,
buffered=False):
changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
Yuya Nishihara
log: do no expect templateresources() returning a dict...
r37090 # tres is shared with _graphnodeformatter()
self._tresources = tres = formatter.templateresources(ui, repo)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 self.t = formatter.loadtemplater(ui, tmplspec,
defaults=templatekw.keywords,
resources=tres,
cache=templatekw.defaulttempl)
self._counter = itertools.count()
self._tref = tmplspec.ref
self._parts = {'header': '', 'footer': '',
tmplspec.ref: tmplspec.ref,
'docheader': '', 'docfooter': '',
'separator': ''}
if tmplspec.mapfile:
# find correct templates for current mode, for backward
# compatibility with 'log -v/-q/--debug' using a mapfile
tmplmodes = [
(True, ''),
(self.ui.verbose, '_verbose'),
(self.ui.quiet, '_quiet'),
(self.ui.debugflag, '_debug'),
]
for mode, postfix in tmplmodes:
for t in self._parts:
cur = t + postfix
if mode and cur in self.t:
self._parts[t] = cur
else:
partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
m = formatter.templatepartsmap(tmplspec, self.t, partnames)
self._parts.update(m)
if self._parts['docheader']:
Yuya Nishihara
templater: factor out helper that renders named template as string...
r37004 self.ui.write(self.t.render(self._parts['docheader'], {}))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
def close(self):
if self._parts['docfooter']:
if not self.footer:
self.footer = ""
Yuya Nishihara
templater: factor out helper that renders named template as string...
r37004 self.footer += self.t.render(self._parts['docfooter'], {})
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 return super(changesettemplater, self).close()
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 def _show(self, ctx, copies, props):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 '''show a single changeset or file revision'''
props = props.copy()
props['ctx'] = ctx
props['index'] = index = next(self._counter)
props['revcache'] = {'copies': copies}
# write separator, which wouldn't work well with the header part below
# since there's inherently a conflict between header (across items) and
# separator (per item)
if self._parts['separator'] and index > 0:
Yuya Nishihara
templater: factor out helper that renders named template as string...
r37004 self.ui.write(self.t.render(self._parts['separator'], {}))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
# write header
if self._parts['header']:
Yuya Nishihara
templater: factor out helper that renders named template as string...
r37004 h = self.t.render(self._parts['header'], props)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if self.buffered:
self.header[ctx.rev()] = h
else:
if self.lastheader != h:
self.lastheader = h
self.ui.write(h)
# write changeset metadata, then patch if requested
key = self._parts[self._tref]
Yuya Nishihara
templater: factor out helper that renders named template as string...
r37004 self.ui.write(self.t.render(key, props))
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 self._showpatch(ctx)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
if self._parts['footer']:
if not self.footer:
Yuya Nishihara
templater: factor out helper that renders named template as string...
r37004 self.footer = self.t.render(self._parts['footer'], props)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def templatespec(tmpl, mapfile):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if mapfile:
return formatter.templatespec('changeset', tmpl, mapfile)
else:
return formatter.templatespec('', tmpl, None)
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def _lookuptemplate(ui, tmpl, style):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """Find the template matching the given template spec or style
See formatter.lookuptemplate() for details.
"""
# ui settings
if not tmpl and not style: # template are stronger than style
tmpl = ui.config('ui', 'logtemplate')
if tmpl:
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 return templatespec(templater.unquotestring(tmpl), None)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 else:
style = util.expandpath(ui.config('ui', 'style'))
if not tmpl and style:
mapfile = style
if not os.path.split(mapfile)[0]:
mapname = (templater.templatepath('map-cmdline.' + mapfile)
or templater.templatepath(mapfile))
if mapname:
mapfile = mapname
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 return templatespec(None, mapfile)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
if not tmpl:
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 return templatespec(None, None)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
return formatter.lookuptemplate(ui, 'changeset', tmpl)
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def maketemplater(ui, repo, tmpl, buffered=False):
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 """Create a changesettemplater from a literal template 'tmpl'
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 byte-string."""
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 spec = templatespec(tmpl, None)
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 return changesettemplater(ui, repo, spec, buffered=buffered)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """show one changeset using template or regular display.
Display format will be the first non-empty hit of:
1. option 'template'
2. option 'style'
3. [ui] setting 'logtemplate'
4. [ui] setting 'style'
If all of these values are either the unset or the empty string,
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 regular display via changesetprinter() is done.
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 postargs = (differ, opts, buffered)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if opts.get('template') == 'json':
Yuya Nishihara
logcmdutil: pass formatter to jsonchangeset as argument...
r37791 fm = ui.formatter('log', opts)
return changesetformatter(ui, repo, fm, *postargs)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 spec = _lookuptemplate(ui, opts.get('template'), opts.get('style'))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
if not spec.ref and not spec.tmpl and not spec.mapfile:
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 return changesetprinter(ui, repo, *postargs)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 return changesettemplater(ui, repo, spec, *postargs)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def _makematcher(repo, revs, pats, opts):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """Build matcher and expanded patterns from log options
If --follow, revs are the revisions to follow from.
Returns (match, pats, slowpath) where
- match: a matcher built from the given pats and -I/-X opts
- pats: patterns used (globs are expanded on Windows)
- slowpath: True if patterns aren't as simple as scanning filelogs
"""
# pats/include/exclude are passed to match.match() directly in
# _matchfiles() revset but walkchangerevs() builds its matcher with
# scmutil.match(). The difference is input pats are globbed on
# platforms without shell expansion (windows).
wctx = repo[None]
match, pats = scmutil.matchandpats(wctx, pats, opts)
slowpath = match.anypats() or (not match.always() and opts.get('removed'))
if not slowpath:
follow = opts.get('follow') or opts.get('follow_first')
startctxs = []
if follow and opts.get('rev'):
startctxs = [repo[r] for r in revs]
for f in match.files():
if follow and startctxs:
# No idea if the path was a directory at that revision, so
# take the slow path.
if any(f not in c for c in startctxs):
slowpath = True
continue
elif follow and f not in wctx:
# If the file exists, it may be a directory, so let it
# take the slow path.
if os.path.exists(repo.wjoin(f)):
slowpath = True
continue
else:
raise error.Abort(_('cannot follow file not in parent '
'revision: "%s"') % f)
filelog = repo.file(f)
if not filelog:
# A zero count may be a directory or deleted file, so
# try to find matching entries on the slow path.
if follow:
raise error.Abort(
_('cannot follow nonexistent file: "%s"') % f)
slowpath = True
# We decided to fall back to the slowpath because at least one
# of the paths was not a file. Check to see if at least one of them
# existed in history - in that case, we'll continue down the
# slowpath; otherwise, we can turn off the slowpath
if slowpath:
for path in match.files():
if path == '.' or path in repo.store:
break
else:
slowpath = False
return match, pats, slowpath
def _fileancestors(repo, revs, match, followfirst):
fctxs = []
for r in revs:
ctx = repo[r]
fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
# When displaying a revision with --patch --follow FILE, we have
# to know which file of the revision must be diffed. With
# --follow, we want the names of the ancestors of FILE in the
# revision, stored in "fcache". "fcache" is populated as a side effect
# of the graph traversal.
fcache = {}
Yuya Nishihara
log: pass ctx to makefilematcher() and makehunksfilter() functions...
r36019 def filematcher(ctx):
return scmutil.matchfiles(repo, fcache.get(ctx.rev(), []))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
def revgen():
for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
fcache[rev] = [c.path() for c in cs]
yield rev
return smartset.generatorset(revgen(), iterasc=False), filematcher
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def _makenofollowfilematcher(repo, pats, opts):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 '''hook for extensions to override the filematcher for non-follow cases'''
return None
_opt2logrevset = {
'no_merges': ('not merge()', None),
'only_merges': ('merge()', None),
'_matchfiles': (None, '_matchfiles(%ps)'),
'date': ('date(%s)', None),
'branch': ('branch(%s)', '%lr'),
'_patslog': ('filelog(%s)', '%lr'),
'keyword': ('keyword(%s)', '%lr'),
'prune': ('ancestors(%s)', 'not %lr'),
'user': ('user(%s)', '%lr'),
}
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def _makerevset(repo, match, pats, slowpath, opts):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """Return a revset string built from log options and file patterns"""
opts = dict(opts)
# follow or not follow?
follow = opts.get('follow') or opts.get('follow_first')
# branch and only_branch are really aliases and must be handled at
# the same time
opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
if slowpath:
# See walkchangerevs() slow path.
#
# pats/include/exclude cannot be represented as separate
# revset expressions as their filtering logic applies at file
# level. For instance "-I a -X b" matches a revision touching
# "a" and "b" while "file(a) and not file(b)" does
# not. Besides, filesets are evaluated against the working
# directory.
matchargs = ['r:', 'd:relpath']
for p in pats:
matchargs.append('p:' + p)
for p in opts.get('include', []):
matchargs.append('i:' + p)
for p in opts.get('exclude', []):
matchargs.append('x:' + p)
opts['_matchfiles'] = matchargs
elif not follow:
opts['_patslog'] = list(pats)
expr = []
for op, val in sorted(opts.iteritems()):
if not val:
continue
if op not in _opt2logrevset:
continue
revop, listop = _opt2logrevset[op]
if revop and '%' not in revop:
expr.append(revop)
elif not listop:
expr.append(revsetlang.formatspec(revop, val))
else:
if revop:
val = [revsetlang.formatspec(revop, v) for v in val]
expr.append(revsetlang.formatspec(listop, val))
if expr:
expr = '(' + ' and '.join(expr) + ')'
else:
expr = None
return expr
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def _initialrevs(repo, opts):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """Return the initial set of revisions to be filtered or followed"""
follow = opts.get('follow') or opts.get('follow_first')
if opts.get('rev'):
revs = scmutil.revrange(repo, opts['rev'])
elif follow and repo.dirstate.p1() == nullid:
revs = smartset.baseset()
elif follow:
revs = repo.revs('.')
else:
revs = smartset.spanset(repo)
revs.reverse()
return revs
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def getrevs(repo, pats, opts):
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 """Return (revs, differ) where revs is a smartset
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 differ is a changesetdiffer with pre-configured file matcher.
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """
follow = opts.get('follow') or opts.get('follow_first')
followfirst = opts.get('follow_first')
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 limit = getlimit(opts)
revs = _initialrevs(repo, opts)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if not revs:
return smartset.baseset(), None
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 match, pats, slowpath = _makematcher(repo, revs, pats, opts)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 filematcher = None
if follow:
if slowpath or match.always():
revs = dagop.revancestors(repo, revs, followfirst=followfirst)
else:
revs, filematcher = _fileancestors(repo, revs, match, followfirst)
revs.reverse()
if filematcher is None:
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 filematcher = _makenofollowfilematcher(repo, pats, opts)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if filematcher is None:
Yuya Nishihara
log: pass ctx to makefilematcher() and makehunksfilter() functions...
r36019 def filematcher(ctx):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 return match
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 expr = _makerevset(repo, match, pats, slowpath, opts)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if opts.get('graph') and opts.get('rev'):
# User-specified revs might be unsorted, but don't sort before
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 # _makerevset because it might depend on the order of revs
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if not (revs.isdescending() or revs.istopo()):
revs.sort(reverse=True)
if expr:
matcher = revset.match(None, expr)
revs = matcher(repo, revs)
if limit is not None:
revs = revs.slice(0, limit)
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024
differ = changesetdiffer()
differ._makefilematcher = filematcher
return revs, differ
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def _parselinerangeopt(repo, opts):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """Parse --line-range log option and return a list of tuples (filename,
(fromline, toline)).
"""
linerangebyfname = []
for pat in opts.get('line_range', []):
try:
pat, linerange = pat.rsplit(',', 1)
except ValueError:
raise error.Abort(_('malformatted line-range pattern %s') % pat)
try:
fromline, toline = map(int, linerange.split(':'))
except ValueError:
raise error.Abort(_("invalid line range for %s") % pat)
msg = _("line range pattern '%s' must match exactly one file") % pat
fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
linerangebyfname.append(
(fname, util.processlinerange(fromline, toline)))
return linerangebyfname
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 def getlinerangerevs(repo, userrevs, opts):
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 """Return (revs, differ).
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
"revs" are revisions obtained by processing "line-range" log options and
walking block ancestors of each specified file/line-range.
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 "differ" is a changesetdiffer with pre-configured file matcher and hunks
filter.
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 """
wctx = repo[None]
# Two-levels map of "rev -> file ctx -> [line range]".
linerangesbyrev = {}
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 if fname not in wctx:
raise error.Abort(_('cannot follow file not in parent '
'revision: "%s"') % fname)
fctx = wctx.filectx(fname)
for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
rev = fctx.introrev()
if rev not in userrevs:
continue
linerangesbyrev.setdefault(
rev, {}).setdefault(
fctx.path(), []).append(linerange)
Yuya Nishihara
logcmdutil: create hunksfilter and filematcher even if no diff option given...
r36022 def nofilterhunksfn(fctx, hunks):
return hunks
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: create hunksfilter and filematcher even if no diff option given...
r36022 def hunksfilter(ctx):
fctxlineranges = linerangesbyrev.get(ctx.rev())
if fctxlineranges is None:
return nofilterhunksfn
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: create hunksfilter and filematcher even if no diff option given...
r36022 def filterfn(fctx, hunks):
lineranges = fctxlineranges.get(fctx.path())
if lineranges is not None:
for hr, lines in hunks:
if hr is None: # binary
yield hr, lines
continue
if any(mdiff.hunkinrange(hr[2:], lr)
for lr in lineranges):
yield hr, lines
else:
for hunk in hunks:
yield hunk
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: create hunksfilter and filematcher even if no diff option given...
r36022 return filterfn
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
logcmdutil: create hunksfilter and filematcher even if no diff option given...
r36022 def filematcher(ctx):
files = list(linerangesbyrev.get(ctx.rev(), []))
return scmutil.matchfiles(repo, files)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
revs = sorted(linerangesbyrev, reverse=True)
Yuya Nishihara
log: pack filematcher and hunksfilter into changesetdiffer object...
r36024 differ = changesetdiffer()
differ._makefilematcher = filematcher
differ._makehunksfilter = hunksfilter
return revs, differ
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
def _graphnodeformatter(ui, displayer):
spec = ui.config('ui', 'graphnodetemplate')
if not spec:
Yuya Nishihara
templatekw: extract non-templatekw function as getgraphnode()...
r36530 return templatekw.getgraphnode # fast path for "{graphnode}"
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
spec = templater.unquotestring(spec)
Yuya Nishihara
logcmdutil: rename classes and functions to conform to our coding style (API)...
r35904 if isinstance(displayer, changesettemplater):
Yuya Nishihara
templater: convert resources to a table of callables for future extension...
r36997 # reuse cache of slow templates
Yuya Nishihara
log: do no expect templateresources() returning a dict...
r37090 tres = displayer._tresources
else:
tres = formatter.templateresources(ui)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 templ = formatter.maketemplater(ui, spec, defaults=templatekw.keywords,
resources=tres)
def formatnode(repo, ctx):
Yuya Nishihara
templater: switch 'revcache' based on new mapping items...
r37121 props = {'ctx': ctx, 'repo': repo}
Yuya Nishihara
templater: rename .render(mapping) to .renderdefault(mapping) (API)...
r37003 return templ.renderdefault(props)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 return formatnode
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None, props=None):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 props = props or {}
formatnode = _graphnodeformatter(ui, displayer)
state = graphmod.asciistate()
styles = state['styles']
# only set graph styling if HGPLAIN is not set.
if ui.plain('graph'):
# set all edge styles to |, the default pre-3.8 behaviour
styles.update(dict.fromkeys(styles, '|'))
else:
edgetypes = {
'parent': graphmod.PARENT,
'grandparent': graphmod.GRANDPARENT,
'missing': graphmod.MISSINGPARENT
}
for name, key in edgetypes.items():
# experimental config: experimental.graphstyle.*
styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
styles[key])
if not styles[key]:
styles[key] = None
# experimental config: experimental.graphshorten
state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
for rev, type, ctx, parents in dag:
char = formatnode(repo, ctx)
copies = None
if getrenamed and ctx.rev():
copies = []
for fn in ctx.files():
rename = getrenamed(fn, ctx.rev())
if rename:
Martin von Zweigbergk
templatekw: make getrenamed() return only filename, not nodeid...
r38185 copies.append((fn, rename))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 edges = edgefn(type, char, state, rev, parents)
firstedge = next(edges)
width = firstedge[2]
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 displayer.show(ctx, copies=copies,
Yuya Nishihara
templatekw: simply override {graphwidth} function by mapping variable
r36459 graphwidth=width, **pycompat.strkwargs(props))
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 lines = displayer.hunk.pop(rev).split('\n')
if not lines[-1]:
del lines[-1]
displayer.flush(ctx)
for type, char, width, coldata in itertools.chain([firstedge], edges):
graphmod.ascii(ui, state, type, char, lines, coldata)
lines = []
displayer.close()
Yuya Nishihara
log: factor out function to feed revisions to displayer
r36216 def displaygraphrevs(ui, repo, revs, displayer, getrenamed):
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 revdag = graphmod.dagwalker(repo, revs)
Yuya Nishihara
logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API)...
r36020 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903
Yuya Nishihara
log: factor out function to feed revisions to displayer
r36216 def displayrevs(ui, repo, revs, displayer, getrenamed):
for rev in revs:
ctx = repo[rev]
copies = None
if getrenamed is not None and rev:
copies = []
for fn in ctx.files():
rename = getrenamed(fn, rev)
if rename:
Martin von Zweigbergk
templatekw: make getrenamed() return only filename, not nodeid...
r38185 copies.append((fn, rename))
Yuya Nishihara
log: factor out function to feed revisions to displayer
r36216 displayer.show(ctx, copies=copies)
displayer.flush(ctx)
displayer.close()
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 def checkunsupportedgraphflags(pats, opts):
for op in ["newest_first"]:
if op in opts and opts[op]:
raise error.Abort(_("-G/--graph option is incompatible with --%s")
% op.replace("_", "-"))
def graphrevs(repo, nodes, opts):
Yuya Nishihara
logcmdutil: drop redundant "log" from function names (API)...
r35905 limit = getlimit(opts)
Yuya Nishihara
cmdutil: split functions of log-like commands to new module (API)...
r35903 nodes.reverse()
if limit is not None:
nodes = nodes[:limit]
return graphmod.nodes(repo, nodes)