Would also help with structuring the stuff in commands.py a bit.
-parren
_______________________________________________
Mercurial-devel mailing list
Mercuri...@selenic.com
http://selenic.com/mailman/listinfo/mercurial-devel
+1
I've had similar thoughts in the past, breaking commands.py into commands/
--
Steve Borho
Let's see some timings.
--
Mathematics is the supreme nostalgia of our time.
As a quick experiment I replaced all of the bodies in commands.py with
pass. Doesn't pay off.
-parren
There's still a case to be made for not having a four thousand line source file
--
Steve Borho
Not sure it's really worth doing. I'm not expecting this to make things faster
(at least not intentionally).
It cuts commands.py down a bit though (from almost 5000 lines to ~4200).
In case your're interested in including such a patch, I would need to merge
it first with tip, since it's not synced to the latest churn in commands.py.
The patch passed the tests.
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -8,14 +8,12 @@
from node import hex, bin, nullid, nullrev, short
from lock import release
from i18n import _, gettext
-import os, re, sys, difflib, time, tempfile
-import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
+import os, re, sys, difflib
+import hg, scmutil, util, extensions, copies, error, bookmarks
import patch, help, url, encoding, templatekw, discovery
import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
import merge as mergemod
import minirst, revset, templatefilters
-import dagparser, context, simplemerge
-import random, setdiscovery, treediscovery, dagutil
# Commands start here, listed alphabetically
@@ -950,231 +948,6 @@
finally:
wlock.release()
-def debugancestor(ui, repo, *args):
- """find the ancestor revision of two revisions in a given index"""
- if len(args) == 3:
- index, rev1, rev2 = args
- r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
- lookup = r.lookup
- elif len(args) == 2:
- if not repo:
- raise util.Abort(_("there is no Mercurial repository here "
- "(.hg not found)"))
- rev1, rev2 = args
- r = repo.changelog
- lookup = repo.lookup
- else:
- raise util.Abort(_('either two or three arguments required'))
- a = r.ancestor(lookup(rev1), lookup(rev2))
- ui.write("%d:%s\n" % (r.rev(a), hex(a)))
-
-def debugbuilddag(ui, repo, text,
- mergeable_file=False,
- overwritten_file=False,
- new_file=False):
- """builds a repo with a given dag from scratch in the current empty repo
-
- Elements:
-
- - "+n" is a linear run of n nodes based on the current default parent
- - "." is a single node based on the current default parent
- - "$" resets the default parent to null (implied at the start);
- otherwise the default parent is always the last node created
- - "<p" sets the default parent to the backref p
- - "*p" is a fork at parent p, which is a backref
- - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
- - "/p2" is a merge of the preceding node and p2
- - ":tag" defines a local tag for the preceding node
- - "@branch" sets the named branch for subsequent nodes
- - "#...\\n" is a comment up to the end of the line
-
- Whitespace between the above elements is ignored.
-
- A backref is either
-
- - a number n, which references the node curr-n, where curr is the current
- node, or
- - the name of a local tag you placed earlier using ":tag", or
- - empty to denote the default parent.
-
- All string valued-elements are either strictly alphanumeric, or must
- be enclosed in double quotes ("..."), with "\\" as escape character.
- """
-
- cl = repo.changelog
- if len(cl) > 0:
- raise util.Abort(_('repository is not empty'))
-
- if mergeable_file:
- linesperrev = 2
- # determine number of revs in DAG
- n = 0
- for type, data in dagparser.parsedag(text):
- if type == 'n':
- n += 1
- # make a file with k lines per rev
- initialmergedlines = [str(i) for i in xrange(0, n * linesperrev)]
- initialmergedlines.append("")
-
- tags = []
-
- tr = repo.transaction("builddag")
- try:
-
- at = -1
- atbranch = 'default'
- nodeids = []
- for type, data in dagparser.parsedag(text):
- if type == 'n':
- ui.note('node %s\n' % str(data))
- id, ps = data
-
- files = []
- fctxs = {}
-
- p2 = None
- if mergeable_file:
- fn = "mf"
- p1 = repo[ps[0]]
- if len(ps) > 1:
- p2 = repo[ps[1]]
- pa = p1.ancestor(p2)
- base, local, other = [x[fn].data() for x in pa, p1, p2]
- m3 = simplemerge.Merge3Text(base, local, other)
- ml = [l.strip() for l in m3.merge_lines()]
- ml.append("")
- elif at > 0:
- ml = p1[fn].data().split("\n")
- else:
- ml = initialmergedlines
- ml[id * linesperrev] += " r%i" % id
- mergedtext = "\n".join(ml)
- files.append(fn)
- fctxs[fn] = context.memfilectx(fn, mergedtext)
-
- if overwritten_file:
- fn = "of"
- files.append(fn)
- fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
-
- if new_file:
- fn = "nf%i" % id
- files.append(fn)
- fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
- if len(ps) > 1:
- if not p2:
- p2 = repo[ps[1]]
- for fn in p2:
- if fn.startswith("nf"):
- files.append(fn)
- fctxs[fn] = p2[fn]
-
- def fctxfn(repo, cx, path):
- return fctxs.get(path)
-
- if len(ps) == 0 or ps[0] < 0:
- pars = [None, None]
- elif len(ps) == 1:
- pars = [nodeids[ps[0]], None]
- else:
- pars = [nodeids[p] for p in ps]
- cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
- date=(id, 0),
- user="debugbuilddag",
- extra={'branch': atbranch})
- nodeid = repo.commitctx(cx)
- nodeids.append(nodeid)
- at = id
- elif type == 'l':
- id, name = data
- ui.note('tag %s\n' % name)
- tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
- elif type == 'a':
- ui.note('branch %s\n' % data)
- atbranch = data
- tr.close()
- finally:
- tr.release()
-
- if tags:
- repo.opener.write("localtags", "".join(tags))
-
-def debugcommands(ui, cmd='', *args):
- """list all available commands and options"""
- for cmd, vals in sorted(table.iteritems()):
- cmd = cmd.split('|')[0].strip('^')
- opts = ', '.join([i[1] for i in vals[1]])
- ui.write('%s: %s\n' % (cmd, opts))
-
-def debugcomplete(ui, cmd='', **opts):
- """returns the completion list associated with the given command"""
-
- if opts.get('options'):
- options = []
- otables = [globalopts]
- if cmd:
- aliases, entry = cmdutil.findcmd(cmd, table, False)
- otables.append(entry[1])
- for t in otables:
- for o in t:
- if "(DEPRECATED)" in o[3]:
- continue
- if o[0]:
- options.append('-%s' % o[0])
- options.append('--%s' % o[1])
- ui.write("%s\n" % "\n".join(options))
- return
-
- cmdlist = cmdutil.findpossible(cmd, table)
- if ui.verbose:
- cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
- ui.write("%s\n" % "\n".join(sorted(cmdlist)))
-
-def debugfsinfo(ui, path = "."):
- """show information detected about current filesystem"""
- util.writefile('.debugfsinfo', '')
- ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
- ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
- ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
- and 'yes' or 'no'))
- os.unlink('.debugfsinfo')
-
-def debugrebuildstate(ui, repo, rev="tip"):
- """rebuild the dirstate as it would look like for the given revision"""
- ctx = cmdutil.revsingle(repo, rev)
- wlock = repo.wlock()
- try:
- repo.dirstate.rebuild(ctx.node(), ctx.manifest())
- finally:
- wlock.release()
-
-def debugcheckstate(ui, repo):
- """validate the correctness of the current dirstate"""
- parent1, parent2 = repo.dirstate.parents()
- m1 = repo[parent1].manifest()
- m2 = repo[parent2].manifest()
- errors = 0
- for f in repo.dirstate:
- state = repo.dirstate[f]
- if state in "nr" and f not in m1:
- ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
- errors += 1
- if state in "a" and f in m1:
- ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
- errors += 1
- if state in "m" and f not in m1 and f not in m2:
- ui.warn(_("%s in state %s, but not in either manifest\n") %
- (f, state))
- errors += 1
- for f in m1:
- state = repo.dirstate[f]
- if state not in "nrm":
- ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
- errors += 1
- if errors:
- error = _(".hg/dirstate inconsistent with current parent's manifest")
- raise util.Abort(error)
-
def showconfig(ui, repo, *values, **opts):
"""show combined config settings from all hgrc files
@@ -1218,507 +991,6 @@
ui.configsource(section, name, untrusted))
ui.write('%s=%s\n' % (sectname, value))
-def debugknown(ui, repopath, *ids, **opts):
- """test whether node ids are known to a repo
-
- Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
- indicating unknown/known.
- """
- repo = hg.repository(ui, repopath)
- if not repo.capable('known'):
- raise util.Abort("known() not supported by target repository")
- flags = repo.known([bin(s) for s in ids])
- ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
-
-def debugbundle(ui, bundlepath, all=None, **opts):
- """lists the contents of a bundle"""
- f = url.open(ui, bundlepath)
- try:
- gen = changegroup.readbundle(f, bundlepath)
- if all:
- ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
-
- def showchunks(named):
- ui.write("\n%s\n" % named)
- chain = None
- while 1:
- chunkdata = gen.deltachunk(chain)
- if not chunkdata:
- break
- node = chunkdata['node']
- p1 = chunkdata['p1']
- p2 = chunkdata['p2']
- cs = chunkdata['cs']
- deltabase = chunkdata['deltabase']
- delta = chunkdata['delta']
- ui.write("%s %s %s %s %s %s\n" %
- (hex(node), hex(p1), hex(p2),
- hex(cs), hex(deltabase), len(delta)))
- chain = node
-
- chunkdata = gen.changelogheader()
- showchunks("changelog")
- chunkdata = gen.manifestheader()
- showchunks("manifest")
- while 1:
- chunkdata = gen.filelogheader()
- if not chunkdata:
- break
- fname = chunkdata['filename']
- showchunks(fname)
- else:
- chunkdata = gen.changelogheader()
- chain = None
- while 1:
- chunkdata = gen.deltachunk(chain)
- if not chunkdata:
- break
- node = chunkdata['node']
- ui.write("%s\n" % hex(node))
- chain = node
- finally:
- f.close()
-
-def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
- """retrieves a bundle from a repo
-
- Every ID must be a full-length hex node id string. Saves the bundle to the
- given file.
- """
- repo = hg.repository(ui, repopath)
- if not repo.capable('getbundle'):
- raise util.Abort("getbundle() not supported by target repository")
- args = {}
- if common:
- args['common'] = [bin(s) for s in common]
- if head:
- args['heads'] = [bin(s) for s in head]
- bundle = repo.getbundle('debug', **args)
-
- bundletype = opts.get('type', 'bzip2').lower()
- btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
- bundletype = btypes.get(bundletype)
- if bundletype not in changegroup.bundletypes:
- raise util.Abort(_('unknown bundle type specified with --type'))
- changegroup.writebundle(bundle, bundlepath, bundletype)
-
-def debugpushkey(ui, repopath, namespace, *keyinfo):
- '''access the pushkey key/value protocol
-
- With two args, list the keys in the given namespace.
-
- With five args, set a key to new if it currently is set to old.
- Reports success or failure.
- '''
-
- target = hg.repository(ui, repopath)
- if keyinfo:
- key, old, new = keyinfo
- r = target.pushkey(namespace, key, old, new)
- ui.status(str(r) + '\n')
- return not r
- else:
- for k, v in target.listkeys(namespace).iteritems():
- ui.write("%s\t%s\n" % (k.encode('string-escape'),
- v.encode('string-escape')))
-
-def debugrevspec(ui, repo, expr):
- '''parse and apply a revision specification'''
- if ui.verbose:
- tree = revset.parse(expr)[0]
- ui.note(tree, "\n")
- newtree = revset.findaliases(ui, tree)
- if newtree != tree:
- ui.note(newtree, "\n")
- func = revset.match(ui, expr)
- for c in func(repo, range(len(repo))):
- ui.write("%s\n" % c)
-
-def debugsetparents(ui, repo, rev1, rev2=None):
- """manually set the parents of the current working directory
-
- This is useful for writing repository conversion tools, but should
- be used with care.
-
- Returns 0 on success.
- """
-
- r1 = cmdutil.revsingle(repo, rev1).node()
- r2 = cmdutil.revsingle(repo, rev2, 'null').node()
-
- wlock = repo.wlock()
- try:
- repo.dirstate.setparents(r1, r2)
- finally:
- wlock.release()
-
-def debugstate(ui, repo, nodates=None, datesort=None):
- """show the contents of the current dirstate"""
- timestr = ""
- showdate = not nodates
- if datesort:
- keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
- else:
- keyfunc = None # sort by filename
- for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
- if showdate:
- if ent[3] == -1:
- # Pad or slice to locale representation
- locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
- time.localtime(0)))
- timestr = 'unset'
- timestr = (timestr[:locale_len] +
- ' ' * (locale_len - len(timestr)))
- else:
- timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
- time.localtime(ent[3]))
- if ent[1] & 020000:
- mode = 'lnk'
- else:
- mode = '%3o' % (ent[1] & 0777)
- ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
- for f in repo.dirstate.copies():
- ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
-
-def debugsub(ui, repo, rev=None):
- ctx = cmdutil.revsingle(repo, rev, None)
- for k, v in sorted(ctx.substate.items()):
- ui.write('path %s\n' % k)
- ui.write(' source %s\n' % v[0])
- ui.write(' revision %s\n' % v[1])
-
-def debugdag(ui, repo, file_=None, *revs, **opts):
- """format the changelog or an index DAG as a concise textual description
-
- If you pass a revlog index, the revlog's DAG is emitted. If you list
- revision numbers, they get labelled in the output as rN.
-
- Otherwise, the changelog DAG of the current repo is emitted.
- """
- spaces = opts.get('spaces')
- dots = opts.get('dots')
- if file_:
- rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
- revs = set((int(r) for r in revs))
- def events():
- for r in rlog:
- yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
- if r in revs:
- yield 'l', (r, "r%i" % r)
- elif repo:
- cl = repo.changelog
- tags = opts.get('tags')
- branches = opts.get('branches')
- if tags:
- labels = {}
- for l, n in repo.tags().items():
- labels.setdefault(cl.rev(n), []).append(l)
- def events():
- b = "default"
- for r in cl:
- if branches:
- newb = cl.read(cl.node(r))[5]['branch']
- if newb != b:
- yield 'a', newb
- b = newb
- yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
- if tags:
- ls = labels.get(r)
- if ls:
- for l in ls:
- yield 'l', (r, l)
- else:
- raise util.Abort(_('need repo for changelog dag'))
-
- for line in dagparser.dagtextlines(events(),
- addspaces=spaces,
- wraplabels=True,
- wrapannotations=True,
- wrapnonlinear=dots,
- usedots=dots,
- maxlinewidth=70):
- ui.write(line)
- ui.write("\n")
-
-def debugdata(ui, repo, file_, rev):
- """dump the contents of a data file revision"""
- r = None
- if repo:
- filelog = repo.file(file_)
- if len(filelog):
- r = filelog
- if not r:
- r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
- file_[:-2] + ".i")
- try:
- ui.write(r.revision(r.lookup(rev)))
- except KeyError:
- raise util.Abort(_('invalid revision identifier %s') % rev)
-
-def debugdate(ui, date, range=None, **opts):
- """parse and display a date"""
- if opts["extended"]:
- d = util.parsedate(date, util.extendeddateformats)
- else:
- d = util.parsedate(date)
- ui.write("internal: %s %s\n" % d)
- ui.write("standard: %s\n" % util.datestr(d))
- if range:
- m = util.matchdate(range)
- ui.write("match: %s\n" % m(d[0]))
-
-def debugignore(ui, repo, *values, **opts):
- """display the combined ignore pattern"""
- ignore = repo.dirstate._ignore
- if hasattr(ignore, 'includepat'):
- ui.write("%s\n" % ignore.includepat)
- else:
- raise util.Abort(_("no ignore patterns found"))
-
-def debugdiscovery(ui, repo, remoteurl="default", **opts):
- """runs the changeset discovery protocol in isolation"""
- remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
- remote = hg.repository(hg.remoteui(repo, opts), remoteurl)
- ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
-
- # make sure tests are repeatable
- random.seed(12323)
-
- def doit(localheads, remoteheads):
- if opts.get('old'):
- if localheads:
- raise util.Abort('cannot use localheads with old style discovery')
- common, _in, hds = treediscovery.findcommonincoming(repo, remote,
- force=True)
- common = set(common)
- if not opts.get('nonheads'):
- ui.write("unpruned common: %s\n" % " ".join([short(n)
- for n in common]))
- dag = dagutil.revlogdag(repo.changelog)
- all = dag.ancestorset(dag.internalizeall(common))
- common = dag.externalizeall(dag.headsetofconnecteds(all))
- else:
- common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
- common = set(common)
- rheads = set(hds)
- lheads = set(repo.heads())
- ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
- if lheads <= common:
- ui.write("local is subset\n")
- elif rheads <= common:
- ui.write("remote is subset\n")
-
- serverlogs = opts.get('serverlog')
- if serverlogs:
- for filename in serverlogs:
- logfile = open(filename, 'r')
- try:
- line = logfile.readline()
- while line:
- parts = line.strip().split(';')
- op = parts[1]
- if op == 'cg':
- pass
- elif op == 'cgss':
- doit(parts[2].split(' '), parts[3].split(' '))
- elif op == 'unb':
- doit(parts[3].split(' '), parts[2].split(' '))
- line = logfile.readline()
- finally:
- logfile.close()
-
- else:
- remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
- opts.get('remote_head'))
- localrevs = opts.get('local_head')
- doit(localrevs, remoterevs)
-
-
-def debugindex(ui, repo, file_, **opts):
- """dump the contents of an index file"""
- r = None
- if repo:
- filelog = repo.file(file_)
- if len(filelog):
- r = filelog
-
- format = opts.get('format', 0)
- if format not in (0, 1):
- raise util.Abort(_("unknown format %d") % format)
-
- if not r:
- r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
-
- generaldelta = r.version & revlog.REVLOGGENERALDELTA
- if generaldelta:
- basehdr = ' delta'
- else:
- basehdr = ' base'
-
- if format == 0:
- ui.write(" rev offset length " + basehdr + " linkrev"
- " nodeid p1 p2\n")
- elif format == 1:
- ui.write(" rev flag offset length"
- " size " + basehdr + " link p1 p2 nodeid\n")
-
- for i in r:
- node = r.node(i)
- if generaldelta:
- base = r.deltaparent(i)
- else:
- base = r.chainbase(i)
- if format == 0:
- try:
- pp = r.parents(node)
- except:
- pp = [nullid, nullid]
- ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
- i, r.start(i), r.length(i), base, r.linkrev(i),
- short(node), short(pp[0]), short(pp[1])))
- elif format == 1:
- pr = r.parentrevs(i)
- ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
- i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
- base, r.linkrev(i), pr[0], pr[1], short(node)))
-
-def debugindexdot(ui, repo, file_):
- """dump an index DAG as a graphviz dot file"""
- r = None
- if repo:
- filelog = repo.file(file_)
- if len(filelog):
- r = filelog
- if not r:
- r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
- ui.write("digraph G {\n")
- for i in r:
- node = r.node(i)
- pp = r.parents(node)
- ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
- if pp[1] != nullid:
- ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
- ui.write("}\n")
-
-def debuginstall(ui):
- '''test Mercurial installation
-
- Returns 0 on success.
- '''
-
- def writetemp(contents):
- (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
- f = os.fdopen(fd, "wb")
- f.write(contents)
- f.close()
- return name
-
- problems = 0
-
- # encoding
- ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
- try:
- encoding.fromlocal("test")
- except util.Abort, inst:
- ui.write(" %s\n" % inst)
- ui.write(_(" (check that your locale is properly set)\n"))
- problems += 1
-
- # compiled modules
- ui.status(_("Checking installed modules (%s)...\n")
- % os.path.dirname(__file__))
- try:
- import bdiff, mpatch, base85, osutil
- except Exception, inst:
- ui.write(" %s\n" % inst)
- ui.write(_(" One or more extensions could not be found"))
- ui.write(_(" (check that you compiled the extensions)\n"))
- problems += 1
-
- # templates
- ui.status(_("Checking templates...\n"))
- try:
- import templater
- templater.templater(templater.templatepath("map-cmdline.default"))
- except Exception, inst:
- ui.write(" %s\n" % inst)
- ui.write(_(" (templates seem to have been installed incorrectly)\n"))
- problems += 1
-
- # editor
- ui.status(_("Checking commit editor...\n"))
- editor = ui.geteditor()
- cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
- if not cmdpath:
- if editor == 'vi':
- ui.write(_(" No commit editor set and can't find vi in PATH\n"))
- ui.write(_(" (specify a commit editor in your configuration"
- " file)\n"))
- else:
- ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
- ui.write(_(" (specify a commit editor in your configuration"
- " file)\n"))
- problems += 1
-
- # check username
- ui.status(_("Checking username...\n"))
- try:
- ui.username()
- except util.Abort, e:
- ui.write(" %s\n" % e)
- ui.write(_(" (specify a username in your configuration file)\n"))
- problems += 1
-
- if not problems:
- ui.status(_("No problems detected\n"))
- else:
- ui.write(_("%s problems detected,"
- " please check your install!\n") % problems)
-
- return problems
-
-def debugrename(ui, repo, file1, *pats, **opts):
- """dump rename information"""
-
- ctx = cmdutil.revsingle(repo, opts.get('rev'))
- m = cmdutil.match(repo, (file1,) + pats, opts)
- for abs in ctx.walk(m):
- fctx = ctx[abs]
- o = fctx.filelog().renamed(fctx.filenode())
- rel = m.rel(abs)
- if o:
- ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
- else:
- ui.write(_("%s not renamed\n") % rel)
-
-def debugwalk(ui, repo, *pats, **opts):
- """show how files match on given patterns"""
- m = cmdutil.match(repo, pats, opts)
- items = list(repo.walk(m))
- if not items:
- return
- fmt = 'f %%-%ds %%-%ds %%s' % (
- max([len(abs) for abs in items]),
- max([len(m.rel(abs)) for abs in items]))
- for abs in items:
- line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
- ui.write("%s\n" % line.rstrip())
-
-def debugwireargs(ui, repopath, *vals, **opts):
- repo = hg.repository(hg.remoteui(ui, opts), repopath)
- for opt in remoteopts:
- del opts[opt[1]]
- args = {}
- for k, v in opts.iteritems():
- if v:
- args[k] = v
- # run twice to check that we don't mess up the stream for the next command
- res1 = repo.debugwireargs(*vals, **args)
- res2 = repo.debugwireargs(*vals, **args)
- ui.write("%s\n" % res1)
- if res1 != res2:
- ui.warn("%s\n" % res2)
-
def diff(ui, repo, *pats, **opts):
"""diff repository (or selected files)
@@ -4555,94 +3827,6 @@
_('forcibly copy over an existing managed file')),
] + walkopts + dryrunopts,
_('[OPTION]... [SOURCE]... DEST')),
- "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
- "debugbuilddag":
- (debugbuilddag,
- [('m', 'mergeable-file', None, _('add single file mergeable changes')),
- ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
- ('n', 'new-file', None, _('add new file at each rev')),
- ],
- _('[OPTION]... TEXT')),
- "debugbundle":
- (debugbundle,
- [('a', 'all', None, _('show all details')),
- ],
- _('FILE')),
- "debugcheckstate": (debugcheckstate, [], ''),
- "debugcommands": (debugcommands, [], _('[COMMAND]')),
- "debugcomplete":
- (debugcomplete,
- [('o', 'options', None, _('show the command options'))],
- _('[-o] CMD')),
- "debugdag":
- (debugdag,
- [('t', 'tags', None, _('use tags as labels')),
- ('b', 'branches', None, _('annotate with branch names')),
- ('', 'dots', None, _('use dots for runs')),
- ('s', 'spaces', None, _('separate elements by spaces')),
- ],
- _('[OPTION]... [FILE [REV]...]')),
- "debugdate":
- (debugdate,
- [('e', 'extended', None, _('try extended date formats'))],
- _('[-e] DATE [RANGE]')),
- "debugdata": (debugdata, [], _('FILE REV')),
- "debugdiscovery": (debugdiscovery,
- [('', 'old', None,
- _('use old-style discovery')),
- ('', 'nonheads', None,
- _('use old-style discovery with non-heads included')),
- ] + remoteopts,
- _('[-l REV] [-r REV] [-b BRANCH]...'
- ' [OTHER]')),
- "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
- "debuggetbundle":
- (debuggetbundle,
- [('H', 'head', [], _('id of head node'), _('ID')),
- ('C', 'common', [], _('id of common node'), _('ID')),
- ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
- ],
- _('REPO FILE [-H|-C ID]...')),
- "debugignore": (debugignore, [], ''),
- "debugindex": (debugindex,
- [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
- _('FILE')),
- "debugindexdot": (debugindexdot, [], _('FILE')),
- "debuginstall": (debuginstall, [], ''),
- "debugknown": (debugknown, [], _('REPO ID...')),
- "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
- "debugrebuildstate":
- (debugrebuildstate,
- [('r', 'rev', '',
- _('revision to rebuild to'), _('REV'))],
- _('[-r REV] [REV]')),
- "debugrename":
- (debugrename,
- [('r', 'rev', '',
- _('revision to debug'), _('REV'))],
- _('[-r REV] FILE')),
- "debugrevspec":
- (debugrevspec, [], ('REVSPEC')),
- "debugsetparents":
- (debugsetparents, [], _('REV1 [REV2]')),
- "debugstate":
- (debugstate,
- [('', 'nodates', None, _('do not display the saved mtime')),
- ('', 'datesort', None, _('sort by saved mtime'))],
- _('[OPTION]...')),
- "debugsub":
- (debugsub,
- [('r', 'rev', '',
- _('revision to check'), _('REV'))],
- _('[-r REV] [REV]')),
- "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
- "debugwireargs":
- (debugwireargs,
- [('', 'three', '', 'three'),
- ('', 'four', '', 'four'),
- ('', 'five', '', 'five'),
- ] + remoteopts,
- _('REPO [OPTIONS]... [ONE [TWO]]')),
"^diff":
(diff,
[('r', 'rev', [],
@@ -4976,8 +4160,5 @@
"version": (version_, []),
}
-norepo = ("clone init version help debugcommands debugcomplete"
- " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
- " debugknown debuggetbundle debugbundle")
-optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
- " debugdata debugindex debugindexdot")
+norepo = ("clone init version help")
+optionalrepo = ("identify paths serve showconfig")
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
new file mode 100644
--- /dev/null
+++ b/mercurial/debugcommands.py
@@ -0,0 +1,840 @@
+# debugcommands.py - debug commands for mercurial
+#
+# Copyright Matt Mackall <m...@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 node import hex, bin, nullid, nullrev, short
+from i18n import _
+import os, time, random
+import hg, scmutil, util, revlog, error, url, encoding
+import context, simplemerge
+import changegroup, discovery, cmdutil, revset
+import setdiscovery, treediscovery, dagparser, dagutil
+import commands
+
+# Commands start here, listed alphabetically
+
+def allcommands(ui, cmd='', *args):
+ """list all available commands and options"""
+ commands.table.update(table)
+ for cmd, vals in sorted(commands.table.iteritems()):
+ cmd = cmd.split('|')[0].strip('^')
+ opts = ', '.join([i[1] for i in vals[1]])
+ ui.write('%s: %s\n' % (cmd, opts))
+
+def ancestor(ui, repo, *args):
+ """find the ancestor revision of two revisions in a given index"""
+ if len(args) == 3:
+ index, rev1, rev2 = args
+ r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
+ lookup = r.lookup
+ elif len(args) == 2:
+ if not repo:
+ raise util.Abort(_("there is no Mercurial repository here "
+ "(.hg not found)"))
+ rev1, rev2 = args
+ r = repo.changelog
+ lookup = repo.lookup
+ else:
+ raise util.Abort(_('either two or three arguments required'))
+ a = r.ancestor(lookup(rev1), lookup(rev2))
+ ui.write("%d:%s\n" % (r.rev(a), hex(a)))
+
+def builddag(ui, repo, text, mergeable_file=False, overwritten_file=False,
+ new_file=False):
+ """builds a repo with a given dag from scratch in the current empty repo
+
+ Elements:
+
+ - "+n" is a linear run of n nodes based on the current default parent
+ - "." is a single node based on the current default parent
+ - "$" resets the default parent to null (implied at the start);
+ otherwise the default parent is always the last node created
+ - "<p" sets the default parent to the backref p
+ - "*p" is a fork at parent p, which is a backref
+ - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
+ - "/p2" is a merge of the preceding node and p2
+ - ":tag" defines a local tag for the preceding node
+ - "@branch" sets the named branch for subsequent nodes
+ - "#...\\n" is a comment up to the end of the line
+
+ Whitespace between the above elements is ignored.
+
+ A backref is either
+
+ - a number n, which references the node curr-n, where curr is the current
+ node, or
+ - the name of a local tag you placed earlier using ":tag", or
+ - empty to denote the default parent.
+
+ All string valued-elements are either strictly alphanumeric, or must
+ be enclosed in double quotes ("..."), with "\\" as escape character.
+ """
+
+ cl = repo.changelog
+ if len(cl) > 0:
+ raise util.Abort(_('repository is not empty'))
+
+ if mergeable_file:
+ linesperrev = 2
+ # determine number of revs in DAG
+ n = 0
+ for type, data in dagparser.parsedag(text):
+ if type == 'n':
+ n += 1
+ # make a file with k lines per rev
+ initialmergedlines = [str(i) for i in xrange(0, n * linesperrev)]
+ initialmergedlines.append("")
+
+ tags = []
+
+ tr = repo.transaction("builddag")
+ try:
+
+ at = -1
+ atbranch = 'default'
+ nodeids = []
+ for type, data in dagparser.parsedag(text):
+ if type == 'n':
+ ui.note('node %s\n' % str(data))
+ id, ps = data
+
+ files = []
+ fctxs = {}
+
+ p2 = None
+ if mergeable_file:
+ fn = "mf"
+ p1 = repo[ps[0]]
+ if len(ps) > 1:
+ p2 = repo[ps[1]]
+ pa = p1.ancestor(p2)
+ base, local, other = [x[fn].data() for x in pa, p1, p2]
+ m3 = simplemerge.Merge3Text(base, local, other)
+ ml = [l.strip() for l in m3.merge_lines()]
+ ml.append("")
+ elif at > 0:
+ ml = p1[fn].data().split("\n")
+ else:
+ ml = initialmergedlines
+ ml[id * linesperrev] += " r%i" % id
+ mergedtext = "\n".join(ml)
+ files.append(fn)
+ fctxs[fn] = context.memfilectx(fn, mergedtext)
+
+ if overwritten_file:
+ fn = "of"
+ files.append(fn)
+ fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
+
+ if new_file:
+ fn = "nf%i" % id
+ files.append(fn)
+ fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
+ if len(ps) > 1:
+ if not p2:
+ p2 = repo[ps[1]]
+ for fn in p2:
+ if fn.startswith("nf"):
+ files.append(fn)
+ fctxs[fn] = p2[fn]
+
+ def fctxfn(repo, cx, path):
+ return fctxs.get(path)
+
+ if len(ps) == 0 or ps[0] < 0:
+ pars = [None, None]
+ elif len(ps) == 1:
+ pars = [nodeids[ps[0]], None]
+ else:
+ pars = [nodeids[p] for p in ps]
+ cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
+ date=(id, 0),
+ user="debugbuilddag",
+ extra={'branch': atbranch})
+ nodeid = repo.commitctx(cx)
+ nodeids.append(nodeid)
+ at = id
+ elif type == 'l':
+ id, name = data
+ ui.note('tag %s\n' % name)
+ tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
+ elif type == 'a':
+ ui.note('branch %s\n' % data)
+ atbranch = data
+ tr.close()
+ finally:
+ tr.release()
+
+ if tags:
+ repo.opener.write("localtags", "".join(tags))
+
+def bundle(ui, bundlepath, all=None, **opts):
+ """lists the contents of a bundle"""
+ f = url.open(ui, bundlepath)
+ try:
+ gen = changegroup.readbundle(f, bundlepath)
+ if all:
+ ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
+
+ def showchunks(named):
+ ui.write("\n%s\n" % named)
+ chain = None
+ while 1:
+ chunkdata = gen.deltachunk(chain)
+ if not chunkdata:
+ break
+ node = chunkdata['node']
+ p1 = chunkdata['p1']
+ p2 = chunkdata['p2']
+ cs = chunkdata['cs']
+ deltabase = chunkdata['deltabase']
+ delta = chunkdata['delta']
+ ui.write("%s %s %s %s %s %s\n" %
+ (hex(node), hex(p1), hex(p2),
+ hex(cs), hex(deltabase), len(delta)))
+ chain = node
+
+ chunkdata = gen.changelogheader()
+ showchunks("changelog")
+ chunkdata = gen.manifestheader()
+ showchunks("manifest")
+ while 1:
+ chunkdata = gen.filelogheader()
+ if not chunkdata:
+ break
+ fname = chunkdata['filename']
+ showchunks(fname)
+ else:
+ chunkdata = gen.changelogheader()
+ chain = None
+ while 1:
+ chunkdata = gen.deltachunk(chain)
+ if not chunkdata:
+ break
+ node = chunkdata['node']
+ ui.write("%s\n" % hex(node))
+ chain = node
+ finally:
+ f.close()
+
+def checkstate(ui, repo):
+ """validate the correctness of the current dirstate"""
+ parent1, parent2 = repo.dirstate.parents()
+ m1 = repo[parent1].manifest()
+ m2 = repo[parent2].manifest()
+ errors = 0
+ for f in repo.dirstate:
+ state = repo.dirstate[f]
+ if state in "nr" and f not in m1:
+ ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
+ errors += 1
+ if state in "a" and f in m1:
+ ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
+ errors += 1
+ if state in "m" and f not in m1 and f not in m2:
+ ui.warn(_("%s in state %s, but not in either manifest\n") %
+ (f, state))
+ errors += 1
+ for f in m1:
+ state = repo.dirstate[f]
+ if state not in "nrm":
+ ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
+ errors += 1
+ if errors:
+ error = _(".hg/dirstate inconsistent with current parent's manifest")
+ raise util.Abort(error)
+
+def complete(ui, cmd='', **opts):
+ """returns the completion list associated with the given command"""
+
+ commands.table.update(table)
+
+ if opts.get('options'):
+ options = []
+ otables = [commands.globalopts]
+ if cmd:
+ aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
+ otables.append(entry[1])
+ for t in otables:
+ for o in t:
+ if "(DEPRECATED)" in o[3]:
+ continue
+ if o[0]:
+ options.append('-%s' % o[0])
+ options.append('--%s' % o[1])
+ ui.write("%s\n" % "\n".join(options))
+ return
+
+ cmdlist = cmdutil.findpossible(cmd, commands.table)
+ if ui.verbose:
+ cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
+ ui.write("%s\n" % "\n".join(sorted(cmdlist)))
+
+def dag(ui, repo, file_=None, *revs, **opts):
+ """format the changelog or an index DAG as a concise textual description
+
+ If you pass a revlog index, the revlog's DAG is emitted. If you list
+ revision numbers, they get labelled in the output as rN.
+
+ Otherwise, the changelog DAG of the current repo is emitted.
+ """
+ spaces = opts.get('spaces')
+ dots = opts.get('dots')
+ if file_:
+ rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
+ revs = set((int(r) for r in revs))
+ def events():
+ for r in rlog:
+ yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
+ if r in revs:
+ yield 'l', (r, "r%i" % r)
+ elif repo:
+ cl = repo.changelog
+ tags = opts.get('tags')
+ branches = opts.get('branches')
+ if tags:
+ labels = {}
+ for l, n in repo.tags().items():
+ labels.setdefault(cl.rev(n), []).append(l)
+ def events():
+ b = "default"
+ for r in cl:
+ if branches:
+ newb = cl.read(cl.node(r))[5]['branch']
+ if newb != b:
+ yield 'a', newb
+ b = newb
+ yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
+ if tags:
+ ls = labels.get(r)
+ if ls:
+ for l in ls:
+ yield 'l', (r, l)
+ else:
+ raise util.Abort(_('need repo for changelog dag'))
+
+ for line in dagparser.dagtextlines(events(),
+ addspaces=spaces,
+ wraplabels=True,
+ wrapannotations=True,
+ wrapnonlinear=dots,
+ usedots=dots,
+ maxlinewidth=70):
+ ui.write(line)
+ ui.write("\n")
+
+def data(ui, repo, file_, rev):
+ """dump the contents of a data file revision"""
+ r = None
+ if repo:
+ filelog = repo.file(file_)
+ if len(filelog):
+ r = filelog
+ if not r:
+ r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
+ file_[:-2] + ".i")
+ try:
+ ui.write(r.revision(r.lookup(rev)))
+ except KeyError:
+ raise util.Abort(_('invalid revision identifier %s') % rev)
+
+def date(ui, date, range=None, **opts):
+ """parse and display a date"""
+ if opts["extended"]:
+ d = util.parsedate(date, util.extendeddateformats)
+ else:
+ d = util.parsedate(date)
+ ui.write("internal: %s %s\n" % d)
+ ui.write("standard: %s\n" % util.datestr(d))
+ if range:
+ m = util.matchdate(range)
+ ui.write("match: %s\n" % m(d[0]))
+
+def discovery(ui, repo, remoteurl="default", **opts):
+ """runs the changeset discovery protocol in isolation"""
+ remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
+ remote = hg.repository(hg.remoteui(repo, opts), remoteurl)
+ ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
+
+ # make sure tests are repeatable
+ random.seed(12323)
+
+ def doit(localheads, remoteheads):
+ if opts.get('old'):
+ if localheads:
+ raise util.Abort('cannot use localheads with old style discovery')
+ common, _in, hds = treediscovery.findcommonincoming(repo, remote,
+ force=True)
+ common = set(common)
+ if not opts.get('nonheads'):
+ ui.write("unpruned common: %s\n" % " ".join([short(n)
+ for n in common]))
+ dag = dagutil.revlogdag(repo.changelog)
+ all = dag.ancestorset(dag.internalizeall(common))
+ common = dag.externalizeall(dag.headsetofconnecteds(all))
+ else:
+ common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
+ common = set(common)
+ rheads = set(hds)
+ lheads = set(repo.heads())
+ ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
+ if lheads <= common:
+ ui.write("local is subset\n")
+ elif rheads <= common:
+ ui.write("remote is subset\n")
+
+ serverlogs = opts.get('serverlog')
+ if serverlogs:
+ for filename in serverlogs:
+ logfile = open(filename, 'r')
+ try:
+ line = logfile.readline()
+ while line:
+ parts = line.strip().split(';')
+ op = parts[1]
+ if op == 'cg':
+ pass
+ elif op == 'cgss':
+ doit(parts[2].split(' '), parts[3].split(' '))
+ elif op == 'unb':
+ doit(parts[3].split(' '), parts[2].split(' '))
+ line = logfile.readline()
+ finally:
+ logfile.close()
+
+ else:
+ remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
+ opts.get('remote_head'))
+ localrevs = opts.get('local_head')
+ doit(localrevs, remoterevs)
+
+def fsinfo(ui, path = "."):
+ """show information detected about current filesystem"""
+ util.writefile('.debugfsinfo', '')
+ ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
+ ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
+ ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
+ and 'yes' or 'no'))
+ os.unlink('.debugfsinfo')
+
+def getbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
+ """retrieves a bundle from a repo
+
+ Every ID must be a full-length hex node id string. Saves the bundle to the
+ given file.
+ """
+ repo = hg.repository(ui, repopath)
+ if not repo.capable('getbundle'):
+ raise util.Abort("getbundle() not supported by target repository")
+ args = {}
+ if common:
+ args['common'] = [bin(s) for s in common]
+ if head:
+ args['heads'] = [bin(s) for s in head]
+ bundle = repo.getbundle('debug', **args)
+
+ bundletype = opts.get('type', 'bzip2').lower()
+ btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
+ bundletype = btypes.get(bundletype)
+ if bundletype not in changegroup.bundletypes:
+ raise util.Abort(_('unknown bundle type specified with --type'))
+ changegroup.writebundle(bundle, bundlepath, bundletype)
+
+def ignore(ui, repo, *values, **opts):
+ """display the combined ignore pattern"""
+ ignore = repo.dirstate._ignore
+ if hasattr(ignore, 'includepat'):
+ ui.write("%s\n" % ignore.includepat)
+ else:
+ raise util.Abort(_("no ignore patterns found"))
+
+def index(ui, repo, file_, **opts):
+ """dump the contents of an index file"""
+ r = None
+ if repo:
+ filelog = repo.file(file_)
+ if len(filelog):
+ r = filelog
+
+ format = opts.get('format', 0)
+ if format not in (0, 1):
+ raise util.Abort(_("unknown format %d") % format)
+
+ if not r:
+ r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
+
+ generaldelta = r.version & revlog.REVLOGGENERALDELTA
+ if generaldelta:
+ basehdr = ' delta'
+ else:
+ basehdr = ' base'
+
+ if format == 0:
+ ui.write(" rev offset length " + basehdr + " linkrev"
+ " nodeid p1 p2\n")
+ elif format == 1:
+ ui.write(" rev flag offset length"
+ " size " + basehdr + " link p1 p2 nodeid\n")
+
+ for i in r:
+ node = r.node(i)
+ if generaldelta:
+ base = r.deltaparent(i)
+ else:
+ base = r.chainbase(i)
+ if format == 0:
+ try:
+ pp = r.parents(node)
+ except:
+ pp = [nullid, nullid]
+ ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
+ i, r.start(i), r.length(i), base, r.linkrev(i),
+ short(node), short(pp[0]), short(pp[1])))
+ elif format == 1:
+ pr = r.parentrevs(i)
+ ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
+ i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
+ base, r.linkrev(i), pr[0], pr[1], short(node)))
+
+def indexdot(ui, repo, file_):
+ """dump an index DAG as a graphviz dot file"""
+ r = None
+ if repo:
+ filelog = repo.file(file_)
+ if len(filelog):
+ r = filelog
+ if not r:
+ r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
+ ui.write("digraph G {\n")
+ for i in r:
+ node = r.node(i)
+ pp = r.parents(node)
+ ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
+ if pp[1] != nullid:
+ ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
+ ui.write("}\n")
+
+def install(ui):
+ '''test Mercurial installation
+
+ Returns 0 on success.
+ '''
+
+ def writetemp(contents):
+ (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
+ f = os.fdopen(fd, "wb")
+ f.write(contents)
+ f.close()
+ return name
+
+ problems = 0
+
+ # encoding
+ ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
+ try:
+ encoding.fromlocal("test")
+ except util.Abort, inst:
+ ui.write(" %s\n" % inst)
+ ui.write(_(" (check that your locale is properly set)\n"))
+ problems += 1
+
+ # compiled modules
+ ui.status(_("Checking installed modules (%s)...\n")
+ % os.path.dirname(__file__))
+ try:
+ import bdiff, mpatch, base85, osutil
+ except Exception, inst:
+ ui.write(" %s\n" % inst)
+ ui.write(_(" One or more extensions could not be found"))
+ ui.write(_(" (check that you compiled the extensions)\n"))
+ problems += 1
+
+ # templates
+ ui.status(_("Checking templates...\n"))
+ try:
+ import templater
+ templater.templater(templater.templatepath("map-cmdline.default"))
+ except Exception, inst:
+ ui.write(" %s\n" % inst)
+ ui.write(_(" (templates seem to have been installed incorrectly)\n"))
+ problems += 1
+
+ # editor
+ ui.status(_("Checking commit editor...\n"))
+ editor = ui.geteditor()
+ cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
+ if not cmdpath:
+ if editor == 'vi':
+ ui.write(_(" No commit editor set and can't find vi in PATH\n"))
+ ui.write(_(" (specify a commit editor in your configuration"
+ " file)\n"))
+ else:
+ ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
+ ui.write(_(" (specify a commit editor in your configuration"
+ " file)\n"))
+ problems += 1
+
+ # check username
+ ui.status(_("Checking username...\n"))
+ try:
+ ui.username()
+ except util.Abort, e:
+ ui.write(" %s\n" % e)
+ ui.write(_(" (specify a username in your configuration file)\n"))
+ problems += 1
+
+ if not problems:
+ ui.status(_("No problems detected\n"))
+ else:
+ ui.write(_("%s problems detected,"
+ " please check your install!\n") % problems)
+
+ return problems
+
+def known(ui, repopath, *ids, **opts):
+ """test whether node ids are known to a repo
+
+ Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
+ indicating unknown/known.
+ """
+ repo = hg.repository(ui, repopath)
+ if not repo.capable('known'):
+ raise util.Abort("known() not supported by target repository")
+ flags = repo.known([bin(s) for s in ids])
+ ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
+
+def pushkey(ui, repopath, namespace, *keyinfo):
+ '''access the pushkey key/value protocol
+
+ With two args, list the keys in the given namespace.
+
+ With five args, set a key to new if it currently is set to old.
+ Reports success or failure.
+ '''
+
+ target = hg.repository(ui, repopath)
+ if keyinfo:
+ key, old, new = keyinfo
+ r = target.pushkey(namespace, key, old, new)
+ ui.status(str(r) + '\n')
+ return not r
+ else:
+ for k, v in target.listkeys(namespace).iteritems():
+ ui.write("%s\t%s\n" % (k.encode('string-escape'),
+ v.encode('string-escape')))
+
+def rebuildstate(ui, repo, rev="tip"):
+ """rebuild the dirstate as it would look like for the given revision"""
+ ctx = cmdutil.revsingle(repo, rev)
+ wlock = repo.wlock()
+ try:
+ repo.dirstate.rebuild(ctx.node(), ctx.manifest())
+ finally:
+ wlock.release()
+
+def rename(ui, repo, file1, *pats, **opts):
+ """dump rename information"""
+
+ ctx = cmdutil.revsingle(repo, opts.get('rev'))
+ m = cmdutil.match(repo, (file1,) + pats, opts)
+ for abs in ctx.walk(m):
+ fctx = ctx[abs]
+ o = fctx.filelog().renamed(fctx.filenode())
+ rel = m.rel(abs)
+ if o:
+ ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
+ else:
+ ui.write(_("%s not renamed\n") % rel)
+
+def revspec(ui, repo, expr):
+ '''parse and apply a revision specification'''
+ if ui.verbose:
+ tree = revset.parse(expr)[0]
+ ui.note(tree, "\n")
+ newtree = revset.findaliases(ui, tree)
+ if newtree != tree:
+ ui.note(newtree, "\n")
+ func = revset.match(ui, expr)
+ for c in func(repo, range(len(repo))):
+ ui.write("%s\n" % c)
+
+def setparents(ui, repo, rev1, rev2=None):
+ """manually set the parents of the current working directory
+
+ This is useful for writing repository conversion tools, but should
+ be used with care.
+
+ Returns 0 on success.
+ """
+
+ r1 = cmdutil.revsingle(repo, rev1).node()
+ r2 = cmdutil.revsingle(repo, rev2, 'null').node()
+
+ wlock = repo.wlock()
+ try:
+ repo.dirstate.setparents(r1, r2)
+ finally:
+ wlock.release()
+
+def state(ui, repo, nodates=None, datesort=None):
+ """show the contents of the current dirstate"""
+ timestr = ""
+ showdate = not nodates
+ if datesort:
+ keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
+ else:
+ keyfunc = None # sort by filename
+ for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
+ if showdate:
+ if ent[3] == -1:
+ # Pad or slice to locale representation
+ locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
+ time.localtime(0)))
+ timestr = 'unset'
+ timestr = (timestr[:locale_len] +
+ ' ' * (locale_len - len(timestr)))
+ else:
+ timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
+ time.localtime(ent[3]))
+ if ent[1] & 020000:
+ mode = 'lnk'
+ else:
+ mode = '%3o' % (ent[1] & 0777)
+ ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
+ for f in repo.dirstate.copies():
+ ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
+
+def sub(ui, repo, rev=None):
+ ctx = cmdutil.revsingle(repo, rev, None)
+ for k, v in sorted(ctx.substate.items()):
+ ui.write('path %s\n' % k)
+ ui.write(' source %s\n' % v[0])
+ ui.write(' revision %s\n' % v[1])
+
+def walk(ui, repo, *pats, **opts):
+ """show how files match on given patterns"""
+ m = cmdutil.match(repo, pats, opts)
+ items = list(repo.walk(m))
+ if not items:
+ return
+ fmt = 'f %%-%ds %%-%ds %%s' % (
+ max([len(abs) for abs in items]),
+ max([len(m.rel(abs)) for abs in items]))
+ for abs in items:
+ line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
+ ui.write("%s\n" % line.rstrip())
+
+def wireargs(ui, repopath, *vals, **opts):
+ repo = hg.repository(hg.remoteui(ui, opts), repopath)
+ for opt in commands.remoteopts:
+ del opts[opt[1]]
+ args = {}
+ for k, v in opts.iteritems():
+ if v:
+ args[k] = v
+ # run twice to check that we don't mess up the stream for the next command
+ res1 = repo.debugwireargs(*vals, **args)
+ res2 = repo.debugwireargs(*vals, **args)
+ ui.write("%s\n" % res1)
+ if res1 != res2:
+ ui.warn("%s\n" % res2)
+
+# Command options and aliases are listed here, alphabetically
+
+table = {
+ "debugancestor": (ancestor, [], _('[INDEX] REV1 REV2')),
+ "debugbuilddag":
+ (builddag,
+ [('m', 'mergeable-file', None, _('add single file mergeable changes')),
+ ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
+ ('n', 'new-file', None, _('add new file at each rev')),
+ ],
+ _('[OPTION]... TEXT')),
+ "debugbundle":
+ (bundle,
+ [('a', 'all', None, _('show all details')),
+ ],
+ _('FILE')),
+ "debugcheckstate": (checkstate, [], ''),
+ "debugcommands": (allcommands, [], _('[COMMAND]')),
+ "debugcomplete":
+ (complete,
+ [('o', 'options', None, _('show the command options'))],
+ _('[-o] CMD')),
+ "debugdag":
+ (dag,
+ [('t', 'tags', None, _('use tags as labels')),
+ ('b', 'branches', None, _('annotate with branch names')),
+ ('', 'dots', None, _('use dots for runs')),
+ ('s', 'spaces', None, _('separate elements by spaces')),
+ ],
+ _('[OPTION]... [FILE [REV]...]')),
+ "debugdate":
+ (date,
+ [('e', 'extended', None, _('try extended date formats'))],
+ _('[-e] DATE [RANGE]')),
+ "debugdata": (data, [], _('FILE REV')),
+ "debugdiscovery": (discovery,
+ [('', 'old', None,
+ _('use old-style discovery')),
+ ('', 'nonheads', None,
+ _('use old-style discovery with non-heads included')),
+ ] + commands.remoteopts,
+ _('[-l REV] [-r REV] [-b BRANCH]...'
+ ' [OTHER]')),
+ "debugfsinfo": (fsinfo, [], _('[PATH]')),
+ "debuggetbundle":
+ (getbundle,
+ [('H', 'head', [], _('id of head node'), _('ID')),
+ ('C', 'common', [], _('id of common node'), _('ID')),
+ ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
+ ],
+ _('REPO FILE [-H|-C ID]...')),
+ "debugignore": (ignore, [], ''),
+ "debugindex": (index,
+ [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
+ _('FILE')),
+ "debugindexdot": (indexdot, [], _('FILE')),
+ "debuginstall": (install, [], ''),
+ "debugknown": (known, [], _('REPO ID...')),
+ "debugpushkey": (pushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
+ "debugrebuildstate":
+ (rebuildstate,
+ [('r', 'rev', '',
+ _('revision to rebuild to'), _('REV'))],
+ _('[-r REV] [REV]')),
+ "debugrename":
+ (rename,
+ [('r', 'rev', '',
+ _('revision to debug'), _('REV'))],
+ _('[-r REV] FILE')),
+ "debugrevspec":
+ (revspec, [], ('REVSPEC')),
+ "debugsetparents":
+ (setparents, [], _('REV1 [REV2]')),
+ "debugstate":
+ (state,
+ [('', 'nodates', None, _('do not display the saved mtime')),
+ ('', 'datesort', None, _('sort by saved mtime'))],
+ _('[OPTION]...')),
+ "debugsub":
+ (sub,
+ [('r', 'rev', '',
+ _('revision to check'), _('REV'))],
+ _('[-r REV] [REV]')),
+ "debugwalk": (walk, commands.walkopts, _('[OPTION]... [FILE]...')),
+ "debugwireargs":
+ (wireargs,
+ [('', 'three', '', 'three'),
+ ('', 'four', '', 'four'),
+ ('', 'five', '', 'five'),
+ ] + commands.remoteopts,
+ _('REPO [OPTIONS]... [ONE [TWO]]')),
+}
+
+norepo = ("debugcommands debugcomplete debugdate debuginstall debugfsinfo"
+ " debugpushkey debugwireargs debugknown debuggetbundle debugbundle")
+optionalrepo = ("debugancestor debugdag debugdata debugindex debugindexdot")
diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -7,7 +7,7 @@
from i18n import _
import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
-import util, commands, hg, fancyopts, extensions, hook, error
+import util, commands, debugcommands, hg, fancyopts, extensions, hook, error
import cmdutil, encoding
import ui as uimod
@@ -503,6 +503,12 @@
rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
path, lui = _getlocal(ui, rpath)
+ if '_debugcommands' not in _loaded:
+ commands.table.update(debugcommands.table)
+ commands.norepo += ' ' + debugcommands.norepo
+ commands.optionalrepo += ' ' + debugcommands.optionalrepo
+ _loaded.add('_debugcommands')
+
# Configure extensions in phases: uisetup, extsetup, cmdtable, and
# reposetup. Programs like TortoiseHg will call _dispatch several
# times so we keep track of configured extensions in _loaded.
I'm +1 on that. You'd need to also update the comment on the top of
the files (after the imports), to point to the location of the debug
commands.
Benoit
I'm mostly indifferent about moving the debug commands as a group, and
not really excited about other splits.
Thing is, I've really never had any trouble with the size of this file.
Each function is alphabetized, easy to grep for, well contained, and
when I'm doing bulk changes, it's convenient to have everything in one
place. And I never need to ask myself "how the hell does this module
work?" which I often have to do with much smaller modules like, say,
dispatch.py. This file is just not a problem organizationally.
One of the few things I would consider changing here is the commands
table. Ideally the command table entries would somehow be stored right
next to the command definition.
--
Mathematics is the supreme nostalgia of our time.
Might be doable with some kind of decorator filling the table maybe?
(But it could lead to a worse startup time).
Benoit
That's one possibility. I don't know enough about what actually happens
in the 'compile stage', but I suspect you're right about startup time.
--
Mathematics is the supreme nostalgia of our time.
Example:
<snip>
table = {}
...
def init(ui, dest=".", **opts):
"""create a new repository in the given directory
Initialize a new repository in the given directory. If the given
directory does not exist, it will be created.
If no directory is given, the current directory is used.
It is possible to specify an ``ssh://`` URL as the destination.
See :hg:`help urls` for more information.
Returns 0 on success.
"""
hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
table["^init"] = (init,
remoteopts,
_('[-e CMD] [--remotecmd CMD] [DEST]'))
</snip>
would that be acceptable?
If yes, I'd probably do such a full patch if you like that pattern.
This would help a lot!
-parren
> On 2011-05-10 22:28, Matt Mackall wrote:
>> One of the few things I would consider changing here is the commands
>> table. Ideally the command table entries would somehow be stored right
>> next to the command definition.
>
> Example:
>
> <snip>
>
> table = {}
>
> ...
>
> def init(ui, dest=".", **opts):
> """create a new repository in the given directory
>
> Initialize a new repository in the given directory. If the given
> directory does not exist, it will be created.
>
> If no directory is given, the current directory is used.
>
> It is possible to specify an ``ssh://`` URL as the destination.
> See :hg:`help urls` for more information.
>
> Returns 0 on success.
> """
> hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
>
> table["^init"] = (init,
> remoteopts,
> _('[-e CMD] [--remotecmd CMD] [DEST]'))
>
> </snip>
>
> would that be acceptable?
I think a decorator would be nicer:
@command(name="^init",
options=remoteopts,
synopsis=_('[-e CMD] [--remotecmd CMD] [DEST]'))
def init(ui, dest=".", **opts):
...
But as Benoit mentioned, this might impact the startup time negatively.
The decorator would have to do something like this:
def command(name, options, synopsis):
def decorator(func):
table[name] = (func, options, synopsis)
return func
return decorator
which means that we get two extra function calls for every defined
command.
--
Martin Geisler
aragost Trifork
Professional Mercurial support
http://mercurial.aragost.com/kick-start/
Thanks for the hint. Amazing stuff these decorators!
Or (after having pondered http://www.artima.com/weblogs/viewpost.jsp?thread=240845 ):
table = {}
class command(object):
def __init__(self, name, options, synopsis):
self.name = name
self.options = options
self.synopsis = synopsis
def __call__(self, func):
table[self.name] = (func, self.options, self.synopsis)
...
@command('^log|history',
[('f', 'follow', None,
_('follow changeset history,'
' or file history across copies and renames')),
('', 'follow-first', None,
_('only follow the first parent of merge changesets')),
('d', 'date', '',
_('show revisions matching date spec'), _('DATE')),
('C', 'copies', None, _('show copied files')),
('k', 'keyword', [],
_('do case-insensitive search for a given text'), _('TEXT')),
('r', 'rev', [],
_('show the specified revision or range'), _('REV')),
('', 'removed', None, _('include revisions where files were removed')),
('m', 'only-merges', None, _('show only merges')),
('u', 'user', [],
_('revisions committed by user'), _('USER')),
('', 'only-branch', [],
_('show only changesets within the given named branch (DEPRECATED)'),
_('BRANCH')),
('b', 'branch', [],
_('show changesets within the given named branch'), _('BRANCH')),
('P', 'prune', [],
_('do not display revision or any of its ancestors'), _('REV')),
] + logopts + walkopts,
_('[OPTION]... [FILE]')
)
def log(ui, repo, *pats, **opts):
...
I think I'll do a full patch for this and then we can measure how fast
(or slow) it is. The increased beauty of the code might be worth it.
> Thanks for the hint. Amazing stuff these decorators!
Yeah, they're quite fun :)
> Or (after having pondered
> http://www.artima.com/weblogs/viewpost.jsp?thread=240845 ):
>
> table = {}
>
> class command(object):
> def __init__(self, name, options, synopsis):
> self.name = name
> self.options = options
> self.synopsis = synopsis
>
> def __call__(self, func):
> table[self.name] = (func, self.options, self.synopsis)
We'll also need to copy the __doc__ over.
If we go this route, then maybe this can be combined with the way normal
and shell aliases are created. They are already created as cmdalias
objects in dispatch which are then inserted into commands.table.
--
Martin Geisler
aragost Trifork
Professional Mercurial support
http://mercurial.aragost.com/kick-start/
Oh yes. I need to return func on __call__ (as you did already in your
proposal):
class command(object):
def __init__(self, name, options, synopsis):
self.name = name
self.options = options
self.synopsis = synopsis
def __call__(self, func):
table[self.name] = (func, self.options, self.synopsis)
return func
For others who learn like I do right now:
As Bruce Eckel notes about decorators *with* arguments: "Notice that
__call__() is now only invoked once, during decoration, and after that
the decorated function that you return from __call__() is used for the
actual calls." [1] (same link as already posted)
(FWIW, I agree with Bruce that class decorators are a bit easier to
understand.)
> If we go this route, then maybe this can be combined with the way normal
> and shell aliases are created. They are already created as cmdalias
> objects in dispatch which are then inserted into commands.table.
Yeah. There might be more fun ahead.
[1] http://www.artima.com/weblogs/viewpost.jsp?thread=240845
> Or (after having pondered http://www.artima.com/weblogs/viewpost.jsp?thread=240845 ):
>
> table = {}
>
> class command(object):
> def __init__(self, name, options, synopsis):
> self.name = name
> self.options = options
> self.synopsis = synopsis
>
> def __call__(self, func):
> table[self.name] = (func, self.options, self.synopsis)
>
If module load time is an issue, this is going to be much slower than
the simple function-based decorator, due to:
- creation of an instance instead of a function
- storing and retrieving data on the instance
In terms of performance after that, it's not affected in either case,
because the original function is returned, and not a wrapper. Most of
the cases on Bruce Eckel's post deal with wrappers being returned, which
is not necessary in this case.
Luke
--
Parenthetical remarks (however relevant) are unnecessary
Luke Plant || http://lukeplant.me.uk/