##// END OF EJS Templates
debugextensions: process extensions loaded from the local repository too
Matt Harbison -
r37998:8ba0344f default
parent child Browse files
Show More
@@ -1,3138 +1,3138
1 # debugcommands.py - command processing for debug* commands
1 # debugcommands.py - command processing for debug* commands
2 #
2 #
3 # Copyright 2005-2016 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2016 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import codecs
10 import codecs
11 import collections
11 import collections
12 import difflib
12 import difflib
13 import errno
13 import errno
14 import operator
14 import operator
15 import os
15 import os
16 import random
16 import random
17 import re
17 import re
18 import socket
18 import socket
19 import ssl
19 import ssl
20 import stat
20 import stat
21 import string
21 import string
22 import subprocess
22 import subprocess
23 import sys
23 import sys
24 import tempfile
24 import tempfile
25 import time
25 import time
26
26
27 from .i18n import _
27 from .i18n import _
28 from .node import (
28 from .node import (
29 bin,
29 bin,
30 hex,
30 hex,
31 nullhex,
31 nullhex,
32 nullid,
32 nullid,
33 nullrev,
33 nullrev,
34 short,
34 short,
35 )
35 )
36 from .thirdparty import (
36 from .thirdparty import (
37 cbor,
37 cbor,
38 )
38 )
39 from . import (
39 from . import (
40 bundle2,
40 bundle2,
41 changegroup,
41 changegroup,
42 cmdutil,
42 cmdutil,
43 color,
43 color,
44 context,
44 context,
45 dagparser,
45 dagparser,
46 dagutil,
46 dagutil,
47 encoding,
47 encoding,
48 error,
48 error,
49 exchange,
49 exchange,
50 extensions,
50 extensions,
51 filemerge,
51 filemerge,
52 fileset,
52 fileset,
53 formatter,
53 formatter,
54 hg,
54 hg,
55 httppeer,
55 httppeer,
56 localrepo,
56 localrepo,
57 lock as lockmod,
57 lock as lockmod,
58 logcmdutil,
58 logcmdutil,
59 merge as mergemod,
59 merge as mergemod,
60 obsolete,
60 obsolete,
61 obsutil,
61 obsutil,
62 phases,
62 phases,
63 policy,
63 policy,
64 pvec,
64 pvec,
65 pycompat,
65 pycompat,
66 registrar,
66 registrar,
67 repair,
67 repair,
68 revlog,
68 revlog,
69 revset,
69 revset,
70 revsetlang,
70 revsetlang,
71 scmutil,
71 scmutil,
72 setdiscovery,
72 setdiscovery,
73 simplemerge,
73 simplemerge,
74 smartset,
74 smartset,
75 sshpeer,
75 sshpeer,
76 sslutil,
76 sslutil,
77 streamclone,
77 streamclone,
78 templater,
78 templater,
79 treediscovery,
79 treediscovery,
80 upgrade,
80 upgrade,
81 url as urlmod,
81 url as urlmod,
82 util,
82 util,
83 vfs as vfsmod,
83 vfs as vfsmod,
84 wireprotoframing,
84 wireprotoframing,
85 wireprotoserver,
85 wireprotoserver,
86 wireprotov2peer,
86 wireprotov2peer,
87 )
87 )
88 from .utils import (
88 from .utils import (
89 dateutil,
89 dateutil,
90 procutil,
90 procutil,
91 stringutil,
91 stringutil,
92 )
92 )
93
93
94 release = lockmod.release
94 release = lockmod.release
95
95
96 command = registrar.command()
96 command = registrar.command()
97
97
98 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
98 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
99 def debugancestor(ui, repo, *args):
99 def debugancestor(ui, repo, *args):
100 """find the ancestor revision of two revisions in a given index"""
100 """find the ancestor revision of two revisions in a given index"""
101 if len(args) == 3:
101 if len(args) == 3:
102 index, rev1, rev2 = args
102 index, rev1, rev2 = args
103 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
103 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
104 lookup = r.lookup
104 lookup = r.lookup
105 elif len(args) == 2:
105 elif len(args) == 2:
106 if not repo:
106 if not repo:
107 raise error.Abort(_('there is no Mercurial repository here '
107 raise error.Abort(_('there is no Mercurial repository here '
108 '(.hg not found)'))
108 '(.hg not found)'))
109 rev1, rev2 = args
109 rev1, rev2 = args
110 r = repo.changelog
110 r = repo.changelog
111 lookup = repo.lookup
111 lookup = repo.lookup
112 else:
112 else:
113 raise error.Abort(_('either two or three arguments required'))
113 raise error.Abort(_('either two or three arguments required'))
114 a = r.ancestor(lookup(rev1), lookup(rev2))
114 a = r.ancestor(lookup(rev1), lookup(rev2))
115 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
115 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
116
116
117 @command('debugapplystreamclonebundle', [], 'FILE')
117 @command('debugapplystreamclonebundle', [], 'FILE')
118 def debugapplystreamclonebundle(ui, repo, fname):
118 def debugapplystreamclonebundle(ui, repo, fname):
119 """apply a stream clone bundle file"""
119 """apply a stream clone bundle file"""
120 f = hg.openpath(ui, fname)
120 f = hg.openpath(ui, fname)
121 gen = exchange.readbundle(ui, f, fname)
121 gen = exchange.readbundle(ui, f, fname)
122 gen.apply(repo)
122 gen.apply(repo)
123
123
124 @command('debugbuilddag',
124 @command('debugbuilddag',
125 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
125 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
126 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
126 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
127 ('n', 'new-file', None, _('add new file at each rev'))],
127 ('n', 'new-file', None, _('add new file at each rev'))],
128 _('[OPTION]... [TEXT]'))
128 _('[OPTION]... [TEXT]'))
129 def debugbuilddag(ui, repo, text=None,
129 def debugbuilddag(ui, repo, text=None,
130 mergeable_file=False,
130 mergeable_file=False,
131 overwritten_file=False,
131 overwritten_file=False,
132 new_file=False):
132 new_file=False):
133 """builds a repo with a given DAG from scratch in the current empty repo
133 """builds a repo with a given DAG from scratch in the current empty repo
134
134
135 The description of the DAG is read from stdin if not given on the
135 The description of the DAG is read from stdin if not given on the
136 command line.
136 command line.
137
137
138 Elements:
138 Elements:
139
139
140 - "+n" is a linear run of n nodes based on the current default parent
140 - "+n" is a linear run of n nodes based on the current default parent
141 - "." is a single node based on the current default parent
141 - "." is a single node based on the current default parent
142 - "$" resets the default parent to null (implied at the start);
142 - "$" resets the default parent to null (implied at the start);
143 otherwise the default parent is always the last node created
143 otherwise the default parent is always the last node created
144 - "<p" sets the default parent to the backref p
144 - "<p" sets the default parent to the backref p
145 - "*p" is a fork at parent p, which is a backref
145 - "*p" is a fork at parent p, which is a backref
146 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
146 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
147 - "/p2" is a merge of the preceding node and p2
147 - "/p2" is a merge of the preceding node and p2
148 - ":tag" defines a local tag for the preceding node
148 - ":tag" defines a local tag for the preceding node
149 - "@branch" sets the named branch for subsequent nodes
149 - "@branch" sets the named branch for subsequent nodes
150 - "#...\\n" is a comment up to the end of the line
150 - "#...\\n" is a comment up to the end of the line
151
151
152 Whitespace between the above elements is ignored.
152 Whitespace between the above elements is ignored.
153
153
154 A backref is either
154 A backref is either
155
155
156 - a number n, which references the node curr-n, where curr is the current
156 - a number n, which references the node curr-n, where curr is the current
157 node, or
157 node, or
158 - the name of a local tag you placed earlier using ":tag", or
158 - the name of a local tag you placed earlier using ":tag", or
159 - empty to denote the default parent.
159 - empty to denote the default parent.
160
160
161 All string valued-elements are either strictly alphanumeric, or must
161 All string valued-elements are either strictly alphanumeric, or must
162 be enclosed in double quotes ("..."), with "\\" as escape character.
162 be enclosed in double quotes ("..."), with "\\" as escape character.
163 """
163 """
164
164
165 if text is None:
165 if text is None:
166 ui.status(_("reading DAG from stdin\n"))
166 ui.status(_("reading DAG from stdin\n"))
167 text = ui.fin.read()
167 text = ui.fin.read()
168
168
169 cl = repo.changelog
169 cl = repo.changelog
170 if len(cl) > 0:
170 if len(cl) > 0:
171 raise error.Abort(_('repository is not empty'))
171 raise error.Abort(_('repository is not empty'))
172
172
173 # determine number of revs in DAG
173 # determine number of revs in DAG
174 total = 0
174 total = 0
175 for type, data in dagparser.parsedag(text):
175 for type, data in dagparser.parsedag(text):
176 if type == 'n':
176 if type == 'n':
177 total += 1
177 total += 1
178
178
179 if mergeable_file:
179 if mergeable_file:
180 linesperrev = 2
180 linesperrev = 2
181 # make a file with k lines per rev
181 # make a file with k lines per rev
182 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
182 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
183 initialmergedlines.append("")
183 initialmergedlines.append("")
184
184
185 tags = []
185 tags = []
186
186
187 wlock = lock = tr = None
187 wlock = lock = tr = None
188 try:
188 try:
189 wlock = repo.wlock()
189 wlock = repo.wlock()
190 lock = repo.lock()
190 lock = repo.lock()
191 tr = repo.transaction("builddag")
191 tr = repo.transaction("builddag")
192
192
193 at = -1
193 at = -1
194 atbranch = 'default'
194 atbranch = 'default'
195 nodeids = []
195 nodeids = []
196 id = 0
196 id = 0
197 ui.progress(_('building'), id, unit=_('revisions'), total=total)
197 ui.progress(_('building'), id, unit=_('revisions'), total=total)
198 for type, data in dagparser.parsedag(text):
198 for type, data in dagparser.parsedag(text):
199 if type == 'n':
199 if type == 'n':
200 ui.note(('node %s\n' % pycompat.bytestr(data)))
200 ui.note(('node %s\n' % pycompat.bytestr(data)))
201 id, ps = data
201 id, ps = data
202
202
203 files = []
203 files = []
204 filecontent = {}
204 filecontent = {}
205
205
206 p2 = None
206 p2 = None
207 if mergeable_file:
207 if mergeable_file:
208 fn = "mf"
208 fn = "mf"
209 p1 = repo[ps[0]]
209 p1 = repo[ps[0]]
210 if len(ps) > 1:
210 if len(ps) > 1:
211 p2 = repo[ps[1]]
211 p2 = repo[ps[1]]
212 pa = p1.ancestor(p2)
212 pa = p1.ancestor(p2)
213 base, local, other = [x[fn].data() for x in (pa, p1,
213 base, local, other = [x[fn].data() for x in (pa, p1,
214 p2)]
214 p2)]
215 m3 = simplemerge.Merge3Text(base, local, other)
215 m3 = simplemerge.Merge3Text(base, local, other)
216 ml = [l.strip() for l in m3.merge_lines()]
216 ml = [l.strip() for l in m3.merge_lines()]
217 ml.append("")
217 ml.append("")
218 elif at > 0:
218 elif at > 0:
219 ml = p1[fn].data().split("\n")
219 ml = p1[fn].data().split("\n")
220 else:
220 else:
221 ml = initialmergedlines
221 ml = initialmergedlines
222 ml[id * linesperrev] += " r%i" % id
222 ml[id * linesperrev] += " r%i" % id
223 mergedtext = "\n".join(ml)
223 mergedtext = "\n".join(ml)
224 files.append(fn)
224 files.append(fn)
225 filecontent[fn] = mergedtext
225 filecontent[fn] = mergedtext
226
226
227 if overwritten_file:
227 if overwritten_file:
228 fn = "of"
228 fn = "of"
229 files.append(fn)
229 files.append(fn)
230 filecontent[fn] = "r%i\n" % id
230 filecontent[fn] = "r%i\n" % id
231
231
232 if new_file:
232 if new_file:
233 fn = "nf%i" % id
233 fn = "nf%i" % id
234 files.append(fn)
234 files.append(fn)
235 filecontent[fn] = "r%i\n" % id
235 filecontent[fn] = "r%i\n" % id
236 if len(ps) > 1:
236 if len(ps) > 1:
237 if not p2:
237 if not p2:
238 p2 = repo[ps[1]]
238 p2 = repo[ps[1]]
239 for fn in p2:
239 for fn in p2:
240 if fn.startswith("nf"):
240 if fn.startswith("nf"):
241 files.append(fn)
241 files.append(fn)
242 filecontent[fn] = p2[fn].data()
242 filecontent[fn] = p2[fn].data()
243
243
244 def fctxfn(repo, cx, path):
244 def fctxfn(repo, cx, path):
245 if path in filecontent:
245 if path in filecontent:
246 return context.memfilectx(repo, cx, path,
246 return context.memfilectx(repo, cx, path,
247 filecontent[path])
247 filecontent[path])
248 return None
248 return None
249
249
250 if len(ps) == 0 or ps[0] < 0:
250 if len(ps) == 0 or ps[0] < 0:
251 pars = [None, None]
251 pars = [None, None]
252 elif len(ps) == 1:
252 elif len(ps) == 1:
253 pars = [nodeids[ps[0]], None]
253 pars = [nodeids[ps[0]], None]
254 else:
254 else:
255 pars = [nodeids[p] for p in ps]
255 pars = [nodeids[p] for p in ps]
256 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
256 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
257 date=(id, 0),
257 date=(id, 0),
258 user="debugbuilddag",
258 user="debugbuilddag",
259 extra={'branch': atbranch})
259 extra={'branch': atbranch})
260 nodeid = repo.commitctx(cx)
260 nodeid = repo.commitctx(cx)
261 nodeids.append(nodeid)
261 nodeids.append(nodeid)
262 at = id
262 at = id
263 elif type == 'l':
263 elif type == 'l':
264 id, name = data
264 id, name = data
265 ui.note(('tag %s\n' % name))
265 ui.note(('tag %s\n' % name))
266 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
266 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
267 elif type == 'a':
267 elif type == 'a':
268 ui.note(('branch %s\n' % data))
268 ui.note(('branch %s\n' % data))
269 atbranch = data
269 atbranch = data
270 ui.progress(_('building'), id, unit=_('revisions'), total=total)
270 ui.progress(_('building'), id, unit=_('revisions'), total=total)
271 tr.close()
271 tr.close()
272
272
273 if tags:
273 if tags:
274 repo.vfs.write("localtags", "".join(tags))
274 repo.vfs.write("localtags", "".join(tags))
275 finally:
275 finally:
276 ui.progress(_('building'), None)
276 ui.progress(_('building'), None)
277 release(tr, lock, wlock)
277 release(tr, lock, wlock)
278
278
279 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
279 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
280 indent_string = ' ' * indent
280 indent_string = ' ' * indent
281 if all:
281 if all:
282 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
282 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
283 % indent_string)
283 % indent_string)
284
284
285 def showchunks(named):
285 def showchunks(named):
286 ui.write("\n%s%s\n" % (indent_string, named))
286 ui.write("\n%s%s\n" % (indent_string, named))
287 for deltadata in gen.deltaiter():
287 for deltadata in gen.deltaiter():
288 node, p1, p2, cs, deltabase, delta, flags = deltadata
288 node, p1, p2, cs, deltabase, delta, flags = deltadata
289 ui.write("%s%s %s %s %s %s %d\n" %
289 ui.write("%s%s %s %s %s %s %d\n" %
290 (indent_string, hex(node), hex(p1), hex(p2),
290 (indent_string, hex(node), hex(p1), hex(p2),
291 hex(cs), hex(deltabase), len(delta)))
291 hex(cs), hex(deltabase), len(delta)))
292
292
293 chunkdata = gen.changelogheader()
293 chunkdata = gen.changelogheader()
294 showchunks("changelog")
294 showchunks("changelog")
295 chunkdata = gen.manifestheader()
295 chunkdata = gen.manifestheader()
296 showchunks("manifest")
296 showchunks("manifest")
297 for chunkdata in iter(gen.filelogheader, {}):
297 for chunkdata in iter(gen.filelogheader, {}):
298 fname = chunkdata['filename']
298 fname = chunkdata['filename']
299 showchunks(fname)
299 showchunks(fname)
300 else:
300 else:
301 if isinstance(gen, bundle2.unbundle20):
301 if isinstance(gen, bundle2.unbundle20):
302 raise error.Abort(_('use debugbundle2 for this file'))
302 raise error.Abort(_('use debugbundle2 for this file'))
303 chunkdata = gen.changelogheader()
303 chunkdata = gen.changelogheader()
304 for deltadata in gen.deltaiter():
304 for deltadata in gen.deltaiter():
305 node, p1, p2, cs, deltabase, delta, flags = deltadata
305 node, p1, p2, cs, deltabase, delta, flags = deltadata
306 ui.write("%s%s\n" % (indent_string, hex(node)))
306 ui.write("%s%s\n" % (indent_string, hex(node)))
307
307
308 def _debugobsmarkers(ui, part, indent=0, **opts):
308 def _debugobsmarkers(ui, part, indent=0, **opts):
309 """display version and markers contained in 'data'"""
309 """display version and markers contained in 'data'"""
310 opts = pycompat.byteskwargs(opts)
310 opts = pycompat.byteskwargs(opts)
311 data = part.read()
311 data = part.read()
312 indent_string = ' ' * indent
312 indent_string = ' ' * indent
313 try:
313 try:
314 version, markers = obsolete._readmarkers(data)
314 version, markers = obsolete._readmarkers(data)
315 except error.UnknownVersion as exc:
315 except error.UnknownVersion as exc:
316 msg = "%sunsupported version: %s (%d bytes)\n"
316 msg = "%sunsupported version: %s (%d bytes)\n"
317 msg %= indent_string, exc.version, len(data)
317 msg %= indent_string, exc.version, len(data)
318 ui.write(msg)
318 ui.write(msg)
319 else:
319 else:
320 msg = "%sversion: %d (%d bytes)\n"
320 msg = "%sversion: %d (%d bytes)\n"
321 msg %= indent_string, version, len(data)
321 msg %= indent_string, version, len(data)
322 ui.write(msg)
322 ui.write(msg)
323 fm = ui.formatter('debugobsolete', opts)
323 fm = ui.formatter('debugobsolete', opts)
324 for rawmarker in sorted(markers):
324 for rawmarker in sorted(markers):
325 m = obsutil.marker(None, rawmarker)
325 m = obsutil.marker(None, rawmarker)
326 fm.startitem()
326 fm.startitem()
327 fm.plain(indent_string)
327 fm.plain(indent_string)
328 cmdutil.showmarker(fm, m)
328 cmdutil.showmarker(fm, m)
329 fm.end()
329 fm.end()
330
330
331 def _debugphaseheads(ui, data, indent=0):
331 def _debugphaseheads(ui, data, indent=0):
332 """display version and markers contained in 'data'"""
332 """display version and markers contained in 'data'"""
333 indent_string = ' ' * indent
333 indent_string = ' ' * indent
334 headsbyphase = phases.binarydecode(data)
334 headsbyphase = phases.binarydecode(data)
335 for phase in phases.allphases:
335 for phase in phases.allphases:
336 for head in headsbyphase[phase]:
336 for head in headsbyphase[phase]:
337 ui.write(indent_string)
337 ui.write(indent_string)
338 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
338 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
339
339
340 def _quasirepr(thing):
340 def _quasirepr(thing):
341 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
341 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
342 return '{%s}' % (
342 return '{%s}' % (
343 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
343 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
344 return pycompat.bytestr(repr(thing))
344 return pycompat.bytestr(repr(thing))
345
345
346 def _debugbundle2(ui, gen, all=None, **opts):
346 def _debugbundle2(ui, gen, all=None, **opts):
347 """lists the contents of a bundle2"""
347 """lists the contents of a bundle2"""
348 if not isinstance(gen, bundle2.unbundle20):
348 if not isinstance(gen, bundle2.unbundle20):
349 raise error.Abort(_('not a bundle2 file'))
349 raise error.Abort(_('not a bundle2 file'))
350 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
350 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
351 parttypes = opts.get(r'part_type', [])
351 parttypes = opts.get(r'part_type', [])
352 for part in gen.iterparts():
352 for part in gen.iterparts():
353 if parttypes and part.type not in parttypes:
353 if parttypes and part.type not in parttypes:
354 continue
354 continue
355 ui.write('%s -- %s\n' % (part.type, _quasirepr(part.params)))
355 ui.write('%s -- %s\n' % (part.type, _quasirepr(part.params)))
356 if part.type == 'changegroup':
356 if part.type == 'changegroup':
357 version = part.params.get('version', '01')
357 version = part.params.get('version', '01')
358 cg = changegroup.getunbundler(version, part, 'UN')
358 cg = changegroup.getunbundler(version, part, 'UN')
359 if not ui.quiet:
359 if not ui.quiet:
360 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
360 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
361 if part.type == 'obsmarkers':
361 if part.type == 'obsmarkers':
362 if not ui.quiet:
362 if not ui.quiet:
363 _debugobsmarkers(ui, part, indent=4, **opts)
363 _debugobsmarkers(ui, part, indent=4, **opts)
364 if part.type == 'phase-heads':
364 if part.type == 'phase-heads':
365 if not ui.quiet:
365 if not ui.quiet:
366 _debugphaseheads(ui, part, indent=4)
366 _debugphaseheads(ui, part, indent=4)
367
367
368 @command('debugbundle',
368 @command('debugbundle',
369 [('a', 'all', None, _('show all details')),
369 [('a', 'all', None, _('show all details')),
370 ('', 'part-type', [], _('show only the named part type')),
370 ('', 'part-type', [], _('show only the named part type')),
371 ('', 'spec', None, _('print the bundlespec of the bundle'))],
371 ('', 'spec', None, _('print the bundlespec of the bundle'))],
372 _('FILE'),
372 _('FILE'),
373 norepo=True)
373 norepo=True)
374 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
374 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
375 """lists the contents of a bundle"""
375 """lists the contents of a bundle"""
376 with hg.openpath(ui, bundlepath) as f:
376 with hg.openpath(ui, bundlepath) as f:
377 if spec:
377 if spec:
378 spec = exchange.getbundlespec(ui, f)
378 spec = exchange.getbundlespec(ui, f)
379 ui.write('%s\n' % spec)
379 ui.write('%s\n' % spec)
380 return
380 return
381
381
382 gen = exchange.readbundle(ui, f, bundlepath)
382 gen = exchange.readbundle(ui, f, bundlepath)
383 if isinstance(gen, bundle2.unbundle20):
383 if isinstance(gen, bundle2.unbundle20):
384 return _debugbundle2(ui, gen, all=all, **opts)
384 return _debugbundle2(ui, gen, all=all, **opts)
385 _debugchangegroup(ui, gen, all=all, **opts)
385 _debugchangegroup(ui, gen, all=all, **opts)
386
386
387 @command('debugcapabilities',
387 @command('debugcapabilities',
388 [], _('PATH'),
388 [], _('PATH'),
389 norepo=True)
389 norepo=True)
390 def debugcapabilities(ui, path, **opts):
390 def debugcapabilities(ui, path, **opts):
391 """lists the capabilities of a remote peer"""
391 """lists the capabilities of a remote peer"""
392 opts = pycompat.byteskwargs(opts)
392 opts = pycompat.byteskwargs(opts)
393 peer = hg.peer(ui, opts, path)
393 peer = hg.peer(ui, opts, path)
394 caps = peer.capabilities()
394 caps = peer.capabilities()
395 ui.write(('Main capabilities:\n'))
395 ui.write(('Main capabilities:\n'))
396 for c in sorted(caps):
396 for c in sorted(caps):
397 ui.write((' %s\n') % c)
397 ui.write((' %s\n') % c)
398 b2caps = bundle2.bundle2caps(peer)
398 b2caps = bundle2.bundle2caps(peer)
399 if b2caps:
399 if b2caps:
400 ui.write(('Bundle2 capabilities:\n'))
400 ui.write(('Bundle2 capabilities:\n'))
401 for key, values in sorted(b2caps.iteritems()):
401 for key, values in sorted(b2caps.iteritems()):
402 ui.write((' %s\n') % key)
402 ui.write((' %s\n') % key)
403 for v in values:
403 for v in values:
404 ui.write((' %s\n') % v)
404 ui.write((' %s\n') % v)
405
405
406 @command('debugcheckstate', [], '')
406 @command('debugcheckstate', [], '')
407 def debugcheckstate(ui, repo):
407 def debugcheckstate(ui, repo):
408 """validate the correctness of the current dirstate"""
408 """validate the correctness of the current dirstate"""
409 parent1, parent2 = repo.dirstate.parents()
409 parent1, parent2 = repo.dirstate.parents()
410 m1 = repo[parent1].manifest()
410 m1 = repo[parent1].manifest()
411 m2 = repo[parent2].manifest()
411 m2 = repo[parent2].manifest()
412 errors = 0
412 errors = 0
413 for f in repo.dirstate:
413 for f in repo.dirstate:
414 state = repo.dirstate[f]
414 state = repo.dirstate[f]
415 if state in "nr" and f not in m1:
415 if state in "nr" and f not in m1:
416 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
416 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
417 errors += 1
417 errors += 1
418 if state in "a" and f in m1:
418 if state in "a" and f in m1:
419 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
419 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
420 errors += 1
420 errors += 1
421 if state in "m" and f not in m1 and f not in m2:
421 if state in "m" and f not in m1 and f not in m2:
422 ui.warn(_("%s in state %s, but not in either manifest\n") %
422 ui.warn(_("%s in state %s, but not in either manifest\n") %
423 (f, state))
423 (f, state))
424 errors += 1
424 errors += 1
425 for f in m1:
425 for f in m1:
426 state = repo.dirstate[f]
426 state = repo.dirstate[f]
427 if state not in "nrm":
427 if state not in "nrm":
428 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
428 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
429 errors += 1
429 errors += 1
430 if errors:
430 if errors:
431 error = _(".hg/dirstate inconsistent with current parent's manifest")
431 error = _(".hg/dirstate inconsistent with current parent's manifest")
432 raise error.Abort(error)
432 raise error.Abort(error)
433
433
434 @command('debugcolor',
434 @command('debugcolor',
435 [('', 'style', None, _('show all configured styles'))],
435 [('', 'style', None, _('show all configured styles'))],
436 'hg debugcolor')
436 'hg debugcolor')
437 def debugcolor(ui, repo, **opts):
437 def debugcolor(ui, repo, **opts):
438 """show available color, effects or style"""
438 """show available color, effects or style"""
439 ui.write(('color mode: %s\n') % ui._colormode)
439 ui.write(('color mode: %s\n') % ui._colormode)
440 if opts.get(r'style'):
440 if opts.get(r'style'):
441 return _debugdisplaystyle(ui)
441 return _debugdisplaystyle(ui)
442 else:
442 else:
443 return _debugdisplaycolor(ui)
443 return _debugdisplaycolor(ui)
444
444
445 def _debugdisplaycolor(ui):
445 def _debugdisplaycolor(ui):
446 ui = ui.copy()
446 ui = ui.copy()
447 ui._styles.clear()
447 ui._styles.clear()
448 for effect in color._activeeffects(ui).keys():
448 for effect in color._activeeffects(ui).keys():
449 ui._styles[effect] = effect
449 ui._styles[effect] = effect
450 if ui._terminfoparams:
450 if ui._terminfoparams:
451 for k, v in ui.configitems('color'):
451 for k, v in ui.configitems('color'):
452 if k.startswith('color.'):
452 if k.startswith('color.'):
453 ui._styles[k] = k[6:]
453 ui._styles[k] = k[6:]
454 elif k.startswith('terminfo.'):
454 elif k.startswith('terminfo.'):
455 ui._styles[k] = k[9:]
455 ui._styles[k] = k[9:]
456 ui.write(_('available colors:\n'))
456 ui.write(_('available colors:\n'))
457 # sort label with a '_' after the other to group '_background' entry.
457 # sort label with a '_' after the other to group '_background' entry.
458 items = sorted(ui._styles.items(),
458 items = sorted(ui._styles.items(),
459 key=lambda i: ('_' in i[0], i[0], i[1]))
459 key=lambda i: ('_' in i[0], i[0], i[1]))
460 for colorname, label in items:
460 for colorname, label in items:
461 ui.write(('%s\n') % colorname, label=label)
461 ui.write(('%s\n') % colorname, label=label)
462
462
463 def _debugdisplaystyle(ui):
463 def _debugdisplaystyle(ui):
464 ui.write(_('available style:\n'))
464 ui.write(_('available style:\n'))
465 if not ui._styles:
465 if not ui._styles:
466 return
466 return
467 width = max(len(s) for s in ui._styles)
467 width = max(len(s) for s in ui._styles)
468 for label, effects in sorted(ui._styles.items()):
468 for label, effects in sorted(ui._styles.items()):
469 ui.write('%s' % label, label=label)
469 ui.write('%s' % label, label=label)
470 if effects:
470 if effects:
471 # 50
471 # 50
472 ui.write(': ')
472 ui.write(': ')
473 ui.write(' ' * (max(0, width - len(label))))
473 ui.write(' ' * (max(0, width - len(label))))
474 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
474 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
475 ui.write('\n')
475 ui.write('\n')
476
476
477 @command('debugcreatestreamclonebundle', [], 'FILE')
477 @command('debugcreatestreamclonebundle', [], 'FILE')
478 def debugcreatestreamclonebundle(ui, repo, fname):
478 def debugcreatestreamclonebundle(ui, repo, fname):
479 """create a stream clone bundle file
479 """create a stream clone bundle file
480
480
481 Stream bundles are special bundles that are essentially archives of
481 Stream bundles are special bundles that are essentially archives of
482 revlog files. They are commonly used for cloning very quickly.
482 revlog files. They are commonly used for cloning very quickly.
483 """
483 """
484 # TODO we may want to turn this into an abort when this functionality
484 # TODO we may want to turn this into an abort when this functionality
485 # is moved into `hg bundle`.
485 # is moved into `hg bundle`.
486 if phases.hassecret(repo):
486 if phases.hassecret(repo):
487 ui.warn(_('(warning: stream clone bundle will contain secret '
487 ui.warn(_('(warning: stream clone bundle will contain secret '
488 'revisions)\n'))
488 'revisions)\n'))
489
489
490 requirements, gen = streamclone.generatebundlev1(repo)
490 requirements, gen = streamclone.generatebundlev1(repo)
491 changegroup.writechunks(ui, gen, fname)
491 changegroup.writechunks(ui, gen, fname)
492
492
493 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
493 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
494
494
495 @command('debugdag',
495 @command('debugdag',
496 [('t', 'tags', None, _('use tags as labels')),
496 [('t', 'tags', None, _('use tags as labels')),
497 ('b', 'branches', None, _('annotate with branch names')),
497 ('b', 'branches', None, _('annotate with branch names')),
498 ('', 'dots', None, _('use dots for runs')),
498 ('', 'dots', None, _('use dots for runs')),
499 ('s', 'spaces', None, _('separate elements by spaces'))],
499 ('s', 'spaces', None, _('separate elements by spaces'))],
500 _('[OPTION]... [FILE [REV]...]'),
500 _('[OPTION]... [FILE [REV]...]'),
501 optionalrepo=True)
501 optionalrepo=True)
502 def debugdag(ui, repo, file_=None, *revs, **opts):
502 def debugdag(ui, repo, file_=None, *revs, **opts):
503 """format the changelog or an index DAG as a concise textual description
503 """format the changelog or an index DAG as a concise textual description
504
504
505 If you pass a revlog index, the revlog's DAG is emitted. If you list
505 If you pass a revlog index, the revlog's DAG is emitted. If you list
506 revision numbers, they get labeled in the output as rN.
506 revision numbers, they get labeled in the output as rN.
507
507
508 Otherwise, the changelog DAG of the current repo is emitted.
508 Otherwise, the changelog DAG of the current repo is emitted.
509 """
509 """
510 spaces = opts.get(r'spaces')
510 spaces = opts.get(r'spaces')
511 dots = opts.get(r'dots')
511 dots = opts.get(r'dots')
512 if file_:
512 if file_:
513 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
513 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
514 file_)
514 file_)
515 revs = set((int(r) for r in revs))
515 revs = set((int(r) for r in revs))
516 def events():
516 def events():
517 for r in rlog:
517 for r in rlog:
518 yield 'n', (r, list(p for p in rlog.parentrevs(r)
518 yield 'n', (r, list(p for p in rlog.parentrevs(r)
519 if p != -1))
519 if p != -1))
520 if r in revs:
520 if r in revs:
521 yield 'l', (r, "r%i" % r)
521 yield 'l', (r, "r%i" % r)
522 elif repo:
522 elif repo:
523 cl = repo.changelog
523 cl = repo.changelog
524 tags = opts.get(r'tags')
524 tags = opts.get(r'tags')
525 branches = opts.get(r'branches')
525 branches = opts.get(r'branches')
526 if tags:
526 if tags:
527 labels = {}
527 labels = {}
528 for l, n in repo.tags().items():
528 for l, n in repo.tags().items():
529 labels.setdefault(cl.rev(n), []).append(l)
529 labels.setdefault(cl.rev(n), []).append(l)
530 def events():
530 def events():
531 b = "default"
531 b = "default"
532 for r in cl:
532 for r in cl:
533 if branches:
533 if branches:
534 newb = cl.read(cl.node(r))[5]['branch']
534 newb = cl.read(cl.node(r))[5]['branch']
535 if newb != b:
535 if newb != b:
536 yield 'a', newb
536 yield 'a', newb
537 b = newb
537 b = newb
538 yield 'n', (r, list(p for p in cl.parentrevs(r)
538 yield 'n', (r, list(p for p in cl.parentrevs(r)
539 if p != -1))
539 if p != -1))
540 if tags:
540 if tags:
541 ls = labels.get(r)
541 ls = labels.get(r)
542 if ls:
542 if ls:
543 for l in ls:
543 for l in ls:
544 yield 'l', (r, l)
544 yield 'l', (r, l)
545 else:
545 else:
546 raise error.Abort(_('need repo for changelog dag'))
546 raise error.Abort(_('need repo for changelog dag'))
547
547
548 for line in dagparser.dagtextlines(events(),
548 for line in dagparser.dagtextlines(events(),
549 addspaces=spaces,
549 addspaces=spaces,
550 wraplabels=True,
550 wraplabels=True,
551 wrapannotations=True,
551 wrapannotations=True,
552 wrapnonlinear=dots,
552 wrapnonlinear=dots,
553 usedots=dots,
553 usedots=dots,
554 maxlinewidth=70):
554 maxlinewidth=70):
555 ui.write(line)
555 ui.write(line)
556 ui.write("\n")
556 ui.write("\n")
557
557
558 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
558 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
559 def debugdata(ui, repo, file_, rev=None, **opts):
559 def debugdata(ui, repo, file_, rev=None, **opts):
560 """dump the contents of a data file revision"""
560 """dump the contents of a data file revision"""
561 opts = pycompat.byteskwargs(opts)
561 opts = pycompat.byteskwargs(opts)
562 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
562 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
563 if rev is not None:
563 if rev is not None:
564 raise error.CommandError('debugdata', _('invalid arguments'))
564 raise error.CommandError('debugdata', _('invalid arguments'))
565 file_, rev = None, file_
565 file_, rev = None, file_
566 elif rev is None:
566 elif rev is None:
567 raise error.CommandError('debugdata', _('invalid arguments'))
567 raise error.CommandError('debugdata', _('invalid arguments'))
568 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
568 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
569 try:
569 try:
570 ui.write(r.revision(r.lookup(rev), raw=True))
570 ui.write(r.revision(r.lookup(rev), raw=True))
571 except KeyError:
571 except KeyError:
572 raise error.Abort(_('invalid revision identifier %s') % rev)
572 raise error.Abort(_('invalid revision identifier %s') % rev)
573
573
574 @command('debugdate',
574 @command('debugdate',
575 [('e', 'extended', None, _('try extended date formats'))],
575 [('e', 'extended', None, _('try extended date formats'))],
576 _('[-e] DATE [RANGE]'),
576 _('[-e] DATE [RANGE]'),
577 norepo=True, optionalrepo=True)
577 norepo=True, optionalrepo=True)
578 def debugdate(ui, date, range=None, **opts):
578 def debugdate(ui, date, range=None, **opts):
579 """parse and display a date"""
579 """parse and display a date"""
580 if opts[r"extended"]:
580 if opts[r"extended"]:
581 d = dateutil.parsedate(date, util.extendeddateformats)
581 d = dateutil.parsedate(date, util.extendeddateformats)
582 else:
582 else:
583 d = dateutil.parsedate(date)
583 d = dateutil.parsedate(date)
584 ui.write(("internal: %d %d\n") % d)
584 ui.write(("internal: %d %d\n") % d)
585 ui.write(("standard: %s\n") % dateutil.datestr(d))
585 ui.write(("standard: %s\n") % dateutil.datestr(d))
586 if range:
586 if range:
587 m = dateutil.matchdate(range)
587 m = dateutil.matchdate(range)
588 ui.write(("match: %s\n") % m(d[0]))
588 ui.write(("match: %s\n") % m(d[0]))
589
589
590 @command('debugdeltachain',
590 @command('debugdeltachain',
591 cmdutil.debugrevlogopts + cmdutil.formatteropts,
591 cmdutil.debugrevlogopts + cmdutil.formatteropts,
592 _('-c|-m|FILE'),
592 _('-c|-m|FILE'),
593 optionalrepo=True)
593 optionalrepo=True)
594 def debugdeltachain(ui, repo, file_=None, **opts):
594 def debugdeltachain(ui, repo, file_=None, **opts):
595 """dump information about delta chains in a revlog
595 """dump information about delta chains in a revlog
596
596
597 Output can be templatized. Available template keywords are:
597 Output can be templatized. Available template keywords are:
598
598
599 :``rev``: revision number
599 :``rev``: revision number
600 :``chainid``: delta chain identifier (numbered by unique base)
600 :``chainid``: delta chain identifier (numbered by unique base)
601 :``chainlen``: delta chain length to this revision
601 :``chainlen``: delta chain length to this revision
602 :``prevrev``: previous revision in delta chain
602 :``prevrev``: previous revision in delta chain
603 :``deltatype``: role of delta / how it was computed
603 :``deltatype``: role of delta / how it was computed
604 :``compsize``: compressed size of revision
604 :``compsize``: compressed size of revision
605 :``uncompsize``: uncompressed size of revision
605 :``uncompsize``: uncompressed size of revision
606 :``chainsize``: total size of compressed revisions in chain
606 :``chainsize``: total size of compressed revisions in chain
607 :``chainratio``: total chain size divided by uncompressed revision size
607 :``chainratio``: total chain size divided by uncompressed revision size
608 (new delta chains typically start at ratio 2.00)
608 (new delta chains typically start at ratio 2.00)
609 :``lindist``: linear distance from base revision in delta chain to end
609 :``lindist``: linear distance from base revision in delta chain to end
610 of this revision
610 of this revision
611 :``extradist``: total size of revisions not part of this delta chain from
611 :``extradist``: total size of revisions not part of this delta chain from
612 base of delta chain to end of this revision; a measurement
612 base of delta chain to end of this revision; a measurement
613 of how much extra data we need to read/seek across to read
613 of how much extra data we need to read/seek across to read
614 the delta chain for this revision
614 the delta chain for this revision
615 :``extraratio``: extradist divided by chainsize; another representation of
615 :``extraratio``: extradist divided by chainsize; another representation of
616 how much unrelated data is needed to load this delta chain
616 how much unrelated data is needed to load this delta chain
617
617
618 If the repository is configured to use the sparse read, additional keywords
618 If the repository is configured to use the sparse read, additional keywords
619 are available:
619 are available:
620
620
621 :``readsize``: total size of data read from the disk for a revision
621 :``readsize``: total size of data read from the disk for a revision
622 (sum of the sizes of all the blocks)
622 (sum of the sizes of all the blocks)
623 :``largestblock``: size of the largest block of data read from the disk
623 :``largestblock``: size of the largest block of data read from the disk
624 :``readdensity``: density of useful bytes in the data read from the disk
624 :``readdensity``: density of useful bytes in the data read from the disk
625 :``srchunks``: in how many data hunks the whole revision would be read
625 :``srchunks``: in how many data hunks the whole revision would be read
626
626
627 The sparse read can be enabled with experimental.sparse-read = True
627 The sparse read can be enabled with experimental.sparse-read = True
628 """
628 """
629 opts = pycompat.byteskwargs(opts)
629 opts = pycompat.byteskwargs(opts)
630 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
630 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
631 index = r.index
631 index = r.index
632 generaldelta = r.version & revlog.FLAG_GENERALDELTA
632 generaldelta = r.version & revlog.FLAG_GENERALDELTA
633 withsparseread = getattr(r, '_withsparseread', False)
633 withsparseread = getattr(r, '_withsparseread', False)
634
634
635 def revinfo(rev):
635 def revinfo(rev):
636 e = index[rev]
636 e = index[rev]
637 compsize = e[1]
637 compsize = e[1]
638 uncompsize = e[2]
638 uncompsize = e[2]
639 chainsize = 0
639 chainsize = 0
640
640
641 if generaldelta:
641 if generaldelta:
642 if e[3] == e[5]:
642 if e[3] == e[5]:
643 deltatype = 'p1'
643 deltatype = 'p1'
644 elif e[3] == e[6]:
644 elif e[3] == e[6]:
645 deltatype = 'p2'
645 deltatype = 'p2'
646 elif e[3] == rev - 1:
646 elif e[3] == rev - 1:
647 deltatype = 'prev'
647 deltatype = 'prev'
648 elif e[3] == rev:
648 elif e[3] == rev:
649 deltatype = 'base'
649 deltatype = 'base'
650 else:
650 else:
651 deltatype = 'other'
651 deltatype = 'other'
652 else:
652 else:
653 if e[3] == rev:
653 if e[3] == rev:
654 deltatype = 'base'
654 deltatype = 'base'
655 else:
655 else:
656 deltatype = 'prev'
656 deltatype = 'prev'
657
657
658 chain = r._deltachain(rev)[0]
658 chain = r._deltachain(rev)[0]
659 for iterrev in chain:
659 for iterrev in chain:
660 e = index[iterrev]
660 e = index[iterrev]
661 chainsize += e[1]
661 chainsize += e[1]
662
662
663 return compsize, uncompsize, deltatype, chain, chainsize
663 return compsize, uncompsize, deltatype, chain, chainsize
664
664
665 fm = ui.formatter('debugdeltachain', opts)
665 fm = ui.formatter('debugdeltachain', opts)
666
666
667 fm.plain(' rev chain# chainlen prev delta '
667 fm.plain(' rev chain# chainlen prev delta '
668 'size rawsize chainsize ratio lindist extradist '
668 'size rawsize chainsize ratio lindist extradist '
669 'extraratio')
669 'extraratio')
670 if withsparseread:
670 if withsparseread:
671 fm.plain(' readsize largestblk rddensity srchunks')
671 fm.plain(' readsize largestblk rddensity srchunks')
672 fm.plain('\n')
672 fm.plain('\n')
673
673
674 chainbases = {}
674 chainbases = {}
675 for rev in r:
675 for rev in r:
676 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
676 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
677 chainbase = chain[0]
677 chainbase = chain[0]
678 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
678 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
679 start = r.start
679 start = r.start
680 length = r.length
680 length = r.length
681 basestart = start(chainbase)
681 basestart = start(chainbase)
682 revstart = start(rev)
682 revstart = start(rev)
683 lineardist = revstart + comp - basestart
683 lineardist = revstart + comp - basestart
684 extradist = lineardist - chainsize
684 extradist = lineardist - chainsize
685 try:
685 try:
686 prevrev = chain[-2]
686 prevrev = chain[-2]
687 except IndexError:
687 except IndexError:
688 prevrev = -1
688 prevrev = -1
689
689
690 chainratio = float(chainsize) / float(uncomp)
690 chainratio = float(chainsize) / float(uncomp)
691 extraratio = float(extradist) / float(chainsize)
691 extraratio = float(extradist) / float(chainsize)
692
692
693 fm.startitem()
693 fm.startitem()
694 fm.write('rev chainid chainlen prevrev deltatype compsize '
694 fm.write('rev chainid chainlen prevrev deltatype compsize '
695 'uncompsize chainsize chainratio lindist extradist '
695 'uncompsize chainsize chainratio lindist extradist '
696 'extraratio',
696 'extraratio',
697 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
697 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
698 rev, chainid, len(chain), prevrev, deltatype, comp,
698 rev, chainid, len(chain), prevrev, deltatype, comp,
699 uncomp, chainsize, chainratio, lineardist, extradist,
699 uncomp, chainsize, chainratio, lineardist, extradist,
700 extraratio,
700 extraratio,
701 rev=rev, chainid=chainid, chainlen=len(chain),
701 rev=rev, chainid=chainid, chainlen=len(chain),
702 prevrev=prevrev, deltatype=deltatype, compsize=comp,
702 prevrev=prevrev, deltatype=deltatype, compsize=comp,
703 uncompsize=uncomp, chainsize=chainsize,
703 uncompsize=uncomp, chainsize=chainsize,
704 chainratio=chainratio, lindist=lineardist,
704 chainratio=chainratio, lindist=lineardist,
705 extradist=extradist, extraratio=extraratio)
705 extradist=extradist, extraratio=extraratio)
706 if withsparseread:
706 if withsparseread:
707 readsize = 0
707 readsize = 0
708 largestblock = 0
708 largestblock = 0
709 srchunks = 0
709 srchunks = 0
710
710
711 for revschunk in revlog._slicechunk(r, chain):
711 for revschunk in revlog._slicechunk(r, chain):
712 srchunks += 1
712 srchunks += 1
713 blkend = start(revschunk[-1]) + length(revschunk[-1])
713 blkend = start(revschunk[-1]) + length(revschunk[-1])
714 blksize = blkend - start(revschunk[0])
714 blksize = blkend - start(revschunk[0])
715
715
716 readsize += blksize
716 readsize += blksize
717 if largestblock < blksize:
717 if largestblock < blksize:
718 largestblock = blksize
718 largestblock = blksize
719
719
720 readdensity = float(chainsize) / float(readsize)
720 readdensity = float(chainsize) / float(readsize)
721
721
722 fm.write('readsize largestblock readdensity srchunks',
722 fm.write('readsize largestblock readdensity srchunks',
723 ' %10d %10d %9.5f %8d',
723 ' %10d %10d %9.5f %8d',
724 readsize, largestblock, readdensity, srchunks,
724 readsize, largestblock, readdensity, srchunks,
725 readsize=readsize, largestblock=largestblock,
725 readsize=readsize, largestblock=largestblock,
726 readdensity=readdensity, srchunks=srchunks)
726 readdensity=readdensity, srchunks=srchunks)
727
727
728 fm.plain('\n')
728 fm.plain('\n')
729
729
730 fm.end()
730 fm.end()
731
731
732 @command('debugdirstate|debugstate',
732 @command('debugdirstate|debugstate',
733 [('', 'nodates', None, _('do not display the saved mtime')),
733 [('', 'nodates', None, _('do not display the saved mtime')),
734 ('', 'datesort', None, _('sort by saved mtime'))],
734 ('', 'datesort', None, _('sort by saved mtime'))],
735 _('[OPTION]...'))
735 _('[OPTION]...'))
736 def debugstate(ui, repo, **opts):
736 def debugstate(ui, repo, **opts):
737 """show the contents of the current dirstate"""
737 """show the contents of the current dirstate"""
738
738
739 nodates = opts.get(r'nodates')
739 nodates = opts.get(r'nodates')
740 datesort = opts.get(r'datesort')
740 datesort = opts.get(r'datesort')
741
741
742 timestr = ""
742 timestr = ""
743 if datesort:
743 if datesort:
744 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
744 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
745 else:
745 else:
746 keyfunc = None # sort by filename
746 keyfunc = None # sort by filename
747 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
747 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
748 if ent[3] == -1:
748 if ent[3] == -1:
749 timestr = 'unset '
749 timestr = 'unset '
750 elif nodates:
750 elif nodates:
751 timestr = 'set '
751 timestr = 'set '
752 else:
752 else:
753 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
753 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
754 time.localtime(ent[3]))
754 time.localtime(ent[3]))
755 timestr = encoding.strtolocal(timestr)
755 timestr = encoding.strtolocal(timestr)
756 if ent[1] & 0o20000:
756 if ent[1] & 0o20000:
757 mode = 'lnk'
757 mode = 'lnk'
758 else:
758 else:
759 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
759 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
760 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
760 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
761 for f in repo.dirstate.copies():
761 for f in repo.dirstate.copies():
762 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
762 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
763
763
764 @command('debugdiscovery',
764 @command('debugdiscovery',
765 [('', 'old', None, _('use old-style discovery')),
765 [('', 'old', None, _('use old-style discovery')),
766 ('', 'nonheads', None,
766 ('', 'nonheads', None,
767 _('use old-style discovery with non-heads included')),
767 _('use old-style discovery with non-heads included')),
768 ('', 'rev', [], 'restrict discovery to this set of revs'),
768 ('', 'rev', [], 'restrict discovery to this set of revs'),
769 ] + cmdutil.remoteopts,
769 ] + cmdutil.remoteopts,
770 _('[--rev REV] [OTHER]'))
770 _('[--rev REV] [OTHER]'))
771 def debugdiscovery(ui, repo, remoteurl="default", **opts):
771 def debugdiscovery(ui, repo, remoteurl="default", **opts):
772 """runs the changeset discovery protocol in isolation"""
772 """runs the changeset discovery protocol in isolation"""
773 opts = pycompat.byteskwargs(opts)
773 opts = pycompat.byteskwargs(opts)
774 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
774 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
775 remote = hg.peer(repo, opts, remoteurl)
775 remote = hg.peer(repo, opts, remoteurl)
776 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
776 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
777
777
778 # make sure tests are repeatable
778 # make sure tests are repeatable
779 random.seed(12323)
779 random.seed(12323)
780
780
781 def doit(pushedrevs, remoteheads, remote=remote):
781 def doit(pushedrevs, remoteheads, remote=remote):
782 if opts.get('old'):
782 if opts.get('old'):
783 if not util.safehasattr(remote, 'branches'):
783 if not util.safehasattr(remote, 'branches'):
784 # enable in-client legacy support
784 # enable in-client legacy support
785 remote = localrepo.locallegacypeer(remote.local())
785 remote = localrepo.locallegacypeer(remote.local())
786 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
786 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
787 force=True)
787 force=True)
788 common = set(common)
788 common = set(common)
789 if not opts.get('nonheads'):
789 if not opts.get('nonheads'):
790 ui.write(("unpruned common: %s\n") %
790 ui.write(("unpruned common: %s\n") %
791 " ".join(sorted(short(n) for n in common)))
791 " ".join(sorted(short(n) for n in common)))
792 dag = dagutil.revlogdag(repo.changelog)
792 dag = dagutil.revlogdag(repo.changelog)
793 all = dag.ancestorset(dag.internalizeall(common))
793 all = dag.ancestorset(dag.internalizeall(common))
794 common = dag.externalizeall(dag.headsetofconnecteds(all))
794 common = dag.externalizeall(dag.headsetofconnecteds(all))
795 else:
795 else:
796 nodes = None
796 nodes = None
797 if pushedrevs:
797 if pushedrevs:
798 revs = scmutil.revrange(repo, pushedrevs)
798 revs = scmutil.revrange(repo, pushedrevs)
799 nodes = [repo[r].node() for r in revs]
799 nodes = [repo[r].node() for r in revs]
800 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
800 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
801 ancestorsof=nodes)
801 ancestorsof=nodes)
802 common = set(common)
802 common = set(common)
803 rheads = set(hds)
803 rheads = set(hds)
804 lheads = set(repo.heads())
804 lheads = set(repo.heads())
805 ui.write(("common heads: %s\n") %
805 ui.write(("common heads: %s\n") %
806 " ".join(sorted(short(n) for n in common)))
806 " ".join(sorted(short(n) for n in common)))
807 if lheads <= common:
807 if lheads <= common:
808 ui.write(("local is subset\n"))
808 ui.write(("local is subset\n"))
809 elif rheads <= common:
809 elif rheads <= common:
810 ui.write(("remote is subset\n"))
810 ui.write(("remote is subset\n"))
811
811
812 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
812 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
813 localrevs = opts['rev']
813 localrevs = opts['rev']
814 doit(localrevs, remoterevs)
814 doit(localrevs, remoterevs)
815
815
816 _chunksize = 4 << 10
816 _chunksize = 4 << 10
817
817
818 @command('debugdownload',
818 @command('debugdownload',
819 [
819 [
820 ('o', 'output', '', _('path')),
820 ('o', 'output', '', _('path')),
821 ],
821 ],
822 optionalrepo=True)
822 optionalrepo=True)
823 def debugdownload(ui, repo, url, output=None, **opts):
823 def debugdownload(ui, repo, url, output=None, **opts):
824 """download a resource using Mercurial logic and config
824 """download a resource using Mercurial logic and config
825 """
825 """
826 fh = urlmod.open(ui, url, output)
826 fh = urlmod.open(ui, url, output)
827
827
828 dest = ui
828 dest = ui
829 if output:
829 if output:
830 dest = open(output, "wb", _chunksize)
830 dest = open(output, "wb", _chunksize)
831 try:
831 try:
832 data = fh.read(_chunksize)
832 data = fh.read(_chunksize)
833 while data:
833 while data:
834 dest.write(data)
834 dest.write(data)
835 data = fh.read(_chunksize)
835 data = fh.read(_chunksize)
836 finally:
836 finally:
837 if output:
837 if output:
838 dest.close()
838 dest.close()
839
839
840 @command('debugextensions', cmdutil.formatteropts, [], norepo=True)
840 @command('debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
841 def debugextensions(ui, **opts):
841 def debugextensions(ui, repo, **opts):
842 '''show information about active extensions'''
842 '''show information about active extensions'''
843 opts = pycompat.byteskwargs(opts)
843 opts = pycompat.byteskwargs(opts)
844 exts = extensions.extensions(ui)
844 exts = extensions.extensions(ui)
845 hgver = util.version()
845 hgver = util.version()
846 fm = ui.formatter('debugextensions', opts)
846 fm = ui.formatter('debugextensions', opts)
847 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
847 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
848 isinternal = extensions.ismoduleinternal(extmod)
848 isinternal = extensions.ismoduleinternal(extmod)
849 extsource = pycompat.fsencode(extmod.__file__)
849 extsource = pycompat.fsencode(extmod.__file__)
850 if isinternal:
850 if isinternal:
851 exttestedwith = [] # never expose magic string to users
851 exttestedwith = [] # never expose magic string to users
852 else:
852 else:
853 exttestedwith = getattr(extmod, 'testedwith', '').split()
853 exttestedwith = getattr(extmod, 'testedwith', '').split()
854 extbuglink = getattr(extmod, 'buglink', None)
854 extbuglink = getattr(extmod, 'buglink', None)
855
855
856 fm.startitem()
856 fm.startitem()
857
857
858 if ui.quiet or ui.verbose:
858 if ui.quiet or ui.verbose:
859 fm.write('name', '%s\n', extname)
859 fm.write('name', '%s\n', extname)
860 else:
860 else:
861 fm.write('name', '%s', extname)
861 fm.write('name', '%s', extname)
862 if isinternal or hgver in exttestedwith:
862 if isinternal or hgver in exttestedwith:
863 fm.plain('\n')
863 fm.plain('\n')
864 elif not exttestedwith:
864 elif not exttestedwith:
865 fm.plain(_(' (untested!)\n'))
865 fm.plain(_(' (untested!)\n'))
866 else:
866 else:
867 lasttestedversion = exttestedwith[-1]
867 lasttestedversion = exttestedwith[-1]
868 fm.plain(' (%s!)\n' % lasttestedversion)
868 fm.plain(' (%s!)\n' % lasttestedversion)
869
869
870 fm.condwrite(ui.verbose and extsource, 'source',
870 fm.condwrite(ui.verbose and extsource, 'source',
871 _(' location: %s\n'), extsource or "")
871 _(' location: %s\n'), extsource or "")
872
872
873 if ui.verbose:
873 if ui.verbose:
874 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
874 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
875 fm.data(bundled=isinternal)
875 fm.data(bundled=isinternal)
876
876
877 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
877 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
878 _(' tested with: %s\n'),
878 _(' tested with: %s\n'),
879 fm.formatlist(exttestedwith, name='ver'))
879 fm.formatlist(exttestedwith, name='ver'))
880
880
881 fm.condwrite(ui.verbose and extbuglink, 'buglink',
881 fm.condwrite(ui.verbose and extbuglink, 'buglink',
882 _(' bug reporting: %s\n'), extbuglink or "")
882 _(' bug reporting: %s\n'), extbuglink or "")
883
883
884 fm.end()
884 fm.end()
885
885
886 @command('debugfileset',
886 @command('debugfileset',
887 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
887 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
888 _('[-r REV] FILESPEC'))
888 _('[-r REV] FILESPEC'))
889 def debugfileset(ui, repo, expr, **opts):
889 def debugfileset(ui, repo, expr, **opts):
890 '''parse and apply a fileset specification'''
890 '''parse and apply a fileset specification'''
891 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
891 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
892 if ui.verbose:
892 if ui.verbose:
893 tree = fileset.parse(expr)
893 tree = fileset.parse(expr)
894 ui.note(fileset.prettyformat(tree), "\n")
894 ui.note(fileset.prettyformat(tree), "\n")
895
895
896 for f in ctx.getfileset(expr):
896 for f in ctx.getfileset(expr):
897 ui.write("%s\n" % f)
897 ui.write("%s\n" % f)
898
898
899 @command('debugformat',
899 @command('debugformat',
900 [] + cmdutil.formatteropts,
900 [] + cmdutil.formatteropts,
901 _(''))
901 _(''))
902 def debugformat(ui, repo, **opts):
902 def debugformat(ui, repo, **opts):
903 """display format information about the current repository
903 """display format information about the current repository
904
904
905 Use --verbose to get extra information about current config value and
905 Use --verbose to get extra information about current config value and
906 Mercurial default."""
906 Mercurial default."""
907 opts = pycompat.byteskwargs(opts)
907 opts = pycompat.byteskwargs(opts)
908 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
908 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
909 maxvariantlength = max(len('format-variant'), maxvariantlength)
909 maxvariantlength = max(len('format-variant'), maxvariantlength)
910
910
911 def makeformatname(name):
911 def makeformatname(name):
912 return '%s:' + (' ' * (maxvariantlength - len(name)))
912 return '%s:' + (' ' * (maxvariantlength - len(name)))
913
913
914 fm = ui.formatter('debugformat', opts)
914 fm = ui.formatter('debugformat', opts)
915 if fm.isplain():
915 if fm.isplain():
916 def formatvalue(value):
916 def formatvalue(value):
917 if util.safehasattr(value, 'startswith'):
917 if util.safehasattr(value, 'startswith'):
918 return value
918 return value
919 if value:
919 if value:
920 return 'yes'
920 return 'yes'
921 else:
921 else:
922 return 'no'
922 return 'no'
923 else:
923 else:
924 formatvalue = pycompat.identity
924 formatvalue = pycompat.identity
925
925
926 fm.plain('format-variant')
926 fm.plain('format-variant')
927 fm.plain(' ' * (maxvariantlength - len('format-variant')))
927 fm.plain(' ' * (maxvariantlength - len('format-variant')))
928 fm.plain(' repo')
928 fm.plain(' repo')
929 if ui.verbose:
929 if ui.verbose:
930 fm.plain(' config default')
930 fm.plain(' config default')
931 fm.plain('\n')
931 fm.plain('\n')
932 for fv in upgrade.allformatvariant:
932 for fv in upgrade.allformatvariant:
933 fm.startitem()
933 fm.startitem()
934 repovalue = fv.fromrepo(repo)
934 repovalue = fv.fromrepo(repo)
935 configvalue = fv.fromconfig(repo)
935 configvalue = fv.fromconfig(repo)
936
936
937 if repovalue != configvalue:
937 if repovalue != configvalue:
938 namelabel = 'formatvariant.name.mismatchconfig'
938 namelabel = 'formatvariant.name.mismatchconfig'
939 repolabel = 'formatvariant.repo.mismatchconfig'
939 repolabel = 'formatvariant.repo.mismatchconfig'
940 elif repovalue != fv.default:
940 elif repovalue != fv.default:
941 namelabel = 'formatvariant.name.mismatchdefault'
941 namelabel = 'formatvariant.name.mismatchdefault'
942 repolabel = 'formatvariant.repo.mismatchdefault'
942 repolabel = 'formatvariant.repo.mismatchdefault'
943 else:
943 else:
944 namelabel = 'formatvariant.name.uptodate'
944 namelabel = 'formatvariant.name.uptodate'
945 repolabel = 'formatvariant.repo.uptodate'
945 repolabel = 'formatvariant.repo.uptodate'
946
946
947 fm.write('name', makeformatname(fv.name), fv.name,
947 fm.write('name', makeformatname(fv.name), fv.name,
948 label=namelabel)
948 label=namelabel)
949 fm.write('repo', ' %3s', formatvalue(repovalue),
949 fm.write('repo', ' %3s', formatvalue(repovalue),
950 label=repolabel)
950 label=repolabel)
951 if fv.default != configvalue:
951 if fv.default != configvalue:
952 configlabel = 'formatvariant.config.special'
952 configlabel = 'formatvariant.config.special'
953 else:
953 else:
954 configlabel = 'formatvariant.config.default'
954 configlabel = 'formatvariant.config.default'
955 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
955 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
956 label=configlabel)
956 label=configlabel)
957 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
957 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
958 label='formatvariant.default')
958 label='formatvariant.default')
959 fm.plain('\n')
959 fm.plain('\n')
960 fm.end()
960 fm.end()
961
961
962 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
962 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
963 def debugfsinfo(ui, path="."):
963 def debugfsinfo(ui, path="."):
964 """show information detected about current filesystem"""
964 """show information detected about current filesystem"""
965 ui.write(('path: %s\n') % path)
965 ui.write(('path: %s\n') % path)
966 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
966 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
967 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
967 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
968 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
968 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
969 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
969 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
970 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
970 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
971 casesensitive = '(unknown)'
971 casesensitive = '(unknown)'
972 try:
972 try:
973 with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f:
973 with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f:
974 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
974 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
975 except OSError:
975 except OSError:
976 pass
976 pass
977 ui.write(('case-sensitive: %s\n') % casesensitive)
977 ui.write(('case-sensitive: %s\n') % casesensitive)
978
978
979 @command('debuggetbundle',
979 @command('debuggetbundle',
980 [('H', 'head', [], _('id of head node'), _('ID')),
980 [('H', 'head', [], _('id of head node'), _('ID')),
981 ('C', 'common', [], _('id of common node'), _('ID')),
981 ('C', 'common', [], _('id of common node'), _('ID')),
982 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
982 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
983 _('REPO FILE [-H|-C ID]...'),
983 _('REPO FILE [-H|-C ID]...'),
984 norepo=True)
984 norepo=True)
985 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
985 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
986 """retrieves a bundle from a repo
986 """retrieves a bundle from a repo
987
987
988 Every ID must be a full-length hex node id string. Saves the bundle to the
988 Every ID must be a full-length hex node id string. Saves the bundle to the
989 given file.
989 given file.
990 """
990 """
991 opts = pycompat.byteskwargs(opts)
991 opts = pycompat.byteskwargs(opts)
992 repo = hg.peer(ui, opts, repopath)
992 repo = hg.peer(ui, opts, repopath)
993 if not repo.capable('getbundle'):
993 if not repo.capable('getbundle'):
994 raise error.Abort("getbundle() not supported by target repository")
994 raise error.Abort("getbundle() not supported by target repository")
995 args = {}
995 args = {}
996 if common:
996 if common:
997 args[r'common'] = [bin(s) for s in common]
997 args[r'common'] = [bin(s) for s in common]
998 if head:
998 if head:
999 args[r'heads'] = [bin(s) for s in head]
999 args[r'heads'] = [bin(s) for s in head]
1000 # TODO: get desired bundlecaps from command line.
1000 # TODO: get desired bundlecaps from command line.
1001 args[r'bundlecaps'] = None
1001 args[r'bundlecaps'] = None
1002 bundle = repo.getbundle('debug', **args)
1002 bundle = repo.getbundle('debug', **args)
1003
1003
1004 bundletype = opts.get('type', 'bzip2').lower()
1004 bundletype = opts.get('type', 'bzip2').lower()
1005 btypes = {'none': 'HG10UN',
1005 btypes = {'none': 'HG10UN',
1006 'bzip2': 'HG10BZ',
1006 'bzip2': 'HG10BZ',
1007 'gzip': 'HG10GZ',
1007 'gzip': 'HG10GZ',
1008 'bundle2': 'HG20'}
1008 'bundle2': 'HG20'}
1009 bundletype = btypes.get(bundletype)
1009 bundletype = btypes.get(bundletype)
1010 if bundletype not in bundle2.bundletypes:
1010 if bundletype not in bundle2.bundletypes:
1011 raise error.Abort(_('unknown bundle type specified with --type'))
1011 raise error.Abort(_('unknown bundle type specified with --type'))
1012 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1012 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1013
1013
1014 @command('debugignore', [], '[FILE]')
1014 @command('debugignore', [], '[FILE]')
1015 def debugignore(ui, repo, *files, **opts):
1015 def debugignore(ui, repo, *files, **opts):
1016 """display the combined ignore pattern and information about ignored files
1016 """display the combined ignore pattern and information about ignored files
1017
1017
1018 With no argument display the combined ignore pattern.
1018 With no argument display the combined ignore pattern.
1019
1019
1020 Given space separated file names, shows if the given file is ignored and
1020 Given space separated file names, shows if the given file is ignored and
1021 if so, show the ignore rule (file and line number) that matched it.
1021 if so, show the ignore rule (file and line number) that matched it.
1022 """
1022 """
1023 ignore = repo.dirstate._ignore
1023 ignore = repo.dirstate._ignore
1024 if not files:
1024 if not files:
1025 # Show all the patterns
1025 # Show all the patterns
1026 ui.write("%s\n" % pycompat.byterepr(ignore))
1026 ui.write("%s\n" % pycompat.byterepr(ignore))
1027 else:
1027 else:
1028 m = scmutil.match(repo[None], pats=files)
1028 m = scmutil.match(repo[None], pats=files)
1029 for f in m.files():
1029 for f in m.files():
1030 nf = util.normpath(f)
1030 nf = util.normpath(f)
1031 ignored = None
1031 ignored = None
1032 ignoredata = None
1032 ignoredata = None
1033 if nf != '.':
1033 if nf != '.':
1034 if ignore(nf):
1034 if ignore(nf):
1035 ignored = nf
1035 ignored = nf
1036 ignoredata = repo.dirstate._ignorefileandline(nf)
1036 ignoredata = repo.dirstate._ignorefileandline(nf)
1037 else:
1037 else:
1038 for p in util.finddirs(nf):
1038 for p in util.finddirs(nf):
1039 if ignore(p):
1039 if ignore(p):
1040 ignored = p
1040 ignored = p
1041 ignoredata = repo.dirstate._ignorefileandline(p)
1041 ignoredata = repo.dirstate._ignorefileandline(p)
1042 break
1042 break
1043 if ignored:
1043 if ignored:
1044 if ignored == nf:
1044 if ignored == nf:
1045 ui.write(_("%s is ignored\n") % m.uipath(f))
1045 ui.write(_("%s is ignored\n") % m.uipath(f))
1046 else:
1046 else:
1047 ui.write(_("%s is ignored because of "
1047 ui.write(_("%s is ignored because of "
1048 "containing folder %s\n")
1048 "containing folder %s\n")
1049 % (m.uipath(f), ignored))
1049 % (m.uipath(f), ignored))
1050 ignorefile, lineno, line = ignoredata
1050 ignorefile, lineno, line = ignoredata
1051 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1051 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1052 % (ignorefile, lineno, line))
1052 % (ignorefile, lineno, line))
1053 else:
1053 else:
1054 ui.write(_("%s is not ignored\n") % m.uipath(f))
1054 ui.write(_("%s is not ignored\n") % m.uipath(f))
1055
1055
1056 @command('debugindex', cmdutil.debugrevlogopts +
1056 @command('debugindex', cmdutil.debugrevlogopts +
1057 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1057 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1058 _('[-f FORMAT] -c|-m|FILE'),
1058 _('[-f FORMAT] -c|-m|FILE'),
1059 optionalrepo=True)
1059 optionalrepo=True)
1060 def debugindex(ui, repo, file_=None, **opts):
1060 def debugindex(ui, repo, file_=None, **opts):
1061 """dump the contents of an index file"""
1061 """dump the contents of an index file"""
1062 opts = pycompat.byteskwargs(opts)
1062 opts = pycompat.byteskwargs(opts)
1063 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1063 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1064 format = opts.get('format', 0)
1064 format = opts.get('format', 0)
1065 if format not in (0, 1):
1065 if format not in (0, 1):
1066 raise error.Abort(_("unknown format %d") % format)
1066 raise error.Abort(_("unknown format %d") % format)
1067
1067
1068 if ui.debugflag:
1068 if ui.debugflag:
1069 shortfn = hex
1069 shortfn = hex
1070 else:
1070 else:
1071 shortfn = short
1071 shortfn = short
1072
1072
1073 # There might not be anything in r, so have a sane default
1073 # There might not be anything in r, so have a sane default
1074 idlen = 12
1074 idlen = 12
1075 for i in r:
1075 for i in r:
1076 idlen = len(shortfn(r.node(i)))
1076 idlen = len(shortfn(r.node(i)))
1077 break
1077 break
1078
1078
1079 if format == 0:
1079 if format == 0:
1080 if ui.verbose:
1080 if ui.verbose:
1081 ui.write((" rev offset length linkrev"
1081 ui.write((" rev offset length linkrev"
1082 " %s %s p2\n") % ("nodeid".ljust(idlen),
1082 " %s %s p2\n") % ("nodeid".ljust(idlen),
1083 "p1".ljust(idlen)))
1083 "p1".ljust(idlen)))
1084 else:
1084 else:
1085 ui.write((" rev linkrev %s %s p2\n") % (
1085 ui.write((" rev linkrev %s %s p2\n") % (
1086 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1086 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1087 elif format == 1:
1087 elif format == 1:
1088 if ui.verbose:
1088 if ui.verbose:
1089 ui.write((" rev flag offset length size link p1"
1089 ui.write((" rev flag offset length size link p1"
1090 " p2 %s\n") % "nodeid".rjust(idlen))
1090 " p2 %s\n") % "nodeid".rjust(idlen))
1091 else:
1091 else:
1092 ui.write((" rev flag size link p1 p2 %s\n") %
1092 ui.write((" rev flag size link p1 p2 %s\n") %
1093 "nodeid".rjust(idlen))
1093 "nodeid".rjust(idlen))
1094
1094
1095 for i in r:
1095 for i in r:
1096 node = r.node(i)
1096 node = r.node(i)
1097 if format == 0:
1097 if format == 0:
1098 try:
1098 try:
1099 pp = r.parents(node)
1099 pp = r.parents(node)
1100 except Exception:
1100 except Exception:
1101 pp = [nullid, nullid]
1101 pp = [nullid, nullid]
1102 if ui.verbose:
1102 if ui.verbose:
1103 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1103 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1104 i, r.start(i), r.length(i), r.linkrev(i),
1104 i, r.start(i), r.length(i), r.linkrev(i),
1105 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1105 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1106 else:
1106 else:
1107 ui.write("% 6d % 7d %s %s %s\n" % (
1107 ui.write("% 6d % 7d %s %s %s\n" % (
1108 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1108 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1109 shortfn(pp[1])))
1109 shortfn(pp[1])))
1110 elif format == 1:
1110 elif format == 1:
1111 pr = r.parentrevs(i)
1111 pr = r.parentrevs(i)
1112 if ui.verbose:
1112 if ui.verbose:
1113 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1113 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1114 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1114 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1115 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1115 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1116 else:
1116 else:
1117 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1117 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1118 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1118 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1119 shortfn(node)))
1119 shortfn(node)))
1120
1120
1121 @command('debugindexdot', cmdutil.debugrevlogopts,
1121 @command('debugindexdot', cmdutil.debugrevlogopts,
1122 _('-c|-m|FILE'), optionalrepo=True)
1122 _('-c|-m|FILE'), optionalrepo=True)
1123 def debugindexdot(ui, repo, file_=None, **opts):
1123 def debugindexdot(ui, repo, file_=None, **opts):
1124 """dump an index DAG as a graphviz dot file"""
1124 """dump an index DAG as a graphviz dot file"""
1125 opts = pycompat.byteskwargs(opts)
1125 opts = pycompat.byteskwargs(opts)
1126 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1126 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1127 ui.write(("digraph G {\n"))
1127 ui.write(("digraph G {\n"))
1128 for i in r:
1128 for i in r:
1129 node = r.node(i)
1129 node = r.node(i)
1130 pp = r.parents(node)
1130 pp = r.parents(node)
1131 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1131 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1132 if pp[1] != nullid:
1132 if pp[1] != nullid:
1133 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1133 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1134 ui.write("}\n")
1134 ui.write("}\n")
1135
1135
1136 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1136 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1137 def debuginstall(ui, **opts):
1137 def debuginstall(ui, **opts):
1138 '''test Mercurial installation
1138 '''test Mercurial installation
1139
1139
1140 Returns 0 on success.
1140 Returns 0 on success.
1141 '''
1141 '''
1142 opts = pycompat.byteskwargs(opts)
1142 opts = pycompat.byteskwargs(opts)
1143
1143
1144 def writetemp(contents):
1144 def writetemp(contents):
1145 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1145 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1146 f = os.fdopen(fd, r"wb")
1146 f = os.fdopen(fd, r"wb")
1147 f.write(contents)
1147 f.write(contents)
1148 f.close()
1148 f.close()
1149 return name
1149 return name
1150
1150
1151 problems = 0
1151 problems = 0
1152
1152
1153 fm = ui.formatter('debuginstall', opts)
1153 fm = ui.formatter('debuginstall', opts)
1154 fm.startitem()
1154 fm.startitem()
1155
1155
1156 # encoding
1156 # encoding
1157 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1157 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1158 err = None
1158 err = None
1159 try:
1159 try:
1160 codecs.lookup(pycompat.sysstr(encoding.encoding))
1160 codecs.lookup(pycompat.sysstr(encoding.encoding))
1161 except LookupError as inst:
1161 except LookupError as inst:
1162 err = stringutil.forcebytestr(inst)
1162 err = stringutil.forcebytestr(inst)
1163 problems += 1
1163 problems += 1
1164 fm.condwrite(err, 'encodingerror', _(" %s\n"
1164 fm.condwrite(err, 'encodingerror', _(" %s\n"
1165 " (check that your locale is properly set)\n"), err)
1165 " (check that your locale is properly set)\n"), err)
1166
1166
1167 # Python
1167 # Python
1168 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1168 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1169 pycompat.sysexecutable)
1169 pycompat.sysexecutable)
1170 fm.write('pythonver', _("checking Python version (%s)\n"),
1170 fm.write('pythonver', _("checking Python version (%s)\n"),
1171 ("%d.%d.%d" % sys.version_info[:3]))
1171 ("%d.%d.%d" % sys.version_info[:3]))
1172 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1172 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1173 os.path.dirname(pycompat.fsencode(os.__file__)))
1173 os.path.dirname(pycompat.fsencode(os.__file__)))
1174
1174
1175 security = set(sslutil.supportedprotocols)
1175 security = set(sslutil.supportedprotocols)
1176 if sslutil.hassni:
1176 if sslutil.hassni:
1177 security.add('sni')
1177 security.add('sni')
1178
1178
1179 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1179 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1180 fm.formatlist(sorted(security), name='protocol',
1180 fm.formatlist(sorted(security), name='protocol',
1181 fmt='%s', sep=','))
1181 fmt='%s', sep=','))
1182
1182
1183 # These are warnings, not errors. So don't increment problem count. This
1183 # These are warnings, not errors. So don't increment problem count. This
1184 # may change in the future.
1184 # may change in the future.
1185 if 'tls1.2' not in security:
1185 if 'tls1.2' not in security:
1186 fm.plain(_(' TLS 1.2 not supported by Python install; '
1186 fm.plain(_(' TLS 1.2 not supported by Python install; '
1187 'network connections lack modern security\n'))
1187 'network connections lack modern security\n'))
1188 if 'sni' not in security:
1188 if 'sni' not in security:
1189 fm.plain(_(' SNI not supported by Python install; may have '
1189 fm.plain(_(' SNI not supported by Python install; may have '
1190 'connectivity issues with some servers\n'))
1190 'connectivity issues with some servers\n'))
1191
1191
1192 # TODO print CA cert info
1192 # TODO print CA cert info
1193
1193
1194 # hg version
1194 # hg version
1195 hgver = util.version()
1195 hgver = util.version()
1196 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1196 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1197 hgver.split('+')[0])
1197 hgver.split('+')[0])
1198 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1198 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1199 '+'.join(hgver.split('+')[1:]))
1199 '+'.join(hgver.split('+')[1:]))
1200
1200
1201 # compiled modules
1201 # compiled modules
1202 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1202 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1203 policy.policy)
1203 policy.policy)
1204 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1204 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1205 os.path.dirname(pycompat.fsencode(__file__)))
1205 os.path.dirname(pycompat.fsencode(__file__)))
1206
1206
1207 if policy.policy in ('c', 'allow'):
1207 if policy.policy in ('c', 'allow'):
1208 err = None
1208 err = None
1209 try:
1209 try:
1210 from .cext import (
1210 from .cext import (
1211 base85,
1211 base85,
1212 bdiff,
1212 bdiff,
1213 mpatch,
1213 mpatch,
1214 osutil,
1214 osutil,
1215 )
1215 )
1216 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1216 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1217 except Exception as inst:
1217 except Exception as inst:
1218 err = stringutil.forcebytestr(inst)
1218 err = stringutil.forcebytestr(inst)
1219 problems += 1
1219 problems += 1
1220 fm.condwrite(err, 'extensionserror', " %s\n", err)
1220 fm.condwrite(err, 'extensionserror', " %s\n", err)
1221
1221
1222 compengines = util.compengines._engines.values()
1222 compengines = util.compengines._engines.values()
1223 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1223 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1224 fm.formatlist(sorted(e.name() for e in compengines),
1224 fm.formatlist(sorted(e.name() for e in compengines),
1225 name='compengine', fmt='%s', sep=', '))
1225 name='compengine', fmt='%s', sep=', '))
1226 fm.write('compenginesavail', _('checking available compression engines '
1226 fm.write('compenginesavail', _('checking available compression engines '
1227 '(%s)\n'),
1227 '(%s)\n'),
1228 fm.formatlist(sorted(e.name() for e in compengines
1228 fm.formatlist(sorted(e.name() for e in compengines
1229 if e.available()),
1229 if e.available()),
1230 name='compengine', fmt='%s', sep=', '))
1230 name='compengine', fmt='%s', sep=', '))
1231 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1231 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1232 fm.write('compenginesserver', _('checking available compression engines '
1232 fm.write('compenginesserver', _('checking available compression engines '
1233 'for wire protocol (%s)\n'),
1233 'for wire protocol (%s)\n'),
1234 fm.formatlist([e.name() for e in wirecompengines
1234 fm.formatlist([e.name() for e in wirecompengines
1235 if e.wireprotosupport()],
1235 if e.wireprotosupport()],
1236 name='compengine', fmt='%s', sep=', '))
1236 name='compengine', fmt='%s', sep=', '))
1237 re2 = 'missing'
1237 re2 = 'missing'
1238 if util._re2:
1238 if util._re2:
1239 re2 = 'available'
1239 re2 = 'available'
1240 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1240 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1241 fm.data(re2=bool(util._re2))
1241 fm.data(re2=bool(util._re2))
1242
1242
1243 # templates
1243 # templates
1244 p = templater.templatepaths()
1244 p = templater.templatepaths()
1245 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1245 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1246 fm.condwrite(not p, '', _(" no template directories found\n"))
1246 fm.condwrite(not p, '', _(" no template directories found\n"))
1247 if p:
1247 if p:
1248 m = templater.templatepath("map-cmdline.default")
1248 m = templater.templatepath("map-cmdline.default")
1249 if m:
1249 if m:
1250 # template found, check if it is working
1250 # template found, check if it is working
1251 err = None
1251 err = None
1252 try:
1252 try:
1253 templater.templater.frommapfile(m)
1253 templater.templater.frommapfile(m)
1254 except Exception as inst:
1254 except Exception as inst:
1255 err = stringutil.forcebytestr(inst)
1255 err = stringutil.forcebytestr(inst)
1256 p = None
1256 p = None
1257 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1257 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1258 else:
1258 else:
1259 p = None
1259 p = None
1260 fm.condwrite(p, 'defaulttemplate',
1260 fm.condwrite(p, 'defaulttemplate',
1261 _("checking default template (%s)\n"), m)
1261 _("checking default template (%s)\n"), m)
1262 fm.condwrite(not m, 'defaulttemplatenotfound',
1262 fm.condwrite(not m, 'defaulttemplatenotfound',
1263 _(" template '%s' not found\n"), "default")
1263 _(" template '%s' not found\n"), "default")
1264 if not p:
1264 if not p:
1265 problems += 1
1265 problems += 1
1266 fm.condwrite(not p, '',
1266 fm.condwrite(not p, '',
1267 _(" (templates seem to have been installed incorrectly)\n"))
1267 _(" (templates seem to have been installed incorrectly)\n"))
1268
1268
1269 # editor
1269 # editor
1270 editor = ui.geteditor()
1270 editor = ui.geteditor()
1271 editor = util.expandpath(editor)
1271 editor = util.expandpath(editor)
1272 editorbin = procutil.shellsplit(editor)[0]
1272 editorbin = procutil.shellsplit(editor)[0]
1273 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1273 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1274 cmdpath = procutil.findexe(editorbin)
1274 cmdpath = procutil.findexe(editorbin)
1275 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1275 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1276 _(" No commit editor set and can't find %s in PATH\n"
1276 _(" No commit editor set and can't find %s in PATH\n"
1277 " (specify a commit editor in your configuration"
1277 " (specify a commit editor in your configuration"
1278 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1278 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1279 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1279 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1280 _(" Can't find editor '%s' in PATH\n"
1280 _(" Can't find editor '%s' in PATH\n"
1281 " (specify a commit editor in your configuration"
1281 " (specify a commit editor in your configuration"
1282 " file)\n"), not cmdpath and editorbin)
1282 " file)\n"), not cmdpath and editorbin)
1283 if not cmdpath and editor != 'vi':
1283 if not cmdpath and editor != 'vi':
1284 problems += 1
1284 problems += 1
1285
1285
1286 # check username
1286 # check username
1287 username = None
1287 username = None
1288 err = None
1288 err = None
1289 try:
1289 try:
1290 username = ui.username()
1290 username = ui.username()
1291 except error.Abort as e:
1291 except error.Abort as e:
1292 err = stringutil.forcebytestr(e)
1292 err = stringutil.forcebytestr(e)
1293 problems += 1
1293 problems += 1
1294
1294
1295 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1295 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1296 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1296 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1297 " (specify a username in your configuration file)\n"), err)
1297 " (specify a username in your configuration file)\n"), err)
1298
1298
1299 fm.condwrite(not problems, '',
1299 fm.condwrite(not problems, '',
1300 _("no problems detected\n"))
1300 _("no problems detected\n"))
1301 if not problems:
1301 if not problems:
1302 fm.data(problems=problems)
1302 fm.data(problems=problems)
1303 fm.condwrite(problems, 'problems',
1303 fm.condwrite(problems, 'problems',
1304 _("%d problems detected,"
1304 _("%d problems detected,"
1305 " please check your install!\n"), problems)
1305 " please check your install!\n"), problems)
1306 fm.end()
1306 fm.end()
1307
1307
1308 return problems
1308 return problems
1309
1309
1310 @command('debugknown', [], _('REPO ID...'), norepo=True)
1310 @command('debugknown', [], _('REPO ID...'), norepo=True)
1311 def debugknown(ui, repopath, *ids, **opts):
1311 def debugknown(ui, repopath, *ids, **opts):
1312 """test whether node ids are known to a repo
1312 """test whether node ids are known to a repo
1313
1313
1314 Every ID must be a full-length hex node id string. Returns a list of 0s
1314 Every ID must be a full-length hex node id string. Returns a list of 0s
1315 and 1s indicating unknown/known.
1315 and 1s indicating unknown/known.
1316 """
1316 """
1317 opts = pycompat.byteskwargs(opts)
1317 opts = pycompat.byteskwargs(opts)
1318 repo = hg.peer(ui, opts, repopath)
1318 repo = hg.peer(ui, opts, repopath)
1319 if not repo.capable('known'):
1319 if not repo.capable('known'):
1320 raise error.Abort("known() not supported by target repository")
1320 raise error.Abort("known() not supported by target repository")
1321 flags = repo.known([bin(s) for s in ids])
1321 flags = repo.known([bin(s) for s in ids])
1322 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1322 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1323
1323
1324 @command('debuglabelcomplete', [], _('LABEL...'))
1324 @command('debuglabelcomplete', [], _('LABEL...'))
1325 def debuglabelcomplete(ui, repo, *args):
1325 def debuglabelcomplete(ui, repo, *args):
1326 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1326 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1327 debugnamecomplete(ui, repo, *args)
1327 debugnamecomplete(ui, repo, *args)
1328
1328
1329 @command('debuglocks',
1329 @command('debuglocks',
1330 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1330 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1331 ('W', 'force-wlock', None,
1331 ('W', 'force-wlock', None,
1332 _('free the working state lock (DANGEROUS)')),
1332 _('free the working state lock (DANGEROUS)')),
1333 ('s', 'set-lock', None, _('set the store lock until stopped')),
1333 ('s', 'set-lock', None, _('set the store lock until stopped')),
1334 ('S', 'set-wlock', None,
1334 ('S', 'set-wlock', None,
1335 _('set the working state lock until stopped'))],
1335 _('set the working state lock until stopped'))],
1336 _('[OPTION]...'))
1336 _('[OPTION]...'))
1337 def debuglocks(ui, repo, **opts):
1337 def debuglocks(ui, repo, **opts):
1338 """show or modify state of locks
1338 """show or modify state of locks
1339
1339
1340 By default, this command will show which locks are held. This
1340 By default, this command will show which locks are held. This
1341 includes the user and process holding the lock, the amount of time
1341 includes the user and process holding the lock, the amount of time
1342 the lock has been held, and the machine name where the process is
1342 the lock has been held, and the machine name where the process is
1343 running if it's not local.
1343 running if it's not local.
1344
1344
1345 Locks protect the integrity of Mercurial's data, so should be
1345 Locks protect the integrity of Mercurial's data, so should be
1346 treated with care. System crashes or other interruptions may cause
1346 treated with care. System crashes or other interruptions may cause
1347 locks to not be properly released, though Mercurial will usually
1347 locks to not be properly released, though Mercurial will usually
1348 detect and remove such stale locks automatically.
1348 detect and remove such stale locks automatically.
1349
1349
1350 However, detecting stale locks may not always be possible (for
1350 However, detecting stale locks may not always be possible (for
1351 instance, on a shared filesystem). Removing locks may also be
1351 instance, on a shared filesystem). Removing locks may also be
1352 blocked by filesystem permissions.
1352 blocked by filesystem permissions.
1353
1353
1354 Setting a lock will prevent other commands from changing the data.
1354 Setting a lock will prevent other commands from changing the data.
1355 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1355 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1356 The set locks are removed when the command exits.
1356 The set locks are removed when the command exits.
1357
1357
1358 Returns 0 if no locks are held.
1358 Returns 0 if no locks are held.
1359
1359
1360 """
1360 """
1361
1361
1362 if opts.get(r'force_lock'):
1362 if opts.get(r'force_lock'):
1363 repo.svfs.unlink('lock')
1363 repo.svfs.unlink('lock')
1364 if opts.get(r'force_wlock'):
1364 if opts.get(r'force_wlock'):
1365 repo.vfs.unlink('wlock')
1365 repo.vfs.unlink('wlock')
1366 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1366 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1367 return 0
1367 return 0
1368
1368
1369 locks = []
1369 locks = []
1370 try:
1370 try:
1371 if opts.get(r'set_wlock'):
1371 if opts.get(r'set_wlock'):
1372 try:
1372 try:
1373 locks.append(repo.wlock(False))
1373 locks.append(repo.wlock(False))
1374 except error.LockHeld:
1374 except error.LockHeld:
1375 raise error.Abort(_('wlock is already held'))
1375 raise error.Abort(_('wlock is already held'))
1376 if opts.get(r'set_lock'):
1376 if opts.get(r'set_lock'):
1377 try:
1377 try:
1378 locks.append(repo.lock(False))
1378 locks.append(repo.lock(False))
1379 except error.LockHeld:
1379 except error.LockHeld:
1380 raise error.Abort(_('lock is already held'))
1380 raise error.Abort(_('lock is already held'))
1381 if len(locks):
1381 if len(locks):
1382 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1382 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1383 return 0
1383 return 0
1384 finally:
1384 finally:
1385 release(*locks)
1385 release(*locks)
1386
1386
1387 now = time.time()
1387 now = time.time()
1388 held = 0
1388 held = 0
1389
1389
1390 def report(vfs, name, method):
1390 def report(vfs, name, method):
1391 # this causes stale locks to get reaped for more accurate reporting
1391 # this causes stale locks to get reaped for more accurate reporting
1392 try:
1392 try:
1393 l = method(False)
1393 l = method(False)
1394 except error.LockHeld:
1394 except error.LockHeld:
1395 l = None
1395 l = None
1396
1396
1397 if l:
1397 if l:
1398 l.release()
1398 l.release()
1399 else:
1399 else:
1400 try:
1400 try:
1401 st = vfs.lstat(name)
1401 st = vfs.lstat(name)
1402 age = now - st[stat.ST_MTIME]
1402 age = now - st[stat.ST_MTIME]
1403 user = util.username(st.st_uid)
1403 user = util.username(st.st_uid)
1404 locker = vfs.readlock(name)
1404 locker = vfs.readlock(name)
1405 if ":" in locker:
1405 if ":" in locker:
1406 host, pid = locker.split(':')
1406 host, pid = locker.split(':')
1407 if host == socket.gethostname():
1407 if host == socket.gethostname():
1408 locker = 'user %s, process %s' % (user, pid)
1408 locker = 'user %s, process %s' % (user, pid)
1409 else:
1409 else:
1410 locker = 'user %s, process %s, host %s' \
1410 locker = 'user %s, process %s, host %s' \
1411 % (user, pid, host)
1411 % (user, pid, host)
1412 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1412 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1413 return 1
1413 return 1
1414 except OSError as e:
1414 except OSError as e:
1415 if e.errno != errno.ENOENT:
1415 if e.errno != errno.ENOENT:
1416 raise
1416 raise
1417
1417
1418 ui.write(("%-6s free\n") % (name + ":"))
1418 ui.write(("%-6s free\n") % (name + ":"))
1419 return 0
1419 return 0
1420
1420
1421 held += report(repo.svfs, "lock", repo.lock)
1421 held += report(repo.svfs, "lock", repo.lock)
1422 held += report(repo.vfs, "wlock", repo.wlock)
1422 held += report(repo.vfs, "wlock", repo.wlock)
1423
1423
1424 return held
1424 return held
1425
1425
1426 @command('debugmergestate', [], '')
1426 @command('debugmergestate', [], '')
1427 def debugmergestate(ui, repo, *args):
1427 def debugmergestate(ui, repo, *args):
1428 """print merge state
1428 """print merge state
1429
1429
1430 Use --verbose to print out information about whether v1 or v2 merge state
1430 Use --verbose to print out information about whether v1 or v2 merge state
1431 was chosen."""
1431 was chosen."""
1432 def _hashornull(h):
1432 def _hashornull(h):
1433 if h == nullhex:
1433 if h == nullhex:
1434 return 'null'
1434 return 'null'
1435 else:
1435 else:
1436 return h
1436 return h
1437
1437
1438 def printrecords(version):
1438 def printrecords(version):
1439 ui.write(('* version %d records\n') % version)
1439 ui.write(('* version %d records\n') % version)
1440 if version == 1:
1440 if version == 1:
1441 records = v1records
1441 records = v1records
1442 else:
1442 else:
1443 records = v2records
1443 records = v2records
1444
1444
1445 for rtype, record in records:
1445 for rtype, record in records:
1446 # pretty print some record types
1446 # pretty print some record types
1447 if rtype == 'L':
1447 if rtype == 'L':
1448 ui.write(('local: %s\n') % record)
1448 ui.write(('local: %s\n') % record)
1449 elif rtype == 'O':
1449 elif rtype == 'O':
1450 ui.write(('other: %s\n') % record)
1450 ui.write(('other: %s\n') % record)
1451 elif rtype == 'm':
1451 elif rtype == 'm':
1452 driver, mdstate = record.split('\0', 1)
1452 driver, mdstate = record.split('\0', 1)
1453 ui.write(('merge driver: %s (state "%s")\n')
1453 ui.write(('merge driver: %s (state "%s")\n')
1454 % (driver, mdstate))
1454 % (driver, mdstate))
1455 elif rtype in 'FDC':
1455 elif rtype in 'FDC':
1456 r = record.split('\0')
1456 r = record.split('\0')
1457 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1457 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1458 if version == 1:
1458 if version == 1:
1459 onode = 'not stored in v1 format'
1459 onode = 'not stored in v1 format'
1460 flags = r[7]
1460 flags = r[7]
1461 else:
1461 else:
1462 onode, flags = r[7:9]
1462 onode, flags = r[7:9]
1463 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1463 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1464 % (f, rtype, state, _hashornull(hash)))
1464 % (f, rtype, state, _hashornull(hash)))
1465 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1465 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1466 ui.write((' ancestor path: %s (node %s)\n')
1466 ui.write((' ancestor path: %s (node %s)\n')
1467 % (afile, _hashornull(anode)))
1467 % (afile, _hashornull(anode)))
1468 ui.write((' other path: %s (node %s)\n')
1468 ui.write((' other path: %s (node %s)\n')
1469 % (ofile, _hashornull(onode)))
1469 % (ofile, _hashornull(onode)))
1470 elif rtype == 'f':
1470 elif rtype == 'f':
1471 filename, rawextras = record.split('\0', 1)
1471 filename, rawextras = record.split('\0', 1)
1472 extras = rawextras.split('\0')
1472 extras = rawextras.split('\0')
1473 i = 0
1473 i = 0
1474 extrastrings = []
1474 extrastrings = []
1475 while i < len(extras):
1475 while i < len(extras):
1476 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1476 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1477 i += 2
1477 i += 2
1478
1478
1479 ui.write(('file extras: %s (%s)\n')
1479 ui.write(('file extras: %s (%s)\n')
1480 % (filename, ', '.join(extrastrings)))
1480 % (filename, ', '.join(extrastrings)))
1481 elif rtype == 'l':
1481 elif rtype == 'l':
1482 labels = record.split('\0', 2)
1482 labels = record.split('\0', 2)
1483 labels = [l for l in labels if len(l) > 0]
1483 labels = [l for l in labels if len(l) > 0]
1484 ui.write(('labels:\n'))
1484 ui.write(('labels:\n'))
1485 ui.write((' local: %s\n' % labels[0]))
1485 ui.write((' local: %s\n' % labels[0]))
1486 ui.write((' other: %s\n' % labels[1]))
1486 ui.write((' other: %s\n' % labels[1]))
1487 if len(labels) > 2:
1487 if len(labels) > 2:
1488 ui.write((' base: %s\n' % labels[2]))
1488 ui.write((' base: %s\n' % labels[2]))
1489 else:
1489 else:
1490 ui.write(('unrecognized entry: %s\t%s\n')
1490 ui.write(('unrecognized entry: %s\t%s\n')
1491 % (rtype, record.replace('\0', '\t')))
1491 % (rtype, record.replace('\0', '\t')))
1492
1492
1493 # Avoid mergestate.read() since it may raise an exception for unsupported
1493 # Avoid mergestate.read() since it may raise an exception for unsupported
1494 # merge state records. We shouldn't be doing this, but this is OK since this
1494 # merge state records. We shouldn't be doing this, but this is OK since this
1495 # command is pretty low-level.
1495 # command is pretty low-level.
1496 ms = mergemod.mergestate(repo)
1496 ms = mergemod.mergestate(repo)
1497
1497
1498 # sort so that reasonable information is on top
1498 # sort so that reasonable information is on top
1499 v1records = ms._readrecordsv1()
1499 v1records = ms._readrecordsv1()
1500 v2records = ms._readrecordsv2()
1500 v2records = ms._readrecordsv2()
1501 order = 'LOml'
1501 order = 'LOml'
1502 def key(r):
1502 def key(r):
1503 idx = order.find(r[0])
1503 idx = order.find(r[0])
1504 if idx == -1:
1504 if idx == -1:
1505 return (1, r[1])
1505 return (1, r[1])
1506 else:
1506 else:
1507 return (0, idx)
1507 return (0, idx)
1508 v1records.sort(key=key)
1508 v1records.sort(key=key)
1509 v2records.sort(key=key)
1509 v2records.sort(key=key)
1510
1510
1511 if not v1records and not v2records:
1511 if not v1records and not v2records:
1512 ui.write(('no merge state found\n'))
1512 ui.write(('no merge state found\n'))
1513 elif not v2records:
1513 elif not v2records:
1514 ui.note(('no version 2 merge state\n'))
1514 ui.note(('no version 2 merge state\n'))
1515 printrecords(1)
1515 printrecords(1)
1516 elif ms._v1v2match(v1records, v2records):
1516 elif ms._v1v2match(v1records, v2records):
1517 ui.note(('v1 and v2 states match: using v2\n'))
1517 ui.note(('v1 and v2 states match: using v2\n'))
1518 printrecords(2)
1518 printrecords(2)
1519 else:
1519 else:
1520 ui.note(('v1 and v2 states mismatch: using v1\n'))
1520 ui.note(('v1 and v2 states mismatch: using v1\n'))
1521 printrecords(1)
1521 printrecords(1)
1522 if ui.verbose:
1522 if ui.verbose:
1523 printrecords(2)
1523 printrecords(2)
1524
1524
1525 @command('debugnamecomplete', [], _('NAME...'))
1525 @command('debugnamecomplete', [], _('NAME...'))
1526 def debugnamecomplete(ui, repo, *args):
1526 def debugnamecomplete(ui, repo, *args):
1527 '''complete "names" - tags, open branch names, bookmark names'''
1527 '''complete "names" - tags, open branch names, bookmark names'''
1528
1528
1529 names = set()
1529 names = set()
1530 # since we previously only listed open branches, we will handle that
1530 # since we previously only listed open branches, we will handle that
1531 # specially (after this for loop)
1531 # specially (after this for loop)
1532 for name, ns in repo.names.iteritems():
1532 for name, ns in repo.names.iteritems():
1533 if name != 'branches':
1533 if name != 'branches':
1534 names.update(ns.listnames(repo))
1534 names.update(ns.listnames(repo))
1535 names.update(tag for (tag, heads, tip, closed)
1535 names.update(tag for (tag, heads, tip, closed)
1536 in repo.branchmap().iterbranches() if not closed)
1536 in repo.branchmap().iterbranches() if not closed)
1537 completions = set()
1537 completions = set()
1538 if not args:
1538 if not args:
1539 args = ['']
1539 args = ['']
1540 for a in args:
1540 for a in args:
1541 completions.update(n for n in names if n.startswith(a))
1541 completions.update(n for n in names if n.startswith(a))
1542 ui.write('\n'.join(sorted(completions)))
1542 ui.write('\n'.join(sorted(completions)))
1543 ui.write('\n')
1543 ui.write('\n')
1544
1544
1545 @command('debugobsolete',
1545 @command('debugobsolete',
1546 [('', 'flags', 0, _('markers flag')),
1546 [('', 'flags', 0, _('markers flag')),
1547 ('', 'record-parents', False,
1547 ('', 'record-parents', False,
1548 _('record parent information for the precursor')),
1548 _('record parent information for the precursor')),
1549 ('r', 'rev', [], _('display markers relevant to REV')),
1549 ('r', 'rev', [], _('display markers relevant to REV')),
1550 ('', 'exclusive', False, _('restrict display to markers only '
1550 ('', 'exclusive', False, _('restrict display to markers only '
1551 'relevant to REV')),
1551 'relevant to REV')),
1552 ('', 'index', False, _('display index of the marker')),
1552 ('', 'index', False, _('display index of the marker')),
1553 ('', 'delete', [], _('delete markers specified by indices')),
1553 ('', 'delete', [], _('delete markers specified by indices')),
1554 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1554 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1555 _('[OBSOLETED [REPLACEMENT ...]]'))
1555 _('[OBSOLETED [REPLACEMENT ...]]'))
1556 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1556 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1557 """create arbitrary obsolete marker
1557 """create arbitrary obsolete marker
1558
1558
1559 With no arguments, displays the list of obsolescence markers."""
1559 With no arguments, displays the list of obsolescence markers."""
1560
1560
1561 opts = pycompat.byteskwargs(opts)
1561 opts = pycompat.byteskwargs(opts)
1562
1562
1563 def parsenodeid(s):
1563 def parsenodeid(s):
1564 try:
1564 try:
1565 # We do not use revsingle/revrange functions here to accept
1565 # We do not use revsingle/revrange functions here to accept
1566 # arbitrary node identifiers, possibly not present in the
1566 # arbitrary node identifiers, possibly not present in the
1567 # local repository.
1567 # local repository.
1568 n = bin(s)
1568 n = bin(s)
1569 if len(n) != len(nullid):
1569 if len(n) != len(nullid):
1570 raise TypeError()
1570 raise TypeError()
1571 return n
1571 return n
1572 except TypeError:
1572 except TypeError:
1573 raise error.Abort('changeset references must be full hexadecimal '
1573 raise error.Abort('changeset references must be full hexadecimal '
1574 'node identifiers')
1574 'node identifiers')
1575
1575
1576 if opts.get('delete'):
1576 if opts.get('delete'):
1577 indices = []
1577 indices = []
1578 for v in opts.get('delete'):
1578 for v in opts.get('delete'):
1579 try:
1579 try:
1580 indices.append(int(v))
1580 indices.append(int(v))
1581 except ValueError:
1581 except ValueError:
1582 raise error.Abort(_('invalid index value: %r') % v,
1582 raise error.Abort(_('invalid index value: %r') % v,
1583 hint=_('use integers for indices'))
1583 hint=_('use integers for indices'))
1584
1584
1585 if repo.currenttransaction():
1585 if repo.currenttransaction():
1586 raise error.Abort(_('cannot delete obsmarkers in the middle '
1586 raise error.Abort(_('cannot delete obsmarkers in the middle '
1587 'of transaction.'))
1587 'of transaction.'))
1588
1588
1589 with repo.lock():
1589 with repo.lock():
1590 n = repair.deleteobsmarkers(repo.obsstore, indices)
1590 n = repair.deleteobsmarkers(repo.obsstore, indices)
1591 ui.write(_('deleted %i obsolescence markers\n') % n)
1591 ui.write(_('deleted %i obsolescence markers\n') % n)
1592
1592
1593 return
1593 return
1594
1594
1595 if precursor is not None:
1595 if precursor is not None:
1596 if opts['rev']:
1596 if opts['rev']:
1597 raise error.Abort('cannot select revision when creating marker')
1597 raise error.Abort('cannot select revision when creating marker')
1598 metadata = {}
1598 metadata = {}
1599 metadata['user'] = opts['user'] or ui.username()
1599 metadata['user'] = opts['user'] or ui.username()
1600 succs = tuple(parsenodeid(succ) for succ in successors)
1600 succs = tuple(parsenodeid(succ) for succ in successors)
1601 l = repo.lock()
1601 l = repo.lock()
1602 try:
1602 try:
1603 tr = repo.transaction('debugobsolete')
1603 tr = repo.transaction('debugobsolete')
1604 try:
1604 try:
1605 date = opts.get('date')
1605 date = opts.get('date')
1606 if date:
1606 if date:
1607 date = dateutil.parsedate(date)
1607 date = dateutil.parsedate(date)
1608 else:
1608 else:
1609 date = None
1609 date = None
1610 prec = parsenodeid(precursor)
1610 prec = parsenodeid(precursor)
1611 parents = None
1611 parents = None
1612 if opts['record_parents']:
1612 if opts['record_parents']:
1613 if prec not in repo.unfiltered():
1613 if prec not in repo.unfiltered():
1614 raise error.Abort('cannot used --record-parents on '
1614 raise error.Abort('cannot used --record-parents on '
1615 'unknown changesets')
1615 'unknown changesets')
1616 parents = repo.unfiltered()[prec].parents()
1616 parents = repo.unfiltered()[prec].parents()
1617 parents = tuple(p.node() for p in parents)
1617 parents = tuple(p.node() for p in parents)
1618 repo.obsstore.create(tr, prec, succs, opts['flags'],
1618 repo.obsstore.create(tr, prec, succs, opts['flags'],
1619 parents=parents, date=date,
1619 parents=parents, date=date,
1620 metadata=metadata, ui=ui)
1620 metadata=metadata, ui=ui)
1621 tr.close()
1621 tr.close()
1622 except ValueError as exc:
1622 except ValueError as exc:
1623 raise error.Abort(_('bad obsmarker input: %s') %
1623 raise error.Abort(_('bad obsmarker input: %s') %
1624 pycompat.bytestr(exc))
1624 pycompat.bytestr(exc))
1625 finally:
1625 finally:
1626 tr.release()
1626 tr.release()
1627 finally:
1627 finally:
1628 l.release()
1628 l.release()
1629 else:
1629 else:
1630 if opts['rev']:
1630 if opts['rev']:
1631 revs = scmutil.revrange(repo, opts['rev'])
1631 revs = scmutil.revrange(repo, opts['rev'])
1632 nodes = [repo[r].node() for r in revs]
1632 nodes = [repo[r].node() for r in revs]
1633 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1633 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1634 exclusive=opts['exclusive']))
1634 exclusive=opts['exclusive']))
1635 markers.sort(key=lambda x: x._data)
1635 markers.sort(key=lambda x: x._data)
1636 else:
1636 else:
1637 markers = obsutil.getmarkers(repo)
1637 markers = obsutil.getmarkers(repo)
1638
1638
1639 markerstoiter = markers
1639 markerstoiter = markers
1640 isrelevant = lambda m: True
1640 isrelevant = lambda m: True
1641 if opts.get('rev') and opts.get('index'):
1641 if opts.get('rev') and opts.get('index'):
1642 markerstoiter = obsutil.getmarkers(repo)
1642 markerstoiter = obsutil.getmarkers(repo)
1643 markerset = set(markers)
1643 markerset = set(markers)
1644 isrelevant = lambda m: m in markerset
1644 isrelevant = lambda m: m in markerset
1645
1645
1646 fm = ui.formatter('debugobsolete', opts)
1646 fm = ui.formatter('debugobsolete', opts)
1647 for i, m in enumerate(markerstoiter):
1647 for i, m in enumerate(markerstoiter):
1648 if not isrelevant(m):
1648 if not isrelevant(m):
1649 # marker can be irrelevant when we're iterating over a set
1649 # marker can be irrelevant when we're iterating over a set
1650 # of markers (markerstoiter) which is bigger than the set
1650 # of markers (markerstoiter) which is bigger than the set
1651 # of markers we want to display (markers)
1651 # of markers we want to display (markers)
1652 # this can happen if both --index and --rev options are
1652 # this can happen if both --index and --rev options are
1653 # provided and thus we need to iterate over all of the markers
1653 # provided and thus we need to iterate over all of the markers
1654 # to get the correct indices, but only display the ones that
1654 # to get the correct indices, but only display the ones that
1655 # are relevant to --rev value
1655 # are relevant to --rev value
1656 continue
1656 continue
1657 fm.startitem()
1657 fm.startitem()
1658 ind = i if opts.get('index') else None
1658 ind = i if opts.get('index') else None
1659 cmdutil.showmarker(fm, m, index=ind)
1659 cmdutil.showmarker(fm, m, index=ind)
1660 fm.end()
1660 fm.end()
1661
1661
1662 @command('debugpathcomplete',
1662 @command('debugpathcomplete',
1663 [('f', 'full', None, _('complete an entire path')),
1663 [('f', 'full', None, _('complete an entire path')),
1664 ('n', 'normal', None, _('show only normal files')),
1664 ('n', 'normal', None, _('show only normal files')),
1665 ('a', 'added', None, _('show only added files')),
1665 ('a', 'added', None, _('show only added files')),
1666 ('r', 'removed', None, _('show only removed files'))],
1666 ('r', 'removed', None, _('show only removed files'))],
1667 _('FILESPEC...'))
1667 _('FILESPEC...'))
1668 def debugpathcomplete(ui, repo, *specs, **opts):
1668 def debugpathcomplete(ui, repo, *specs, **opts):
1669 '''complete part or all of a tracked path
1669 '''complete part or all of a tracked path
1670
1670
1671 This command supports shells that offer path name completion. It
1671 This command supports shells that offer path name completion. It
1672 currently completes only files already known to the dirstate.
1672 currently completes only files already known to the dirstate.
1673
1673
1674 Completion extends only to the next path segment unless
1674 Completion extends only to the next path segment unless
1675 --full is specified, in which case entire paths are used.'''
1675 --full is specified, in which case entire paths are used.'''
1676
1676
1677 def complete(path, acceptable):
1677 def complete(path, acceptable):
1678 dirstate = repo.dirstate
1678 dirstate = repo.dirstate
1679 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1679 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1680 rootdir = repo.root + pycompat.ossep
1680 rootdir = repo.root + pycompat.ossep
1681 if spec != repo.root and not spec.startswith(rootdir):
1681 if spec != repo.root and not spec.startswith(rootdir):
1682 return [], []
1682 return [], []
1683 if os.path.isdir(spec):
1683 if os.path.isdir(spec):
1684 spec += '/'
1684 spec += '/'
1685 spec = spec[len(rootdir):]
1685 spec = spec[len(rootdir):]
1686 fixpaths = pycompat.ossep != '/'
1686 fixpaths = pycompat.ossep != '/'
1687 if fixpaths:
1687 if fixpaths:
1688 spec = spec.replace(pycompat.ossep, '/')
1688 spec = spec.replace(pycompat.ossep, '/')
1689 speclen = len(spec)
1689 speclen = len(spec)
1690 fullpaths = opts[r'full']
1690 fullpaths = opts[r'full']
1691 files, dirs = set(), set()
1691 files, dirs = set(), set()
1692 adddir, addfile = dirs.add, files.add
1692 adddir, addfile = dirs.add, files.add
1693 for f, st in dirstate.iteritems():
1693 for f, st in dirstate.iteritems():
1694 if f.startswith(spec) and st[0] in acceptable:
1694 if f.startswith(spec) and st[0] in acceptable:
1695 if fixpaths:
1695 if fixpaths:
1696 f = f.replace('/', pycompat.ossep)
1696 f = f.replace('/', pycompat.ossep)
1697 if fullpaths:
1697 if fullpaths:
1698 addfile(f)
1698 addfile(f)
1699 continue
1699 continue
1700 s = f.find(pycompat.ossep, speclen)
1700 s = f.find(pycompat.ossep, speclen)
1701 if s >= 0:
1701 if s >= 0:
1702 adddir(f[:s])
1702 adddir(f[:s])
1703 else:
1703 else:
1704 addfile(f)
1704 addfile(f)
1705 return files, dirs
1705 return files, dirs
1706
1706
1707 acceptable = ''
1707 acceptable = ''
1708 if opts[r'normal']:
1708 if opts[r'normal']:
1709 acceptable += 'nm'
1709 acceptable += 'nm'
1710 if opts[r'added']:
1710 if opts[r'added']:
1711 acceptable += 'a'
1711 acceptable += 'a'
1712 if opts[r'removed']:
1712 if opts[r'removed']:
1713 acceptable += 'r'
1713 acceptable += 'r'
1714 cwd = repo.getcwd()
1714 cwd = repo.getcwd()
1715 if not specs:
1715 if not specs:
1716 specs = ['.']
1716 specs = ['.']
1717
1717
1718 files, dirs = set(), set()
1718 files, dirs = set(), set()
1719 for spec in specs:
1719 for spec in specs:
1720 f, d = complete(spec, acceptable or 'nmar')
1720 f, d = complete(spec, acceptable or 'nmar')
1721 files.update(f)
1721 files.update(f)
1722 dirs.update(d)
1722 dirs.update(d)
1723 files.update(dirs)
1723 files.update(dirs)
1724 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1724 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1725 ui.write('\n')
1725 ui.write('\n')
1726
1726
1727 @command('debugpeer', [], _('PATH'), norepo=True)
1727 @command('debugpeer', [], _('PATH'), norepo=True)
1728 def debugpeer(ui, path):
1728 def debugpeer(ui, path):
1729 """establish a connection to a peer repository"""
1729 """establish a connection to a peer repository"""
1730 # Always enable peer request logging. Requires --debug to display
1730 # Always enable peer request logging. Requires --debug to display
1731 # though.
1731 # though.
1732 overrides = {
1732 overrides = {
1733 ('devel', 'debug.peer-request'): True,
1733 ('devel', 'debug.peer-request'): True,
1734 }
1734 }
1735
1735
1736 with ui.configoverride(overrides):
1736 with ui.configoverride(overrides):
1737 peer = hg.peer(ui, {}, path)
1737 peer = hg.peer(ui, {}, path)
1738
1738
1739 local = peer.local() is not None
1739 local = peer.local() is not None
1740 canpush = peer.canpush()
1740 canpush = peer.canpush()
1741
1741
1742 ui.write(_('url: %s\n') % peer.url())
1742 ui.write(_('url: %s\n') % peer.url())
1743 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1743 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1744 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1744 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1745
1745
1746 @command('debugpickmergetool',
1746 @command('debugpickmergetool',
1747 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1747 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1748 ('', 'changedelete', None, _('emulate merging change and delete')),
1748 ('', 'changedelete', None, _('emulate merging change and delete')),
1749 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1749 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1750 _('[PATTERN]...'),
1750 _('[PATTERN]...'),
1751 inferrepo=True)
1751 inferrepo=True)
1752 def debugpickmergetool(ui, repo, *pats, **opts):
1752 def debugpickmergetool(ui, repo, *pats, **opts):
1753 """examine which merge tool is chosen for specified file
1753 """examine which merge tool is chosen for specified file
1754
1754
1755 As described in :hg:`help merge-tools`, Mercurial examines
1755 As described in :hg:`help merge-tools`, Mercurial examines
1756 configurations below in this order to decide which merge tool is
1756 configurations below in this order to decide which merge tool is
1757 chosen for specified file.
1757 chosen for specified file.
1758
1758
1759 1. ``--tool`` option
1759 1. ``--tool`` option
1760 2. ``HGMERGE`` environment variable
1760 2. ``HGMERGE`` environment variable
1761 3. configurations in ``merge-patterns`` section
1761 3. configurations in ``merge-patterns`` section
1762 4. configuration of ``ui.merge``
1762 4. configuration of ``ui.merge``
1763 5. configurations in ``merge-tools`` section
1763 5. configurations in ``merge-tools`` section
1764 6. ``hgmerge`` tool (for historical reason only)
1764 6. ``hgmerge`` tool (for historical reason only)
1765 7. default tool for fallback (``:merge`` or ``:prompt``)
1765 7. default tool for fallback (``:merge`` or ``:prompt``)
1766
1766
1767 This command writes out examination result in the style below::
1767 This command writes out examination result in the style below::
1768
1768
1769 FILE = MERGETOOL
1769 FILE = MERGETOOL
1770
1770
1771 By default, all files known in the first parent context of the
1771 By default, all files known in the first parent context of the
1772 working directory are examined. Use file patterns and/or -I/-X
1772 working directory are examined. Use file patterns and/or -I/-X
1773 options to limit target files. -r/--rev is also useful to examine
1773 options to limit target files. -r/--rev is also useful to examine
1774 files in another context without actual updating to it.
1774 files in another context without actual updating to it.
1775
1775
1776 With --debug, this command shows warning messages while matching
1776 With --debug, this command shows warning messages while matching
1777 against ``merge-patterns`` and so on, too. It is recommended to
1777 against ``merge-patterns`` and so on, too. It is recommended to
1778 use this option with explicit file patterns and/or -I/-X options,
1778 use this option with explicit file patterns and/or -I/-X options,
1779 because this option increases amount of output per file according
1779 because this option increases amount of output per file according
1780 to configurations in hgrc.
1780 to configurations in hgrc.
1781
1781
1782 With -v/--verbose, this command shows configurations below at
1782 With -v/--verbose, this command shows configurations below at
1783 first (only if specified).
1783 first (only if specified).
1784
1784
1785 - ``--tool`` option
1785 - ``--tool`` option
1786 - ``HGMERGE`` environment variable
1786 - ``HGMERGE`` environment variable
1787 - configuration of ``ui.merge``
1787 - configuration of ``ui.merge``
1788
1788
1789 If merge tool is chosen before matching against
1789 If merge tool is chosen before matching against
1790 ``merge-patterns``, this command can't show any helpful
1790 ``merge-patterns``, this command can't show any helpful
1791 information, even with --debug. In such case, information above is
1791 information, even with --debug. In such case, information above is
1792 useful to know why a merge tool is chosen.
1792 useful to know why a merge tool is chosen.
1793 """
1793 """
1794 opts = pycompat.byteskwargs(opts)
1794 opts = pycompat.byteskwargs(opts)
1795 overrides = {}
1795 overrides = {}
1796 if opts['tool']:
1796 if opts['tool']:
1797 overrides[('ui', 'forcemerge')] = opts['tool']
1797 overrides[('ui', 'forcemerge')] = opts['tool']
1798 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1798 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1799
1799
1800 with ui.configoverride(overrides, 'debugmergepatterns'):
1800 with ui.configoverride(overrides, 'debugmergepatterns'):
1801 hgmerge = encoding.environ.get("HGMERGE")
1801 hgmerge = encoding.environ.get("HGMERGE")
1802 if hgmerge is not None:
1802 if hgmerge is not None:
1803 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1803 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1804 uimerge = ui.config("ui", "merge")
1804 uimerge = ui.config("ui", "merge")
1805 if uimerge:
1805 if uimerge:
1806 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1806 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1807
1807
1808 ctx = scmutil.revsingle(repo, opts.get('rev'))
1808 ctx = scmutil.revsingle(repo, opts.get('rev'))
1809 m = scmutil.match(ctx, pats, opts)
1809 m = scmutil.match(ctx, pats, opts)
1810 changedelete = opts['changedelete']
1810 changedelete = opts['changedelete']
1811 for path in ctx.walk(m):
1811 for path in ctx.walk(m):
1812 fctx = ctx[path]
1812 fctx = ctx[path]
1813 try:
1813 try:
1814 if not ui.debugflag:
1814 if not ui.debugflag:
1815 ui.pushbuffer(error=True)
1815 ui.pushbuffer(error=True)
1816 tool, toolpath = filemerge._picktool(repo, ui, path,
1816 tool, toolpath = filemerge._picktool(repo, ui, path,
1817 fctx.isbinary(),
1817 fctx.isbinary(),
1818 'l' in fctx.flags(),
1818 'l' in fctx.flags(),
1819 changedelete)
1819 changedelete)
1820 finally:
1820 finally:
1821 if not ui.debugflag:
1821 if not ui.debugflag:
1822 ui.popbuffer()
1822 ui.popbuffer()
1823 ui.write(('%s = %s\n') % (path, tool))
1823 ui.write(('%s = %s\n') % (path, tool))
1824
1824
1825 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1825 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1826 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1826 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1827 '''access the pushkey key/value protocol
1827 '''access the pushkey key/value protocol
1828
1828
1829 With two args, list the keys in the given namespace.
1829 With two args, list the keys in the given namespace.
1830
1830
1831 With five args, set a key to new if it currently is set to old.
1831 With five args, set a key to new if it currently is set to old.
1832 Reports success or failure.
1832 Reports success or failure.
1833 '''
1833 '''
1834
1834
1835 target = hg.peer(ui, {}, repopath)
1835 target = hg.peer(ui, {}, repopath)
1836 if keyinfo:
1836 if keyinfo:
1837 key, old, new = keyinfo
1837 key, old, new = keyinfo
1838 with target.commandexecutor() as e:
1838 with target.commandexecutor() as e:
1839 r = e.callcommand('pushkey', {
1839 r = e.callcommand('pushkey', {
1840 'namespace': namespace,
1840 'namespace': namespace,
1841 'key': key,
1841 'key': key,
1842 'old': old,
1842 'old': old,
1843 'new': new,
1843 'new': new,
1844 }).result()
1844 }).result()
1845
1845
1846 ui.status(pycompat.bytestr(r) + '\n')
1846 ui.status(pycompat.bytestr(r) + '\n')
1847 return not r
1847 return not r
1848 else:
1848 else:
1849 for k, v in sorted(target.listkeys(namespace).iteritems()):
1849 for k, v in sorted(target.listkeys(namespace).iteritems()):
1850 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1850 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1851 stringutil.escapestr(v)))
1851 stringutil.escapestr(v)))
1852
1852
1853 @command('debugpvec', [], _('A B'))
1853 @command('debugpvec', [], _('A B'))
1854 def debugpvec(ui, repo, a, b=None):
1854 def debugpvec(ui, repo, a, b=None):
1855 ca = scmutil.revsingle(repo, a)
1855 ca = scmutil.revsingle(repo, a)
1856 cb = scmutil.revsingle(repo, b)
1856 cb = scmutil.revsingle(repo, b)
1857 pa = pvec.ctxpvec(ca)
1857 pa = pvec.ctxpvec(ca)
1858 pb = pvec.ctxpvec(cb)
1858 pb = pvec.ctxpvec(cb)
1859 if pa == pb:
1859 if pa == pb:
1860 rel = "="
1860 rel = "="
1861 elif pa > pb:
1861 elif pa > pb:
1862 rel = ">"
1862 rel = ">"
1863 elif pa < pb:
1863 elif pa < pb:
1864 rel = "<"
1864 rel = "<"
1865 elif pa | pb:
1865 elif pa | pb:
1866 rel = "|"
1866 rel = "|"
1867 ui.write(_("a: %s\n") % pa)
1867 ui.write(_("a: %s\n") % pa)
1868 ui.write(_("b: %s\n") % pb)
1868 ui.write(_("b: %s\n") % pb)
1869 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1869 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1870 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1870 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1871 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1871 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1872 pa.distance(pb), rel))
1872 pa.distance(pb), rel))
1873
1873
1874 @command('debugrebuilddirstate|debugrebuildstate',
1874 @command('debugrebuilddirstate|debugrebuildstate',
1875 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1875 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1876 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1876 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1877 'the working copy parent')),
1877 'the working copy parent')),
1878 ],
1878 ],
1879 _('[-r REV]'))
1879 _('[-r REV]'))
1880 def debugrebuilddirstate(ui, repo, rev, **opts):
1880 def debugrebuilddirstate(ui, repo, rev, **opts):
1881 """rebuild the dirstate as it would look like for the given revision
1881 """rebuild the dirstate as it would look like for the given revision
1882
1882
1883 If no revision is specified the first current parent will be used.
1883 If no revision is specified the first current parent will be used.
1884
1884
1885 The dirstate will be set to the files of the given revision.
1885 The dirstate will be set to the files of the given revision.
1886 The actual working directory content or existing dirstate
1886 The actual working directory content or existing dirstate
1887 information such as adds or removes is not considered.
1887 information such as adds or removes is not considered.
1888
1888
1889 ``minimal`` will only rebuild the dirstate status for files that claim to be
1889 ``minimal`` will only rebuild the dirstate status for files that claim to be
1890 tracked but are not in the parent manifest, or that exist in the parent
1890 tracked but are not in the parent manifest, or that exist in the parent
1891 manifest but are not in the dirstate. It will not change adds, removes, or
1891 manifest but are not in the dirstate. It will not change adds, removes, or
1892 modified files that are in the working copy parent.
1892 modified files that are in the working copy parent.
1893
1893
1894 One use of this command is to make the next :hg:`status` invocation
1894 One use of this command is to make the next :hg:`status` invocation
1895 check the actual file content.
1895 check the actual file content.
1896 """
1896 """
1897 ctx = scmutil.revsingle(repo, rev)
1897 ctx = scmutil.revsingle(repo, rev)
1898 with repo.wlock():
1898 with repo.wlock():
1899 dirstate = repo.dirstate
1899 dirstate = repo.dirstate
1900 changedfiles = None
1900 changedfiles = None
1901 # See command doc for what minimal does.
1901 # See command doc for what minimal does.
1902 if opts.get(r'minimal'):
1902 if opts.get(r'minimal'):
1903 manifestfiles = set(ctx.manifest().keys())
1903 manifestfiles = set(ctx.manifest().keys())
1904 dirstatefiles = set(dirstate)
1904 dirstatefiles = set(dirstate)
1905 manifestonly = manifestfiles - dirstatefiles
1905 manifestonly = manifestfiles - dirstatefiles
1906 dsonly = dirstatefiles - manifestfiles
1906 dsonly = dirstatefiles - manifestfiles
1907 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1907 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1908 changedfiles = manifestonly | dsnotadded
1908 changedfiles = manifestonly | dsnotadded
1909
1909
1910 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1910 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1911
1911
1912 @command('debugrebuildfncache', [], '')
1912 @command('debugrebuildfncache', [], '')
1913 def debugrebuildfncache(ui, repo):
1913 def debugrebuildfncache(ui, repo):
1914 """rebuild the fncache file"""
1914 """rebuild the fncache file"""
1915 repair.rebuildfncache(ui, repo)
1915 repair.rebuildfncache(ui, repo)
1916
1916
1917 @command('debugrename',
1917 @command('debugrename',
1918 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1918 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1919 _('[-r REV] FILE'))
1919 _('[-r REV] FILE'))
1920 def debugrename(ui, repo, file1, *pats, **opts):
1920 def debugrename(ui, repo, file1, *pats, **opts):
1921 """dump rename information"""
1921 """dump rename information"""
1922
1922
1923 opts = pycompat.byteskwargs(opts)
1923 opts = pycompat.byteskwargs(opts)
1924 ctx = scmutil.revsingle(repo, opts.get('rev'))
1924 ctx = scmutil.revsingle(repo, opts.get('rev'))
1925 m = scmutil.match(ctx, (file1,) + pats, opts)
1925 m = scmutil.match(ctx, (file1,) + pats, opts)
1926 for abs in ctx.walk(m):
1926 for abs in ctx.walk(m):
1927 fctx = ctx[abs]
1927 fctx = ctx[abs]
1928 o = fctx.filelog().renamed(fctx.filenode())
1928 o = fctx.filelog().renamed(fctx.filenode())
1929 rel = m.rel(abs)
1929 rel = m.rel(abs)
1930 if o:
1930 if o:
1931 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1931 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1932 else:
1932 else:
1933 ui.write(_("%s not renamed\n") % rel)
1933 ui.write(_("%s not renamed\n") % rel)
1934
1934
1935 @command('debugrevlog', cmdutil.debugrevlogopts +
1935 @command('debugrevlog', cmdutil.debugrevlogopts +
1936 [('d', 'dump', False, _('dump index data'))],
1936 [('d', 'dump', False, _('dump index data'))],
1937 _('-c|-m|FILE'),
1937 _('-c|-m|FILE'),
1938 optionalrepo=True)
1938 optionalrepo=True)
1939 def debugrevlog(ui, repo, file_=None, **opts):
1939 def debugrevlog(ui, repo, file_=None, **opts):
1940 """show data and statistics about a revlog"""
1940 """show data and statistics about a revlog"""
1941 opts = pycompat.byteskwargs(opts)
1941 opts = pycompat.byteskwargs(opts)
1942 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1942 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1943
1943
1944 if opts.get("dump"):
1944 if opts.get("dump"):
1945 numrevs = len(r)
1945 numrevs = len(r)
1946 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1946 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1947 " rawsize totalsize compression heads chainlen\n"))
1947 " rawsize totalsize compression heads chainlen\n"))
1948 ts = 0
1948 ts = 0
1949 heads = set()
1949 heads = set()
1950
1950
1951 for rev in xrange(numrevs):
1951 for rev in xrange(numrevs):
1952 dbase = r.deltaparent(rev)
1952 dbase = r.deltaparent(rev)
1953 if dbase == -1:
1953 if dbase == -1:
1954 dbase = rev
1954 dbase = rev
1955 cbase = r.chainbase(rev)
1955 cbase = r.chainbase(rev)
1956 clen = r.chainlen(rev)
1956 clen = r.chainlen(rev)
1957 p1, p2 = r.parentrevs(rev)
1957 p1, p2 = r.parentrevs(rev)
1958 rs = r.rawsize(rev)
1958 rs = r.rawsize(rev)
1959 ts = ts + rs
1959 ts = ts + rs
1960 heads -= set(r.parentrevs(rev))
1960 heads -= set(r.parentrevs(rev))
1961 heads.add(rev)
1961 heads.add(rev)
1962 try:
1962 try:
1963 compression = ts / r.end(rev)
1963 compression = ts / r.end(rev)
1964 except ZeroDivisionError:
1964 except ZeroDivisionError:
1965 compression = 0
1965 compression = 0
1966 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1966 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1967 "%11d %5d %8d\n" %
1967 "%11d %5d %8d\n" %
1968 (rev, p1, p2, r.start(rev), r.end(rev),
1968 (rev, p1, p2, r.start(rev), r.end(rev),
1969 r.start(dbase), r.start(cbase),
1969 r.start(dbase), r.start(cbase),
1970 r.start(p1), r.start(p2),
1970 r.start(p1), r.start(p2),
1971 rs, ts, compression, len(heads), clen))
1971 rs, ts, compression, len(heads), clen))
1972 return 0
1972 return 0
1973
1973
1974 v = r.version
1974 v = r.version
1975 format = v & 0xFFFF
1975 format = v & 0xFFFF
1976 flags = []
1976 flags = []
1977 gdelta = False
1977 gdelta = False
1978 if v & revlog.FLAG_INLINE_DATA:
1978 if v & revlog.FLAG_INLINE_DATA:
1979 flags.append('inline')
1979 flags.append('inline')
1980 if v & revlog.FLAG_GENERALDELTA:
1980 if v & revlog.FLAG_GENERALDELTA:
1981 gdelta = True
1981 gdelta = True
1982 flags.append('generaldelta')
1982 flags.append('generaldelta')
1983 if not flags:
1983 if not flags:
1984 flags = ['(none)']
1984 flags = ['(none)']
1985
1985
1986 nummerges = 0
1986 nummerges = 0
1987 numfull = 0
1987 numfull = 0
1988 numprev = 0
1988 numprev = 0
1989 nump1 = 0
1989 nump1 = 0
1990 nump2 = 0
1990 nump2 = 0
1991 numother = 0
1991 numother = 0
1992 nump1prev = 0
1992 nump1prev = 0
1993 nump2prev = 0
1993 nump2prev = 0
1994 chainlengths = []
1994 chainlengths = []
1995 chainbases = []
1995 chainbases = []
1996 chainspans = []
1996 chainspans = []
1997
1997
1998 datasize = [None, 0, 0]
1998 datasize = [None, 0, 0]
1999 fullsize = [None, 0, 0]
1999 fullsize = [None, 0, 0]
2000 deltasize = [None, 0, 0]
2000 deltasize = [None, 0, 0]
2001 chunktypecounts = {}
2001 chunktypecounts = {}
2002 chunktypesizes = {}
2002 chunktypesizes = {}
2003
2003
2004 def addsize(size, l):
2004 def addsize(size, l):
2005 if l[0] is None or size < l[0]:
2005 if l[0] is None or size < l[0]:
2006 l[0] = size
2006 l[0] = size
2007 if size > l[1]:
2007 if size > l[1]:
2008 l[1] = size
2008 l[1] = size
2009 l[2] += size
2009 l[2] += size
2010
2010
2011 numrevs = len(r)
2011 numrevs = len(r)
2012 for rev in xrange(numrevs):
2012 for rev in xrange(numrevs):
2013 p1, p2 = r.parentrevs(rev)
2013 p1, p2 = r.parentrevs(rev)
2014 delta = r.deltaparent(rev)
2014 delta = r.deltaparent(rev)
2015 if format > 0:
2015 if format > 0:
2016 addsize(r.rawsize(rev), datasize)
2016 addsize(r.rawsize(rev), datasize)
2017 if p2 != nullrev:
2017 if p2 != nullrev:
2018 nummerges += 1
2018 nummerges += 1
2019 size = r.length(rev)
2019 size = r.length(rev)
2020 if delta == nullrev:
2020 if delta == nullrev:
2021 chainlengths.append(0)
2021 chainlengths.append(0)
2022 chainbases.append(r.start(rev))
2022 chainbases.append(r.start(rev))
2023 chainspans.append(size)
2023 chainspans.append(size)
2024 numfull += 1
2024 numfull += 1
2025 addsize(size, fullsize)
2025 addsize(size, fullsize)
2026 else:
2026 else:
2027 chainlengths.append(chainlengths[delta] + 1)
2027 chainlengths.append(chainlengths[delta] + 1)
2028 baseaddr = chainbases[delta]
2028 baseaddr = chainbases[delta]
2029 revaddr = r.start(rev)
2029 revaddr = r.start(rev)
2030 chainbases.append(baseaddr)
2030 chainbases.append(baseaddr)
2031 chainspans.append((revaddr - baseaddr) + size)
2031 chainspans.append((revaddr - baseaddr) + size)
2032 addsize(size, deltasize)
2032 addsize(size, deltasize)
2033 if delta == rev - 1:
2033 if delta == rev - 1:
2034 numprev += 1
2034 numprev += 1
2035 if delta == p1:
2035 if delta == p1:
2036 nump1prev += 1
2036 nump1prev += 1
2037 elif delta == p2:
2037 elif delta == p2:
2038 nump2prev += 1
2038 nump2prev += 1
2039 elif delta == p1:
2039 elif delta == p1:
2040 nump1 += 1
2040 nump1 += 1
2041 elif delta == p2:
2041 elif delta == p2:
2042 nump2 += 1
2042 nump2 += 1
2043 elif delta != nullrev:
2043 elif delta != nullrev:
2044 numother += 1
2044 numother += 1
2045
2045
2046 # Obtain data on the raw chunks in the revlog.
2046 # Obtain data on the raw chunks in the revlog.
2047 segment = r._getsegmentforrevs(rev, rev)[1]
2047 segment = r._getsegmentforrevs(rev, rev)[1]
2048 if segment:
2048 if segment:
2049 chunktype = bytes(segment[0:1])
2049 chunktype = bytes(segment[0:1])
2050 else:
2050 else:
2051 chunktype = 'empty'
2051 chunktype = 'empty'
2052
2052
2053 if chunktype not in chunktypecounts:
2053 if chunktype not in chunktypecounts:
2054 chunktypecounts[chunktype] = 0
2054 chunktypecounts[chunktype] = 0
2055 chunktypesizes[chunktype] = 0
2055 chunktypesizes[chunktype] = 0
2056
2056
2057 chunktypecounts[chunktype] += 1
2057 chunktypecounts[chunktype] += 1
2058 chunktypesizes[chunktype] += size
2058 chunktypesizes[chunktype] += size
2059
2059
2060 # Adjust size min value for empty cases
2060 # Adjust size min value for empty cases
2061 for size in (datasize, fullsize, deltasize):
2061 for size in (datasize, fullsize, deltasize):
2062 if size[0] is None:
2062 if size[0] is None:
2063 size[0] = 0
2063 size[0] = 0
2064
2064
2065 numdeltas = numrevs - numfull
2065 numdeltas = numrevs - numfull
2066 numoprev = numprev - nump1prev - nump2prev
2066 numoprev = numprev - nump1prev - nump2prev
2067 totalrawsize = datasize[2]
2067 totalrawsize = datasize[2]
2068 datasize[2] /= numrevs
2068 datasize[2] /= numrevs
2069 fulltotal = fullsize[2]
2069 fulltotal = fullsize[2]
2070 fullsize[2] /= numfull
2070 fullsize[2] /= numfull
2071 deltatotal = deltasize[2]
2071 deltatotal = deltasize[2]
2072 if numrevs - numfull > 0:
2072 if numrevs - numfull > 0:
2073 deltasize[2] /= numrevs - numfull
2073 deltasize[2] /= numrevs - numfull
2074 totalsize = fulltotal + deltatotal
2074 totalsize = fulltotal + deltatotal
2075 avgchainlen = sum(chainlengths) / numrevs
2075 avgchainlen = sum(chainlengths) / numrevs
2076 maxchainlen = max(chainlengths)
2076 maxchainlen = max(chainlengths)
2077 maxchainspan = max(chainspans)
2077 maxchainspan = max(chainspans)
2078 compratio = 1
2078 compratio = 1
2079 if totalsize:
2079 if totalsize:
2080 compratio = totalrawsize / totalsize
2080 compratio = totalrawsize / totalsize
2081
2081
2082 basedfmtstr = '%%%dd\n'
2082 basedfmtstr = '%%%dd\n'
2083 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2083 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2084
2084
2085 def dfmtstr(max):
2085 def dfmtstr(max):
2086 return basedfmtstr % len(str(max))
2086 return basedfmtstr % len(str(max))
2087 def pcfmtstr(max, padding=0):
2087 def pcfmtstr(max, padding=0):
2088 return basepcfmtstr % (len(str(max)), ' ' * padding)
2088 return basepcfmtstr % (len(str(max)), ' ' * padding)
2089
2089
2090 def pcfmt(value, total):
2090 def pcfmt(value, total):
2091 if total:
2091 if total:
2092 return (value, 100 * float(value) / total)
2092 return (value, 100 * float(value) / total)
2093 else:
2093 else:
2094 return value, 100.0
2094 return value, 100.0
2095
2095
2096 ui.write(('format : %d\n') % format)
2096 ui.write(('format : %d\n') % format)
2097 ui.write(('flags : %s\n') % ', '.join(flags))
2097 ui.write(('flags : %s\n') % ', '.join(flags))
2098
2098
2099 ui.write('\n')
2099 ui.write('\n')
2100 fmt = pcfmtstr(totalsize)
2100 fmt = pcfmtstr(totalsize)
2101 fmt2 = dfmtstr(totalsize)
2101 fmt2 = dfmtstr(totalsize)
2102 ui.write(('revisions : ') + fmt2 % numrevs)
2102 ui.write(('revisions : ') + fmt2 % numrevs)
2103 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2103 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2104 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2104 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2105 ui.write(('revisions : ') + fmt2 % numrevs)
2105 ui.write(('revisions : ') + fmt2 % numrevs)
2106 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2106 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2107 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2107 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2108 ui.write(('revision size : ') + fmt2 % totalsize)
2108 ui.write(('revision size : ') + fmt2 % totalsize)
2109 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2109 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2110 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2110 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2111
2111
2112 def fmtchunktype(chunktype):
2112 def fmtchunktype(chunktype):
2113 if chunktype == 'empty':
2113 if chunktype == 'empty':
2114 return ' %s : ' % chunktype
2114 return ' %s : ' % chunktype
2115 elif chunktype in pycompat.bytestr(string.ascii_letters):
2115 elif chunktype in pycompat.bytestr(string.ascii_letters):
2116 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2116 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2117 else:
2117 else:
2118 return ' 0x%s : ' % hex(chunktype)
2118 return ' 0x%s : ' % hex(chunktype)
2119
2119
2120 ui.write('\n')
2120 ui.write('\n')
2121 ui.write(('chunks : ') + fmt2 % numrevs)
2121 ui.write(('chunks : ') + fmt2 % numrevs)
2122 for chunktype in sorted(chunktypecounts):
2122 for chunktype in sorted(chunktypecounts):
2123 ui.write(fmtchunktype(chunktype))
2123 ui.write(fmtchunktype(chunktype))
2124 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2124 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2125 ui.write(('chunks size : ') + fmt2 % totalsize)
2125 ui.write(('chunks size : ') + fmt2 % totalsize)
2126 for chunktype in sorted(chunktypecounts):
2126 for chunktype in sorted(chunktypecounts):
2127 ui.write(fmtchunktype(chunktype))
2127 ui.write(fmtchunktype(chunktype))
2128 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2128 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2129
2129
2130 ui.write('\n')
2130 ui.write('\n')
2131 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2131 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2132 ui.write(('avg chain length : ') + fmt % avgchainlen)
2132 ui.write(('avg chain length : ') + fmt % avgchainlen)
2133 ui.write(('max chain length : ') + fmt % maxchainlen)
2133 ui.write(('max chain length : ') + fmt % maxchainlen)
2134 ui.write(('max chain reach : ') + fmt % maxchainspan)
2134 ui.write(('max chain reach : ') + fmt % maxchainspan)
2135 ui.write(('compression ratio : ') + fmt % compratio)
2135 ui.write(('compression ratio : ') + fmt % compratio)
2136
2136
2137 if format > 0:
2137 if format > 0:
2138 ui.write('\n')
2138 ui.write('\n')
2139 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2139 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2140 % tuple(datasize))
2140 % tuple(datasize))
2141 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2141 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2142 % tuple(fullsize))
2142 % tuple(fullsize))
2143 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2143 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2144 % tuple(deltasize))
2144 % tuple(deltasize))
2145
2145
2146 if numdeltas > 0:
2146 if numdeltas > 0:
2147 ui.write('\n')
2147 ui.write('\n')
2148 fmt = pcfmtstr(numdeltas)
2148 fmt = pcfmtstr(numdeltas)
2149 fmt2 = pcfmtstr(numdeltas, 4)
2149 fmt2 = pcfmtstr(numdeltas, 4)
2150 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2150 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2151 if numprev > 0:
2151 if numprev > 0:
2152 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2152 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2153 numprev))
2153 numprev))
2154 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2154 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2155 numprev))
2155 numprev))
2156 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2156 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2157 numprev))
2157 numprev))
2158 if gdelta:
2158 if gdelta:
2159 ui.write(('deltas against p1 : ')
2159 ui.write(('deltas against p1 : ')
2160 + fmt % pcfmt(nump1, numdeltas))
2160 + fmt % pcfmt(nump1, numdeltas))
2161 ui.write(('deltas against p2 : ')
2161 ui.write(('deltas against p2 : ')
2162 + fmt % pcfmt(nump2, numdeltas))
2162 + fmt % pcfmt(nump2, numdeltas))
2163 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2163 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2164 numdeltas))
2164 numdeltas))
2165
2165
2166 @command('debugrevspec',
2166 @command('debugrevspec',
2167 [('', 'optimize', None,
2167 [('', 'optimize', None,
2168 _('print parsed tree after optimizing (DEPRECATED)')),
2168 _('print parsed tree after optimizing (DEPRECATED)')),
2169 ('', 'show-revs', True, _('print list of result revisions (default)')),
2169 ('', 'show-revs', True, _('print list of result revisions (default)')),
2170 ('s', 'show-set', None, _('print internal representation of result set')),
2170 ('s', 'show-set', None, _('print internal representation of result set')),
2171 ('p', 'show-stage', [],
2171 ('p', 'show-stage', [],
2172 _('print parsed tree at the given stage'), _('NAME')),
2172 _('print parsed tree at the given stage'), _('NAME')),
2173 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2173 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2174 ('', 'verify-optimized', False, _('verify optimized result')),
2174 ('', 'verify-optimized', False, _('verify optimized result')),
2175 ],
2175 ],
2176 ('REVSPEC'))
2176 ('REVSPEC'))
2177 def debugrevspec(ui, repo, expr, **opts):
2177 def debugrevspec(ui, repo, expr, **opts):
2178 """parse and apply a revision specification
2178 """parse and apply a revision specification
2179
2179
2180 Use -p/--show-stage option to print the parsed tree at the given stages.
2180 Use -p/--show-stage option to print the parsed tree at the given stages.
2181 Use -p all to print tree at every stage.
2181 Use -p all to print tree at every stage.
2182
2182
2183 Use --no-show-revs option with -s or -p to print only the set
2183 Use --no-show-revs option with -s or -p to print only the set
2184 representation or the parsed tree respectively.
2184 representation or the parsed tree respectively.
2185
2185
2186 Use --verify-optimized to compare the optimized result with the unoptimized
2186 Use --verify-optimized to compare the optimized result with the unoptimized
2187 one. Returns 1 if the optimized result differs.
2187 one. Returns 1 if the optimized result differs.
2188 """
2188 """
2189 opts = pycompat.byteskwargs(opts)
2189 opts = pycompat.byteskwargs(opts)
2190 aliases = ui.configitems('revsetalias')
2190 aliases = ui.configitems('revsetalias')
2191 stages = [
2191 stages = [
2192 ('parsed', lambda tree: tree),
2192 ('parsed', lambda tree: tree),
2193 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2193 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2194 ui.warn)),
2194 ui.warn)),
2195 ('concatenated', revsetlang.foldconcat),
2195 ('concatenated', revsetlang.foldconcat),
2196 ('analyzed', revsetlang.analyze),
2196 ('analyzed', revsetlang.analyze),
2197 ('optimized', revsetlang.optimize),
2197 ('optimized', revsetlang.optimize),
2198 ]
2198 ]
2199 if opts['no_optimized']:
2199 if opts['no_optimized']:
2200 stages = stages[:-1]
2200 stages = stages[:-1]
2201 if opts['verify_optimized'] and opts['no_optimized']:
2201 if opts['verify_optimized'] and opts['no_optimized']:
2202 raise error.Abort(_('cannot use --verify-optimized with '
2202 raise error.Abort(_('cannot use --verify-optimized with '
2203 '--no-optimized'))
2203 '--no-optimized'))
2204 stagenames = set(n for n, f in stages)
2204 stagenames = set(n for n, f in stages)
2205
2205
2206 showalways = set()
2206 showalways = set()
2207 showchanged = set()
2207 showchanged = set()
2208 if ui.verbose and not opts['show_stage']:
2208 if ui.verbose and not opts['show_stage']:
2209 # show parsed tree by --verbose (deprecated)
2209 # show parsed tree by --verbose (deprecated)
2210 showalways.add('parsed')
2210 showalways.add('parsed')
2211 showchanged.update(['expanded', 'concatenated'])
2211 showchanged.update(['expanded', 'concatenated'])
2212 if opts['optimize']:
2212 if opts['optimize']:
2213 showalways.add('optimized')
2213 showalways.add('optimized')
2214 if opts['show_stage'] and opts['optimize']:
2214 if opts['show_stage'] and opts['optimize']:
2215 raise error.Abort(_('cannot use --optimize with --show-stage'))
2215 raise error.Abort(_('cannot use --optimize with --show-stage'))
2216 if opts['show_stage'] == ['all']:
2216 if opts['show_stage'] == ['all']:
2217 showalways.update(stagenames)
2217 showalways.update(stagenames)
2218 else:
2218 else:
2219 for n in opts['show_stage']:
2219 for n in opts['show_stage']:
2220 if n not in stagenames:
2220 if n not in stagenames:
2221 raise error.Abort(_('invalid stage name: %s') % n)
2221 raise error.Abort(_('invalid stage name: %s') % n)
2222 showalways.update(opts['show_stage'])
2222 showalways.update(opts['show_stage'])
2223
2223
2224 treebystage = {}
2224 treebystage = {}
2225 printedtree = None
2225 printedtree = None
2226 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2226 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2227 for n, f in stages:
2227 for n, f in stages:
2228 treebystage[n] = tree = f(tree)
2228 treebystage[n] = tree = f(tree)
2229 if n in showalways or (n in showchanged and tree != printedtree):
2229 if n in showalways or (n in showchanged and tree != printedtree):
2230 if opts['show_stage'] or n != 'parsed':
2230 if opts['show_stage'] or n != 'parsed':
2231 ui.write(("* %s:\n") % n)
2231 ui.write(("* %s:\n") % n)
2232 ui.write(revsetlang.prettyformat(tree), "\n")
2232 ui.write(revsetlang.prettyformat(tree), "\n")
2233 printedtree = tree
2233 printedtree = tree
2234
2234
2235 if opts['verify_optimized']:
2235 if opts['verify_optimized']:
2236 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2236 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2237 brevs = revset.makematcher(treebystage['optimized'])(repo)
2237 brevs = revset.makematcher(treebystage['optimized'])(repo)
2238 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2238 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2239 ui.write(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
2239 ui.write(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
2240 ui.write(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
2240 ui.write(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
2241 arevs = list(arevs)
2241 arevs = list(arevs)
2242 brevs = list(brevs)
2242 brevs = list(brevs)
2243 if arevs == brevs:
2243 if arevs == brevs:
2244 return 0
2244 return 0
2245 ui.write(('--- analyzed\n'), label='diff.file_a')
2245 ui.write(('--- analyzed\n'), label='diff.file_a')
2246 ui.write(('+++ optimized\n'), label='diff.file_b')
2246 ui.write(('+++ optimized\n'), label='diff.file_b')
2247 sm = difflib.SequenceMatcher(None, arevs, brevs)
2247 sm = difflib.SequenceMatcher(None, arevs, brevs)
2248 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2248 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2249 if tag in ('delete', 'replace'):
2249 if tag in ('delete', 'replace'):
2250 for c in arevs[alo:ahi]:
2250 for c in arevs[alo:ahi]:
2251 ui.write('-%s\n' % c, label='diff.deleted')
2251 ui.write('-%s\n' % c, label='diff.deleted')
2252 if tag in ('insert', 'replace'):
2252 if tag in ('insert', 'replace'):
2253 for c in brevs[blo:bhi]:
2253 for c in brevs[blo:bhi]:
2254 ui.write('+%s\n' % c, label='diff.inserted')
2254 ui.write('+%s\n' % c, label='diff.inserted')
2255 if tag == 'equal':
2255 if tag == 'equal':
2256 for c in arevs[alo:ahi]:
2256 for c in arevs[alo:ahi]:
2257 ui.write(' %s\n' % c)
2257 ui.write(' %s\n' % c)
2258 return 1
2258 return 1
2259
2259
2260 func = revset.makematcher(tree)
2260 func = revset.makematcher(tree)
2261 revs = func(repo)
2261 revs = func(repo)
2262 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2262 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2263 ui.write(("* set:\n"), smartset.prettyformat(revs), "\n")
2263 ui.write(("* set:\n"), smartset.prettyformat(revs), "\n")
2264 if not opts['show_revs']:
2264 if not opts['show_revs']:
2265 return
2265 return
2266 for c in revs:
2266 for c in revs:
2267 ui.write("%d\n" % c)
2267 ui.write("%d\n" % c)
2268
2268
2269 @command('debugserve', [
2269 @command('debugserve', [
2270 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2270 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2271 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2271 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2272 ('', 'logiofile', '', _('file to log server I/O to')),
2272 ('', 'logiofile', '', _('file to log server I/O to')),
2273 ], '')
2273 ], '')
2274 def debugserve(ui, repo, **opts):
2274 def debugserve(ui, repo, **opts):
2275 """run a server with advanced settings
2275 """run a server with advanced settings
2276
2276
2277 This command is similar to :hg:`serve`. It exists partially as a
2277 This command is similar to :hg:`serve`. It exists partially as a
2278 workaround to the fact that ``hg serve --stdio`` must have specific
2278 workaround to the fact that ``hg serve --stdio`` must have specific
2279 arguments for security reasons.
2279 arguments for security reasons.
2280 """
2280 """
2281 opts = pycompat.byteskwargs(opts)
2281 opts = pycompat.byteskwargs(opts)
2282
2282
2283 if not opts['sshstdio']:
2283 if not opts['sshstdio']:
2284 raise error.Abort(_('only --sshstdio is currently supported'))
2284 raise error.Abort(_('only --sshstdio is currently supported'))
2285
2285
2286 logfh = None
2286 logfh = None
2287
2287
2288 if opts['logiofd'] and opts['logiofile']:
2288 if opts['logiofd'] and opts['logiofile']:
2289 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2289 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2290
2290
2291 if opts['logiofd']:
2291 if opts['logiofd']:
2292 # Line buffered because output is line based.
2292 # Line buffered because output is line based.
2293 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2293 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2294 elif opts['logiofile']:
2294 elif opts['logiofile']:
2295 logfh = open(opts['logiofile'], 'ab', 1)
2295 logfh = open(opts['logiofile'], 'ab', 1)
2296
2296
2297 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2297 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2298 s.serve_forever()
2298 s.serve_forever()
2299
2299
2300 @command('debugsetparents', [], _('REV1 [REV2]'))
2300 @command('debugsetparents', [], _('REV1 [REV2]'))
2301 def debugsetparents(ui, repo, rev1, rev2=None):
2301 def debugsetparents(ui, repo, rev1, rev2=None):
2302 """manually set the parents of the current working directory
2302 """manually set the parents of the current working directory
2303
2303
2304 This is useful for writing repository conversion tools, but should
2304 This is useful for writing repository conversion tools, but should
2305 be used with care. For example, neither the working directory nor the
2305 be used with care. For example, neither the working directory nor the
2306 dirstate is updated, so file status may be incorrect after running this
2306 dirstate is updated, so file status may be incorrect after running this
2307 command.
2307 command.
2308
2308
2309 Returns 0 on success.
2309 Returns 0 on success.
2310 """
2310 """
2311
2311
2312 node1 = scmutil.revsingle(repo, rev1).node()
2312 node1 = scmutil.revsingle(repo, rev1).node()
2313 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2313 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2314
2314
2315 with repo.wlock():
2315 with repo.wlock():
2316 repo.setparents(node1, node2)
2316 repo.setparents(node1, node2)
2317
2317
2318 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2318 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2319 def debugssl(ui, repo, source=None, **opts):
2319 def debugssl(ui, repo, source=None, **opts):
2320 '''test a secure connection to a server
2320 '''test a secure connection to a server
2321
2321
2322 This builds the certificate chain for the server on Windows, installing the
2322 This builds the certificate chain for the server on Windows, installing the
2323 missing intermediates and trusted root via Windows Update if necessary. It
2323 missing intermediates and trusted root via Windows Update if necessary. It
2324 does nothing on other platforms.
2324 does nothing on other platforms.
2325
2325
2326 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2326 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2327 that server is used. See :hg:`help urls` for more information.
2327 that server is used. See :hg:`help urls` for more information.
2328
2328
2329 If the update succeeds, retry the original operation. Otherwise, the cause
2329 If the update succeeds, retry the original operation. Otherwise, the cause
2330 of the SSL error is likely another issue.
2330 of the SSL error is likely another issue.
2331 '''
2331 '''
2332 if not pycompat.iswindows:
2332 if not pycompat.iswindows:
2333 raise error.Abort(_('certificate chain building is only possible on '
2333 raise error.Abort(_('certificate chain building is only possible on '
2334 'Windows'))
2334 'Windows'))
2335
2335
2336 if not source:
2336 if not source:
2337 if not repo:
2337 if not repo:
2338 raise error.Abort(_("there is no Mercurial repository here, and no "
2338 raise error.Abort(_("there is no Mercurial repository here, and no "
2339 "server specified"))
2339 "server specified"))
2340 source = "default"
2340 source = "default"
2341
2341
2342 source, branches = hg.parseurl(ui.expandpath(source))
2342 source, branches = hg.parseurl(ui.expandpath(source))
2343 url = util.url(source)
2343 url = util.url(source)
2344 addr = None
2344 addr = None
2345
2345
2346 defaultport = {'https': 443, 'ssh': 22}
2346 defaultport = {'https': 443, 'ssh': 22}
2347 if url.scheme in defaultport:
2347 if url.scheme in defaultport:
2348 try:
2348 try:
2349 addr = (url.host, int(url.port or defaultport[url.scheme]))
2349 addr = (url.host, int(url.port or defaultport[url.scheme]))
2350 except ValueError:
2350 except ValueError:
2351 raise error.Abort(_("malformed port number in URL"))
2351 raise error.Abort(_("malformed port number in URL"))
2352 else:
2352 else:
2353 raise error.Abort(_("only https and ssh connections are supported"))
2353 raise error.Abort(_("only https and ssh connections are supported"))
2354
2354
2355 from . import win32
2355 from . import win32
2356
2356
2357 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2357 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2358 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2358 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2359
2359
2360 try:
2360 try:
2361 s.connect(addr)
2361 s.connect(addr)
2362 cert = s.getpeercert(True)
2362 cert = s.getpeercert(True)
2363
2363
2364 ui.status(_('checking the certificate chain for %s\n') % url.host)
2364 ui.status(_('checking the certificate chain for %s\n') % url.host)
2365
2365
2366 complete = win32.checkcertificatechain(cert, build=False)
2366 complete = win32.checkcertificatechain(cert, build=False)
2367
2367
2368 if not complete:
2368 if not complete:
2369 ui.status(_('certificate chain is incomplete, updating... '))
2369 ui.status(_('certificate chain is incomplete, updating... '))
2370
2370
2371 if not win32.checkcertificatechain(cert):
2371 if not win32.checkcertificatechain(cert):
2372 ui.status(_('failed.\n'))
2372 ui.status(_('failed.\n'))
2373 else:
2373 else:
2374 ui.status(_('done.\n'))
2374 ui.status(_('done.\n'))
2375 else:
2375 else:
2376 ui.status(_('full certificate chain is available\n'))
2376 ui.status(_('full certificate chain is available\n'))
2377 finally:
2377 finally:
2378 s.close()
2378 s.close()
2379
2379
2380 @command('debugsub',
2380 @command('debugsub',
2381 [('r', 'rev', '',
2381 [('r', 'rev', '',
2382 _('revision to check'), _('REV'))],
2382 _('revision to check'), _('REV'))],
2383 _('[-r REV] [REV]'))
2383 _('[-r REV] [REV]'))
2384 def debugsub(ui, repo, rev=None):
2384 def debugsub(ui, repo, rev=None):
2385 ctx = scmutil.revsingle(repo, rev, None)
2385 ctx = scmutil.revsingle(repo, rev, None)
2386 for k, v in sorted(ctx.substate.items()):
2386 for k, v in sorted(ctx.substate.items()):
2387 ui.write(('path %s\n') % k)
2387 ui.write(('path %s\n') % k)
2388 ui.write((' source %s\n') % v[0])
2388 ui.write((' source %s\n') % v[0])
2389 ui.write((' revision %s\n') % v[1])
2389 ui.write((' revision %s\n') % v[1])
2390
2390
2391 @command('debugsuccessorssets',
2391 @command('debugsuccessorssets',
2392 [('', 'closest', False, _('return closest successors sets only'))],
2392 [('', 'closest', False, _('return closest successors sets only'))],
2393 _('[REV]'))
2393 _('[REV]'))
2394 def debugsuccessorssets(ui, repo, *revs, **opts):
2394 def debugsuccessorssets(ui, repo, *revs, **opts):
2395 """show set of successors for revision
2395 """show set of successors for revision
2396
2396
2397 A successors set of changeset A is a consistent group of revisions that
2397 A successors set of changeset A is a consistent group of revisions that
2398 succeed A. It contains non-obsolete changesets only unless closests
2398 succeed A. It contains non-obsolete changesets only unless closests
2399 successors set is set.
2399 successors set is set.
2400
2400
2401 In most cases a changeset A has a single successors set containing a single
2401 In most cases a changeset A has a single successors set containing a single
2402 successor (changeset A replaced by A').
2402 successor (changeset A replaced by A').
2403
2403
2404 A changeset that is made obsolete with no successors are called "pruned".
2404 A changeset that is made obsolete with no successors are called "pruned".
2405 Such changesets have no successors sets at all.
2405 Such changesets have no successors sets at all.
2406
2406
2407 A changeset that has been "split" will have a successors set containing
2407 A changeset that has been "split" will have a successors set containing
2408 more than one successor.
2408 more than one successor.
2409
2409
2410 A changeset that has been rewritten in multiple different ways is called
2410 A changeset that has been rewritten in multiple different ways is called
2411 "divergent". Such changesets have multiple successor sets (each of which
2411 "divergent". Such changesets have multiple successor sets (each of which
2412 may also be split, i.e. have multiple successors).
2412 may also be split, i.e. have multiple successors).
2413
2413
2414 Results are displayed as follows::
2414 Results are displayed as follows::
2415
2415
2416 <rev1>
2416 <rev1>
2417 <successors-1A>
2417 <successors-1A>
2418 <rev2>
2418 <rev2>
2419 <successors-2A>
2419 <successors-2A>
2420 <successors-2B1> <successors-2B2> <successors-2B3>
2420 <successors-2B1> <successors-2B2> <successors-2B3>
2421
2421
2422 Here rev2 has two possible (i.e. divergent) successors sets. The first
2422 Here rev2 has two possible (i.e. divergent) successors sets. The first
2423 holds one element, whereas the second holds three (i.e. the changeset has
2423 holds one element, whereas the second holds three (i.e. the changeset has
2424 been split).
2424 been split).
2425 """
2425 """
2426 # passed to successorssets caching computation from one call to another
2426 # passed to successorssets caching computation from one call to another
2427 cache = {}
2427 cache = {}
2428 ctx2str = bytes
2428 ctx2str = bytes
2429 node2str = short
2429 node2str = short
2430 for rev in scmutil.revrange(repo, revs):
2430 for rev in scmutil.revrange(repo, revs):
2431 ctx = repo[rev]
2431 ctx = repo[rev]
2432 ui.write('%s\n'% ctx2str(ctx))
2432 ui.write('%s\n'% ctx2str(ctx))
2433 for succsset in obsutil.successorssets(repo, ctx.node(),
2433 for succsset in obsutil.successorssets(repo, ctx.node(),
2434 closest=opts[r'closest'],
2434 closest=opts[r'closest'],
2435 cache=cache):
2435 cache=cache):
2436 if succsset:
2436 if succsset:
2437 ui.write(' ')
2437 ui.write(' ')
2438 ui.write(node2str(succsset[0]))
2438 ui.write(node2str(succsset[0]))
2439 for node in succsset[1:]:
2439 for node in succsset[1:]:
2440 ui.write(' ')
2440 ui.write(' ')
2441 ui.write(node2str(node))
2441 ui.write(node2str(node))
2442 ui.write('\n')
2442 ui.write('\n')
2443
2443
2444 @command('debugtemplate',
2444 @command('debugtemplate',
2445 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2445 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2446 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2446 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2447 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2447 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2448 optionalrepo=True)
2448 optionalrepo=True)
2449 def debugtemplate(ui, repo, tmpl, **opts):
2449 def debugtemplate(ui, repo, tmpl, **opts):
2450 """parse and apply a template
2450 """parse and apply a template
2451
2451
2452 If -r/--rev is given, the template is processed as a log template and
2452 If -r/--rev is given, the template is processed as a log template and
2453 applied to the given changesets. Otherwise, it is processed as a generic
2453 applied to the given changesets. Otherwise, it is processed as a generic
2454 template.
2454 template.
2455
2455
2456 Use --verbose to print the parsed tree.
2456 Use --verbose to print the parsed tree.
2457 """
2457 """
2458 revs = None
2458 revs = None
2459 if opts[r'rev']:
2459 if opts[r'rev']:
2460 if repo is None:
2460 if repo is None:
2461 raise error.RepoError(_('there is no Mercurial repository here '
2461 raise error.RepoError(_('there is no Mercurial repository here '
2462 '(.hg not found)'))
2462 '(.hg not found)'))
2463 revs = scmutil.revrange(repo, opts[r'rev'])
2463 revs = scmutil.revrange(repo, opts[r'rev'])
2464
2464
2465 props = {}
2465 props = {}
2466 for d in opts[r'define']:
2466 for d in opts[r'define']:
2467 try:
2467 try:
2468 k, v = (e.strip() for e in d.split('=', 1))
2468 k, v = (e.strip() for e in d.split('=', 1))
2469 if not k or k == 'ui':
2469 if not k or k == 'ui':
2470 raise ValueError
2470 raise ValueError
2471 props[k] = v
2471 props[k] = v
2472 except ValueError:
2472 except ValueError:
2473 raise error.Abort(_('malformed keyword definition: %s') % d)
2473 raise error.Abort(_('malformed keyword definition: %s') % d)
2474
2474
2475 if ui.verbose:
2475 if ui.verbose:
2476 aliases = ui.configitems('templatealias')
2476 aliases = ui.configitems('templatealias')
2477 tree = templater.parse(tmpl)
2477 tree = templater.parse(tmpl)
2478 ui.note(templater.prettyformat(tree), '\n')
2478 ui.note(templater.prettyformat(tree), '\n')
2479 newtree = templater.expandaliases(tree, aliases)
2479 newtree = templater.expandaliases(tree, aliases)
2480 if newtree != tree:
2480 if newtree != tree:
2481 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2481 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2482
2482
2483 if revs is None:
2483 if revs is None:
2484 tres = formatter.templateresources(ui, repo)
2484 tres = formatter.templateresources(ui, repo)
2485 t = formatter.maketemplater(ui, tmpl, resources=tres)
2485 t = formatter.maketemplater(ui, tmpl, resources=tres)
2486 ui.write(t.renderdefault(props))
2486 ui.write(t.renderdefault(props))
2487 else:
2487 else:
2488 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2488 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2489 for r in revs:
2489 for r in revs:
2490 displayer.show(repo[r], **pycompat.strkwargs(props))
2490 displayer.show(repo[r], **pycompat.strkwargs(props))
2491 displayer.close()
2491 displayer.close()
2492
2492
2493 @command('debuguigetpass', [
2493 @command('debuguigetpass', [
2494 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2494 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2495 ], _('[-p TEXT]'), norepo=True)
2495 ], _('[-p TEXT]'), norepo=True)
2496 def debuguigetpass(ui, prompt=''):
2496 def debuguigetpass(ui, prompt=''):
2497 """show prompt to type password"""
2497 """show prompt to type password"""
2498 r = ui.getpass(prompt)
2498 r = ui.getpass(prompt)
2499 ui.write(('respose: %s\n') % r)
2499 ui.write(('respose: %s\n') % r)
2500
2500
2501 @command('debuguiprompt', [
2501 @command('debuguiprompt', [
2502 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2502 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2503 ], _('[-p TEXT]'), norepo=True)
2503 ], _('[-p TEXT]'), norepo=True)
2504 def debuguiprompt(ui, prompt=''):
2504 def debuguiprompt(ui, prompt=''):
2505 """show plain prompt"""
2505 """show plain prompt"""
2506 r = ui.prompt(prompt)
2506 r = ui.prompt(prompt)
2507 ui.write(('response: %s\n') % r)
2507 ui.write(('response: %s\n') % r)
2508
2508
2509 @command('debugupdatecaches', [])
2509 @command('debugupdatecaches', [])
2510 def debugupdatecaches(ui, repo, *pats, **opts):
2510 def debugupdatecaches(ui, repo, *pats, **opts):
2511 """warm all known caches in the repository"""
2511 """warm all known caches in the repository"""
2512 with repo.wlock(), repo.lock():
2512 with repo.wlock(), repo.lock():
2513 repo.updatecaches(full=True)
2513 repo.updatecaches(full=True)
2514
2514
2515 @command('debugupgraderepo', [
2515 @command('debugupgraderepo', [
2516 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2516 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2517 ('', 'run', False, _('performs an upgrade')),
2517 ('', 'run', False, _('performs an upgrade')),
2518 ])
2518 ])
2519 def debugupgraderepo(ui, repo, run=False, optimize=None):
2519 def debugupgraderepo(ui, repo, run=False, optimize=None):
2520 """upgrade a repository to use different features
2520 """upgrade a repository to use different features
2521
2521
2522 If no arguments are specified, the repository is evaluated for upgrade
2522 If no arguments are specified, the repository is evaluated for upgrade
2523 and a list of problems and potential optimizations is printed.
2523 and a list of problems and potential optimizations is printed.
2524
2524
2525 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2525 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2526 can be influenced via additional arguments. More details will be provided
2526 can be influenced via additional arguments. More details will be provided
2527 by the command output when run without ``--run``.
2527 by the command output when run without ``--run``.
2528
2528
2529 During the upgrade, the repository will be locked and no writes will be
2529 During the upgrade, the repository will be locked and no writes will be
2530 allowed.
2530 allowed.
2531
2531
2532 At the end of the upgrade, the repository may not be readable while new
2532 At the end of the upgrade, the repository may not be readable while new
2533 repository data is swapped in. This window will be as long as it takes to
2533 repository data is swapped in. This window will be as long as it takes to
2534 rename some directories inside the ``.hg`` directory. On most machines, this
2534 rename some directories inside the ``.hg`` directory. On most machines, this
2535 should complete almost instantaneously and the chances of a consumer being
2535 should complete almost instantaneously and the chances of a consumer being
2536 unable to access the repository should be low.
2536 unable to access the repository should be low.
2537 """
2537 """
2538 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2538 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2539
2539
2540 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2540 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2541 inferrepo=True)
2541 inferrepo=True)
2542 def debugwalk(ui, repo, *pats, **opts):
2542 def debugwalk(ui, repo, *pats, **opts):
2543 """show how files match on given patterns"""
2543 """show how files match on given patterns"""
2544 opts = pycompat.byteskwargs(opts)
2544 opts = pycompat.byteskwargs(opts)
2545 m = scmutil.match(repo[None], pats, opts)
2545 m = scmutil.match(repo[None], pats, opts)
2546 ui.write(('matcher: %r\n' % m))
2546 ui.write(('matcher: %r\n' % m))
2547 items = list(repo[None].walk(m))
2547 items = list(repo[None].walk(m))
2548 if not items:
2548 if not items:
2549 return
2549 return
2550 f = lambda fn: fn
2550 f = lambda fn: fn
2551 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2551 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2552 f = lambda fn: util.normpath(fn)
2552 f = lambda fn: util.normpath(fn)
2553 fmt = 'f %%-%ds %%-%ds %%s' % (
2553 fmt = 'f %%-%ds %%-%ds %%s' % (
2554 max([len(abs) for abs in items]),
2554 max([len(abs) for abs in items]),
2555 max([len(m.rel(abs)) for abs in items]))
2555 max([len(m.rel(abs)) for abs in items]))
2556 for abs in items:
2556 for abs in items:
2557 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2557 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2558 ui.write("%s\n" % line.rstrip())
2558 ui.write("%s\n" % line.rstrip())
2559
2559
2560 @command('debugwhyunstable', [], _('REV'))
2560 @command('debugwhyunstable', [], _('REV'))
2561 def debugwhyunstable(ui, repo, rev):
2561 def debugwhyunstable(ui, repo, rev):
2562 """explain instabilities of a changeset"""
2562 """explain instabilities of a changeset"""
2563 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2563 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2564 dnodes = ''
2564 dnodes = ''
2565 if entry.get('divergentnodes'):
2565 if entry.get('divergentnodes'):
2566 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2566 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2567 for ctx in entry['divergentnodes']) + ' '
2567 for ctx in entry['divergentnodes']) + ' '
2568 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2568 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2569 entry['reason'], entry['node']))
2569 entry['reason'], entry['node']))
2570
2570
2571 @command('debugwireargs',
2571 @command('debugwireargs',
2572 [('', 'three', '', 'three'),
2572 [('', 'three', '', 'three'),
2573 ('', 'four', '', 'four'),
2573 ('', 'four', '', 'four'),
2574 ('', 'five', '', 'five'),
2574 ('', 'five', '', 'five'),
2575 ] + cmdutil.remoteopts,
2575 ] + cmdutil.remoteopts,
2576 _('REPO [OPTIONS]... [ONE [TWO]]'),
2576 _('REPO [OPTIONS]... [ONE [TWO]]'),
2577 norepo=True)
2577 norepo=True)
2578 def debugwireargs(ui, repopath, *vals, **opts):
2578 def debugwireargs(ui, repopath, *vals, **opts):
2579 opts = pycompat.byteskwargs(opts)
2579 opts = pycompat.byteskwargs(opts)
2580 repo = hg.peer(ui, opts, repopath)
2580 repo = hg.peer(ui, opts, repopath)
2581 for opt in cmdutil.remoteopts:
2581 for opt in cmdutil.remoteopts:
2582 del opts[opt[1]]
2582 del opts[opt[1]]
2583 args = {}
2583 args = {}
2584 for k, v in opts.iteritems():
2584 for k, v in opts.iteritems():
2585 if v:
2585 if v:
2586 args[k] = v
2586 args[k] = v
2587 args = pycompat.strkwargs(args)
2587 args = pycompat.strkwargs(args)
2588 # run twice to check that we don't mess up the stream for the next command
2588 # run twice to check that we don't mess up the stream for the next command
2589 res1 = repo.debugwireargs(*vals, **args)
2589 res1 = repo.debugwireargs(*vals, **args)
2590 res2 = repo.debugwireargs(*vals, **args)
2590 res2 = repo.debugwireargs(*vals, **args)
2591 ui.write("%s\n" % res1)
2591 ui.write("%s\n" % res1)
2592 if res1 != res2:
2592 if res1 != res2:
2593 ui.warn("%s\n" % res2)
2593 ui.warn("%s\n" % res2)
2594
2594
2595 def _parsewirelangblocks(fh):
2595 def _parsewirelangblocks(fh):
2596 activeaction = None
2596 activeaction = None
2597 blocklines = []
2597 blocklines = []
2598
2598
2599 for line in fh:
2599 for line in fh:
2600 line = line.rstrip()
2600 line = line.rstrip()
2601 if not line:
2601 if not line:
2602 continue
2602 continue
2603
2603
2604 if line.startswith(b'#'):
2604 if line.startswith(b'#'):
2605 continue
2605 continue
2606
2606
2607 if not line.startswith(' '):
2607 if not line.startswith(' '):
2608 # New block. Flush previous one.
2608 # New block. Flush previous one.
2609 if activeaction:
2609 if activeaction:
2610 yield activeaction, blocklines
2610 yield activeaction, blocklines
2611
2611
2612 activeaction = line
2612 activeaction = line
2613 blocklines = []
2613 blocklines = []
2614 continue
2614 continue
2615
2615
2616 # Else we start with an indent.
2616 # Else we start with an indent.
2617
2617
2618 if not activeaction:
2618 if not activeaction:
2619 raise error.Abort(_('indented line outside of block'))
2619 raise error.Abort(_('indented line outside of block'))
2620
2620
2621 blocklines.append(line)
2621 blocklines.append(line)
2622
2622
2623 # Flush last block.
2623 # Flush last block.
2624 if activeaction:
2624 if activeaction:
2625 yield activeaction, blocklines
2625 yield activeaction, blocklines
2626
2626
2627 @command('debugwireproto',
2627 @command('debugwireproto',
2628 [
2628 [
2629 ('', 'localssh', False, _('start an SSH server for this repo')),
2629 ('', 'localssh', False, _('start an SSH server for this repo')),
2630 ('', 'peer', '', _('construct a specific version of the peer')),
2630 ('', 'peer', '', _('construct a specific version of the peer')),
2631 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2631 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2632 ('', 'nologhandshake', False,
2632 ('', 'nologhandshake', False,
2633 _('do not log I/O related to the peer handshake')),
2633 _('do not log I/O related to the peer handshake')),
2634 ] + cmdutil.remoteopts,
2634 ] + cmdutil.remoteopts,
2635 _('[PATH]'),
2635 _('[PATH]'),
2636 optionalrepo=True)
2636 optionalrepo=True)
2637 def debugwireproto(ui, repo, path=None, **opts):
2637 def debugwireproto(ui, repo, path=None, **opts):
2638 """send wire protocol commands to a server
2638 """send wire protocol commands to a server
2639
2639
2640 This command can be used to issue wire protocol commands to remote
2640 This command can be used to issue wire protocol commands to remote
2641 peers and to debug the raw data being exchanged.
2641 peers and to debug the raw data being exchanged.
2642
2642
2643 ``--localssh`` will start an SSH server against the current repository
2643 ``--localssh`` will start an SSH server against the current repository
2644 and connect to that. By default, the connection will perform a handshake
2644 and connect to that. By default, the connection will perform a handshake
2645 and establish an appropriate peer instance.
2645 and establish an appropriate peer instance.
2646
2646
2647 ``--peer`` can be used to bypass the handshake protocol and construct a
2647 ``--peer`` can be used to bypass the handshake protocol and construct a
2648 peer instance using the specified class type. Valid values are ``raw``,
2648 peer instance using the specified class type. Valid values are ``raw``,
2649 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2649 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2650 raw data payloads and don't support higher-level command actions.
2650 raw data payloads and don't support higher-level command actions.
2651
2651
2652 ``--noreadstderr`` can be used to disable automatic reading from stderr
2652 ``--noreadstderr`` can be used to disable automatic reading from stderr
2653 of the peer (for SSH connections only). Disabling automatic reading of
2653 of the peer (for SSH connections only). Disabling automatic reading of
2654 stderr is useful for making output more deterministic.
2654 stderr is useful for making output more deterministic.
2655
2655
2656 Commands are issued via a mini language which is specified via stdin.
2656 Commands are issued via a mini language which is specified via stdin.
2657 The language consists of individual actions to perform. An action is
2657 The language consists of individual actions to perform. An action is
2658 defined by a block. A block is defined as a line with no leading
2658 defined by a block. A block is defined as a line with no leading
2659 space followed by 0 or more lines with leading space. Blocks are
2659 space followed by 0 or more lines with leading space. Blocks are
2660 effectively a high-level command with additional metadata.
2660 effectively a high-level command with additional metadata.
2661
2661
2662 Lines beginning with ``#`` are ignored.
2662 Lines beginning with ``#`` are ignored.
2663
2663
2664 The following sections denote available actions.
2664 The following sections denote available actions.
2665
2665
2666 raw
2666 raw
2667 ---
2667 ---
2668
2668
2669 Send raw data to the server.
2669 Send raw data to the server.
2670
2670
2671 The block payload contains the raw data to send as one atomic send
2671 The block payload contains the raw data to send as one atomic send
2672 operation. The data may not actually be delivered in a single system
2672 operation. The data may not actually be delivered in a single system
2673 call: it depends on the abilities of the transport being used.
2673 call: it depends on the abilities of the transport being used.
2674
2674
2675 Each line in the block is de-indented and concatenated. Then, that
2675 Each line in the block is de-indented and concatenated. Then, that
2676 value is evaluated as a Python b'' literal. This allows the use of
2676 value is evaluated as a Python b'' literal. This allows the use of
2677 backslash escaping, etc.
2677 backslash escaping, etc.
2678
2678
2679 raw+
2679 raw+
2680 ----
2680 ----
2681
2681
2682 Behaves like ``raw`` except flushes output afterwards.
2682 Behaves like ``raw`` except flushes output afterwards.
2683
2683
2684 command <X>
2684 command <X>
2685 -----------
2685 -----------
2686
2686
2687 Send a request to run a named command, whose name follows the ``command``
2687 Send a request to run a named command, whose name follows the ``command``
2688 string.
2688 string.
2689
2689
2690 Arguments to the command are defined as lines in this block. The format of
2690 Arguments to the command are defined as lines in this block. The format of
2691 each line is ``<key> <value>``. e.g.::
2691 each line is ``<key> <value>``. e.g.::
2692
2692
2693 command listkeys
2693 command listkeys
2694 namespace bookmarks
2694 namespace bookmarks
2695
2695
2696 If the value begins with ``eval:``, it will be interpreted as a Python
2696 If the value begins with ``eval:``, it will be interpreted as a Python
2697 literal expression. Otherwise values are interpreted as Python b'' literals.
2697 literal expression. Otherwise values are interpreted as Python b'' literals.
2698 This allows sending complex types and encoding special byte sequences via
2698 This allows sending complex types and encoding special byte sequences via
2699 backslash escaping.
2699 backslash escaping.
2700
2700
2701 The following arguments have special meaning:
2701 The following arguments have special meaning:
2702
2702
2703 ``PUSHFILE``
2703 ``PUSHFILE``
2704 When defined, the *push* mechanism of the peer will be used instead
2704 When defined, the *push* mechanism of the peer will be used instead
2705 of the static request-response mechanism and the content of the
2705 of the static request-response mechanism and the content of the
2706 file specified in the value of this argument will be sent as the
2706 file specified in the value of this argument will be sent as the
2707 command payload.
2707 command payload.
2708
2708
2709 This can be used to submit a local bundle file to the remote.
2709 This can be used to submit a local bundle file to the remote.
2710
2710
2711 batchbegin
2711 batchbegin
2712 ----------
2712 ----------
2713
2713
2714 Instruct the peer to begin a batched send.
2714 Instruct the peer to begin a batched send.
2715
2715
2716 All ``command`` blocks are queued for execution until the next
2716 All ``command`` blocks are queued for execution until the next
2717 ``batchsubmit`` block.
2717 ``batchsubmit`` block.
2718
2718
2719 batchsubmit
2719 batchsubmit
2720 -----------
2720 -----------
2721
2721
2722 Submit previously queued ``command`` blocks as a batch request.
2722 Submit previously queued ``command`` blocks as a batch request.
2723
2723
2724 This action MUST be paired with a ``batchbegin`` action.
2724 This action MUST be paired with a ``batchbegin`` action.
2725
2725
2726 httprequest <method> <path>
2726 httprequest <method> <path>
2727 ---------------------------
2727 ---------------------------
2728
2728
2729 (HTTP peer only)
2729 (HTTP peer only)
2730
2730
2731 Send an HTTP request to the peer.
2731 Send an HTTP request to the peer.
2732
2732
2733 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2733 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2734
2734
2735 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2735 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2736 headers to add to the request. e.g. ``Accept: foo``.
2736 headers to add to the request. e.g. ``Accept: foo``.
2737
2737
2738 The following arguments are special:
2738 The following arguments are special:
2739
2739
2740 ``BODYFILE``
2740 ``BODYFILE``
2741 The content of the file defined as the value to this argument will be
2741 The content of the file defined as the value to this argument will be
2742 transferred verbatim as the HTTP request body.
2742 transferred verbatim as the HTTP request body.
2743
2743
2744 ``frame <type> <flags> <payload>``
2744 ``frame <type> <flags> <payload>``
2745 Send a unified protocol frame as part of the request body.
2745 Send a unified protocol frame as part of the request body.
2746
2746
2747 All frames will be collected and sent as the body to the HTTP
2747 All frames will be collected and sent as the body to the HTTP
2748 request.
2748 request.
2749
2749
2750 close
2750 close
2751 -----
2751 -----
2752
2752
2753 Close the connection to the server.
2753 Close the connection to the server.
2754
2754
2755 flush
2755 flush
2756 -----
2756 -----
2757
2757
2758 Flush data written to the server.
2758 Flush data written to the server.
2759
2759
2760 readavailable
2760 readavailable
2761 -------------
2761 -------------
2762
2762
2763 Close the write end of the connection and read all available data from
2763 Close the write end of the connection and read all available data from
2764 the server.
2764 the server.
2765
2765
2766 If the connection to the server encompasses multiple pipes, we poll both
2766 If the connection to the server encompasses multiple pipes, we poll both
2767 pipes and read available data.
2767 pipes and read available data.
2768
2768
2769 readline
2769 readline
2770 --------
2770 --------
2771
2771
2772 Read a line of output from the server. If there are multiple output
2772 Read a line of output from the server. If there are multiple output
2773 pipes, reads only the main pipe.
2773 pipes, reads only the main pipe.
2774
2774
2775 ereadline
2775 ereadline
2776 ---------
2776 ---------
2777
2777
2778 Like ``readline``, but read from the stderr pipe, if available.
2778 Like ``readline``, but read from the stderr pipe, if available.
2779
2779
2780 read <X>
2780 read <X>
2781 --------
2781 --------
2782
2782
2783 ``read()`` N bytes from the server's main output pipe.
2783 ``read()`` N bytes from the server's main output pipe.
2784
2784
2785 eread <X>
2785 eread <X>
2786 ---------
2786 ---------
2787
2787
2788 ``read()`` N bytes from the server's stderr pipe, if available.
2788 ``read()`` N bytes from the server's stderr pipe, if available.
2789
2789
2790 Specifying Unified Frame-Based Protocol Frames
2790 Specifying Unified Frame-Based Protocol Frames
2791 ----------------------------------------------
2791 ----------------------------------------------
2792
2792
2793 It is possible to emit a *Unified Frame-Based Protocol* by using special
2793 It is possible to emit a *Unified Frame-Based Protocol* by using special
2794 syntax.
2794 syntax.
2795
2795
2796 A frame is composed as a type, flags, and payload. These can be parsed
2796 A frame is composed as a type, flags, and payload. These can be parsed
2797 from a string of the form:
2797 from a string of the form:
2798
2798
2799 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2799 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2800
2800
2801 ``request-id`` and ``stream-id`` are integers defining the request and
2801 ``request-id`` and ``stream-id`` are integers defining the request and
2802 stream identifiers.
2802 stream identifiers.
2803
2803
2804 ``type`` can be an integer value for the frame type or the string name
2804 ``type`` can be an integer value for the frame type or the string name
2805 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2805 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2806 ``command-name``.
2806 ``command-name``.
2807
2807
2808 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2808 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2809 components. Each component (and there can be just one) can be an integer
2809 components. Each component (and there can be just one) can be an integer
2810 or a flag name for stream flags or frame flags, respectively. Values are
2810 or a flag name for stream flags or frame flags, respectively. Values are
2811 resolved to integers and then bitwise OR'd together.
2811 resolved to integers and then bitwise OR'd together.
2812
2812
2813 ``payload`` represents the raw frame payload. If it begins with
2813 ``payload`` represents the raw frame payload. If it begins with
2814 ``cbor:``, the following string is evaluated as Python code and the
2814 ``cbor:``, the following string is evaluated as Python code and the
2815 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2815 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2816 as a Python byte string literal.
2816 as a Python byte string literal.
2817 """
2817 """
2818 opts = pycompat.byteskwargs(opts)
2818 opts = pycompat.byteskwargs(opts)
2819
2819
2820 if opts['localssh'] and not repo:
2820 if opts['localssh'] and not repo:
2821 raise error.Abort(_('--localssh requires a repository'))
2821 raise error.Abort(_('--localssh requires a repository'))
2822
2822
2823 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2823 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2824 raise error.Abort(_('invalid value for --peer'),
2824 raise error.Abort(_('invalid value for --peer'),
2825 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2825 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2826
2826
2827 if path and opts['localssh']:
2827 if path and opts['localssh']:
2828 raise error.Abort(_('cannot specify --localssh with an explicit '
2828 raise error.Abort(_('cannot specify --localssh with an explicit '
2829 'path'))
2829 'path'))
2830
2830
2831 if ui.interactive():
2831 if ui.interactive():
2832 ui.write(_('(waiting for commands on stdin)\n'))
2832 ui.write(_('(waiting for commands on stdin)\n'))
2833
2833
2834 blocks = list(_parsewirelangblocks(ui.fin))
2834 blocks = list(_parsewirelangblocks(ui.fin))
2835
2835
2836 proc = None
2836 proc = None
2837 stdin = None
2837 stdin = None
2838 stdout = None
2838 stdout = None
2839 stderr = None
2839 stderr = None
2840 opener = None
2840 opener = None
2841
2841
2842 if opts['localssh']:
2842 if opts['localssh']:
2843 # We start the SSH server in its own process so there is process
2843 # We start the SSH server in its own process so there is process
2844 # separation. This prevents a whole class of potential bugs around
2844 # separation. This prevents a whole class of potential bugs around
2845 # shared state from interfering with server operation.
2845 # shared state from interfering with server operation.
2846 args = procutil.hgcmd() + [
2846 args = procutil.hgcmd() + [
2847 '-R', repo.root,
2847 '-R', repo.root,
2848 'debugserve', '--sshstdio',
2848 'debugserve', '--sshstdio',
2849 ]
2849 ]
2850 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2850 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2851 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2851 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2852 bufsize=0)
2852 bufsize=0)
2853
2853
2854 stdin = proc.stdin
2854 stdin = proc.stdin
2855 stdout = proc.stdout
2855 stdout = proc.stdout
2856 stderr = proc.stderr
2856 stderr = proc.stderr
2857
2857
2858 # We turn the pipes into observers so we can log I/O.
2858 # We turn the pipes into observers so we can log I/O.
2859 if ui.verbose or opts['peer'] == 'raw':
2859 if ui.verbose or opts['peer'] == 'raw':
2860 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2860 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2861 logdata=True)
2861 logdata=True)
2862 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2862 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2863 logdata=True)
2863 logdata=True)
2864 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2864 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2865 logdata=True)
2865 logdata=True)
2866
2866
2867 # --localssh also implies the peer connection settings.
2867 # --localssh also implies the peer connection settings.
2868
2868
2869 url = 'ssh://localserver'
2869 url = 'ssh://localserver'
2870 autoreadstderr = not opts['noreadstderr']
2870 autoreadstderr = not opts['noreadstderr']
2871
2871
2872 if opts['peer'] == 'ssh1':
2872 if opts['peer'] == 'ssh1':
2873 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2873 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2874 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2874 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2875 None, autoreadstderr=autoreadstderr)
2875 None, autoreadstderr=autoreadstderr)
2876 elif opts['peer'] == 'ssh2':
2876 elif opts['peer'] == 'ssh2':
2877 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2877 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2878 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2878 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2879 None, autoreadstderr=autoreadstderr)
2879 None, autoreadstderr=autoreadstderr)
2880 elif opts['peer'] == 'raw':
2880 elif opts['peer'] == 'raw':
2881 ui.write(_('using raw connection to peer\n'))
2881 ui.write(_('using raw connection to peer\n'))
2882 peer = None
2882 peer = None
2883 else:
2883 else:
2884 ui.write(_('creating ssh peer from handshake results\n'))
2884 ui.write(_('creating ssh peer from handshake results\n'))
2885 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2885 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2886 autoreadstderr=autoreadstderr)
2886 autoreadstderr=autoreadstderr)
2887
2887
2888 elif path:
2888 elif path:
2889 # We bypass hg.peer() so we can proxy the sockets.
2889 # We bypass hg.peer() so we can proxy the sockets.
2890 # TODO consider not doing this because we skip
2890 # TODO consider not doing this because we skip
2891 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2891 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2892 u = util.url(path)
2892 u = util.url(path)
2893 if u.scheme != 'http':
2893 if u.scheme != 'http':
2894 raise error.Abort(_('only http:// paths are currently supported'))
2894 raise error.Abort(_('only http:// paths are currently supported'))
2895
2895
2896 url, authinfo = u.authinfo()
2896 url, authinfo = u.authinfo()
2897 openerargs = {
2897 openerargs = {
2898 r'useragent': b'Mercurial debugwireproto',
2898 r'useragent': b'Mercurial debugwireproto',
2899 }
2899 }
2900
2900
2901 # Turn pipes/sockets into observers so we can log I/O.
2901 # Turn pipes/sockets into observers so we can log I/O.
2902 if ui.verbose:
2902 if ui.verbose:
2903 openerargs.update({
2903 openerargs.update({
2904 r'loggingfh': ui,
2904 r'loggingfh': ui,
2905 r'loggingname': b's',
2905 r'loggingname': b's',
2906 r'loggingopts': {
2906 r'loggingopts': {
2907 r'logdata': True,
2907 r'logdata': True,
2908 r'logdataapis': False,
2908 r'logdataapis': False,
2909 },
2909 },
2910 })
2910 })
2911
2911
2912 if ui.debugflag:
2912 if ui.debugflag:
2913 openerargs[r'loggingopts'][r'logdataapis'] = True
2913 openerargs[r'loggingopts'][r'logdataapis'] = True
2914
2914
2915 # Don't send default headers when in raw mode. This allows us to
2915 # Don't send default headers when in raw mode. This allows us to
2916 # bypass most of the behavior of our URL handling code so we can
2916 # bypass most of the behavior of our URL handling code so we can
2917 # have near complete control over what's sent on the wire.
2917 # have near complete control over what's sent on the wire.
2918 if opts['peer'] == 'raw':
2918 if opts['peer'] == 'raw':
2919 openerargs[r'sendaccept'] = False
2919 openerargs[r'sendaccept'] = False
2920
2920
2921 opener = urlmod.opener(ui, authinfo, **openerargs)
2921 opener = urlmod.opener(ui, authinfo, **openerargs)
2922
2922
2923 if opts['peer'] == 'http2':
2923 if opts['peer'] == 'http2':
2924 ui.write(_('creating http peer for wire protocol version 2\n'))
2924 ui.write(_('creating http peer for wire protocol version 2\n'))
2925 # We go through makepeer() because we need an API descriptor for
2925 # We go through makepeer() because we need an API descriptor for
2926 # the peer instance to be useful.
2926 # the peer instance to be useful.
2927 with ui.configoverride({
2927 with ui.configoverride({
2928 ('experimental', 'httppeer.advertise-v2'): True}):
2928 ('experimental', 'httppeer.advertise-v2'): True}):
2929 if opts['nologhandshake']:
2929 if opts['nologhandshake']:
2930 ui.pushbuffer()
2930 ui.pushbuffer()
2931
2931
2932 peer = httppeer.makepeer(ui, path, opener=opener)
2932 peer = httppeer.makepeer(ui, path, opener=opener)
2933
2933
2934 if opts['nologhandshake']:
2934 if opts['nologhandshake']:
2935 ui.popbuffer()
2935 ui.popbuffer()
2936
2936
2937 if not isinstance(peer, httppeer.httpv2peer):
2937 if not isinstance(peer, httppeer.httpv2peer):
2938 raise error.Abort(_('could not instantiate HTTP peer for '
2938 raise error.Abort(_('could not instantiate HTTP peer for '
2939 'wire protocol version 2'),
2939 'wire protocol version 2'),
2940 hint=_('the server may not have the feature '
2940 hint=_('the server may not have the feature '
2941 'enabled or is not allowing this '
2941 'enabled or is not allowing this '
2942 'client version'))
2942 'client version'))
2943
2943
2944 elif opts['peer'] == 'raw':
2944 elif opts['peer'] == 'raw':
2945 ui.write(_('using raw connection to peer\n'))
2945 ui.write(_('using raw connection to peer\n'))
2946 peer = None
2946 peer = None
2947 elif opts['peer']:
2947 elif opts['peer']:
2948 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2948 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2949 opts['peer'])
2949 opts['peer'])
2950 else:
2950 else:
2951 peer = httppeer.makepeer(ui, path, opener=opener)
2951 peer = httppeer.makepeer(ui, path, opener=opener)
2952
2952
2953 # We /could/ populate stdin/stdout with sock.makefile()...
2953 # We /could/ populate stdin/stdout with sock.makefile()...
2954 else:
2954 else:
2955 raise error.Abort(_('unsupported connection configuration'))
2955 raise error.Abort(_('unsupported connection configuration'))
2956
2956
2957 batchedcommands = None
2957 batchedcommands = None
2958
2958
2959 # Now perform actions based on the parsed wire language instructions.
2959 # Now perform actions based on the parsed wire language instructions.
2960 for action, lines in blocks:
2960 for action, lines in blocks:
2961 if action in ('raw', 'raw+'):
2961 if action in ('raw', 'raw+'):
2962 if not stdin:
2962 if not stdin:
2963 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2963 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2964
2964
2965 # Concatenate the data together.
2965 # Concatenate the data together.
2966 data = ''.join(l.lstrip() for l in lines)
2966 data = ''.join(l.lstrip() for l in lines)
2967 data = stringutil.unescapestr(data)
2967 data = stringutil.unescapestr(data)
2968 stdin.write(data)
2968 stdin.write(data)
2969
2969
2970 if action == 'raw+':
2970 if action == 'raw+':
2971 stdin.flush()
2971 stdin.flush()
2972 elif action == 'flush':
2972 elif action == 'flush':
2973 if not stdin:
2973 if not stdin:
2974 raise error.Abort(_('cannot call flush on this peer'))
2974 raise error.Abort(_('cannot call flush on this peer'))
2975 stdin.flush()
2975 stdin.flush()
2976 elif action.startswith('command'):
2976 elif action.startswith('command'):
2977 if not peer:
2977 if not peer:
2978 raise error.Abort(_('cannot send commands unless peer instance '
2978 raise error.Abort(_('cannot send commands unless peer instance '
2979 'is available'))
2979 'is available'))
2980
2980
2981 command = action.split(' ', 1)[1]
2981 command = action.split(' ', 1)[1]
2982
2982
2983 args = {}
2983 args = {}
2984 for line in lines:
2984 for line in lines:
2985 # We need to allow empty values.
2985 # We need to allow empty values.
2986 fields = line.lstrip().split(' ', 1)
2986 fields = line.lstrip().split(' ', 1)
2987 if len(fields) == 1:
2987 if len(fields) == 1:
2988 key = fields[0]
2988 key = fields[0]
2989 value = ''
2989 value = ''
2990 else:
2990 else:
2991 key, value = fields
2991 key, value = fields
2992
2992
2993 if value.startswith('eval:'):
2993 if value.startswith('eval:'):
2994 value = stringutil.evalpythonliteral(value[5:])
2994 value = stringutil.evalpythonliteral(value[5:])
2995 else:
2995 else:
2996 value = stringutil.unescapestr(value)
2996 value = stringutil.unescapestr(value)
2997
2997
2998 args[key] = value
2998 args[key] = value
2999
2999
3000 if batchedcommands is not None:
3000 if batchedcommands is not None:
3001 batchedcommands.append((command, args))
3001 batchedcommands.append((command, args))
3002 continue
3002 continue
3003
3003
3004 ui.status(_('sending %s command\n') % command)
3004 ui.status(_('sending %s command\n') % command)
3005
3005
3006 if 'PUSHFILE' in args:
3006 if 'PUSHFILE' in args:
3007 with open(args['PUSHFILE'], r'rb') as fh:
3007 with open(args['PUSHFILE'], r'rb') as fh:
3008 del args['PUSHFILE']
3008 del args['PUSHFILE']
3009 res, output = peer._callpush(command, fh,
3009 res, output = peer._callpush(command, fh,
3010 **pycompat.strkwargs(args))
3010 **pycompat.strkwargs(args))
3011 ui.status(_('result: %s\n') % stringutil.escapestr(res))
3011 ui.status(_('result: %s\n') % stringutil.escapestr(res))
3012 ui.status(_('remote output: %s\n') %
3012 ui.status(_('remote output: %s\n') %
3013 stringutil.escapestr(output))
3013 stringutil.escapestr(output))
3014 else:
3014 else:
3015 with peer.commandexecutor() as e:
3015 with peer.commandexecutor() as e:
3016 res = e.callcommand(command, args).result()
3016 res = e.callcommand(command, args).result()
3017
3017
3018 if isinstance(res, wireprotov2peer.commandresponse):
3018 if isinstance(res, wireprotov2peer.commandresponse):
3019 val = list(res.cborobjects())
3019 val = list(res.cborobjects())
3020 ui.status(_('response: %s\n') %
3020 ui.status(_('response: %s\n') %
3021 stringutil.pprint(val, bprefix=True))
3021 stringutil.pprint(val, bprefix=True))
3022
3022
3023 else:
3023 else:
3024 ui.status(_('response: %s\n') %
3024 ui.status(_('response: %s\n') %
3025 stringutil.pprint(res, bprefix=True))
3025 stringutil.pprint(res, bprefix=True))
3026
3026
3027 elif action == 'batchbegin':
3027 elif action == 'batchbegin':
3028 if batchedcommands is not None:
3028 if batchedcommands is not None:
3029 raise error.Abort(_('nested batchbegin not allowed'))
3029 raise error.Abort(_('nested batchbegin not allowed'))
3030
3030
3031 batchedcommands = []
3031 batchedcommands = []
3032 elif action == 'batchsubmit':
3032 elif action == 'batchsubmit':
3033 # There is a batching API we could go through. But it would be
3033 # There is a batching API we could go through. But it would be
3034 # difficult to normalize requests into function calls. It is easier
3034 # difficult to normalize requests into function calls. It is easier
3035 # to bypass this layer and normalize to commands + args.
3035 # to bypass this layer and normalize to commands + args.
3036 ui.status(_('sending batch with %d sub-commands\n') %
3036 ui.status(_('sending batch with %d sub-commands\n') %
3037 len(batchedcommands))
3037 len(batchedcommands))
3038 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
3038 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
3039 ui.status(_('response #%d: %s\n') %
3039 ui.status(_('response #%d: %s\n') %
3040 (i, stringutil.escapestr(chunk)))
3040 (i, stringutil.escapestr(chunk)))
3041
3041
3042 batchedcommands = None
3042 batchedcommands = None
3043
3043
3044 elif action.startswith('httprequest '):
3044 elif action.startswith('httprequest '):
3045 if not opener:
3045 if not opener:
3046 raise error.Abort(_('cannot use httprequest without an HTTP '
3046 raise error.Abort(_('cannot use httprequest without an HTTP '
3047 'peer'))
3047 'peer'))
3048
3048
3049 request = action.split(' ', 2)
3049 request = action.split(' ', 2)
3050 if len(request) != 3:
3050 if len(request) != 3:
3051 raise error.Abort(_('invalid httprequest: expected format is '
3051 raise error.Abort(_('invalid httprequest: expected format is '
3052 '"httprequest <method> <path>'))
3052 '"httprequest <method> <path>'))
3053
3053
3054 method, httppath = request[1:]
3054 method, httppath = request[1:]
3055 headers = {}
3055 headers = {}
3056 body = None
3056 body = None
3057 frames = []
3057 frames = []
3058 for line in lines:
3058 for line in lines:
3059 line = line.lstrip()
3059 line = line.lstrip()
3060 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3060 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3061 if m:
3061 if m:
3062 headers[m.group(1)] = m.group(2)
3062 headers[m.group(1)] = m.group(2)
3063 continue
3063 continue
3064
3064
3065 if line.startswith(b'BODYFILE '):
3065 if line.startswith(b'BODYFILE '):
3066 with open(line.split(b' ', 1), 'rb') as fh:
3066 with open(line.split(b' ', 1), 'rb') as fh:
3067 body = fh.read()
3067 body = fh.read()
3068 elif line.startswith(b'frame '):
3068 elif line.startswith(b'frame '):
3069 frame = wireprotoframing.makeframefromhumanstring(
3069 frame = wireprotoframing.makeframefromhumanstring(
3070 line[len(b'frame '):])
3070 line[len(b'frame '):])
3071
3071
3072 frames.append(frame)
3072 frames.append(frame)
3073 else:
3073 else:
3074 raise error.Abort(_('unknown argument to httprequest: %s') %
3074 raise error.Abort(_('unknown argument to httprequest: %s') %
3075 line)
3075 line)
3076
3076
3077 url = path + httppath
3077 url = path + httppath
3078
3078
3079 if frames:
3079 if frames:
3080 body = b''.join(bytes(f) for f in frames)
3080 body = b''.join(bytes(f) for f in frames)
3081
3081
3082 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3082 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3083
3083
3084 # urllib.Request insists on using has_data() as a proxy for
3084 # urllib.Request insists on using has_data() as a proxy for
3085 # determining the request method. Override that to use our
3085 # determining the request method. Override that to use our
3086 # explicitly requested method.
3086 # explicitly requested method.
3087 req.get_method = lambda: method
3087 req.get_method = lambda: method
3088
3088
3089 try:
3089 try:
3090 res = opener.open(req)
3090 res = opener.open(req)
3091 body = res.read()
3091 body = res.read()
3092 except util.urlerr.urlerror as e:
3092 except util.urlerr.urlerror as e:
3093 e.read()
3093 e.read()
3094 continue
3094 continue
3095
3095
3096 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3096 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3097 ui.write(_('cbor> %s\n') %
3097 ui.write(_('cbor> %s\n') %
3098 stringutil.pprint(cbor.loads(body), bprefix=True))
3098 stringutil.pprint(cbor.loads(body), bprefix=True))
3099
3099
3100 elif action == 'close':
3100 elif action == 'close':
3101 peer.close()
3101 peer.close()
3102 elif action == 'readavailable':
3102 elif action == 'readavailable':
3103 if not stdout or not stderr:
3103 if not stdout or not stderr:
3104 raise error.Abort(_('readavailable not available on this peer'))
3104 raise error.Abort(_('readavailable not available on this peer'))
3105
3105
3106 stdin.close()
3106 stdin.close()
3107 stdout.read()
3107 stdout.read()
3108 stderr.read()
3108 stderr.read()
3109
3109
3110 elif action == 'readline':
3110 elif action == 'readline':
3111 if not stdout:
3111 if not stdout:
3112 raise error.Abort(_('readline not available on this peer'))
3112 raise error.Abort(_('readline not available on this peer'))
3113 stdout.readline()
3113 stdout.readline()
3114 elif action == 'ereadline':
3114 elif action == 'ereadline':
3115 if not stderr:
3115 if not stderr:
3116 raise error.Abort(_('ereadline not available on this peer'))
3116 raise error.Abort(_('ereadline not available on this peer'))
3117 stderr.readline()
3117 stderr.readline()
3118 elif action.startswith('read '):
3118 elif action.startswith('read '):
3119 count = int(action.split(' ', 1)[1])
3119 count = int(action.split(' ', 1)[1])
3120 if not stdout:
3120 if not stdout:
3121 raise error.Abort(_('read not available on this peer'))
3121 raise error.Abort(_('read not available on this peer'))
3122 stdout.read(count)
3122 stdout.read(count)
3123 elif action.startswith('eread '):
3123 elif action.startswith('eread '):
3124 count = int(action.split(' ', 1)[1])
3124 count = int(action.split(' ', 1)[1])
3125 if not stderr:
3125 if not stderr:
3126 raise error.Abort(_('eread not available on this peer'))
3126 raise error.Abort(_('eread not available on this peer'))
3127 stderr.read(count)
3127 stderr.read(count)
3128 else:
3128 else:
3129 raise error.Abort(_('unknown action: %s') % action)
3129 raise error.Abort(_('unknown action: %s') % action)
3130
3130
3131 if batchedcommands is not None:
3131 if batchedcommands is not None:
3132 raise error.Abort(_('unclosed "batchbegin" request'))
3132 raise error.Abort(_('unclosed "batchbegin" request'))
3133
3133
3134 if peer:
3134 if peer:
3135 peer.close()
3135 peer.close()
3136
3136
3137 if proc:
3137 if proc:
3138 proc.kill()
3138 proc.kill()
@@ -1,1721 +1,1729
1 Test basic extension support
1 Test basic extension support
2
2
3 $ cat > foobar.py <<EOF
3 $ cat > foobar.py <<EOF
4 > import os
4 > import os
5 > from mercurial import commands, registrar
5 > from mercurial import commands, registrar
6 > cmdtable = {}
6 > cmdtable = {}
7 > command = registrar.command(cmdtable)
7 > command = registrar.command(cmdtable)
8 > configtable = {}
8 > configtable = {}
9 > configitem = registrar.configitem(configtable)
9 > configitem = registrar.configitem(configtable)
10 > configitem(b'tests', b'foo', default=b"Foo")
10 > configitem(b'tests', b'foo', default=b"Foo")
11 > def uisetup(ui):
11 > def uisetup(ui):
12 > ui.write(b"uisetup called\\n")
12 > ui.write(b"uisetup called\\n")
13 > ui.flush()
13 > ui.flush()
14 > def reposetup(ui, repo):
14 > def reposetup(ui, repo):
15 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
15 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
16 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
16 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
17 > ui.flush()
17 > ui.flush()
18 > @command(b'foo', [], b'hg foo')
18 > @command(b'foo', [], b'hg foo')
19 > def foo(ui, *args, **kwargs):
19 > def foo(ui, *args, **kwargs):
20 > foo = ui.config(b'tests', b'foo')
20 > foo = ui.config(b'tests', b'foo')
21 > ui.write(foo)
21 > ui.write(foo)
22 > ui.write(b"\\n")
22 > ui.write(b"\\n")
23 > @command(b'bar', [], b'hg bar', norepo=True)
23 > @command(b'bar', [], b'hg bar', norepo=True)
24 > def bar(ui, *args, **kwargs):
24 > def bar(ui, *args, **kwargs):
25 > ui.write(b"Bar\\n")
25 > ui.write(b"Bar\\n")
26 > EOF
26 > EOF
27 $ abspath=`pwd`/foobar.py
27 $ abspath=`pwd`/foobar.py
28
28
29 $ mkdir barfoo
29 $ mkdir barfoo
30 $ cp foobar.py barfoo/__init__.py
30 $ cp foobar.py barfoo/__init__.py
31 $ barfoopath=`pwd`/barfoo
31 $ barfoopath=`pwd`/barfoo
32
32
33 $ hg init a
33 $ hg init a
34 $ cd a
34 $ cd a
35 $ echo foo > file
35 $ echo foo > file
36 $ hg add file
36 $ hg add file
37 $ hg commit -m 'add file'
37 $ hg commit -m 'add file'
38
38
39 $ echo '[extensions]' >> $HGRCPATH
39 $ echo '[extensions]' >> $HGRCPATH
40 $ echo "foobar = $abspath" >> $HGRCPATH
40 $ echo "foobar = $abspath" >> $HGRCPATH
41 $ hg foo
41 $ hg foo
42 uisetup called
42 uisetup called
43 reposetup called for a
43 reposetup called for a
44 ui == repo.ui
44 ui == repo.ui
45 reposetup called for a (chg !)
45 reposetup called for a (chg !)
46 ui == repo.ui (chg !)
46 ui == repo.ui (chg !)
47 Foo
47 Foo
48
48
49 $ cd ..
49 $ cd ..
50 $ hg clone a b
50 $ hg clone a b
51 uisetup called (no-chg !)
51 uisetup called (no-chg !)
52 reposetup called for a
52 reposetup called for a
53 ui == repo.ui
53 ui == repo.ui
54 reposetup called for b
54 reposetup called for b
55 ui == repo.ui
55 ui == repo.ui
56 updating to branch default
56 updating to branch default
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58
58
59 $ hg bar
59 $ hg bar
60 uisetup called (no-chg !)
60 uisetup called (no-chg !)
61 Bar
61 Bar
62 $ echo 'foobar = !' >> $HGRCPATH
62 $ echo 'foobar = !' >> $HGRCPATH
63
63
64 module/__init__.py-style
64 module/__init__.py-style
65
65
66 $ echo "barfoo = $barfoopath" >> $HGRCPATH
66 $ echo "barfoo = $barfoopath" >> $HGRCPATH
67 $ cd a
67 $ cd a
68 $ hg foo
68 $ hg foo
69 uisetup called
69 uisetup called
70 reposetup called for a
70 reposetup called for a
71 ui == repo.ui
71 ui == repo.ui
72 reposetup called for a (chg !)
72 reposetup called for a (chg !)
73 ui == repo.ui (chg !)
73 ui == repo.ui (chg !)
74 Foo
74 Foo
75 $ echo 'barfoo = !' >> $HGRCPATH
75 $ echo 'barfoo = !' >> $HGRCPATH
76
76
77 Check that extensions are loaded in phases:
77 Check that extensions are loaded in phases:
78
78
79 $ cat > foo.py <<EOF
79 $ cat > foo.py <<EOF
80 > import os
80 > import os
81 > name = os.path.basename(__file__).rsplit('.', 1)[0]
81 > name = os.path.basename(__file__).rsplit('.', 1)[0]
82 > print("1) %s imported" % name)
82 > print("1) %s imported" % name)
83 > def uisetup(ui):
83 > def uisetup(ui):
84 > print("2) %s uisetup" % name)
84 > print("2) %s uisetup" % name)
85 > def extsetup():
85 > def extsetup():
86 > print("3) %s extsetup" % name)
86 > print("3) %s extsetup" % name)
87 > def reposetup(ui, repo):
87 > def reposetup(ui, repo):
88 > print("4) %s reposetup" % name)
88 > print("4) %s reposetup" % name)
89 >
89 >
90 > # custom predicate to check registration of functions at loading
90 > # custom predicate to check registration of functions at loading
91 > from mercurial import (
91 > from mercurial import (
92 > registrar,
92 > registrar,
93 > smartset,
93 > smartset,
94 > )
94 > )
95 > revsetpredicate = registrar.revsetpredicate()
95 > revsetpredicate = registrar.revsetpredicate()
96 > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
96 > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
97 > def custompredicate(repo, subset, x):
97 > def custompredicate(repo, subset, x):
98 > return smartset.baseset([r for r in subset if r in {0}])
98 > return smartset.baseset([r for r in subset if r in {0}])
99 > EOF
99 > EOF
100
100
101 $ cp foo.py bar.py
101 $ cp foo.py bar.py
102 $ echo 'foo = foo.py' >> $HGRCPATH
102 $ echo 'foo = foo.py' >> $HGRCPATH
103 $ echo 'bar = bar.py' >> $HGRCPATH
103 $ echo 'bar = bar.py' >> $HGRCPATH
104
104
105 Check normal command's load order of extensions and registration of functions
105 Check normal command's load order of extensions and registration of functions
106
106
107 $ hg log -r "foo() and bar()" -q
107 $ hg log -r "foo() and bar()" -q
108 1) foo imported
108 1) foo imported
109 1) bar imported
109 1) bar imported
110 2) foo uisetup
110 2) foo uisetup
111 2) bar uisetup
111 2) bar uisetup
112 3) foo extsetup
112 3) foo extsetup
113 3) bar extsetup
113 3) bar extsetup
114 4) foo reposetup
114 4) foo reposetup
115 4) bar reposetup
115 4) bar reposetup
116 0:c24b9ac61126
116 0:c24b9ac61126
117
117
118 Check hgweb's load order of extensions and registration of functions
118 Check hgweb's load order of extensions and registration of functions
119
119
120 $ cat > hgweb.cgi <<EOF
120 $ cat > hgweb.cgi <<EOF
121 > #!$PYTHON
121 > #!$PYTHON
122 > from mercurial import demandimport; demandimport.enable()
122 > from mercurial import demandimport; demandimport.enable()
123 > from mercurial.hgweb import hgweb
123 > from mercurial.hgweb import hgweb
124 > from mercurial.hgweb import wsgicgi
124 > from mercurial.hgweb import wsgicgi
125 > application = hgweb('.', 'test repo')
125 > application = hgweb('.', 'test repo')
126 > wsgicgi.launch(application)
126 > wsgicgi.launch(application)
127 > EOF
127 > EOF
128 $ . "$TESTDIR/cgienv"
128 $ . "$TESTDIR/cgienv"
129
129
130 $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \
130 $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \
131 > | grep '^[0-9]) ' # ignores HTML output
131 > | grep '^[0-9]) ' # ignores HTML output
132 1) foo imported
132 1) foo imported
133 1) bar imported
133 1) bar imported
134 2) foo uisetup
134 2) foo uisetup
135 2) bar uisetup
135 2) bar uisetup
136 3) foo extsetup
136 3) foo extsetup
137 3) bar extsetup
137 3) bar extsetup
138 4) foo reposetup
138 4) foo reposetup
139 4) bar reposetup
139 4) bar reposetup
140
140
141 (check that revset predicate foo() and bar() are available)
141 (check that revset predicate foo() and bar() are available)
142
142
143 #if msys
143 #if msys
144 $ PATH_INFO='//shortlog'
144 $ PATH_INFO='//shortlog'
145 #else
145 #else
146 $ PATH_INFO='/shortlog'
146 $ PATH_INFO='/shortlog'
147 #endif
147 #endif
148 $ export PATH_INFO
148 $ export PATH_INFO
149 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \
149 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \
150 > | grep '<a href="/rev/[0-9a-z]*">'
150 > | grep '<a href="/rev/[0-9a-z]*">'
151 <a href="/rev/c24b9ac61126">add file</a>
151 <a href="/rev/c24b9ac61126">add file</a>
152
152
153 $ echo 'foo = !' >> $HGRCPATH
153 $ echo 'foo = !' >> $HGRCPATH
154 $ echo 'bar = !' >> $HGRCPATH
154 $ echo 'bar = !' >> $HGRCPATH
155
155
156 Check "from __future__ import absolute_import" support for external libraries
156 Check "from __future__ import absolute_import" support for external libraries
157
157
158 #if windows
158 #if windows
159 $ PATHSEP=";"
159 $ PATHSEP=";"
160 #else
160 #else
161 $ PATHSEP=":"
161 $ PATHSEP=":"
162 #endif
162 #endif
163 $ export PATHSEP
163 $ export PATHSEP
164
164
165 $ mkdir $TESTTMP/libroot
165 $ mkdir $TESTTMP/libroot
166 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
166 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
167 $ mkdir $TESTTMP/libroot/mod
167 $ mkdir $TESTTMP/libroot/mod
168 $ touch $TESTTMP/libroot/mod/__init__.py
168 $ touch $TESTTMP/libroot/mod/__init__.py
169 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
169 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
170
170
171 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
171 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
172 > from __future__ import absolute_import
172 > from __future__ import absolute_import
173 > import ambig # should load "libroot/ambig.py"
173 > import ambig # should load "libroot/ambig.py"
174 > s = ambig.s
174 > s = ambig.s
175 > EOF
175 > EOF
176 $ cat > loadabs.py <<EOF
176 $ cat > loadabs.py <<EOF
177 > import mod.ambigabs as ambigabs
177 > import mod.ambigabs as ambigabs
178 > def extsetup():
178 > def extsetup():
179 > print('ambigabs.s=%s' % ambigabs.s)
179 > print('ambigabs.s=%s' % ambigabs.s)
180 > EOF
180 > EOF
181 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
181 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
182 ambigabs.s=libroot/ambig.py
182 ambigabs.s=libroot/ambig.py
183 $TESTTMP/a
183 $TESTTMP/a
184
184
185 #if no-py3k
185 #if no-py3k
186 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
186 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
187 > import ambig # should load "libroot/mod/ambig.py"
187 > import ambig # should load "libroot/mod/ambig.py"
188 > s = ambig.s
188 > s = ambig.s
189 > EOF
189 > EOF
190 $ cat > loadrel.py <<EOF
190 $ cat > loadrel.py <<EOF
191 > import mod.ambigrel as ambigrel
191 > import mod.ambigrel as ambigrel
192 > def extsetup():
192 > def extsetup():
193 > print('ambigrel.s=%s' % ambigrel.s)
193 > print('ambigrel.s=%s' % ambigrel.s)
194 > EOF
194 > EOF
195 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
195 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
196 ambigrel.s=libroot/mod/ambig.py
196 ambigrel.s=libroot/mod/ambig.py
197 $TESTTMP/a
197 $TESTTMP/a
198 #endif
198 #endif
199
199
200 Check absolute/relative import of extension specific modules
200 Check absolute/relative import of extension specific modules
201
201
202 $ mkdir $TESTTMP/extroot
202 $ mkdir $TESTTMP/extroot
203 $ cat > $TESTTMP/extroot/bar.py <<EOF
203 $ cat > $TESTTMP/extroot/bar.py <<EOF
204 > s = 'this is extroot.bar'
204 > s = 'this is extroot.bar'
205 > EOF
205 > EOF
206 $ mkdir $TESTTMP/extroot/sub1
206 $ mkdir $TESTTMP/extroot/sub1
207 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
207 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
208 > s = 'this is extroot.sub1.__init__'
208 > s = 'this is extroot.sub1.__init__'
209 > EOF
209 > EOF
210 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
210 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
211 > s = 'this is extroot.sub1.baz'
211 > s = 'this is extroot.sub1.baz'
212 > EOF
212 > EOF
213 $ cat > $TESTTMP/extroot/__init__.py <<EOF
213 $ cat > $TESTTMP/extroot/__init__.py <<EOF
214 > s = 'this is extroot.__init__'
214 > s = 'this is extroot.__init__'
215 > import foo
215 > import foo
216 > def extsetup(ui):
216 > def extsetup(ui):
217 > ui.write('(extroot) ', foo.func(), '\n')
217 > ui.write('(extroot) ', foo.func(), '\n')
218 > ui.flush()
218 > ui.flush()
219 > EOF
219 > EOF
220
220
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
222 > # test absolute import
222 > # test absolute import
223 > buf = []
223 > buf = []
224 > def func():
224 > def func():
225 > # "not locals" case
225 > # "not locals" case
226 > import extroot.bar
226 > import extroot.bar
227 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
227 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
228 > return '\n(extroot) '.join(buf)
228 > return '\n(extroot) '.join(buf)
229 > # "fromlist == ('*',)" case
229 > # "fromlist == ('*',)" case
230 > from extroot.bar import *
230 > from extroot.bar import *
231 > buf.append('from extroot.bar import *: %s' % s)
231 > buf.append('from extroot.bar import *: %s' % s)
232 > # "not fromlist" and "if '.' in name" case
232 > # "not fromlist" and "if '.' in name" case
233 > import extroot.sub1.baz
233 > import extroot.sub1.baz
234 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
234 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
235 > # "not fromlist" and NOT "if '.' in name" case
235 > # "not fromlist" and NOT "if '.' in name" case
236 > import extroot
236 > import extroot
237 > buf.append('import extroot: %s' % extroot.s)
237 > buf.append('import extroot: %s' % extroot.s)
238 > # NOT "not fromlist" and NOT "level != -1" case
238 > # NOT "not fromlist" and NOT "level != -1" case
239 > from extroot.bar import s
239 > from extroot.bar import s
240 > buf.append('from extroot.bar import s: %s' % s)
240 > buf.append('from extroot.bar import s: %s' % s)
241 > EOF
241 > EOF
242 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
242 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
243 (extroot) from extroot.bar import *: this is extroot.bar
243 (extroot) from extroot.bar import *: this is extroot.bar
244 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
244 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
245 (extroot) import extroot: this is extroot.__init__
245 (extroot) import extroot: this is extroot.__init__
246 (extroot) from extroot.bar import s: this is extroot.bar
246 (extroot) from extroot.bar import s: this is extroot.bar
247 (extroot) import extroot.bar in func(): this is extroot.bar
247 (extroot) import extroot.bar in func(): this is extroot.bar
248 $TESTTMP/a
248 $TESTTMP/a
249
249
250 #if no-py3k
250 #if no-py3k
251 $ rm "$TESTTMP"/extroot/foo.*
251 $ rm "$TESTTMP"/extroot/foo.*
252 $ rm -Rf "$TESTTMP/extroot/__pycache__"
252 $ rm -Rf "$TESTTMP/extroot/__pycache__"
253 $ cat > $TESTTMP/extroot/foo.py <<EOF
253 $ cat > $TESTTMP/extroot/foo.py <<EOF
254 > # test relative import
254 > # test relative import
255 > buf = []
255 > buf = []
256 > def func():
256 > def func():
257 > # "not locals" case
257 > # "not locals" case
258 > import bar
258 > import bar
259 > buf.append('import bar in func(): %s' % bar.s)
259 > buf.append('import bar in func(): %s' % bar.s)
260 > return '\n(extroot) '.join(buf)
260 > return '\n(extroot) '.join(buf)
261 > # "fromlist == ('*',)" case
261 > # "fromlist == ('*',)" case
262 > from bar import *
262 > from bar import *
263 > buf.append('from bar import *: %s' % s)
263 > buf.append('from bar import *: %s' % s)
264 > # "not fromlist" and "if '.' in name" case
264 > # "not fromlist" and "if '.' in name" case
265 > import sub1.baz
265 > import sub1.baz
266 > buf.append('import sub1.baz: %s' % sub1.baz.s)
266 > buf.append('import sub1.baz: %s' % sub1.baz.s)
267 > # "not fromlist" and NOT "if '.' in name" case
267 > # "not fromlist" and NOT "if '.' in name" case
268 > import sub1
268 > import sub1
269 > buf.append('import sub1: %s' % sub1.s)
269 > buf.append('import sub1: %s' % sub1.s)
270 > # NOT "not fromlist" and NOT "level != -1" case
270 > # NOT "not fromlist" and NOT "level != -1" case
271 > from bar import s
271 > from bar import s
272 > buf.append('from bar import s: %s' % s)
272 > buf.append('from bar import s: %s' % s)
273 > EOF
273 > EOF
274 $ hg --config extensions.extroot=$TESTTMP/extroot root
274 $ hg --config extensions.extroot=$TESTTMP/extroot root
275 (extroot) from bar import *: this is extroot.bar
275 (extroot) from bar import *: this is extroot.bar
276 (extroot) import sub1.baz: this is extroot.sub1.baz
276 (extroot) import sub1.baz: this is extroot.sub1.baz
277 (extroot) import sub1: this is extroot.sub1.__init__
277 (extroot) import sub1: this is extroot.sub1.__init__
278 (extroot) from bar import s: this is extroot.bar
278 (extroot) from bar import s: this is extroot.bar
279 (extroot) import bar in func(): this is extroot.bar
279 (extroot) import bar in func(): this is extroot.bar
280 $TESTTMP/a
280 $TESTTMP/a
281 #endif
281 #endif
282
282
283 #if demandimport
283 #if demandimport
284
284
285 Examine whether module loading is delayed until actual referring, even
285 Examine whether module loading is delayed until actual referring, even
286 though module is imported with "absolute_import" feature.
286 though module is imported with "absolute_import" feature.
287
287
288 Files below in each packages are used for described purpose:
288 Files below in each packages are used for described purpose:
289
289
290 - "called": examine whether "from MODULE import ATTR" works correctly
290 - "called": examine whether "from MODULE import ATTR" works correctly
291 - "unused": examine whether loading is delayed correctly
291 - "unused": examine whether loading is delayed correctly
292 - "used": examine whether "from PACKAGE import MODULE" works correctly
292 - "used": examine whether "from PACKAGE import MODULE" works correctly
293
293
294 Package hierarchy is needed to examine whether demand importing works
294 Package hierarchy is needed to examine whether demand importing works
295 as expected for "from SUB.PACK.AGE import MODULE".
295 as expected for "from SUB.PACK.AGE import MODULE".
296
296
297 Setup "external library" to be imported with "absolute_import"
297 Setup "external library" to be imported with "absolute_import"
298 feature.
298 feature.
299
299
300 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
300 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
301 $ touch $TESTTMP/extlibroot/__init__.py
301 $ touch $TESTTMP/extlibroot/__init__.py
302 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
302 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
303 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
303 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
304
304
305 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
305 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
306 > def func():
306 > def func():
307 > return "this is extlibroot.lsub1.lsub2.called.func()"
307 > return "this is extlibroot.lsub1.lsub2.called.func()"
308 > EOF
308 > EOF
309 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
309 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
310 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
310 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
311 > EOF
311 > EOF
312 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
312 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
313 > detail = "this is extlibroot.lsub1.lsub2.used"
313 > detail = "this is extlibroot.lsub1.lsub2.used"
314 > EOF
314 > EOF
315
315
316 Setup sub-package of "external library", which causes instantiation of
316 Setup sub-package of "external library", which causes instantiation of
317 demandmod in "recurse down the module chain" code path. Relative
317 demandmod in "recurse down the module chain" code path. Relative
318 importing with "absolute_import" feature isn't tested, because "level
318 importing with "absolute_import" feature isn't tested, because "level
319 >=1 " doesn't cause instantiation of demandmod.
319 >=1 " doesn't cause instantiation of demandmod.
320
320
321 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
321 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
322 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
322 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
323 > detail = "this is extlibroot.recursedown.abs.used"
323 > detail = "this is extlibroot.recursedown.abs.used"
324 > EOF
324 > EOF
325 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
325 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
326 > from __future__ import absolute_import
326 > from __future__ import absolute_import
327 > from extlibroot.recursedown.abs.used import detail
327 > from extlibroot.recursedown.abs.used import detail
328 > EOF
328 > EOF
329
329
330 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
330 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
331 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
331 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
332 > detail = "this is extlibroot.recursedown.legacy.used"
332 > detail = "this is extlibroot.recursedown.legacy.used"
333 > EOF
333 > EOF
334 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
334 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
335 > # legacy style (level == -1) import
335 > # legacy style (level == -1) import
336 > from extlibroot.recursedown.legacy.used import detail
336 > from extlibroot.recursedown.legacy.used import detail
337 > EOF
337 > EOF
338
338
339 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
339 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
340 > from __future__ import absolute_import
340 > from __future__ import absolute_import
341 > from extlibroot.recursedown.abs import detail as absdetail
341 > from extlibroot.recursedown.abs import detail as absdetail
342 > from .legacy import detail as legacydetail
342 > from .legacy import detail as legacydetail
343 > EOF
343 > EOF
344
344
345 Setup package that re-exports an attribute of its submodule as the same
345 Setup package that re-exports an attribute of its submodule as the same
346 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
346 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
347 the submodule 'used' should be somehow accessible. (issue5617)
347 the submodule 'used' should be somehow accessible. (issue5617)
348
348
349 $ mkdir -p $TESTTMP/extlibroot/shadowing
349 $ mkdir -p $TESTTMP/extlibroot/shadowing
350 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<EOF
350 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<EOF
351 > detail = "this is extlibroot.shadowing.used"
351 > detail = "this is extlibroot.shadowing.used"
352 > EOF
352 > EOF
353 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<EOF
353 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<EOF
354 > from __future__ import absolute_import
354 > from __future__ import absolute_import
355 > from extlibroot.shadowing.used import detail
355 > from extlibroot.shadowing.used import detail
356 > EOF
356 > EOF
357 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<EOF
357 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<EOF
358 > from __future__ import absolute_import
358 > from __future__ import absolute_import
359 > from .used import detail as used
359 > from .used import detail as used
360 > EOF
360 > EOF
361
361
362 Setup extension local modules to be imported with "absolute_import"
362 Setup extension local modules to be imported with "absolute_import"
363 feature.
363 feature.
364
364
365 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
365 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
366 $ touch $TESTTMP/absextroot/xsub1/__init__.py
366 $ touch $TESTTMP/absextroot/xsub1/__init__.py
367 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
367 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
368
368
369 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
369 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
370 > def func():
370 > def func():
371 > return "this is absextroot.xsub1.xsub2.called.func()"
371 > return "this is absextroot.xsub1.xsub2.called.func()"
372 > EOF
372 > EOF
373 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
373 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
374 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
374 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
375 > EOF
375 > EOF
376 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
376 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
377 > detail = "this is absextroot.xsub1.xsub2.used"
377 > detail = "this is absextroot.xsub1.xsub2.used"
378 > EOF
378 > EOF
379
379
380 Setup extension local modules to examine whether demand importing
380 Setup extension local modules to examine whether demand importing
381 works as expected in "level > 1" case.
381 works as expected in "level > 1" case.
382
382
383 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
383 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
384 > detail = "this is absextroot.relimportee"
384 > detail = "this is absextroot.relimportee"
385 > EOF
385 > EOF
386 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
386 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
387 > from __future__ import absolute_import
387 > from __future__ import absolute_import
388 > from ... import relimportee
388 > from ... import relimportee
389 > detail = "this relimporter imports %r" % (relimportee.detail)
389 > detail = "this relimporter imports %r" % (relimportee.detail)
390 > EOF
390 > EOF
391
391
392 Setup modules, which actually import extension local modules at
392 Setup modules, which actually import extension local modules at
393 runtime.
393 runtime.
394
394
395 $ cat > $TESTTMP/absextroot/absolute.py << EOF
395 $ cat > $TESTTMP/absextroot/absolute.py << EOF
396 > from __future__ import absolute_import
396 > from __future__ import absolute_import
397 >
397 >
398 > # import extension local modules absolutely (level = 0)
398 > # import extension local modules absolutely (level = 0)
399 > from absextroot.xsub1.xsub2 import used, unused
399 > from absextroot.xsub1.xsub2 import used, unused
400 > from absextroot.xsub1.xsub2.called import func
400 > from absextroot.xsub1.xsub2.called import func
401 >
401 >
402 > def getresult():
402 > def getresult():
403 > result = []
403 > result = []
404 > result.append(used.detail)
404 > result.append(used.detail)
405 > result.append(func())
405 > result.append(func())
406 > return result
406 > return result
407 > EOF
407 > EOF
408
408
409 $ cat > $TESTTMP/absextroot/relative.py << EOF
409 $ cat > $TESTTMP/absextroot/relative.py << EOF
410 > from __future__ import absolute_import
410 > from __future__ import absolute_import
411 >
411 >
412 > # import extension local modules relatively (level == 1)
412 > # import extension local modules relatively (level == 1)
413 > from .xsub1.xsub2 import used, unused
413 > from .xsub1.xsub2 import used, unused
414 > from .xsub1.xsub2.called import func
414 > from .xsub1.xsub2.called import func
415 >
415 >
416 > # import a module, which implies "importing with level > 1"
416 > # import a module, which implies "importing with level > 1"
417 > from .xsub1.xsub2 import relimporter
417 > from .xsub1.xsub2 import relimporter
418 >
418 >
419 > def getresult():
419 > def getresult():
420 > result = []
420 > result = []
421 > result.append(used.detail)
421 > result.append(used.detail)
422 > result.append(func())
422 > result.append(func())
423 > result.append(relimporter.detail)
423 > result.append(relimporter.detail)
424 > return result
424 > return result
425 > EOF
425 > EOF
426
426
427 Setup main procedure of extension.
427 Setup main procedure of extension.
428
428
429 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
429 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
430 > from __future__ import absolute_import
430 > from __future__ import absolute_import
431 > from mercurial import registrar
431 > from mercurial import registrar
432 > cmdtable = {}
432 > cmdtable = {}
433 > command = registrar.command(cmdtable)
433 > command = registrar.command(cmdtable)
434 >
434 >
435 > # "absolute" and "relative" shouldn't be imported before actual
435 > # "absolute" and "relative" shouldn't be imported before actual
436 > # command execution, because (1) they import same modules, and (2)
436 > # command execution, because (1) they import same modules, and (2)
437 > # preceding import (= instantiate "demandmod" object instead of
437 > # preceding import (= instantiate "demandmod" object instead of
438 > # real "module" object) might hide problem of succeeding import.
438 > # real "module" object) might hide problem of succeeding import.
439 >
439 >
440 > @command(b'showabsolute', [], norepo=True)
440 > @command(b'showabsolute', [], norepo=True)
441 > def showabsolute(ui, *args, **opts):
441 > def showabsolute(ui, *args, **opts):
442 > from absextroot import absolute
442 > from absextroot import absolute
443 > ui.write(b'ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
443 > ui.write(b'ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
444 >
444 >
445 > @command(b'showrelative', [], norepo=True)
445 > @command(b'showrelative', [], norepo=True)
446 > def showrelative(ui, *args, **opts):
446 > def showrelative(ui, *args, **opts):
447 > from . import relative
447 > from . import relative
448 > ui.write(b'REL: %s\n' % '\nREL: '.join(relative.getresult()))
448 > ui.write(b'REL: %s\n' % '\nREL: '.join(relative.getresult()))
449 >
449 >
450 > # import modules from external library
450 > # import modules from external library
451 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
451 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
452 > from extlibroot.lsub1.lsub2.called import func as lfunc
452 > from extlibroot.lsub1.lsub2.called import func as lfunc
453 > from extlibroot.recursedown import absdetail, legacydetail
453 > from extlibroot.recursedown import absdetail, legacydetail
454 > from extlibroot.shadowing import proxied
454 > from extlibroot.shadowing import proxied
455 >
455 >
456 > def uisetup(ui):
456 > def uisetup(ui):
457 > result = []
457 > result = []
458 > result.append(lused.detail)
458 > result.append(lused.detail)
459 > result.append(lfunc())
459 > result.append(lfunc())
460 > result.append(absdetail)
460 > result.append(absdetail)
461 > result.append(legacydetail)
461 > result.append(legacydetail)
462 > result.append(proxied.detail)
462 > result.append(proxied.detail)
463 > ui.write('LIB: %s\n' % '\nLIB: '.join(result))
463 > ui.write('LIB: %s\n' % '\nLIB: '.join(result))
464 > EOF
464 > EOF
465
465
466 Examine module importing.
466 Examine module importing.
467
467
468 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
468 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
469 LIB: this is extlibroot.lsub1.lsub2.used
469 LIB: this is extlibroot.lsub1.lsub2.used
470 LIB: this is extlibroot.lsub1.lsub2.called.func()
470 LIB: this is extlibroot.lsub1.lsub2.called.func()
471 LIB: this is extlibroot.recursedown.abs.used
471 LIB: this is extlibroot.recursedown.abs.used
472 LIB: this is extlibroot.recursedown.legacy.used
472 LIB: this is extlibroot.recursedown.legacy.used
473 LIB: this is extlibroot.shadowing.used
473 LIB: this is extlibroot.shadowing.used
474 ABS: this is absextroot.xsub1.xsub2.used
474 ABS: this is absextroot.xsub1.xsub2.used
475 ABS: this is absextroot.xsub1.xsub2.called.func()
475 ABS: this is absextroot.xsub1.xsub2.called.func()
476
476
477 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
477 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
478 LIB: this is extlibroot.lsub1.lsub2.used
478 LIB: this is extlibroot.lsub1.lsub2.used
479 LIB: this is extlibroot.lsub1.lsub2.called.func()
479 LIB: this is extlibroot.lsub1.lsub2.called.func()
480 LIB: this is extlibroot.recursedown.abs.used
480 LIB: this is extlibroot.recursedown.abs.used
481 LIB: this is extlibroot.recursedown.legacy.used
481 LIB: this is extlibroot.recursedown.legacy.used
482 LIB: this is extlibroot.shadowing.used
482 LIB: this is extlibroot.shadowing.used
483 REL: this is absextroot.xsub1.xsub2.used
483 REL: this is absextroot.xsub1.xsub2.used
484 REL: this is absextroot.xsub1.xsub2.called.func()
484 REL: this is absextroot.xsub1.xsub2.called.func()
485 REL: this relimporter imports 'this is absextroot.relimportee'
485 REL: this relimporter imports 'this is absextroot.relimportee'
486
486
487 Examine whether sub-module is imported relatively as expected.
487 Examine whether sub-module is imported relatively as expected.
488
488
489 See also issue5208 for detail about example case on Python 3.x.
489 See also issue5208 for detail about example case on Python 3.x.
490
490
491 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
491 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
492 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
492 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
493
493
494 $ cat > $TESTTMP/notexist.py <<EOF
494 $ cat > $TESTTMP/notexist.py <<EOF
495 > text = 'notexist.py at root is loaded unintentionally\n'
495 > text = 'notexist.py at root is loaded unintentionally\n'
496 > EOF
496 > EOF
497
497
498 $ cat > $TESTTMP/checkrelativity.py <<EOF
498 $ cat > $TESTTMP/checkrelativity.py <<EOF
499 > from mercurial import registrar
499 > from mercurial import registrar
500 > cmdtable = {}
500 > cmdtable = {}
501 > command = registrar.command(cmdtable)
501 > command = registrar.command(cmdtable)
502 >
502 >
503 > # demand import avoids failure of importing notexist here
503 > # demand import avoids failure of importing notexist here
504 > import extlibroot.lsub1.lsub2.notexist
504 > import extlibroot.lsub1.lsub2.notexist
505 >
505 >
506 > @command(b'checkrelativity', [], norepo=True)
506 > @command(b'checkrelativity', [], norepo=True)
507 > def checkrelativity(ui, *args, **opts):
507 > def checkrelativity(ui, *args, **opts):
508 > try:
508 > try:
509 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
509 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
510 > return 1 # unintentional success
510 > return 1 # unintentional success
511 > except ImportError:
511 > except ImportError:
512 > pass # intentional failure
512 > pass # intentional failure
513 > EOF
513 > EOF
514
514
515 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
515 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
516
516
517 #endif
517 #endif
518
518
519 Make sure a broken uisetup doesn't globally break hg:
519 Make sure a broken uisetup doesn't globally break hg:
520 $ cat > $TESTTMP/baduisetup.py <<EOF
520 $ cat > $TESTTMP/baduisetup.py <<EOF
521 > def uisetup(ui):
521 > def uisetup(ui):
522 > 1/0
522 > 1/0
523 > EOF
523 > EOF
524
524
525 Even though the extension fails during uisetup, hg is still basically usable:
525 Even though the extension fails during uisetup, hg is still basically usable:
526 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
526 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
527 Traceback (most recent call last):
527 Traceback (most recent call last):
528 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
528 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
529 uisetup(ui)
529 uisetup(ui)
530 File "$TESTTMP/baduisetup.py", line 2, in uisetup
530 File "$TESTTMP/baduisetup.py", line 2, in uisetup
531 1/0
531 1/0
532 ZeroDivisionError: integer division or modulo by zero
532 ZeroDivisionError: integer division or modulo by zero
533 *** failed to set up extension baduisetup: integer division or modulo by zero
533 *** failed to set up extension baduisetup: integer division or modulo by zero
534 Mercurial Distributed SCM (version *) (glob)
534 Mercurial Distributed SCM (version *) (glob)
535 (see https://mercurial-scm.org for more information)
535 (see https://mercurial-scm.org for more information)
536
536
537 Copyright (C) 2005-* Matt Mackall and others (glob)
537 Copyright (C) 2005-* Matt Mackall and others (glob)
538 This is free software; see the source for copying conditions. There is NO
538 This is free software; see the source for copying conditions. There is NO
539 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
539 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
540
540
541 $ cd ..
541 $ cd ..
542
542
543 hide outer repo
543 hide outer repo
544 $ hg init
544 $ hg init
545
545
546 $ cat > empty.py <<EOF
546 $ cat > empty.py <<EOF
547 > '''empty cmdtable
547 > '''empty cmdtable
548 > '''
548 > '''
549 > cmdtable = {}
549 > cmdtable = {}
550 > EOF
550 > EOF
551 $ emptypath=`pwd`/empty.py
551 $ emptypath=`pwd`/empty.py
552 $ echo "empty = $emptypath" >> $HGRCPATH
552 $ echo "empty = $emptypath" >> $HGRCPATH
553 $ hg help empty
553 $ hg help empty
554 empty extension - empty cmdtable
554 empty extension - empty cmdtable
555
555
556 no commands defined
556 no commands defined
557
557
558
558
559 $ echo 'empty = !' >> $HGRCPATH
559 $ echo 'empty = !' >> $HGRCPATH
560
560
561 $ cat > debugextension.py <<EOF
561 $ cat > debugextension.py <<EOF
562 > '''only debugcommands
562 > '''only debugcommands
563 > '''
563 > '''
564 > from mercurial import registrar
564 > from mercurial import registrar
565 > cmdtable = {}
565 > cmdtable = {}
566 > command = registrar.command(cmdtable)
566 > command = registrar.command(cmdtable)
567 > @command(b'debugfoobar', [], b'hg debugfoobar')
567 > @command(b'debugfoobar', [], b'hg debugfoobar')
568 > def debugfoobar(ui, repo, *args, **opts):
568 > def debugfoobar(ui, repo, *args, **opts):
569 > "yet another debug command"
569 > "yet another debug command"
570 > pass
570 > pass
571 > @command(b'foo', [], b'hg foo')
571 > @command(b'foo', [], b'hg foo')
572 > def foo(ui, repo, *args, **opts):
572 > def foo(ui, repo, *args, **opts):
573 > """yet another foo command
573 > """yet another foo command
574 > This command has been DEPRECATED since forever.
574 > This command has been DEPRECATED since forever.
575 > """
575 > """
576 > pass
576 > pass
577 > EOF
577 > EOF
578 $ debugpath=`pwd`/debugextension.py
578 $ debugpath=`pwd`/debugextension.py
579 $ echo "debugextension = $debugpath" >> $HGRCPATH
579 $ echo "debugextension = $debugpath" >> $HGRCPATH
580
580
581 $ hg help debugextension
581 $ hg help debugextension
582 hg debugextensions
582 hg debugextensions
583
583
584 show information about active extensions
584 show information about active extensions
585
585
586 options:
586 options:
587
587
588 (some details hidden, use --verbose to show complete help)
588 (some details hidden, use --verbose to show complete help)
589
589
590
590
591 $ hg --verbose help debugextension
591 $ hg --verbose help debugextension
592 hg debugextensions
592 hg debugextensions
593
593
594 show information about active extensions
594 show information about active extensions
595
595
596 options:
596 options:
597
597
598 -T --template TEMPLATE display with template (EXPERIMENTAL)
598 -T --template TEMPLATE display with template (EXPERIMENTAL)
599
599
600 global options ([+] can be repeated):
600 global options ([+] can be repeated):
601
601
602 -R --repository REPO repository root directory or name of overlay bundle
602 -R --repository REPO repository root directory or name of overlay bundle
603 file
603 file
604 --cwd DIR change working directory
604 --cwd DIR change working directory
605 -y --noninteractive do not prompt, automatically pick the first choice for
605 -y --noninteractive do not prompt, automatically pick the first choice for
606 all prompts
606 all prompts
607 -q --quiet suppress output
607 -q --quiet suppress output
608 -v --verbose enable additional output
608 -v --verbose enable additional output
609 --color TYPE when to colorize (boolean, always, auto, never, or
609 --color TYPE when to colorize (boolean, always, auto, never, or
610 debug)
610 debug)
611 --config CONFIG [+] set/override config option (use 'section.name=value')
611 --config CONFIG [+] set/override config option (use 'section.name=value')
612 --debug enable debugging output
612 --debug enable debugging output
613 --debugger start debugger
613 --debugger start debugger
614 --encoding ENCODE set the charset encoding (default: ascii)
614 --encoding ENCODE set the charset encoding (default: ascii)
615 --encodingmode MODE set the charset encoding mode (default: strict)
615 --encodingmode MODE set the charset encoding mode (default: strict)
616 --traceback always print a traceback on exception
616 --traceback always print a traceback on exception
617 --time time how long the command takes
617 --time time how long the command takes
618 --profile print command execution profile
618 --profile print command execution profile
619 --version output version information and exit
619 --version output version information and exit
620 -h --help display help and exit
620 -h --help display help and exit
621 --hidden consider hidden changesets
621 --hidden consider hidden changesets
622 --pager TYPE when to paginate (boolean, always, auto, or never)
622 --pager TYPE when to paginate (boolean, always, auto, or never)
623 (default: auto)
623 (default: auto)
624
624
625
625
626
626
627
627
628
628
629
629
630 $ hg --debug help debugextension
630 $ hg --debug help debugextension
631 hg debugextensions
631 hg debugextensions
632
632
633 show information about active extensions
633 show information about active extensions
634
634
635 options:
635 options:
636
636
637 -T --template TEMPLATE display with template (EXPERIMENTAL)
637 -T --template TEMPLATE display with template (EXPERIMENTAL)
638
638
639 global options ([+] can be repeated):
639 global options ([+] can be repeated):
640
640
641 -R --repository REPO repository root directory or name of overlay bundle
641 -R --repository REPO repository root directory or name of overlay bundle
642 file
642 file
643 --cwd DIR change working directory
643 --cwd DIR change working directory
644 -y --noninteractive do not prompt, automatically pick the first choice for
644 -y --noninteractive do not prompt, automatically pick the first choice for
645 all prompts
645 all prompts
646 -q --quiet suppress output
646 -q --quiet suppress output
647 -v --verbose enable additional output
647 -v --verbose enable additional output
648 --color TYPE when to colorize (boolean, always, auto, never, or
648 --color TYPE when to colorize (boolean, always, auto, never, or
649 debug)
649 debug)
650 --config CONFIG [+] set/override config option (use 'section.name=value')
650 --config CONFIG [+] set/override config option (use 'section.name=value')
651 --debug enable debugging output
651 --debug enable debugging output
652 --debugger start debugger
652 --debugger start debugger
653 --encoding ENCODE set the charset encoding (default: ascii)
653 --encoding ENCODE set the charset encoding (default: ascii)
654 --encodingmode MODE set the charset encoding mode (default: strict)
654 --encodingmode MODE set the charset encoding mode (default: strict)
655 --traceback always print a traceback on exception
655 --traceback always print a traceback on exception
656 --time time how long the command takes
656 --time time how long the command takes
657 --profile print command execution profile
657 --profile print command execution profile
658 --version output version information and exit
658 --version output version information and exit
659 -h --help display help and exit
659 -h --help display help and exit
660 --hidden consider hidden changesets
660 --hidden consider hidden changesets
661 --pager TYPE when to paginate (boolean, always, auto, or never)
661 --pager TYPE when to paginate (boolean, always, auto, or never)
662 (default: auto)
662 (default: auto)
663
663
664
664
665
665
666
666
667
667
668 $ echo 'debugextension = !' >> $HGRCPATH
668 $ echo 'debugextension = !' >> $HGRCPATH
669
669
670 Asking for help about a deprecated extension should do something useful:
670 Asking for help about a deprecated extension should do something useful:
671
671
672 $ hg help glog
672 $ hg help glog
673 'glog' is provided by the following extension:
673 'glog' is provided by the following extension:
674
674
675 graphlog command to view revision graphs from a shell (DEPRECATED)
675 graphlog command to view revision graphs from a shell (DEPRECATED)
676
676
677 (use 'hg help extensions' for information on enabling extensions)
677 (use 'hg help extensions' for information on enabling extensions)
678
678
679 Extension module help vs command help:
679 Extension module help vs command help:
680
680
681 $ echo 'extdiff =' >> $HGRCPATH
681 $ echo 'extdiff =' >> $HGRCPATH
682 $ hg help extdiff
682 $ hg help extdiff
683 hg extdiff [OPT]... [FILE]...
683 hg extdiff [OPT]... [FILE]...
684
684
685 use external program to diff repository (or selected files)
685 use external program to diff repository (or selected files)
686
686
687 Show differences between revisions for the specified files, using an
687 Show differences between revisions for the specified files, using an
688 external program. The default program used is diff, with default options
688 external program. The default program used is diff, with default options
689 "-Npru".
689 "-Npru".
690
690
691 To select a different program, use the -p/--program option. The program
691 To select a different program, use the -p/--program option. The program
692 will be passed the names of two directories to compare. To pass additional
692 will be passed the names of two directories to compare. To pass additional
693 options to the program, use -o/--option. These will be passed before the
693 options to the program, use -o/--option. These will be passed before the
694 names of the directories to compare.
694 names of the directories to compare.
695
695
696 When two revision arguments are given, then changes are shown between
696 When two revision arguments are given, then changes are shown between
697 those revisions. If only one revision is specified then that revision is
697 those revisions. If only one revision is specified then that revision is
698 compared to the working directory, and, when no revisions are specified,
698 compared to the working directory, and, when no revisions are specified,
699 the working directory files are compared to its parent.
699 the working directory files are compared to its parent.
700
700
701 (use 'hg help -e extdiff' to show help for the extdiff extension)
701 (use 'hg help -e extdiff' to show help for the extdiff extension)
702
702
703 options ([+] can be repeated):
703 options ([+] can be repeated):
704
704
705 -p --program CMD comparison program to run
705 -p --program CMD comparison program to run
706 -o --option OPT [+] pass option to comparison program
706 -o --option OPT [+] pass option to comparison program
707 -r --rev REV [+] revision
707 -r --rev REV [+] revision
708 -c --change REV change made by revision
708 -c --change REV change made by revision
709 --patch compare patches for two revisions
709 --patch compare patches for two revisions
710 -I --include PATTERN [+] include names matching the given patterns
710 -I --include PATTERN [+] include names matching the given patterns
711 -X --exclude PATTERN [+] exclude names matching the given patterns
711 -X --exclude PATTERN [+] exclude names matching the given patterns
712 -S --subrepos recurse into subrepositories
712 -S --subrepos recurse into subrepositories
713
713
714 (some details hidden, use --verbose to show complete help)
714 (some details hidden, use --verbose to show complete help)
715
715
716
716
717
717
718
718
719
719
720
720
721
721
722
722
723
723
724
724
725 $ hg help --extension extdiff
725 $ hg help --extension extdiff
726 extdiff extension - command to allow external programs to compare revisions
726 extdiff extension - command to allow external programs to compare revisions
727
727
728 The extdiff Mercurial extension allows you to use external programs to compare
728 The extdiff Mercurial extension allows you to use external programs to compare
729 revisions, or revision with working directory. The external diff programs are
729 revisions, or revision with working directory. The external diff programs are
730 called with a configurable set of options and two non-option arguments: paths
730 called with a configurable set of options and two non-option arguments: paths
731 to directories containing snapshots of files to compare.
731 to directories containing snapshots of files to compare.
732
732
733 If there is more than one file being compared and the "child" revision is the
733 If there is more than one file being compared and the "child" revision is the
734 working directory, any modifications made in the external diff program will be
734 working directory, any modifications made in the external diff program will be
735 copied back to the working directory from the temporary directory.
735 copied back to the working directory from the temporary directory.
736
736
737 The extdiff extension also allows you to configure new diff commands, so you
737 The extdiff extension also allows you to configure new diff commands, so you
738 do not need to type 'hg extdiff -p kdiff3' always.
738 do not need to type 'hg extdiff -p kdiff3' always.
739
739
740 [extdiff]
740 [extdiff]
741 # add new command that runs GNU diff(1) in 'context diff' mode
741 # add new command that runs GNU diff(1) in 'context diff' mode
742 cdiff = gdiff -Nprc5
742 cdiff = gdiff -Nprc5
743 ## or the old way:
743 ## or the old way:
744 #cmd.cdiff = gdiff
744 #cmd.cdiff = gdiff
745 #opts.cdiff = -Nprc5
745 #opts.cdiff = -Nprc5
746
746
747 # add new command called meld, runs meld (no need to name twice). If
747 # add new command called meld, runs meld (no need to name twice). If
748 # the meld executable is not available, the meld tool in [merge-tools]
748 # the meld executable is not available, the meld tool in [merge-tools]
749 # will be used, if available
749 # will be used, if available
750 meld =
750 meld =
751
751
752 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
752 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
753 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
753 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
754 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
754 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
755 # your .vimrc
755 # your .vimrc
756 vimdiff = gvim -f "+next" \
756 vimdiff = gvim -f "+next" \
757 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
757 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
758
758
759 Tool arguments can include variables that are expanded at runtime:
759 Tool arguments can include variables that are expanded at runtime:
760
760
761 $parent1, $plabel1 - filename, descriptive label of first parent
761 $parent1, $plabel1 - filename, descriptive label of first parent
762 $child, $clabel - filename, descriptive label of child revision
762 $child, $clabel - filename, descriptive label of child revision
763 $parent2, $plabel2 - filename, descriptive label of second parent
763 $parent2, $plabel2 - filename, descriptive label of second parent
764 $root - repository root
764 $root - repository root
765 $parent is an alias for $parent1.
765 $parent is an alias for $parent1.
766
766
767 The extdiff extension will look in your [diff-tools] and [merge-tools]
767 The extdiff extension will look in your [diff-tools] and [merge-tools]
768 sections for diff tool arguments, when none are specified in [extdiff].
768 sections for diff tool arguments, when none are specified in [extdiff].
769
769
770 [extdiff]
770 [extdiff]
771 kdiff3 =
771 kdiff3 =
772
772
773 [diff-tools]
773 [diff-tools]
774 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
774 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
775
775
776 You can use -I/-X and list of file or directory names like normal 'hg diff'
776 You can use -I/-X and list of file or directory names like normal 'hg diff'
777 command. The extdiff extension makes snapshots of only needed files, so
777 command. The extdiff extension makes snapshots of only needed files, so
778 running the external diff program will actually be pretty fast (at least
778 running the external diff program will actually be pretty fast (at least
779 faster than having to compare the entire tree).
779 faster than having to compare the entire tree).
780
780
781 list of commands:
781 list of commands:
782
782
783 extdiff use external program to diff repository (or selected files)
783 extdiff use external program to diff repository (or selected files)
784
784
785 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
785 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
786
786
787
787
788
788
789
789
790
790
791
791
792
792
793
793
794
794
795
795
796
796
797
797
798
798
799
799
800
800
801
801
802 $ echo 'extdiff = !' >> $HGRCPATH
802 $ echo 'extdiff = !' >> $HGRCPATH
803
803
804 Test help topic with same name as extension
804 Test help topic with same name as extension
805
805
806 $ cat > multirevs.py <<EOF
806 $ cat > multirevs.py <<EOF
807 > from mercurial import commands, registrar
807 > from mercurial import commands, registrar
808 > cmdtable = {}
808 > cmdtable = {}
809 > command = registrar.command(cmdtable)
809 > command = registrar.command(cmdtable)
810 > """multirevs extension
810 > """multirevs extension
811 > Big multi-line module docstring."""
811 > Big multi-line module docstring."""
812 > @command(b'multirevs', [], b'ARG', norepo=True)
812 > @command(b'multirevs', [], b'ARG', norepo=True)
813 > def multirevs(ui, repo, arg, *args, **opts):
813 > def multirevs(ui, repo, arg, *args, **opts):
814 > """multirevs command"""
814 > """multirevs command"""
815 > pass
815 > pass
816 > EOF
816 > EOF
817 $ echo "multirevs = multirevs.py" >> $HGRCPATH
817 $ echo "multirevs = multirevs.py" >> $HGRCPATH
818
818
819 $ hg help multirevs | tail
819 $ hg help multirevs | tail
820 used):
820 used):
821
821
822 hg update :@
822 hg update :@
823
823
824 - Show diff between tags 1.3 and 1.5 (this works because the first and the
824 - Show diff between tags 1.3 and 1.5 (this works because the first and the
825 last revisions of the revset are used):
825 last revisions of the revset are used):
826
826
827 hg diff -r 1.3::1.5
827 hg diff -r 1.3::1.5
828
828
829 use 'hg help -c multirevs' to see help for the multirevs command
829 use 'hg help -c multirevs' to see help for the multirevs command
830
830
831
831
832
832
833
833
834
834
835
835
836 $ hg help -c multirevs
836 $ hg help -c multirevs
837 hg multirevs ARG
837 hg multirevs ARG
838
838
839 multirevs command
839 multirevs command
840
840
841 (some details hidden, use --verbose to show complete help)
841 (some details hidden, use --verbose to show complete help)
842
842
843
843
844
844
845 $ hg multirevs
845 $ hg multirevs
846 hg multirevs: invalid arguments
846 hg multirevs: invalid arguments
847 hg multirevs ARG
847 hg multirevs ARG
848
848
849 multirevs command
849 multirevs command
850
850
851 (use 'hg multirevs -h' to show more help)
851 (use 'hg multirevs -h' to show more help)
852 [255]
852 [255]
853
853
854
854
855
855
856 $ echo "multirevs = !" >> $HGRCPATH
856 $ echo "multirevs = !" >> $HGRCPATH
857
857
858 Issue811: Problem loading extensions twice (by site and by user)
858 Issue811: Problem loading extensions twice (by site and by user)
859
859
860 $ cat <<EOF >> $HGRCPATH
860 $ cat <<EOF >> $HGRCPATH
861 > mq =
861 > mq =
862 > strip =
862 > strip =
863 > hgext.mq =
863 > hgext.mq =
864 > hgext/mq =
864 > hgext/mq =
865 > EOF
865 > EOF
866
866
867 Show extensions:
867 Show extensions:
868 (note that mq force load strip, also checking it's not loaded twice)
868 (note that mq force load strip, also checking it's not loaded twice)
869
869
870 #if no-extraextensions
870 #if no-extraextensions
871 $ hg debugextensions
871 $ hg debugextensions
872 mq
872 mq
873 strip
873 strip
874 #endif
874 #endif
875
875
876 For extensions, which name matches one of its commands, help
876 For extensions, which name matches one of its commands, help
877 message should ask '-v -e' to get list of built-in aliases
877 message should ask '-v -e' to get list of built-in aliases
878 along with extension help itself
878 along with extension help itself
879
879
880 $ mkdir $TESTTMP/d
880 $ mkdir $TESTTMP/d
881 $ cat > $TESTTMP/d/dodo.py <<EOF
881 $ cat > $TESTTMP/d/dodo.py <<EOF
882 > """
882 > """
883 > This is an awesome 'dodo' extension. It does nothing and
883 > This is an awesome 'dodo' extension. It does nothing and
884 > writes 'Foo foo'
884 > writes 'Foo foo'
885 > """
885 > """
886 > from mercurial import commands, registrar
886 > from mercurial import commands, registrar
887 > cmdtable = {}
887 > cmdtable = {}
888 > command = registrar.command(cmdtable)
888 > command = registrar.command(cmdtable)
889 > @command(b'dodo', [], b'hg dodo')
889 > @command(b'dodo', [], b'hg dodo')
890 > def dodo(ui, *args, **kwargs):
890 > def dodo(ui, *args, **kwargs):
891 > """Does nothing"""
891 > """Does nothing"""
892 > ui.write(b"I do nothing. Yay\\n")
892 > ui.write(b"I do nothing. Yay\\n")
893 > @command(b'foofoo', [], b'hg foofoo')
893 > @command(b'foofoo', [], b'hg foofoo')
894 > def foofoo(ui, *args, **kwargs):
894 > def foofoo(ui, *args, **kwargs):
895 > """Writes 'Foo foo'"""
895 > """Writes 'Foo foo'"""
896 > ui.write(b"Foo foo\\n")
896 > ui.write(b"Foo foo\\n")
897 > EOF
897 > EOF
898 $ dodopath=$TESTTMP/d/dodo.py
898 $ dodopath=$TESTTMP/d/dodo.py
899
899
900 $ echo "dodo = $dodopath" >> $HGRCPATH
900 $ echo "dodo = $dodopath" >> $HGRCPATH
901
901
902 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
902 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
903 $ hg help -e dodo
903 $ hg help -e dodo
904 dodo extension -
904 dodo extension -
905
905
906 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
906 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
907
907
908 list of commands:
908 list of commands:
909
909
910 dodo Does nothing
910 dodo Does nothing
911 foofoo Writes 'Foo foo'
911 foofoo Writes 'Foo foo'
912
912
913 (use 'hg help -v -e dodo' to show built-in aliases and global options)
913 (use 'hg help -v -e dodo' to show built-in aliases and global options)
914
914
915 Make sure that '-v -e' prints list of built-in aliases along with
915 Make sure that '-v -e' prints list of built-in aliases along with
916 extension help itself
916 extension help itself
917 $ hg help -v -e dodo
917 $ hg help -v -e dodo
918 dodo extension -
918 dodo extension -
919
919
920 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
920 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
921
921
922 list of commands:
922 list of commands:
923
923
924 dodo Does nothing
924 dodo Does nothing
925 foofoo Writes 'Foo foo'
925 foofoo Writes 'Foo foo'
926
926
927 global options ([+] can be repeated):
927 global options ([+] can be repeated):
928
928
929 -R --repository REPO repository root directory or name of overlay bundle
929 -R --repository REPO repository root directory or name of overlay bundle
930 file
930 file
931 --cwd DIR change working directory
931 --cwd DIR change working directory
932 -y --noninteractive do not prompt, automatically pick the first choice for
932 -y --noninteractive do not prompt, automatically pick the first choice for
933 all prompts
933 all prompts
934 -q --quiet suppress output
934 -q --quiet suppress output
935 -v --verbose enable additional output
935 -v --verbose enable additional output
936 --color TYPE when to colorize (boolean, always, auto, never, or
936 --color TYPE when to colorize (boolean, always, auto, never, or
937 debug)
937 debug)
938 --config CONFIG [+] set/override config option (use 'section.name=value')
938 --config CONFIG [+] set/override config option (use 'section.name=value')
939 --debug enable debugging output
939 --debug enable debugging output
940 --debugger start debugger
940 --debugger start debugger
941 --encoding ENCODE set the charset encoding (default: ascii)
941 --encoding ENCODE set the charset encoding (default: ascii)
942 --encodingmode MODE set the charset encoding mode (default: strict)
942 --encodingmode MODE set the charset encoding mode (default: strict)
943 --traceback always print a traceback on exception
943 --traceback always print a traceback on exception
944 --time time how long the command takes
944 --time time how long the command takes
945 --profile print command execution profile
945 --profile print command execution profile
946 --version output version information and exit
946 --version output version information and exit
947 -h --help display help and exit
947 -h --help display help and exit
948 --hidden consider hidden changesets
948 --hidden consider hidden changesets
949 --pager TYPE when to paginate (boolean, always, auto, or never)
949 --pager TYPE when to paginate (boolean, always, auto, or never)
950 (default: auto)
950 (default: auto)
951
951
952 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
952 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
953 $ hg help -v dodo
953 $ hg help -v dodo
954 hg dodo
954 hg dodo
955
955
956 Does nothing
956 Does nothing
957
957
958 (use 'hg help -e dodo' to show help for the dodo extension)
958 (use 'hg help -e dodo' to show help for the dodo extension)
959
959
960 options:
960 options:
961
961
962 --mq operate on patch repository
962 --mq operate on patch repository
963
963
964 global options ([+] can be repeated):
964 global options ([+] can be repeated):
965
965
966 -R --repository REPO repository root directory or name of overlay bundle
966 -R --repository REPO repository root directory or name of overlay bundle
967 file
967 file
968 --cwd DIR change working directory
968 --cwd DIR change working directory
969 -y --noninteractive do not prompt, automatically pick the first choice for
969 -y --noninteractive do not prompt, automatically pick the first choice for
970 all prompts
970 all prompts
971 -q --quiet suppress output
971 -q --quiet suppress output
972 -v --verbose enable additional output
972 -v --verbose enable additional output
973 --color TYPE when to colorize (boolean, always, auto, never, or
973 --color TYPE when to colorize (boolean, always, auto, never, or
974 debug)
974 debug)
975 --config CONFIG [+] set/override config option (use 'section.name=value')
975 --config CONFIG [+] set/override config option (use 'section.name=value')
976 --debug enable debugging output
976 --debug enable debugging output
977 --debugger start debugger
977 --debugger start debugger
978 --encoding ENCODE set the charset encoding (default: ascii)
978 --encoding ENCODE set the charset encoding (default: ascii)
979 --encodingmode MODE set the charset encoding mode (default: strict)
979 --encodingmode MODE set the charset encoding mode (default: strict)
980 --traceback always print a traceback on exception
980 --traceback always print a traceback on exception
981 --time time how long the command takes
981 --time time how long the command takes
982 --profile print command execution profile
982 --profile print command execution profile
983 --version output version information and exit
983 --version output version information and exit
984 -h --help display help and exit
984 -h --help display help and exit
985 --hidden consider hidden changesets
985 --hidden consider hidden changesets
986 --pager TYPE when to paginate (boolean, always, auto, or never)
986 --pager TYPE when to paginate (boolean, always, auto, or never)
987 (default: auto)
987 (default: auto)
988
988
989 In case when extension name doesn't match any of its commands,
989 In case when extension name doesn't match any of its commands,
990 help message should ask for '-v' to get list of built-in aliases
990 help message should ask for '-v' to get list of built-in aliases
991 along with extension help
991 along with extension help
992 $ cat > $TESTTMP/d/dudu.py <<EOF
992 $ cat > $TESTTMP/d/dudu.py <<EOF
993 > """
993 > """
994 > This is an awesome 'dudu' extension. It does something and
994 > This is an awesome 'dudu' extension. It does something and
995 > also writes 'Beep beep'
995 > also writes 'Beep beep'
996 > """
996 > """
997 > from mercurial import commands, registrar
997 > from mercurial import commands, registrar
998 > cmdtable = {}
998 > cmdtable = {}
999 > command = registrar.command(cmdtable)
999 > command = registrar.command(cmdtable)
1000 > @command(b'something', [], b'hg something')
1000 > @command(b'something', [], b'hg something')
1001 > def something(ui, *args, **kwargs):
1001 > def something(ui, *args, **kwargs):
1002 > """Does something"""
1002 > """Does something"""
1003 > ui.write(b"I do something. Yaaay\\n")
1003 > ui.write(b"I do something. Yaaay\\n")
1004 > @command(b'beep', [], b'hg beep')
1004 > @command(b'beep', [], b'hg beep')
1005 > def beep(ui, *args, **kwargs):
1005 > def beep(ui, *args, **kwargs):
1006 > """Writes 'Beep beep'"""
1006 > """Writes 'Beep beep'"""
1007 > ui.write(b"Beep beep\\n")
1007 > ui.write(b"Beep beep\\n")
1008 > EOF
1008 > EOF
1009 $ dudupath=$TESTTMP/d/dudu.py
1009 $ dudupath=$TESTTMP/d/dudu.py
1010
1010
1011 $ echo "dudu = $dudupath" >> $HGRCPATH
1011 $ echo "dudu = $dudupath" >> $HGRCPATH
1012
1012
1013 $ hg help -e dudu
1013 $ hg help -e dudu
1014 dudu extension -
1014 dudu extension -
1015
1015
1016 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1016 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1017 beep'
1017 beep'
1018
1018
1019 list of commands:
1019 list of commands:
1020
1020
1021 beep Writes 'Beep beep'
1021 beep Writes 'Beep beep'
1022 something Does something
1022 something Does something
1023
1023
1024 (use 'hg help -v dudu' to show built-in aliases and global options)
1024 (use 'hg help -v dudu' to show built-in aliases and global options)
1025
1025
1026 In case when extension name doesn't match any of its commands,
1026 In case when extension name doesn't match any of its commands,
1027 help options '-v' and '-v -e' should be equivalent
1027 help options '-v' and '-v -e' should be equivalent
1028 $ hg help -v dudu
1028 $ hg help -v dudu
1029 dudu extension -
1029 dudu extension -
1030
1030
1031 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1031 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1032 beep'
1032 beep'
1033
1033
1034 list of commands:
1034 list of commands:
1035
1035
1036 beep Writes 'Beep beep'
1036 beep Writes 'Beep beep'
1037 something Does something
1037 something Does something
1038
1038
1039 global options ([+] can be repeated):
1039 global options ([+] can be repeated):
1040
1040
1041 -R --repository REPO repository root directory or name of overlay bundle
1041 -R --repository REPO repository root directory or name of overlay bundle
1042 file
1042 file
1043 --cwd DIR change working directory
1043 --cwd DIR change working directory
1044 -y --noninteractive do not prompt, automatically pick the first choice for
1044 -y --noninteractive do not prompt, automatically pick the first choice for
1045 all prompts
1045 all prompts
1046 -q --quiet suppress output
1046 -q --quiet suppress output
1047 -v --verbose enable additional output
1047 -v --verbose enable additional output
1048 --color TYPE when to colorize (boolean, always, auto, never, or
1048 --color TYPE when to colorize (boolean, always, auto, never, or
1049 debug)
1049 debug)
1050 --config CONFIG [+] set/override config option (use 'section.name=value')
1050 --config CONFIG [+] set/override config option (use 'section.name=value')
1051 --debug enable debugging output
1051 --debug enable debugging output
1052 --debugger start debugger
1052 --debugger start debugger
1053 --encoding ENCODE set the charset encoding (default: ascii)
1053 --encoding ENCODE set the charset encoding (default: ascii)
1054 --encodingmode MODE set the charset encoding mode (default: strict)
1054 --encodingmode MODE set the charset encoding mode (default: strict)
1055 --traceback always print a traceback on exception
1055 --traceback always print a traceback on exception
1056 --time time how long the command takes
1056 --time time how long the command takes
1057 --profile print command execution profile
1057 --profile print command execution profile
1058 --version output version information and exit
1058 --version output version information and exit
1059 -h --help display help and exit
1059 -h --help display help and exit
1060 --hidden consider hidden changesets
1060 --hidden consider hidden changesets
1061 --pager TYPE when to paginate (boolean, always, auto, or never)
1061 --pager TYPE when to paginate (boolean, always, auto, or never)
1062 (default: auto)
1062 (default: auto)
1063
1063
1064 $ hg help -v -e dudu
1064 $ hg help -v -e dudu
1065 dudu extension -
1065 dudu extension -
1066
1066
1067 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1067 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1068 beep'
1068 beep'
1069
1069
1070 list of commands:
1070 list of commands:
1071
1071
1072 beep Writes 'Beep beep'
1072 beep Writes 'Beep beep'
1073 something Does something
1073 something Does something
1074
1074
1075 global options ([+] can be repeated):
1075 global options ([+] can be repeated):
1076
1076
1077 -R --repository REPO repository root directory or name of overlay bundle
1077 -R --repository REPO repository root directory or name of overlay bundle
1078 file
1078 file
1079 --cwd DIR change working directory
1079 --cwd DIR change working directory
1080 -y --noninteractive do not prompt, automatically pick the first choice for
1080 -y --noninteractive do not prompt, automatically pick the first choice for
1081 all prompts
1081 all prompts
1082 -q --quiet suppress output
1082 -q --quiet suppress output
1083 -v --verbose enable additional output
1083 -v --verbose enable additional output
1084 --color TYPE when to colorize (boolean, always, auto, never, or
1084 --color TYPE when to colorize (boolean, always, auto, never, or
1085 debug)
1085 debug)
1086 --config CONFIG [+] set/override config option (use 'section.name=value')
1086 --config CONFIG [+] set/override config option (use 'section.name=value')
1087 --debug enable debugging output
1087 --debug enable debugging output
1088 --debugger start debugger
1088 --debugger start debugger
1089 --encoding ENCODE set the charset encoding (default: ascii)
1089 --encoding ENCODE set the charset encoding (default: ascii)
1090 --encodingmode MODE set the charset encoding mode (default: strict)
1090 --encodingmode MODE set the charset encoding mode (default: strict)
1091 --traceback always print a traceback on exception
1091 --traceback always print a traceback on exception
1092 --time time how long the command takes
1092 --time time how long the command takes
1093 --profile print command execution profile
1093 --profile print command execution profile
1094 --version output version information and exit
1094 --version output version information and exit
1095 -h --help display help and exit
1095 -h --help display help and exit
1096 --hidden consider hidden changesets
1096 --hidden consider hidden changesets
1097 --pager TYPE when to paginate (boolean, always, auto, or never)
1097 --pager TYPE when to paginate (boolean, always, auto, or never)
1098 (default: auto)
1098 (default: auto)
1099
1099
1100 Disabled extension commands:
1100 Disabled extension commands:
1101
1101
1102 $ ORGHGRCPATH=$HGRCPATH
1102 $ ORGHGRCPATH=$HGRCPATH
1103 $ HGRCPATH=
1103 $ HGRCPATH=
1104 $ export HGRCPATH
1104 $ export HGRCPATH
1105 $ hg help email
1105 $ hg help email
1106 'email' is provided by the following extension:
1106 'email' is provided by the following extension:
1107
1107
1108 patchbomb command to send changesets as (a series of) patch emails
1108 patchbomb command to send changesets as (a series of) patch emails
1109
1109
1110 (use 'hg help extensions' for information on enabling extensions)
1110 (use 'hg help extensions' for information on enabling extensions)
1111
1111
1112
1112
1113 $ hg qdel
1113 $ hg qdel
1114 hg: unknown command 'qdel'
1114 hg: unknown command 'qdel'
1115 'qdelete' is provided by the following extension:
1115 'qdelete' is provided by the following extension:
1116
1116
1117 mq manage a stack of patches
1117 mq manage a stack of patches
1118
1118
1119 (use 'hg help extensions' for information on enabling extensions)
1119 (use 'hg help extensions' for information on enabling extensions)
1120 [255]
1120 [255]
1121
1121
1122
1122
1123 $ hg churn
1123 $ hg churn
1124 hg: unknown command 'churn'
1124 hg: unknown command 'churn'
1125 'churn' is provided by the following extension:
1125 'churn' is provided by the following extension:
1126
1126
1127 churn command to display statistics about repository history
1127 churn command to display statistics about repository history
1128
1128
1129 (use 'hg help extensions' for information on enabling extensions)
1129 (use 'hg help extensions' for information on enabling extensions)
1130 [255]
1130 [255]
1131
1131
1132
1132
1133
1133
1134 Disabled extensions:
1134 Disabled extensions:
1135
1135
1136 $ hg help churn
1136 $ hg help churn
1137 churn extension - command to display statistics about repository history
1137 churn extension - command to display statistics about repository history
1138
1138
1139 (use 'hg help extensions' for information on enabling extensions)
1139 (use 'hg help extensions' for information on enabling extensions)
1140
1140
1141 $ hg help patchbomb
1141 $ hg help patchbomb
1142 patchbomb extension - command to send changesets as (a series of) patch emails
1142 patchbomb extension - command to send changesets as (a series of) patch emails
1143
1143
1144 The series is started off with a "[PATCH 0 of N]" introduction, which
1144 The series is started off with a "[PATCH 0 of N]" introduction, which
1145 describes the series as a whole.
1145 describes the series as a whole.
1146
1146
1147 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1147 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1148 line of the changeset description as the subject text. The message contains
1148 line of the changeset description as the subject text. The message contains
1149 two or three body parts:
1149 two or three body parts:
1150
1150
1151 - The changeset description.
1151 - The changeset description.
1152 - [Optional] The result of running diffstat on the patch.
1152 - [Optional] The result of running diffstat on the patch.
1153 - The patch itself, as generated by 'hg export'.
1153 - The patch itself, as generated by 'hg export'.
1154
1154
1155 Each message refers to the first in the series using the In-Reply-To and
1155 Each message refers to the first in the series using the In-Reply-To and
1156 References headers, so they will show up as a sequence in threaded mail and
1156 References headers, so they will show up as a sequence in threaded mail and
1157 news readers, and in mail archives.
1157 news readers, and in mail archives.
1158
1158
1159 To configure other defaults, add a section like this to your configuration
1159 To configure other defaults, add a section like this to your configuration
1160 file:
1160 file:
1161
1161
1162 [email]
1162 [email]
1163 from = My Name <my@email>
1163 from = My Name <my@email>
1164 to = recipient1, recipient2, ...
1164 to = recipient1, recipient2, ...
1165 cc = cc1, cc2, ...
1165 cc = cc1, cc2, ...
1166 bcc = bcc1, bcc2, ...
1166 bcc = bcc1, bcc2, ...
1167 reply-to = address1, address2, ...
1167 reply-to = address1, address2, ...
1168
1168
1169 Use "[patchbomb]" as configuration section name if you need to override global
1169 Use "[patchbomb]" as configuration section name if you need to override global
1170 "[email]" address settings.
1170 "[email]" address settings.
1171
1171
1172 Then you can use the 'hg email' command to mail a series of changesets as a
1172 Then you can use the 'hg email' command to mail a series of changesets as a
1173 patchbomb.
1173 patchbomb.
1174
1174
1175 You can also either configure the method option in the email section to be a
1175 You can also either configure the method option in the email section to be a
1176 sendmail compatible mailer or fill out the [smtp] section so that the
1176 sendmail compatible mailer or fill out the [smtp] section so that the
1177 patchbomb extension can automatically send patchbombs directly from the
1177 patchbomb extension can automatically send patchbombs directly from the
1178 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1178 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1179
1179
1180 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1180 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1181 supply one via configuration or the command line. You can override this to
1181 supply one via configuration or the command line. You can override this to
1182 never prompt by configuring an empty value:
1182 never prompt by configuring an empty value:
1183
1183
1184 [email]
1184 [email]
1185 cc =
1185 cc =
1186
1186
1187 You can control the default inclusion of an introduction message with the
1187 You can control the default inclusion of an introduction message with the
1188 "patchbomb.intro" configuration option. The configuration is always
1188 "patchbomb.intro" configuration option. The configuration is always
1189 overwritten by command line flags like --intro and --desc:
1189 overwritten by command line flags like --intro and --desc:
1190
1190
1191 [patchbomb]
1191 [patchbomb]
1192 intro=auto # include introduction message if more than 1 patch (default)
1192 intro=auto # include introduction message if more than 1 patch (default)
1193 intro=never # never include an introduction message
1193 intro=never # never include an introduction message
1194 intro=always # always include an introduction message
1194 intro=always # always include an introduction message
1195
1195
1196 You can specify a template for flags to be added in subject prefixes. Flags
1196 You can specify a template for flags to be added in subject prefixes. Flags
1197 specified by --flag option are exported as "{flags}" keyword:
1197 specified by --flag option are exported as "{flags}" keyword:
1198
1198
1199 [patchbomb]
1199 [patchbomb]
1200 flagtemplate = "{separate(' ',
1200 flagtemplate = "{separate(' ',
1201 ifeq(branch, 'default', '', branch|upper),
1201 ifeq(branch, 'default', '', branch|upper),
1202 flags)}"
1202 flags)}"
1203
1203
1204 You can set patchbomb to always ask for confirmation by setting
1204 You can set patchbomb to always ask for confirmation by setting
1205 "patchbomb.confirm" to true.
1205 "patchbomb.confirm" to true.
1206
1206
1207 (use 'hg help extensions' for information on enabling extensions)
1207 (use 'hg help extensions' for information on enabling extensions)
1208
1208
1209
1209
1210 Broken disabled extension and command:
1210 Broken disabled extension and command:
1211
1211
1212 $ mkdir hgext
1212 $ mkdir hgext
1213 $ echo > hgext/__init__.py
1213 $ echo > hgext/__init__.py
1214 $ cat > hgext/broken.py <<EOF
1214 $ cat > hgext/broken.py <<EOF
1215 > "broken extension'
1215 > "broken extension'
1216 > EOF
1216 > EOF
1217 $ cat > path.py <<EOF
1217 $ cat > path.py <<EOF
1218 > import os, sys
1218 > import os, sys
1219 > sys.path.insert(0, os.environ['HGEXTPATH'])
1219 > sys.path.insert(0, os.environ['HGEXTPATH'])
1220 > EOF
1220 > EOF
1221 $ HGEXTPATH=`pwd`
1221 $ HGEXTPATH=`pwd`
1222 $ export HGEXTPATH
1222 $ export HGEXTPATH
1223
1223
1224 $ hg --config extensions.path=./path.py help broken
1224 $ hg --config extensions.path=./path.py help broken
1225 broken extension - (no help text available)
1225 broken extension - (no help text available)
1226
1226
1227 (use 'hg help extensions' for information on enabling extensions)
1227 (use 'hg help extensions' for information on enabling extensions)
1228
1228
1229
1229
1230 $ cat > hgext/forest.py <<EOF
1230 $ cat > hgext/forest.py <<EOF
1231 > cmdtable = None
1231 > cmdtable = None
1232 > EOF
1232 > EOF
1233 $ hg --config extensions.path=./path.py help foo > /dev/null
1233 $ hg --config extensions.path=./path.py help foo > /dev/null
1234 warning: error finding commands in $TESTTMP/hgext/forest.py
1234 warning: error finding commands in $TESTTMP/hgext/forest.py
1235 abort: no such help topic: foo
1235 abort: no such help topic: foo
1236 (try 'hg help --keyword foo')
1236 (try 'hg help --keyword foo')
1237 [255]
1237 [255]
1238
1238
1239 $ cat > throw.py <<EOF
1239 $ cat > throw.py <<EOF
1240 > from mercurial import commands, registrar, util
1240 > from mercurial import commands, registrar, util
1241 > cmdtable = {}
1241 > cmdtable = {}
1242 > command = registrar.command(cmdtable)
1242 > command = registrar.command(cmdtable)
1243 > class Bogon(Exception): pass
1243 > class Bogon(Exception): pass
1244 > @command(b'throw', [], b'hg throw', norepo=True)
1244 > @command(b'throw', [], b'hg throw', norepo=True)
1245 > def throw(ui, **opts):
1245 > def throw(ui, **opts):
1246 > """throws an exception"""
1246 > """throws an exception"""
1247 > raise Bogon()
1247 > raise Bogon()
1248 > EOF
1248 > EOF
1249
1249
1250 No declared supported version, extension complains:
1250 No declared supported version, extension complains:
1251 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1251 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1252 ** Unknown exception encountered with possibly-broken third-party extension throw
1252 ** Unknown exception encountered with possibly-broken third-party extension throw
1253 ** which supports versions unknown of Mercurial.
1253 ** which supports versions unknown of Mercurial.
1254 ** Please disable throw and try your action again.
1254 ** Please disable throw and try your action again.
1255 ** If that fixes the bug please report it to the extension author.
1255 ** If that fixes the bug please report it to the extension author.
1256 ** Python * (glob)
1256 ** Python * (glob)
1257 ** Mercurial Distributed SCM * (glob)
1257 ** Mercurial Distributed SCM * (glob)
1258 ** Extensions loaded: throw
1258 ** Extensions loaded: throw
1259
1259
1260 empty declaration of supported version, extension complains:
1260 empty declaration of supported version, extension complains:
1261 $ echo "testedwith = ''" >> throw.py
1261 $ echo "testedwith = ''" >> throw.py
1262 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1262 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1263 ** Unknown exception encountered with possibly-broken third-party extension throw
1263 ** Unknown exception encountered with possibly-broken third-party extension throw
1264 ** which supports versions unknown of Mercurial.
1264 ** which supports versions unknown of Mercurial.
1265 ** Please disable throw and try your action again.
1265 ** Please disable throw and try your action again.
1266 ** If that fixes the bug please report it to the extension author.
1266 ** If that fixes the bug please report it to the extension author.
1267 ** Python * (glob)
1267 ** Python * (glob)
1268 ** Mercurial Distributed SCM (*) (glob)
1268 ** Mercurial Distributed SCM (*) (glob)
1269 ** Extensions loaded: throw
1269 ** Extensions loaded: throw
1270
1270
1271 If the extension specifies a buglink, show that:
1271 If the extension specifies a buglink, show that:
1272 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1272 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1273 $ rm -f throw.pyc throw.pyo
1273 $ rm -f throw.pyc throw.pyo
1274 $ rm -Rf __pycache__
1274 $ rm -Rf __pycache__
1275 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1275 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1276 ** Unknown exception encountered with possibly-broken third-party extension throw
1276 ** Unknown exception encountered with possibly-broken third-party extension throw
1277 ** which supports versions unknown of Mercurial.
1277 ** which supports versions unknown of Mercurial.
1278 ** Please disable throw and try your action again.
1278 ** Please disable throw and try your action again.
1279 ** If that fixes the bug please report it to http://example.com/bts
1279 ** If that fixes the bug please report it to http://example.com/bts
1280 ** Python * (glob)
1280 ** Python * (glob)
1281 ** Mercurial Distributed SCM (*) (glob)
1281 ** Mercurial Distributed SCM (*) (glob)
1282 ** Extensions loaded: throw
1282 ** Extensions loaded: throw
1283
1283
1284 If the extensions declare outdated versions, accuse the older extension first:
1284 If the extensions declare outdated versions, accuse the older extension first:
1285 $ echo "from mercurial import util" >> older.py
1285 $ echo "from mercurial import util" >> older.py
1286 $ echo "util.version = lambda:'2.2'" >> older.py
1286 $ echo "util.version = lambda:'2.2'" >> older.py
1287 $ echo "testedwith = b'1.9.3'" >> older.py
1287 $ echo "testedwith = b'1.9.3'" >> older.py
1288 $ echo "testedwith = b'2.1.1'" >> throw.py
1288 $ echo "testedwith = b'2.1.1'" >> throw.py
1289 $ rm -f throw.pyc throw.pyo
1289 $ rm -f throw.pyc throw.pyo
1290 $ rm -Rf __pycache__
1290 $ rm -Rf __pycache__
1291 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1291 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1292 > throw 2>&1 | egrep '^\*\*'
1292 > throw 2>&1 | egrep '^\*\*'
1293 ** Unknown exception encountered with possibly-broken third-party extension older
1293 ** Unknown exception encountered with possibly-broken third-party extension older
1294 ** which supports versions 1.9 of Mercurial.
1294 ** which supports versions 1.9 of Mercurial.
1295 ** Please disable older and try your action again.
1295 ** Please disable older and try your action again.
1296 ** If that fixes the bug please report it to the extension author.
1296 ** If that fixes the bug please report it to the extension author.
1297 ** Python * (glob)
1297 ** Python * (glob)
1298 ** Mercurial Distributed SCM (version 2.2)
1298 ** Mercurial Distributed SCM (version 2.2)
1299 ** Extensions loaded: throw, older
1299 ** Extensions loaded: throw, older
1300
1300
1301 One extension only tested with older, one only with newer versions:
1301 One extension only tested with older, one only with newer versions:
1302 $ echo "util.version = lambda:b'2.1'" >> older.py
1302 $ echo "util.version = lambda:b'2.1'" >> older.py
1303 $ rm -f older.pyc older.pyo
1303 $ rm -f older.pyc older.pyo
1304 $ rm -Rf __pycache__
1304 $ rm -Rf __pycache__
1305 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1305 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1306 > throw 2>&1 | egrep '^\*\*'
1306 > throw 2>&1 | egrep '^\*\*'
1307 ** Unknown exception encountered with possibly-broken third-party extension older
1307 ** Unknown exception encountered with possibly-broken third-party extension older
1308 ** which supports versions 1.9 of Mercurial.
1308 ** which supports versions 1.9 of Mercurial.
1309 ** Please disable older and try your action again.
1309 ** Please disable older and try your action again.
1310 ** If that fixes the bug please report it to the extension author.
1310 ** If that fixes the bug please report it to the extension author.
1311 ** Python * (glob)
1311 ** Python * (glob)
1312 ** Mercurial Distributed SCM (version 2.1)
1312 ** Mercurial Distributed SCM (version 2.1)
1313 ** Extensions loaded: throw, older
1313 ** Extensions loaded: throw, older
1314
1314
1315 Older extension is tested with current version, the other only with newer:
1315 Older extension is tested with current version, the other only with newer:
1316 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1316 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1317 $ rm -f older.pyc older.pyo
1317 $ rm -f older.pyc older.pyo
1318 $ rm -Rf __pycache__
1318 $ rm -Rf __pycache__
1319 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1319 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1320 > throw 2>&1 | egrep '^\*\*'
1320 > throw 2>&1 | egrep '^\*\*'
1321 ** Unknown exception encountered with possibly-broken third-party extension throw
1321 ** Unknown exception encountered with possibly-broken third-party extension throw
1322 ** which supports versions 2.1 of Mercurial.
1322 ** which supports versions 2.1 of Mercurial.
1323 ** Please disable throw and try your action again.
1323 ** Please disable throw and try your action again.
1324 ** If that fixes the bug please report it to http://example.com/bts
1324 ** If that fixes the bug please report it to http://example.com/bts
1325 ** Python * (glob)
1325 ** Python * (glob)
1326 ** Mercurial Distributed SCM (version 1.9.3)
1326 ** Mercurial Distributed SCM (version 1.9.3)
1327 ** Extensions loaded: throw, older
1327 ** Extensions loaded: throw, older
1328
1328
1329 Ability to point to a different point
1329 Ability to point to a different point
1330 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1330 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1331 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1331 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1332 ** unknown exception encountered, please report by visiting
1332 ** unknown exception encountered, please report by visiting
1333 ** Your Local Goat Lenders
1333 ** Your Local Goat Lenders
1334 ** Python * (glob)
1334 ** Python * (glob)
1335 ** Mercurial Distributed SCM (*) (glob)
1335 ** Mercurial Distributed SCM (*) (glob)
1336 ** Extensions loaded: throw, older
1336 ** Extensions loaded: throw, older
1337
1337
1338 Declare the version as supporting this hg version, show regular bts link:
1338 Declare the version as supporting this hg version, show regular bts link:
1339 $ hgver=`hg debuginstall -T '{hgver}'`
1339 $ hgver=`hg debuginstall -T '{hgver}'`
1340 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1340 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1341 $ if [ -z "$hgver" ]; then
1341 $ if [ -z "$hgver" ]; then
1342 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1342 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1343 > fi
1343 > fi
1344 $ rm -f throw.pyc throw.pyo
1344 $ rm -f throw.pyc throw.pyo
1345 $ rm -Rf __pycache__
1345 $ rm -Rf __pycache__
1346 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1346 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1347 ** unknown exception encountered, please report by visiting
1347 ** unknown exception encountered, please report by visiting
1348 ** https://mercurial-scm.org/wiki/BugTracker
1348 ** https://mercurial-scm.org/wiki/BugTracker
1349 ** Python * (glob)
1349 ** Python * (glob)
1350 ** Mercurial Distributed SCM (*) (glob)
1350 ** Mercurial Distributed SCM (*) (glob)
1351 ** Extensions loaded: throw
1351 ** Extensions loaded: throw
1352
1352
1353 Patch version is ignored during compatibility check
1353 Patch version is ignored during compatibility check
1354 $ echo "testedwith = b'3.2'" >> throw.py
1354 $ echo "testedwith = b'3.2'" >> throw.py
1355 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1355 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1356 $ rm -f throw.pyc throw.pyo
1356 $ rm -f throw.pyc throw.pyo
1357 $ rm -Rf __pycache__
1357 $ rm -Rf __pycache__
1358 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1358 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1359 ** unknown exception encountered, please report by visiting
1359 ** unknown exception encountered, please report by visiting
1360 ** https://mercurial-scm.org/wiki/BugTracker
1360 ** https://mercurial-scm.org/wiki/BugTracker
1361 ** Python * (glob)
1361 ** Python * (glob)
1362 ** Mercurial Distributed SCM (*) (glob)
1362 ** Mercurial Distributed SCM (*) (glob)
1363 ** Extensions loaded: throw
1363 ** Extensions loaded: throw
1364
1364
1365 Test version number support in 'hg version':
1365 Test version number support in 'hg version':
1366 $ echo '__version__ = (1, 2, 3)' >> throw.py
1366 $ echo '__version__ = (1, 2, 3)' >> throw.py
1367 $ rm -f throw.pyc throw.pyo
1367 $ rm -f throw.pyc throw.pyo
1368 $ rm -Rf __pycache__
1368 $ rm -Rf __pycache__
1369 $ hg version -v
1369 $ hg version -v
1370 Mercurial Distributed SCM (version *) (glob)
1370 Mercurial Distributed SCM (version *) (glob)
1371 (see https://mercurial-scm.org for more information)
1371 (see https://mercurial-scm.org for more information)
1372
1372
1373 Copyright (C) 2005-* Matt Mackall and others (glob)
1373 Copyright (C) 2005-* Matt Mackall and others (glob)
1374 This is free software; see the source for copying conditions. There is NO
1374 This is free software; see the source for copying conditions. There is NO
1375 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1375 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1376
1376
1377 Enabled extensions:
1377 Enabled extensions:
1378
1378
1379
1379
1380 $ hg version -v --config extensions.throw=throw.py
1380 $ hg version -v --config extensions.throw=throw.py
1381 Mercurial Distributed SCM (version *) (glob)
1381 Mercurial Distributed SCM (version *) (glob)
1382 (see https://mercurial-scm.org for more information)
1382 (see https://mercurial-scm.org for more information)
1383
1383
1384 Copyright (C) 2005-* Matt Mackall and others (glob)
1384 Copyright (C) 2005-* Matt Mackall and others (glob)
1385 This is free software; see the source for copying conditions. There is NO
1385 This is free software; see the source for copying conditions. There is NO
1386 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1386 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1387
1387
1388 Enabled extensions:
1388 Enabled extensions:
1389
1389
1390 throw external 1.2.3
1390 throw external 1.2.3
1391 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1391 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1392 $ rm -f throw.pyc throw.pyo
1392 $ rm -f throw.pyc throw.pyo
1393 $ rm -Rf __pycache__
1393 $ rm -Rf __pycache__
1394 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1394 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1395 Mercurial Distributed SCM (version *) (glob)
1395 Mercurial Distributed SCM (version *) (glob)
1396 (see https://mercurial-scm.org for more information)
1396 (see https://mercurial-scm.org for more information)
1397
1397
1398 Copyright (C) 2005-* Matt Mackall and others (glob)
1398 Copyright (C) 2005-* Matt Mackall and others (glob)
1399 This is free software; see the source for copying conditions. There is NO
1399 This is free software; see the source for copying conditions. There is NO
1400 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1400 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1401
1401
1402 Enabled extensions:
1402 Enabled extensions:
1403
1403
1404 throw external 1.twentythree
1404 throw external 1.twentythree
1405 strip internal
1405 strip internal
1406
1406
1407 $ hg version -q --config extensions.throw=throw.py
1407 $ hg version -q --config extensions.throw=throw.py
1408 Mercurial Distributed SCM (version *) (glob)
1408 Mercurial Distributed SCM (version *) (glob)
1409
1409
1410 Test template output:
1410 Test template output:
1411
1411
1412 $ hg version --config extensions.strip= -T'{extensions}'
1412 $ hg version --config extensions.strip= -T'{extensions}'
1413 strip
1413 strip
1414
1414
1415 Test JSON output of version:
1415 Test JSON output of version:
1416
1416
1417 $ hg version -Tjson
1417 $ hg version -Tjson
1418 [
1418 [
1419 {
1419 {
1420 "extensions": [],
1420 "extensions": [],
1421 "ver": "*" (glob)
1421 "ver": "*" (glob)
1422 }
1422 }
1423 ]
1423 ]
1424
1424
1425 $ hg version --config extensions.throw=throw.py -Tjson
1425 $ hg version --config extensions.throw=throw.py -Tjson
1426 [
1426 [
1427 {
1427 {
1428 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1428 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1429 "ver": "3.2.2"
1429 "ver": "3.2.2"
1430 }
1430 }
1431 ]
1431 ]
1432
1432
1433 $ hg version --config extensions.strip= -Tjson
1433 $ hg version --config extensions.strip= -Tjson
1434 [
1434 [
1435 {
1435 {
1436 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1436 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1437 "ver": "*" (glob)
1437 "ver": "*" (glob)
1438 }
1438 }
1439 ]
1439 ]
1440
1440
1441 Test template output of version:
1441 Test template output of version:
1442
1442
1443 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1443 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1444 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1444 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1445 throw 1.twentythree (external)
1445 throw 1.twentythree (external)
1446 strip (internal)
1446 strip (internal)
1447
1447
1448 Refuse to load extensions with minimum version requirements
1448 Refuse to load extensions with minimum version requirements
1449
1449
1450 $ cat > minversion1.py << EOF
1450 $ cat > minversion1.py << EOF
1451 > from mercurial import util
1451 > from mercurial import util
1452 > util.version = lambda: b'3.5.2'
1452 > util.version = lambda: b'3.5.2'
1453 > minimumhgversion = b'3.6'
1453 > minimumhgversion = b'3.6'
1454 > EOF
1454 > EOF
1455 $ hg --config extensions.minversion=minversion1.py version
1455 $ hg --config extensions.minversion=minversion1.py version
1456 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1456 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1457 Mercurial Distributed SCM (version 3.5.2)
1457 Mercurial Distributed SCM (version 3.5.2)
1458 (see https://mercurial-scm.org for more information)
1458 (see https://mercurial-scm.org for more information)
1459
1459
1460 Copyright (C) 2005-* Matt Mackall and others (glob)
1460 Copyright (C) 2005-* Matt Mackall and others (glob)
1461 This is free software; see the source for copying conditions. There is NO
1461 This is free software; see the source for copying conditions. There is NO
1462 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1462 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1463
1463
1464 $ cat > minversion2.py << EOF
1464 $ cat > minversion2.py << EOF
1465 > from mercurial import util
1465 > from mercurial import util
1466 > util.version = lambda: b'3.6'
1466 > util.version = lambda: b'3.6'
1467 > minimumhgversion = b'3.7'
1467 > minimumhgversion = b'3.7'
1468 > EOF
1468 > EOF
1469 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1469 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1470 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1470 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1471
1471
1472 Can load version that is only off by point release
1472 Can load version that is only off by point release
1473
1473
1474 $ cat > minversion2.py << EOF
1474 $ cat > minversion2.py << EOF
1475 > from mercurial import util
1475 > from mercurial import util
1476 > util.version = lambda: b'3.6.1'
1476 > util.version = lambda: b'3.6.1'
1477 > minimumhgversion = b'3.6'
1477 > minimumhgversion = b'3.6'
1478 > EOF
1478 > EOF
1479 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1479 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1480 [1]
1480 [1]
1481
1481
1482 Can load minimum version identical to current
1482 Can load minimum version identical to current
1483
1483
1484 $ cat > minversion3.py << EOF
1484 $ cat > minversion3.py << EOF
1485 > from mercurial import util
1485 > from mercurial import util
1486 > util.version = lambda: b'3.5'
1486 > util.version = lambda: b'3.5'
1487 > minimumhgversion = b'3.5'
1487 > minimumhgversion = b'3.5'
1488 > EOF
1488 > EOF
1489 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1489 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1490 [1]
1490 [1]
1491
1491
1492 Restore HGRCPATH
1492 Restore HGRCPATH
1493
1493
1494 $ HGRCPATH=$ORGHGRCPATH
1494 $ HGRCPATH=$ORGHGRCPATH
1495 $ export HGRCPATH
1495 $ export HGRCPATH
1496
1496
1497 Commands handling multiple repositories at a time should invoke only
1497 Commands handling multiple repositories at a time should invoke only
1498 "reposetup()" of extensions enabling in the target repository.
1498 "reposetup()" of extensions enabling in the target repository.
1499
1499
1500 $ mkdir reposetup-test
1500 $ mkdir reposetup-test
1501 $ cd reposetup-test
1501 $ cd reposetup-test
1502
1502
1503 $ cat > $TESTTMP/reposetuptest.py <<EOF
1503 $ cat > $TESTTMP/reposetuptest.py <<EOF
1504 > from mercurial import extensions
1504 > from mercurial import extensions
1505 > def reposetup(ui, repo):
1505 > def reposetup(ui, repo):
1506 > ui.write(b'reposetup() for %s\n' % (repo.root))
1506 > ui.write(b'reposetup() for %s\n' % (repo.root))
1507 > ui.flush()
1507 > ui.flush()
1508 > EOF
1508 > EOF
1509 $ hg init src
1509 $ hg init src
1510 $ echo a > src/a
1510 $ echo a > src/a
1511 $ hg -R src commit -Am '#0 at src/a'
1511 $ hg -R src commit -Am '#0 at src/a'
1512 adding a
1512 adding a
1513 $ echo '[extensions]' >> src/.hg/hgrc
1513 $ echo '[extensions]' >> src/.hg/hgrc
1514 $ echo '# enable extension locally' >> src/.hg/hgrc
1514 $ echo '# enable extension locally' >> src/.hg/hgrc
1515 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1515 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1516 $ hg -R src status
1516 $ hg -R src status
1517 reposetup() for $TESTTMP/reposetup-test/src
1517 reposetup() for $TESTTMP/reposetup-test/src
1518 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1518 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1519
1519
1520 $ hg --cwd src debugextensions
1521 reposetup() for $TESTTMP/reposetup-test/src
1522 dodo (untested!)
1523 dudu (untested!)
1524 mq
1525 reposetuptest (untested!)
1526 strip
1527
1520 $ hg clone -U src clone-dst1
1528 $ hg clone -U src clone-dst1
1521 reposetup() for $TESTTMP/reposetup-test/src
1529 reposetup() for $TESTTMP/reposetup-test/src
1522 $ hg init push-dst1
1530 $ hg init push-dst1
1523 $ hg -q -R src push push-dst1
1531 $ hg -q -R src push push-dst1
1524 reposetup() for $TESTTMP/reposetup-test/src
1532 reposetup() for $TESTTMP/reposetup-test/src
1525 $ hg init pull-src1
1533 $ hg init pull-src1
1526 $ hg -q -R pull-src1 pull src
1534 $ hg -q -R pull-src1 pull src
1527 reposetup() for $TESTTMP/reposetup-test/src
1535 reposetup() for $TESTTMP/reposetup-test/src
1528
1536
1529 $ cat <<EOF >> $HGRCPATH
1537 $ cat <<EOF >> $HGRCPATH
1530 > [extensions]
1538 > [extensions]
1531 > # disable extension globally and explicitly
1539 > # disable extension globally and explicitly
1532 > reposetuptest = !
1540 > reposetuptest = !
1533 > EOF
1541 > EOF
1534 $ hg clone -U src clone-dst2
1542 $ hg clone -U src clone-dst2
1535 reposetup() for $TESTTMP/reposetup-test/src
1543 reposetup() for $TESTTMP/reposetup-test/src
1536 $ hg init push-dst2
1544 $ hg init push-dst2
1537 $ hg -q -R src push push-dst2
1545 $ hg -q -R src push push-dst2
1538 reposetup() for $TESTTMP/reposetup-test/src
1546 reposetup() for $TESTTMP/reposetup-test/src
1539 $ hg init pull-src2
1547 $ hg init pull-src2
1540 $ hg -q -R pull-src2 pull src
1548 $ hg -q -R pull-src2 pull src
1541 reposetup() for $TESTTMP/reposetup-test/src
1549 reposetup() for $TESTTMP/reposetup-test/src
1542
1550
1543 $ cat <<EOF >> $HGRCPATH
1551 $ cat <<EOF >> $HGRCPATH
1544 > [extensions]
1552 > [extensions]
1545 > # enable extension globally
1553 > # enable extension globally
1546 > reposetuptest = $TESTTMP/reposetuptest.py
1554 > reposetuptest = $TESTTMP/reposetuptest.py
1547 > EOF
1555 > EOF
1548 $ hg clone -U src clone-dst3
1556 $ hg clone -U src clone-dst3
1549 reposetup() for $TESTTMP/reposetup-test/src
1557 reposetup() for $TESTTMP/reposetup-test/src
1550 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1558 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1551 $ hg init push-dst3
1559 $ hg init push-dst3
1552 reposetup() for $TESTTMP/reposetup-test/push-dst3
1560 reposetup() for $TESTTMP/reposetup-test/push-dst3
1553 $ hg -q -R src push push-dst3
1561 $ hg -q -R src push push-dst3
1554 reposetup() for $TESTTMP/reposetup-test/src
1562 reposetup() for $TESTTMP/reposetup-test/src
1555 reposetup() for $TESTTMP/reposetup-test/push-dst3
1563 reposetup() for $TESTTMP/reposetup-test/push-dst3
1556 $ hg init pull-src3
1564 $ hg init pull-src3
1557 reposetup() for $TESTTMP/reposetup-test/pull-src3
1565 reposetup() for $TESTTMP/reposetup-test/pull-src3
1558 $ hg -q -R pull-src3 pull src
1566 $ hg -q -R pull-src3 pull src
1559 reposetup() for $TESTTMP/reposetup-test/pull-src3
1567 reposetup() for $TESTTMP/reposetup-test/pull-src3
1560 reposetup() for $TESTTMP/reposetup-test/src
1568 reposetup() for $TESTTMP/reposetup-test/src
1561
1569
1562 $ echo '[extensions]' >> src/.hg/hgrc
1570 $ echo '[extensions]' >> src/.hg/hgrc
1563 $ echo '# disable extension locally' >> src/.hg/hgrc
1571 $ echo '# disable extension locally' >> src/.hg/hgrc
1564 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1572 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1565 $ hg clone -U src clone-dst4
1573 $ hg clone -U src clone-dst4
1566 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1574 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1567 $ hg init push-dst4
1575 $ hg init push-dst4
1568 reposetup() for $TESTTMP/reposetup-test/push-dst4
1576 reposetup() for $TESTTMP/reposetup-test/push-dst4
1569 $ hg -q -R src push push-dst4
1577 $ hg -q -R src push push-dst4
1570 reposetup() for $TESTTMP/reposetup-test/push-dst4
1578 reposetup() for $TESTTMP/reposetup-test/push-dst4
1571 $ hg init pull-src4
1579 $ hg init pull-src4
1572 reposetup() for $TESTTMP/reposetup-test/pull-src4
1580 reposetup() for $TESTTMP/reposetup-test/pull-src4
1573 $ hg -q -R pull-src4 pull src
1581 $ hg -q -R pull-src4 pull src
1574 reposetup() for $TESTTMP/reposetup-test/pull-src4
1582 reposetup() for $TESTTMP/reposetup-test/pull-src4
1575
1583
1576 disabling in command line overlays with all configuration
1584 disabling in command line overlays with all configuration
1577 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1585 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1578 $ hg --config extensions.reposetuptest=! init push-dst5
1586 $ hg --config extensions.reposetuptest=! init push-dst5
1579 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1587 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1580 $ hg --config extensions.reposetuptest=! init pull-src5
1588 $ hg --config extensions.reposetuptest=! init pull-src5
1581 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1589 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1582
1590
1583 $ cat <<EOF >> $HGRCPATH
1591 $ cat <<EOF >> $HGRCPATH
1584 > [extensions]
1592 > [extensions]
1585 > # disable extension globally and explicitly
1593 > # disable extension globally and explicitly
1586 > reposetuptest = !
1594 > reposetuptest = !
1587 > EOF
1595 > EOF
1588 $ hg init parent
1596 $ hg init parent
1589 $ hg init parent/sub1
1597 $ hg init parent/sub1
1590 $ echo 1 > parent/sub1/1
1598 $ echo 1 > parent/sub1/1
1591 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1599 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1592 adding 1
1600 adding 1
1593 $ hg init parent/sub2
1601 $ hg init parent/sub2
1594 $ hg init parent/sub2/sub21
1602 $ hg init parent/sub2/sub21
1595 $ echo 21 > parent/sub2/sub21/21
1603 $ echo 21 > parent/sub2/sub21/21
1596 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1604 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1597 adding 21
1605 adding 21
1598 $ cat > parent/sub2/.hgsub <<EOF
1606 $ cat > parent/sub2/.hgsub <<EOF
1599 > sub21 = sub21
1607 > sub21 = sub21
1600 > EOF
1608 > EOF
1601 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1609 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1602 adding .hgsub
1610 adding .hgsub
1603 $ hg init parent/sub3
1611 $ hg init parent/sub3
1604 $ echo 3 > parent/sub3/3
1612 $ echo 3 > parent/sub3/3
1605 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1613 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1606 adding 3
1614 adding 3
1607 $ cat > parent/.hgsub <<EOF
1615 $ cat > parent/.hgsub <<EOF
1608 > sub1 = sub1
1616 > sub1 = sub1
1609 > sub2 = sub2
1617 > sub2 = sub2
1610 > sub3 = sub3
1618 > sub3 = sub3
1611 > EOF
1619 > EOF
1612 $ hg -R parent commit -Am '#0 at parent'
1620 $ hg -R parent commit -Am '#0 at parent'
1613 adding .hgsub
1621 adding .hgsub
1614 $ echo '[extensions]' >> parent/.hg/hgrc
1622 $ echo '[extensions]' >> parent/.hg/hgrc
1615 $ echo '# enable extension locally' >> parent/.hg/hgrc
1623 $ echo '# enable extension locally' >> parent/.hg/hgrc
1616 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1624 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1617 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1625 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1618 $ hg -R parent status -S -A
1626 $ hg -R parent status -S -A
1619 reposetup() for $TESTTMP/reposetup-test/parent
1627 reposetup() for $TESTTMP/reposetup-test/parent
1620 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1628 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1621 C .hgsub
1629 C .hgsub
1622 C .hgsubstate
1630 C .hgsubstate
1623 C sub1/1
1631 C sub1/1
1624 C sub2/.hgsub
1632 C sub2/.hgsub
1625 C sub2/.hgsubstate
1633 C sub2/.hgsubstate
1626 C sub2/sub21/21
1634 C sub2/sub21/21
1627 C sub3/3
1635 C sub3/3
1628
1636
1629 $ cd ..
1637 $ cd ..
1630
1638
1631 Prohibit registration of commands that don't use @command (issue5137)
1639 Prohibit registration of commands that don't use @command (issue5137)
1632
1640
1633 $ hg init deprecated
1641 $ hg init deprecated
1634 $ cd deprecated
1642 $ cd deprecated
1635
1643
1636 $ cat <<EOF > deprecatedcmd.py
1644 $ cat <<EOF > deprecatedcmd.py
1637 > def deprecatedcmd(repo, ui):
1645 > def deprecatedcmd(repo, ui):
1638 > pass
1646 > pass
1639 > cmdtable = {
1647 > cmdtable = {
1640 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1648 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1641 > }
1649 > }
1642 > EOF
1650 > EOF
1643 $ cat <<EOF > .hg/hgrc
1651 $ cat <<EOF > .hg/hgrc
1644 > [extensions]
1652 > [extensions]
1645 > deprecatedcmd = `pwd`/deprecatedcmd.py
1653 > deprecatedcmd = `pwd`/deprecatedcmd.py
1646 > mq = !
1654 > mq = !
1647 > hgext.mq = !
1655 > hgext.mq = !
1648 > hgext/mq = !
1656 > hgext/mq = !
1649 > EOF
1657 > EOF
1650
1658
1651 $ hg deprecatedcmd > /dev/null
1659 $ hg deprecatedcmd > /dev/null
1652 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1660 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1653 *** (use @command decorator to register 'deprecatedcmd')
1661 *** (use @command decorator to register 'deprecatedcmd')
1654 hg: unknown command 'deprecatedcmd'
1662 hg: unknown command 'deprecatedcmd'
1655 [255]
1663 [255]
1656
1664
1657 the extension shouldn't be loaded at all so the mq works:
1665 the extension shouldn't be loaded at all so the mq works:
1658
1666
1659 $ hg qseries --config extensions.mq= > /dev/null
1667 $ hg qseries --config extensions.mq= > /dev/null
1660 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1668 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1661 *** (use @command decorator to register 'deprecatedcmd')
1669 *** (use @command decorator to register 'deprecatedcmd')
1662
1670
1663 $ cd ..
1671 $ cd ..
1664
1672
1665 Test synopsis and docstring extending
1673 Test synopsis and docstring extending
1666
1674
1667 $ hg init exthelp
1675 $ hg init exthelp
1668 $ cat > exthelp.py <<EOF
1676 $ cat > exthelp.py <<EOF
1669 > from mercurial import commands, extensions
1677 > from mercurial import commands, extensions
1670 > def exbookmarks(orig, *args, **opts):
1678 > def exbookmarks(orig, *args, **opts):
1671 > return orig(*args, **opts)
1679 > return orig(*args, **opts)
1672 > def uisetup(ui):
1680 > def uisetup(ui):
1673 > synopsis = ' GREPME [--foo] [-x]'
1681 > synopsis = ' GREPME [--foo] [-x]'
1674 > docstring = '''
1682 > docstring = '''
1675 > GREPME make sure that this is in the help!
1683 > GREPME make sure that this is in the help!
1676 > '''
1684 > '''
1677 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1685 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1678 > synopsis, docstring)
1686 > synopsis, docstring)
1679 > EOF
1687 > EOF
1680 $ abspath=`pwd`/exthelp.py
1688 $ abspath=`pwd`/exthelp.py
1681 $ echo '[extensions]' >> $HGRCPATH
1689 $ echo '[extensions]' >> $HGRCPATH
1682 $ echo "exthelp = $abspath" >> $HGRCPATH
1690 $ echo "exthelp = $abspath" >> $HGRCPATH
1683 $ cd exthelp
1691 $ cd exthelp
1684 $ hg help bookmarks | grep GREPME
1692 $ hg help bookmarks | grep GREPME
1685 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1693 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1686 GREPME make sure that this is in the help!
1694 GREPME make sure that this is in the help!
1687 $ cd ..
1695 $ cd ..
1688
1696
1689 Show deprecation warning for the use of cmdutil.command
1697 Show deprecation warning for the use of cmdutil.command
1690
1698
1691 $ cat > nonregistrar.py <<EOF
1699 $ cat > nonregistrar.py <<EOF
1692 > from mercurial import cmdutil
1700 > from mercurial import cmdutil
1693 > cmdtable = {}
1701 > cmdtable = {}
1694 > command = cmdutil.command(cmdtable)
1702 > command = cmdutil.command(cmdtable)
1695 > @command(b'foo', [], norepo=True)
1703 > @command(b'foo', [], norepo=True)
1696 > def foo(ui):
1704 > def foo(ui):
1697 > pass
1705 > pass
1698 > EOF
1706 > EOF
1699
1707
1700 Prohibit the use of unicode strings as the default value of options
1708 Prohibit the use of unicode strings as the default value of options
1701
1709
1702 $ hg init $TESTTMP/opt-unicode-default
1710 $ hg init $TESTTMP/opt-unicode-default
1703
1711
1704 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1712 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1705 > from mercurial import registrar
1713 > from mercurial import registrar
1706 > cmdtable = {}
1714 > cmdtable = {}
1707 > command = registrar.command(cmdtable)
1715 > command = registrar.command(cmdtable)
1708 > @command(b'dummy', [('', 'opt', u'value', u'help')], 'ext [OPTIONS]')
1716 > @command(b'dummy', [('', 'opt', u'value', u'help')], 'ext [OPTIONS]')
1709 > def ext(*args, **opts):
1717 > def ext(*args, **opts):
1710 > print(opts['opt'])
1718 > print(opts['opt'])
1711 > EOF
1719 > EOF
1712 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1720 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1713 > [extensions]
1721 > [extensions]
1714 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1722 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1715 > EOF
1723 > EOF
1716 $ hg -R $TESTTMP/opt-unicode-default dummy
1724 $ hg -R $TESTTMP/opt-unicode-default dummy
1717 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy
1725 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy
1718 *** (use b'' to make it byte string)
1726 *** (use b'' to make it byte string)
1719 hg: unknown command 'dummy'
1727 hg: unknown command 'dummy'
1720 (did you mean summary?)
1728 (did you mean summary?)
1721 [255]
1729 [255]
General Comments 0
You need to be logged in to leave comments. Login now