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