##// END OF EJS Templates
Cleanup of whitespace, indentation and line continuation.
Cleanup of whitespace, indentation and line continuation.

File last commit:

r4633:ff7253a0 default
r4633:ff7253a0 default
Show More
cmdutil.py
1225 lines | 42.4 KiB | text/x-python | PythonLexer
Vadim Gelfer
fix comment.
r2957 # cmdutil.py - help for command processing in mercurial
Vadim Gelfer
refactor text diff/patch code....
r2874 #
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
from node import *
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Brendan Cully
dispatch: restore a dropped shlex import...
r4563 import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex
Patrick Mezard
cmdutil: add missing "time" import.
r4598 import mdiff, bdiff, util, templater, patch, commands, hg, lock, time
Matt Mackall
dispatch: add generic pre- and post-command hooks
r4630 import fancyopts, revlog, version, extensions, hook
Vadim Gelfer
refactor text diff/patch code....
r2874
Brendan Cully
Move revision parsing into cmdutil.
r3090 revrangesep = ':'
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 class UnknownCommand(Exception):
"""Exception raised if command is not in the command table."""
class AmbiguousCommand(Exception):
"""Exception raised if command shortcut matches more than one command."""
class ParseError(Exception):
"""Exception raised on errors in parsing the command line."""
Matt Mackall
dispatch: rename variables after code motion
r4551 def runcatch(ui, args):
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 def catchterm(*args):
raise util.SignalInterrupt
for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
num = getattr(signal, name, None)
if num: signal.signal(num, catchterm)
try:
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 try:
# enter the debugger before command execution
if '--debugger' in args:
pdb.set_trace()
try:
Matt Mackall
dispatch: rename variables after code motion
r4551 return dispatch(ui, args)
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 finally:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.flush()
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 except:
# enter the debugger when we hit an exception
if '--debugger' in args:
pdb.post_mortem(sys.exc_info()[2])
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.print_exc()
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 raise
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except ParseError, inst:
if inst.args[0]:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
commands.help_(ui, inst.args[0])
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 else:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("hg: %s\n") % inst.args[1])
commands.help_(ui, 'shortlist')
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except AmbiguousCommand, inst:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 (inst.args[0], " ".join(inst.args[1])))
except UnknownCommand, inst:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
commands.help_(ui, 'shortlist')
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except hg.RepoError, inst:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s!\n") % inst)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except lock.LockHeld, inst:
if inst.errno == errno.ETIMEDOUT:
reason = _('timed out waiting for lock held by %s') % inst.locker
else:
reason = _('lock held by %s') % inst.locker
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except lock.LockUnavailable, inst:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: could not lock %s: %s\n") %
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 (inst.desc or inst.filename, inst.strerror))
except revlog.RevlogError, inst:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s!\n") % inst)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except util.SignalInterrupt:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("killed!\n"))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except KeyboardInterrupt:
try:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("interrupted!\n"))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except IOError, inst:
if inst.errno == errno.EPIPE:
Matt Mackall
dispatch: rename variables after code motion
r4551 if ui.debugflag:
ui.warn(_("\nbroken pipe\n"))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 else:
raise
except socket.error, inst:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s\n") % inst[1])
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except IOError, inst:
if hasattr(inst, "code"):
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s\n") % inst)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 elif hasattr(inst, "reason"):
try: # usually it is in the form (errno, strerror)
reason = inst.reason.args[1]
except: # it might be anything, for example a string
reason = inst.reason
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: error: %s\n") % reason)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
Matt Mackall
dispatch: rename variables after code motion
r4551 if ui.debugflag:
ui.warn(_("broken pipe\n"))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 elif getattr(inst, "strerror", None):
if getattr(inst, "filename", None):
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 else:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s\n") % inst.strerror)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 else:
raise
except OSError, inst:
if getattr(inst, "filename", None):
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 else:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s\n") % inst.strerror)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except util.UnexpectedOutput, inst:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s") % inst[0])
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 if not isinstance(inst[1], basestring):
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(" %r\n" % (inst[1],))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 elif not inst[1]:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_(" empty string\n"))
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 else:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
Matt Mackall
dispatch: report failed imports nicely
r4632 except ImportError, inst:
m = str(inst).split()[-1]
ui.warn(_("abort: could not import module %s!\n" % m))
if m in "mpatch bdiff".split():
ui.warn(_("(did you forget to compile extensions?)\n"))
elif m in "zlib".split():
ui.warn(_("(is your Python install correct?)\n"))
Thomas Arendsen Hein
Cleanup of whitespace, indentation and line continuation.
r4633
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except util.Abort, inst:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("abort: %s\n") % inst)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except SystemExit, inst:
# Commands shouldn't sys.exit directly, but give a return code.
# Just in case catch this and and pass exit code to caller.
return inst.code
except:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("** unknown exception encountered, details follow\n"))
ui.warn(_("** report bug details to "
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 "http://www.selenic.com/mercurial/bts\n"))
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("** or mercurial@selenic.com\n"))
ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 % version.get_version())
raise
return -1
def findpossible(ui, cmd):
"""
Return cmd -> (aliases, command table entry)
for each matching command.
Return debug commands (or their aliases) only if no normal command matches.
"""
choice = {}
debugchoice = {}
for e in commands.table.keys():
aliases = e.lstrip("^").split("|")
found = None
if cmd in aliases:
found = cmd
elif not ui.config("ui", "strict"):
for a in aliases:
if a.startswith(cmd):
found = a
break
if found is not None:
if aliases[0].startswith("debug") or found.startswith("debug"):
debugchoice[found] = (aliases, commands.table[e])
else:
choice[found] = (aliases, commands.table[e])
if not choice and debugchoice:
choice = debugchoice
return choice
def findcmd(ui, cmd):
"""Return (aliases, command table entry) for command string."""
choice = findpossible(ui, cmd)
if choice.has_key(cmd):
return choice[cmd]
if len(choice) > 1:
clist = choice.keys()
clist.sort()
raise AmbiguousCommand(cmd, clist)
if choice:
return choice.values()[0]
raise UnknownCommand(cmd)
Matt Mackall
dispatch: move findrepo to cmdutil
r4556 def findrepo():
p = os.getcwd()
while not os.path.isdir(os.path.join(p, ".hg")):
oldp, p = p, os.path.dirname(p)
if p == oldp:
return None
return p
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 def parse(ui, args):
options = {}
cmdoptions = {}
try:
args = fancyopts.fancyopts(args, commands.globalopts, options)
except fancyopts.getopt.GetoptError, inst:
raise ParseError(None, inst)
if args:
cmd, args = args[0], args[1:]
aliases, i = findcmd(ui, cmd)
cmd = aliases[0]
defaults = ui.config("defaults", cmd)
if defaults:
args = shlex.split(defaults) + args
c = list(i[1])
else:
cmd = None
c = []
# combine global options into local
for o in commands.globalopts:
c.append((o[0], o[1], options[o[1]], o[3]))
try:
args = fancyopts.fancyopts(args, c, cmdoptions)
except fancyopts.getopt.GetoptError, inst:
raise ParseError(cmd, inst)
# separate global options back out
for o in commands.globalopts:
n = o[1]
options[n] = cmdoptions[n]
del cmdoptions[n]
return (cmd, cmd and i[0] or None, args, options, cmdoptions)
def parseconfig(config):
"""parse the --config options from the command line"""
parsed = []
for cfg in config:
try:
name, value = cfg.split('=', 1)
section, name = name.split('.', 1)
if not section or not name:
raise IndexError
parsed.append((section, name, value))
except (IndexError, ValueError):
raise util.Abort(_('malformed --config option: %s') % cfg)
return parsed
Matt Mackall
dispatch: parse and apply cwd opt early
r4552 def earlygetopt(aliases, args):
if "--" in args:
args = args[:args.index("--")]
for opt in aliases:
if opt in args:
return args[args.index(opt) + 1]
return None
Matt Mackall
dispatch: rename variables after code motion
r4551 def dispatch(ui, args):
Matt Mackall
dispatch: parse and apply cwd opt early
r4552 # check for cwd first
cwd = earlygetopt(['--cwd'], args)
if cwd:
os.chdir(cwd)
Matt Mackall
localrepo: don't search from path...
r4555 # read the local repository .hgrc into a local ui object
# this will trigger its extensions to load
Matt Mackall
dispatch: accept an abbreviation of --repository
r4557 path = earlygetopt(["-R", "--repository", "--repo"], args)
Matt Mackall
localrepo: don't search from path...
r4555 if not path:
Matt Mackall
dispatch: move findrepo to cmdutil
r4556 path = findrepo() or ""
Matt Mackall
encoding: pull fallbackencoding out of localrepo into early parsing
r4619 lui = ui
Matt Mackall
dispatch: use the repo path found by early parsing...
r4554 if path:
Matt Mackall
dispatch: parse and apply -R early...
r4553 try:
lui = commands.ui.ui(parentui=ui)
Matt Mackall
dispatch: use the repo path found by early parsing...
r4554 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
Matt Mackall
dispatch: parse and apply -R early...
r4553 except IOError:
Matt Mackall
encoding: pull fallbackencoding out of localrepo into early parsing
r4619 pass
extensions.loadall(lui)
# check for fallback encoding
fallback = lui.config('ui', 'fallbackencoding')
if fallback:
util._fallbackencoding = fallback
Matt Mackall
dispatch: parse and apply -R early...
r4553
Matt Mackall
dispatch: add generic pre- and post-command hooks
r4630 fullargs = args
Matt Mackall
dispatch: rename variables after code motion
r4551 cmd, func, args, options, cmdoptions = parse(ui, args)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549
if options["encoding"]:
util._encoding = options["encoding"]
if options["encodingmode"]:
util._encodingmode = options["encodingmode"]
if options["time"]:
def get_times():
t = os.times()
if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
t = (t[0], t[1], t[2], t[3], time.clock())
return t
s = get_times()
def print_time():
t = get_times()
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
atexit.register(print_time)
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 not options["noninteractive"], options["traceback"],
parseconfig(options["config"]))
if options['help']:
Matt Mackall
dispatch: rename variables after code motion
r4551 return commands.help_(ui, cmd, options['version'])
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 elif options['version']:
Matt Mackall
dispatch: rename variables after code motion
r4551 return commands.version_(ui)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 elif not cmd:
Matt Mackall
dispatch: rename variables after code motion
r4551 return commands.help_(ui, 'shortlist')
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549
Matt Mackall
dispatch: add generic pre- and post-command hooks
r4630 repo = None
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 if cmd not in commands.norepo.split():
try:
Matt Mackall
dispatch: use the repo path found by early parsing...
r4554 repo = hg.repository(ui, path=path)
Matt Mackall
localrepo: don't search from path...
r4555 ui = repo.ui
Matt Mackall
dispatch: use the repo path found by early parsing...
r4554 if not repo.local():
raise util.Abort(_("repository '%s' is not local") % path)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 except hg.RepoError:
if cmd not in commands.optionalrepo.split():
raise
Matt Mackall
dispatch: rename variables after code motion
r4551 d = lambda: func(ui, repo, *args, **cmdoptions)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 else:
Matt Mackall
dispatch: rename variables after code motion
r4551 d = lambda: func(ui, *args, **cmdoptions)
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549
Matt Mackall
dispatch: add generic pre- and post-command hooks
r4630 # run pre-hook, and abort if it fails
ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
if ret:
return ret
ret = runcommand(ui, options, cmd, d)
# run post-hook, passing command result
hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
Thomas Arendsen Hein
Cleanup of whitespace, indentation and line continuation.
r4633 result = ret)
Matt Mackall
dispatch: add generic pre- and post-command hooks
r4630 return ret
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549
Matt Mackall
dispatch: fix handling of incorrect number of arguments
r4621 def runcommand(ui, options, cmd, cmdfunc):
def checkargs():
try:
return cmdfunc()
except TypeError, inst:
# was this an argument error?
tb = traceback.extract_tb(sys.exc_info()[2])
if len(tb) != 2: # no
raise
raise ParseError(cmd, _("invalid arguments"))
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 if options['profile']:
import hotshot, hotshot.stats
prof = hotshot.Profile("hg.prof")
Matt Mackall
dispatch: move runcommand to cmdutil
r4548 try:
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 try:
Matt Mackall
dispatch: fix handling of incorrect number of arguments
r4621 return prof.runcall(checkargs)
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 except:
Matt Mackall
dispatch: move runcommand to cmdutil
r4548 try:
Matt Mackall
dispatch: rename variables after code motion
r4551 ui.warn(_('exception raised - generating '
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 'profile anyway\n'))
except:
pass
raise
Matt Mackall
dispatch: move runcommand to cmdutil
r4548 finally:
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 prof.close()
stats = hotshot.stats.load("hg.prof")
stats.strip_dirs()
stats.sort_stats('time', 'calls')
stats.print_stats(40)
elif options['lsprof']:
try:
from mercurial import lsprof
except ImportError:
raise util.Abort(_(
'lsprof not available - install from '
'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
p = lsprof.Profiler()
p.enable(subcalls=True)
try:
Matt Mackall
dispatch: fix handling of incorrect number of arguments
r4621 return checkargs()
Matt Mackall
dispatch: hoist debugging hook to runcatch
r4550 finally:
p.disable()
stats = lsprof.Stats(p.getstats())
stats.sort()
stats.pprint(top=10, file=sys.stderr, climit=5)
else:
Matt Mackall
dispatch: fix handling of incorrect number of arguments
r4621 return checkargs()
Matt Mackall
dispatch: move runcommand to cmdutil
r4548
Matt Mackall
dispatch: move dispatching code to cmdutil
r4549 def bail_if_changed(repo):
modified, added, removed, deleted = repo.status()[:4]
if modified or added or removed or deleted:
raise util.Abort(_("outstanding uncommitted changes"))
def logmessage(opts):
""" get the log message according to -m and -l option """
message = opts['message']
logfile = opts['logfile']
if message and logfile:
raise util.Abort(_('options --message and --logfile are mutually '
'exclusive'))
if not message and logfile:
try:
if logfile == '-':
message = sys.stdin.read()
else:
message = open(logfile).read()
except IOError, inst:
raise util.Abort(_("can't read commit message '%s': %s") %
(logfile, inst.strerror))
return message
def setremoteconfig(ui, opts):
"copy remote options to ui tree"
if opts.get('ssh'):
ui.setconfig("ui", "ssh", opts['ssh'])
if opts.get('remotecmd'):
ui.setconfig("ui", "remotecmd", opts['remotecmd'])
Matt Mackall
Add support for url#id syntax...
r4478 def parseurl(url, revs):
'''parse url#branch, returning url, branch + revs'''
if '#' not in url:
return url, (revs or None)
url, rev = url.split('#', 1)
return url, revs + [rev]
Thomas Arendsen Hein
Removed unused ui parameter from revpair/revrange and fix its users.
r3707 def revpair(repo, revs):
Brendan Cully
Move revision parsing into cmdutil.
r3090 '''return pair of nodes, given list of revisions. second item can
be None, meaning use working dir.'''
Matt Mackall
simplify revrange and revpair
r3525
def revfix(repo, val, defval):
Alexis S. L. Carvalho
fix hg diff -r ''
r3825 if not val and val != 0 and defval is not None:
Matt Mackall
simplify revrange and revpair
r3525 val = defval
return repo.lookup(val)
Brendan Cully
Move revision parsing into cmdutil.
r3090 if not revs:
return repo.dirstate.parents()[0], None
end = None
if len(revs) == 1:
Matt Mackall
simplify revrange and revpair
r3525 if revrangesep in revs[0]:
start, end = revs[0].split(revrangesep, 1)
Brendan Cully
Move revision parsing into cmdutil.
r3090 start = revfix(repo, start, 0)
end = revfix(repo, end, repo.changelog.count() - 1)
else:
Matt Mackall
simplify revrange and revpair
r3525 start = revfix(repo, revs[0], None)
Brendan Cully
Move revision parsing into cmdutil.
r3090 elif len(revs) == 2:
if revrangesep in revs[0] or revrangesep in revs[1]:
raise util.Abort(_('too many revisions specified'))
start = revfix(repo, revs[0], None)
end = revfix(repo, revs[1], None)
else:
raise util.Abort(_('too many revisions specified'))
Matt Mackall
simplify revrange and revpair
r3525 return start, end
Brendan Cully
Move revision parsing into cmdutil.
r3090
Thomas Arendsen Hein
Removed unused ui parameter from revpair/revrange and fix its users.
r3707 def revrange(repo, revs):
Brendan Cully
Move revision parsing into cmdutil.
r3090 """Yield revision as strings from a list of revision specifications."""
Matt Mackall
simplify revrange and revpair
r3525
def revfix(repo, val, defval):
Alexis S. L. Carvalho
fix hg log -r ''
r3718 if not val and val != 0 and defval is not None:
Matt Mackall
simplify revrange and revpair
r3525 return defval
return repo.changelog.rev(repo.lookup(val))
Matt Mackall
Make revrange return a list of ints so that callers don't have to convert
r3526 seen, l = {}, []
Brendan Cully
Move revision parsing into cmdutil.
r3090 for spec in revs:
if revrangesep in spec:
start, end = spec.split(revrangesep, 1)
start = revfix(repo, start, 0)
end = revfix(repo, end, repo.changelog.count() - 1)
step = start > end and -1 or 1
for rev in xrange(start, end+step, step):
if rev in seen:
continue
seen[rev] = 1
Matt Mackall
Make revrange return a list of ints so that callers don't have to convert
r3526 l.append(rev)
Brendan Cully
Move revision parsing into cmdutil.
r3090 else:
rev = revfix(repo, spec, None)
if rev in seen:
continue
seen[rev] = 1
Matt Mackall
Make revrange return a list of ints so that callers don't have to convert
r3526 l.append(rev)
return l
Brendan Cully
Move revision parsing into cmdutil.
r3090
Vadim Gelfer
refactor text diff/patch code....
r2874 def make_filename(repo, pat, node,
total=None, seqno=None, revwidth=None, pathname=None):
node_expander = {
'H': lambda: hex(node),
'R': lambda: str(repo.changelog.rev(node)),
'h': lambda: short(node),
}
expander = {
'%': lambda: '%',
'b': lambda: os.path.basename(repo.root),
}
try:
if node:
expander.update(node_expander)
if node and revwidth is not None:
expander['r'] = (lambda:
str(repo.changelog.rev(node)).zfill(revwidth))
if total is not None:
expander['N'] = lambda: str(total)
if seqno is not None:
expander['n'] = lambda: str(seqno)
if total is not None and seqno is not None:
Thomas Arendsen Hein
white space and line break cleanups
r3673 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
Vadim Gelfer
refactor text diff/patch code....
r2874 if pathname is not None:
expander['s'] = lambda: os.path.basename(pathname)
expander['d'] = lambda: os.path.dirname(pathname) or '.'
expander['p'] = lambda: pathname
newname = []
patlen = len(pat)
i = 0
while i < patlen:
c = pat[i]
if c == '%':
i += 1
c = pat[i]
c = expander[c]()
newname.append(c)
i += 1
return ''.join(newname)
except KeyError, inst:
Thomas Arendsen Hein
Never apply string formatting to generated errors with util.Abort....
r3072 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
inst.args[0])
Vadim Gelfer
refactor text diff/patch code....
r2874
def make_file(repo, pat, node=None,
total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
if not pat or pat == '-':
return 'w' in mode and sys.stdout or sys.stdin
if hasattr(pat, 'write') and 'w' in mode:
return pat
if hasattr(pat, 'read') and 'r' in mode:
return pat
return open(make_filename(repo, pat, node, total, seqno, revwidth,
pathname),
mode)
Vadim Gelfer
move walk and matchpats from commands to cmdutil.
r2882
Alexis S. L. Carvalho
remove unused "head" hack from util._matcher
r4197 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
Vadim Gelfer
move walk and matchpats from commands to cmdutil.
r2882 cwd = repo.getcwd()
Alexis S. L. Carvalho
Leave normalization of patterns to util._matcher...
r4186 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
Alexis S. L. Carvalho
remove unused "head" hack from util._matcher
r4197 opts.get('exclude'), globbed=globbed,
Alexis S. L. Carvalho
change locate to use relglobs by default...
r4195 default=default)
Vadim Gelfer
move walk and matchpats from commands to cmdutil.
r2882
Alexis S. L. Carvalho
remove unused "head" hack from util._matcher
r4197 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
default=None):
files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
default=default)
Matt Mackall
convert dict(zip(x,x)) to dict.fromkeys(x)
r3531 exact = dict.fromkeys(files)
Alexis S. L. Carvalho
Add dirstate.pathto and localrepo.pathto....
r4525 cwd = repo.getcwd()
Matt Mackall
kill makewalk function
r3528 for src, fn in repo.walk(node=node, files=files, match=matchfn,
badmatch=badmatch):
Alexis S. L. Carvalho
Add dirstate.pathto and localrepo.pathto....
r4525 yield src, fn, repo.pathto(fn, cwd), fn in exact
Vadim Gelfer
move commands.addremove_lock to cmdutil.addremove
r2883
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 def findrenames(repo, added=None, removed=None, threshold=0.5):
Erling Ellingsen
Avoid some false positives for addremove -s...
r4135 '''find renamed files -- yields (before, after, score) tuples'''
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 if added is None or removed is None:
added, removed = repo.status()[1:3]
Benoit Boissinot
cmdutil.py: use contexts in findrenames
r3971 ctx = repo.changectx()
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 for a in added:
aa = repo.wread(a)
Erling Ellingsen
Avoid some false positives for addremove -s...
r4135 bestname, bestscore = None, threshold
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 for r in removed:
Benoit Boissinot
cmdutil.py: use contexts in findrenames
r3971 rr = ctx.filectx(r).data()
Erling Ellingsen
Avoid some false positives for addremove -s...
r4135
# bdiff.blocks() returns blocks of matching lines
# count the number of bytes in each
equal = 0
alines = mdiff.splitnewlines(aa)
matches = bdiff.blocks(aa, rr)
for x1,x2,y1,y2 in matches:
for line in alines[x1:x2]:
equal += len(line)
Thomas Arendsen Hein
addremove: comparing two empty files caused ZeroDivisionError...
r4472 lengths = len(aa) + len(rr)
if lengths:
myscore = equal*2.0 / lengths
if myscore >= bestscore:
bestname, bestscore = r, myscore
Erling Ellingsen
Avoid some false positives for addremove -s...
r4135 if bestname:
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 yield bestname, a, bestscore
def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
similarity=None):
Vadim Gelfer
move commands.addremove_lock to cmdutil.addremove
r2883 if dry_run is None:
dry_run = opts.get('dry_run')
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 if similarity is None:
similarity = float(opts.get('similarity') or 0)
Vadim Gelfer
move commands.addremove_lock to cmdutil.addremove
r2883 add, remove = [], []
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 mapping = {}
Vadim Gelfer
move commands.addremove_lock to cmdutil.addremove
r2883 for src, abs, rel, exact in walk(repo, pats, opts):
Alexis S. L. Carvalho
Use absolute paths in addremove....
r4522 target = repo.wjoin(abs)
Vadim Gelfer
move commands.addremove_lock to cmdutil.addremove
r2883 if src == 'f' and repo.dirstate.state(abs) == '?':
add.append(abs)
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 mapping[abs] = rel, exact
Vadim Gelfer
move commands.addremove_lock to cmdutil.addremove
r2883 if repo.ui.verbose or not exact:
repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
Alexis S. L. Carvalho
addremove: use util.lexists
r4573 if repo.dirstate.state(abs) != 'r' and not util.lexists(target):
Vadim Gelfer
move commands.addremove_lock to cmdutil.addremove
r2883 remove.append(abs)
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 mapping[abs] = rel, exact
Vadim Gelfer
move commands.addremove_lock to cmdutil.addremove
r2883 if repo.ui.verbose or not exact:
repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
if not dry_run:
repo.add(add, wlock=wlock)
repo.remove(remove, wlock=wlock)
Vadim Gelfer
addremove: add -s/--similarity option...
r2958 if similarity > 0:
for old, new, score in findrenames(repo, add, remove, similarity):
oldrel, oldexact = mapping[old]
newrel, newexact = mapping[new]
if repo.ui.verbose or not oldexact or not newexact:
repo.ui.status(_('recording removal of %s as rename to %s '
'(%d%% similar)\n') %
(oldrel, newrel, score * 100))
if not dry_run:
repo.copy(old, new, wlock=wlock)
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643
Bryan O'Sullivan
Refactor commands.serve to allow other commands to run as services....
r4380 def service(opts, parentfn=None, initfn=None, runfn=None):
'''Run a command as a service.'''
if opts['daemon'] and not opts['daemon_pipefds']:
rfd, wfd = os.pipe()
args = sys.argv[:]
args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
args[0], args)
os.close(wfd)
os.read(rfd, 1)
if parentfn:
return parentfn(pid)
else:
os._exit(0)
if initfn:
initfn()
if opts['pid_file']:
fp = open(opts['pid_file'], 'w')
fp.write(str(os.getpid()) + '\n')
fp.close()
if opts['daemon_pipefds']:
rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
os.close(rfd)
try:
os.setsid()
except AttributeError:
pass
os.write(wfd, 'y')
os.close(wfd)
sys.stdout.flush()
sys.stderr.flush()
fd = os.open(util.nulldev, os.O_RDWR)
if fd != 0: os.dup2(fd, 0)
if fd != 1: os.dup2(fd, 1)
if fd != 2: os.dup2(fd, 2)
if fd not in (0, 1, 2): os.close(fd)
if runfn:
return runfn()
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 class changeset_printer(object):
'''show changeset information when templating not requested.'''
Matt Mackall
Remove deprecated old-style branch support
r3876 def __init__(self, ui, repo, patch, buffered):
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 self.ui = ui
self.repo = repo
Matt Mackall
Refactor log ui buffering and patch display
r3645 self.buffered = buffered
self.patch = patch
Matt Mackall
use ui buffering in changeset printer...
r3738 self.header = {}
self.hunk = {}
self.lastheader = None
Matt Mackall
Refactor log ui buffering and patch display
r3645
def flush(self, rev):
Matt Mackall
use ui buffering in changeset printer...
r3738 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]
return 1
return 0
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643
Alexis S. L. Carvalho
fix 'hg <not-log> -v --template foo' with revisions without copies
r4351 def show(self, rev=0, changenode=None, copies=(), **props):
Matt Mackall
use ui buffering in changeset printer...
r3738 if self.buffered:
self.ui.pushbuffer()
self._show(rev, changenode, copies, props)
self.hunk[rev] = self.ui.popbuffer()
else:
self._show(rev, changenode, copies, props)
def _show(self, rev, changenode, copies, props):
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 '''show a single changeset or file revision'''
log = self.repo.changelog
if changenode is None:
changenode = log.node(rev)
elif not rev:
rev = log.rev(changenode)
if self.ui.quiet:
self.ui.write("%d:%s\n" % (rev, short(changenode)))
return
changes = log.read(changenode)
date = util.datestr(changes[2])
extra = changes[5]
branch = extra.get("branch")
hexfunc = self.ui.debugflag and hex or short
parents = log.parentrevs(rev)
if not self.ui.debugflag:
if parents[1] == nullrev:
if parents[0] >= rev - 1:
parents = []
else:
parents = [parents[0]]
parents = [(p, hexfunc(log.node(p))) for p in parents]
self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
Alexis S. L. Carvalho
"default" is the default branch name
r4176 # don't show the default branch name
if branch != 'default':
Alexis S. L. Carvalho
log: convert branch names to the local encoding
r3827 branch = util.tolocal(branch)
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 self.ui.write(_("branch: %s\n") % branch)
for tag in self.repo.nodetags(changenode):
self.ui.write(_("tag: %s\n") % tag)
for parent in parents:
self.ui.write(_("parent: %d:%s\n") % parent)
if self.ui.debugflag:
self.ui.write(_("manifest: %d:%s\n") %
(self.repo.manifest.rev(changes[0]), hex(changes[0])))
self.ui.write(_("user: %s\n") % changes[1])
self.ui.write(_("date: %s\n") % date)
if self.ui.debugflag:
files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
for key, value in zip([_("files:"), _("files+:"), _("files-:")],
files):
if value:
self.ui.write("%-12s %s\n" % (key, " ".join(value)))
elif changes[3] and self.ui.verbose:
self.ui.write(_("files: %s\n") % " ".join(changes[3]))
if copies and self.ui.verbose:
copies = ['%s (%s)' % c for c in copies]
self.ui.write(_("copies: %s\n") % ' '.join(copies))
if extra and self.ui.debugflag:
extraitems = extra.items()
extraitems.sort()
for key, value in extraitems:
self.ui.write(_("extra: %s=%s\n")
% (key, value.encode('string_escape')))
description = changes[4].strip()
if description:
if self.ui.verbose:
self.ui.write(_("description:\n"))
self.ui.write(description)
self.ui.write("\n\n")
else:
self.ui.write(_("summary: %s\n") %
description.splitlines()[0])
self.ui.write("\n")
Matt Mackall
Refactor log ui buffering and patch display
r3645 self.showpatch(changenode)
def showpatch(self, node):
if self.patch:
prev = self.repo.changelog.parents(node)[0]
Matt Mackall
Fix log regression where log -p file showed diffs for other files
r3837 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui)
Matt Mackall
Refactor log ui buffering and patch display
r3645 self.ui.write("\n")
class changeset_templater(changeset_printer):
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 '''format changeset information.'''
Matt Mackall
Remove deprecated old-style branch support
r3876 def __init__(self, ui, repo, patch, mapfile, buffered):
changeset_printer.__init__(self, ui, repo, patch, buffered)
Alexis S. L. Carvalho
command line templates: add formatnode filter...
r4352 filters = templater.common_filters.copy()
filters['formatnode'] = (ui.debugflag and (lambda x: x)
or (lambda x: x[:12]))
self.t = templater.templater(mapfile, filters,
cache={
'parent': '{rev}:{node|formatnode} ',
'manifest': '{rev}:{node|formatnode}',
'filecopy': '{name} ({source})'})
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643
def use_template(self, t):
'''set template string to use'''
self.t.cache['changeset'] = t
Matt Mackall
use ui buffering in changeset printer...
r3738 def _show(self, rev, changenode, copies, props):
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 '''show a single changeset or file revision'''
log = self.repo.changelog
if changenode is None:
changenode = log.node(rev)
elif not rev:
rev = log.rev(changenode)
changes = log.read(changenode)
def showlist(name, values, plural=None, **args):
'''expand set of values.
name is name of key in template map.
values is list of strings or dicts.
plural is plural of name, if not simply name + 's'.
expansion works like this, given name 'foo'.
if values is empty, expand 'no_foos'.
if 'foo' not in template map, return values as a string,
joined by space.
expand 'start_foos'.
for each value, expand 'foo'. if 'last_foo' in template
map, expand it instead of 'foo' for last key.
expand 'end_foos'.
'''
if plural: names = plural
else: names = name + 's'
if not values:
noname = 'no_' + names
if noname in self.t:
yield self.t(noname, **args)
return
if name not in self.t:
if isinstance(values[0], str):
yield ' '.join(values)
else:
for v in values:
yield dict(v, **args)
return
startname = 'start_' + names
if startname in self.t:
yield self.t(startname, **args)
vargs = args.copy()
def one(v, tag=name):
try:
vargs.update(v)
except (AttributeError, ValueError):
try:
for a, b in v:
vargs[a] = b
except ValueError:
vargs[name] = v
return self.t(tag, **vargs)
lastname = 'last_' + name
if lastname in self.t:
last = values.pop()
else:
last = None
for v in values:
yield one(v)
if last is not None:
yield one(last, tag=lastname)
endname = 'end_' + names
if endname in self.t:
yield self.t(endname, **args)
def showbranches(**args):
branch = changes[5].get("branch")
Alexis S. L. Carvalho
"default" is the default branch name
r4176 if branch != 'default':
Alexis S. L. Carvalho
log: convert branch names to the local encoding
r3827 branch = util.tolocal(branch)
Matt Mackall
changeset templater: convert some unnecessary yields to returns
r3648 return showlist('branch', [branch], plural='branches', **args)
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643
def showparents(**args):
parents = [[('rev', log.rev(p)), ('node', hex(p))]
for p in log.parents(changenode)
if self.ui.debugflag or p != nullid]
if (not self.ui.debugflag and len(parents) == 1 and
parents[0][0][1] == rev - 1):
return
return showlist('parent', parents, **args)
def showtags(**args):
return showlist('tag', self.repo.nodetags(changenode), **args)
def showextras(**args):
extras = changes[5].items()
extras.sort()
for key, value in extras:
args = args.copy()
args.update(dict(key=key, value=value))
yield self.t('extra', **args)
def showcopies(**args):
c = [{'name': x[0], 'source': x[1]} for x in copies]
return showlist('file_copy', c, plural='file_copies', **args)
if self.ui.debugflag:
files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
def showfiles(**args):
return showlist('file', files[0], **args)
def showadds(**args):
return showlist('file_add', files[1], **args)
def showdels(**args):
return showlist('file_del', files[2], **args)
def showmanifest(**args):
args = args.copy()
args.update(dict(rev=self.repo.manifest.rev(changes[0]),
node=hex(changes[0])))
return self.t('manifest', **args)
else:
def showfiles(**args):
Matt Mackall
changeset templater: convert some unnecessary yields to returns
r3648 return showlist('file', changes[3], **args)
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 showadds = ''
showdels = ''
showmanifest = ''
defprops = {
'author': changes[1],
'branches': showbranches,
'date': changes[2],
'desc': changes[4],
'file_adds': showadds,
'file_dels': showdels,
'files': showfiles,
'file_copies': showcopies,
'manifest': showmanifest,
'node': hex(changenode),
'parents': showparents,
'rev': rev,
'tags': showtags,
'extras': showextras,
}
props = props.copy()
props.update(defprops)
try:
if self.ui.debugflag and 'header_debug' in self.t:
key = 'header_debug'
elif self.ui.quiet and 'header_quiet' in self.t:
key = 'header_quiet'
elif self.ui.verbose and 'header_verbose' in self.t:
key = 'header_verbose'
elif 'header' in self.t:
key = 'header'
else:
key = ''
if key:
Matt Mackall
Refactor log ui buffering and patch display
r3645 h = templater.stringify(self.t(key, **props))
if self.buffered:
Matt Mackall
use ui buffering in changeset printer...
r3738 self.header[rev] = h
Matt Mackall
Refactor log ui buffering and patch display
r3645 else:
self.ui.write(h)
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 if self.ui.debugflag and 'changeset_debug' in self.t:
key = 'changeset_debug'
elif self.ui.quiet and 'changeset_quiet' in self.t:
key = 'changeset_quiet'
elif self.ui.verbose and 'changeset_verbose' in self.t:
key = 'changeset_verbose'
else:
key = 'changeset'
Matt Mackall
Refactor log ui buffering and patch display
r3645 self.ui.write(templater.stringify(self.t(key, **props)))
self.showpatch(changenode)
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 except KeyError, inst:
raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
inst.args[0]))
except SyntaxError, inst:
raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
Matt Mackall
Fix log regression where log -p file showed diffs for other files
r3837 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 """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,
regular display via changeset_printer() is done.
"""
# options
Matt Mackall
Fix log regression where log -p file showed diffs for other files
r3837 patch = False
if opts.get('patch'):
Thomas Arendsen Hein
Use util.always instead of creating a new lambda function in show_changeset...
r3838 patch = matchfn or util.always
Matt Mackall
Fix log regression where log -p file showed diffs for other files
r3837
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 tmpl = opts.get('template')
mapfile = None
if tmpl:
tmpl = templater.parsestring(tmpl, quoted=False)
else:
mapfile = opts.get('style')
# ui settings
if not mapfile:
tmpl = ui.config('ui', 'logtemplate')
if tmpl:
tmpl = templater.parsestring(tmpl)
else:
mapfile = ui.config('ui', 'style')
if tmpl or mapfile:
if mapfile:
if not os.path.split(mapfile)[0]:
mapname = (templater.templatepath('map-cmdline.' + mapfile)
or templater.templatepath(mapfile))
if mapname: mapfile = mapname
try:
Matt Mackall
Remove deprecated old-style branch support
r3876 t = changeset_templater(ui, repo, patch, mapfile, buffered)
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643 except SyntaxError, inst:
raise util.Abort(inst.args[0])
if tmpl: t.use_template(tmpl)
return t
Matt Mackall
Remove deprecated old-style branch support
r3876 return changeset_printer(ui, repo, patch, buffered)
Matt Mackall
templates: move changeset templating bits to cmdutils
r3643
Matt Mackall
Add --date support to update and revert...
r3814 def finddate(ui, repo, date):
"""Find the tipmost changeset that matches the given date spec"""
df = util.matchdate(date + " to " + date)
get = util.cachefunc(lambda r: repo.changectx(r).changeset())
changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
results = {}
for st, rev, fns in changeiter:
if st == 'add':
d = get(rev)[2]
if df(d[0]):
results[rev] = d
elif st == 'iter':
if rev in results:
ui.status("Found revision %s from %s\n" %
(rev, util.datestr(results[rev])))
return str(rev)
raise util.Abort(_("revision matching date not found"))
Matt Mackall
move walkchangerevs to cmdutils
r3650 def walkchangerevs(ui, repo, pats, change, opts):
'''Iterate over files and the revs they changed in.
Callers most commonly need to iterate backwards over the history
it is interested in. Doing so has awful (quadratic-looking)
performance, so we use iterators in a "windowed" way.
We walk a window of revisions in the desired order. Within the
window, we first walk forwards to gather data, then in the desired
order (usually backwards) to display it.
This function returns an (iterator, matchfn) tuple. The iterator
yields 3-tuples. They will be of one of the following forms:
"window", incrementing, lastrev: stepping through a window,
positive if walking forwards through revs, last rev in the
sequence iterated over - use to reset state for the current window
"add", rev, fns: out-of-order traversal of the given file names
fns, which changed during revision rev - use to gather data for
possible display
"iter", rev, None: in-order traversal of the revs earlier iterated
over with "add" - use to display data'''
def increasing_windows(start, end, windowsize=8, sizelimit=512):
if start < end:
while start < end:
yield start, min(windowsize, end-start)
start += windowsize
if windowsize < sizelimit:
windowsize *= 2
else:
while start > end:
yield start, min(windowsize, start-end-1)
start -= windowsize
if windowsize < sizelimit:
windowsize *= 2
files, matchfn, anypats = matchpats(repo, pats, opts)
follow = opts.get('follow') or opts.get('follow_first')
if repo.changelog.count() == 0:
return [], matchfn
if follow:
defrange = '%s:0' % repo.changectx().rev()
else:
defrange = 'tip:0'
Thomas Arendsen Hein
Removed unused ui parameter from revpair/revrange and fix its users.
r3707 revs = revrange(repo, opts['rev'] or [defrange])
Matt Mackall
move walkchangerevs to cmdutils
r3650 wanted = {}
Matt Mackall
add log --removed
r3657 slowpath = anypats or opts.get('removed')
Matt Mackall
move walkchangerevs to cmdutils
r3650 fncache = {}
if not slowpath and not files:
# No files, no patterns. Display all revs.
wanted = dict.fromkeys(revs)
copies = []
if not slowpath:
# Only files, no patterns. Check the history of each file.
def filerevgen(filelog, node):
cl_count = repo.changelog.count()
if node is None:
last = filelog.count() - 1
else:
last = filelog.rev(node)
for i, window in increasing_windows(last, nullrev):
revs = []
for j in xrange(i - window, i + 1):
n = filelog.node(j)
revs.append((filelog.linkrev(n),
follow and filelog.renamed(n)))
revs.reverse()
for rev in revs:
# only yield rev for which we have the changelog, it can
# happen while doing "hg log" during a pull or commit
if rev[0] < cl_count:
yield rev
def iterfiles():
for filename in files:
yield filename, None
for filename_node in copies:
yield filename_node
minrev, maxrev = min(revs), max(revs)
for file_, node in iterfiles():
filelog = repo.file(file_)
# A zero count may be a directory or deleted file, so
# try to find matching entries on the slow path.
if filelog.count() == 0:
slowpath = True
break
for rev, copied in filerevgen(filelog, node):
if rev <= maxrev:
if rev < minrev:
break
fncache.setdefault(rev, [])
fncache[rev].append(file_)
wanted[rev] = 1
if follow and copied:
copies.append(copied)
if slowpath:
if follow:
raise util.Abort(_('can only follow copies/renames for explicit '
'file names'))
# The slow path checks files modified in every changeset.
def changerevgen():
for i, window in increasing_windows(repo.changelog.count()-1,
nullrev):
for j in xrange(i - window, i + 1):
yield j, change(j)[3]
for rev, changefiles in changerevgen():
matches = filter(matchfn, changefiles)
if matches:
fncache[rev] = matches
wanted[rev] = 1
class followfilter:
def __init__(self, onlyfirst=False):
self.startrev = nullrev
self.roots = []
self.onlyfirst = onlyfirst
def match(self, rev):
def realparents(rev):
if self.onlyfirst:
return repo.changelog.parentrevs(rev)[0:1]
else:
return filter(lambda x: x != nullrev,
repo.changelog.parentrevs(rev))
if self.startrev == nullrev:
self.startrev = rev
return True
if rev > self.startrev:
# forward: all descendants
if not self.roots:
self.roots.append(self.startrev)
for parent in realparents(rev):
if parent in self.roots:
self.roots.append(rev)
return True
else:
# backwards: all parents
if not self.roots:
self.roots.extend(realparents(self.startrev))
if rev in self.roots:
self.roots.remove(rev)
self.roots.extend(realparents(rev))
return True
return False
# it might be worthwhile to do this in the iterator if the rev range
# is descending and the prune args are all within that range
for rev in opts.get('prune', ()):
rev = repo.changelog.rev(repo.lookup(rev))
ff = followfilter()
stop = min(revs[0], revs[-1])
for x in xrange(rev, stop-1, -1):
if ff.match(x) and x in wanted:
del wanted[x]
def iterate():
if follow and not files:
ff = followfilter(onlyfirst=opts.get('follow_first'))
def want(rev):
if ff.match(rev) and rev in wanted:
return True
return False
else:
def want(rev):
return rev in wanted
for i, window in increasing_windows(0, len(revs)):
yield 'window', revs[0] < revs[-1], revs[-1]
nrevs = [rev for rev in revs[i:i+window] if want(rev)]
srevs = list(nrevs)
srevs.sort()
for rev in srevs:
fns = fncache.get(rev)
if not fns:
def fns_generator():
for f in change(rev)[3]:
if matchfn(f):
yield f
fns = fns_generator()
yield 'add', rev, fns
for rev in nrevs:
yield 'iter', rev, None
return iterate(), matchfn