##// END OF EJS Templates
wireproto: define and implement HTTP handshake to upgrade protocol...
Gregory Szorc -
r37575:734515ac default
parent child Browse files
Show More
@@ -1,3090 +1,3098 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 re
18 import socket
18 import socket
19 import ssl
19 import ssl
20 import stat
20 import stat
21 import string
21 import string
22 import subprocess
22 import subprocess
23 import sys
23 import sys
24 import tempfile
24 import tempfile
25 import time
25 import time
26
26
27 from .i18n import _
27 from .i18n import _
28 from .node import (
28 from .node import (
29 bin,
29 bin,
30 hex,
30 hex,
31 nullhex,
31 nullhex,
32 nullid,
32 nullid,
33 nullrev,
33 nullrev,
34 short,
34 short,
35 )
35 )
36 from .thirdparty import (
37 cbor,
38 )
36 from . import (
39 from . import (
37 bundle2,
40 bundle2,
38 changegroup,
41 changegroup,
39 cmdutil,
42 cmdutil,
40 color,
43 color,
41 context,
44 context,
42 dagparser,
45 dagparser,
43 dagutil,
46 dagutil,
44 encoding,
47 encoding,
45 error,
48 error,
46 exchange,
49 exchange,
47 extensions,
50 extensions,
48 filemerge,
51 filemerge,
49 fileset,
52 fileset,
50 formatter,
53 formatter,
51 hg,
54 hg,
52 httppeer,
55 httppeer,
53 localrepo,
56 localrepo,
54 lock as lockmod,
57 lock as lockmod,
55 logcmdutil,
58 logcmdutil,
56 merge as mergemod,
59 merge as mergemod,
57 obsolete,
60 obsolete,
58 obsutil,
61 obsutil,
59 phases,
62 phases,
60 policy,
63 policy,
61 pvec,
64 pvec,
62 pycompat,
65 pycompat,
63 registrar,
66 registrar,
64 repair,
67 repair,
65 revlog,
68 revlog,
66 revset,
69 revset,
67 revsetlang,
70 revsetlang,
68 scmutil,
71 scmutil,
69 setdiscovery,
72 setdiscovery,
70 simplemerge,
73 simplemerge,
71 smartset,
74 smartset,
72 sshpeer,
75 sshpeer,
73 sslutil,
76 sslutil,
74 streamclone,
77 streamclone,
75 templater,
78 templater,
76 treediscovery,
79 treediscovery,
77 upgrade,
80 upgrade,
78 url as urlmod,
81 url as urlmod,
79 util,
82 util,
80 vfs as vfsmod,
83 vfs as vfsmod,
81 wireprotoframing,
84 wireprotoframing,
82 wireprotoserver,
85 wireprotoserver,
83 )
86 )
84 from .utils import (
87 from .utils import (
85 dateutil,
88 dateutil,
86 procutil,
89 procutil,
87 stringutil,
90 stringutil,
88 )
91 )
89
92
90 release = lockmod.release
93 release = lockmod.release
91
94
92 command = registrar.command()
95 command = registrar.command()
93
96
94 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
97 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
95 def debugancestor(ui, repo, *args):
98 def debugancestor(ui, repo, *args):
96 """find the ancestor revision of two revisions in a given index"""
99 """find the ancestor revision of two revisions in a given index"""
97 if len(args) == 3:
100 if len(args) == 3:
98 index, rev1, rev2 = args
101 index, rev1, rev2 = args
99 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
102 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
100 lookup = r.lookup
103 lookup = r.lookup
101 elif len(args) == 2:
104 elif len(args) == 2:
102 if not repo:
105 if not repo:
103 raise error.Abort(_('there is no Mercurial repository here '
106 raise error.Abort(_('there is no Mercurial repository here '
104 '(.hg not found)'))
107 '(.hg not found)'))
105 rev1, rev2 = args
108 rev1, rev2 = args
106 r = repo.changelog
109 r = repo.changelog
107 lookup = repo.lookup
110 lookup = repo.lookup
108 else:
111 else:
109 raise error.Abort(_('either two or three arguments required'))
112 raise error.Abort(_('either two or three arguments required'))
110 a = r.ancestor(lookup(rev1), lookup(rev2))
113 a = r.ancestor(lookup(rev1), lookup(rev2))
111 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
114 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
112
115
113 @command('debugapplystreamclonebundle', [], 'FILE')
116 @command('debugapplystreamclonebundle', [], 'FILE')
114 def debugapplystreamclonebundle(ui, repo, fname):
117 def debugapplystreamclonebundle(ui, repo, fname):
115 """apply a stream clone bundle file"""
118 """apply a stream clone bundle file"""
116 f = hg.openpath(ui, fname)
119 f = hg.openpath(ui, fname)
117 gen = exchange.readbundle(ui, f, fname)
120 gen = exchange.readbundle(ui, f, fname)
118 gen.apply(repo)
121 gen.apply(repo)
119
122
120 @command('debugbuilddag',
123 @command('debugbuilddag',
121 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
124 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
122 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
125 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
123 ('n', 'new-file', None, _('add new file at each rev'))],
126 ('n', 'new-file', None, _('add new file at each rev'))],
124 _('[OPTION]... [TEXT]'))
127 _('[OPTION]... [TEXT]'))
125 def debugbuilddag(ui, repo, text=None,
128 def debugbuilddag(ui, repo, text=None,
126 mergeable_file=False,
129 mergeable_file=False,
127 overwritten_file=False,
130 overwritten_file=False,
128 new_file=False):
131 new_file=False):
129 """builds a repo with a given DAG from scratch in the current empty repo
132 """builds a repo with a given DAG from scratch in the current empty repo
130
133
131 The description of the DAG is read from stdin if not given on the
134 The description of the DAG is read from stdin if not given on the
132 command line.
135 command line.
133
136
134 Elements:
137 Elements:
135
138
136 - "+n" is a linear run of n nodes based on the current default parent
139 - "+n" is a linear run of n nodes based on the current default parent
137 - "." is a single node based on the current default parent
140 - "." is a single node based on the current default parent
138 - "$" resets the default parent to null (implied at the start);
141 - "$" resets the default parent to null (implied at the start);
139 otherwise the default parent is always the last node created
142 otherwise the default parent is always the last node created
140 - "<p" sets the default parent to the backref p
143 - "<p" sets the default parent to the backref p
141 - "*p" is a fork at parent p, which is a backref
144 - "*p" is a fork at parent p, which is a backref
142 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
145 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
143 - "/p2" is a merge of the preceding node and p2
146 - "/p2" is a merge of the preceding node and p2
144 - ":tag" defines a local tag for the preceding node
147 - ":tag" defines a local tag for the preceding node
145 - "@branch" sets the named branch for subsequent nodes
148 - "@branch" sets the named branch for subsequent nodes
146 - "#...\\n" is a comment up to the end of the line
149 - "#...\\n" is a comment up to the end of the line
147
150
148 Whitespace between the above elements is ignored.
151 Whitespace between the above elements is ignored.
149
152
150 A backref is either
153 A backref is either
151
154
152 - a number n, which references the node curr-n, where curr is the current
155 - a number n, which references the node curr-n, where curr is the current
153 node, or
156 node, or
154 - the name of a local tag you placed earlier using ":tag", or
157 - the name of a local tag you placed earlier using ":tag", or
155 - empty to denote the default parent.
158 - empty to denote the default parent.
156
159
157 All string valued-elements are either strictly alphanumeric, or must
160 All string valued-elements are either strictly alphanumeric, or must
158 be enclosed in double quotes ("..."), with "\\" as escape character.
161 be enclosed in double quotes ("..."), with "\\" as escape character.
159 """
162 """
160
163
161 if text is None:
164 if text is None:
162 ui.status(_("reading DAG from stdin\n"))
165 ui.status(_("reading DAG from stdin\n"))
163 text = ui.fin.read()
166 text = ui.fin.read()
164
167
165 cl = repo.changelog
168 cl = repo.changelog
166 if len(cl) > 0:
169 if len(cl) > 0:
167 raise error.Abort(_('repository is not empty'))
170 raise error.Abort(_('repository is not empty'))
168
171
169 # determine number of revs in DAG
172 # determine number of revs in DAG
170 total = 0
173 total = 0
171 for type, data in dagparser.parsedag(text):
174 for type, data in dagparser.parsedag(text):
172 if type == 'n':
175 if type == 'n':
173 total += 1
176 total += 1
174
177
175 if mergeable_file:
178 if mergeable_file:
176 linesperrev = 2
179 linesperrev = 2
177 # make a file with k lines per rev
180 # make a file with k lines per rev
178 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
181 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
179 initialmergedlines.append("")
182 initialmergedlines.append("")
180
183
181 tags = []
184 tags = []
182
185
183 wlock = lock = tr = None
186 wlock = lock = tr = None
184 try:
187 try:
185 wlock = repo.wlock()
188 wlock = repo.wlock()
186 lock = repo.lock()
189 lock = repo.lock()
187 tr = repo.transaction("builddag")
190 tr = repo.transaction("builddag")
188
191
189 at = -1
192 at = -1
190 atbranch = 'default'
193 atbranch = 'default'
191 nodeids = []
194 nodeids = []
192 id = 0
195 id = 0
193 ui.progress(_('building'), id, unit=_('revisions'), total=total)
196 ui.progress(_('building'), id, unit=_('revisions'), total=total)
194 for type, data in dagparser.parsedag(text):
197 for type, data in dagparser.parsedag(text):
195 if type == 'n':
198 if type == 'n':
196 ui.note(('node %s\n' % pycompat.bytestr(data)))
199 ui.note(('node %s\n' % pycompat.bytestr(data)))
197 id, ps = data
200 id, ps = data
198
201
199 files = []
202 files = []
200 filecontent = {}
203 filecontent = {}
201
204
202 p2 = None
205 p2 = None
203 if mergeable_file:
206 if mergeable_file:
204 fn = "mf"
207 fn = "mf"
205 p1 = repo[ps[0]]
208 p1 = repo[ps[0]]
206 if len(ps) > 1:
209 if len(ps) > 1:
207 p2 = repo[ps[1]]
210 p2 = repo[ps[1]]
208 pa = p1.ancestor(p2)
211 pa = p1.ancestor(p2)
209 base, local, other = [x[fn].data() for x in (pa, p1,
212 base, local, other = [x[fn].data() for x in (pa, p1,
210 p2)]
213 p2)]
211 m3 = simplemerge.Merge3Text(base, local, other)
214 m3 = simplemerge.Merge3Text(base, local, other)
212 ml = [l.strip() for l in m3.merge_lines()]
215 ml = [l.strip() for l in m3.merge_lines()]
213 ml.append("")
216 ml.append("")
214 elif at > 0:
217 elif at > 0:
215 ml = p1[fn].data().split("\n")
218 ml = p1[fn].data().split("\n")
216 else:
219 else:
217 ml = initialmergedlines
220 ml = initialmergedlines
218 ml[id * linesperrev] += " r%i" % id
221 ml[id * linesperrev] += " r%i" % id
219 mergedtext = "\n".join(ml)
222 mergedtext = "\n".join(ml)
220 files.append(fn)
223 files.append(fn)
221 filecontent[fn] = mergedtext
224 filecontent[fn] = mergedtext
222
225
223 if overwritten_file:
226 if overwritten_file:
224 fn = "of"
227 fn = "of"
225 files.append(fn)
228 files.append(fn)
226 filecontent[fn] = "r%i\n" % id
229 filecontent[fn] = "r%i\n" % id
227
230
228 if new_file:
231 if new_file:
229 fn = "nf%i" % id
232 fn = "nf%i" % id
230 files.append(fn)
233 files.append(fn)
231 filecontent[fn] = "r%i\n" % id
234 filecontent[fn] = "r%i\n" % id
232 if len(ps) > 1:
235 if len(ps) > 1:
233 if not p2:
236 if not p2:
234 p2 = repo[ps[1]]
237 p2 = repo[ps[1]]
235 for fn in p2:
238 for fn in p2:
236 if fn.startswith("nf"):
239 if fn.startswith("nf"):
237 files.append(fn)
240 files.append(fn)
238 filecontent[fn] = p2[fn].data()
241 filecontent[fn] = p2[fn].data()
239
242
240 def fctxfn(repo, cx, path):
243 def fctxfn(repo, cx, path):
241 if path in filecontent:
244 if path in filecontent:
242 return context.memfilectx(repo, cx, path,
245 return context.memfilectx(repo, cx, path,
243 filecontent[path])
246 filecontent[path])
244 return None
247 return None
245
248
246 if len(ps) == 0 or ps[0] < 0:
249 if len(ps) == 0 or ps[0] < 0:
247 pars = [None, None]
250 pars = [None, None]
248 elif len(ps) == 1:
251 elif len(ps) == 1:
249 pars = [nodeids[ps[0]], None]
252 pars = [nodeids[ps[0]], None]
250 else:
253 else:
251 pars = [nodeids[p] for p in ps]
254 pars = [nodeids[p] for p in ps]
252 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
255 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
253 date=(id, 0),
256 date=(id, 0),
254 user="debugbuilddag",
257 user="debugbuilddag",
255 extra={'branch': atbranch})
258 extra={'branch': atbranch})
256 nodeid = repo.commitctx(cx)
259 nodeid = repo.commitctx(cx)
257 nodeids.append(nodeid)
260 nodeids.append(nodeid)
258 at = id
261 at = id
259 elif type == 'l':
262 elif type == 'l':
260 id, name = data
263 id, name = data
261 ui.note(('tag %s\n' % name))
264 ui.note(('tag %s\n' % name))
262 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
265 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
263 elif type == 'a':
266 elif type == 'a':
264 ui.note(('branch %s\n' % data))
267 ui.note(('branch %s\n' % data))
265 atbranch = data
268 atbranch = data
266 ui.progress(_('building'), id, unit=_('revisions'), total=total)
269 ui.progress(_('building'), id, unit=_('revisions'), total=total)
267 tr.close()
270 tr.close()
268
271
269 if tags:
272 if tags:
270 repo.vfs.write("localtags", "".join(tags))
273 repo.vfs.write("localtags", "".join(tags))
271 finally:
274 finally:
272 ui.progress(_('building'), None)
275 ui.progress(_('building'), None)
273 release(tr, lock, wlock)
276 release(tr, lock, wlock)
274
277
275 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
278 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
276 indent_string = ' ' * indent
279 indent_string = ' ' * indent
277 if all:
280 if all:
278 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
281 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
279 % indent_string)
282 % indent_string)
280
283
281 def showchunks(named):
284 def showchunks(named):
282 ui.write("\n%s%s\n" % (indent_string, named))
285 ui.write("\n%s%s\n" % (indent_string, named))
283 for deltadata in gen.deltaiter():
286 for deltadata in gen.deltaiter():
284 node, p1, p2, cs, deltabase, delta, flags = deltadata
287 node, p1, p2, cs, deltabase, delta, flags = deltadata
285 ui.write("%s%s %s %s %s %s %d\n" %
288 ui.write("%s%s %s %s %s %s %d\n" %
286 (indent_string, hex(node), hex(p1), hex(p2),
289 (indent_string, hex(node), hex(p1), hex(p2),
287 hex(cs), hex(deltabase), len(delta)))
290 hex(cs), hex(deltabase), len(delta)))
288
291
289 chunkdata = gen.changelogheader()
292 chunkdata = gen.changelogheader()
290 showchunks("changelog")
293 showchunks("changelog")
291 chunkdata = gen.manifestheader()
294 chunkdata = gen.manifestheader()
292 showchunks("manifest")
295 showchunks("manifest")
293 for chunkdata in iter(gen.filelogheader, {}):
296 for chunkdata in iter(gen.filelogheader, {}):
294 fname = chunkdata['filename']
297 fname = chunkdata['filename']
295 showchunks(fname)
298 showchunks(fname)
296 else:
299 else:
297 if isinstance(gen, bundle2.unbundle20):
300 if isinstance(gen, bundle2.unbundle20):
298 raise error.Abort(_('use debugbundle2 for this file'))
301 raise error.Abort(_('use debugbundle2 for this file'))
299 chunkdata = gen.changelogheader()
302 chunkdata = gen.changelogheader()
300 for deltadata in gen.deltaiter():
303 for deltadata in gen.deltaiter():
301 node, p1, p2, cs, deltabase, delta, flags = deltadata
304 node, p1, p2, cs, deltabase, delta, flags = deltadata
302 ui.write("%s%s\n" % (indent_string, hex(node)))
305 ui.write("%s%s\n" % (indent_string, hex(node)))
303
306
304 def _debugobsmarkers(ui, part, indent=0, **opts):
307 def _debugobsmarkers(ui, part, indent=0, **opts):
305 """display version and markers contained in 'data'"""
308 """display version and markers contained in 'data'"""
306 opts = pycompat.byteskwargs(opts)
309 opts = pycompat.byteskwargs(opts)
307 data = part.read()
310 data = part.read()
308 indent_string = ' ' * indent
311 indent_string = ' ' * indent
309 try:
312 try:
310 version, markers = obsolete._readmarkers(data)
313 version, markers = obsolete._readmarkers(data)
311 except error.UnknownVersion as exc:
314 except error.UnknownVersion as exc:
312 msg = "%sunsupported version: %s (%d bytes)\n"
315 msg = "%sunsupported version: %s (%d bytes)\n"
313 msg %= indent_string, exc.version, len(data)
316 msg %= indent_string, exc.version, len(data)
314 ui.write(msg)
317 ui.write(msg)
315 else:
318 else:
316 msg = "%sversion: %d (%d bytes)\n"
319 msg = "%sversion: %d (%d bytes)\n"
317 msg %= indent_string, version, len(data)
320 msg %= indent_string, version, len(data)
318 ui.write(msg)
321 ui.write(msg)
319 fm = ui.formatter('debugobsolete', opts)
322 fm = ui.formatter('debugobsolete', opts)
320 for rawmarker in sorted(markers):
323 for rawmarker in sorted(markers):
321 m = obsutil.marker(None, rawmarker)
324 m = obsutil.marker(None, rawmarker)
322 fm.startitem()
325 fm.startitem()
323 fm.plain(indent_string)
326 fm.plain(indent_string)
324 cmdutil.showmarker(fm, m)
327 cmdutil.showmarker(fm, m)
325 fm.end()
328 fm.end()
326
329
327 def _debugphaseheads(ui, data, indent=0):
330 def _debugphaseheads(ui, data, indent=0):
328 """display version and markers contained in 'data'"""
331 """display version and markers contained in 'data'"""
329 indent_string = ' ' * indent
332 indent_string = ' ' * indent
330 headsbyphase = phases.binarydecode(data)
333 headsbyphase = phases.binarydecode(data)
331 for phase in phases.allphases:
334 for phase in phases.allphases:
332 for head in headsbyphase[phase]:
335 for head in headsbyphase[phase]:
333 ui.write(indent_string)
336 ui.write(indent_string)
334 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
337 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
335
338
336 def _quasirepr(thing):
339 def _quasirepr(thing):
337 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
340 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
338 return '{%s}' % (
341 return '{%s}' % (
339 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
342 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
340 return pycompat.bytestr(repr(thing))
343 return pycompat.bytestr(repr(thing))
341
344
342 def _debugbundle2(ui, gen, all=None, **opts):
345 def _debugbundle2(ui, gen, all=None, **opts):
343 """lists the contents of a bundle2"""
346 """lists the contents of a bundle2"""
344 if not isinstance(gen, bundle2.unbundle20):
347 if not isinstance(gen, bundle2.unbundle20):
345 raise error.Abort(_('not a bundle2 file'))
348 raise error.Abort(_('not a bundle2 file'))
346 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
349 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
347 parttypes = opts.get(r'part_type', [])
350 parttypes = opts.get(r'part_type', [])
348 for part in gen.iterparts():
351 for part in gen.iterparts():
349 if parttypes and part.type not in parttypes:
352 if parttypes and part.type not in parttypes:
350 continue
353 continue
351 ui.write('%s -- %s\n' % (part.type, _quasirepr(part.params)))
354 ui.write('%s -- %s\n' % (part.type, _quasirepr(part.params)))
352 if part.type == 'changegroup':
355 if part.type == 'changegroup':
353 version = part.params.get('version', '01')
356 version = part.params.get('version', '01')
354 cg = changegroup.getunbundler(version, part, 'UN')
357 cg = changegroup.getunbundler(version, part, 'UN')
355 if not ui.quiet:
358 if not ui.quiet:
356 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
359 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
357 if part.type == 'obsmarkers':
360 if part.type == 'obsmarkers':
358 if not ui.quiet:
361 if not ui.quiet:
359 _debugobsmarkers(ui, part, indent=4, **opts)
362 _debugobsmarkers(ui, part, indent=4, **opts)
360 if part.type == 'phase-heads':
363 if part.type == 'phase-heads':
361 if not ui.quiet:
364 if not ui.quiet:
362 _debugphaseheads(ui, part, indent=4)
365 _debugphaseheads(ui, part, indent=4)
363
366
364 @command('debugbundle',
367 @command('debugbundle',
365 [('a', 'all', None, _('show all details')),
368 [('a', 'all', None, _('show all details')),
366 ('', 'part-type', [], _('show only the named part type')),
369 ('', 'part-type', [], _('show only the named part type')),
367 ('', 'spec', None, _('print the bundlespec of the bundle'))],
370 ('', 'spec', None, _('print the bundlespec of the bundle'))],
368 _('FILE'),
371 _('FILE'),
369 norepo=True)
372 norepo=True)
370 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
373 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
371 """lists the contents of a bundle"""
374 """lists the contents of a bundle"""
372 with hg.openpath(ui, bundlepath) as f:
375 with hg.openpath(ui, bundlepath) as f:
373 if spec:
376 if spec:
374 spec = exchange.getbundlespec(ui, f)
377 spec = exchange.getbundlespec(ui, f)
375 ui.write('%s\n' % spec)
378 ui.write('%s\n' % spec)
376 return
379 return
377
380
378 gen = exchange.readbundle(ui, f, bundlepath)
381 gen = exchange.readbundle(ui, f, bundlepath)
379 if isinstance(gen, bundle2.unbundle20):
382 if isinstance(gen, bundle2.unbundle20):
380 return _debugbundle2(ui, gen, all=all, **opts)
383 return _debugbundle2(ui, gen, all=all, **opts)
381 _debugchangegroup(ui, gen, all=all, **opts)
384 _debugchangegroup(ui, gen, all=all, **opts)
382
385
383 @command('debugcapabilities',
386 @command('debugcapabilities',
384 [], _('PATH'),
387 [], _('PATH'),
385 norepo=True)
388 norepo=True)
386 def debugcapabilities(ui, path, **opts):
389 def debugcapabilities(ui, path, **opts):
387 """lists the capabilities of a remote peer"""
390 """lists the capabilities of a remote peer"""
388 opts = pycompat.byteskwargs(opts)
391 opts = pycompat.byteskwargs(opts)
389 peer = hg.peer(ui, opts, path)
392 peer = hg.peer(ui, opts, path)
390 caps = peer.capabilities()
393 caps = peer.capabilities()
391 ui.write(('Main capabilities:\n'))
394 ui.write(('Main capabilities:\n'))
392 for c in sorted(caps):
395 for c in sorted(caps):
393 ui.write((' %s\n') % c)
396 ui.write((' %s\n') % c)
394 b2caps = bundle2.bundle2caps(peer)
397 b2caps = bundle2.bundle2caps(peer)
395 if b2caps:
398 if b2caps:
396 ui.write(('Bundle2 capabilities:\n'))
399 ui.write(('Bundle2 capabilities:\n'))
397 for key, values in sorted(b2caps.iteritems()):
400 for key, values in sorted(b2caps.iteritems()):
398 ui.write((' %s\n') % key)
401 ui.write((' %s\n') % key)
399 for v in values:
402 for v in values:
400 ui.write((' %s\n') % v)
403 ui.write((' %s\n') % v)
401
404
402 @command('debugcheckstate', [], '')
405 @command('debugcheckstate', [], '')
403 def debugcheckstate(ui, repo):
406 def debugcheckstate(ui, repo):
404 """validate the correctness of the current dirstate"""
407 """validate the correctness of the current dirstate"""
405 parent1, parent2 = repo.dirstate.parents()
408 parent1, parent2 = repo.dirstate.parents()
406 m1 = repo[parent1].manifest()
409 m1 = repo[parent1].manifest()
407 m2 = repo[parent2].manifest()
410 m2 = repo[parent2].manifest()
408 errors = 0
411 errors = 0
409 for f in repo.dirstate:
412 for f in repo.dirstate:
410 state = repo.dirstate[f]
413 state = repo.dirstate[f]
411 if state in "nr" and f not in m1:
414 if state in "nr" and f not in m1:
412 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
415 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
413 errors += 1
416 errors += 1
414 if state in "a" and f in m1:
417 if state in "a" and f in m1:
415 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
418 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
416 errors += 1
419 errors += 1
417 if state in "m" and f not in m1 and f not in m2:
420 if state in "m" and f not in m1 and f not in m2:
418 ui.warn(_("%s in state %s, but not in either manifest\n") %
421 ui.warn(_("%s in state %s, but not in either manifest\n") %
419 (f, state))
422 (f, state))
420 errors += 1
423 errors += 1
421 for f in m1:
424 for f in m1:
422 state = repo.dirstate[f]
425 state = repo.dirstate[f]
423 if state not in "nrm":
426 if state not in "nrm":
424 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
427 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
425 errors += 1
428 errors += 1
426 if errors:
429 if errors:
427 error = _(".hg/dirstate inconsistent with current parent's manifest")
430 error = _(".hg/dirstate inconsistent with current parent's manifest")
428 raise error.Abort(error)
431 raise error.Abort(error)
429
432
430 @command('debugcolor',
433 @command('debugcolor',
431 [('', 'style', None, _('show all configured styles'))],
434 [('', 'style', None, _('show all configured styles'))],
432 'hg debugcolor')
435 'hg debugcolor')
433 def debugcolor(ui, repo, **opts):
436 def debugcolor(ui, repo, **opts):
434 """show available color, effects or style"""
437 """show available color, effects or style"""
435 ui.write(('color mode: %s\n') % ui._colormode)
438 ui.write(('color mode: %s\n') % ui._colormode)
436 if opts.get(r'style'):
439 if opts.get(r'style'):
437 return _debugdisplaystyle(ui)
440 return _debugdisplaystyle(ui)
438 else:
441 else:
439 return _debugdisplaycolor(ui)
442 return _debugdisplaycolor(ui)
440
443
441 def _debugdisplaycolor(ui):
444 def _debugdisplaycolor(ui):
442 ui = ui.copy()
445 ui = ui.copy()
443 ui._styles.clear()
446 ui._styles.clear()
444 for effect in color._activeeffects(ui).keys():
447 for effect in color._activeeffects(ui).keys():
445 ui._styles[effect] = effect
448 ui._styles[effect] = effect
446 if ui._terminfoparams:
449 if ui._terminfoparams:
447 for k, v in ui.configitems('color'):
450 for k, v in ui.configitems('color'):
448 if k.startswith('color.'):
451 if k.startswith('color.'):
449 ui._styles[k] = k[6:]
452 ui._styles[k] = k[6:]
450 elif k.startswith('terminfo.'):
453 elif k.startswith('terminfo.'):
451 ui._styles[k] = k[9:]
454 ui._styles[k] = k[9:]
452 ui.write(_('available colors:\n'))
455 ui.write(_('available colors:\n'))
453 # sort label with a '_' after the other to group '_background' entry.
456 # sort label with a '_' after the other to group '_background' entry.
454 items = sorted(ui._styles.items(),
457 items = sorted(ui._styles.items(),
455 key=lambda i: ('_' in i[0], i[0], i[1]))
458 key=lambda i: ('_' in i[0], i[0], i[1]))
456 for colorname, label in items:
459 for colorname, label in items:
457 ui.write(('%s\n') % colorname, label=label)
460 ui.write(('%s\n') % colorname, label=label)
458
461
459 def _debugdisplaystyle(ui):
462 def _debugdisplaystyle(ui):
460 ui.write(_('available style:\n'))
463 ui.write(_('available style:\n'))
461 width = max(len(s) for s in ui._styles)
464 width = max(len(s) for s in ui._styles)
462 for label, effects in sorted(ui._styles.items()):
465 for label, effects in sorted(ui._styles.items()):
463 ui.write('%s' % label, label=label)
466 ui.write('%s' % label, label=label)
464 if effects:
467 if effects:
465 # 50
468 # 50
466 ui.write(': ')
469 ui.write(': ')
467 ui.write(' ' * (max(0, width - len(label))))
470 ui.write(' ' * (max(0, width - len(label))))
468 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
471 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
469 ui.write('\n')
472 ui.write('\n')
470
473
471 @command('debugcreatestreamclonebundle', [], 'FILE')
474 @command('debugcreatestreamclonebundle', [], 'FILE')
472 def debugcreatestreamclonebundle(ui, repo, fname):
475 def debugcreatestreamclonebundle(ui, repo, fname):
473 """create a stream clone bundle file
476 """create a stream clone bundle file
474
477
475 Stream bundles are special bundles that are essentially archives of
478 Stream bundles are special bundles that are essentially archives of
476 revlog files. They are commonly used for cloning very quickly.
479 revlog files. They are commonly used for cloning very quickly.
477 """
480 """
478 # TODO we may want to turn this into an abort when this functionality
481 # TODO we may want to turn this into an abort when this functionality
479 # is moved into `hg bundle`.
482 # is moved into `hg bundle`.
480 if phases.hassecret(repo):
483 if phases.hassecret(repo):
481 ui.warn(_('(warning: stream clone bundle will contain secret '
484 ui.warn(_('(warning: stream clone bundle will contain secret '
482 'revisions)\n'))
485 'revisions)\n'))
483
486
484 requirements, gen = streamclone.generatebundlev1(repo)
487 requirements, gen = streamclone.generatebundlev1(repo)
485 changegroup.writechunks(ui, gen, fname)
488 changegroup.writechunks(ui, gen, fname)
486
489
487 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
490 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
488
491
489 @command('debugdag',
492 @command('debugdag',
490 [('t', 'tags', None, _('use tags as labels')),
493 [('t', 'tags', None, _('use tags as labels')),
491 ('b', 'branches', None, _('annotate with branch names')),
494 ('b', 'branches', None, _('annotate with branch names')),
492 ('', 'dots', None, _('use dots for runs')),
495 ('', 'dots', None, _('use dots for runs')),
493 ('s', 'spaces', None, _('separate elements by spaces'))],
496 ('s', 'spaces', None, _('separate elements by spaces'))],
494 _('[OPTION]... [FILE [REV]...]'),
497 _('[OPTION]... [FILE [REV]...]'),
495 optionalrepo=True)
498 optionalrepo=True)
496 def debugdag(ui, repo, file_=None, *revs, **opts):
499 def debugdag(ui, repo, file_=None, *revs, **opts):
497 """format the changelog or an index DAG as a concise textual description
500 """format the changelog or an index DAG as a concise textual description
498
501
499 If you pass a revlog index, the revlog's DAG is emitted. If you list
502 If you pass a revlog index, the revlog's DAG is emitted. If you list
500 revision numbers, they get labeled in the output as rN.
503 revision numbers, they get labeled in the output as rN.
501
504
502 Otherwise, the changelog DAG of the current repo is emitted.
505 Otherwise, the changelog DAG of the current repo is emitted.
503 """
506 """
504 spaces = opts.get(r'spaces')
507 spaces = opts.get(r'spaces')
505 dots = opts.get(r'dots')
508 dots = opts.get(r'dots')
506 if file_:
509 if file_:
507 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
510 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
508 file_)
511 file_)
509 revs = set((int(r) for r in revs))
512 revs = set((int(r) for r in revs))
510 def events():
513 def events():
511 for r in rlog:
514 for r in rlog:
512 yield 'n', (r, list(p for p in rlog.parentrevs(r)
515 yield 'n', (r, list(p for p in rlog.parentrevs(r)
513 if p != -1))
516 if p != -1))
514 if r in revs:
517 if r in revs:
515 yield 'l', (r, "r%i" % r)
518 yield 'l', (r, "r%i" % r)
516 elif repo:
519 elif repo:
517 cl = repo.changelog
520 cl = repo.changelog
518 tags = opts.get(r'tags')
521 tags = opts.get(r'tags')
519 branches = opts.get(r'branches')
522 branches = opts.get(r'branches')
520 if tags:
523 if tags:
521 labels = {}
524 labels = {}
522 for l, n in repo.tags().items():
525 for l, n in repo.tags().items():
523 labels.setdefault(cl.rev(n), []).append(l)
526 labels.setdefault(cl.rev(n), []).append(l)
524 def events():
527 def events():
525 b = "default"
528 b = "default"
526 for r in cl:
529 for r in cl:
527 if branches:
530 if branches:
528 newb = cl.read(cl.node(r))[5]['branch']
531 newb = cl.read(cl.node(r))[5]['branch']
529 if newb != b:
532 if newb != b:
530 yield 'a', newb
533 yield 'a', newb
531 b = newb
534 b = newb
532 yield 'n', (r, list(p for p in cl.parentrevs(r)
535 yield 'n', (r, list(p for p in cl.parentrevs(r)
533 if p != -1))
536 if p != -1))
534 if tags:
537 if tags:
535 ls = labels.get(r)
538 ls = labels.get(r)
536 if ls:
539 if ls:
537 for l in ls:
540 for l in ls:
538 yield 'l', (r, l)
541 yield 'l', (r, l)
539 else:
542 else:
540 raise error.Abort(_('need repo for changelog dag'))
543 raise error.Abort(_('need repo for changelog dag'))
541
544
542 for line in dagparser.dagtextlines(events(),
545 for line in dagparser.dagtextlines(events(),
543 addspaces=spaces,
546 addspaces=spaces,
544 wraplabels=True,
547 wraplabels=True,
545 wrapannotations=True,
548 wrapannotations=True,
546 wrapnonlinear=dots,
549 wrapnonlinear=dots,
547 usedots=dots,
550 usedots=dots,
548 maxlinewidth=70):
551 maxlinewidth=70):
549 ui.write(line)
552 ui.write(line)
550 ui.write("\n")
553 ui.write("\n")
551
554
552 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
555 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
553 def debugdata(ui, repo, file_, rev=None, **opts):
556 def debugdata(ui, repo, file_, rev=None, **opts):
554 """dump the contents of a data file revision"""
557 """dump the contents of a data file revision"""
555 opts = pycompat.byteskwargs(opts)
558 opts = pycompat.byteskwargs(opts)
556 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
559 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
557 if rev is not None:
560 if rev is not None:
558 raise error.CommandError('debugdata', _('invalid arguments'))
561 raise error.CommandError('debugdata', _('invalid arguments'))
559 file_, rev = None, file_
562 file_, rev = None, file_
560 elif rev is None:
563 elif rev is None:
561 raise error.CommandError('debugdata', _('invalid arguments'))
564 raise error.CommandError('debugdata', _('invalid arguments'))
562 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
565 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
563 try:
566 try:
564 ui.write(r.revision(r.lookup(rev), raw=True))
567 ui.write(r.revision(r.lookup(rev), raw=True))
565 except KeyError:
568 except KeyError:
566 raise error.Abort(_('invalid revision identifier %s') % rev)
569 raise error.Abort(_('invalid revision identifier %s') % rev)
567
570
568 @command('debugdate',
571 @command('debugdate',
569 [('e', 'extended', None, _('try extended date formats'))],
572 [('e', 'extended', None, _('try extended date formats'))],
570 _('[-e] DATE [RANGE]'),
573 _('[-e] DATE [RANGE]'),
571 norepo=True, optionalrepo=True)
574 norepo=True, optionalrepo=True)
572 def debugdate(ui, date, range=None, **opts):
575 def debugdate(ui, date, range=None, **opts):
573 """parse and display a date"""
576 """parse and display a date"""
574 if opts[r"extended"]:
577 if opts[r"extended"]:
575 d = dateutil.parsedate(date, util.extendeddateformats)
578 d = dateutil.parsedate(date, util.extendeddateformats)
576 else:
579 else:
577 d = dateutil.parsedate(date)
580 d = dateutil.parsedate(date)
578 ui.write(("internal: %d %d\n") % d)
581 ui.write(("internal: %d %d\n") % d)
579 ui.write(("standard: %s\n") % dateutil.datestr(d))
582 ui.write(("standard: %s\n") % dateutil.datestr(d))
580 if range:
583 if range:
581 m = dateutil.matchdate(range)
584 m = dateutil.matchdate(range)
582 ui.write(("match: %s\n") % m(d[0]))
585 ui.write(("match: %s\n") % m(d[0]))
583
586
584 @command('debugdeltachain',
587 @command('debugdeltachain',
585 cmdutil.debugrevlogopts + cmdutil.formatteropts,
588 cmdutil.debugrevlogopts + cmdutil.formatteropts,
586 _('-c|-m|FILE'),
589 _('-c|-m|FILE'),
587 optionalrepo=True)
590 optionalrepo=True)
588 def debugdeltachain(ui, repo, file_=None, **opts):
591 def debugdeltachain(ui, repo, file_=None, **opts):
589 """dump information about delta chains in a revlog
592 """dump information about delta chains in a revlog
590
593
591 Output can be templatized. Available template keywords are:
594 Output can be templatized. Available template keywords are:
592
595
593 :``rev``: revision number
596 :``rev``: revision number
594 :``chainid``: delta chain identifier (numbered by unique base)
597 :``chainid``: delta chain identifier (numbered by unique base)
595 :``chainlen``: delta chain length to this revision
598 :``chainlen``: delta chain length to this revision
596 :``prevrev``: previous revision in delta chain
599 :``prevrev``: previous revision in delta chain
597 :``deltatype``: role of delta / how it was computed
600 :``deltatype``: role of delta / how it was computed
598 :``compsize``: compressed size of revision
601 :``compsize``: compressed size of revision
599 :``uncompsize``: uncompressed size of revision
602 :``uncompsize``: uncompressed size of revision
600 :``chainsize``: total size of compressed revisions in chain
603 :``chainsize``: total size of compressed revisions in chain
601 :``chainratio``: total chain size divided by uncompressed revision size
604 :``chainratio``: total chain size divided by uncompressed revision size
602 (new delta chains typically start at ratio 2.00)
605 (new delta chains typically start at ratio 2.00)
603 :``lindist``: linear distance from base revision in delta chain to end
606 :``lindist``: linear distance from base revision in delta chain to end
604 of this revision
607 of this revision
605 :``extradist``: total size of revisions not part of this delta chain from
608 :``extradist``: total size of revisions not part of this delta chain from
606 base of delta chain to end of this revision; a measurement
609 base of delta chain to end of this revision; a measurement
607 of how much extra data we need to read/seek across to read
610 of how much extra data we need to read/seek across to read
608 the delta chain for this revision
611 the delta chain for this revision
609 :``extraratio``: extradist divided by chainsize; another representation of
612 :``extraratio``: extradist divided by chainsize; another representation of
610 how much unrelated data is needed to load this delta chain
613 how much unrelated data is needed to load this delta chain
611
614
612 If the repository is configured to use the sparse read, additional keywords
615 If the repository is configured to use the sparse read, additional keywords
613 are available:
616 are available:
614
617
615 :``readsize``: total size of data read from the disk for a revision
618 :``readsize``: total size of data read from the disk for a revision
616 (sum of the sizes of all the blocks)
619 (sum of the sizes of all the blocks)
617 :``largestblock``: size of the largest block of data read from the disk
620 :``largestblock``: size of the largest block of data read from the disk
618 :``readdensity``: density of useful bytes in the data read from the disk
621 :``readdensity``: density of useful bytes in the data read from the disk
619 :``srchunks``: in how many data hunks the whole revision would be read
622 :``srchunks``: in how many data hunks the whole revision would be read
620
623
621 The sparse read can be enabled with experimental.sparse-read = True
624 The sparse read can be enabled with experimental.sparse-read = True
622 """
625 """
623 opts = pycompat.byteskwargs(opts)
626 opts = pycompat.byteskwargs(opts)
624 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
627 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
625 index = r.index
628 index = r.index
626 generaldelta = r.version & revlog.FLAG_GENERALDELTA
629 generaldelta = r.version & revlog.FLAG_GENERALDELTA
627 withsparseread = getattr(r, '_withsparseread', False)
630 withsparseread = getattr(r, '_withsparseread', False)
628
631
629 def revinfo(rev):
632 def revinfo(rev):
630 e = index[rev]
633 e = index[rev]
631 compsize = e[1]
634 compsize = e[1]
632 uncompsize = e[2]
635 uncompsize = e[2]
633 chainsize = 0
636 chainsize = 0
634
637
635 if generaldelta:
638 if generaldelta:
636 if e[3] == e[5]:
639 if e[3] == e[5]:
637 deltatype = 'p1'
640 deltatype = 'p1'
638 elif e[3] == e[6]:
641 elif e[3] == e[6]:
639 deltatype = 'p2'
642 deltatype = 'p2'
640 elif e[3] == rev - 1:
643 elif e[3] == rev - 1:
641 deltatype = 'prev'
644 deltatype = 'prev'
642 elif e[3] == rev:
645 elif e[3] == rev:
643 deltatype = 'base'
646 deltatype = 'base'
644 else:
647 else:
645 deltatype = 'other'
648 deltatype = 'other'
646 else:
649 else:
647 if e[3] == rev:
650 if e[3] == rev:
648 deltatype = 'base'
651 deltatype = 'base'
649 else:
652 else:
650 deltatype = 'prev'
653 deltatype = 'prev'
651
654
652 chain = r._deltachain(rev)[0]
655 chain = r._deltachain(rev)[0]
653 for iterrev in chain:
656 for iterrev in chain:
654 e = index[iterrev]
657 e = index[iterrev]
655 chainsize += e[1]
658 chainsize += e[1]
656
659
657 return compsize, uncompsize, deltatype, chain, chainsize
660 return compsize, uncompsize, deltatype, chain, chainsize
658
661
659 fm = ui.formatter('debugdeltachain', opts)
662 fm = ui.formatter('debugdeltachain', opts)
660
663
661 fm.plain(' rev chain# chainlen prev delta '
664 fm.plain(' rev chain# chainlen prev delta '
662 'size rawsize chainsize ratio lindist extradist '
665 'size rawsize chainsize ratio lindist extradist '
663 'extraratio')
666 'extraratio')
664 if withsparseread:
667 if withsparseread:
665 fm.plain(' readsize largestblk rddensity srchunks')
668 fm.plain(' readsize largestblk rddensity srchunks')
666 fm.plain('\n')
669 fm.plain('\n')
667
670
668 chainbases = {}
671 chainbases = {}
669 for rev in r:
672 for rev in r:
670 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
673 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
671 chainbase = chain[0]
674 chainbase = chain[0]
672 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
675 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
673 start = r.start
676 start = r.start
674 length = r.length
677 length = r.length
675 basestart = start(chainbase)
678 basestart = start(chainbase)
676 revstart = start(rev)
679 revstart = start(rev)
677 lineardist = revstart + comp - basestart
680 lineardist = revstart + comp - basestart
678 extradist = lineardist - chainsize
681 extradist = lineardist - chainsize
679 try:
682 try:
680 prevrev = chain[-2]
683 prevrev = chain[-2]
681 except IndexError:
684 except IndexError:
682 prevrev = -1
685 prevrev = -1
683
686
684 chainratio = float(chainsize) / float(uncomp)
687 chainratio = float(chainsize) / float(uncomp)
685 extraratio = float(extradist) / float(chainsize)
688 extraratio = float(extradist) / float(chainsize)
686
689
687 fm.startitem()
690 fm.startitem()
688 fm.write('rev chainid chainlen prevrev deltatype compsize '
691 fm.write('rev chainid chainlen prevrev deltatype compsize '
689 'uncompsize chainsize chainratio lindist extradist '
692 'uncompsize chainsize chainratio lindist extradist '
690 'extraratio',
693 'extraratio',
691 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
694 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
692 rev, chainid, len(chain), prevrev, deltatype, comp,
695 rev, chainid, len(chain), prevrev, deltatype, comp,
693 uncomp, chainsize, chainratio, lineardist, extradist,
696 uncomp, chainsize, chainratio, lineardist, extradist,
694 extraratio,
697 extraratio,
695 rev=rev, chainid=chainid, chainlen=len(chain),
698 rev=rev, chainid=chainid, chainlen=len(chain),
696 prevrev=prevrev, deltatype=deltatype, compsize=comp,
699 prevrev=prevrev, deltatype=deltatype, compsize=comp,
697 uncompsize=uncomp, chainsize=chainsize,
700 uncompsize=uncomp, chainsize=chainsize,
698 chainratio=chainratio, lindist=lineardist,
701 chainratio=chainratio, lindist=lineardist,
699 extradist=extradist, extraratio=extraratio)
702 extradist=extradist, extraratio=extraratio)
700 if withsparseread:
703 if withsparseread:
701 readsize = 0
704 readsize = 0
702 largestblock = 0
705 largestblock = 0
703 srchunks = 0
706 srchunks = 0
704
707
705 for revschunk in revlog._slicechunk(r, chain):
708 for revschunk in revlog._slicechunk(r, chain):
706 srchunks += 1
709 srchunks += 1
707 blkend = start(revschunk[-1]) + length(revschunk[-1])
710 blkend = start(revschunk[-1]) + length(revschunk[-1])
708 blksize = blkend - start(revschunk[0])
711 blksize = blkend - start(revschunk[0])
709
712
710 readsize += blksize
713 readsize += blksize
711 if largestblock < blksize:
714 if largestblock < blksize:
712 largestblock = blksize
715 largestblock = blksize
713
716
714 readdensity = float(chainsize) / float(readsize)
717 readdensity = float(chainsize) / float(readsize)
715
718
716 fm.write('readsize largestblock readdensity srchunks',
719 fm.write('readsize largestblock readdensity srchunks',
717 ' %10d %10d %9.5f %8d',
720 ' %10d %10d %9.5f %8d',
718 readsize, largestblock, readdensity, srchunks,
721 readsize, largestblock, readdensity, srchunks,
719 readsize=readsize, largestblock=largestblock,
722 readsize=readsize, largestblock=largestblock,
720 readdensity=readdensity, srchunks=srchunks)
723 readdensity=readdensity, srchunks=srchunks)
721
724
722 fm.plain('\n')
725 fm.plain('\n')
723
726
724 fm.end()
727 fm.end()
725
728
726 @command('debugdirstate|debugstate',
729 @command('debugdirstate|debugstate',
727 [('', 'nodates', None, _('do not display the saved mtime')),
730 [('', 'nodates', None, _('do not display the saved mtime')),
728 ('', 'datesort', None, _('sort by saved mtime'))],
731 ('', 'datesort', None, _('sort by saved mtime'))],
729 _('[OPTION]...'))
732 _('[OPTION]...'))
730 def debugstate(ui, repo, **opts):
733 def debugstate(ui, repo, **opts):
731 """show the contents of the current dirstate"""
734 """show the contents of the current dirstate"""
732
735
733 nodates = opts.get(r'nodates')
736 nodates = opts.get(r'nodates')
734 datesort = opts.get(r'datesort')
737 datesort = opts.get(r'datesort')
735
738
736 timestr = ""
739 timestr = ""
737 if datesort:
740 if datesort:
738 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
741 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
739 else:
742 else:
740 keyfunc = None # sort by filename
743 keyfunc = None # sort by filename
741 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
744 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
742 if ent[3] == -1:
745 if ent[3] == -1:
743 timestr = 'unset '
746 timestr = 'unset '
744 elif nodates:
747 elif nodates:
745 timestr = 'set '
748 timestr = 'set '
746 else:
749 else:
747 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
750 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
748 time.localtime(ent[3]))
751 time.localtime(ent[3]))
749 timestr = encoding.strtolocal(timestr)
752 timestr = encoding.strtolocal(timestr)
750 if ent[1] & 0o20000:
753 if ent[1] & 0o20000:
751 mode = 'lnk'
754 mode = 'lnk'
752 else:
755 else:
753 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
756 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
754 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
757 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
755 for f in repo.dirstate.copies():
758 for f in repo.dirstate.copies():
756 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
759 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
757
760
758 @command('debugdiscovery',
761 @command('debugdiscovery',
759 [('', 'old', None, _('use old-style discovery')),
762 [('', 'old', None, _('use old-style discovery')),
760 ('', 'nonheads', None,
763 ('', 'nonheads', None,
761 _('use old-style discovery with non-heads included')),
764 _('use old-style discovery with non-heads included')),
762 ('', 'rev', [], 'restrict discovery to this set of revs'),
765 ('', 'rev', [], 'restrict discovery to this set of revs'),
763 ] + cmdutil.remoteopts,
766 ] + cmdutil.remoteopts,
764 _('[--rev REV] [OTHER]'))
767 _('[--rev REV] [OTHER]'))
765 def debugdiscovery(ui, repo, remoteurl="default", **opts):
768 def debugdiscovery(ui, repo, remoteurl="default", **opts):
766 """runs the changeset discovery protocol in isolation"""
769 """runs the changeset discovery protocol in isolation"""
767 opts = pycompat.byteskwargs(opts)
770 opts = pycompat.byteskwargs(opts)
768 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
771 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
769 remote = hg.peer(repo, opts, remoteurl)
772 remote = hg.peer(repo, opts, remoteurl)
770 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
773 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
771
774
772 # make sure tests are repeatable
775 # make sure tests are repeatable
773 random.seed(12323)
776 random.seed(12323)
774
777
775 def doit(pushedrevs, remoteheads, remote=remote):
778 def doit(pushedrevs, remoteheads, remote=remote):
776 if opts.get('old'):
779 if opts.get('old'):
777 if not util.safehasattr(remote, 'branches'):
780 if not util.safehasattr(remote, 'branches'):
778 # enable in-client legacy support
781 # enable in-client legacy support
779 remote = localrepo.locallegacypeer(remote.local())
782 remote = localrepo.locallegacypeer(remote.local())
780 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
783 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
781 force=True)
784 force=True)
782 common = set(common)
785 common = set(common)
783 if not opts.get('nonheads'):
786 if not opts.get('nonheads'):
784 ui.write(("unpruned common: %s\n") %
787 ui.write(("unpruned common: %s\n") %
785 " ".join(sorted(short(n) for n in common)))
788 " ".join(sorted(short(n) for n in common)))
786 dag = dagutil.revlogdag(repo.changelog)
789 dag = dagutil.revlogdag(repo.changelog)
787 all = dag.ancestorset(dag.internalizeall(common))
790 all = dag.ancestorset(dag.internalizeall(common))
788 common = dag.externalizeall(dag.headsetofconnecteds(all))
791 common = dag.externalizeall(dag.headsetofconnecteds(all))
789 else:
792 else:
790 nodes = None
793 nodes = None
791 if pushedrevs:
794 if pushedrevs:
792 revs = scmutil.revrange(repo, pushedrevs)
795 revs = scmutil.revrange(repo, pushedrevs)
793 nodes = [repo[r].node() for r in revs]
796 nodes = [repo[r].node() for r in revs]
794 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
797 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
795 ancestorsof=nodes)
798 ancestorsof=nodes)
796 common = set(common)
799 common = set(common)
797 rheads = set(hds)
800 rheads = set(hds)
798 lheads = set(repo.heads())
801 lheads = set(repo.heads())
799 ui.write(("common heads: %s\n") %
802 ui.write(("common heads: %s\n") %
800 " ".join(sorted(short(n) for n in common)))
803 " ".join(sorted(short(n) for n in common)))
801 if lheads <= common:
804 if lheads <= common:
802 ui.write(("local is subset\n"))
805 ui.write(("local is subset\n"))
803 elif rheads <= common:
806 elif rheads <= common:
804 ui.write(("remote is subset\n"))
807 ui.write(("remote is subset\n"))
805
808
806 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
809 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
807 localrevs = opts['rev']
810 localrevs = opts['rev']
808 doit(localrevs, remoterevs)
811 doit(localrevs, remoterevs)
809
812
810 _chunksize = 4 << 10
813 _chunksize = 4 << 10
811
814
812 @command('debugdownload',
815 @command('debugdownload',
813 [
816 [
814 ('o', 'output', '', _('path')),
817 ('o', 'output', '', _('path')),
815 ],
818 ],
816 optionalrepo=True)
819 optionalrepo=True)
817 def debugdownload(ui, repo, url, output=None, **opts):
820 def debugdownload(ui, repo, url, output=None, **opts):
818 """download a resource using Mercurial logic and config
821 """download a resource using Mercurial logic and config
819 """
822 """
820 fh = urlmod.open(ui, url, output)
823 fh = urlmod.open(ui, url, output)
821
824
822 dest = ui
825 dest = ui
823 if output:
826 if output:
824 dest = open(output, "wb", _chunksize)
827 dest = open(output, "wb", _chunksize)
825 try:
828 try:
826 data = fh.read(_chunksize)
829 data = fh.read(_chunksize)
827 while data:
830 while data:
828 dest.write(data)
831 dest.write(data)
829 data = fh.read(_chunksize)
832 data = fh.read(_chunksize)
830 finally:
833 finally:
831 if output:
834 if output:
832 dest.close()
835 dest.close()
833
836
834 @command('debugextensions', cmdutil.formatteropts, [], norepo=True)
837 @command('debugextensions', cmdutil.formatteropts, [], norepo=True)
835 def debugextensions(ui, **opts):
838 def debugextensions(ui, **opts):
836 '''show information about active extensions'''
839 '''show information about active extensions'''
837 opts = pycompat.byteskwargs(opts)
840 opts = pycompat.byteskwargs(opts)
838 exts = extensions.extensions(ui)
841 exts = extensions.extensions(ui)
839 hgver = util.version()
842 hgver = util.version()
840 fm = ui.formatter('debugextensions', opts)
843 fm = ui.formatter('debugextensions', opts)
841 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
844 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
842 isinternal = extensions.ismoduleinternal(extmod)
845 isinternal = extensions.ismoduleinternal(extmod)
843 extsource = pycompat.fsencode(extmod.__file__)
846 extsource = pycompat.fsencode(extmod.__file__)
844 if isinternal:
847 if isinternal:
845 exttestedwith = [] # never expose magic string to users
848 exttestedwith = [] # never expose magic string to users
846 else:
849 else:
847 exttestedwith = getattr(extmod, 'testedwith', '').split()
850 exttestedwith = getattr(extmod, 'testedwith', '').split()
848 extbuglink = getattr(extmod, 'buglink', None)
851 extbuglink = getattr(extmod, 'buglink', None)
849
852
850 fm.startitem()
853 fm.startitem()
851
854
852 if ui.quiet or ui.verbose:
855 if ui.quiet or ui.verbose:
853 fm.write('name', '%s\n', extname)
856 fm.write('name', '%s\n', extname)
854 else:
857 else:
855 fm.write('name', '%s', extname)
858 fm.write('name', '%s', extname)
856 if isinternal or hgver in exttestedwith:
859 if isinternal or hgver in exttestedwith:
857 fm.plain('\n')
860 fm.plain('\n')
858 elif not exttestedwith:
861 elif not exttestedwith:
859 fm.plain(_(' (untested!)\n'))
862 fm.plain(_(' (untested!)\n'))
860 else:
863 else:
861 lasttestedversion = exttestedwith[-1]
864 lasttestedversion = exttestedwith[-1]
862 fm.plain(' (%s!)\n' % lasttestedversion)
865 fm.plain(' (%s!)\n' % lasttestedversion)
863
866
864 fm.condwrite(ui.verbose and extsource, 'source',
867 fm.condwrite(ui.verbose and extsource, 'source',
865 _(' location: %s\n'), extsource or "")
868 _(' location: %s\n'), extsource or "")
866
869
867 if ui.verbose:
870 if ui.verbose:
868 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
871 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
869 fm.data(bundled=isinternal)
872 fm.data(bundled=isinternal)
870
873
871 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
874 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
872 _(' tested with: %s\n'),
875 _(' tested with: %s\n'),
873 fm.formatlist(exttestedwith, name='ver'))
876 fm.formatlist(exttestedwith, name='ver'))
874
877
875 fm.condwrite(ui.verbose and extbuglink, 'buglink',
878 fm.condwrite(ui.verbose and extbuglink, 'buglink',
876 _(' bug reporting: %s\n'), extbuglink or "")
879 _(' bug reporting: %s\n'), extbuglink or "")
877
880
878 fm.end()
881 fm.end()
879
882
880 @command('debugfileset',
883 @command('debugfileset',
881 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
884 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
882 _('[-r REV] FILESPEC'))
885 _('[-r REV] FILESPEC'))
883 def debugfileset(ui, repo, expr, **opts):
886 def debugfileset(ui, repo, expr, **opts):
884 '''parse and apply a fileset specification'''
887 '''parse and apply a fileset specification'''
885 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
888 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
886 if ui.verbose:
889 if ui.verbose:
887 tree = fileset.parse(expr)
890 tree = fileset.parse(expr)
888 ui.note(fileset.prettyformat(tree), "\n")
891 ui.note(fileset.prettyformat(tree), "\n")
889
892
890 for f in ctx.getfileset(expr):
893 for f in ctx.getfileset(expr):
891 ui.write("%s\n" % f)
894 ui.write("%s\n" % f)
892
895
893 @command('debugformat',
896 @command('debugformat',
894 [] + cmdutil.formatteropts,
897 [] + cmdutil.formatteropts,
895 _(''))
898 _(''))
896 def debugformat(ui, repo, **opts):
899 def debugformat(ui, repo, **opts):
897 """display format information about the current repository
900 """display format information about the current repository
898
901
899 Use --verbose to get extra information about current config value and
902 Use --verbose to get extra information about current config value and
900 Mercurial default."""
903 Mercurial default."""
901 opts = pycompat.byteskwargs(opts)
904 opts = pycompat.byteskwargs(opts)
902 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
905 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
903 maxvariantlength = max(len('format-variant'), maxvariantlength)
906 maxvariantlength = max(len('format-variant'), maxvariantlength)
904
907
905 def makeformatname(name):
908 def makeformatname(name):
906 return '%s:' + (' ' * (maxvariantlength - len(name)))
909 return '%s:' + (' ' * (maxvariantlength - len(name)))
907
910
908 fm = ui.formatter('debugformat', opts)
911 fm = ui.formatter('debugformat', opts)
909 if fm.isplain():
912 if fm.isplain():
910 def formatvalue(value):
913 def formatvalue(value):
911 if util.safehasattr(value, 'startswith'):
914 if util.safehasattr(value, 'startswith'):
912 return value
915 return value
913 if value:
916 if value:
914 return 'yes'
917 return 'yes'
915 else:
918 else:
916 return 'no'
919 return 'no'
917 else:
920 else:
918 formatvalue = pycompat.identity
921 formatvalue = pycompat.identity
919
922
920 fm.plain('format-variant')
923 fm.plain('format-variant')
921 fm.plain(' ' * (maxvariantlength - len('format-variant')))
924 fm.plain(' ' * (maxvariantlength - len('format-variant')))
922 fm.plain(' repo')
925 fm.plain(' repo')
923 if ui.verbose:
926 if ui.verbose:
924 fm.plain(' config default')
927 fm.plain(' config default')
925 fm.plain('\n')
928 fm.plain('\n')
926 for fv in upgrade.allformatvariant:
929 for fv in upgrade.allformatvariant:
927 fm.startitem()
930 fm.startitem()
928 repovalue = fv.fromrepo(repo)
931 repovalue = fv.fromrepo(repo)
929 configvalue = fv.fromconfig(repo)
932 configvalue = fv.fromconfig(repo)
930
933
931 if repovalue != configvalue:
934 if repovalue != configvalue:
932 namelabel = 'formatvariant.name.mismatchconfig'
935 namelabel = 'formatvariant.name.mismatchconfig'
933 repolabel = 'formatvariant.repo.mismatchconfig'
936 repolabel = 'formatvariant.repo.mismatchconfig'
934 elif repovalue != fv.default:
937 elif repovalue != fv.default:
935 namelabel = 'formatvariant.name.mismatchdefault'
938 namelabel = 'formatvariant.name.mismatchdefault'
936 repolabel = 'formatvariant.repo.mismatchdefault'
939 repolabel = 'formatvariant.repo.mismatchdefault'
937 else:
940 else:
938 namelabel = 'formatvariant.name.uptodate'
941 namelabel = 'formatvariant.name.uptodate'
939 repolabel = 'formatvariant.repo.uptodate'
942 repolabel = 'formatvariant.repo.uptodate'
940
943
941 fm.write('name', makeformatname(fv.name), fv.name,
944 fm.write('name', makeformatname(fv.name), fv.name,
942 label=namelabel)
945 label=namelabel)
943 fm.write('repo', ' %3s', formatvalue(repovalue),
946 fm.write('repo', ' %3s', formatvalue(repovalue),
944 label=repolabel)
947 label=repolabel)
945 if fv.default != configvalue:
948 if fv.default != configvalue:
946 configlabel = 'formatvariant.config.special'
949 configlabel = 'formatvariant.config.special'
947 else:
950 else:
948 configlabel = 'formatvariant.config.default'
951 configlabel = 'formatvariant.config.default'
949 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
952 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
950 label=configlabel)
953 label=configlabel)
951 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
954 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
952 label='formatvariant.default')
955 label='formatvariant.default')
953 fm.plain('\n')
956 fm.plain('\n')
954 fm.end()
957 fm.end()
955
958
956 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
959 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
957 def debugfsinfo(ui, path="."):
960 def debugfsinfo(ui, path="."):
958 """show information detected about current filesystem"""
961 """show information detected about current filesystem"""
959 ui.write(('path: %s\n') % path)
962 ui.write(('path: %s\n') % path)
960 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
963 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
961 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
964 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
962 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
965 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
963 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
966 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
964 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
967 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
965 casesensitive = '(unknown)'
968 casesensitive = '(unknown)'
966 try:
969 try:
967 with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f:
970 with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f:
968 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
971 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
969 except OSError:
972 except OSError:
970 pass
973 pass
971 ui.write(('case-sensitive: %s\n') % casesensitive)
974 ui.write(('case-sensitive: %s\n') % casesensitive)
972
975
973 @command('debuggetbundle',
976 @command('debuggetbundle',
974 [('H', 'head', [], _('id of head node'), _('ID')),
977 [('H', 'head', [], _('id of head node'), _('ID')),
975 ('C', 'common', [], _('id of common node'), _('ID')),
978 ('C', 'common', [], _('id of common node'), _('ID')),
976 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
979 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
977 _('REPO FILE [-H|-C ID]...'),
980 _('REPO FILE [-H|-C ID]...'),
978 norepo=True)
981 norepo=True)
979 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
982 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
980 """retrieves a bundle from a repo
983 """retrieves a bundle from a repo
981
984
982 Every ID must be a full-length hex node id string. Saves the bundle to the
985 Every ID must be a full-length hex node id string. Saves the bundle to the
983 given file.
986 given file.
984 """
987 """
985 opts = pycompat.byteskwargs(opts)
988 opts = pycompat.byteskwargs(opts)
986 repo = hg.peer(ui, opts, repopath)
989 repo = hg.peer(ui, opts, repopath)
987 if not repo.capable('getbundle'):
990 if not repo.capable('getbundle'):
988 raise error.Abort("getbundle() not supported by target repository")
991 raise error.Abort("getbundle() not supported by target repository")
989 args = {}
992 args = {}
990 if common:
993 if common:
991 args[r'common'] = [bin(s) for s in common]
994 args[r'common'] = [bin(s) for s in common]
992 if head:
995 if head:
993 args[r'heads'] = [bin(s) for s in head]
996 args[r'heads'] = [bin(s) for s in head]
994 # TODO: get desired bundlecaps from command line.
997 # TODO: get desired bundlecaps from command line.
995 args[r'bundlecaps'] = None
998 args[r'bundlecaps'] = None
996 bundle = repo.getbundle('debug', **args)
999 bundle = repo.getbundle('debug', **args)
997
1000
998 bundletype = opts.get('type', 'bzip2').lower()
1001 bundletype = opts.get('type', 'bzip2').lower()
999 btypes = {'none': 'HG10UN',
1002 btypes = {'none': 'HG10UN',
1000 'bzip2': 'HG10BZ',
1003 'bzip2': 'HG10BZ',
1001 'gzip': 'HG10GZ',
1004 'gzip': 'HG10GZ',
1002 'bundle2': 'HG20'}
1005 'bundle2': 'HG20'}
1003 bundletype = btypes.get(bundletype)
1006 bundletype = btypes.get(bundletype)
1004 if bundletype not in bundle2.bundletypes:
1007 if bundletype not in bundle2.bundletypes:
1005 raise error.Abort(_('unknown bundle type specified with --type'))
1008 raise error.Abort(_('unknown bundle type specified with --type'))
1006 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1009 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1007
1010
1008 @command('debugignore', [], '[FILE]')
1011 @command('debugignore', [], '[FILE]')
1009 def debugignore(ui, repo, *files, **opts):
1012 def debugignore(ui, repo, *files, **opts):
1010 """display the combined ignore pattern and information about ignored files
1013 """display the combined ignore pattern and information about ignored files
1011
1014
1012 With no argument display the combined ignore pattern.
1015 With no argument display the combined ignore pattern.
1013
1016
1014 Given space separated file names, shows if the given file is ignored and
1017 Given space separated file names, shows if the given file is ignored and
1015 if so, show the ignore rule (file and line number) that matched it.
1018 if so, show the ignore rule (file and line number) that matched it.
1016 """
1019 """
1017 ignore = repo.dirstate._ignore
1020 ignore = repo.dirstate._ignore
1018 if not files:
1021 if not files:
1019 # Show all the patterns
1022 # Show all the patterns
1020 ui.write("%s\n" % pycompat.byterepr(ignore))
1023 ui.write("%s\n" % pycompat.byterepr(ignore))
1021 else:
1024 else:
1022 m = scmutil.match(repo[None], pats=files)
1025 m = scmutil.match(repo[None], pats=files)
1023 for f in m.files():
1026 for f in m.files():
1024 nf = util.normpath(f)
1027 nf = util.normpath(f)
1025 ignored = None
1028 ignored = None
1026 ignoredata = None
1029 ignoredata = None
1027 if nf != '.':
1030 if nf != '.':
1028 if ignore(nf):
1031 if ignore(nf):
1029 ignored = nf
1032 ignored = nf
1030 ignoredata = repo.dirstate._ignorefileandline(nf)
1033 ignoredata = repo.dirstate._ignorefileandline(nf)
1031 else:
1034 else:
1032 for p in util.finddirs(nf):
1035 for p in util.finddirs(nf):
1033 if ignore(p):
1036 if ignore(p):
1034 ignored = p
1037 ignored = p
1035 ignoredata = repo.dirstate._ignorefileandline(p)
1038 ignoredata = repo.dirstate._ignorefileandline(p)
1036 break
1039 break
1037 if ignored:
1040 if ignored:
1038 if ignored == nf:
1041 if ignored == nf:
1039 ui.write(_("%s is ignored\n") % m.uipath(f))
1042 ui.write(_("%s is ignored\n") % m.uipath(f))
1040 else:
1043 else:
1041 ui.write(_("%s is ignored because of "
1044 ui.write(_("%s is ignored because of "
1042 "containing folder %s\n")
1045 "containing folder %s\n")
1043 % (m.uipath(f), ignored))
1046 % (m.uipath(f), ignored))
1044 ignorefile, lineno, line = ignoredata
1047 ignorefile, lineno, line = ignoredata
1045 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1048 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1046 % (ignorefile, lineno, line))
1049 % (ignorefile, lineno, line))
1047 else:
1050 else:
1048 ui.write(_("%s is not ignored\n") % m.uipath(f))
1051 ui.write(_("%s is not ignored\n") % m.uipath(f))
1049
1052
1050 @command('debugindex', cmdutil.debugrevlogopts +
1053 @command('debugindex', cmdutil.debugrevlogopts +
1051 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1054 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1052 _('[-f FORMAT] -c|-m|FILE'),
1055 _('[-f FORMAT] -c|-m|FILE'),
1053 optionalrepo=True)
1056 optionalrepo=True)
1054 def debugindex(ui, repo, file_=None, **opts):
1057 def debugindex(ui, repo, file_=None, **opts):
1055 """dump the contents of an index file"""
1058 """dump the contents of an index file"""
1056 opts = pycompat.byteskwargs(opts)
1059 opts = pycompat.byteskwargs(opts)
1057 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1060 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1058 format = opts.get('format', 0)
1061 format = opts.get('format', 0)
1059 if format not in (0, 1):
1062 if format not in (0, 1):
1060 raise error.Abort(_("unknown format %d") % format)
1063 raise error.Abort(_("unknown format %d") % format)
1061
1064
1062 if ui.debugflag:
1065 if ui.debugflag:
1063 shortfn = hex
1066 shortfn = hex
1064 else:
1067 else:
1065 shortfn = short
1068 shortfn = short
1066
1069
1067 # There might not be anything in r, so have a sane default
1070 # There might not be anything in r, so have a sane default
1068 idlen = 12
1071 idlen = 12
1069 for i in r:
1072 for i in r:
1070 idlen = len(shortfn(r.node(i)))
1073 idlen = len(shortfn(r.node(i)))
1071 break
1074 break
1072
1075
1073 if format == 0:
1076 if format == 0:
1074 if ui.verbose:
1077 if ui.verbose:
1075 ui.write((" rev offset length linkrev"
1078 ui.write((" rev offset length linkrev"
1076 " %s %s p2\n") % ("nodeid".ljust(idlen),
1079 " %s %s p2\n") % ("nodeid".ljust(idlen),
1077 "p1".ljust(idlen)))
1080 "p1".ljust(idlen)))
1078 else:
1081 else:
1079 ui.write((" rev linkrev %s %s p2\n") % (
1082 ui.write((" rev linkrev %s %s p2\n") % (
1080 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1083 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1081 elif format == 1:
1084 elif format == 1:
1082 if ui.verbose:
1085 if ui.verbose:
1083 ui.write((" rev flag offset length size link p1"
1086 ui.write((" rev flag offset length size link p1"
1084 " p2 %s\n") % "nodeid".rjust(idlen))
1087 " p2 %s\n") % "nodeid".rjust(idlen))
1085 else:
1088 else:
1086 ui.write((" rev flag size link p1 p2 %s\n") %
1089 ui.write((" rev flag size link p1 p2 %s\n") %
1087 "nodeid".rjust(idlen))
1090 "nodeid".rjust(idlen))
1088
1091
1089 for i in r:
1092 for i in r:
1090 node = r.node(i)
1093 node = r.node(i)
1091 if format == 0:
1094 if format == 0:
1092 try:
1095 try:
1093 pp = r.parents(node)
1096 pp = r.parents(node)
1094 except Exception:
1097 except Exception:
1095 pp = [nullid, nullid]
1098 pp = [nullid, nullid]
1096 if ui.verbose:
1099 if ui.verbose:
1097 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1100 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1098 i, r.start(i), r.length(i), r.linkrev(i),
1101 i, r.start(i), r.length(i), r.linkrev(i),
1099 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1102 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1100 else:
1103 else:
1101 ui.write("% 6d % 7d %s %s %s\n" % (
1104 ui.write("% 6d % 7d %s %s %s\n" % (
1102 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1105 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1103 shortfn(pp[1])))
1106 shortfn(pp[1])))
1104 elif format == 1:
1107 elif format == 1:
1105 pr = r.parentrevs(i)
1108 pr = r.parentrevs(i)
1106 if ui.verbose:
1109 if ui.verbose:
1107 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1110 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1108 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1111 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1109 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1112 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1110 else:
1113 else:
1111 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1114 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1112 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1115 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1113 shortfn(node)))
1116 shortfn(node)))
1114
1117
1115 @command('debugindexdot', cmdutil.debugrevlogopts,
1118 @command('debugindexdot', cmdutil.debugrevlogopts,
1116 _('-c|-m|FILE'), optionalrepo=True)
1119 _('-c|-m|FILE'), optionalrepo=True)
1117 def debugindexdot(ui, repo, file_=None, **opts):
1120 def debugindexdot(ui, repo, file_=None, **opts):
1118 """dump an index DAG as a graphviz dot file"""
1121 """dump an index DAG as a graphviz dot file"""
1119 opts = pycompat.byteskwargs(opts)
1122 opts = pycompat.byteskwargs(opts)
1120 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1123 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1121 ui.write(("digraph G {\n"))
1124 ui.write(("digraph G {\n"))
1122 for i in r:
1125 for i in r:
1123 node = r.node(i)
1126 node = r.node(i)
1124 pp = r.parents(node)
1127 pp = r.parents(node)
1125 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1128 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1126 if pp[1] != nullid:
1129 if pp[1] != nullid:
1127 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1130 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1128 ui.write("}\n")
1131 ui.write("}\n")
1129
1132
1130 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1133 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1131 def debuginstall(ui, **opts):
1134 def debuginstall(ui, **opts):
1132 '''test Mercurial installation
1135 '''test Mercurial installation
1133
1136
1134 Returns 0 on success.
1137 Returns 0 on success.
1135 '''
1138 '''
1136 opts = pycompat.byteskwargs(opts)
1139 opts = pycompat.byteskwargs(opts)
1137
1140
1138 def writetemp(contents):
1141 def writetemp(contents):
1139 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1142 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1140 f = os.fdopen(fd, r"wb")
1143 f = os.fdopen(fd, r"wb")
1141 f.write(contents)
1144 f.write(contents)
1142 f.close()
1145 f.close()
1143 return name
1146 return name
1144
1147
1145 problems = 0
1148 problems = 0
1146
1149
1147 fm = ui.formatter('debuginstall', opts)
1150 fm = ui.formatter('debuginstall', opts)
1148 fm.startitem()
1151 fm.startitem()
1149
1152
1150 # encoding
1153 # encoding
1151 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1154 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1152 err = None
1155 err = None
1153 try:
1156 try:
1154 codecs.lookup(pycompat.sysstr(encoding.encoding))
1157 codecs.lookup(pycompat.sysstr(encoding.encoding))
1155 except LookupError as inst:
1158 except LookupError as inst:
1156 err = stringutil.forcebytestr(inst)
1159 err = stringutil.forcebytestr(inst)
1157 problems += 1
1160 problems += 1
1158 fm.condwrite(err, 'encodingerror', _(" %s\n"
1161 fm.condwrite(err, 'encodingerror', _(" %s\n"
1159 " (check that your locale is properly set)\n"), err)
1162 " (check that your locale is properly set)\n"), err)
1160
1163
1161 # Python
1164 # Python
1162 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1165 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1163 pycompat.sysexecutable)
1166 pycompat.sysexecutable)
1164 fm.write('pythonver', _("checking Python version (%s)\n"),
1167 fm.write('pythonver', _("checking Python version (%s)\n"),
1165 ("%d.%d.%d" % sys.version_info[:3]))
1168 ("%d.%d.%d" % sys.version_info[:3]))
1166 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1169 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1167 os.path.dirname(pycompat.fsencode(os.__file__)))
1170 os.path.dirname(pycompat.fsencode(os.__file__)))
1168
1171
1169 security = set(sslutil.supportedprotocols)
1172 security = set(sslutil.supportedprotocols)
1170 if sslutil.hassni:
1173 if sslutil.hassni:
1171 security.add('sni')
1174 security.add('sni')
1172
1175
1173 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1176 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1174 fm.formatlist(sorted(security), name='protocol',
1177 fm.formatlist(sorted(security), name='protocol',
1175 fmt='%s', sep=','))
1178 fmt='%s', sep=','))
1176
1179
1177 # These are warnings, not errors. So don't increment problem count. This
1180 # These are warnings, not errors. So don't increment problem count. This
1178 # may change in the future.
1181 # may change in the future.
1179 if 'tls1.2' not in security:
1182 if 'tls1.2' not in security:
1180 fm.plain(_(' TLS 1.2 not supported by Python install; '
1183 fm.plain(_(' TLS 1.2 not supported by Python install; '
1181 'network connections lack modern security\n'))
1184 'network connections lack modern security\n'))
1182 if 'sni' not in security:
1185 if 'sni' not in security:
1183 fm.plain(_(' SNI not supported by Python install; may have '
1186 fm.plain(_(' SNI not supported by Python install; may have '
1184 'connectivity issues with some servers\n'))
1187 'connectivity issues with some servers\n'))
1185
1188
1186 # TODO print CA cert info
1189 # TODO print CA cert info
1187
1190
1188 # hg version
1191 # hg version
1189 hgver = util.version()
1192 hgver = util.version()
1190 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1193 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1191 hgver.split('+')[0])
1194 hgver.split('+')[0])
1192 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1195 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1193 '+'.join(hgver.split('+')[1:]))
1196 '+'.join(hgver.split('+')[1:]))
1194
1197
1195 # compiled modules
1198 # compiled modules
1196 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1199 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1197 policy.policy)
1200 policy.policy)
1198 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1201 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1199 os.path.dirname(pycompat.fsencode(__file__)))
1202 os.path.dirname(pycompat.fsencode(__file__)))
1200
1203
1201 if policy.policy in ('c', 'allow'):
1204 if policy.policy in ('c', 'allow'):
1202 err = None
1205 err = None
1203 try:
1206 try:
1204 from .cext import (
1207 from .cext import (
1205 base85,
1208 base85,
1206 bdiff,
1209 bdiff,
1207 mpatch,
1210 mpatch,
1208 osutil,
1211 osutil,
1209 )
1212 )
1210 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1213 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1211 except Exception as inst:
1214 except Exception as inst:
1212 err = stringutil.forcebytestr(inst)
1215 err = stringutil.forcebytestr(inst)
1213 problems += 1
1216 problems += 1
1214 fm.condwrite(err, 'extensionserror', " %s\n", err)
1217 fm.condwrite(err, 'extensionserror', " %s\n", err)
1215
1218
1216 compengines = util.compengines._engines.values()
1219 compengines = util.compengines._engines.values()
1217 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1220 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1218 fm.formatlist(sorted(e.name() for e in compengines),
1221 fm.formatlist(sorted(e.name() for e in compengines),
1219 name='compengine', fmt='%s', sep=', '))
1222 name='compengine', fmt='%s', sep=', '))
1220 fm.write('compenginesavail', _('checking available compression engines '
1223 fm.write('compenginesavail', _('checking available compression engines '
1221 '(%s)\n'),
1224 '(%s)\n'),
1222 fm.formatlist(sorted(e.name() for e in compengines
1225 fm.formatlist(sorted(e.name() for e in compengines
1223 if e.available()),
1226 if e.available()),
1224 name='compengine', fmt='%s', sep=', '))
1227 name='compengine', fmt='%s', sep=', '))
1225 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1228 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1226 fm.write('compenginesserver', _('checking available compression engines '
1229 fm.write('compenginesserver', _('checking available compression engines '
1227 'for wire protocol (%s)\n'),
1230 'for wire protocol (%s)\n'),
1228 fm.formatlist([e.name() for e in wirecompengines
1231 fm.formatlist([e.name() for e in wirecompengines
1229 if e.wireprotosupport()],
1232 if e.wireprotosupport()],
1230 name='compengine', fmt='%s', sep=', '))
1233 name='compengine', fmt='%s', sep=', '))
1231 re2 = 'missing'
1234 re2 = 'missing'
1232 if util._re2:
1235 if util._re2:
1233 re2 = 'available'
1236 re2 = 'available'
1234 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1237 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1235 fm.data(re2=bool(util._re2))
1238 fm.data(re2=bool(util._re2))
1236
1239
1237 # templates
1240 # templates
1238 p = templater.templatepaths()
1241 p = templater.templatepaths()
1239 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1242 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1240 fm.condwrite(not p, '', _(" no template directories found\n"))
1243 fm.condwrite(not p, '', _(" no template directories found\n"))
1241 if p:
1244 if p:
1242 m = templater.templatepath("map-cmdline.default")
1245 m = templater.templatepath("map-cmdline.default")
1243 if m:
1246 if m:
1244 # template found, check if it is working
1247 # template found, check if it is working
1245 err = None
1248 err = None
1246 try:
1249 try:
1247 templater.templater.frommapfile(m)
1250 templater.templater.frommapfile(m)
1248 except Exception as inst:
1251 except Exception as inst:
1249 err = stringutil.forcebytestr(inst)
1252 err = stringutil.forcebytestr(inst)
1250 p = None
1253 p = None
1251 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1254 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1252 else:
1255 else:
1253 p = None
1256 p = None
1254 fm.condwrite(p, 'defaulttemplate',
1257 fm.condwrite(p, 'defaulttemplate',
1255 _("checking default template (%s)\n"), m)
1258 _("checking default template (%s)\n"), m)
1256 fm.condwrite(not m, 'defaulttemplatenotfound',
1259 fm.condwrite(not m, 'defaulttemplatenotfound',
1257 _(" template '%s' not found\n"), "default")
1260 _(" template '%s' not found\n"), "default")
1258 if not p:
1261 if not p:
1259 problems += 1
1262 problems += 1
1260 fm.condwrite(not p, '',
1263 fm.condwrite(not p, '',
1261 _(" (templates seem to have been installed incorrectly)\n"))
1264 _(" (templates seem to have been installed incorrectly)\n"))
1262
1265
1263 # editor
1266 # editor
1264 editor = ui.geteditor()
1267 editor = ui.geteditor()
1265 editor = util.expandpath(editor)
1268 editor = util.expandpath(editor)
1266 editorbin = procutil.shellsplit(editor)[0]
1269 editorbin = procutil.shellsplit(editor)[0]
1267 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1270 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1268 cmdpath = procutil.findexe(editorbin)
1271 cmdpath = procutil.findexe(editorbin)
1269 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1272 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1270 _(" No commit editor set and can't find %s in PATH\n"
1273 _(" No commit editor set and can't find %s in PATH\n"
1271 " (specify a commit editor in your configuration"
1274 " (specify a commit editor in your configuration"
1272 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1275 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1273 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1276 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1274 _(" Can't find editor '%s' in PATH\n"
1277 _(" Can't find editor '%s' in PATH\n"
1275 " (specify a commit editor in your configuration"
1278 " (specify a commit editor in your configuration"
1276 " file)\n"), not cmdpath and editorbin)
1279 " file)\n"), not cmdpath and editorbin)
1277 if not cmdpath and editor != 'vi':
1280 if not cmdpath and editor != 'vi':
1278 problems += 1
1281 problems += 1
1279
1282
1280 # check username
1283 # check username
1281 username = None
1284 username = None
1282 err = None
1285 err = None
1283 try:
1286 try:
1284 username = ui.username()
1287 username = ui.username()
1285 except error.Abort as e:
1288 except error.Abort as e:
1286 err = stringutil.forcebytestr(e)
1289 err = stringutil.forcebytestr(e)
1287 problems += 1
1290 problems += 1
1288
1291
1289 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1292 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1290 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1293 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1291 " (specify a username in your configuration file)\n"), err)
1294 " (specify a username in your configuration file)\n"), err)
1292
1295
1293 fm.condwrite(not problems, '',
1296 fm.condwrite(not problems, '',
1294 _("no problems detected\n"))
1297 _("no problems detected\n"))
1295 if not problems:
1298 if not problems:
1296 fm.data(problems=problems)
1299 fm.data(problems=problems)
1297 fm.condwrite(problems, 'problems',
1300 fm.condwrite(problems, 'problems',
1298 _("%d problems detected,"
1301 _("%d problems detected,"
1299 " please check your install!\n"), problems)
1302 " please check your install!\n"), problems)
1300 fm.end()
1303 fm.end()
1301
1304
1302 return problems
1305 return problems
1303
1306
1304 @command('debugknown', [], _('REPO ID...'), norepo=True)
1307 @command('debugknown', [], _('REPO ID...'), norepo=True)
1305 def debugknown(ui, repopath, *ids, **opts):
1308 def debugknown(ui, repopath, *ids, **opts):
1306 """test whether node ids are known to a repo
1309 """test whether node ids are known to a repo
1307
1310
1308 Every ID must be a full-length hex node id string. Returns a list of 0s
1311 Every ID must be a full-length hex node id string. Returns a list of 0s
1309 and 1s indicating unknown/known.
1312 and 1s indicating unknown/known.
1310 """
1313 """
1311 opts = pycompat.byteskwargs(opts)
1314 opts = pycompat.byteskwargs(opts)
1312 repo = hg.peer(ui, opts, repopath)
1315 repo = hg.peer(ui, opts, repopath)
1313 if not repo.capable('known'):
1316 if not repo.capable('known'):
1314 raise error.Abort("known() not supported by target repository")
1317 raise error.Abort("known() not supported by target repository")
1315 flags = repo.known([bin(s) for s in ids])
1318 flags = repo.known([bin(s) for s in ids])
1316 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1319 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1317
1320
1318 @command('debuglabelcomplete', [], _('LABEL...'))
1321 @command('debuglabelcomplete', [], _('LABEL...'))
1319 def debuglabelcomplete(ui, repo, *args):
1322 def debuglabelcomplete(ui, repo, *args):
1320 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1323 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1321 debugnamecomplete(ui, repo, *args)
1324 debugnamecomplete(ui, repo, *args)
1322
1325
1323 @command('debuglocks',
1326 @command('debuglocks',
1324 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1327 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1325 ('W', 'force-wlock', None,
1328 ('W', 'force-wlock', None,
1326 _('free the working state lock (DANGEROUS)')),
1329 _('free the working state lock (DANGEROUS)')),
1327 ('s', 'set-lock', None, _('set the store lock until stopped')),
1330 ('s', 'set-lock', None, _('set the store lock until stopped')),
1328 ('S', 'set-wlock', None,
1331 ('S', 'set-wlock', None,
1329 _('set the working state lock until stopped'))],
1332 _('set the working state lock until stopped'))],
1330 _('[OPTION]...'))
1333 _('[OPTION]...'))
1331 def debuglocks(ui, repo, **opts):
1334 def debuglocks(ui, repo, **opts):
1332 """show or modify state of locks
1335 """show or modify state of locks
1333
1336
1334 By default, this command will show which locks are held. This
1337 By default, this command will show which locks are held. This
1335 includes the user and process holding the lock, the amount of time
1338 includes the user and process holding the lock, the amount of time
1336 the lock has been held, and the machine name where the process is
1339 the lock has been held, and the machine name where the process is
1337 running if it's not local.
1340 running if it's not local.
1338
1341
1339 Locks protect the integrity of Mercurial's data, so should be
1342 Locks protect the integrity of Mercurial's data, so should be
1340 treated with care. System crashes or other interruptions may cause
1343 treated with care. System crashes or other interruptions may cause
1341 locks to not be properly released, though Mercurial will usually
1344 locks to not be properly released, though Mercurial will usually
1342 detect and remove such stale locks automatically.
1345 detect and remove such stale locks automatically.
1343
1346
1344 However, detecting stale locks may not always be possible (for
1347 However, detecting stale locks may not always be possible (for
1345 instance, on a shared filesystem). Removing locks may also be
1348 instance, on a shared filesystem). Removing locks may also be
1346 blocked by filesystem permissions.
1349 blocked by filesystem permissions.
1347
1350
1348 Setting a lock will prevent other commands from changing the data.
1351 Setting a lock will prevent other commands from changing the data.
1349 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1352 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1350 The set locks are removed when the command exits.
1353 The set locks are removed when the command exits.
1351
1354
1352 Returns 0 if no locks are held.
1355 Returns 0 if no locks are held.
1353
1356
1354 """
1357 """
1355
1358
1356 if opts.get(r'force_lock'):
1359 if opts.get(r'force_lock'):
1357 repo.svfs.unlink('lock')
1360 repo.svfs.unlink('lock')
1358 if opts.get(r'force_wlock'):
1361 if opts.get(r'force_wlock'):
1359 repo.vfs.unlink('wlock')
1362 repo.vfs.unlink('wlock')
1360 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1363 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1361 return 0
1364 return 0
1362
1365
1363 locks = []
1366 locks = []
1364 try:
1367 try:
1365 if opts.get(r'set_wlock'):
1368 if opts.get(r'set_wlock'):
1366 try:
1369 try:
1367 locks.append(repo.wlock(False))
1370 locks.append(repo.wlock(False))
1368 except error.LockHeld:
1371 except error.LockHeld:
1369 raise error.Abort(_('wlock is already held'))
1372 raise error.Abort(_('wlock is already held'))
1370 if opts.get(r'set_lock'):
1373 if opts.get(r'set_lock'):
1371 try:
1374 try:
1372 locks.append(repo.lock(False))
1375 locks.append(repo.lock(False))
1373 except error.LockHeld:
1376 except error.LockHeld:
1374 raise error.Abort(_('lock is already held'))
1377 raise error.Abort(_('lock is already held'))
1375 if len(locks):
1378 if len(locks):
1376 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1379 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1377 return 0
1380 return 0
1378 finally:
1381 finally:
1379 release(*locks)
1382 release(*locks)
1380
1383
1381 now = time.time()
1384 now = time.time()
1382 held = 0
1385 held = 0
1383
1386
1384 def report(vfs, name, method):
1387 def report(vfs, name, method):
1385 # this causes stale locks to get reaped for more accurate reporting
1388 # this causes stale locks to get reaped for more accurate reporting
1386 try:
1389 try:
1387 l = method(False)
1390 l = method(False)
1388 except error.LockHeld:
1391 except error.LockHeld:
1389 l = None
1392 l = None
1390
1393
1391 if l:
1394 if l:
1392 l.release()
1395 l.release()
1393 else:
1396 else:
1394 try:
1397 try:
1395 st = vfs.lstat(name)
1398 st = vfs.lstat(name)
1396 age = now - st[stat.ST_MTIME]
1399 age = now - st[stat.ST_MTIME]
1397 user = util.username(st.st_uid)
1400 user = util.username(st.st_uid)
1398 locker = vfs.readlock(name)
1401 locker = vfs.readlock(name)
1399 if ":" in locker:
1402 if ":" in locker:
1400 host, pid = locker.split(':')
1403 host, pid = locker.split(':')
1401 if host == socket.gethostname():
1404 if host == socket.gethostname():
1402 locker = 'user %s, process %s' % (user, pid)
1405 locker = 'user %s, process %s' % (user, pid)
1403 else:
1406 else:
1404 locker = 'user %s, process %s, host %s' \
1407 locker = 'user %s, process %s, host %s' \
1405 % (user, pid, host)
1408 % (user, pid, host)
1406 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1409 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1407 return 1
1410 return 1
1408 except OSError as e:
1411 except OSError as e:
1409 if e.errno != errno.ENOENT:
1412 if e.errno != errno.ENOENT:
1410 raise
1413 raise
1411
1414
1412 ui.write(("%-6s free\n") % (name + ":"))
1415 ui.write(("%-6s free\n") % (name + ":"))
1413 return 0
1416 return 0
1414
1417
1415 held += report(repo.svfs, "lock", repo.lock)
1418 held += report(repo.svfs, "lock", repo.lock)
1416 held += report(repo.vfs, "wlock", repo.wlock)
1419 held += report(repo.vfs, "wlock", repo.wlock)
1417
1420
1418 return held
1421 return held
1419
1422
1420 @command('debugmergestate', [], '')
1423 @command('debugmergestate', [], '')
1421 def debugmergestate(ui, repo, *args):
1424 def debugmergestate(ui, repo, *args):
1422 """print merge state
1425 """print merge state
1423
1426
1424 Use --verbose to print out information about whether v1 or v2 merge state
1427 Use --verbose to print out information about whether v1 or v2 merge state
1425 was chosen."""
1428 was chosen."""
1426 def _hashornull(h):
1429 def _hashornull(h):
1427 if h == nullhex:
1430 if h == nullhex:
1428 return 'null'
1431 return 'null'
1429 else:
1432 else:
1430 return h
1433 return h
1431
1434
1432 def printrecords(version):
1435 def printrecords(version):
1433 ui.write(('* version %d records\n') % version)
1436 ui.write(('* version %d records\n') % version)
1434 if version == 1:
1437 if version == 1:
1435 records = v1records
1438 records = v1records
1436 else:
1439 else:
1437 records = v2records
1440 records = v2records
1438
1441
1439 for rtype, record in records:
1442 for rtype, record in records:
1440 # pretty print some record types
1443 # pretty print some record types
1441 if rtype == 'L':
1444 if rtype == 'L':
1442 ui.write(('local: %s\n') % record)
1445 ui.write(('local: %s\n') % record)
1443 elif rtype == 'O':
1446 elif rtype == 'O':
1444 ui.write(('other: %s\n') % record)
1447 ui.write(('other: %s\n') % record)
1445 elif rtype == 'm':
1448 elif rtype == 'm':
1446 driver, mdstate = record.split('\0', 1)
1449 driver, mdstate = record.split('\0', 1)
1447 ui.write(('merge driver: %s (state "%s")\n')
1450 ui.write(('merge driver: %s (state "%s")\n')
1448 % (driver, mdstate))
1451 % (driver, mdstate))
1449 elif rtype in 'FDC':
1452 elif rtype in 'FDC':
1450 r = record.split('\0')
1453 r = record.split('\0')
1451 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1454 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1452 if version == 1:
1455 if version == 1:
1453 onode = 'not stored in v1 format'
1456 onode = 'not stored in v1 format'
1454 flags = r[7]
1457 flags = r[7]
1455 else:
1458 else:
1456 onode, flags = r[7:9]
1459 onode, flags = r[7:9]
1457 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1460 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1458 % (f, rtype, state, _hashornull(hash)))
1461 % (f, rtype, state, _hashornull(hash)))
1459 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1462 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1460 ui.write((' ancestor path: %s (node %s)\n')
1463 ui.write((' ancestor path: %s (node %s)\n')
1461 % (afile, _hashornull(anode)))
1464 % (afile, _hashornull(anode)))
1462 ui.write((' other path: %s (node %s)\n')
1465 ui.write((' other path: %s (node %s)\n')
1463 % (ofile, _hashornull(onode)))
1466 % (ofile, _hashornull(onode)))
1464 elif rtype == 'f':
1467 elif rtype == 'f':
1465 filename, rawextras = record.split('\0', 1)
1468 filename, rawextras = record.split('\0', 1)
1466 extras = rawextras.split('\0')
1469 extras = rawextras.split('\0')
1467 i = 0
1470 i = 0
1468 extrastrings = []
1471 extrastrings = []
1469 while i < len(extras):
1472 while i < len(extras):
1470 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1473 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1471 i += 2
1474 i += 2
1472
1475
1473 ui.write(('file extras: %s (%s)\n')
1476 ui.write(('file extras: %s (%s)\n')
1474 % (filename, ', '.join(extrastrings)))
1477 % (filename, ', '.join(extrastrings)))
1475 elif rtype == 'l':
1478 elif rtype == 'l':
1476 labels = record.split('\0', 2)
1479 labels = record.split('\0', 2)
1477 labels = [l for l in labels if len(l) > 0]
1480 labels = [l for l in labels if len(l) > 0]
1478 ui.write(('labels:\n'))
1481 ui.write(('labels:\n'))
1479 ui.write((' local: %s\n' % labels[0]))
1482 ui.write((' local: %s\n' % labels[0]))
1480 ui.write((' other: %s\n' % labels[1]))
1483 ui.write((' other: %s\n' % labels[1]))
1481 if len(labels) > 2:
1484 if len(labels) > 2:
1482 ui.write((' base: %s\n' % labels[2]))
1485 ui.write((' base: %s\n' % labels[2]))
1483 else:
1486 else:
1484 ui.write(('unrecognized entry: %s\t%s\n')
1487 ui.write(('unrecognized entry: %s\t%s\n')
1485 % (rtype, record.replace('\0', '\t')))
1488 % (rtype, record.replace('\0', '\t')))
1486
1489
1487 # Avoid mergestate.read() since it may raise an exception for unsupported
1490 # Avoid mergestate.read() since it may raise an exception for unsupported
1488 # merge state records. We shouldn't be doing this, but this is OK since this
1491 # merge state records. We shouldn't be doing this, but this is OK since this
1489 # command is pretty low-level.
1492 # command is pretty low-level.
1490 ms = mergemod.mergestate(repo)
1493 ms = mergemod.mergestate(repo)
1491
1494
1492 # sort so that reasonable information is on top
1495 # sort so that reasonable information is on top
1493 v1records = ms._readrecordsv1()
1496 v1records = ms._readrecordsv1()
1494 v2records = ms._readrecordsv2()
1497 v2records = ms._readrecordsv2()
1495 order = 'LOml'
1498 order = 'LOml'
1496 def key(r):
1499 def key(r):
1497 idx = order.find(r[0])
1500 idx = order.find(r[0])
1498 if idx == -1:
1501 if idx == -1:
1499 return (1, r[1])
1502 return (1, r[1])
1500 else:
1503 else:
1501 return (0, idx)
1504 return (0, idx)
1502 v1records.sort(key=key)
1505 v1records.sort(key=key)
1503 v2records.sort(key=key)
1506 v2records.sort(key=key)
1504
1507
1505 if not v1records and not v2records:
1508 if not v1records and not v2records:
1506 ui.write(('no merge state found\n'))
1509 ui.write(('no merge state found\n'))
1507 elif not v2records:
1510 elif not v2records:
1508 ui.note(('no version 2 merge state\n'))
1511 ui.note(('no version 2 merge state\n'))
1509 printrecords(1)
1512 printrecords(1)
1510 elif ms._v1v2match(v1records, v2records):
1513 elif ms._v1v2match(v1records, v2records):
1511 ui.note(('v1 and v2 states match: using v2\n'))
1514 ui.note(('v1 and v2 states match: using v2\n'))
1512 printrecords(2)
1515 printrecords(2)
1513 else:
1516 else:
1514 ui.note(('v1 and v2 states mismatch: using v1\n'))
1517 ui.note(('v1 and v2 states mismatch: using v1\n'))
1515 printrecords(1)
1518 printrecords(1)
1516 if ui.verbose:
1519 if ui.verbose:
1517 printrecords(2)
1520 printrecords(2)
1518
1521
1519 @command('debugnamecomplete', [], _('NAME...'))
1522 @command('debugnamecomplete', [], _('NAME...'))
1520 def debugnamecomplete(ui, repo, *args):
1523 def debugnamecomplete(ui, repo, *args):
1521 '''complete "names" - tags, open branch names, bookmark names'''
1524 '''complete "names" - tags, open branch names, bookmark names'''
1522
1525
1523 names = set()
1526 names = set()
1524 # since we previously only listed open branches, we will handle that
1527 # since we previously only listed open branches, we will handle that
1525 # specially (after this for loop)
1528 # specially (after this for loop)
1526 for name, ns in repo.names.iteritems():
1529 for name, ns in repo.names.iteritems():
1527 if name != 'branches':
1530 if name != 'branches':
1528 names.update(ns.listnames(repo))
1531 names.update(ns.listnames(repo))
1529 names.update(tag for (tag, heads, tip, closed)
1532 names.update(tag for (tag, heads, tip, closed)
1530 in repo.branchmap().iterbranches() if not closed)
1533 in repo.branchmap().iterbranches() if not closed)
1531 completions = set()
1534 completions = set()
1532 if not args:
1535 if not args:
1533 args = ['']
1536 args = ['']
1534 for a in args:
1537 for a in args:
1535 completions.update(n for n in names if n.startswith(a))
1538 completions.update(n for n in names if n.startswith(a))
1536 ui.write('\n'.join(sorted(completions)))
1539 ui.write('\n'.join(sorted(completions)))
1537 ui.write('\n')
1540 ui.write('\n')
1538
1541
1539 @command('debugobsolete',
1542 @command('debugobsolete',
1540 [('', 'flags', 0, _('markers flag')),
1543 [('', 'flags', 0, _('markers flag')),
1541 ('', 'record-parents', False,
1544 ('', 'record-parents', False,
1542 _('record parent information for the precursor')),
1545 _('record parent information for the precursor')),
1543 ('r', 'rev', [], _('display markers relevant to REV')),
1546 ('r', 'rev', [], _('display markers relevant to REV')),
1544 ('', 'exclusive', False, _('restrict display to markers only '
1547 ('', 'exclusive', False, _('restrict display to markers only '
1545 'relevant to REV')),
1548 'relevant to REV')),
1546 ('', 'index', False, _('display index of the marker')),
1549 ('', 'index', False, _('display index of the marker')),
1547 ('', 'delete', [], _('delete markers specified by indices')),
1550 ('', 'delete', [], _('delete markers specified by indices')),
1548 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1551 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1549 _('[OBSOLETED [REPLACEMENT ...]]'))
1552 _('[OBSOLETED [REPLACEMENT ...]]'))
1550 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1553 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1551 """create arbitrary obsolete marker
1554 """create arbitrary obsolete marker
1552
1555
1553 With no arguments, displays the list of obsolescence markers."""
1556 With no arguments, displays the list of obsolescence markers."""
1554
1557
1555 opts = pycompat.byteskwargs(opts)
1558 opts = pycompat.byteskwargs(opts)
1556
1559
1557 def parsenodeid(s):
1560 def parsenodeid(s):
1558 try:
1561 try:
1559 # We do not use revsingle/revrange functions here to accept
1562 # We do not use revsingle/revrange functions here to accept
1560 # arbitrary node identifiers, possibly not present in the
1563 # arbitrary node identifiers, possibly not present in the
1561 # local repository.
1564 # local repository.
1562 n = bin(s)
1565 n = bin(s)
1563 if len(n) != len(nullid):
1566 if len(n) != len(nullid):
1564 raise TypeError()
1567 raise TypeError()
1565 return n
1568 return n
1566 except TypeError:
1569 except TypeError:
1567 raise error.Abort('changeset references must be full hexadecimal '
1570 raise error.Abort('changeset references must be full hexadecimal '
1568 'node identifiers')
1571 'node identifiers')
1569
1572
1570 if opts.get('delete'):
1573 if opts.get('delete'):
1571 indices = []
1574 indices = []
1572 for v in opts.get('delete'):
1575 for v in opts.get('delete'):
1573 try:
1576 try:
1574 indices.append(int(v))
1577 indices.append(int(v))
1575 except ValueError:
1578 except ValueError:
1576 raise error.Abort(_('invalid index value: %r') % v,
1579 raise error.Abort(_('invalid index value: %r') % v,
1577 hint=_('use integers for indices'))
1580 hint=_('use integers for indices'))
1578
1581
1579 if repo.currenttransaction():
1582 if repo.currenttransaction():
1580 raise error.Abort(_('cannot delete obsmarkers in the middle '
1583 raise error.Abort(_('cannot delete obsmarkers in the middle '
1581 'of transaction.'))
1584 'of transaction.'))
1582
1585
1583 with repo.lock():
1586 with repo.lock():
1584 n = repair.deleteobsmarkers(repo.obsstore, indices)
1587 n = repair.deleteobsmarkers(repo.obsstore, indices)
1585 ui.write(_('deleted %i obsolescence markers\n') % n)
1588 ui.write(_('deleted %i obsolescence markers\n') % n)
1586
1589
1587 return
1590 return
1588
1591
1589 if precursor is not None:
1592 if precursor is not None:
1590 if opts['rev']:
1593 if opts['rev']:
1591 raise error.Abort('cannot select revision when creating marker')
1594 raise error.Abort('cannot select revision when creating marker')
1592 metadata = {}
1595 metadata = {}
1593 metadata['user'] = opts['user'] or ui.username()
1596 metadata['user'] = opts['user'] or ui.username()
1594 succs = tuple(parsenodeid(succ) for succ in successors)
1597 succs = tuple(parsenodeid(succ) for succ in successors)
1595 l = repo.lock()
1598 l = repo.lock()
1596 try:
1599 try:
1597 tr = repo.transaction('debugobsolete')
1600 tr = repo.transaction('debugobsolete')
1598 try:
1601 try:
1599 date = opts.get('date')
1602 date = opts.get('date')
1600 if date:
1603 if date:
1601 date = dateutil.parsedate(date)
1604 date = dateutil.parsedate(date)
1602 else:
1605 else:
1603 date = None
1606 date = None
1604 prec = parsenodeid(precursor)
1607 prec = parsenodeid(precursor)
1605 parents = None
1608 parents = None
1606 if opts['record_parents']:
1609 if opts['record_parents']:
1607 if prec not in repo.unfiltered():
1610 if prec not in repo.unfiltered():
1608 raise error.Abort('cannot used --record-parents on '
1611 raise error.Abort('cannot used --record-parents on '
1609 'unknown changesets')
1612 'unknown changesets')
1610 parents = repo.unfiltered()[prec].parents()
1613 parents = repo.unfiltered()[prec].parents()
1611 parents = tuple(p.node() for p in parents)
1614 parents = tuple(p.node() for p in parents)
1612 repo.obsstore.create(tr, prec, succs, opts['flags'],
1615 repo.obsstore.create(tr, prec, succs, opts['flags'],
1613 parents=parents, date=date,
1616 parents=parents, date=date,
1614 metadata=metadata, ui=ui)
1617 metadata=metadata, ui=ui)
1615 tr.close()
1618 tr.close()
1616 except ValueError as exc:
1619 except ValueError as exc:
1617 raise error.Abort(_('bad obsmarker input: %s') %
1620 raise error.Abort(_('bad obsmarker input: %s') %
1618 pycompat.bytestr(exc))
1621 pycompat.bytestr(exc))
1619 finally:
1622 finally:
1620 tr.release()
1623 tr.release()
1621 finally:
1624 finally:
1622 l.release()
1625 l.release()
1623 else:
1626 else:
1624 if opts['rev']:
1627 if opts['rev']:
1625 revs = scmutil.revrange(repo, opts['rev'])
1628 revs = scmutil.revrange(repo, opts['rev'])
1626 nodes = [repo[r].node() for r in revs]
1629 nodes = [repo[r].node() for r in revs]
1627 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1630 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1628 exclusive=opts['exclusive']))
1631 exclusive=opts['exclusive']))
1629 markers.sort(key=lambda x: x._data)
1632 markers.sort(key=lambda x: x._data)
1630 else:
1633 else:
1631 markers = obsutil.getmarkers(repo)
1634 markers = obsutil.getmarkers(repo)
1632
1635
1633 markerstoiter = markers
1636 markerstoiter = markers
1634 isrelevant = lambda m: True
1637 isrelevant = lambda m: True
1635 if opts.get('rev') and opts.get('index'):
1638 if opts.get('rev') and opts.get('index'):
1636 markerstoiter = obsutil.getmarkers(repo)
1639 markerstoiter = obsutil.getmarkers(repo)
1637 markerset = set(markers)
1640 markerset = set(markers)
1638 isrelevant = lambda m: m in markerset
1641 isrelevant = lambda m: m in markerset
1639
1642
1640 fm = ui.formatter('debugobsolete', opts)
1643 fm = ui.formatter('debugobsolete', opts)
1641 for i, m in enumerate(markerstoiter):
1644 for i, m in enumerate(markerstoiter):
1642 if not isrelevant(m):
1645 if not isrelevant(m):
1643 # marker can be irrelevant when we're iterating over a set
1646 # marker can be irrelevant when we're iterating over a set
1644 # of markers (markerstoiter) which is bigger than the set
1647 # of markers (markerstoiter) which is bigger than the set
1645 # of markers we want to display (markers)
1648 # of markers we want to display (markers)
1646 # this can happen if both --index and --rev options are
1649 # this can happen if both --index and --rev options are
1647 # provided and thus we need to iterate over all of the markers
1650 # provided and thus we need to iterate over all of the markers
1648 # to get the correct indices, but only display the ones that
1651 # to get the correct indices, but only display the ones that
1649 # are relevant to --rev value
1652 # are relevant to --rev value
1650 continue
1653 continue
1651 fm.startitem()
1654 fm.startitem()
1652 ind = i if opts.get('index') else None
1655 ind = i if opts.get('index') else None
1653 cmdutil.showmarker(fm, m, index=ind)
1656 cmdutil.showmarker(fm, m, index=ind)
1654 fm.end()
1657 fm.end()
1655
1658
1656 @command('debugpathcomplete',
1659 @command('debugpathcomplete',
1657 [('f', 'full', None, _('complete an entire path')),
1660 [('f', 'full', None, _('complete an entire path')),
1658 ('n', 'normal', None, _('show only normal files')),
1661 ('n', 'normal', None, _('show only normal files')),
1659 ('a', 'added', None, _('show only added files')),
1662 ('a', 'added', None, _('show only added files')),
1660 ('r', 'removed', None, _('show only removed files'))],
1663 ('r', 'removed', None, _('show only removed files'))],
1661 _('FILESPEC...'))
1664 _('FILESPEC...'))
1662 def debugpathcomplete(ui, repo, *specs, **opts):
1665 def debugpathcomplete(ui, repo, *specs, **opts):
1663 '''complete part or all of a tracked path
1666 '''complete part or all of a tracked path
1664
1667
1665 This command supports shells that offer path name completion. It
1668 This command supports shells that offer path name completion. It
1666 currently completes only files already known to the dirstate.
1669 currently completes only files already known to the dirstate.
1667
1670
1668 Completion extends only to the next path segment unless
1671 Completion extends only to the next path segment unless
1669 --full is specified, in which case entire paths are used.'''
1672 --full is specified, in which case entire paths are used.'''
1670
1673
1671 def complete(path, acceptable):
1674 def complete(path, acceptable):
1672 dirstate = repo.dirstate
1675 dirstate = repo.dirstate
1673 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1676 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1674 rootdir = repo.root + pycompat.ossep
1677 rootdir = repo.root + pycompat.ossep
1675 if spec != repo.root and not spec.startswith(rootdir):
1678 if spec != repo.root and not spec.startswith(rootdir):
1676 return [], []
1679 return [], []
1677 if os.path.isdir(spec):
1680 if os.path.isdir(spec):
1678 spec += '/'
1681 spec += '/'
1679 spec = spec[len(rootdir):]
1682 spec = spec[len(rootdir):]
1680 fixpaths = pycompat.ossep != '/'
1683 fixpaths = pycompat.ossep != '/'
1681 if fixpaths:
1684 if fixpaths:
1682 spec = spec.replace(pycompat.ossep, '/')
1685 spec = spec.replace(pycompat.ossep, '/')
1683 speclen = len(spec)
1686 speclen = len(spec)
1684 fullpaths = opts[r'full']
1687 fullpaths = opts[r'full']
1685 files, dirs = set(), set()
1688 files, dirs = set(), set()
1686 adddir, addfile = dirs.add, files.add
1689 adddir, addfile = dirs.add, files.add
1687 for f, st in dirstate.iteritems():
1690 for f, st in dirstate.iteritems():
1688 if f.startswith(spec) and st[0] in acceptable:
1691 if f.startswith(spec) and st[0] in acceptable:
1689 if fixpaths:
1692 if fixpaths:
1690 f = f.replace('/', pycompat.ossep)
1693 f = f.replace('/', pycompat.ossep)
1691 if fullpaths:
1694 if fullpaths:
1692 addfile(f)
1695 addfile(f)
1693 continue
1696 continue
1694 s = f.find(pycompat.ossep, speclen)
1697 s = f.find(pycompat.ossep, speclen)
1695 if s >= 0:
1698 if s >= 0:
1696 adddir(f[:s])
1699 adddir(f[:s])
1697 else:
1700 else:
1698 addfile(f)
1701 addfile(f)
1699 return files, dirs
1702 return files, dirs
1700
1703
1701 acceptable = ''
1704 acceptable = ''
1702 if opts[r'normal']:
1705 if opts[r'normal']:
1703 acceptable += 'nm'
1706 acceptable += 'nm'
1704 if opts[r'added']:
1707 if opts[r'added']:
1705 acceptable += 'a'
1708 acceptable += 'a'
1706 if opts[r'removed']:
1709 if opts[r'removed']:
1707 acceptable += 'r'
1710 acceptable += 'r'
1708 cwd = repo.getcwd()
1711 cwd = repo.getcwd()
1709 if not specs:
1712 if not specs:
1710 specs = ['.']
1713 specs = ['.']
1711
1714
1712 files, dirs = set(), set()
1715 files, dirs = set(), set()
1713 for spec in specs:
1716 for spec in specs:
1714 f, d = complete(spec, acceptable or 'nmar')
1717 f, d = complete(spec, acceptable or 'nmar')
1715 files.update(f)
1718 files.update(f)
1716 dirs.update(d)
1719 dirs.update(d)
1717 files.update(dirs)
1720 files.update(dirs)
1718 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1721 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1719 ui.write('\n')
1722 ui.write('\n')
1720
1723
1721 @command('debugpeer', [], _('PATH'), norepo=True)
1724 @command('debugpeer', [], _('PATH'), norepo=True)
1722 def debugpeer(ui, path):
1725 def debugpeer(ui, path):
1723 """establish a connection to a peer repository"""
1726 """establish a connection to a peer repository"""
1724 # Always enable peer request logging. Requires --debug to display
1727 # Always enable peer request logging. Requires --debug to display
1725 # though.
1728 # though.
1726 overrides = {
1729 overrides = {
1727 ('devel', 'debug.peer-request'): True,
1730 ('devel', 'debug.peer-request'): True,
1728 }
1731 }
1729
1732
1730 with ui.configoverride(overrides):
1733 with ui.configoverride(overrides):
1731 peer = hg.peer(ui, {}, path)
1734 peer = hg.peer(ui, {}, path)
1732
1735
1733 local = peer.local() is not None
1736 local = peer.local() is not None
1734 canpush = peer.canpush()
1737 canpush = peer.canpush()
1735
1738
1736 ui.write(_('url: %s\n') % peer.url())
1739 ui.write(_('url: %s\n') % peer.url())
1737 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1740 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1738 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1741 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1739
1742
1740 @command('debugpickmergetool',
1743 @command('debugpickmergetool',
1741 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1744 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1742 ('', 'changedelete', None, _('emulate merging change and delete')),
1745 ('', 'changedelete', None, _('emulate merging change and delete')),
1743 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1746 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1744 _('[PATTERN]...'),
1747 _('[PATTERN]...'),
1745 inferrepo=True)
1748 inferrepo=True)
1746 def debugpickmergetool(ui, repo, *pats, **opts):
1749 def debugpickmergetool(ui, repo, *pats, **opts):
1747 """examine which merge tool is chosen for specified file
1750 """examine which merge tool is chosen for specified file
1748
1751
1749 As described in :hg:`help merge-tools`, Mercurial examines
1752 As described in :hg:`help merge-tools`, Mercurial examines
1750 configurations below in this order to decide which merge tool is
1753 configurations below in this order to decide which merge tool is
1751 chosen for specified file.
1754 chosen for specified file.
1752
1755
1753 1. ``--tool`` option
1756 1. ``--tool`` option
1754 2. ``HGMERGE`` environment variable
1757 2. ``HGMERGE`` environment variable
1755 3. configurations in ``merge-patterns`` section
1758 3. configurations in ``merge-patterns`` section
1756 4. configuration of ``ui.merge``
1759 4. configuration of ``ui.merge``
1757 5. configurations in ``merge-tools`` section
1760 5. configurations in ``merge-tools`` section
1758 6. ``hgmerge`` tool (for historical reason only)
1761 6. ``hgmerge`` tool (for historical reason only)
1759 7. default tool for fallback (``:merge`` or ``:prompt``)
1762 7. default tool for fallback (``:merge`` or ``:prompt``)
1760
1763
1761 This command writes out examination result in the style below::
1764 This command writes out examination result in the style below::
1762
1765
1763 FILE = MERGETOOL
1766 FILE = MERGETOOL
1764
1767
1765 By default, all files known in the first parent context of the
1768 By default, all files known in the first parent context of the
1766 working directory are examined. Use file patterns and/or -I/-X
1769 working directory are examined. Use file patterns and/or -I/-X
1767 options to limit target files. -r/--rev is also useful to examine
1770 options to limit target files. -r/--rev is also useful to examine
1768 files in another context without actual updating to it.
1771 files in another context without actual updating to it.
1769
1772
1770 With --debug, this command shows warning messages while matching
1773 With --debug, this command shows warning messages while matching
1771 against ``merge-patterns`` and so on, too. It is recommended to
1774 against ``merge-patterns`` and so on, too. It is recommended to
1772 use this option with explicit file patterns and/or -I/-X options,
1775 use this option with explicit file patterns and/or -I/-X options,
1773 because this option increases amount of output per file according
1776 because this option increases amount of output per file according
1774 to configurations in hgrc.
1777 to configurations in hgrc.
1775
1778
1776 With -v/--verbose, this command shows configurations below at
1779 With -v/--verbose, this command shows configurations below at
1777 first (only if specified).
1780 first (only if specified).
1778
1781
1779 - ``--tool`` option
1782 - ``--tool`` option
1780 - ``HGMERGE`` environment variable
1783 - ``HGMERGE`` environment variable
1781 - configuration of ``ui.merge``
1784 - configuration of ``ui.merge``
1782
1785
1783 If merge tool is chosen before matching against
1786 If merge tool is chosen before matching against
1784 ``merge-patterns``, this command can't show any helpful
1787 ``merge-patterns``, this command can't show any helpful
1785 information, even with --debug. In such case, information above is
1788 information, even with --debug. In such case, information above is
1786 useful to know why a merge tool is chosen.
1789 useful to know why a merge tool is chosen.
1787 """
1790 """
1788 opts = pycompat.byteskwargs(opts)
1791 opts = pycompat.byteskwargs(opts)
1789 overrides = {}
1792 overrides = {}
1790 if opts['tool']:
1793 if opts['tool']:
1791 overrides[('ui', 'forcemerge')] = opts['tool']
1794 overrides[('ui', 'forcemerge')] = opts['tool']
1792 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1795 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1793
1796
1794 with ui.configoverride(overrides, 'debugmergepatterns'):
1797 with ui.configoverride(overrides, 'debugmergepatterns'):
1795 hgmerge = encoding.environ.get("HGMERGE")
1798 hgmerge = encoding.environ.get("HGMERGE")
1796 if hgmerge is not None:
1799 if hgmerge is not None:
1797 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1800 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1798 uimerge = ui.config("ui", "merge")
1801 uimerge = ui.config("ui", "merge")
1799 if uimerge:
1802 if uimerge:
1800 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1803 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1801
1804
1802 ctx = scmutil.revsingle(repo, opts.get('rev'))
1805 ctx = scmutil.revsingle(repo, opts.get('rev'))
1803 m = scmutil.match(ctx, pats, opts)
1806 m = scmutil.match(ctx, pats, opts)
1804 changedelete = opts['changedelete']
1807 changedelete = opts['changedelete']
1805 for path in ctx.walk(m):
1808 for path in ctx.walk(m):
1806 fctx = ctx[path]
1809 fctx = ctx[path]
1807 try:
1810 try:
1808 if not ui.debugflag:
1811 if not ui.debugflag:
1809 ui.pushbuffer(error=True)
1812 ui.pushbuffer(error=True)
1810 tool, toolpath = filemerge._picktool(repo, ui, path,
1813 tool, toolpath = filemerge._picktool(repo, ui, path,
1811 fctx.isbinary(),
1814 fctx.isbinary(),
1812 'l' in fctx.flags(),
1815 'l' in fctx.flags(),
1813 changedelete)
1816 changedelete)
1814 finally:
1817 finally:
1815 if not ui.debugflag:
1818 if not ui.debugflag:
1816 ui.popbuffer()
1819 ui.popbuffer()
1817 ui.write(('%s = %s\n') % (path, tool))
1820 ui.write(('%s = %s\n') % (path, tool))
1818
1821
1819 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1822 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1820 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1823 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1821 '''access the pushkey key/value protocol
1824 '''access the pushkey key/value protocol
1822
1825
1823 With two args, list the keys in the given namespace.
1826 With two args, list the keys in the given namespace.
1824
1827
1825 With five args, set a key to new if it currently is set to old.
1828 With five args, set a key to new if it currently is set to old.
1826 Reports success or failure.
1829 Reports success or failure.
1827 '''
1830 '''
1828
1831
1829 target = hg.peer(ui, {}, repopath)
1832 target = hg.peer(ui, {}, repopath)
1830 if keyinfo:
1833 if keyinfo:
1831 key, old, new = keyinfo
1834 key, old, new = keyinfo
1832 r = target.pushkey(namespace, key, old, new)
1835 r = target.pushkey(namespace, key, old, new)
1833 ui.status(pycompat.bytestr(r) + '\n')
1836 ui.status(pycompat.bytestr(r) + '\n')
1834 return not r
1837 return not r
1835 else:
1838 else:
1836 for k, v in sorted(target.listkeys(namespace).iteritems()):
1839 for k, v in sorted(target.listkeys(namespace).iteritems()):
1837 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1840 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1838 stringutil.escapestr(v)))
1841 stringutil.escapestr(v)))
1839
1842
1840 @command('debugpvec', [], _('A B'))
1843 @command('debugpvec', [], _('A B'))
1841 def debugpvec(ui, repo, a, b=None):
1844 def debugpvec(ui, repo, a, b=None):
1842 ca = scmutil.revsingle(repo, a)
1845 ca = scmutil.revsingle(repo, a)
1843 cb = scmutil.revsingle(repo, b)
1846 cb = scmutil.revsingle(repo, b)
1844 pa = pvec.ctxpvec(ca)
1847 pa = pvec.ctxpvec(ca)
1845 pb = pvec.ctxpvec(cb)
1848 pb = pvec.ctxpvec(cb)
1846 if pa == pb:
1849 if pa == pb:
1847 rel = "="
1850 rel = "="
1848 elif pa > pb:
1851 elif pa > pb:
1849 rel = ">"
1852 rel = ">"
1850 elif pa < pb:
1853 elif pa < pb:
1851 rel = "<"
1854 rel = "<"
1852 elif pa | pb:
1855 elif pa | pb:
1853 rel = "|"
1856 rel = "|"
1854 ui.write(_("a: %s\n") % pa)
1857 ui.write(_("a: %s\n") % pa)
1855 ui.write(_("b: %s\n") % pb)
1858 ui.write(_("b: %s\n") % pb)
1856 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1859 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1857 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1860 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1858 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1861 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1859 pa.distance(pb), rel))
1862 pa.distance(pb), rel))
1860
1863
1861 @command('debugrebuilddirstate|debugrebuildstate',
1864 @command('debugrebuilddirstate|debugrebuildstate',
1862 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1865 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1863 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1866 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1864 'the working copy parent')),
1867 'the working copy parent')),
1865 ],
1868 ],
1866 _('[-r REV]'))
1869 _('[-r REV]'))
1867 def debugrebuilddirstate(ui, repo, rev, **opts):
1870 def debugrebuilddirstate(ui, repo, rev, **opts):
1868 """rebuild the dirstate as it would look like for the given revision
1871 """rebuild the dirstate as it would look like for the given revision
1869
1872
1870 If no revision is specified the first current parent will be used.
1873 If no revision is specified the first current parent will be used.
1871
1874
1872 The dirstate will be set to the files of the given revision.
1875 The dirstate will be set to the files of the given revision.
1873 The actual working directory content or existing dirstate
1876 The actual working directory content or existing dirstate
1874 information such as adds or removes is not considered.
1877 information such as adds or removes is not considered.
1875
1878
1876 ``minimal`` will only rebuild the dirstate status for files that claim to be
1879 ``minimal`` will only rebuild the dirstate status for files that claim to be
1877 tracked but are not in the parent manifest, or that exist in the parent
1880 tracked but are not in the parent manifest, or that exist in the parent
1878 manifest but are not in the dirstate. It will not change adds, removes, or
1881 manifest but are not in the dirstate. It will not change adds, removes, or
1879 modified files that are in the working copy parent.
1882 modified files that are in the working copy parent.
1880
1883
1881 One use of this command is to make the next :hg:`status` invocation
1884 One use of this command is to make the next :hg:`status` invocation
1882 check the actual file content.
1885 check the actual file content.
1883 """
1886 """
1884 ctx = scmutil.revsingle(repo, rev)
1887 ctx = scmutil.revsingle(repo, rev)
1885 with repo.wlock():
1888 with repo.wlock():
1886 dirstate = repo.dirstate
1889 dirstate = repo.dirstate
1887 changedfiles = None
1890 changedfiles = None
1888 # See command doc for what minimal does.
1891 # See command doc for what minimal does.
1889 if opts.get(r'minimal'):
1892 if opts.get(r'minimal'):
1890 manifestfiles = set(ctx.manifest().keys())
1893 manifestfiles = set(ctx.manifest().keys())
1891 dirstatefiles = set(dirstate)
1894 dirstatefiles = set(dirstate)
1892 manifestonly = manifestfiles - dirstatefiles
1895 manifestonly = manifestfiles - dirstatefiles
1893 dsonly = dirstatefiles - manifestfiles
1896 dsonly = dirstatefiles - manifestfiles
1894 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1897 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1895 changedfiles = manifestonly | dsnotadded
1898 changedfiles = manifestonly | dsnotadded
1896
1899
1897 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1900 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1898
1901
1899 @command('debugrebuildfncache', [], '')
1902 @command('debugrebuildfncache', [], '')
1900 def debugrebuildfncache(ui, repo):
1903 def debugrebuildfncache(ui, repo):
1901 """rebuild the fncache file"""
1904 """rebuild the fncache file"""
1902 repair.rebuildfncache(ui, repo)
1905 repair.rebuildfncache(ui, repo)
1903
1906
1904 @command('debugrename',
1907 @command('debugrename',
1905 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1908 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1906 _('[-r REV] FILE'))
1909 _('[-r REV] FILE'))
1907 def debugrename(ui, repo, file1, *pats, **opts):
1910 def debugrename(ui, repo, file1, *pats, **opts):
1908 """dump rename information"""
1911 """dump rename information"""
1909
1912
1910 opts = pycompat.byteskwargs(opts)
1913 opts = pycompat.byteskwargs(opts)
1911 ctx = scmutil.revsingle(repo, opts.get('rev'))
1914 ctx = scmutil.revsingle(repo, opts.get('rev'))
1912 m = scmutil.match(ctx, (file1,) + pats, opts)
1915 m = scmutil.match(ctx, (file1,) + pats, opts)
1913 for abs in ctx.walk(m):
1916 for abs in ctx.walk(m):
1914 fctx = ctx[abs]
1917 fctx = ctx[abs]
1915 o = fctx.filelog().renamed(fctx.filenode())
1918 o = fctx.filelog().renamed(fctx.filenode())
1916 rel = m.rel(abs)
1919 rel = m.rel(abs)
1917 if o:
1920 if o:
1918 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1921 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1919 else:
1922 else:
1920 ui.write(_("%s not renamed\n") % rel)
1923 ui.write(_("%s not renamed\n") % rel)
1921
1924
1922 @command('debugrevlog', cmdutil.debugrevlogopts +
1925 @command('debugrevlog', cmdutil.debugrevlogopts +
1923 [('d', 'dump', False, _('dump index data'))],
1926 [('d', 'dump', False, _('dump index data'))],
1924 _('-c|-m|FILE'),
1927 _('-c|-m|FILE'),
1925 optionalrepo=True)
1928 optionalrepo=True)
1926 def debugrevlog(ui, repo, file_=None, **opts):
1929 def debugrevlog(ui, repo, file_=None, **opts):
1927 """show data and statistics about a revlog"""
1930 """show data and statistics about a revlog"""
1928 opts = pycompat.byteskwargs(opts)
1931 opts = pycompat.byteskwargs(opts)
1929 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1932 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1930
1933
1931 if opts.get("dump"):
1934 if opts.get("dump"):
1932 numrevs = len(r)
1935 numrevs = len(r)
1933 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1936 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1934 " rawsize totalsize compression heads chainlen\n"))
1937 " rawsize totalsize compression heads chainlen\n"))
1935 ts = 0
1938 ts = 0
1936 heads = set()
1939 heads = set()
1937
1940
1938 for rev in xrange(numrevs):
1941 for rev in xrange(numrevs):
1939 dbase = r.deltaparent(rev)
1942 dbase = r.deltaparent(rev)
1940 if dbase == -1:
1943 if dbase == -1:
1941 dbase = rev
1944 dbase = rev
1942 cbase = r.chainbase(rev)
1945 cbase = r.chainbase(rev)
1943 clen = r.chainlen(rev)
1946 clen = r.chainlen(rev)
1944 p1, p2 = r.parentrevs(rev)
1947 p1, p2 = r.parentrevs(rev)
1945 rs = r.rawsize(rev)
1948 rs = r.rawsize(rev)
1946 ts = ts + rs
1949 ts = ts + rs
1947 heads -= set(r.parentrevs(rev))
1950 heads -= set(r.parentrevs(rev))
1948 heads.add(rev)
1951 heads.add(rev)
1949 try:
1952 try:
1950 compression = ts / r.end(rev)
1953 compression = ts / r.end(rev)
1951 except ZeroDivisionError:
1954 except ZeroDivisionError:
1952 compression = 0
1955 compression = 0
1953 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1956 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1954 "%11d %5d %8d\n" %
1957 "%11d %5d %8d\n" %
1955 (rev, p1, p2, r.start(rev), r.end(rev),
1958 (rev, p1, p2, r.start(rev), r.end(rev),
1956 r.start(dbase), r.start(cbase),
1959 r.start(dbase), r.start(cbase),
1957 r.start(p1), r.start(p2),
1960 r.start(p1), r.start(p2),
1958 rs, ts, compression, len(heads), clen))
1961 rs, ts, compression, len(heads), clen))
1959 return 0
1962 return 0
1960
1963
1961 v = r.version
1964 v = r.version
1962 format = v & 0xFFFF
1965 format = v & 0xFFFF
1963 flags = []
1966 flags = []
1964 gdelta = False
1967 gdelta = False
1965 if v & revlog.FLAG_INLINE_DATA:
1968 if v & revlog.FLAG_INLINE_DATA:
1966 flags.append('inline')
1969 flags.append('inline')
1967 if v & revlog.FLAG_GENERALDELTA:
1970 if v & revlog.FLAG_GENERALDELTA:
1968 gdelta = True
1971 gdelta = True
1969 flags.append('generaldelta')
1972 flags.append('generaldelta')
1970 if not flags:
1973 if not flags:
1971 flags = ['(none)']
1974 flags = ['(none)']
1972
1975
1973 nummerges = 0
1976 nummerges = 0
1974 numfull = 0
1977 numfull = 0
1975 numprev = 0
1978 numprev = 0
1976 nump1 = 0
1979 nump1 = 0
1977 nump2 = 0
1980 nump2 = 0
1978 numother = 0
1981 numother = 0
1979 nump1prev = 0
1982 nump1prev = 0
1980 nump2prev = 0
1983 nump2prev = 0
1981 chainlengths = []
1984 chainlengths = []
1982 chainbases = []
1985 chainbases = []
1983 chainspans = []
1986 chainspans = []
1984
1987
1985 datasize = [None, 0, 0]
1988 datasize = [None, 0, 0]
1986 fullsize = [None, 0, 0]
1989 fullsize = [None, 0, 0]
1987 deltasize = [None, 0, 0]
1990 deltasize = [None, 0, 0]
1988 chunktypecounts = {}
1991 chunktypecounts = {}
1989 chunktypesizes = {}
1992 chunktypesizes = {}
1990
1993
1991 def addsize(size, l):
1994 def addsize(size, l):
1992 if l[0] is None or size < l[0]:
1995 if l[0] is None or size < l[0]:
1993 l[0] = size
1996 l[0] = size
1994 if size > l[1]:
1997 if size > l[1]:
1995 l[1] = size
1998 l[1] = size
1996 l[2] += size
1999 l[2] += size
1997
2000
1998 numrevs = len(r)
2001 numrevs = len(r)
1999 for rev in xrange(numrevs):
2002 for rev in xrange(numrevs):
2000 p1, p2 = r.parentrevs(rev)
2003 p1, p2 = r.parentrevs(rev)
2001 delta = r.deltaparent(rev)
2004 delta = r.deltaparent(rev)
2002 if format > 0:
2005 if format > 0:
2003 addsize(r.rawsize(rev), datasize)
2006 addsize(r.rawsize(rev), datasize)
2004 if p2 != nullrev:
2007 if p2 != nullrev:
2005 nummerges += 1
2008 nummerges += 1
2006 size = r.length(rev)
2009 size = r.length(rev)
2007 if delta == nullrev:
2010 if delta == nullrev:
2008 chainlengths.append(0)
2011 chainlengths.append(0)
2009 chainbases.append(r.start(rev))
2012 chainbases.append(r.start(rev))
2010 chainspans.append(size)
2013 chainspans.append(size)
2011 numfull += 1
2014 numfull += 1
2012 addsize(size, fullsize)
2015 addsize(size, fullsize)
2013 else:
2016 else:
2014 chainlengths.append(chainlengths[delta] + 1)
2017 chainlengths.append(chainlengths[delta] + 1)
2015 baseaddr = chainbases[delta]
2018 baseaddr = chainbases[delta]
2016 revaddr = r.start(rev)
2019 revaddr = r.start(rev)
2017 chainbases.append(baseaddr)
2020 chainbases.append(baseaddr)
2018 chainspans.append((revaddr - baseaddr) + size)
2021 chainspans.append((revaddr - baseaddr) + size)
2019 addsize(size, deltasize)
2022 addsize(size, deltasize)
2020 if delta == rev - 1:
2023 if delta == rev - 1:
2021 numprev += 1
2024 numprev += 1
2022 if delta == p1:
2025 if delta == p1:
2023 nump1prev += 1
2026 nump1prev += 1
2024 elif delta == p2:
2027 elif delta == p2:
2025 nump2prev += 1
2028 nump2prev += 1
2026 elif delta == p1:
2029 elif delta == p1:
2027 nump1 += 1
2030 nump1 += 1
2028 elif delta == p2:
2031 elif delta == p2:
2029 nump2 += 1
2032 nump2 += 1
2030 elif delta != nullrev:
2033 elif delta != nullrev:
2031 numother += 1
2034 numother += 1
2032
2035
2033 # Obtain data on the raw chunks in the revlog.
2036 # Obtain data on the raw chunks in the revlog.
2034 segment = r._getsegmentforrevs(rev, rev)[1]
2037 segment = r._getsegmentforrevs(rev, rev)[1]
2035 if segment:
2038 if segment:
2036 chunktype = bytes(segment[0:1])
2039 chunktype = bytes(segment[0:1])
2037 else:
2040 else:
2038 chunktype = 'empty'
2041 chunktype = 'empty'
2039
2042
2040 if chunktype not in chunktypecounts:
2043 if chunktype not in chunktypecounts:
2041 chunktypecounts[chunktype] = 0
2044 chunktypecounts[chunktype] = 0
2042 chunktypesizes[chunktype] = 0
2045 chunktypesizes[chunktype] = 0
2043
2046
2044 chunktypecounts[chunktype] += 1
2047 chunktypecounts[chunktype] += 1
2045 chunktypesizes[chunktype] += size
2048 chunktypesizes[chunktype] += size
2046
2049
2047 # Adjust size min value for empty cases
2050 # Adjust size min value for empty cases
2048 for size in (datasize, fullsize, deltasize):
2051 for size in (datasize, fullsize, deltasize):
2049 if size[0] is None:
2052 if size[0] is None:
2050 size[0] = 0
2053 size[0] = 0
2051
2054
2052 numdeltas = numrevs - numfull
2055 numdeltas = numrevs - numfull
2053 numoprev = numprev - nump1prev - nump2prev
2056 numoprev = numprev - nump1prev - nump2prev
2054 totalrawsize = datasize[2]
2057 totalrawsize = datasize[2]
2055 datasize[2] /= numrevs
2058 datasize[2] /= numrevs
2056 fulltotal = fullsize[2]
2059 fulltotal = fullsize[2]
2057 fullsize[2] /= numfull
2060 fullsize[2] /= numfull
2058 deltatotal = deltasize[2]
2061 deltatotal = deltasize[2]
2059 if numrevs - numfull > 0:
2062 if numrevs - numfull > 0:
2060 deltasize[2] /= numrevs - numfull
2063 deltasize[2] /= numrevs - numfull
2061 totalsize = fulltotal + deltatotal
2064 totalsize = fulltotal + deltatotal
2062 avgchainlen = sum(chainlengths) / numrevs
2065 avgchainlen = sum(chainlengths) / numrevs
2063 maxchainlen = max(chainlengths)
2066 maxchainlen = max(chainlengths)
2064 maxchainspan = max(chainspans)
2067 maxchainspan = max(chainspans)
2065 compratio = 1
2068 compratio = 1
2066 if totalsize:
2069 if totalsize:
2067 compratio = totalrawsize / totalsize
2070 compratio = totalrawsize / totalsize
2068
2071
2069 basedfmtstr = '%%%dd\n'
2072 basedfmtstr = '%%%dd\n'
2070 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2073 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2071
2074
2072 def dfmtstr(max):
2075 def dfmtstr(max):
2073 return basedfmtstr % len(str(max))
2076 return basedfmtstr % len(str(max))
2074 def pcfmtstr(max, padding=0):
2077 def pcfmtstr(max, padding=0):
2075 return basepcfmtstr % (len(str(max)), ' ' * padding)
2078 return basepcfmtstr % (len(str(max)), ' ' * padding)
2076
2079
2077 def pcfmt(value, total):
2080 def pcfmt(value, total):
2078 if total:
2081 if total:
2079 return (value, 100 * float(value) / total)
2082 return (value, 100 * float(value) / total)
2080 else:
2083 else:
2081 return value, 100.0
2084 return value, 100.0
2082
2085
2083 ui.write(('format : %d\n') % format)
2086 ui.write(('format : %d\n') % format)
2084 ui.write(('flags : %s\n') % ', '.join(flags))
2087 ui.write(('flags : %s\n') % ', '.join(flags))
2085
2088
2086 ui.write('\n')
2089 ui.write('\n')
2087 fmt = pcfmtstr(totalsize)
2090 fmt = pcfmtstr(totalsize)
2088 fmt2 = dfmtstr(totalsize)
2091 fmt2 = dfmtstr(totalsize)
2089 ui.write(('revisions : ') + fmt2 % numrevs)
2092 ui.write(('revisions : ') + fmt2 % numrevs)
2090 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2093 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2091 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2094 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2092 ui.write(('revisions : ') + fmt2 % numrevs)
2095 ui.write(('revisions : ') + fmt2 % numrevs)
2093 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2096 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2094 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2097 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2095 ui.write(('revision size : ') + fmt2 % totalsize)
2098 ui.write(('revision size : ') + fmt2 % totalsize)
2096 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2099 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2097 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2100 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2098
2101
2099 def fmtchunktype(chunktype):
2102 def fmtchunktype(chunktype):
2100 if chunktype == 'empty':
2103 if chunktype == 'empty':
2101 return ' %s : ' % chunktype
2104 return ' %s : ' % chunktype
2102 elif chunktype in pycompat.bytestr(string.ascii_letters):
2105 elif chunktype in pycompat.bytestr(string.ascii_letters):
2103 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2106 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2104 else:
2107 else:
2105 return ' 0x%s : ' % hex(chunktype)
2108 return ' 0x%s : ' % hex(chunktype)
2106
2109
2107 ui.write('\n')
2110 ui.write('\n')
2108 ui.write(('chunks : ') + fmt2 % numrevs)
2111 ui.write(('chunks : ') + fmt2 % numrevs)
2109 for chunktype in sorted(chunktypecounts):
2112 for chunktype in sorted(chunktypecounts):
2110 ui.write(fmtchunktype(chunktype))
2113 ui.write(fmtchunktype(chunktype))
2111 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2114 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2112 ui.write(('chunks size : ') + fmt2 % totalsize)
2115 ui.write(('chunks size : ') + fmt2 % totalsize)
2113 for chunktype in sorted(chunktypecounts):
2116 for chunktype in sorted(chunktypecounts):
2114 ui.write(fmtchunktype(chunktype))
2117 ui.write(fmtchunktype(chunktype))
2115 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2118 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2116
2119
2117 ui.write('\n')
2120 ui.write('\n')
2118 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2121 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2119 ui.write(('avg chain length : ') + fmt % avgchainlen)
2122 ui.write(('avg chain length : ') + fmt % avgchainlen)
2120 ui.write(('max chain length : ') + fmt % maxchainlen)
2123 ui.write(('max chain length : ') + fmt % maxchainlen)
2121 ui.write(('max chain reach : ') + fmt % maxchainspan)
2124 ui.write(('max chain reach : ') + fmt % maxchainspan)
2122 ui.write(('compression ratio : ') + fmt % compratio)
2125 ui.write(('compression ratio : ') + fmt % compratio)
2123
2126
2124 if format > 0:
2127 if format > 0:
2125 ui.write('\n')
2128 ui.write('\n')
2126 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2129 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2127 % tuple(datasize))
2130 % tuple(datasize))
2128 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2131 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2129 % tuple(fullsize))
2132 % tuple(fullsize))
2130 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2133 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2131 % tuple(deltasize))
2134 % tuple(deltasize))
2132
2135
2133 if numdeltas > 0:
2136 if numdeltas > 0:
2134 ui.write('\n')
2137 ui.write('\n')
2135 fmt = pcfmtstr(numdeltas)
2138 fmt = pcfmtstr(numdeltas)
2136 fmt2 = pcfmtstr(numdeltas, 4)
2139 fmt2 = pcfmtstr(numdeltas, 4)
2137 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2140 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2138 if numprev > 0:
2141 if numprev > 0:
2139 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2142 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2140 numprev))
2143 numprev))
2141 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2144 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2142 numprev))
2145 numprev))
2143 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2146 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2144 numprev))
2147 numprev))
2145 if gdelta:
2148 if gdelta:
2146 ui.write(('deltas against p1 : ')
2149 ui.write(('deltas against p1 : ')
2147 + fmt % pcfmt(nump1, numdeltas))
2150 + fmt % pcfmt(nump1, numdeltas))
2148 ui.write(('deltas against p2 : ')
2151 ui.write(('deltas against p2 : ')
2149 + fmt % pcfmt(nump2, numdeltas))
2152 + fmt % pcfmt(nump2, numdeltas))
2150 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2153 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2151 numdeltas))
2154 numdeltas))
2152
2155
2153 @command('debugrevspec',
2156 @command('debugrevspec',
2154 [('', 'optimize', None,
2157 [('', 'optimize', None,
2155 _('print parsed tree after optimizing (DEPRECATED)')),
2158 _('print parsed tree after optimizing (DEPRECATED)')),
2156 ('', 'show-revs', True, _('print list of result revisions (default)')),
2159 ('', 'show-revs', True, _('print list of result revisions (default)')),
2157 ('s', 'show-set', None, _('print internal representation of result set')),
2160 ('s', 'show-set', None, _('print internal representation of result set')),
2158 ('p', 'show-stage', [],
2161 ('p', 'show-stage', [],
2159 _('print parsed tree at the given stage'), _('NAME')),
2162 _('print parsed tree at the given stage'), _('NAME')),
2160 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2163 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2161 ('', 'verify-optimized', False, _('verify optimized result')),
2164 ('', 'verify-optimized', False, _('verify optimized result')),
2162 ],
2165 ],
2163 ('REVSPEC'))
2166 ('REVSPEC'))
2164 def debugrevspec(ui, repo, expr, **opts):
2167 def debugrevspec(ui, repo, expr, **opts):
2165 """parse and apply a revision specification
2168 """parse and apply a revision specification
2166
2169
2167 Use -p/--show-stage option to print the parsed tree at the given stages.
2170 Use -p/--show-stage option to print the parsed tree at the given stages.
2168 Use -p all to print tree at every stage.
2171 Use -p all to print tree at every stage.
2169
2172
2170 Use --no-show-revs option with -s or -p to print only the set
2173 Use --no-show-revs option with -s or -p to print only the set
2171 representation or the parsed tree respectively.
2174 representation or the parsed tree respectively.
2172
2175
2173 Use --verify-optimized to compare the optimized result with the unoptimized
2176 Use --verify-optimized to compare the optimized result with the unoptimized
2174 one. Returns 1 if the optimized result differs.
2177 one. Returns 1 if the optimized result differs.
2175 """
2178 """
2176 opts = pycompat.byteskwargs(opts)
2179 opts = pycompat.byteskwargs(opts)
2177 aliases = ui.configitems('revsetalias')
2180 aliases = ui.configitems('revsetalias')
2178 stages = [
2181 stages = [
2179 ('parsed', lambda tree: tree),
2182 ('parsed', lambda tree: tree),
2180 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2183 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2181 ui.warn)),
2184 ui.warn)),
2182 ('concatenated', revsetlang.foldconcat),
2185 ('concatenated', revsetlang.foldconcat),
2183 ('analyzed', revsetlang.analyze),
2186 ('analyzed', revsetlang.analyze),
2184 ('optimized', revsetlang.optimize),
2187 ('optimized', revsetlang.optimize),
2185 ]
2188 ]
2186 if opts['no_optimized']:
2189 if opts['no_optimized']:
2187 stages = stages[:-1]
2190 stages = stages[:-1]
2188 if opts['verify_optimized'] and opts['no_optimized']:
2191 if opts['verify_optimized'] and opts['no_optimized']:
2189 raise error.Abort(_('cannot use --verify-optimized with '
2192 raise error.Abort(_('cannot use --verify-optimized with '
2190 '--no-optimized'))
2193 '--no-optimized'))
2191 stagenames = set(n for n, f in stages)
2194 stagenames = set(n for n, f in stages)
2192
2195
2193 showalways = set()
2196 showalways = set()
2194 showchanged = set()
2197 showchanged = set()
2195 if ui.verbose and not opts['show_stage']:
2198 if ui.verbose and not opts['show_stage']:
2196 # show parsed tree by --verbose (deprecated)
2199 # show parsed tree by --verbose (deprecated)
2197 showalways.add('parsed')
2200 showalways.add('parsed')
2198 showchanged.update(['expanded', 'concatenated'])
2201 showchanged.update(['expanded', 'concatenated'])
2199 if opts['optimize']:
2202 if opts['optimize']:
2200 showalways.add('optimized')
2203 showalways.add('optimized')
2201 if opts['show_stage'] and opts['optimize']:
2204 if opts['show_stage'] and opts['optimize']:
2202 raise error.Abort(_('cannot use --optimize with --show-stage'))
2205 raise error.Abort(_('cannot use --optimize with --show-stage'))
2203 if opts['show_stage'] == ['all']:
2206 if opts['show_stage'] == ['all']:
2204 showalways.update(stagenames)
2207 showalways.update(stagenames)
2205 else:
2208 else:
2206 for n in opts['show_stage']:
2209 for n in opts['show_stage']:
2207 if n not in stagenames:
2210 if n not in stagenames:
2208 raise error.Abort(_('invalid stage name: %s') % n)
2211 raise error.Abort(_('invalid stage name: %s') % n)
2209 showalways.update(opts['show_stage'])
2212 showalways.update(opts['show_stage'])
2210
2213
2211 treebystage = {}
2214 treebystage = {}
2212 printedtree = None
2215 printedtree = None
2213 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2216 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2214 for n, f in stages:
2217 for n, f in stages:
2215 treebystage[n] = tree = f(tree)
2218 treebystage[n] = tree = f(tree)
2216 if n in showalways or (n in showchanged and tree != printedtree):
2219 if n in showalways or (n in showchanged and tree != printedtree):
2217 if opts['show_stage'] or n != 'parsed':
2220 if opts['show_stage'] or n != 'parsed':
2218 ui.write(("* %s:\n") % n)
2221 ui.write(("* %s:\n") % n)
2219 ui.write(revsetlang.prettyformat(tree), "\n")
2222 ui.write(revsetlang.prettyformat(tree), "\n")
2220 printedtree = tree
2223 printedtree = tree
2221
2224
2222 if opts['verify_optimized']:
2225 if opts['verify_optimized']:
2223 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2226 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2224 brevs = revset.makematcher(treebystage['optimized'])(repo)
2227 brevs = revset.makematcher(treebystage['optimized'])(repo)
2225 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2228 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2226 ui.write(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
2229 ui.write(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
2227 ui.write(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
2230 ui.write(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
2228 arevs = list(arevs)
2231 arevs = list(arevs)
2229 brevs = list(brevs)
2232 brevs = list(brevs)
2230 if arevs == brevs:
2233 if arevs == brevs:
2231 return 0
2234 return 0
2232 ui.write(('--- analyzed\n'), label='diff.file_a')
2235 ui.write(('--- analyzed\n'), label='diff.file_a')
2233 ui.write(('+++ optimized\n'), label='diff.file_b')
2236 ui.write(('+++ optimized\n'), label='diff.file_b')
2234 sm = difflib.SequenceMatcher(None, arevs, brevs)
2237 sm = difflib.SequenceMatcher(None, arevs, brevs)
2235 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2238 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2236 if tag in ('delete', 'replace'):
2239 if tag in ('delete', 'replace'):
2237 for c in arevs[alo:ahi]:
2240 for c in arevs[alo:ahi]:
2238 ui.write('-%s\n' % c, label='diff.deleted')
2241 ui.write('-%s\n' % c, label='diff.deleted')
2239 if tag in ('insert', 'replace'):
2242 if tag in ('insert', 'replace'):
2240 for c in brevs[blo:bhi]:
2243 for c in brevs[blo:bhi]:
2241 ui.write('+%s\n' % c, label='diff.inserted')
2244 ui.write('+%s\n' % c, label='diff.inserted')
2242 if tag == 'equal':
2245 if tag == 'equal':
2243 for c in arevs[alo:ahi]:
2246 for c in arevs[alo:ahi]:
2244 ui.write(' %s\n' % c)
2247 ui.write(' %s\n' % c)
2245 return 1
2248 return 1
2246
2249
2247 func = revset.makematcher(tree)
2250 func = revset.makematcher(tree)
2248 revs = func(repo)
2251 revs = func(repo)
2249 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2252 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2250 ui.write(("* set:\n"), smartset.prettyformat(revs), "\n")
2253 ui.write(("* set:\n"), smartset.prettyformat(revs), "\n")
2251 if not opts['show_revs']:
2254 if not opts['show_revs']:
2252 return
2255 return
2253 for c in revs:
2256 for c in revs:
2254 ui.write("%d\n" % c)
2257 ui.write("%d\n" % c)
2255
2258
2256 @command('debugserve', [
2259 @command('debugserve', [
2257 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2260 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2258 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2261 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2259 ('', 'logiofile', '', _('file to log server I/O to')),
2262 ('', 'logiofile', '', _('file to log server I/O to')),
2260 ], '')
2263 ], '')
2261 def debugserve(ui, repo, **opts):
2264 def debugserve(ui, repo, **opts):
2262 """run a server with advanced settings
2265 """run a server with advanced settings
2263
2266
2264 This command is similar to :hg:`serve`. It exists partially as a
2267 This command is similar to :hg:`serve`. It exists partially as a
2265 workaround to the fact that ``hg serve --stdio`` must have specific
2268 workaround to the fact that ``hg serve --stdio`` must have specific
2266 arguments for security reasons.
2269 arguments for security reasons.
2267 """
2270 """
2268 opts = pycompat.byteskwargs(opts)
2271 opts = pycompat.byteskwargs(opts)
2269
2272
2270 if not opts['sshstdio']:
2273 if not opts['sshstdio']:
2271 raise error.Abort(_('only --sshstdio is currently supported'))
2274 raise error.Abort(_('only --sshstdio is currently supported'))
2272
2275
2273 logfh = None
2276 logfh = None
2274
2277
2275 if opts['logiofd'] and opts['logiofile']:
2278 if opts['logiofd'] and opts['logiofile']:
2276 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2279 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2277
2280
2278 if opts['logiofd']:
2281 if opts['logiofd']:
2279 # Line buffered because output is line based.
2282 # Line buffered because output is line based.
2280 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2283 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2281 elif opts['logiofile']:
2284 elif opts['logiofile']:
2282 logfh = open(opts['logiofile'], 'ab', 1)
2285 logfh = open(opts['logiofile'], 'ab', 1)
2283
2286
2284 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2287 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2285 s.serve_forever()
2288 s.serve_forever()
2286
2289
2287 @command('debugsetparents', [], _('REV1 [REV2]'))
2290 @command('debugsetparents', [], _('REV1 [REV2]'))
2288 def debugsetparents(ui, repo, rev1, rev2=None):
2291 def debugsetparents(ui, repo, rev1, rev2=None):
2289 """manually set the parents of the current working directory
2292 """manually set the parents of the current working directory
2290
2293
2291 This is useful for writing repository conversion tools, but should
2294 This is useful for writing repository conversion tools, but should
2292 be used with care. For example, neither the working directory nor the
2295 be used with care. For example, neither the working directory nor the
2293 dirstate is updated, so file status may be incorrect after running this
2296 dirstate is updated, so file status may be incorrect after running this
2294 command.
2297 command.
2295
2298
2296 Returns 0 on success.
2299 Returns 0 on success.
2297 """
2300 """
2298
2301
2299 node1 = scmutil.revsingle(repo, rev1).node()
2302 node1 = scmutil.revsingle(repo, rev1).node()
2300 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2303 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2301
2304
2302 with repo.wlock():
2305 with repo.wlock():
2303 repo.setparents(node1, node2)
2306 repo.setparents(node1, node2)
2304
2307
2305 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2308 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2306 def debugssl(ui, repo, source=None, **opts):
2309 def debugssl(ui, repo, source=None, **opts):
2307 '''test a secure connection to a server
2310 '''test a secure connection to a server
2308
2311
2309 This builds the certificate chain for the server on Windows, installing the
2312 This builds the certificate chain for the server on Windows, installing the
2310 missing intermediates and trusted root via Windows Update if necessary. It
2313 missing intermediates and trusted root via Windows Update if necessary. It
2311 does nothing on other platforms.
2314 does nothing on other platforms.
2312
2315
2313 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2316 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2314 that server is used. See :hg:`help urls` for more information.
2317 that server is used. See :hg:`help urls` for more information.
2315
2318
2316 If the update succeeds, retry the original operation. Otherwise, the cause
2319 If the update succeeds, retry the original operation. Otherwise, the cause
2317 of the SSL error is likely another issue.
2320 of the SSL error is likely another issue.
2318 '''
2321 '''
2319 if not pycompat.iswindows:
2322 if not pycompat.iswindows:
2320 raise error.Abort(_('certificate chain building is only possible on '
2323 raise error.Abort(_('certificate chain building is only possible on '
2321 'Windows'))
2324 'Windows'))
2322
2325
2323 if not source:
2326 if not source:
2324 if not repo:
2327 if not repo:
2325 raise error.Abort(_("there is no Mercurial repository here, and no "
2328 raise error.Abort(_("there is no Mercurial repository here, and no "
2326 "server specified"))
2329 "server specified"))
2327 source = "default"
2330 source = "default"
2328
2331
2329 source, branches = hg.parseurl(ui.expandpath(source))
2332 source, branches = hg.parseurl(ui.expandpath(source))
2330 url = util.url(source)
2333 url = util.url(source)
2331 addr = None
2334 addr = None
2332
2335
2333 defaultport = {'https': 443, 'ssh': 22}
2336 defaultport = {'https': 443, 'ssh': 22}
2334 if url.scheme in defaultport:
2337 if url.scheme in defaultport:
2335 try:
2338 try:
2336 addr = (url.host, int(url.port or defaultport[url.scheme]))
2339 addr = (url.host, int(url.port or defaultport[url.scheme]))
2337 except ValueError:
2340 except ValueError:
2338 raise error.Abort(_("malformed port number in URL"))
2341 raise error.Abort(_("malformed port number in URL"))
2339 else:
2342 else:
2340 raise error.Abort(_("only https and ssh connections are supported"))
2343 raise error.Abort(_("only https and ssh connections are supported"))
2341
2344
2342 from . import win32
2345 from . import win32
2343
2346
2344 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2347 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2345 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2348 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2346
2349
2347 try:
2350 try:
2348 s.connect(addr)
2351 s.connect(addr)
2349 cert = s.getpeercert(True)
2352 cert = s.getpeercert(True)
2350
2353
2351 ui.status(_('checking the certificate chain for %s\n') % url.host)
2354 ui.status(_('checking the certificate chain for %s\n') % url.host)
2352
2355
2353 complete = win32.checkcertificatechain(cert, build=False)
2356 complete = win32.checkcertificatechain(cert, build=False)
2354
2357
2355 if not complete:
2358 if not complete:
2356 ui.status(_('certificate chain is incomplete, updating... '))
2359 ui.status(_('certificate chain is incomplete, updating... '))
2357
2360
2358 if not win32.checkcertificatechain(cert):
2361 if not win32.checkcertificatechain(cert):
2359 ui.status(_('failed.\n'))
2362 ui.status(_('failed.\n'))
2360 else:
2363 else:
2361 ui.status(_('done.\n'))
2364 ui.status(_('done.\n'))
2362 else:
2365 else:
2363 ui.status(_('full certificate chain is available\n'))
2366 ui.status(_('full certificate chain is available\n'))
2364 finally:
2367 finally:
2365 s.close()
2368 s.close()
2366
2369
2367 @command('debugsub',
2370 @command('debugsub',
2368 [('r', 'rev', '',
2371 [('r', 'rev', '',
2369 _('revision to check'), _('REV'))],
2372 _('revision to check'), _('REV'))],
2370 _('[-r REV] [REV]'))
2373 _('[-r REV] [REV]'))
2371 def debugsub(ui, repo, rev=None):
2374 def debugsub(ui, repo, rev=None):
2372 ctx = scmutil.revsingle(repo, rev, None)
2375 ctx = scmutil.revsingle(repo, rev, None)
2373 for k, v in sorted(ctx.substate.items()):
2376 for k, v in sorted(ctx.substate.items()):
2374 ui.write(('path %s\n') % k)
2377 ui.write(('path %s\n') % k)
2375 ui.write((' source %s\n') % v[0])
2378 ui.write((' source %s\n') % v[0])
2376 ui.write((' revision %s\n') % v[1])
2379 ui.write((' revision %s\n') % v[1])
2377
2380
2378 @command('debugsuccessorssets',
2381 @command('debugsuccessorssets',
2379 [('', 'closest', False, _('return closest successors sets only'))],
2382 [('', 'closest', False, _('return closest successors sets only'))],
2380 _('[REV]'))
2383 _('[REV]'))
2381 def debugsuccessorssets(ui, repo, *revs, **opts):
2384 def debugsuccessorssets(ui, repo, *revs, **opts):
2382 """show set of successors for revision
2385 """show set of successors for revision
2383
2386
2384 A successors set of changeset A is a consistent group of revisions that
2387 A successors set of changeset A is a consistent group of revisions that
2385 succeed A. It contains non-obsolete changesets only unless closests
2388 succeed A. It contains non-obsolete changesets only unless closests
2386 successors set is set.
2389 successors set is set.
2387
2390
2388 In most cases a changeset A has a single successors set containing a single
2391 In most cases a changeset A has a single successors set containing a single
2389 successor (changeset A replaced by A').
2392 successor (changeset A replaced by A').
2390
2393
2391 A changeset that is made obsolete with no successors are called "pruned".
2394 A changeset that is made obsolete with no successors are called "pruned".
2392 Such changesets have no successors sets at all.
2395 Such changesets have no successors sets at all.
2393
2396
2394 A changeset that has been "split" will have a successors set containing
2397 A changeset that has been "split" will have a successors set containing
2395 more than one successor.
2398 more than one successor.
2396
2399
2397 A changeset that has been rewritten in multiple different ways is called
2400 A changeset that has been rewritten in multiple different ways is called
2398 "divergent". Such changesets have multiple successor sets (each of which
2401 "divergent". Such changesets have multiple successor sets (each of which
2399 may also be split, i.e. have multiple successors).
2402 may also be split, i.e. have multiple successors).
2400
2403
2401 Results are displayed as follows::
2404 Results are displayed as follows::
2402
2405
2403 <rev1>
2406 <rev1>
2404 <successors-1A>
2407 <successors-1A>
2405 <rev2>
2408 <rev2>
2406 <successors-2A>
2409 <successors-2A>
2407 <successors-2B1> <successors-2B2> <successors-2B3>
2410 <successors-2B1> <successors-2B2> <successors-2B3>
2408
2411
2409 Here rev2 has two possible (i.e. divergent) successors sets. The first
2412 Here rev2 has two possible (i.e. divergent) successors sets. The first
2410 holds one element, whereas the second holds three (i.e. the changeset has
2413 holds one element, whereas the second holds three (i.e. the changeset has
2411 been split).
2414 been split).
2412 """
2415 """
2413 # passed to successorssets caching computation from one call to another
2416 # passed to successorssets caching computation from one call to another
2414 cache = {}
2417 cache = {}
2415 ctx2str = bytes
2418 ctx2str = bytes
2416 node2str = short
2419 node2str = short
2417 for rev in scmutil.revrange(repo, revs):
2420 for rev in scmutil.revrange(repo, revs):
2418 ctx = repo[rev]
2421 ctx = repo[rev]
2419 ui.write('%s\n'% ctx2str(ctx))
2422 ui.write('%s\n'% ctx2str(ctx))
2420 for succsset in obsutil.successorssets(repo, ctx.node(),
2423 for succsset in obsutil.successorssets(repo, ctx.node(),
2421 closest=opts[r'closest'],
2424 closest=opts[r'closest'],
2422 cache=cache):
2425 cache=cache):
2423 if succsset:
2426 if succsset:
2424 ui.write(' ')
2427 ui.write(' ')
2425 ui.write(node2str(succsset[0]))
2428 ui.write(node2str(succsset[0]))
2426 for node in succsset[1:]:
2429 for node in succsset[1:]:
2427 ui.write(' ')
2430 ui.write(' ')
2428 ui.write(node2str(node))
2431 ui.write(node2str(node))
2429 ui.write('\n')
2432 ui.write('\n')
2430
2433
2431 @command('debugtemplate',
2434 @command('debugtemplate',
2432 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2435 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2433 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2436 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2434 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2437 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2435 optionalrepo=True)
2438 optionalrepo=True)
2436 def debugtemplate(ui, repo, tmpl, **opts):
2439 def debugtemplate(ui, repo, tmpl, **opts):
2437 """parse and apply a template
2440 """parse and apply a template
2438
2441
2439 If -r/--rev is given, the template is processed as a log template and
2442 If -r/--rev is given, the template is processed as a log template and
2440 applied to the given changesets. Otherwise, it is processed as a generic
2443 applied to the given changesets. Otherwise, it is processed as a generic
2441 template.
2444 template.
2442
2445
2443 Use --verbose to print the parsed tree.
2446 Use --verbose to print the parsed tree.
2444 """
2447 """
2445 revs = None
2448 revs = None
2446 if opts[r'rev']:
2449 if opts[r'rev']:
2447 if repo is None:
2450 if repo is None:
2448 raise error.RepoError(_('there is no Mercurial repository here '
2451 raise error.RepoError(_('there is no Mercurial repository here '
2449 '(.hg not found)'))
2452 '(.hg not found)'))
2450 revs = scmutil.revrange(repo, opts[r'rev'])
2453 revs = scmutil.revrange(repo, opts[r'rev'])
2451
2454
2452 props = {}
2455 props = {}
2453 for d in opts[r'define']:
2456 for d in opts[r'define']:
2454 try:
2457 try:
2455 k, v = (e.strip() for e in d.split('=', 1))
2458 k, v = (e.strip() for e in d.split('=', 1))
2456 if not k or k == 'ui':
2459 if not k or k == 'ui':
2457 raise ValueError
2460 raise ValueError
2458 props[k] = v
2461 props[k] = v
2459 except ValueError:
2462 except ValueError:
2460 raise error.Abort(_('malformed keyword definition: %s') % d)
2463 raise error.Abort(_('malformed keyword definition: %s') % d)
2461
2464
2462 if ui.verbose:
2465 if ui.verbose:
2463 aliases = ui.configitems('templatealias')
2466 aliases = ui.configitems('templatealias')
2464 tree = templater.parse(tmpl)
2467 tree = templater.parse(tmpl)
2465 ui.note(templater.prettyformat(tree), '\n')
2468 ui.note(templater.prettyformat(tree), '\n')
2466 newtree = templater.expandaliases(tree, aliases)
2469 newtree = templater.expandaliases(tree, aliases)
2467 if newtree != tree:
2470 if newtree != tree:
2468 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2471 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2469
2472
2470 if revs is None:
2473 if revs is None:
2471 tres = formatter.templateresources(ui, repo)
2474 tres = formatter.templateresources(ui, repo)
2472 t = formatter.maketemplater(ui, tmpl, resources=tres)
2475 t = formatter.maketemplater(ui, tmpl, resources=tres)
2473 ui.write(t.renderdefault(props))
2476 ui.write(t.renderdefault(props))
2474 else:
2477 else:
2475 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2478 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2476 for r in revs:
2479 for r in revs:
2477 displayer.show(repo[r], **pycompat.strkwargs(props))
2480 displayer.show(repo[r], **pycompat.strkwargs(props))
2478 displayer.close()
2481 displayer.close()
2479
2482
2480 @command('debuguigetpass', [
2483 @command('debuguigetpass', [
2481 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2484 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2482 ], _('[-p TEXT]'), norepo=True)
2485 ], _('[-p TEXT]'), norepo=True)
2483 def debuguigetpass(ui, prompt=''):
2486 def debuguigetpass(ui, prompt=''):
2484 """show prompt to type password"""
2487 """show prompt to type password"""
2485 r = ui.getpass(prompt)
2488 r = ui.getpass(prompt)
2486 ui.write(('respose: %s\n') % r)
2489 ui.write(('respose: %s\n') % r)
2487
2490
2488 @command('debuguiprompt', [
2491 @command('debuguiprompt', [
2489 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2492 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2490 ], _('[-p TEXT]'), norepo=True)
2493 ], _('[-p TEXT]'), norepo=True)
2491 def debuguiprompt(ui, prompt=''):
2494 def debuguiprompt(ui, prompt=''):
2492 """show plain prompt"""
2495 """show plain prompt"""
2493 r = ui.prompt(prompt)
2496 r = ui.prompt(prompt)
2494 ui.write(('response: %s\n') % r)
2497 ui.write(('response: %s\n') % r)
2495
2498
2496 @command('debugupdatecaches', [])
2499 @command('debugupdatecaches', [])
2497 def debugupdatecaches(ui, repo, *pats, **opts):
2500 def debugupdatecaches(ui, repo, *pats, **opts):
2498 """warm all known caches in the repository"""
2501 """warm all known caches in the repository"""
2499 with repo.wlock(), repo.lock():
2502 with repo.wlock(), repo.lock():
2500 repo.updatecaches(full=True)
2503 repo.updatecaches(full=True)
2501
2504
2502 @command('debugupgraderepo', [
2505 @command('debugupgraderepo', [
2503 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2506 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2504 ('', 'run', False, _('performs an upgrade')),
2507 ('', 'run', False, _('performs an upgrade')),
2505 ])
2508 ])
2506 def debugupgraderepo(ui, repo, run=False, optimize=None):
2509 def debugupgraderepo(ui, repo, run=False, optimize=None):
2507 """upgrade a repository to use different features
2510 """upgrade a repository to use different features
2508
2511
2509 If no arguments are specified, the repository is evaluated for upgrade
2512 If no arguments are specified, the repository is evaluated for upgrade
2510 and a list of problems and potential optimizations is printed.
2513 and a list of problems and potential optimizations is printed.
2511
2514
2512 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2515 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2513 can be influenced via additional arguments. More details will be provided
2516 can be influenced via additional arguments. More details will be provided
2514 by the command output when run without ``--run``.
2517 by the command output when run without ``--run``.
2515
2518
2516 During the upgrade, the repository will be locked and no writes will be
2519 During the upgrade, the repository will be locked and no writes will be
2517 allowed.
2520 allowed.
2518
2521
2519 At the end of the upgrade, the repository may not be readable while new
2522 At the end of the upgrade, the repository may not be readable while new
2520 repository data is swapped in. This window will be as long as it takes to
2523 repository data is swapped in. This window will be as long as it takes to
2521 rename some directories inside the ``.hg`` directory. On most machines, this
2524 rename some directories inside the ``.hg`` directory. On most machines, this
2522 should complete almost instantaneously and the chances of a consumer being
2525 should complete almost instantaneously and the chances of a consumer being
2523 unable to access the repository should be low.
2526 unable to access the repository should be low.
2524 """
2527 """
2525 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2528 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2526
2529
2527 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2530 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2528 inferrepo=True)
2531 inferrepo=True)
2529 def debugwalk(ui, repo, *pats, **opts):
2532 def debugwalk(ui, repo, *pats, **opts):
2530 """show how files match on given patterns"""
2533 """show how files match on given patterns"""
2531 opts = pycompat.byteskwargs(opts)
2534 opts = pycompat.byteskwargs(opts)
2532 m = scmutil.match(repo[None], pats, opts)
2535 m = scmutil.match(repo[None], pats, opts)
2533 ui.write(('matcher: %r\n' % m))
2536 ui.write(('matcher: %r\n' % m))
2534 items = list(repo[None].walk(m))
2537 items = list(repo[None].walk(m))
2535 if not items:
2538 if not items:
2536 return
2539 return
2537 f = lambda fn: fn
2540 f = lambda fn: fn
2538 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2541 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2539 f = lambda fn: util.normpath(fn)
2542 f = lambda fn: util.normpath(fn)
2540 fmt = 'f %%-%ds %%-%ds %%s' % (
2543 fmt = 'f %%-%ds %%-%ds %%s' % (
2541 max([len(abs) for abs in items]),
2544 max([len(abs) for abs in items]),
2542 max([len(m.rel(abs)) for abs in items]))
2545 max([len(m.rel(abs)) for abs in items]))
2543 for abs in items:
2546 for abs in items:
2544 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2547 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2545 ui.write("%s\n" % line.rstrip())
2548 ui.write("%s\n" % line.rstrip())
2546
2549
2547 @command('debugwhyunstable', [], _('REV'))
2550 @command('debugwhyunstable', [], _('REV'))
2548 def debugwhyunstable(ui, repo, rev):
2551 def debugwhyunstable(ui, repo, rev):
2549 """explain instabilities of a changeset"""
2552 """explain instabilities of a changeset"""
2550 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2553 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2551 dnodes = ''
2554 dnodes = ''
2552 if entry.get('divergentnodes'):
2555 if entry.get('divergentnodes'):
2553 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2556 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2554 for ctx in entry['divergentnodes']) + ' '
2557 for ctx in entry['divergentnodes']) + ' '
2555 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2558 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2556 entry['reason'], entry['node']))
2559 entry['reason'], entry['node']))
2557
2560
2558 @command('debugwireargs',
2561 @command('debugwireargs',
2559 [('', 'three', '', 'three'),
2562 [('', 'three', '', 'three'),
2560 ('', 'four', '', 'four'),
2563 ('', 'four', '', 'four'),
2561 ('', 'five', '', 'five'),
2564 ('', 'five', '', 'five'),
2562 ] + cmdutil.remoteopts,
2565 ] + cmdutil.remoteopts,
2563 _('REPO [OPTIONS]... [ONE [TWO]]'),
2566 _('REPO [OPTIONS]... [ONE [TWO]]'),
2564 norepo=True)
2567 norepo=True)
2565 def debugwireargs(ui, repopath, *vals, **opts):
2568 def debugwireargs(ui, repopath, *vals, **opts):
2566 opts = pycompat.byteskwargs(opts)
2569 opts = pycompat.byteskwargs(opts)
2567 repo = hg.peer(ui, opts, repopath)
2570 repo = hg.peer(ui, opts, repopath)
2568 for opt in cmdutil.remoteopts:
2571 for opt in cmdutil.remoteopts:
2569 del opts[opt[1]]
2572 del opts[opt[1]]
2570 args = {}
2573 args = {}
2571 for k, v in opts.iteritems():
2574 for k, v in opts.iteritems():
2572 if v:
2575 if v:
2573 args[k] = v
2576 args[k] = v
2574 args = pycompat.strkwargs(args)
2577 args = pycompat.strkwargs(args)
2575 # run twice to check that we don't mess up the stream for the next command
2578 # run twice to check that we don't mess up the stream for the next command
2576 res1 = repo.debugwireargs(*vals, **args)
2579 res1 = repo.debugwireargs(*vals, **args)
2577 res2 = repo.debugwireargs(*vals, **args)
2580 res2 = repo.debugwireargs(*vals, **args)
2578 ui.write("%s\n" % res1)
2581 ui.write("%s\n" % res1)
2579 if res1 != res2:
2582 if res1 != res2:
2580 ui.warn("%s\n" % res2)
2583 ui.warn("%s\n" % res2)
2581
2584
2582 def _parsewirelangblocks(fh):
2585 def _parsewirelangblocks(fh):
2583 activeaction = None
2586 activeaction = None
2584 blocklines = []
2587 blocklines = []
2585
2588
2586 for line in fh:
2589 for line in fh:
2587 line = line.rstrip()
2590 line = line.rstrip()
2588 if not line:
2591 if not line:
2589 continue
2592 continue
2590
2593
2591 if line.startswith(b'#'):
2594 if line.startswith(b'#'):
2592 continue
2595 continue
2593
2596
2594 if not line.startswith(' '):
2597 if not line.startswith(' '):
2595 # New block. Flush previous one.
2598 # New block. Flush previous one.
2596 if activeaction:
2599 if activeaction:
2597 yield activeaction, blocklines
2600 yield activeaction, blocklines
2598
2601
2599 activeaction = line
2602 activeaction = line
2600 blocklines = []
2603 blocklines = []
2601 continue
2604 continue
2602
2605
2603 # Else we start with an indent.
2606 # Else we start with an indent.
2604
2607
2605 if not activeaction:
2608 if not activeaction:
2606 raise error.Abort(_('indented line outside of block'))
2609 raise error.Abort(_('indented line outside of block'))
2607
2610
2608 blocklines.append(line)
2611 blocklines.append(line)
2609
2612
2610 # Flush last block.
2613 # Flush last block.
2611 if activeaction:
2614 if activeaction:
2612 yield activeaction, blocklines
2615 yield activeaction, blocklines
2613
2616
2614 @command('debugwireproto',
2617 @command('debugwireproto',
2615 [
2618 [
2616 ('', 'localssh', False, _('start an SSH server for this repo')),
2619 ('', 'localssh', False, _('start an SSH server for this repo')),
2617 ('', 'peer', '', _('construct a specific version of the peer')),
2620 ('', 'peer', '', _('construct a specific version of the peer')),
2618 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2621 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2619 ] + cmdutil.remoteopts,
2622 ] + cmdutil.remoteopts,
2620 _('[PATH]'),
2623 _('[PATH]'),
2621 optionalrepo=True)
2624 optionalrepo=True)
2622 def debugwireproto(ui, repo, path=None, **opts):
2625 def debugwireproto(ui, repo, path=None, **opts):
2623 """send wire protocol commands to a server
2626 """send wire protocol commands to a server
2624
2627
2625 This command can be used to issue wire protocol commands to remote
2628 This command can be used to issue wire protocol commands to remote
2626 peers and to debug the raw data being exchanged.
2629 peers and to debug the raw data being exchanged.
2627
2630
2628 ``--localssh`` will start an SSH server against the current repository
2631 ``--localssh`` will start an SSH server against the current repository
2629 and connect to that. By default, the connection will perform a handshake
2632 and connect to that. By default, the connection will perform a handshake
2630 and establish an appropriate peer instance.
2633 and establish an appropriate peer instance.
2631
2634
2632 ``--peer`` can be used to bypass the handshake protocol and construct a
2635 ``--peer`` can be used to bypass the handshake protocol and construct a
2633 peer instance using the specified class type. Valid values are ``raw``,
2636 peer instance using the specified class type. Valid values are ``raw``,
2634 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2637 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2635 raw data payloads and don't support higher-level command actions.
2638 raw data payloads and don't support higher-level command actions.
2636
2639
2637 ``--noreadstderr`` can be used to disable automatic reading from stderr
2640 ``--noreadstderr`` can be used to disable automatic reading from stderr
2638 of the peer (for SSH connections only). Disabling automatic reading of
2641 of the peer (for SSH connections only). Disabling automatic reading of
2639 stderr is useful for making output more deterministic.
2642 stderr is useful for making output more deterministic.
2640
2643
2641 Commands are issued via a mini language which is specified via stdin.
2644 Commands are issued via a mini language which is specified via stdin.
2642 The language consists of individual actions to perform. An action is
2645 The language consists of individual actions to perform. An action is
2643 defined by a block. A block is defined as a line with no leading
2646 defined by a block. A block is defined as a line with no leading
2644 space followed by 0 or more lines with leading space. Blocks are
2647 space followed by 0 or more lines with leading space. Blocks are
2645 effectively a high-level command with additional metadata.
2648 effectively a high-level command with additional metadata.
2646
2649
2647 Lines beginning with ``#`` are ignored.
2650 Lines beginning with ``#`` are ignored.
2648
2651
2649 The following sections denote available actions.
2652 The following sections denote available actions.
2650
2653
2651 raw
2654 raw
2652 ---
2655 ---
2653
2656
2654 Send raw data to the server.
2657 Send raw data to the server.
2655
2658
2656 The block payload contains the raw data to send as one atomic send
2659 The block payload contains the raw data to send as one atomic send
2657 operation. The data may not actually be delivered in a single system
2660 operation. The data may not actually be delivered in a single system
2658 call: it depends on the abilities of the transport being used.
2661 call: it depends on the abilities of the transport being used.
2659
2662
2660 Each line in the block is de-indented and concatenated. Then, that
2663 Each line in the block is de-indented and concatenated. Then, that
2661 value is evaluated as a Python b'' literal. This allows the use of
2664 value is evaluated as a Python b'' literal. This allows the use of
2662 backslash escaping, etc.
2665 backslash escaping, etc.
2663
2666
2664 raw+
2667 raw+
2665 ----
2668 ----
2666
2669
2667 Behaves like ``raw`` except flushes output afterwards.
2670 Behaves like ``raw`` except flushes output afterwards.
2668
2671
2669 command <X>
2672 command <X>
2670 -----------
2673 -----------
2671
2674
2672 Send a request to run a named command, whose name follows the ``command``
2675 Send a request to run a named command, whose name follows the ``command``
2673 string.
2676 string.
2674
2677
2675 Arguments to the command are defined as lines in this block. The format of
2678 Arguments to the command are defined as lines in this block. The format of
2676 each line is ``<key> <value>``. e.g.::
2679 each line is ``<key> <value>``. e.g.::
2677
2680
2678 command listkeys
2681 command listkeys
2679 namespace bookmarks
2682 namespace bookmarks
2680
2683
2681 If the value begins with ``eval:``, it will be interpreted as a Python
2684 If the value begins with ``eval:``, it will be interpreted as a Python
2682 literal expression. Otherwise values are interpreted as Python b'' literals.
2685 literal expression. Otherwise values are interpreted as Python b'' literals.
2683 This allows sending complex types and encoding special byte sequences via
2686 This allows sending complex types and encoding special byte sequences via
2684 backslash escaping.
2687 backslash escaping.
2685
2688
2686 The following arguments have special meaning:
2689 The following arguments have special meaning:
2687
2690
2688 ``PUSHFILE``
2691 ``PUSHFILE``
2689 When defined, the *push* mechanism of the peer will be used instead
2692 When defined, the *push* mechanism of the peer will be used instead
2690 of the static request-response mechanism and the content of the
2693 of the static request-response mechanism and the content of the
2691 file specified in the value of this argument will be sent as the
2694 file specified in the value of this argument will be sent as the
2692 command payload.
2695 command payload.
2693
2696
2694 This can be used to submit a local bundle file to the remote.
2697 This can be used to submit a local bundle file to the remote.
2695
2698
2696 batchbegin
2699 batchbegin
2697 ----------
2700 ----------
2698
2701
2699 Instruct the peer to begin a batched send.
2702 Instruct the peer to begin a batched send.
2700
2703
2701 All ``command`` blocks are queued for execution until the next
2704 All ``command`` blocks are queued for execution until the next
2702 ``batchsubmit`` block.
2705 ``batchsubmit`` block.
2703
2706
2704 batchsubmit
2707 batchsubmit
2705 -----------
2708 -----------
2706
2709
2707 Submit previously queued ``command`` blocks as a batch request.
2710 Submit previously queued ``command`` blocks as a batch request.
2708
2711
2709 This action MUST be paired with a ``batchbegin`` action.
2712 This action MUST be paired with a ``batchbegin`` action.
2710
2713
2711 httprequest <method> <path>
2714 httprequest <method> <path>
2712 ---------------------------
2715 ---------------------------
2713
2716
2714 (HTTP peer only)
2717 (HTTP peer only)
2715
2718
2716 Send an HTTP request to the peer.
2719 Send an HTTP request to the peer.
2717
2720
2718 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2721 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2719
2722
2720 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2723 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2721 headers to add to the request. e.g. ``Accept: foo``.
2724 headers to add to the request. e.g. ``Accept: foo``.
2722
2725
2723 The following arguments are special:
2726 The following arguments are special:
2724
2727
2725 ``BODYFILE``
2728 ``BODYFILE``
2726 The content of the file defined as the value to this argument will be
2729 The content of the file defined as the value to this argument will be
2727 transferred verbatim as the HTTP request body.
2730 transferred verbatim as the HTTP request body.
2728
2731
2729 ``frame <type> <flags> <payload>``
2732 ``frame <type> <flags> <payload>``
2730 Send a unified protocol frame as part of the request body.
2733 Send a unified protocol frame as part of the request body.
2731
2734
2732 All frames will be collected and sent as the body to the HTTP
2735 All frames will be collected and sent as the body to the HTTP
2733 request.
2736 request.
2734
2737
2735 close
2738 close
2736 -----
2739 -----
2737
2740
2738 Close the connection to the server.
2741 Close the connection to the server.
2739
2742
2740 flush
2743 flush
2741 -----
2744 -----
2742
2745
2743 Flush data written to the server.
2746 Flush data written to the server.
2744
2747
2745 readavailable
2748 readavailable
2746 -------------
2749 -------------
2747
2750
2748 Close the write end of the connection and read all available data from
2751 Close the write end of the connection and read all available data from
2749 the server.
2752 the server.
2750
2753
2751 If the connection to the server encompasses multiple pipes, we poll both
2754 If the connection to the server encompasses multiple pipes, we poll both
2752 pipes and read available data.
2755 pipes and read available data.
2753
2756
2754 readline
2757 readline
2755 --------
2758 --------
2756
2759
2757 Read a line of output from the server. If there are multiple output
2760 Read a line of output from the server. If there are multiple output
2758 pipes, reads only the main pipe.
2761 pipes, reads only the main pipe.
2759
2762
2760 ereadline
2763 ereadline
2761 ---------
2764 ---------
2762
2765
2763 Like ``readline``, but read from the stderr pipe, if available.
2766 Like ``readline``, but read from the stderr pipe, if available.
2764
2767
2765 read <X>
2768 read <X>
2766 --------
2769 --------
2767
2770
2768 ``read()`` N bytes from the server's main output pipe.
2771 ``read()`` N bytes from the server's main output pipe.
2769
2772
2770 eread <X>
2773 eread <X>
2771 ---------
2774 ---------
2772
2775
2773 ``read()`` N bytes from the server's stderr pipe, if available.
2776 ``read()`` N bytes from the server's stderr pipe, if available.
2774
2777
2775 Specifying Unified Frame-Based Protocol Frames
2778 Specifying Unified Frame-Based Protocol Frames
2776 ----------------------------------------------
2779 ----------------------------------------------
2777
2780
2778 It is possible to emit a *Unified Frame-Based Protocol* by using special
2781 It is possible to emit a *Unified Frame-Based Protocol* by using special
2779 syntax.
2782 syntax.
2780
2783
2781 A frame is composed as a type, flags, and payload. These can be parsed
2784 A frame is composed as a type, flags, and payload. These can be parsed
2782 from a string of the form:
2785 from a string of the form:
2783
2786
2784 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2787 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2785
2788
2786 ``request-id`` and ``stream-id`` are integers defining the request and
2789 ``request-id`` and ``stream-id`` are integers defining the request and
2787 stream identifiers.
2790 stream identifiers.
2788
2791
2789 ``type`` can be an integer value for the frame type or the string name
2792 ``type`` can be an integer value for the frame type or the string name
2790 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2793 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2791 ``command-name``.
2794 ``command-name``.
2792
2795
2793 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2796 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2794 components. Each component (and there can be just one) can be an integer
2797 components. Each component (and there can be just one) can be an integer
2795 or a flag name for stream flags or frame flags, respectively. Values are
2798 or a flag name for stream flags or frame flags, respectively. Values are
2796 resolved to integers and then bitwise OR'd together.
2799 resolved to integers and then bitwise OR'd together.
2797
2800
2798 ``payload`` represents the raw frame payload. If it begins with
2801 ``payload`` represents the raw frame payload. If it begins with
2799 ``cbor:``, the following string is evaluated as Python code and the
2802 ``cbor:``, the following string is evaluated as Python code and the
2800 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2803 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2801 as a Python byte string literal.
2804 as a Python byte string literal.
2802 """
2805 """
2803 opts = pycompat.byteskwargs(opts)
2806 opts = pycompat.byteskwargs(opts)
2804
2807
2805 if opts['localssh'] and not repo:
2808 if opts['localssh'] and not repo:
2806 raise error.Abort(_('--localssh requires a repository'))
2809 raise error.Abort(_('--localssh requires a repository'))
2807
2810
2808 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2811 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2809 raise error.Abort(_('invalid value for --peer'),
2812 raise error.Abort(_('invalid value for --peer'),
2810 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2813 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2811
2814
2812 if path and opts['localssh']:
2815 if path and opts['localssh']:
2813 raise error.Abort(_('cannot specify --localssh with an explicit '
2816 raise error.Abort(_('cannot specify --localssh with an explicit '
2814 'path'))
2817 'path'))
2815
2818
2816 if ui.interactive():
2819 if ui.interactive():
2817 ui.write(_('(waiting for commands on stdin)\n'))
2820 ui.write(_('(waiting for commands on stdin)\n'))
2818
2821
2819 blocks = list(_parsewirelangblocks(ui.fin))
2822 blocks = list(_parsewirelangblocks(ui.fin))
2820
2823
2821 proc = None
2824 proc = None
2822 stdin = None
2825 stdin = None
2823 stdout = None
2826 stdout = None
2824 stderr = None
2827 stderr = None
2825 opener = None
2828 opener = None
2826
2829
2827 if opts['localssh']:
2830 if opts['localssh']:
2828 # We start the SSH server in its own process so there is process
2831 # We start the SSH server in its own process so there is process
2829 # separation. This prevents a whole class of potential bugs around
2832 # separation. This prevents a whole class of potential bugs around
2830 # shared state from interfering with server operation.
2833 # shared state from interfering with server operation.
2831 args = procutil.hgcmd() + [
2834 args = procutil.hgcmd() + [
2832 '-R', repo.root,
2835 '-R', repo.root,
2833 'debugserve', '--sshstdio',
2836 'debugserve', '--sshstdio',
2834 ]
2837 ]
2835 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2838 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2836 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2839 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2837 bufsize=0)
2840 bufsize=0)
2838
2841
2839 stdin = proc.stdin
2842 stdin = proc.stdin
2840 stdout = proc.stdout
2843 stdout = proc.stdout
2841 stderr = proc.stderr
2844 stderr = proc.stderr
2842
2845
2843 # We turn the pipes into observers so we can log I/O.
2846 # We turn the pipes into observers so we can log I/O.
2844 if ui.verbose or opts['peer'] == 'raw':
2847 if ui.verbose or opts['peer'] == 'raw':
2845 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2848 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2846 logdata=True)
2849 logdata=True)
2847 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2850 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2848 logdata=True)
2851 logdata=True)
2849 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2852 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2850 logdata=True)
2853 logdata=True)
2851
2854
2852 # --localssh also implies the peer connection settings.
2855 # --localssh also implies the peer connection settings.
2853
2856
2854 url = 'ssh://localserver'
2857 url = 'ssh://localserver'
2855 autoreadstderr = not opts['noreadstderr']
2858 autoreadstderr = not opts['noreadstderr']
2856
2859
2857 if opts['peer'] == 'ssh1':
2860 if opts['peer'] == 'ssh1':
2858 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2861 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2859 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2862 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2860 None, autoreadstderr=autoreadstderr)
2863 None, autoreadstderr=autoreadstderr)
2861 elif opts['peer'] == 'ssh2':
2864 elif opts['peer'] == 'ssh2':
2862 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2865 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2863 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2866 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2864 None, autoreadstderr=autoreadstderr)
2867 None, autoreadstderr=autoreadstderr)
2865 elif opts['peer'] == 'raw':
2868 elif opts['peer'] == 'raw':
2866 ui.write(_('using raw connection to peer\n'))
2869 ui.write(_('using raw connection to peer\n'))
2867 peer = None
2870 peer = None
2868 else:
2871 else:
2869 ui.write(_('creating ssh peer from handshake results\n'))
2872 ui.write(_('creating ssh peer from handshake results\n'))
2870 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2873 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2871 autoreadstderr=autoreadstderr)
2874 autoreadstderr=autoreadstderr)
2872
2875
2873 elif path:
2876 elif path:
2874 # We bypass hg.peer() so we can proxy the sockets.
2877 # We bypass hg.peer() so we can proxy the sockets.
2875 # TODO consider not doing this because we skip
2878 # TODO consider not doing this because we skip
2876 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2879 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2877 u = util.url(path)
2880 u = util.url(path)
2878 if u.scheme != 'http':
2881 if u.scheme != 'http':
2879 raise error.Abort(_('only http:// paths are currently supported'))
2882 raise error.Abort(_('only http:// paths are currently supported'))
2880
2883
2881 url, authinfo = u.authinfo()
2884 url, authinfo = u.authinfo()
2882 openerargs = {
2885 openerargs = {
2883 r'useragent': b'Mercurial debugwireproto',
2886 r'useragent': b'Mercurial debugwireproto',
2884 }
2887 }
2885
2888
2886 # Turn pipes/sockets into observers so we can log I/O.
2889 # Turn pipes/sockets into observers so we can log I/O.
2887 if ui.verbose:
2890 if ui.verbose:
2888 openerargs.update({
2891 openerargs.update({
2889 r'loggingfh': ui,
2892 r'loggingfh': ui,
2890 r'loggingname': b's',
2893 r'loggingname': b's',
2891 r'loggingopts': {
2894 r'loggingopts': {
2892 r'logdata': True,
2895 r'logdata': True,
2893 r'logdataapis': False,
2896 r'logdataapis': False,
2894 },
2897 },
2895 })
2898 })
2896
2899
2897 if ui.debugflag:
2900 if ui.debugflag:
2898 openerargs[r'loggingopts'][r'logdataapis'] = True
2901 openerargs[r'loggingopts'][r'logdataapis'] = True
2899
2902
2900 # Don't send default headers when in raw mode. This allows us to
2903 # Don't send default headers when in raw mode. This allows us to
2901 # bypass most of the behavior of our URL handling code so we can
2904 # bypass most of the behavior of our URL handling code so we can
2902 # have near complete control over what's sent on the wire.
2905 # have near complete control over what's sent on the wire.
2903 if opts['peer'] == 'raw':
2906 if opts['peer'] == 'raw':
2904 openerargs[r'sendaccept'] = False
2907 openerargs[r'sendaccept'] = False
2905
2908
2906 opener = urlmod.opener(ui, authinfo, **openerargs)
2909 opener = urlmod.opener(ui, authinfo, **openerargs)
2907
2910
2908 if opts['peer'] == 'http2':
2911 if opts['peer'] == 'http2':
2909 ui.write(_('creating http peer for wire protocol version 2\n'))
2912 ui.write(_('creating http peer for wire protocol version 2\n'))
2910 peer = httppeer.httpv2peer(ui, path, opener)
2913 peer = httppeer.httpv2peer(ui, path, opener)
2911 elif opts['peer'] == 'raw':
2914 elif opts['peer'] == 'raw':
2912 ui.write(_('using raw connection to peer\n'))
2915 ui.write(_('using raw connection to peer\n'))
2913 peer = None
2916 peer = None
2914 elif opts['peer']:
2917 elif opts['peer']:
2915 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2918 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2916 opts['peer'])
2919 opts['peer'])
2917 else:
2920 else:
2918 peer = httppeer.makepeer(ui, path, opener=opener)
2921 peer = httppeer.makepeer(ui, path, opener=opener)
2919
2922
2920 # We /could/ populate stdin/stdout with sock.makefile()...
2923 # We /could/ populate stdin/stdout with sock.makefile()...
2921 else:
2924 else:
2922 raise error.Abort(_('unsupported connection configuration'))
2925 raise error.Abort(_('unsupported connection configuration'))
2923
2926
2924 batchedcommands = None
2927 batchedcommands = None
2925
2928
2926 # Now perform actions based on the parsed wire language instructions.
2929 # Now perform actions based on the parsed wire language instructions.
2927 for action, lines in blocks:
2930 for action, lines in blocks:
2928 if action in ('raw', 'raw+'):
2931 if action in ('raw', 'raw+'):
2929 if not stdin:
2932 if not stdin:
2930 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2933 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2931
2934
2932 # Concatenate the data together.
2935 # Concatenate the data together.
2933 data = ''.join(l.lstrip() for l in lines)
2936 data = ''.join(l.lstrip() for l in lines)
2934 data = stringutil.unescapestr(data)
2937 data = stringutil.unescapestr(data)
2935 stdin.write(data)
2938 stdin.write(data)
2936
2939
2937 if action == 'raw+':
2940 if action == 'raw+':
2938 stdin.flush()
2941 stdin.flush()
2939 elif action == 'flush':
2942 elif action == 'flush':
2940 if not stdin:
2943 if not stdin:
2941 raise error.Abort(_('cannot call flush on this peer'))
2944 raise error.Abort(_('cannot call flush on this peer'))
2942 stdin.flush()
2945 stdin.flush()
2943 elif action.startswith('command'):
2946 elif action.startswith('command'):
2944 if not peer:
2947 if not peer:
2945 raise error.Abort(_('cannot send commands unless peer instance '
2948 raise error.Abort(_('cannot send commands unless peer instance '
2946 'is available'))
2949 'is available'))
2947
2950
2948 command = action.split(' ', 1)[1]
2951 command = action.split(' ', 1)[1]
2949
2952
2950 args = {}
2953 args = {}
2951 for line in lines:
2954 for line in lines:
2952 # We need to allow empty values.
2955 # We need to allow empty values.
2953 fields = line.lstrip().split(' ', 1)
2956 fields = line.lstrip().split(' ', 1)
2954 if len(fields) == 1:
2957 if len(fields) == 1:
2955 key = fields[0]
2958 key = fields[0]
2956 value = ''
2959 value = ''
2957 else:
2960 else:
2958 key, value = fields
2961 key, value = fields
2959
2962
2960 if value.startswith('eval:'):
2963 if value.startswith('eval:'):
2961 value = stringutil.evalpythonliteral(value[5:])
2964 value = stringutil.evalpythonliteral(value[5:])
2962 else:
2965 else:
2963 value = stringutil.unescapestr(value)
2966 value = stringutil.unescapestr(value)
2964
2967
2965 args[key] = value
2968 args[key] = value
2966
2969
2967 if batchedcommands is not None:
2970 if batchedcommands is not None:
2968 batchedcommands.append((command, args))
2971 batchedcommands.append((command, args))
2969 continue
2972 continue
2970
2973
2971 ui.status(_('sending %s command\n') % command)
2974 ui.status(_('sending %s command\n') % command)
2972
2975
2973 if 'PUSHFILE' in args:
2976 if 'PUSHFILE' in args:
2974 with open(args['PUSHFILE'], r'rb') as fh:
2977 with open(args['PUSHFILE'], r'rb') as fh:
2975 del args['PUSHFILE']
2978 del args['PUSHFILE']
2976 res, output = peer._callpush(command, fh,
2979 res, output = peer._callpush(command, fh,
2977 **pycompat.strkwargs(args))
2980 **pycompat.strkwargs(args))
2978 ui.status(_('result: %s\n') % stringutil.escapestr(res))
2981 ui.status(_('result: %s\n') % stringutil.escapestr(res))
2979 ui.status(_('remote output: %s\n') %
2982 ui.status(_('remote output: %s\n') %
2980 stringutil.escapestr(output))
2983 stringutil.escapestr(output))
2981 else:
2984 else:
2982 res = peer._call(command, **pycompat.strkwargs(args))
2985 res = peer._call(command, **pycompat.strkwargs(args))
2983 ui.status(_('response: %s\n') % stringutil.pprint(res))
2986 ui.status(_('response: %s\n') % stringutil.pprint(res))
2984
2987
2985 elif action == 'batchbegin':
2988 elif action == 'batchbegin':
2986 if batchedcommands is not None:
2989 if batchedcommands is not None:
2987 raise error.Abort(_('nested batchbegin not allowed'))
2990 raise error.Abort(_('nested batchbegin not allowed'))
2988
2991
2989 batchedcommands = []
2992 batchedcommands = []
2990 elif action == 'batchsubmit':
2993 elif action == 'batchsubmit':
2991 # There is a batching API we could go through. But it would be
2994 # There is a batching API we could go through. But it would be
2992 # difficult to normalize requests into function calls. It is easier
2995 # difficult to normalize requests into function calls. It is easier
2993 # to bypass this layer and normalize to commands + args.
2996 # to bypass this layer and normalize to commands + args.
2994 ui.status(_('sending batch with %d sub-commands\n') %
2997 ui.status(_('sending batch with %d sub-commands\n') %
2995 len(batchedcommands))
2998 len(batchedcommands))
2996 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
2999 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
2997 ui.status(_('response #%d: %s\n') %
3000 ui.status(_('response #%d: %s\n') %
2998 (i, stringutil.escapestr(chunk)))
3001 (i, stringutil.escapestr(chunk)))
2999
3002
3000 batchedcommands = None
3003 batchedcommands = None
3001
3004
3002 elif action.startswith('httprequest '):
3005 elif action.startswith('httprequest '):
3003 if not opener:
3006 if not opener:
3004 raise error.Abort(_('cannot use httprequest without an HTTP '
3007 raise error.Abort(_('cannot use httprequest without an HTTP '
3005 'peer'))
3008 'peer'))
3006
3009
3007 request = action.split(' ', 2)
3010 request = action.split(' ', 2)
3008 if len(request) != 3:
3011 if len(request) != 3:
3009 raise error.Abort(_('invalid httprequest: expected format is '
3012 raise error.Abort(_('invalid httprequest: expected format is '
3010 '"httprequest <method> <path>'))
3013 '"httprequest <method> <path>'))
3011
3014
3012 method, httppath = request[1:]
3015 method, httppath = request[1:]
3013 headers = {}
3016 headers = {}
3014 body = None
3017 body = None
3015 frames = []
3018 frames = []
3016 for line in lines:
3019 for line in lines:
3017 line = line.lstrip()
3020 line = line.lstrip()
3018 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3021 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3019 if m:
3022 if m:
3020 headers[m.group(1)] = m.group(2)
3023 headers[m.group(1)] = m.group(2)
3021 continue
3024 continue
3022
3025
3023 if line.startswith(b'BODYFILE '):
3026 if line.startswith(b'BODYFILE '):
3024 with open(line.split(b' ', 1), 'rb') as fh:
3027 with open(line.split(b' ', 1), 'rb') as fh:
3025 body = fh.read()
3028 body = fh.read()
3026 elif line.startswith(b'frame '):
3029 elif line.startswith(b'frame '):
3027 frame = wireprotoframing.makeframefromhumanstring(
3030 frame = wireprotoframing.makeframefromhumanstring(
3028 line[len(b'frame '):])
3031 line[len(b'frame '):])
3029
3032
3030 frames.append(frame)
3033 frames.append(frame)
3031 else:
3034 else:
3032 raise error.Abort(_('unknown argument to httprequest: %s') %
3035 raise error.Abort(_('unknown argument to httprequest: %s') %
3033 line)
3036 line)
3034
3037
3035 url = path + httppath
3038 url = path + httppath
3036
3039
3037 if frames:
3040 if frames:
3038 body = b''.join(bytes(f) for f in frames)
3041 body = b''.join(bytes(f) for f in frames)
3039
3042
3040 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3043 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3041
3044
3042 # urllib.Request insists on using has_data() as a proxy for
3045 # urllib.Request insists on using has_data() as a proxy for
3043 # determining the request method. Override that to use our
3046 # determining the request method. Override that to use our
3044 # explicitly requested method.
3047 # explicitly requested method.
3045 req.get_method = lambda: method
3048 req.get_method = lambda: method
3046
3049
3047 try:
3050 try:
3048 opener.open(req).read()
3051 res = opener.open(req)
3052 body = res.read()
3049 except util.urlerr.urlerror as e:
3053 except util.urlerr.urlerror as e:
3050 e.read()
3054 e.read()
3055 continue
3056
3057 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3058 ui.write(_('cbor> %s\n') % stringutil.pprint(cbor.loads(body)))
3051
3059
3052 elif action == 'close':
3060 elif action == 'close':
3053 peer.close()
3061 peer.close()
3054 elif action == 'readavailable':
3062 elif action == 'readavailable':
3055 if not stdout or not stderr:
3063 if not stdout or not stderr:
3056 raise error.Abort(_('readavailable not available on this peer'))
3064 raise error.Abort(_('readavailable not available on this peer'))
3057
3065
3058 stdin.close()
3066 stdin.close()
3059 stdout.read()
3067 stdout.read()
3060 stderr.read()
3068 stderr.read()
3061
3069
3062 elif action == 'readline':
3070 elif action == 'readline':
3063 if not stdout:
3071 if not stdout:
3064 raise error.Abort(_('readline not available on this peer'))
3072 raise error.Abort(_('readline not available on this peer'))
3065 stdout.readline()
3073 stdout.readline()
3066 elif action == 'ereadline':
3074 elif action == 'ereadline':
3067 if not stderr:
3075 if not stderr:
3068 raise error.Abort(_('ereadline not available on this peer'))
3076 raise error.Abort(_('ereadline not available on this peer'))
3069 stderr.readline()
3077 stderr.readline()
3070 elif action.startswith('read '):
3078 elif action.startswith('read '):
3071 count = int(action.split(' ', 1)[1])
3079 count = int(action.split(' ', 1)[1])
3072 if not stdout:
3080 if not stdout:
3073 raise error.Abort(_('read not available on this peer'))
3081 raise error.Abort(_('read not available on this peer'))
3074 stdout.read(count)
3082 stdout.read(count)
3075 elif action.startswith('eread '):
3083 elif action.startswith('eread '):
3076 count = int(action.split(' ', 1)[1])
3084 count = int(action.split(' ', 1)[1])
3077 if not stderr:
3085 if not stderr:
3078 raise error.Abort(_('eread not available on this peer'))
3086 raise error.Abort(_('eread not available on this peer'))
3079 stderr.read(count)
3087 stderr.read(count)
3080 else:
3088 else:
3081 raise error.Abort(_('unknown action: %s') % action)
3089 raise error.Abort(_('unknown action: %s') % action)
3082
3090
3083 if batchedcommands is not None:
3091 if batchedcommands is not None:
3084 raise error.Abort(_('unclosed "batchbegin" request'))
3092 raise error.Abort(_('unclosed "batchbegin" request'))
3085
3093
3086 if peer:
3094 if peer:
3087 peer.close()
3095 peer.close()
3088
3096
3089 if proc:
3097 if proc:
3090 proc.kill()
3098 proc.kill()
@@ -1,1807 +1,1852 b''
1 The Mercurial wire protocol is a request-response based protocol
1 The Mercurial wire protocol is a request-response based protocol
2 with multiple wire representations.
2 with multiple wire representations.
3
3
4 Each request is modeled as a command name, a dictionary of arguments, and
4 Each request is modeled as a command name, a dictionary of arguments, and
5 optional raw input. Command arguments and their types are intrinsic
5 optional raw input. Command arguments and their types are intrinsic
6 properties of commands. So is the response type of the command. This means
6 properties of commands. So is the response type of the command. This means
7 clients can't always send arbitrary arguments to servers and servers can't
7 clients can't always send arbitrary arguments to servers and servers can't
8 return multiple response types.
8 return multiple response types.
9
9
10 The protocol is synchronous and does not support multiplexing (concurrent
10 The protocol is synchronous and does not support multiplexing (concurrent
11 commands).
11 commands).
12
12
13 Handshake
13 Handshake
14 =========
14 =========
15
15
16 It is required or common for clients to perform a *handshake* when connecting
16 It is required or common for clients to perform a *handshake* when connecting
17 to a server. The handshake serves the following purposes:
17 to a server. The handshake serves the following purposes:
18
18
19 * Negotiating protocol/transport level options
19 * Negotiating protocol/transport level options
20 * Allows the client to learn about server capabilities to influence
20 * Allows the client to learn about server capabilities to influence
21 future requests
21 future requests
22 * Ensures the underlying transport channel is in a *clean* state
22 * Ensures the underlying transport channel is in a *clean* state
23
23
24 An important goal of the handshake is to allow clients to use more modern
24 An important goal of the handshake is to allow clients to use more modern
25 wire protocol features. By default, clients must assume they are talking
25 wire protocol features. By default, clients must assume they are talking
26 to an old version of Mercurial server (possibly even the very first
26 to an old version of Mercurial server (possibly even the very first
27 implementation). So, clients should not attempt to call or utilize modern
27 implementation). So, clients should not attempt to call or utilize modern
28 wire protocol features until they have confirmation that the server
28 wire protocol features until they have confirmation that the server
29 supports them. The handshake implementation is designed to allow both
29 supports them. The handshake implementation is designed to allow both
30 ends to utilize the latest set of features and capabilities with as
30 ends to utilize the latest set of features and capabilities with as
31 few round trips as possible.
31 few round trips as possible.
32
32
33 The handshake mechanism varies by transport and protocol and is documented
33 The handshake mechanism varies by transport and protocol and is documented
34 in the sections below.
34 in the sections below.
35
35
36 HTTP Protocol
36 HTTP Protocol
37 =============
37 =============
38
38
39 Handshake
39 Handshake
40 ---------
40 ---------
41
41
42 The client sends a ``capabilities`` command request (``?cmd=capabilities``)
42 The client sends a ``capabilities`` command request (``?cmd=capabilities``)
43 as soon as HTTP requests may be issued.
43 as soon as HTTP requests may be issued.
44
44
45 The server responds with a capabilities string, which the client parses to
45 By default, the server responds with a version 1 capabilities string, which
46 learn about the server's abilities.
46 the client parses to learn about the server's abilities. The ``Content-Type``
47 for this response is ``application/mercurial-0.1`` or
48 ``application/mercurial-0.2`` depending on whether the client advertised
49 support for version ``0.2`` in its request. (Clients aren't supposed to
50 advertise support for ``0.2`` until the capabilities response indicates
51 the server's support for that media type. However, a client could
52 conceivably cache this metadata and issue the capabilities request in such
53 a way to elicit an ``application/mercurial-0.2`` response.)
54
55 Clients wishing to switch to a newer API service may send an
56 ``X-HgUpgrade-<X>`` header containing a space-delimited list of API service
57 names the client is capable of speaking. The request MUST also include an
58 ``X-HgProto-<X>`` header advertising a known serialization format for the
59 response. ``cbor`` is currently the only defined serialization format.
60
61 If the request contains these headers, the response ``Content-Type`` MAY
62 be for a different media type. e.g. ``application/mercurial-cbor`` if the
63 client advertises support for CBOR.
64
65 The response MUST be deserializable to a map with the following keys:
66
67 apibase
68 URL path to API services, relative to the repository root. e.g. ``api/``.
69
70 apis
71 A map of API service names to API descriptors. An API descriptor contains
72 more details about that API. In the case of the HTTP Version 2 Transport,
73 it will be the normal response to a ``capabilities`` command.
74
75 Only the services advertised by the client that are also available on
76 the server are advertised.
77
78 v1capabilities
79 The capabilities string that would be returned by a version 1 response.
80
81 The client can then inspect the server-advertised APIs and decide which
82 API to use, including continuing to use the HTTP Version 1 Transport.
47
83
48 HTTP Version 1 Transport
84 HTTP Version 1 Transport
49 ------------------------
85 ------------------------
50
86
51 Commands are issued as HTTP/1.0 or HTTP/1.1 requests. Commands are
87 Commands are issued as HTTP/1.0 or HTTP/1.1 requests. Commands are
52 sent to the base URL of the repository with the command name sent in
88 sent to the base URL of the repository with the command name sent in
53 the ``cmd`` query string parameter. e.g.
89 the ``cmd`` query string parameter. e.g.
54 ``https://example.com/repo?cmd=capabilities``. The HTTP method is ``GET``
90 ``https://example.com/repo?cmd=capabilities``. The HTTP method is ``GET``
55 or ``POST`` depending on the command and whether there is a request
91 or ``POST`` depending on the command and whether there is a request
56 body.
92 body.
57
93
58 Command arguments can be sent multiple ways.
94 Command arguments can be sent multiple ways.
59
95
60 The simplest is part of the URL query string using ``x-www-form-urlencoded``
96 The simplest is part of the URL query string using ``x-www-form-urlencoded``
61 encoding (see Python's ``urllib.urlencode()``. However, many servers impose
97 encoding (see Python's ``urllib.urlencode()``. However, many servers impose
62 length limitations on the URL. So this mechanism is typically only used if
98 length limitations on the URL. So this mechanism is typically only used if
63 the server doesn't support other mechanisms.
99 the server doesn't support other mechanisms.
64
100
65 If the server supports the ``httpheader`` capability, command arguments can
101 If the server supports the ``httpheader`` capability, command arguments can
66 be sent in HTTP request headers named ``X-HgArg-<N>`` where ``<N>`` is an
102 be sent in HTTP request headers named ``X-HgArg-<N>`` where ``<N>`` is an
67 integer starting at 1. A ``x-www-form-urlencoded`` representation of the
103 integer starting at 1. A ``x-www-form-urlencoded`` representation of the
68 arguments is obtained. This full string is then split into chunks and sent
104 arguments is obtained. This full string is then split into chunks and sent
69 in numbered ``X-HgArg-<N>`` headers. The maximum length of each HTTP header
105 in numbered ``X-HgArg-<N>`` headers. The maximum length of each HTTP header
70 is defined by the server in the ``httpheader`` capability value, which defaults
106 is defined by the server in the ``httpheader`` capability value, which defaults
71 to ``1024``. The server reassembles the encoded arguments string by
107 to ``1024``. The server reassembles the encoded arguments string by
72 concatenating the ``X-HgArg-<N>`` headers then URL decodes them into a
108 concatenating the ``X-HgArg-<N>`` headers then URL decodes them into a
73 dictionary.
109 dictionary.
74
110
75 The list of ``X-HgArg-<N>`` headers should be added to the ``Vary`` request
111 The list of ``X-HgArg-<N>`` headers should be added to the ``Vary`` request
76 header to instruct caches to take these headers into consideration when caching
112 header to instruct caches to take these headers into consideration when caching
77 requests.
113 requests.
78
114
79 If the server supports the ``httppostargs`` capability, the client
115 If the server supports the ``httppostargs`` capability, the client
80 may send command arguments in the HTTP request body as part of an
116 may send command arguments in the HTTP request body as part of an
81 HTTP POST request. The command arguments will be URL encoded just like
117 HTTP POST request. The command arguments will be URL encoded just like
82 they would for sending them via HTTP headers. However, no splitting is
118 they would for sending them via HTTP headers. However, no splitting is
83 performed: the raw arguments are included in the HTTP request body.
119 performed: the raw arguments are included in the HTTP request body.
84
120
85 The client sends a ``X-HgArgs-Post`` header with the string length of the
121 The client sends a ``X-HgArgs-Post`` header with the string length of the
86 encoded arguments data. Additional data may be included in the HTTP
122 encoded arguments data. Additional data may be included in the HTTP
87 request body immediately following the argument data. The offset of the
123 request body immediately following the argument data. The offset of the
88 non-argument data is defined by the ``X-HgArgs-Post`` header. The
124 non-argument data is defined by the ``X-HgArgs-Post`` header. The
89 ``X-HgArgs-Post`` header is not required if there is no argument data.
125 ``X-HgArgs-Post`` header is not required if there is no argument data.
90
126
91 Additional command data can be sent as part of the HTTP request body. The
127 Additional command data can be sent as part of the HTTP request body. The
92 default ``Content-Type`` when sending data is ``application/mercurial-0.1``.
128 default ``Content-Type`` when sending data is ``application/mercurial-0.1``.
93 A ``Content-Length`` header is currently always sent.
129 A ``Content-Length`` header is currently always sent.
94
130
95 Example HTTP requests::
131 Example HTTP requests::
96
132
97 GET /repo?cmd=capabilities
133 GET /repo?cmd=capabilities
98 X-HgArg-1: foo=bar&baz=hello%20world
134 X-HgArg-1: foo=bar&baz=hello%20world
99
135
100 The request media type should be chosen based on server support. If the
136 The request media type should be chosen based on server support. If the
101 ``httpmediatype`` server capability is present, the client should send
137 ``httpmediatype`` server capability is present, the client should send
102 the newest mutually supported media type. If this capability is absent,
138 the newest mutually supported media type. If this capability is absent,
103 the client must assume the server only supports the
139 the client must assume the server only supports the
104 ``application/mercurial-0.1`` media type.
140 ``application/mercurial-0.1`` media type.
105
141
106 The ``Content-Type`` HTTP response header identifies the response as coming
142 The ``Content-Type`` HTTP response header identifies the response as coming
107 from Mercurial and can also be used to signal an error has occurred.
143 from Mercurial and can also be used to signal an error has occurred.
108
144
109 The ``application/mercurial-*`` media types indicate a generic Mercurial
145 The ``application/mercurial-*`` media types indicate a generic Mercurial
110 data type.
146 data type.
111
147
112 The ``application/mercurial-0.1`` media type is raw Mercurial data. It is the
148 The ``application/mercurial-0.1`` media type is raw Mercurial data. It is the
113 predecessor of the format below.
149 predecessor of the format below.
114
150
115 The ``application/mercurial-0.2`` media type is compression framed Mercurial
151 The ``application/mercurial-0.2`` media type is compression framed Mercurial
116 data. The first byte of the payload indicates the length of the compression
152 data. The first byte of the payload indicates the length of the compression
117 format identifier that follows. Next are N bytes indicating the compression
153 format identifier that follows. Next are N bytes indicating the compression
118 format. e.g. ``zlib``. The remaining bytes are compressed according to that
154 format. e.g. ``zlib``. The remaining bytes are compressed according to that
119 compression format. The decompressed data behaves the same as with
155 compression format. The decompressed data behaves the same as with
120 ``application/mercurial-0.1``.
156 ``application/mercurial-0.1``.
121
157
122 The ``application/hg-error`` media type indicates a generic error occurred.
158 The ``application/hg-error`` media type indicates a generic error occurred.
123 The content of the HTTP response body typically holds text describing the
159 The content of the HTTP response body typically holds text describing the
124 error.
160 error.
125
161
162 The ``application/mercurial-cbor`` media type indicates a CBOR payload
163 and should be interpreted as identical to ``application/cbor``.
164
126 Behavior of media types is further described in the ``Content Negotiation``
165 Behavior of media types is further described in the ``Content Negotiation``
127 section below.
166 section below.
128
167
129 Clients should issue a ``User-Agent`` request header that identifies the client.
168 Clients should issue a ``User-Agent`` request header that identifies the client.
130 The server should not use the ``User-Agent`` for feature detection.
169 The server should not use the ``User-Agent`` for feature detection.
131
170
132 A command returning a ``string`` response issues a
171 A command returning a ``string`` response issues a
133 ``application/mercurial-0.*`` media type and the HTTP response body contains
172 ``application/mercurial-0.*`` media type and the HTTP response body contains
134 the raw string value (after compression decoding, if used). A
173 the raw string value (after compression decoding, if used). A
135 ``Content-Length`` header is typically issued, but not required.
174 ``Content-Length`` header is typically issued, but not required.
136
175
137 A command returning a ``stream`` response issues a
176 A command returning a ``stream`` response issues a
138 ``application/mercurial-0.*`` media type and the HTTP response is typically
177 ``application/mercurial-0.*`` media type and the HTTP response is typically
139 using *chunked transfer* (``Transfer-Encoding: chunked``).
178 using *chunked transfer* (``Transfer-Encoding: chunked``).
140
179
141 HTTP Version 2 Transport
180 HTTP Version 2 Transport
142 ------------------------
181 ------------------------
143
182
144 **Experimental - feature under active development**
183 **Experimental - feature under active development**
145
184
146 Version 2 of the HTTP protocol is exposed under the ``/api/*`` URL space.
185 Version 2 of the HTTP protocol is exposed under the ``/api/*`` URL space.
147 It's final API name is not yet formalized.
186 It's final API name is not yet formalized.
148
187
149 Commands are triggered by sending HTTP POST requests against URLs of the
188 Commands are triggered by sending HTTP POST requests against URLs of the
150 form ``<permission>/<command>``, where ``<permission>`` is ``ro`` or
189 form ``<permission>/<command>``, where ``<permission>`` is ``ro`` or
151 ``rw``, meaning read-only and read-write, respectively and ``<command>``
190 ``rw``, meaning read-only and read-write, respectively and ``<command>``
152 is a named wire protocol command.
191 is a named wire protocol command.
153
192
154 Non-POST request methods MUST be rejected by the server with an HTTP
193 Non-POST request methods MUST be rejected by the server with an HTTP
155 405 response.
194 405 response.
156
195
157 Commands that modify repository state in meaningful ways MUST NOT be
196 Commands that modify repository state in meaningful ways MUST NOT be
158 exposed under the ``ro`` URL prefix. All available commands MUST be
197 exposed under the ``ro`` URL prefix. All available commands MUST be
159 available under the ``rw`` URL prefix.
198 available under the ``rw`` URL prefix.
160
199
161 Server adminstrators MAY implement blanket HTTP authentication keyed
200 Server adminstrators MAY implement blanket HTTP authentication keyed
162 off the URL prefix. For example, a server may require authentication
201 off the URL prefix. For example, a server may require authentication
163 for all ``rw/*`` URLs and let unauthenticated requests to ``ro/*``
202 for all ``rw/*`` URLs and let unauthenticated requests to ``ro/*``
164 URL proceed. A server MAY issue an HTTP 401, 403, or 407 response
203 URL proceed. A server MAY issue an HTTP 401, 403, or 407 response
165 in accordance with RFC 7235. Clients SHOULD recognize the HTTP Basic
204 in accordance with RFC 7235. Clients SHOULD recognize the HTTP Basic
166 (RFC 7617) and Digest (RFC 7616) authentication schemes. Clients SHOULD
205 (RFC 7617) and Digest (RFC 7616) authentication schemes. Clients SHOULD
167 make an attempt to recognize unknown schemes using the
206 make an attempt to recognize unknown schemes using the
168 ``WWW-Authenticate`` response header on a 401 response, as defined by
207 ``WWW-Authenticate`` response header on a 401 response, as defined by
169 RFC 7235.
208 RFC 7235.
170
209
171 Read-only commands are accessible under ``rw/*`` URLs so clients can
210 Read-only commands are accessible under ``rw/*`` URLs so clients can
172 signal the intent of the operation very early in the connection
211 signal the intent of the operation very early in the connection
173 lifecycle. For example, a ``push`` operation - which consists of
212 lifecycle. For example, a ``push`` operation - which consists of
174 various read-only commands mixed with at least one read-write command -
213 various read-only commands mixed with at least one read-write command -
175 can perform all commands against ``rw/*`` URLs so that any server-side
214 can perform all commands against ``rw/*`` URLs so that any server-side
176 authentication requirements are discovered upon attempting the first
215 authentication requirements are discovered upon attempting the first
177 command - not potentially several commands into the exchange. This
216 command - not potentially several commands into the exchange. This
178 allows clients to fail faster or prompt for credentials as soon as the
217 allows clients to fail faster or prompt for credentials as soon as the
179 exchange takes place. This provides a better end-user experience.
218 exchange takes place. This provides a better end-user experience.
180
219
181 Requests to unknown commands or URLS result in an HTTP 404.
220 Requests to unknown commands or URLS result in an HTTP 404.
182 TODO formally define response type, how error is communicated, etc.
221 TODO formally define response type, how error is communicated, etc.
183
222
184 HTTP request and response bodies use the *Unified Frame-Based Protocol*
223 HTTP request and response bodies use the *Unified Frame-Based Protocol*
185 (defined below) for media exchange. The entirety of the HTTP message
224 (defined below) for media exchange. The entirety of the HTTP message
186 body is 0 or more frames as defined by this protocol.
225 body is 0 or more frames as defined by this protocol.
187
226
188 Clients and servers MUST advertise the ``TBD`` media type via the
227 Clients and servers MUST advertise the ``TBD`` media type via the
189 ``Content-Type`` request and response headers. In addition, clients MUST
228 ``Content-Type`` request and response headers. In addition, clients MUST
190 advertise this media type value in their ``Accept`` request header in all
229 advertise this media type value in their ``Accept`` request header in all
191 requests.
230 requests.
192 TODO finalize the media type. For now, it is defined in wireprotoserver.py.
231 TODO finalize the media type. For now, it is defined in wireprotoserver.py.
193
232
194 Servers receiving requests without an ``Accept`` header SHOULD respond with
233 Servers receiving requests without an ``Accept`` header SHOULD respond with
195 an HTTP 406.
234 an HTTP 406.
196
235
197 Servers receiving requests with an invalid ``Content-Type`` header SHOULD
236 Servers receiving requests with an invalid ``Content-Type`` header SHOULD
198 respond with an HTTP 415.
237 respond with an HTTP 415.
199
238
200 The command to run is specified in the POST payload as defined by the
239 The command to run is specified in the POST payload as defined by the
201 *Unified Frame-Based Protocol*. This is redundant with data already
240 *Unified Frame-Based Protocol*. This is redundant with data already
202 encoded in the URL. This is by design, so server operators can have
241 encoded in the URL. This is by design, so server operators can have
203 better understanding about server activity from looking merely at
242 better understanding about server activity from looking merely at
204 HTTP access logs.
243 HTTP access logs.
205
244
206 In most circumstances, the command specified in the URL MUST match
245 In most circumstances, the command specified in the URL MUST match
207 the command specified in the frame-based payload or the server will
246 the command specified in the frame-based payload or the server will
208 respond with an error. The exception to this is the special
247 respond with an error. The exception to this is the special
209 ``multirequest`` URL. (See below.) In addition, HTTP requests
248 ``multirequest`` URL. (See below.) In addition, HTTP requests
210 are limited to one command invocation. The exception is the special
249 are limited to one command invocation. The exception is the special
211 ``multirequest`` URL.
250 ``multirequest`` URL.
212
251
213 The ``multirequest`` command endpoints (``ro/multirequest`` and
252 The ``multirequest`` command endpoints (``ro/multirequest`` and
214 ``rw/multirequest``) are special in that they allow the execution of
253 ``rw/multirequest``) are special in that they allow the execution of
215 *any* command and allow the execution of multiple commands. If the
254 *any* command and allow the execution of multiple commands. If the
216 HTTP request issues multiple commands across multiple frames, all
255 HTTP request issues multiple commands across multiple frames, all
217 issued commands will be processed by the server. Per the defined
256 issued commands will be processed by the server. Per the defined
218 behavior of the *Unified Frame-Based Protocol*, commands may be
257 behavior of the *Unified Frame-Based Protocol*, commands may be
219 issued interleaved and responses may come back in a different order
258 issued interleaved and responses may come back in a different order
220 than they were issued. Clients MUST be able to deal with this.
259 than they were issued. Clients MUST be able to deal with this.
221
260
222 SSH Protocol
261 SSH Protocol
223 ============
262 ============
224
263
225 Handshake
264 Handshake
226 ---------
265 ---------
227
266
228 For all clients, the handshake consists of the client sending 1 or more
267 For all clients, the handshake consists of the client sending 1 or more
229 commands to the server using version 1 of the transport. Servers respond
268 commands to the server using version 1 of the transport. Servers respond
230 to commands they know how to respond to and send an empty response (``0\n``)
269 to commands they know how to respond to and send an empty response (``0\n``)
231 for unknown commands (per standard behavior of version 1 of the transport).
270 for unknown commands (per standard behavior of version 1 of the transport).
232 Clients then typically look for a response to the newest sent command to
271 Clients then typically look for a response to the newest sent command to
233 determine which transport version to use and what the available features for
272 determine which transport version to use and what the available features for
234 the connection and server are.
273 the connection and server are.
235
274
236 Preceding any response from client-issued commands, the server may print
275 Preceding any response from client-issued commands, the server may print
237 non-protocol output. It is common for SSH servers to print banners, message
276 non-protocol output. It is common for SSH servers to print banners, message
238 of the day announcements, etc when clients connect. It is assumed that any
277 of the day announcements, etc when clients connect. It is assumed that any
239 such *banner* output will precede any Mercurial server output. So clients
278 such *banner* output will precede any Mercurial server output. So clients
240 must be prepared to handle server output on initial connect that isn't
279 must be prepared to handle server output on initial connect that isn't
241 in response to any client-issued command and doesn't conform to Mercurial's
280 in response to any client-issued command and doesn't conform to Mercurial's
242 wire protocol. This *banner* output should only be on stdout. However,
281 wire protocol. This *banner* output should only be on stdout. However,
243 some servers may send output on stderr.
282 some servers may send output on stderr.
244
283
245 Pre 0.9.1 clients issue a ``between`` command with the ``pairs`` argument
284 Pre 0.9.1 clients issue a ``between`` command with the ``pairs`` argument
246 having the value
285 having the value
247 ``0000000000000000000000000000000000000000-0000000000000000000000000000000000000000``.
286 ``0000000000000000000000000000000000000000-0000000000000000000000000000000000000000``.
248
287
249 The ``between`` command has been supported since the original Mercurial
288 The ``between`` command has been supported since the original Mercurial
250 SSH server. Requesting the empty range will return a ``\n`` string response,
289 SSH server. Requesting the empty range will return a ``\n`` string response,
251 which will be encoded as ``1\n\n`` (value length of ``1`` followed by a newline
290 which will be encoded as ``1\n\n`` (value length of ``1`` followed by a newline
252 followed by the value, which happens to be a newline).
291 followed by the value, which happens to be a newline).
253
292
254 For pre 0.9.1 clients and all servers, the exchange looks like::
293 For pre 0.9.1 clients and all servers, the exchange looks like::
255
294
256 c: between\n
295 c: between\n
257 c: pairs 81\n
296 c: pairs 81\n
258 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
297 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
259 s: 1\n
298 s: 1\n
260 s: \n
299 s: \n
261
300
262 0.9.1+ clients send a ``hello`` command (with no arguments) before the
301 0.9.1+ clients send a ``hello`` command (with no arguments) before the
263 ``between`` command. The response to this command allows clients to
302 ``between`` command. The response to this command allows clients to
264 discover server capabilities and settings.
303 discover server capabilities and settings.
265
304
266 An example exchange between 0.9.1+ clients and a ``hello`` aware server looks
305 An example exchange between 0.9.1+ clients and a ``hello`` aware server looks
267 like::
306 like::
268
307
269 c: hello\n
308 c: hello\n
270 c: between\n
309 c: between\n
271 c: pairs 81\n
310 c: pairs 81\n
272 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
311 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
273 s: 324\n
312 s: 324\n
274 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
313 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
275 s: 1\n
314 s: 1\n
276 s: \n
315 s: \n
277
316
278 And a similar scenario but with servers sending a banner on connect::
317 And a similar scenario but with servers sending a banner on connect::
279
318
280 c: hello\n
319 c: hello\n
281 c: between\n
320 c: between\n
282 c: pairs 81\n
321 c: pairs 81\n
283 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
322 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
284 s: welcome to the server\n
323 s: welcome to the server\n
285 s: if you find any issues, email someone@somewhere.com\n
324 s: if you find any issues, email someone@somewhere.com\n
286 s: 324\n
325 s: 324\n
287 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
326 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
288 s: 1\n
327 s: 1\n
289 s: \n
328 s: \n
290
329
291 Note that output from the ``hello`` command is terminated by a ``\n``. This is
330 Note that output from the ``hello`` command is terminated by a ``\n``. This is
292 part of the response payload and not part of the wire protocol adding a newline
331 part of the response payload and not part of the wire protocol adding a newline
293 after responses. In other words, the length of the response contains the
332 after responses. In other words, the length of the response contains the
294 trailing ``\n``.
333 trailing ``\n``.
295
334
296 Clients supporting version 2 of the SSH transport send a line beginning
335 Clients supporting version 2 of the SSH transport send a line beginning
297 with ``upgrade`` before the ``hello`` and ``between`` commands. The line
336 with ``upgrade`` before the ``hello`` and ``between`` commands. The line
298 (which isn't a well-formed command line because it doesn't consist of a
337 (which isn't a well-formed command line because it doesn't consist of a
299 single command name) serves to both communicate the client's intent to
338 single command name) serves to both communicate the client's intent to
300 switch to transport version 2 (transports are version 1 by default) as
339 switch to transport version 2 (transports are version 1 by default) as
301 well as to advertise the client's transport-level capabilities so the
340 well as to advertise the client's transport-level capabilities so the
302 server may satisfy that request immediately.
341 server may satisfy that request immediately.
303
342
304 The upgrade line has the form:
343 The upgrade line has the form:
305
344
306 upgrade <token> <transport capabilities>
345 upgrade <token> <transport capabilities>
307
346
308 That is the literal string ``upgrade`` followed by a space, followed by
347 That is the literal string ``upgrade`` followed by a space, followed by
309 a randomly generated string, followed by a space, followed by a string
348 a randomly generated string, followed by a space, followed by a string
310 denoting the client's transport capabilities.
349 denoting the client's transport capabilities.
311
350
312 The token can be anything. However, a random UUID is recommended. (Use
351 The token can be anything. However, a random UUID is recommended. (Use
313 of version 4 UUIDs is recommended because version 1 UUIDs can leak the
352 of version 4 UUIDs is recommended because version 1 UUIDs can leak the
314 client's MAC address.)
353 client's MAC address.)
315
354
316 The transport capabilities string is a URL/percent encoded string
355 The transport capabilities string is a URL/percent encoded string
317 containing key-value pairs defining the client's transport-level
356 containing key-value pairs defining the client's transport-level
318 capabilities. The following capabilities are defined:
357 capabilities. The following capabilities are defined:
319
358
320 proto
359 proto
321 A comma-delimited list of transport protocol versions the client
360 A comma-delimited list of transport protocol versions the client
322 supports. e.g. ``ssh-v2``.
361 supports. e.g. ``ssh-v2``.
323
362
324 If the server does not recognize the ``upgrade`` line, it should issue
363 If the server does not recognize the ``upgrade`` line, it should issue
325 an empty response and continue processing the ``hello`` and ``between``
364 an empty response and continue processing the ``hello`` and ``between``
326 commands. Here is an example handshake between a version 2 aware client
365 commands. Here is an example handshake between a version 2 aware client
327 and a non version 2 aware server:
366 and a non version 2 aware server:
328
367
329 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
368 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
330 c: hello\n
369 c: hello\n
331 c: between\n
370 c: between\n
332 c: pairs 81\n
371 c: pairs 81\n
333 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
372 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
334 s: 0\n
373 s: 0\n
335 s: 324\n
374 s: 324\n
336 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
375 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
337 s: 1\n
376 s: 1\n
338 s: \n
377 s: \n
339
378
340 (The initial ``0\n`` line from the server indicates an empty response to
379 (The initial ``0\n`` line from the server indicates an empty response to
341 the unknown ``upgrade ..`` command/line.)
380 the unknown ``upgrade ..`` command/line.)
342
381
343 If the server recognizes the ``upgrade`` line and is willing to satisfy that
382 If the server recognizes the ``upgrade`` line and is willing to satisfy that
344 upgrade request, it replies to with a payload of the following form:
383 upgrade request, it replies to with a payload of the following form:
345
384
346 upgraded <token> <transport name>\n
385 upgraded <token> <transport name>\n
347
386
348 This line is the literal string ``upgraded``, a space, the token that was
387 This line is the literal string ``upgraded``, a space, the token that was
349 specified by the client in its ``upgrade ...`` request line, a space, and the
388 specified by the client in its ``upgrade ...`` request line, a space, and the
350 name of the transport protocol that was chosen by the server. The transport
389 name of the transport protocol that was chosen by the server. The transport
351 name MUST match one of the names the client specified in the ``proto`` field
390 name MUST match one of the names the client specified in the ``proto`` field
352 of its ``upgrade ...`` request line.
391 of its ``upgrade ...`` request line.
353
392
354 If a server issues an ``upgraded`` response, it MUST also read and ignore
393 If a server issues an ``upgraded`` response, it MUST also read and ignore
355 the lines associated with the ``hello`` and ``between`` command requests
394 the lines associated with the ``hello`` and ``between`` command requests
356 that were issued by the server. It is assumed that the negotiated transport
395 that were issued by the server. It is assumed that the negotiated transport
357 will respond with equivalent requested information following the transport
396 will respond with equivalent requested information following the transport
358 handshake.
397 handshake.
359
398
360 All data following the ``\n`` terminating the ``upgraded`` line is the
399 All data following the ``\n`` terminating the ``upgraded`` line is the
361 domain of the negotiated transport. It is common for the data immediately
400 domain of the negotiated transport. It is common for the data immediately
362 following to contain additional metadata about the state of the transport and
401 following to contain additional metadata about the state of the transport and
363 the server. However, this isn't strictly speaking part of the transport
402 the server. However, this isn't strictly speaking part of the transport
364 handshake and isn't covered by this section.
403 handshake and isn't covered by this section.
365
404
366 Here is an example handshake between a version 2 aware client and a version
405 Here is an example handshake between a version 2 aware client and a version
367 2 aware server:
406 2 aware server:
368
407
369 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
408 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
370 c: hello\n
409 c: hello\n
371 c: between\n
410 c: between\n
372 c: pairs 81\n
411 c: pairs 81\n
373 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
412 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
374 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
413 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
375 s: <additional transport specific data>
414 s: <additional transport specific data>
376
415
377 The client-issued token that is echoed in the response provides a more
416 The client-issued token that is echoed in the response provides a more
378 resilient mechanism for differentiating *banner* output from Mercurial
417 resilient mechanism for differentiating *banner* output from Mercurial
379 output. In version 1, properly formatted banner output could get confused
418 output. In version 1, properly formatted banner output could get confused
380 for Mercurial server output. By submitting a randomly generated token
419 for Mercurial server output. By submitting a randomly generated token
381 that is then present in the response, the client can look for that token
420 that is then present in the response, the client can look for that token
382 in response lines and have reasonable certainty that the line did not
421 in response lines and have reasonable certainty that the line did not
383 originate from a *banner* message.
422 originate from a *banner* message.
384
423
385 SSH Version 1 Transport
424 SSH Version 1 Transport
386 -----------------------
425 -----------------------
387
426
388 The SSH transport (version 1) is a custom text-based protocol suitable for
427 The SSH transport (version 1) is a custom text-based protocol suitable for
389 use over any bi-directional stream transport. It is most commonly used with
428 use over any bi-directional stream transport. It is most commonly used with
390 SSH.
429 SSH.
391
430
392 A SSH transport server can be started with ``hg serve --stdio``. The stdin,
431 A SSH transport server can be started with ``hg serve --stdio``. The stdin,
393 stderr, and stdout file descriptors of the started process are used to exchange
432 stderr, and stdout file descriptors of the started process are used to exchange
394 data. When Mercurial connects to a remote server over SSH, it actually starts
433 data. When Mercurial connects to a remote server over SSH, it actually starts
395 a ``hg serve --stdio`` process on the remote server.
434 a ``hg serve --stdio`` process on the remote server.
396
435
397 Commands are issued by sending the command name followed by a trailing newline
436 Commands are issued by sending the command name followed by a trailing newline
398 ``\n`` to the server. e.g. ``capabilities\n``.
437 ``\n`` to the server. e.g. ``capabilities\n``.
399
438
400 Command arguments are sent in the following format::
439 Command arguments are sent in the following format::
401
440
402 <argument> <length>\n<value>
441 <argument> <length>\n<value>
403
442
404 That is, the argument string name followed by a space followed by the
443 That is, the argument string name followed by a space followed by the
405 integer length of the value (expressed as a string) followed by a newline
444 integer length of the value (expressed as a string) followed by a newline
406 (``\n``) followed by the raw argument value.
445 (``\n``) followed by the raw argument value.
407
446
408 Dictionary arguments are encoded differently::
447 Dictionary arguments are encoded differently::
409
448
410 <argument> <# elements>\n
449 <argument> <# elements>\n
411 <key1> <length1>\n<value1>
450 <key1> <length1>\n<value1>
412 <key2> <length2>\n<value2>
451 <key2> <length2>\n<value2>
413 ...
452 ...
414
453
415 Non-argument data is sent immediately after the final argument value. It is
454 Non-argument data is sent immediately after the final argument value. It is
416 encoded in chunks::
455 encoded in chunks::
417
456
418 <length>\n<data>
457 <length>\n<data>
419
458
420 Each command declares a list of supported arguments and their types. If a
459 Each command declares a list of supported arguments and their types. If a
421 client sends an unknown argument to the server, the server should abort
460 client sends an unknown argument to the server, the server should abort
422 immediately. The special argument ``*`` in a command's definition indicates
461 immediately. The special argument ``*`` in a command's definition indicates
423 that all argument names are allowed.
462 that all argument names are allowed.
424
463
425 The definition of supported arguments and types is initially made when a
464 The definition of supported arguments and types is initially made when a
426 new command is implemented. The client and server must initially independently
465 new command is implemented. The client and server must initially independently
427 agree on the arguments and their types. This initial set of arguments can be
466 agree on the arguments and their types. This initial set of arguments can be
428 supplemented through the presence of *capabilities* advertised by the server.
467 supplemented through the presence of *capabilities* advertised by the server.
429
468
430 Each command has a defined expected response type.
469 Each command has a defined expected response type.
431
470
432 A ``string`` response type is a length framed value. The response consists of
471 A ``string`` response type is a length framed value. The response consists of
433 the string encoded integer length of a value followed by a newline (``\n``)
472 the string encoded integer length of a value followed by a newline (``\n``)
434 followed by the value. Empty values are allowed (and are represented as
473 followed by the value. Empty values are allowed (and are represented as
435 ``0\n``).
474 ``0\n``).
436
475
437 A ``stream`` response type consists of raw bytes of data. There is no framing.
476 A ``stream`` response type consists of raw bytes of data. There is no framing.
438
477
439 A generic error response type is also supported. It consists of a an error
478 A generic error response type is also supported. It consists of a an error
440 message written to ``stderr`` followed by ``\n-\n``. In addition, ``\n`` is
479 message written to ``stderr`` followed by ``\n-\n``. In addition, ``\n`` is
441 written to ``stdout``.
480 written to ``stdout``.
442
481
443 If the server receives an unknown command, it will send an empty ``string``
482 If the server receives an unknown command, it will send an empty ``string``
444 response.
483 response.
445
484
446 The server terminates if it receives an empty command (a ``\n`` character).
485 The server terminates if it receives an empty command (a ``\n`` character).
447
486
448 If the server announces support for the ``protocaps`` capability, the client
487 If the server announces support for the ``protocaps`` capability, the client
449 should issue a ``protocaps`` command after the initial handshake to annonunce
488 should issue a ``protocaps`` command after the initial handshake to annonunce
450 its own capabilities. The client capabilities are persistent.
489 its own capabilities. The client capabilities are persistent.
451
490
452 SSH Version 2 Transport
491 SSH Version 2 Transport
453 -----------------------
492 -----------------------
454
493
455 **Experimental and under development**
494 **Experimental and under development**
456
495
457 Version 2 of the SSH transport behaves identically to version 1 of the SSH
496 Version 2 of the SSH transport behaves identically to version 1 of the SSH
458 transport with the exception of handshake semantics. See above for how
497 transport with the exception of handshake semantics. See above for how
459 version 2 of the SSH transport is negotiated.
498 version 2 of the SSH transport is negotiated.
460
499
461 Immediately following the ``upgraded`` line signaling a switch to version
500 Immediately following the ``upgraded`` line signaling a switch to version
462 2 of the SSH protocol, the server automatically sends additional details
501 2 of the SSH protocol, the server automatically sends additional details
463 about the capabilities of the remote server. This has the form:
502 about the capabilities of the remote server. This has the form:
464
503
465 <integer length of value>\n
504 <integer length of value>\n
466 capabilities: ...\n
505 capabilities: ...\n
467
506
468 e.g.
507 e.g.
469
508
470 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
509 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
471 s: 240\n
510 s: 240\n
472 s: capabilities: known getbundle batch ...\n
511 s: capabilities: known getbundle batch ...\n
473
512
474 Following capabilities advertisement, the peers communicate using version
513 Following capabilities advertisement, the peers communicate using version
475 1 of the SSH transport.
514 1 of the SSH transport.
476
515
477 Unified Frame-Based Protocol
516 Unified Frame-Based Protocol
478 ============================
517 ============================
479
518
480 **Experimental and under development**
519 **Experimental and under development**
481
520
482 The *Unified Frame-Based Protocol* is a communications protocol between
521 The *Unified Frame-Based Protocol* is a communications protocol between
483 Mercurial peers. The protocol aims to be mostly transport agnostic
522 Mercurial peers. The protocol aims to be mostly transport agnostic
484 (works similarly on HTTP, SSH, etc).
523 (works similarly on HTTP, SSH, etc).
485
524
486 To operate the protocol, a bi-directional, half-duplex pipe supporting
525 To operate the protocol, a bi-directional, half-duplex pipe supporting
487 ordered sends and receives is required. That is, each peer has one pipe
526 ordered sends and receives is required. That is, each peer has one pipe
488 for sending data and another for receiving.
527 for sending data and another for receiving.
489
528
490 All data is read and written in atomic units called *frames*. These
529 All data is read and written in atomic units called *frames*. These
491 are conceptually similar to TCP packets. Higher-level functionality
530 are conceptually similar to TCP packets. Higher-level functionality
492 is built on the exchange and processing of frames.
531 is built on the exchange and processing of frames.
493
532
494 All frames are associated with a *stream*. A *stream* provides a
533 All frames are associated with a *stream*. A *stream* provides a
495 unidirectional grouping of frames. Streams facilitate two goals:
534 unidirectional grouping of frames. Streams facilitate two goals:
496 content encoding and parallelism. There is a dedicated section on
535 content encoding and parallelism. There is a dedicated section on
497 streams below.
536 streams below.
498
537
499 The protocol is request-response based: the client issues requests to
538 The protocol is request-response based: the client issues requests to
500 the server, which issues replies to those requests. Server-initiated
539 the server, which issues replies to those requests. Server-initiated
501 messaging is not currently supported, but this specification carves
540 messaging is not currently supported, but this specification carves
502 out room to implement it.
541 out room to implement it.
503
542
504 All frames are associated with a numbered request. Frames can thus
543 All frames are associated with a numbered request. Frames can thus
505 be logically grouped by their request ID.
544 be logically grouped by their request ID.
506
545
507 Frames begin with an 8 octet header followed by a variable length
546 Frames begin with an 8 octet header followed by a variable length
508 payload::
547 payload::
509
548
510 +------------------------------------------------+
549 +------------------------------------------------+
511 | Length (24) |
550 | Length (24) |
512 +--------------------------------+---------------+
551 +--------------------------------+---------------+
513 | Request ID (16) | Stream ID (8) |
552 | Request ID (16) | Stream ID (8) |
514 +------------------+-------------+---------------+
553 +------------------+-------------+---------------+
515 | Stream Flags (8) |
554 | Stream Flags (8) |
516 +-----------+------+
555 +-----------+------+
517 | Type (4) |
556 | Type (4) |
518 +-----------+
557 +-----------+
519 | Flags (4) |
558 | Flags (4) |
520 +===========+===================================================|
559 +===========+===================================================|
521 | Frame Payload (0...) ...
560 | Frame Payload (0...) ...
522 +---------------------------------------------------------------+
561 +---------------------------------------------------------------+
523
562
524 The length of the frame payload is expressed as an unsigned 24 bit
563 The length of the frame payload is expressed as an unsigned 24 bit
525 little endian integer. Values larger than 65535 MUST NOT be used unless
564 little endian integer. Values larger than 65535 MUST NOT be used unless
526 given permission by the server as part of the negotiated capabilities
565 given permission by the server as part of the negotiated capabilities
527 during the handshake. The frame header is not part of the advertised
566 during the handshake. The frame header is not part of the advertised
528 frame length. The payload length is the over-the-wire length. If there
567 frame length. The payload length is the over-the-wire length. If there
529 is content encoding applied to the payload as part of the frame's stream,
568 is content encoding applied to the payload as part of the frame's stream,
530 the length is the output of that content encoding, not the input.
569 the length is the output of that content encoding, not the input.
531
570
532 The 16-bit ``Request ID`` field denotes the integer request identifier,
571 The 16-bit ``Request ID`` field denotes the integer request identifier,
533 stored as an unsigned little endian integer. Odd numbered requests are
572 stored as an unsigned little endian integer. Odd numbered requests are
534 client-initiated. Even numbered requests are server-initiated. This
573 client-initiated. Even numbered requests are server-initiated. This
535 refers to where the *request* was initiated - not where the *frame* was
574 refers to where the *request* was initiated - not where the *frame* was
536 initiated, so servers will send frames with odd ``Request ID`` in
575 initiated, so servers will send frames with odd ``Request ID`` in
537 response to client-initiated requests. Implementations are advised to
576 response to client-initiated requests. Implementations are advised to
538 start ordering request identifiers at ``1`` and ``0``, increment by
577 start ordering request identifiers at ``1`` and ``0``, increment by
539 ``2``, and wrap around if all available numbers have been exhausted.
578 ``2``, and wrap around if all available numbers have been exhausted.
540
579
541 The 8-bit ``Stream ID`` field denotes the stream that the frame is
580 The 8-bit ``Stream ID`` field denotes the stream that the frame is
542 associated with. Frames belonging to a stream may have content
581 associated with. Frames belonging to a stream may have content
543 encoding applied and the receiver may need to decode the raw frame
582 encoding applied and the receiver may need to decode the raw frame
544 payload to obtain the original data. Odd numbered IDs are
583 payload to obtain the original data. Odd numbered IDs are
545 client-initiated. Even numbered IDs are server-initiated.
584 client-initiated. Even numbered IDs are server-initiated.
546
585
547 The 8-bit ``Stream Flags`` field defines stream processing semantics.
586 The 8-bit ``Stream Flags`` field defines stream processing semantics.
548 See the section on streams below.
587 See the section on streams below.
549
588
550 The 4-bit ``Type`` field denotes the type of frame being sent.
589 The 4-bit ``Type`` field denotes the type of frame being sent.
551
590
552 The 4-bit ``Flags`` field defines special, per-type attributes for
591 The 4-bit ``Flags`` field defines special, per-type attributes for
553 the frame.
592 the frame.
554
593
555 The sections below define the frame types and their behavior.
594 The sections below define the frame types and their behavior.
556
595
557 Command Request (``0x01``)
596 Command Request (``0x01``)
558 --------------------------
597 --------------------------
559
598
560 This frame contains a request to run a command.
599 This frame contains a request to run a command.
561
600
562 The payload consists of a CBOR map defining the command request. The
601 The payload consists of a CBOR map defining the command request. The
563 bytestring keys of that map are:
602 bytestring keys of that map are:
564
603
565 name
604 name
566 Name of the command that should be executed (bytestring).
605 Name of the command that should be executed (bytestring).
567 args
606 args
568 Map of bytestring keys to various value types containing the named
607 Map of bytestring keys to various value types containing the named
569 arguments to this command.
608 arguments to this command.
570
609
571 Each command defines its own set of argument names and their expected
610 Each command defines its own set of argument names and their expected
572 types.
611 types.
573
612
574 This frame type MUST ONLY be sent from clients to servers: it is illegal
613 This frame type MUST ONLY be sent from clients to servers: it is illegal
575 for a server to send this frame to a client.
614 for a server to send this frame to a client.
576
615
577 The following flag values are defined for this type:
616 The following flag values are defined for this type:
578
617
579 0x01
618 0x01
580 New command request. When set, this frame represents the beginning
619 New command request. When set, this frame represents the beginning
581 of a new request to run a command. The ``Request ID`` attached to this
620 of a new request to run a command. The ``Request ID`` attached to this
582 frame MUST NOT be active.
621 frame MUST NOT be active.
583 0x02
622 0x02
584 Command request continuation. When set, this frame is a continuation
623 Command request continuation. When set, this frame is a continuation
585 from a previous command request frame for its ``Request ID``. This
624 from a previous command request frame for its ``Request ID``. This
586 flag is set when the CBOR data for a command request does not fit
625 flag is set when the CBOR data for a command request does not fit
587 in a single frame.
626 in a single frame.
588 0x04
627 0x04
589 Additional frames expected. When set, the command request didn't fit
628 Additional frames expected. When set, the command request didn't fit
590 into a single frame and additional CBOR data follows in a subsequent
629 into a single frame and additional CBOR data follows in a subsequent
591 frame.
630 frame.
592 0x08
631 0x08
593 Command data frames expected. When set, command data frames are
632 Command data frames expected. When set, command data frames are
594 expected to follow the final command request frame for this request.
633 expected to follow the final command request frame for this request.
595
634
596 ``0x01`` MUST be set on the initial command request frame for a
635 ``0x01`` MUST be set on the initial command request frame for a
597 ``Request ID``.
636 ``Request ID``.
598
637
599 ``0x01`` or ``0x02`` MUST be set to indicate this frame's role in
638 ``0x01`` or ``0x02`` MUST be set to indicate this frame's role in
600 a series of command request frames.
639 a series of command request frames.
601
640
602 If command data frames are to be sent, ``0x10`` MUST be set on ALL
641 If command data frames are to be sent, ``0x10`` MUST be set on ALL
603 command request frames.
642 command request frames.
604
643
605 Command Data (``0x03``)
644 Command Data (``0x03``)
606 -----------------------
645 -----------------------
607
646
608 This frame contains raw data for a command.
647 This frame contains raw data for a command.
609
648
610 Most commands can be executed by specifying arguments. However,
649 Most commands can be executed by specifying arguments. However,
611 arguments have an upper bound to their length. For commands that
650 arguments have an upper bound to their length. For commands that
612 accept data that is beyond this length or whose length isn't known
651 accept data that is beyond this length or whose length isn't known
613 when the command is initially sent, they will need to stream
652 when the command is initially sent, they will need to stream
614 arbitrary data to the server. This frame type facilitates the sending
653 arbitrary data to the server. This frame type facilitates the sending
615 of this data.
654 of this data.
616
655
617 The payload of this frame type consists of a stream of raw data to be
656 The payload of this frame type consists of a stream of raw data to be
618 consumed by the command handler on the server. The format of the data
657 consumed by the command handler on the server. The format of the data
619 is command specific.
658 is command specific.
620
659
621 The following flag values are defined for this type:
660 The following flag values are defined for this type:
622
661
623 0x01
662 0x01
624 Command data continuation. When set, the data for this command
663 Command data continuation. When set, the data for this command
625 continues into a subsequent frame.
664 continues into a subsequent frame.
626
665
627 0x02
666 0x02
628 End of data. When set, command data has been fully sent to the
667 End of data. When set, command data has been fully sent to the
629 server. The command has been fully issued and no new data for this
668 server. The command has been fully issued and no new data for this
630 command will be sent. The next frame will belong to a new command.
669 command will be sent. The next frame will belong to a new command.
631
670
632 Response Data (``0x04``)
671 Response Data (``0x04``)
633 ------------------------
672 ------------------------
634
673
635 This frame contains raw response data to an issued command.
674 This frame contains raw response data to an issued command.
636
675
637 The following flag values are defined for this type:
676 The following flag values are defined for this type:
638
677
639 0x01
678 0x01
640 Data continuation. When set, an additional frame containing response data
679 Data continuation. When set, an additional frame containing response data
641 will follow.
680 will follow.
642 0x02
681 0x02
643 End of data. When set, the response data has been fully sent and
682 End of data. When set, the response data has been fully sent and
644 no additional frames for this response will be sent.
683 no additional frames for this response will be sent.
645 0x04
684 0x04
646 CBOR data. When set, the frame payload consists of CBOR data.
685 CBOR data. When set, the frame payload consists of CBOR data.
647
686
648 The ``0x01`` flag is mutually exclusive with the ``0x02`` flag.
687 The ``0x01`` flag is mutually exclusive with the ``0x02`` flag.
649
688
650 Error Response (``0x05``)
689 Error Response (``0x05``)
651 -------------------------
690 -------------------------
652
691
653 An error occurred when processing a request. This could indicate
692 An error occurred when processing a request. This could indicate
654 a protocol-level failure or an application level failure depending
693 a protocol-level failure or an application level failure depending
655 on the flags for this message type.
694 on the flags for this message type.
656
695
657 The payload for this type is an error message that should be
696 The payload for this type is an error message that should be
658 displayed to the user.
697 displayed to the user.
659
698
660 The following flag values are defined for this type:
699 The following flag values are defined for this type:
661
700
662 0x01
701 0x01
663 The error occurred at the transport/protocol level. If set, the
702 The error occurred at the transport/protocol level. If set, the
664 connection should be closed.
703 connection should be closed.
665 0x02
704 0x02
666 The error occurred at the application level. e.g. invalid command.
705 The error occurred at the application level. e.g. invalid command.
667
706
668 Human Output Side-Channel (``0x06``)
707 Human Output Side-Channel (``0x06``)
669 ------------------------------------
708 ------------------------------------
670
709
671 This frame contains a message that is intended to be displayed to
710 This frame contains a message that is intended to be displayed to
672 people. Whereas most frames communicate machine readable data, this
711 people. Whereas most frames communicate machine readable data, this
673 frame communicates textual data that is intended to be shown to
712 frame communicates textual data that is intended to be shown to
674 humans.
713 humans.
675
714
676 The frame consists of a series of *formatting requests*. Each formatting
715 The frame consists of a series of *formatting requests*. Each formatting
677 request consists of a formatting string, arguments for that formatting
716 request consists of a formatting string, arguments for that formatting
678 string, and labels to apply to that formatting string.
717 string, and labels to apply to that formatting string.
679
718
680 A formatting string is a printf()-like string that allows variable
719 A formatting string is a printf()-like string that allows variable
681 substitution within the string. Labels allow the rendered text to be
720 substitution within the string. Labels allow the rendered text to be
682 *decorated*. Assuming use of the canonical Mercurial code base, a
721 *decorated*. Assuming use of the canonical Mercurial code base, a
683 formatting string can be the input to the ``i18n._`` function. This
722 formatting string can be the input to the ``i18n._`` function. This
684 allows messages emitted from the server to be localized. So even if
723 allows messages emitted from the server to be localized. So even if
685 the server has different i18n settings, people could see messages in
724 the server has different i18n settings, people could see messages in
686 their *native* settings. Similarly, the use of labels allows
725 their *native* settings. Similarly, the use of labels allows
687 decorations like coloring and underlining to be applied using the
726 decorations like coloring and underlining to be applied using the
688 client's configured rendering settings.
727 client's configured rendering settings.
689
728
690 Formatting strings are similar to ``printf()`` strings or how
729 Formatting strings are similar to ``printf()`` strings or how
691 Python's ``%`` operator works. The only supported formatting sequences
730 Python's ``%`` operator works. The only supported formatting sequences
692 are ``%s`` and ``%%``. ``%s`` will be replaced by whatever the string
731 are ``%s`` and ``%%``. ``%s`` will be replaced by whatever the string
693 at that position resolves to. ``%%`` will be replaced by ``%``. All
732 at that position resolves to. ``%%`` will be replaced by ``%``. All
694 other 2-byte sequences beginning with ``%`` represent a literal
733 other 2-byte sequences beginning with ``%`` represent a literal
695 ``%`` followed by that character. However, future versions of the
734 ``%`` followed by that character. However, future versions of the
696 wire protocol reserve the right to allow clients to opt in to receiving
735 wire protocol reserve the right to allow clients to opt in to receiving
697 formatting strings with additional formatters, hence why ``%%`` is
736 formatting strings with additional formatters, hence why ``%%`` is
698 required to represent the literal ``%``.
737 required to represent the literal ``%``.
699
738
700 The frame payload consists of a CBOR array of CBOR maps. Each map
739 The frame payload consists of a CBOR array of CBOR maps. Each map
701 defines an *atom* of text data to print. Each *atom* has the following
740 defines an *atom* of text data to print. Each *atom* has the following
702 bytestring keys:
741 bytestring keys:
703
742
704 msg
743 msg
705 (bytestring) The formatting string. Content MUST be ASCII.
744 (bytestring) The formatting string. Content MUST be ASCII.
706 args (optional)
745 args (optional)
707 Array of bytestrings defining arguments to the formatting string.
746 Array of bytestrings defining arguments to the formatting string.
708 labels (optional)
747 labels (optional)
709 Array of bytestrings defining labels to apply to this atom.
748 Array of bytestrings defining labels to apply to this atom.
710
749
711 All data to be printed MUST be encoded into a single frame: this frame
750 All data to be printed MUST be encoded into a single frame: this frame
712 does not support spanning data across multiple frames.
751 does not support spanning data across multiple frames.
713
752
714 All textual data encoded in these frames is assumed to be line delimited.
753 All textual data encoded in these frames is assumed to be line delimited.
715 The last atom in the frame SHOULD end with a newline (``\n``). If it
754 The last atom in the frame SHOULD end with a newline (``\n``). If it
716 doesn't, clients MAY add a newline to facilitate immediate printing.
755 doesn't, clients MAY add a newline to facilitate immediate printing.
717
756
718 Progress Update (``0x07``)
757 Progress Update (``0x07``)
719 --------------------------
758 --------------------------
720
759
721 This frame holds the progress of an operation on the peer. Consumption
760 This frame holds the progress of an operation on the peer. Consumption
722 of these frames allows clients to display progress bars, estimated
761 of these frames allows clients to display progress bars, estimated
723 completion times, etc.
762 completion times, etc.
724
763
725 Each frame defines the progress of a single operation on the peer. The
764 Each frame defines the progress of a single operation on the peer. The
726 payload consists of a CBOR map with the following bytestring keys:
765 payload consists of a CBOR map with the following bytestring keys:
727
766
728 topic
767 topic
729 Topic name (string)
768 Topic name (string)
730 pos
769 pos
731 Current numeric position within the topic (integer)
770 Current numeric position within the topic (integer)
732 total
771 total
733 Total/end numeric position of this topic (unsigned integer)
772 Total/end numeric position of this topic (unsigned integer)
734 label (optional)
773 label (optional)
735 Unit label (string)
774 Unit label (string)
736 item (optional)
775 item (optional)
737 Item name (string)
776 Item name (string)
738
777
739 Progress state is created when a frame is received referencing a
778 Progress state is created when a frame is received referencing a
740 *topic* that isn't currently tracked. Progress tracking for that
779 *topic* that isn't currently tracked. Progress tracking for that
741 *topic* is finished when a frame is received reporting the current
780 *topic* is finished when a frame is received reporting the current
742 position of that topic as ``-1``.
781 position of that topic as ``-1``.
743
782
744 Multiple *topics* may be active at any given time.
783 Multiple *topics* may be active at any given time.
745
784
746 Rendering of progress information is not mandated or governed by this
785 Rendering of progress information is not mandated or governed by this
747 specification: implementations MAY render progress information however
786 specification: implementations MAY render progress information however
748 they see fit, including not at all.
787 they see fit, including not at all.
749
788
750 The string data describing the topic SHOULD be static strings to
789 The string data describing the topic SHOULD be static strings to
751 facilitate receivers localizing that string data. The emitter
790 facilitate receivers localizing that string data. The emitter
752 MUST normalize all string data to valid UTF-8 and receivers SHOULD
791 MUST normalize all string data to valid UTF-8 and receivers SHOULD
753 validate that received data conforms to UTF-8. The topic name
792 validate that received data conforms to UTF-8. The topic name
754 SHOULD be ASCII.
793 SHOULD be ASCII.
755
794
756 Stream Encoding Settings (``0x08``)
795 Stream Encoding Settings (``0x08``)
757 -----------------------------------
796 -----------------------------------
758
797
759 This frame type holds information defining the content encoding
798 This frame type holds information defining the content encoding
760 settings for a *stream*.
799 settings for a *stream*.
761
800
762 This frame type is likely consumed by the protocol layer and is not
801 This frame type is likely consumed by the protocol layer and is not
763 passed on to applications.
802 passed on to applications.
764
803
765 This frame type MUST ONLY occur on frames having the *Beginning of Stream*
804 This frame type MUST ONLY occur on frames having the *Beginning of Stream*
766 ``Stream Flag`` set.
805 ``Stream Flag`` set.
767
806
768 The payload of this frame defines what content encoding has (possibly)
807 The payload of this frame defines what content encoding has (possibly)
769 been applied to the payloads of subsequent frames in this stream.
808 been applied to the payloads of subsequent frames in this stream.
770
809
771 The payload begins with an 8-bit integer defining the length of the
810 The payload begins with an 8-bit integer defining the length of the
772 encoding *profile*, followed by the string name of that profile, which
811 encoding *profile*, followed by the string name of that profile, which
773 must be an ASCII string. All bytes that follow can be used by that
812 must be an ASCII string. All bytes that follow can be used by that
774 profile for supplemental settings definitions. See the section below
813 profile for supplemental settings definitions. See the section below
775 on defined encoding profiles.
814 on defined encoding profiles.
776
815
777 Stream States and Flags
816 Stream States and Flags
778 -----------------------
817 -----------------------
779
818
780 Streams can be in two states: *open* and *closed*. An *open* stream
819 Streams can be in two states: *open* and *closed*. An *open* stream
781 is active and frames attached to that stream could arrive at any time.
820 is active and frames attached to that stream could arrive at any time.
782 A *closed* stream is not active. If a frame attached to a *closed*
821 A *closed* stream is not active. If a frame attached to a *closed*
783 stream arrives, that frame MUST have an appropriate stream flag
822 stream arrives, that frame MUST have an appropriate stream flag
784 set indicating beginning of stream. All streams are in the *closed*
823 set indicating beginning of stream. All streams are in the *closed*
785 state by default.
824 state by default.
786
825
787 The ``Stream Flags`` field denotes a set of bit flags for defining
826 The ``Stream Flags`` field denotes a set of bit flags for defining
788 the relationship of this frame within a stream. The following flags
827 the relationship of this frame within a stream. The following flags
789 are defined:
828 are defined:
790
829
791 0x01
830 0x01
792 Beginning of stream. The first frame in the stream MUST set this
831 Beginning of stream. The first frame in the stream MUST set this
793 flag. When received, the ``Stream ID`` this frame is attached to
832 flag. When received, the ``Stream ID`` this frame is attached to
794 becomes ``open``.
833 becomes ``open``.
795
834
796 0x02
835 0x02
797 End of stream. The last frame in a stream MUST set this flag. When
836 End of stream. The last frame in a stream MUST set this flag. When
798 received, the ``Stream ID`` this frame is attached to becomes
837 received, the ``Stream ID`` this frame is attached to becomes
799 ``closed``. Any content encoding context associated with this stream
838 ``closed``. Any content encoding context associated with this stream
800 can be destroyed after processing the payload of this frame.
839 can be destroyed after processing the payload of this frame.
801
840
802 0x04
841 0x04
803 Apply content encoding. When set, any content encoding settings
842 Apply content encoding. When set, any content encoding settings
804 defined by the stream should be applied when attempting to read
843 defined by the stream should be applied when attempting to read
805 the frame. When not set, the frame payload isn't encoded.
844 the frame. When not set, the frame payload isn't encoded.
806
845
807 Streams
846 Streams
808 -------
847 -------
809
848
810 Streams - along with ``Request IDs`` - facilitate grouping of frames.
849 Streams - along with ``Request IDs`` - facilitate grouping of frames.
811 But the purpose of each is quite different and the groupings they
850 But the purpose of each is quite different and the groupings they
812 constitute are independent.
851 constitute are independent.
813
852
814 A ``Request ID`` is essentially a tag. It tells you which logical
853 A ``Request ID`` is essentially a tag. It tells you which logical
815 request a frame is associated with.
854 request a frame is associated with.
816
855
817 A *stream* is a sequence of frames grouped for the express purpose
856 A *stream* is a sequence of frames grouped for the express purpose
818 of applying a stateful encoding or for denoting sub-groups of frames.
857 of applying a stateful encoding or for denoting sub-groups of frames.
819
858
820 Unlike ``Request ID``s which span the request and response, a stream
859 Unlike ``Request ID``s which span the request and response, a stream
821 is unidirectional and stream IDs are independent from client to
860 is unidirectional and stream IDs are independent from client to
822 server.
861 server.
823
862
824 There is no strict hierarchical relationship between ``Request IDs``
863 There is no strict hierarchical relationship between ``Request IDs``
825 and *streams*. A stream can contain frames having multiple
864 and *streams*. A stream can contain frames having multiple
826 ``Request IDs``. Frames belonging to the same ``Request ID`` can
865 ``Request IDs``. Frames belonging to the same ``Request ID`` can
827 span multiple streams.
866 span multiple streams.
828
867
829 One goal of streams is to facilitate content encoding. A stream can
868 One goal of streams is to facilitate content encoding. A stream can
830 define an encoding to be applied to frame payloads. For example, the
869 define an encoding to be applied to frame payloads. For example, the
831 payload transmitted over the wire may contain output from a
870 payload transmitted over the wire may contain output from a
832 zstandard compression operation and the receiving end may decompress
871 zstandard compression operation and the receiving end may decompress
833 that payload to obtain the original data.
872 that payload to obtain the original data.
834
873
835 The other goal of streams is to facilitate concurrent execution. For
874 The other goal of streams is to facilitate concurrent execution. For
836 example, a server could spawn 4 threads to service a request that can
875 example, a server could spawn 4 threads to service a request that can
837 be easily parallelized. Each of those 4 threads could write into its
876 be easily parallelized. Each of those 4 threads could write into its
838 own stream. Those streams could then in turn be delivered to 4 threads
877 own stream. Those streams could then in turn be delivered to 4 threads
839 on the receiving end, with each thread consuming its stream in near
878 on the receiving end, with each thread consuming its stream in near
840 isolation. The *main* thread on both ends merely does I/O and
879 isolation. The *main* thread on both ends merely does I/O and
841 encodes/decodes frame headers: the bulk of the work is done by worker
880 encodes/decodes frame headers: the bulk of the work is done by worker
842 threads.
881 threads.
843
882
844 In addition, since content encoding is defined per stream, each
883 In addition, since content encoding is defined per stream, each
845 *worker thread* could perform potentially CPU bound work concurrently
884 *worker thread* could perform potentially CPU bound work concurrently
846 with other threads. This approach of applying encoding at the
885 with other threads. This approach of applying encoding at the
847 sub-protocol / stream level eliminates a potential resource constraint
886 sub-protocol / stream level eliminates a potential resource constraint
848 on the protocol stream as a whole (it is common for the throughput of
887 on the protocol stream as a whole (it is common for the throughput of
849 a compression engine to be smaller than the throughput of a network).
888 a compression engine to be smaller than the throughput of a network).
850
889
851 Having multiple streams - each with their own encoding settings - also
890 Having multiple streams - each with their own encoding settings - also
852 facilitates the use of advanced data compression techniques. For
891 facilitates the use of advanced data compression techniques. For
853 example, a transmitter could see that it is generating data faster
892 example, a transmitter could see that it is generating data faster
854 and slower than the receiving end is consuming it and adjust its
893 and slower than the receiving end is consuming it and adjust its
855 compression settings to trade CPU for compression ratio accordingly.
894 compression settings to trade CPU for compression ratio accordingly.
856
895
857 While streams can define a content encoding, not all frames within
896 While streams can define a content encoding, not all frames within
858 that stream must use that content encoding. This can be useful when
897 that stream must use that content encoding. This can be useful when
859 data is being served from caches and being derived dynamically. A
898 data is being served from caches and being derived dynamically. A
860 cache could pre-compressed data so the server doesn't have to
899 cache could pre-compressed data so the server doesn't have to
861 recompress it. The ability to pick and choose which frames are
900 recompress it. The ability to pick and choose which frames are
862 compressed allows servers to easily send data to the wire without
901 compressed allows servers to easily send data to the wire without
863 involving potentially expensive encoding overhead.
902 involving potentially expensive encoding overhead.
864
903
865 Content Encoding Profiles
904 Content Encoding Profiles
866 -------------------------
905 -------------------------
867
906
868 Streams can have named content encoding *profiles* associated with
907 Streams can have named content encoding *profiles* associated with
869 them. A profile defines a shared understanding of content encoding
908 them. A profile defines a shared understanding of content encoding
870 settings and behavior.
909 settings and behavior.
871
910
872 The following profiles are defined:
911 The following profiles are defined:
873
912
874 TBD
913 TBD
875
914
876 Issuing Commands
915 Issuing Commands
877 ----------------
916 ----------------
878
917
879 A client can request that a remote run a command by sending it
918 A client can request that a remote run a command by sending it
880 frames defining that command. This logical stream is composed of
919 frames defining that command. This logical stream is composed of
881 1 or more ``Command Request`` frames and and 0 or more ``Command Data``
920 1 or more ``Command Request`` frames and and 0 or more ``Command Data``
882 frames.
921 frames.
883
922
884 All frames composing a single command request MUST be associated with
923 All frames composing a single command request MUST be associated with
885 the same ``Request ID``.
924 the same ``Request ID``.
886
925
887 Clients MAY send additional command requests without waiting on the
926 Clients MAY send additional command requests without waiting on the
888 response to a previous command request. If they do so, they MUST ensure
927 response to a previous command request. If they do so, they MUST ensure
889 that the ``Request ID`` field of outbound frames does not conflict
928 that the ``Request ID`` field of outbound frames does not conflict
890 with that of an active ``Request ID`` whose response has not yet been
929 with that of an active ``Request ID`` whose response has not yet been
891 fully received.
930 fully received.
892
931
893 Servers MAY respond to commands in a different order than they were
932 Servers MAY respond to commands in a different order than they were
894 sent over the wire. Clients MUST be prepared to deal with this. Servers
933 sent over the wire. Clients MUST be prepared to deal with this. Servers
895 also MAY start executing commands in a different order than they were
934 also MAY start executing commands in a different order than they were
896 received, or MAY execute multiple commands concurrently.
935 received, or MAY execute multiple commands concurrently.
897
936
898 If there is a dependency between commands or a race condition between
937 If there is a dependency between commands or a race condition between
899 commands executing (e.g. a read-only command that depends on the results
938 commands executing (e.g. a read-only command that depends on the results
900 of a command that mutates the repository), then clients MUST NOT send
939 of a command that mutates the repository), then clients MUST NOT send
901 frames issuing a command until a response to all dependent commands has
940 frames issuing a command until a response to all dependent commands has
902 been received.
941 been received.
903 TODO think about whether we should express dependencies between commands
942 TODO think about whether we should express dependencies between commands
904 to avoid roundtrip latency.
943 to avoid roundtrip latency.
905
944
906 A command is defined by a command name, 0 or more command arguments,
945 A command is defined by a command name, 0 or more command arguments,
907 and optional command data.
946 and optional command data.
908
947
909 Arguments are the recommended mechanism for transferring fixed sets of
948 Arguments are the recommended mechanism for transferring fixed sets of
910 parameters to a command. Data is appropriate for transferring variable
949 parameters to a command. Data is appropriate for transferring variable
911 data. Thinking in terms of HTTP, arguments would be headers and data
950 data. Thinking in terms of HTTP, arguments would be headers and data
912 would be the message body.
951 would be the message body.
913
952
914 It is recommended for servers to delay the dispatch of a command
953 It is recommended for servers to delay the dispatch of a command
915 until all argument have been received. Servers MAY impose limits on the
954 until all argument have been received. Servers MAY impose limits on the
916 maximum argument size.
955 maximum argument size.
917 TODO define failure mechanism.
956 TODO define failure mechanism.
918
957
919 Servers MAY dispatch to commands immediately once argument data
958 Servers MAY dispatch to commands immediately once argument data
920 is available or delay until command data is received in full.
959 is available or delay until command data is received in full.
921
960
922 Capabilities
961 Capabilities
923 ============
962 ============
924
963
925 Servers advertise supported wire protocol features. This allows clients to
964 Servers advertise supported wire protocol features. This allows clients to
926 probe for server features before blindly calling a command or passing a
965 probe for server features before blindly calling a command or passing a
927 specific argument.
966 specific argument.
928
967
929 The server's features are exposed via a *capabilities* string. This is a
968 The server's features are exposed via a *capabilities* string. This is a
930 space-delimited string of tokens/features. Some features are single words
969 space-delimited string of tokens/features. Some features are single words
931 like ``lookup`` or ``batch``. Others are complicated key-value pairs
970 like ``lookup`` or ``batch``. Others are complicated key-value pairs
932 advertising sub-features. e.g. ``httpheader=2048``. When complex, non-word
971 advertising sub-features. e.g. ``httpheader=2048``. When complex, non-word
933 values are used, each feature name can define its own encoding of sub-values.
972 values are used, each feature name can define its own encoding of sub-values.
934 Comma-delimited and ``x-www-form-urlencoded`` values are common.
973 Comma-delimited and ``x-www-form-urlencoded`` values are common.
935
974
936 The following document capabilities defined by the canonical Mercurial server
975 The following document capabilities defined by the canonical Mercurial server
937 implementation.
976 implementation.
938
977
939 batch
978 batch
940 -----
979 -----
941
980
942 Whether the server supports the ``batch`` command.
981 Whether the server supports the ``batch`` command.
943
982
944 This capability/command was introduced in Mercurial 1.9 (released July 2011).
983 This capability/command was introduced in Mercurial 1.9 (released July 2011).
945
984
946 branchmap
985 branchmap
947 ---------
986 ---------
948
987
949 Whether the server supports the ``branchmap`` command.
988 Whether the server supports the ``branchmap`` command.
950
989
951 This capability/command was introduced in Mercurial 1.3 (released July 2009).
990 This capability/command was introduced in Mercurial 1.3 (released July 2009).
952
991
953 bundle2-exp
992 bundle2-exp
954 -----------
993 -----------
955
994
956 Precursor to ``bundle2`` capability that was used before bundle2 was a
995 Precursor to ``bundle2`` capability that was used before bundle2 was a
957 stable feature.
996 stable feature.
958
997
959 This capability was introduced in Mercurial 3.0 behind an experimental
998 This capability was introduced in Mercurial 3.0 behind an experimental
960 flag. This capability should not be observed in the wild.
999 flag. This capability should not be observed in the wild.
961
1000
962 bundle2
1001 bundle2
963 -------
1002 -------
964
1003
965 Indicates whether the server supports the ``bundle2`` data exchange format.
1004 Indicates whether the server supports the ``bundle2`` data exchange format.
966
1005
967 The value of the capability is a URL quoted, newline (``\n``) delimited
1006 The value of the capability is a URL quoted, newline (``\n``) delimited
968 list of keys or key-value pairs.
1007 list of keys or key-value pairs.
969
1008
970 A key is simply a URL encoded string.
1009 A key is simply a URL encoded string.
971
1010
972 A key-value pair is a URL encoded key separated from a URL encoded value by
1011 A key-value pair is a URL encoded key separated from a URL encoded value by
973 an ``=``. If the value is a list, elements are delimited by a ``,`` after
1012 an ``=``. If the value is a list, elements are delimited by a ``,`` after
974 URL encoding.
1013 URL encoding.
975
1014
976 For example, say we have the values::
1015 For example, say we have the values::
977
1016
978 {'HG20': [], 'changegroup': ['01', '02'], 'digests': ['sha1', 'sha512']}
1017 {'HG20': [], 'changegroup': ['01', '02'], 'digests': ['sha1', 'sha512']}
979
1018
980 We would first construct a string::
1019 We would first construct a string::
981
1020
982 HG20\nchangegroup=01,02\ndigests=sha1,sha512
1021 HG20\nchangegroup=01,02\ndigests=sha1,sha512
983
1022
984 We would then URL quote this string::
1023 We would then URL quote this string::
985
1024
986 HG20%0Achangegroup%3D01%2C02%0Adigests%3Dsha1%2Csha512
1025 HG20%0Achangegroup%3D01%2C02%0Adigests%3Dsha1%2Csha512
987
1026
988 This capability was introduced in Mercurial 3.4 (released May 2015).
1027 This capability was introduced in Mercurial 3.4 (released May 2015).
989
1028
990 changegroupsubset
1029 changegroupsubset
991 -----------------
1030 -----------------
992
1031
993 Whether the server supports the ``changegroupsubset`` command.
1032 Whether the server supports the ``changegroupsubset`` command.
994
1033
995 This capability was introduced in Mercurial 0.9.2 (released December
1034 This capability was introduced in Mercurial 0.9.2 (released December
996 2006).
1035 2006).
997
1036
998 This capability was introduced at the same time as the ``lookup``
1037 This capability was introduced at the same time as the ``lookup``
999 capability/command.
1038 capability/command.
1000
1039
1001 compression
1040 compression
1002 -----------
1041 -----------
1003
1042
1004 Declares support for negotiating compression formats.
1043 Declares support for negotiating compression formats.
1005
1044
1006 Presence of this capability indicates the server supports dynamic selection
1045 Presence of this capability indicates the server supports dynamic selection
1007 of compression formats based on the client request.
1046 of compression formats based on the client request.
1008
1047
1009 Servers advertising this capability are required to support the
1048 Servers advertising this capability are required to support the
1010 ``application/mercurial-0.2`` media type in response to commands returning
1049 ``application/mercurial-0.2`` media type in response to commands returning
1011 streams. Servers may support this media type on any command.
1050 streams. Servers may support this media type on any command.
1012
1051
1013 The value of the capability is a comma-delimited list of strings declaring
1052 The value of the capability is a comma-delimited list of strings declaring
1014 supported compression formats. The order of the compression formats is in
1053 supported compression formats. The order of the compression formats is in
1015 server-preferred order, most preferred first.
1054 server-preferred order, most preferred first.
1016
1055
1017 The identifiers used by the official Mercurial distribution are:
1056 The identifiers used by the official Mercurial distribution are:
1018
1057
1019 bzip2
1058 bzip2
1020 bzip2
1059 bzip2
1021 none
1060 none
1022 uncompressed / raw data
1061 uncompressed / raw data
1023 zlib
1062 zlib
1024 zlib (no gzip header)
1063 zlib (no gzip header)
1025 zstd
1064 zstd
1026 zstd
1065 zstd
1027
1066
1028 This capability was introduced in Mercurial 4.1 (released February 2017).
1067 This capability was introduced in Mercurial 4.1 (released February 2017).
1029
1068
1030 getbundle
1069 getbundle
1031 ---------
1070 ---------
1032
1071
1033 Whether the server supports the ``getbundle`` command.
1072 Whether the server supports the ``getbundle`` command.
1034
1073
1035 This capability was introduced in Mercurial 1.9 (released July 2011).
1074 This capability was introduced in Mercurial 1.9 (released July 2011).
1036
1075
1037 httpheader
1076 httpheader
1038 ----------
1077 ----------
1039
1078
1040 Whether the server supports receiving command arguments via HTTP request
1079 Whether the server supports receiving command arguments via HTTP request
1041 headers.
1080 headers.
1042
1081
1043 The value of the capability is an integer describing the max header
1082 The value of the capability is an integer describing the max header
1044 length that clients should send. Clients should ignore any content after a
1083 length that clients should send. Clients should ignore any content after a
1045 comma in the value, as this is reserved for future use.
1084 comma in the value, as this is reserved for future use.
1046
1085
1047 This capability was introduced in Mercurial 1.9 (released July 2011).
1086 This capability was introduced in Mercurial 1.9 (released July 2011).
1048
1087
1049 httpmediatype
1088 httpmediatype
1050 -------------
1089 -------------
1051
1090
1052 Indicates which HTTP media types (``Content-Type`` header) the server is
1091 Indicates which HTTP media types (``Content-Type`` header) the server is
1053 capable of receiving and sending.
1092 capable of receiving and sending.
1054
1093
1055 The value of the capability is a comma-delimited list of strings identifying
1094 The value of the capability is a comma-delimited list of strings identifying
1056 support for media type and transmission direction. The following strings may
1095 support for media type and transmission direction. The following strings may
1057 be present:
1096 be present:
1058
1097
1059 0.1rx
1098 0.1rx
1060 Indicates server support for receiving ``application/mercurial-0.1`` media
1099 Indicates server support for receiving ``application/mercurial-0.1`` media
1061 types.
1100 types.
1062
1101
1063 0.1tx
1102 0.1tx
1064 Indicates server support for sending ``application/mercurial-0.1`` media
1103 Indicates server support for sending ``application/mercurial-0.1`` media
1065 types.
1104 types.
1066
1105
1067 0.2rx
1106 0.2rx
1068 Indicates server support for receiving ``application/mercurial-0.2`` media
1107 Indicates server support for receiving ``application/mercurial-0.2`` media
1069 types.
1108 types.
1070
1109
1071 0.2tx
1110 0.2tx
1072 Indicates server support for sending ``application/mercurial-0.2`` media
1111 Indicates server support for sending ``application/mercurial-0.2`` media
1073 types.
1112 types.
1074
1113
1075 minrx=X
1114 minrx=X
1076 Minimum media type version the server is capable of receiving. Value is a
1115 Minimum media type version the server is capable of receiving. Value is a
1077 string like ``0.2``.
1116 string like ``0.2``.
1078
1117
1079 This capability can be used by servers to limit connections from legacy
1118 This capability can be used by servers to limit connections from legacy
1080 clients not using the latest supported media type. However, only clients
1119 clients not using the latest supported media type. However, only clients
1081 with knowledge of this capability will know to consult this value. This
1120 with knowledge of this capability will know to consult this value. This
1082 capability is present so the client may issue a more user-friendly error
1121 capability is present so the client may issue a more user-friendly error
1083 when the server has locked out a legacy client.
1122 when the server has locked out a legacy client.
1084
1123
1085 mintx=X
1124 mintx=X
1086 Minimum media type version the server is capable of sending. Value is a
1125 Minimum media type version the server is capable of sending. Value is a
1087 string like ``0.1``.
1126 string like ``0.1``.
1088
1127
1089 Servers advertising support for the ``application/mercurial-0.2`` media type
1128 Servers advertising support for the ``application/mercurial-0.2`` media type
1090 should also advertise the ``compression`` capability.
1129 should also advertise the ``compression`` capability.
1091
1130
1092 This capability was introduced in Mercurial 4.1 (released February 2017).
1131 This capability was introduced in Mercurial 4.1 (released February 2017).
1093
1132
1094 httppostargs
1133 httppostargs
1095 ------------
1134 ------------
1096
1135
1097 **Experimental**
1136 **Experimental**
1098
1137
1099 Indicates that the server supports and prefers clients send command arguments
1138 Indicates that the server supports and prefers clients send command arguments
1100 via a HTTP POST request as part of the request body.
1139 via a HTTP POST request as part of the request body.
1101
1140
1102 This capability was introduced in Mercurial 3.8 (released May 2016).
1141 This capability was introduced in Mercurial 3.8 (released May 2016).
1103
1142
1104 known
1143 known
1105 -----
1144 -----
1106
1145
1107 Whether the server supports the ``known`` command.
1146 Whether the server supports the ``known`` command.
1108
1147
1109 This capability/command was introduced in Mercurial 1.9 (released July 2011).
1148 This capability/command was introduced in Mercurial 1.9 (released July 2011).
1110
1149
1111 lookup
1150 lookup
1112 ------
1151 ------
1113
1152
1114 Whether the server supports the ``lookup`` command.
1153 Whether the server supports the ``lookup`` command.
1115
1154
1116 This capability was introduced in Mercurial 0.9.2 (released December
1155 This capability was introduced in Mercurial 0.9.2 (released December
1117 2006).
1156 2006).
1118
1157
1119 This capability was introduced at the same time as the ``changegroupsubset``
1158 This capability was introduced at the same time as the ``changegroupsubset``
1120 capability/command.
1159 capability/command.
1121
1160
1122 partial-pull
1161 partial-pull
1123 ------------
1162 ------------
1124
1163
1125 Indicates that the client can deal with partial answers to pull requests
1164 Indicates that the client can deal with partial answers to pull requests
1126 by repeating the request.
1165 by repeating the request.
1127
1166
1128 If this parameter is not advertised, the server will not send pull bundles.
1167 If this parameter is not advertised, the server will not send pull bundles.
1129
1168
1130 This client capability was introduced in Mercurial 4.6.
1169 This client capability was introduced in Mercurial 4.6.
1131
1170
1132 protocaps
1171 protocaps
1133 ---------
1172 ---------
1134
1173
1135 Whether the server supports the ``protocaps`` command for SSH V1 transport.
1174 Whether the server supports the ``protocaps`` command for SSH V1 transport.
1136
1175
1137 This capability was introduced in Mercurial 4.6.
1176 This capability was introduced in Mercurial 4.6.
1138
1177
1139 pushkey
1178 pushkey
1140 -------
1179 -------
1141
1180
1142 Whether the server supports the ``pushkey`` and ``listkeys`` commands.
1181 Whether the server supports the ``pushkey`` and ``listkeys`` commands.
1143
1182
1144 This capability was introduced in Mercurial 1.6 (released July 2010).
1183 This capability was introduced in Mercurial 1.6 (released July 2010).
1145
1184
1146 standardbundle
1185 standardbundle
1147 --------------
1186 --------------
1148
1187
1149 **Unsupported**
1188 **Unsupported**
1150
1189
1151 This capability was introduced during the Mercurial 0.9.2 development cycle in
1190 This capability was introduced during the Mercurial 0.9.2 development cycle in
1152 2006. It was never present in a release, as it was replaced by the ``unbundle``
1191 2006. It was never present in a release, as it was replaced by the ``unbundle``
1153 capability. This capability should not be encountered in the wild.
1192 capability. This capability should not be encountered in the wild.
1154
1193
1155 stream-preferred
1194 stream-preferred
1156 ----------------
1195 ----------------
1157
1196
1158 If present the server prefers that clients clone using the streaming clone
1197 If present the server prefers that clients clone using the streaming clone
1159 protocol (``hg clone --stream``) rather than the standard
1198 protocol (``hg clone --stream``) rather than the standard
1160 changegroup/bundle based protocol.
1199 changegroup/bundle based protocol.
1161
1200
1162 This capability was introduced in Mercurial 2.2 (released May 2012).
1201 This capability was introduced in Mercurial 2.2 (released May 2012).
1163
1202
1164 streamreqs
1203 streamreqs
1165 ----------
1204 ----------
1166
1205
1167 Indicates whether the server supports *streaming clones* and the *requirements*
1206 Indicates whether the server supports *streaming clones* and the *requirements*
1168 that clients must support to receive it.
1207 that clients must support to receive it.
1169
1208
1170 If present, the server supports the ``stream_out`` command, which transmits
1209 If present, the server supports the ``stream_out`` command, which transmits
1171 raw revlogs from the repository instead of changegroups. This provides a faster
1210 raw revlogs from the repository instead of changegroups. This provides a faster
1172 cloning mechanism at the expense of more bandwidth used.
1211 cloning mechanism at the expense of more bandwidth used.
1173
1212
1174 The value of this capability is a comma-delimited list of repo format
1213 The value of this capability is a comma-delimited list of repo format
1175 *requirements*. These are requirements that impact the reading of data in
1214 *requirements*. These are requirements that impact the reading of data in
1176 the ``.hg/store`` directory. An example value is
1215 the ``.hg/store`` directory. An example value is
1177 ``streamreqs=generaldelta,revlogv1`` indicating the server repo requires
1216 ``streamreqs=generaldelta,revlogv1`` indicating the server repo requires
1178 the ``revlogv1`` and ``generaldelta`` requirements.
1217 the ``revlogv1`` and ``generaldelta`` requirements.
1179
1218
1180 If the only format requirement is ``revlogv1``, the server may expose the
1219 If the only format requirement is ``revlogv1``, the server may expose the
1181 ``stream`` capability instead of the ``streamreqs`` capability.
1220 ``stream`` capability instead of the ``streamreqs`` capability.
1182
1221
1183 This capability was introduced in Mercurial 1.7 (released November 2010).
1222 This capability was introduced in Mercurial 1.7 (released November 2010).
1184
1223
1185 stream
1224 stream
1186 ------
1225 ------
1187
1226
1188 Whether the server supports *streaming clones* from ``revlogv1`` repos.
1227 Whether the server supports *streaming clones* from ``revlogv1`` repos.
1189
1228
1190 If present, the server supports the ``stream_out`` command, which transmits
1229 If present, the server supports the ``stream_out`` command, which transmits
1191 raw revlogs from the repository instead of changegroups. This provides a faster
1230 raw revlogs from the repository instead of changegroups. This provides a faster
1192 cloning mechanism at the expense of more bandwidth used.
1231 cloning mechanism at the expense of more bandwidth used.
1193
1232
1194 This capability was introduced in Mercurial 0.9.1 (released July 2006).
1233 This capability was introduced in Mercurial 0.9.1 (released July 2006).
1195
1234
1196 When initially introduced, the value of the capability was the numeric
1235 When initially introduced, the value of the capability was the numeric
1197 revlog revision. e.g. ``stream=1``. This indicates the changegroup is using
1236 revlog revision. e.g. ``stream=1``. This indicates the changegroup is using
1198 ``revlogv1``. This simple integer value wasn't powerful enough, so the
1237 ``revlogv1``. This simple integer value wasn't powerful enough, so the
1199 ``streamreqs`` capability was invented to handle cases where the repo
1238 ``streamreqs`` capability was invented to handle cases where the repo
1200 requirements have more than just ``revlogv1``. Newer servers omit the
1239 requirements have more than just ``revlogv1``. Newer servers omit the
1201 ``=1`` since it was the only value supported and the value of ``1`` can
1240 ``=1`` since it was the only value supported and the value of ``1`` can
1202 be implied by clients.
1241 be implied by clients.
1203
1242
1204 unbundlehash
1243 unbundlehash
1205 ------------
1244 ------------
1206
1245
1207 Whether the ``unbundle`` commands supports receiving a hash of all the
1246 Whether the ``unbundle`` commands supports receiving a hash of all the
1208 heads instead of a list.
1247 heads instead of a list.
1209
1248
1210 For more, see the documentation for the ``unbundle`` command.
1249 For more, see the documentation for the ``unbundle`` command.
1211
1250
1212 This capability was introduced in Mercurial 1.9 (released July 2011).
1251 This capability was introduced in Mercurial 1.9 (released July 2011).
1213
1252
1214 unbundle
1253 unbundle
1215 --------
1254 --------
1216
1255
1217 Whether the server supports pushing via the ``unbundle`` command.
1256 Whether the server supports pushing via the ``unbundle`` command.
1218
1257
1219 This capability/command has been present since Mercurial 0.9.1 (released
1258 This capability/command has been present since Mercurial 0.9.1 (released
1220 July 2006).
1259 July 2006).
1221
1260
1222 Mercurial 0.9.2 (released December 2006) added values to the capability
1261 Mercurial 0.9.2 (released December 2006) added values to the capability
1223 indicating which bundle types the server supports receiving. This value is a
1262 indicating which bundle types the server supports receiving. This value is a
1224 comma-delimited list. e.g. ``HG10GZ,HG10BZ,HG10UN``. The order of values
1263 comma-delimited list. e.g. ``HG10GZ,HG10BZ,HG10UN``. The order of values
1225 reflects the priority/preference of that type, where the first value is the
1264 reflects the priority/preference of that type, where the first value is the
1226 most preferred type.
1265 most preferred type.
1227
1266
1228 Content Negotiation
1267 Content Negotiation
1229 ===================
1268 ===================
1230
1269
1231 The wire protocol has some mechanisms to help peers determine what content
1270 The wire protocol has some mechanisms to help peers determine what content
1232 types and encoding the other side will accept. Historically, these mechanisms
1271 types and encoding the other side will accept. Historically, these mechanisms
1233 have been built into commands themselves because most commands only send a
1272 have been built into commands themselves because most commands only send a
1234 well-defined response type and only certain commands needed to support
1273 well-defined response type and only certain commands needed to support
1235 functionality like compression.
1274 functionality like compression.
1236
1275
1237 Currently, only the HTTP version 1 transport supports content negotiation
1276 Currently, only the HTTP version 1 transport supports content negotiation
1238 at the protocol layer.
1277 at the protocol layer.
1239
1278
1240 HTTP requests advertise supported response formats via the ``X-HgProto-<N>``
1279 HTTP requests advertise supported response formats via the ``X-HgProto-<N>``
1241 request header, where ``<N>`` is an integer starting at 1 allowing the logical
1280 request header, where ``<N>`` is an integer starting at 1 allowing the logical
1242 value to span multiple headers. This value consists of a list of
1281 value to span multiple headers. This value consists of a list of
1243 space-delimited parameters. Each parameter denotes a feature or capability.
1282 space-delimited parameters. Each parameter denotes a feature or capability.
1244
1283
1245 The following parameters are defined:
1284 The following parameters are defined:
1246
1285
1247 0.1
1286 0.1
1248 Indicates the client supports receiving ``application/mercurial-0.1``
1287 Indicates the client supports receiving ``application/mercurial-0.1``
1249 responses.
1288 responses.
1250
1289
1251 0.2
1290 0.2
1252 Indicates the client supports receiving ``application/mercurial-0.2``
1291 Indicates the client supports receiving ``application/mercurial-0.2``
1253 responses.
1292 responses.
1254
1293
1294 cbor
1295 Indicates the client supports receiving ``application/mercurial-cbor``
1296 responses.
1297
1298 (Only intended to be used with version 2 transports.)
1299
1255 comp
1300 comp
1256 Indicates compression formats the client can decode. Value is a list of
1301 Indicates compression formats the client can decode. Value is a list of
1257 comma delimited strings identifying compression formats ordered from
1302 comma delimited strings identifying compression formats ordered from
1258 most preferential to least preferential. e.g. ``comp=zstd,zlib,none``.
1303 most preferential to least preferential. e.g. ``comp=zstd,zlib,none``.
1259
1304
1260 This parameter does not have an effect if only the ``0.1`` parameter
1305 This parameter does not have an effect if only the ``0.1`` parameter
1261 is defined, as support for ``application/mercurial-0.2`` or greater is
1306 is defined, as support for ``application/mercurial-0.2`` or greater is
1262 required to use arbitrary compression formats.
1307 required to use arbitrary compression formats.
1263
1308
1264 If this parameter is not advertised, the server interprets this as
1309 If this parameter is not advertised, the server interprets this as
1265 equivalent to ``zlib,none``.
1310 equivalent to ``zlib,none``.
1266
1311
1267 Clients may choose to only send this header if the ``httpmediatype``
1312 Clients may choose to only send this header if the ``httpmediatype``
1268 server capability is present, as currently all server-side features
1313 server capability is present, as currently all server-side features
1269 consulting this header require the client to opt in to new protocol features
1314 consulting this header require the client to opt in to new protocol features
1270 advertised via the ``httpmediatype`` capability.
1315 advertised via the ``httpmediatype`` capability.
1271
1316
1272 A server that doesn't receive an ``X-HgProto-<N>`` header should infer a
1317 A server that doesn't receive an ``X-HgProto-<N>`` header should infer a
1273 value of ``0.1``. This is compatible with legacy clients.
1318 value of ``0.1``. This is compatible with legacy clients.
1274
1319
1275 A server receiving a request indicating support for multiple media type
1320 A server receiving a request indicating support for multiple media type
1276 versions may respond with any of the supported media types. Not all servers
1321 versions may respond with any of the supported media types. Not all servers
1277 may support all media types on all commands.
1322 may support all media types on all commands.
1278
1323
1279 Commands
1324 Commands
1280 ========
1325 ========
1281
1326
1282 This section contains a list of all wire protocol commands implemented by
1327 This section contains a list of all wire protocol commands implemented by
1283 the canonical Mercurial server.
1328 the canonical Mercurial server.
1284
1329
1285 batch
1330 batch
1286 -----
1331 -----
1287
1332
1288 Issue multiple commands while sending a single command request. The purpose
1333 Issue multiple commands while sending a single command request. The purpose
1289 of this command is to allow a client to issue multiple commands while avoiding
1334 of this command is to allow a client to issue multiple commands while avoiding
1290 multiple round trips to the server therefore enabling commands to complete
1335 multiple round trips to the server therefore enabling commands to complete
1291 quicker.
1336 quicker.
1292
1337
1293 The command accepts a ``cmds`` argument that contains a list of commands to
1338 The command accepts a ``cmds`` argument that contains a list of commands to
1294 execute.
1339 execute.
1295
1340
1296 The value of ``cmds`` is a ``;`` delimited list of strings. Each string has the
1341 The value of ``cmds`` is a ``;`` delimited list of strings. Each string has the
1297 form ``<command> <arguments>``. That is, the command name followed by a space
1342 form ``<command> <arguments>``. That is, the command name followed by a space
1298 followed by an argument string.
1343 followed by an argument string.
1299
1344
1300 The argument string is a ``,`` delimited list of ``<key>=<value>`` values
1345 The argument string is a ``,`` delimited list of ``<key>=<value>`` values
1301 corresponding to command arguments. Both the argument name and value are
1346 corresponding to command arguments. Both the argument name and value are
1302 escaped using a special substitution map::
1347 escaped using a special substitution map::
1303
1348
1304 : -> :c
1349 : -> :c
1305 , -> :o
1350 , -> :o
1306 ; -> :s
1351 ; -> :s
1307 = -> :e
1352 = -> :e
1308
1353
1309 The response type for this command is ``string``. The value contains a
1354 The response type for this command is ``string``. The value contains a
1310 ``;`` delimited list of responses for each requested command. Each value
1355 ``;`` delimited list of responses for each requested command. Each value
1311 in this list is escaped using the same substitution map used for arguments.
1356 in this list is escaped using the same substitution map used for arguments.
1312
1357
1313 If an error occurs, the generic error response may be sent.
1358 If an error occurs, the generic error response may be sent.
1314
1359
1315 between
1360 between
1316 -------
1361 -------
1317
1362
1318 (Legacy command used for discovery in old clients)
1363 (Legacy command used for discovery in old clients)
1319
1364
1320 Obtain nodes between pairs of nodes.
1365 Obtain nodes between pairs of nodes.
1321
1366
1322 The ``pairs`` arguments contains a space-delimited list of ``-`` delimited
1367 The ``pairs`` arguments contains a space-delimited list of ``-`` delimited
1323 hex node pairs. e.g.::
1368 hex node pairs. e.g.::
1324
1369
1325 a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896-6dc58916e7c070f678682bfe404d2e2d68291a18
1370 a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896-6dc58916e7c070f678682bfe404d2e2d68291a18
1326
1371
1327 Return type is a ``string``. Value consists of lines corresponding to each
1372 Return type is a ``string``. Value consists of lines corresponding to each
1328 requested range. Each line contains a space-delimited list of hex nodes.
1373 requested range. Each line contains a space-delimited list of hex nodes.
1329 A newline ``\n`` terminates each line, including the last one.
1374 A newline ``\n`` terminates each line, including the last one.
1330
1375
1331 branchmap
1376 branchmap
1332 ---------
1377 ---------
1333
1378
1334 Obtain heads in named branches.
1379 Obtain heads in named branches.
1335
1380
1336 Accepts no arguments. Return type is a ``string``.
1381 Accepts no arguments. Return type is a ``string``.
1337
1382
1338 Return value contains lines with URL encoded branch names followed by a space
1383 Return value contains lines with URL encoded branch names followed by a space
1339 followed by a space-delimited list of hex nodes of heads on that branch.
1384 followed by a space-delimited list of hex nodes of heads on that branch.
1340 e.g.::
1385 e.g.::
1341
1386
1342 default a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896 6dc58916e7c070f678682bfe404d2e2d68291a18
1387 default a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896 6dc58916e7c070f678682bfe404d2e2d68291a18
1343 stable baae3bf31522f41dd5e6d7377d0edd8d1cf3fccc
1388 stable baae3bf31522f41dd5e6d7377d0edd8d1cf3fccc
1344
1389
1345 There is no trailing newline.
1390 There is no trailing newline.
1346
1391
1347 branches
1392 branches
1348 --------
1393 --------
1349
1394
1350 (Legacy command used for discovery in old clients. Clients with ``getbundle``
1395 (Legacy command used for discovery in old clients. Clients with ``getbundle``
1351 use the ``known`` and ``heads`` commands instead.)
1396 use the ``known`` and ``heads`` commands instead.)
1352
1397
1353 Obtain ancestor changesets of specific nodes back to a branch point.
1398 Obtain ancestor changesets of specific nodes back to a branch point.
1354
1399
1355 Despite the name, this command has nothing to do with Mercurial named branches.
1400 Despite the name, this command has nothing to do with Mercurial named branches.
1356 Instead, it is related to DAG branches.
1401 Instead, it is related to DAG branches.
1357
1402
1358 The command accepts a ``nodes`` argument, which is a string of space-delimited
1403 The command accepts a ``nodes`` argument, which is a string of space-delimited
1359 hex nodes.
1404 hex nodes.
1360
1405
1361 For each node requested, the server will find the first ancestor node that is
1406 For each node requested, the server will find the first ancestor node that is
1362 a DAG root or is a merge.
1407 a DAG root or is a merge.
1363
1408
1364 Return type is a ``string``. Return value contains lines with result data for
1409 Return type is a ``string``. Return value contains lines with result data for
1365 each requested node. Each line contains space-delimited nodes followed by a
1410 each requested node. Each line contains space-delimited nodes followed by a
1366 newline (``\n``). The 4 nodes reported on each line correspond to the requested
1411 newline (``\n``). The 4 nodes reported on each line correspond to the requested
1367 node, the ancestor node found, and its 2 parent nodes (which may be the null
1412 node, the ancestor node found, and its 2 parent nodes (which may be the null
1368 node).
1413 node).
1369
1414
1370 capabilities
1415 capabilities
1371 ------------
1416 ------------
1372
1417
1373 Obtain the capabilities string for the repo.
1418 Obtain the capabilities string for the repo.
1374
1419
1375 Unlike the ``hello`` command, the capabilities string is not prefixed.
1420 Unlike the ``hello`` command, the capabilities string is not prefixed.
1376 There is no trailing newline.
1421 There is no trailing newline.
1377
1422
1378 This command does not accept any arguments. Return type is a ``string``.
1423 This command does not accept any arguments. Return type is a ``string``.
1379
1424
1380 This command was introduced in Mercurial 0.9.1 (released July 2006).
1425 This command was introduced in Mercurial 0.9.1 (released July 2006).
1381
1426
1382 changegroup
1427 changegroup
1383 -----------
1428 -----------
1384
1429
1385 (Legacy command: use ``getbundle`` instead)
1430 (Legacy command: use ``getbundle`` instead)
1386
1431
1387 Obtain a changegroup version 1 with data for changesets that are
1432 Obtain a changegroup version 1 with data for changesets that are
1388 descendants of client-specified changesets.
1433 descendants of client-specified changesets.
1389
1434
1390 The ``roots`` arguments contains a list of space-delimited hex nodes.
1435 The ``roots`` arguments contains a list of space-delimited hex nodes.
1391
1436
1392 The server responds with a changegroup version 1 containing all
1437 The server responds with a changegroup version 1 containing all
1393 changesets between the requested root/base nodes and the repo's head nodes
1438 changesets between the requested root/base nodes and the repo's head nodes
1394 at the time of the request.
1439 at the time of the request.
1395
1440
1396 The return type is a ``stream``.
1441 The return type is a ``stream``.
1397
1442
1398 changegroupsubset
1443 changegroupsubset
1399 -----------------
1444 -----------------
1400
1445
1401 (Legacy command: use ``getbundle`` instead)
1446 (Legacy command: use ``getbundle`` instead)
1402
1447
1403 Obtain a changegroup version 1 with data for changesetsets between
1448 Obtain a changegroup version 1 with data for changesetsets between
1404 client specified base and head nodes.
1449 client specified base and head nodes.
1405
1450
1406 The ``bases`` argument contains a list of space-delimited hex nodes.
1451 The ``bases`` argument contains a list of space-delimited hex nodes.
1407 The ``heads`` argument contains a list of space-delimited hex nodes.
1452 The ``heads`` argument contains a list of space-delimited hex nodes.
1408
1453
1409 The server responds with a changegroup version 1 containing all
1454 The server responds with a changegroup version 1 containing all
1410 changesets between the requested base and head nodes at the time of the
1455 changesets between the requested base and head nodes at the time of the
1411 request.
1456 request.
1412
1457
1413 The return type is a ``stream``.
1458 The return type is a ``stream``.
1414
1459
1415 clonebundles
1460 clonebundles
1416 ------------
1461 ------------
1417
1462
1418 Obtains a manifest of bundle URLs available to seed clones.
1463 Obtains a manifest of bundle URLs available to seed clones.
1419
1464
1420 Each returned line contains a URL followed by metadata. See the
1465 Each returned line contains a URL followed by metadata. See the
1421 documentation in the ``clonebundles`` extension for more.
1466 documentation in the ``clonebundles`` extension for more.
1422
1467
1423 The return type is a ``string``.
1468 The return type is a ``string``.
1424
1469
1425 getbundle
1470 getbundle
1426 ---------
1471 ---------
1427
1472
1428 Obtain a bundle containing repository data.
1473 Obtain a bundle containing repository data.
1429
1474
1430 This command accepts the following arguments:
1475 This command accepts the following arguments:
1431
1476
1432 heads
1477 heads
1433 List of space-delimited hex nodes of heads to retrieve.
1478 List of space-delimited hex nodes of heads to retrieve.
1434 common
1479 common
1435 List of space-delimited hex nodes that the client has in common with the
1480 List of space-delimited hex nodes that the client has in common with the
1436 server.
1481 server.
1437 obsmarkers
1482 obsmarkers
1438 Boolean indicating whether to include obsolescence markers as part
1483 Boolean indicating whether to include obsolescence markers as part
1439 of the response. Only works with bundle2.
1484 of the response. Only works with bundle2.
1440 bundlecaps
1485 bundlecaps
1441 Comma-delimited set of strings defining client bundle capabilities.
1486 Comma-delimited set of strings defining client bundle capabilities.
1442 listkeys
1487 listkeys
1443 Comma-delimited list of strings of ``pushkey`` namespaces. For each
1488 Comma-delimited list of strings of ``pushkey`` namespaces. For each
1444 namespace listed, a bundle2 part will be included with the content of
1489 namespace listed, a bundle2 part will be included with the content of
1445 that namespace.
1490 that namespace.
1446 cg
1491 cg
1447 Boolean indicating whether changegroup data is requested.
1492 Boolean indicating whether changegroup data is requested.
1448 cbattempted
1493 cbattempted
1449 Boolean indicating whether the client attempted to use the *clone bundles*
1494 Boolean indicating whether the client attempted to use the *clone bundles*
1450 feature before performing this request.
1495 feature before performing this request.
1451 bookmarks
1496 bookmarks
1452 Boolean indicating whether bookmark data is requested.
1497 Boolean indicating whether bookmark data is requested.
1453 phases
1498 phases
1454 Boolean indicating whether phases data is requested.
1499 Boolean indicating whether phases data is requested.
1455
1500
1456 The return type on success is a ``stream`` where the value is bundle.
1501 The return type on success is a ``stream`` where the value is bundle.
1457 On the HTTP version 1 transport, the response is zlib compressed.
1502 On the HTTP version 1 transport, the response is zlib compressed.
1458
1503
1459 If an error occurs, a generic error response can be sent.
1504 If an error occurs, a generic error response can be sent.
1460
1505
1461 Unless the client sends a false value for the ``cg`` argument, the returned
1506 Unless the client sends a false value for the ``cg`` argument, the returned
1462 bundle contains a changegroup with the nodes between the specified ``common``
1507 bundle contains a changegroup with the nodes between the specified ``common``
1463 and ``heads`` nodes. Depending on the command arguments, the type and content
1508 and ``heads`` nodes. Depending on the command arguments, the type and content
1464 of the returned bundle can vary significantly.
1509 of the returned bundle can vary significantly.
1465
1510
1466 The default behavior is for the server to send a raw changegroup version
1511 The default behavior is for the server to send a raw changegroup version
1467 ``01`` response.
1512 ``01`` response.
1468
1513
1469 If the ``bundlecaps`` provided by the client contain a value beginning
1514 If the ``bundlecaps`` provided by the client contain a value beginning
1470 with ``HG2``, a bundle2 will be returned. The bundle2 data may contain
1515 with ``HG2``, a bundle2 will be returned. The bundle2 data may contain
1471 additional repository data, such as ``pushkey`` namespace values.
1516 additional repository data, such as ``pushkey`` namespace values.
1472
1517
1473 heads
1518 heads
1474 -----
1519 -----
1475
1520
1476 Returns a list of space-delimited hex nodes of repository heads followed
1521 Returns a list of space-delimited hex nodes of repository heads followed
1477 by a newline. e.g.
1522 by a newline. e.g.
1478 ``a9eeb3adc7ddb5006c088e9eda61791c777cbf7c 31f91a3da534dc849f0d6bfc00a395a97cf218a1\n``
1523 ``a9eeb3adc7ddb5006c088e9eda61791c777cbf7c 31f91a3da534dc849f0d6bfc00a395a97cf218a1\n``
1479
1524
1480 This command does not accept any arguments. The return type is a ``string``.
1525 This command does not accept any arguments. The return type is a ``string``.
1481
1526
1482 hello
1527 hello
1483 -----
1528 -----
1484
1529
1485 Returns lines describing interesting things about the server in an RFC-822
1530 Returns lines describing interesting things about the server in an RFC-822
1486 like format.
1531 like format.
1487
1532
1488 Currently, the only line defines the server capabilities. It has the form::
1533 Currently, the only line defines the server capabilities. It has the form::
1489
1534
1490 capabilities: <value>
1535 capabilities: <value>
1491
1536
1492 See above for more about the capabilities string.
1537 See above for more about the capabilities string.
1493
1538
1494 SSH clients typically issue this command as soon as a connection is
1539 SSH clients typically issue this command as soon as a connection is
1495 established.
1540 established.
1496
1541
1497 This command does not accept any arguments. The return type is a ``string``.
1542 This command does not accept any arguments. The return type is a ``string``.
1498
1543
1499 This command was introduced in Mercurial 0.9.1 (released July 2006).
1544 This command was introduced in Mercurial 0.9.1 (released July 2006).
1500
1545
1501 listkeys
1546 listkeys
1502 --------
1547 --------
1503
1548
1504 List values in a specified ``pushkey`` namespace.
1549 List values in a specified ``pushkey`` namespace.
1505
1550
1506 The ``namespace`` argument defines the pushkey namespace to operate on.
1551 The ``namespace`` argument defines the pushkey namespace to operate on.
1507
1552
1508 The return type is a ``string``. The value is an encoded dictionary of keys.
1553 The return type is a ``string``. The value is an encoded dictionary of keys.
1509
1554
1510 Key-value pairs are delimited by newlines (``\n``). Within each line, keys and
1555 Key-value pairs are delimited by newlines (``\n``). Within each line, keys and
1511 values are separated by a tab (``\t``). Keys and values are both strings.
1556 values are separated by a tab (``\t``). Keys and values are both strings.
1512
1557
1513 lookup
1558 lookup
1514 ------
1559 ------
1515
1560
1516 Try to resolve a value to a known repository revision.
1561 Try to resolve a value to a known repository revision.
1517
1562
1518 The ``key`` argument is converted from bytes to an
1563 The ``key`` argument is converted from bytes to an
1519 ``encoding.localstr`` instance then passed into
1564 ``encoding.localstr`` instance then passed into
1520 ``localrepository.__getitem__`` in an attempt to resolve it.
1565 ``localrepository.__getitem__`` in an attempt to resolve it.
1521
1566
1522 The return type is a ``string``.
1567 The return type is a ``string``.
1523
1568
1524 Upon successful resolution, returns ``1 <hex node>\n``. On failure,
1569 Upon successful resolution, returns ``1 <hex node>\n``. On failure,
1525 returns ``0 <error string>\n``. e.g.::
1570 returns ``0 <error string>\n``. e.g.::
1526
1571
1527 1 273ce12ad8f155317b2c078ec75a4eba507f1fba\n
1572 1 273ce12ad8f155317b2c078ec75a4eba507f1fba\n
1528
1573
1529 0 unknown revision 'foo'\n
1574 0 unknown revision 'foo'\n
1530
1575
1531 known
1576 known
1532 -----
1577 -----
1533
1578
1534 Determine whether multiple nodes are known.
1579 Determine whether multiple nodes are known.
1535
1580
1536 The ``nodes`` argument is a list of space-delimited hex nodes to check
1581 The ``nodes`` argument is a list of space-delimited hex nodes to check
1537 for existence.
1582 for existence.
1538
1583
1539 The return type is ``string``.
1584 The return type is ``string``.
1540
1585
1541 Returns a string consisting of ``0``s and ``1``s indicating whether nodes
1586 Returns a string consisting of ``0``s and ``1``s indicating whether nodes
1542 are known. If the Nth node specified in the ``nodes`` argument is known,
1587 are known. If the Nth node specified in the ``nodes`` argument is known,
1543 a ``1`` will be returned at byte offset N. If the node isn't known, ``0``
1588 a ``1`` will be returned at byte offset N. If the node isn't known, ``0``
1544 will be present at byte offset N.
1589 will be present at byte offset N.
1545
1590
1546 There is no trailing newline.
1591 There is no trailing newline.
1547
1592
1548 protocaps
1593 protocaps
1549 ---------
1594 ---------
1550
1595
1551 Notify the server about the client capabilities in the SSH V1 transport
1596 Notify the server about the client capabilities in the SSH V1 transport
1552 protocol.
1597 protocol.
1553
1598
1554 The ``caps`` argument is a space-delimited list of capabilities.
1599 The ``caps`` argument is a space-delimited list of capabilities.
1555
1600
1556 The server will reply with the string ``OK``.
1601 The server will reply with the string ``OK``.
1557
1602
1558 pushkey
1603 pushkey
1559 -------
1604 -------
1560
1605
1561 Set a value using the ``pushkey`` protocol.
1606 Set a value using the ``pushkey`` protocol.
1562
1607
1563 Accepts arguments ``namespace``, ``key``, ``old``, and ``new``, which
1608 Accepts arguments ``namespace``, ``key``, ``old``, and ``new``, which
1564 correspond to the pushkey namespace to operate on, the key within that
1609 correspond to the pushkey namespace to operate on, the key within that
1565 namespace to change, the old value (which may be empty), and the new value.
1610 namespace to change, the old value (which may be empty), and the new value.
1566 All arguments are string types.
1611 All arguments are string types.
1567
1612
1568 The return type is a ``string``. The value depends on the transport protocol.
1613 The return type is a ``string``. The value depends on the transport protocol.
1569
1614
1570 The SSH version 1 transport sends a string encoded integer followed by a
1615 The SSH version 1 transport sends a string encoded integer followed by a
1571 newline (``\n``) which indicates operation result. The server may send
1616 newline (``\n``) which indicates operation result. The server may send
1572 additional output on the ``stderr`` stream that should be displayed to the
1617 additional output on the ``stderr`` stream that should be displayed to the
1573 user.
1618 user.
1574
1619
1575 The HTTP version 1 transport sends a string encoded integer followed by a
1620 The HTTP version 1 transport sends a string encoded integer followed by a
1576 newline followed by additional server output that should be displayed to
1621 newline followed by additional server output that should be displayed to
1577 the user. This may include output from hooks, etc.
1622 the user. This may include output from hooks, etc.
1578
1623
1579 The integer result varies by namespace. ``0`` means an error has occurred
1624 The integer result varies by namespace. ``0`` means an error has occurred
1580 and there should be additional output to display to the user.
1625 and there should be additional output to display to the user.
1581
1626
1582 stream_out
1627 stream_out
1583 ----------
1628 ----------
1584
1629
1585 Obtain *streaming clone* data.
1630 Obtain *streaming clone* data.
1586
1631
1587 The return type is either a ``string`` or a ``stream``, depending on
1632 The return type is either a ``string`` or a ``stream``, depending on
1588 whether the request was fulfilled properly.
1633 whether the request was fulfilled properly.
1589
1634
1590 A return value of ``1\n`` indicates the server is not configured to serve
1635 A return value of ``1\n`` indicates the server is not configured to serve
1591 this data. If this is seen by the client, they may not have verified the
1636 this data. If this is seen by the client, they may not have verified the
1592 ``stream`` capability is set before making the request.
1637 ``stream`` capability is set before making the request.
1593
1638
1594 A return value of ``2\n`` indicates the server was unable to lock the
1639 A return value of ``2\n`` indicates the server was unable to lock the
1595 repository to generate data.
1640 repository to generate data.
1596
1641
1597 All other responses are a ``stream`` of bytes. The first line of this data
1642 All other responses are a ``stream`` of bytes. The first line of this data
1598 contains 2 space-delimited integers corresponding to the path count and
1643 contains 2 space-delimited integers corresponding to the path count and
1599 payload size, respectively::
1644 payload size, respectively::
1600
1645
1601 <path count> <payload size>\n
1646 <path count> <payload size>\n
1602
1647
1603 The ``<payload size>`` is the total size of path data: it does not include
1648 The ``<payload size>`` is the total size of path data: it does not include
1604 the size of the per-path header lines.
1649 the size of the per-path header lines.
1605
1650
1606 Following that header are ``<path count>`` entries. Each entry consists of a
1651 Following that header are ``<path count>`` entries. Each entry consists of a
1607 line with metadata followed by raw revlog data. The line consists of::
1652 line with metadata followed by raw revlog data. The line consists of::
1608
1653
1609 <store path>\0<size>\n
1654 <store path>\0<size>\n
1610
1655
1611 The ``<store path>`` is the encoded store path of the data that follows.
1656 The ``<store path>`` is the encoded store path of the data that follows.
1612 ``<size>`` is the amount of data for this store path/revlog that follows the
1657 ``<size>`` is the amount of data for this store path/revlog that follows the
1613 newline.
1658 newline.
1614
1659
1615 There is no trailer to indicate end of data. Instead, the client should stop
1660 There is no trailer to indicate end of data. Instead, the client should stop
1616 reading after ``<path count>`` entries are consumed.
1661 reading after ``<path count>`` entries are consumed.
1617
1662
1618 unbundle
1663 unbundle
1619 --------
1664 --------
1620
1665
1621 Send a bundle containing data (usually changegroup data) to the server.
1666 Send a bundle containing data (usually changegroup data) to the server.
1622
1667
1623 Accepts the argument ``heads``, which is a space-delimited list of hex nodes
1668 Accepts the argument ``heads``, which is a space-delimited list of hex nodes
1624 corresponding to server repository heads observed by the client. This is used
1669 corresponding to server repository heads observed by the client. This is used
1625 to detect race conditions and abort push operations before a server performs
1670 to detect race conditions and abort push operations before a server performs
1626 too much work or a client transfers too much data.
1671 too much work or a client transfers too much data.
1627
1672
1628 The request payload consists of a bundle to be applied to the repository,
1673 The request payload consists of a bundle to be applied to the repository,
1629 similarly to as if :hg:`unbundle` were called.
1674 similarly to as if :hg:`unbundle` were called.
1630
1675
1631 In most scenarios, a special ``push response`` type is returned. This type
1676 In most scenarios, a special ``push response`` type is returned. This type
1632 contains an integer describing the change in heads as a result of the
1677 contains an integer describing the change in heads as a result of the
1633 operation. A value of ``0`` indicates nothing changed. ``1`` means the number
1678 operation. A value of ``0`` indicates nothing changed. ``1`` means the number
1634 of heads remained the same. Values ``2`` and larger indicate the number of
1679 of heads remained the same. Values ``2`` and larger indicate the number of
1635 added heads minus 1. e.g. ``3`` means 2 heads were added. Negative values
1680 added heads minus 1. e.g. ``3`` means 2 heads were added. Negative values
1636 indicate the number of fewer heads, also off by 1. e.g. ``-2`` means there
1681 indicate the number of fewer heads, also off by 1. e.g. ``-2`` means there
1637 is 1 fewer head.
1682 is 1 fewer head.
1638
1683
1639 The encoding of the ``push response`` type varies by transport.
1684 The encoding of the ``push response`` type varies by transport.
1640
1685
1641 For the SSH version 1 transport, this type is composed of 2 ``string``
1686 For the SSH version 1 transport, this type is composed of 2 ``string``
1642 responses: an empty response (``0\n``) followed by the integer result value.
1687 responses: an empty response (``0\n``) followed by the integer result value.
1643 e.g. ``1\n2``. So the full response might be ``0\n1\n2``.
1688 e.g. ``1\n2``. So the full response might be ``0\n1\n2``.
1644
1689
1645 For the HTTP version 1 transport, the response is a ``string`` type composed
1690 For the HTTP version 1 transport, the response is a ``string`` type composed
1646 of an integer result value followed by a newline (``\n``) followed by string
1691 of an integer result value followed by a newline (``\n``) followed by string
1647 content holding server output that should be displayed on the client (output
1692 content holding server output that should be displayed on the client (output
1648 hooks, etc).
1693 hooks, etc).
1649
1694
1650 In some cases, the server may respond with a ``bundle2`` bundle. In this
1695 In some cases, the server may respond with a ``bundle2`` bundle. In this
1651 case, the response type is ``stream``. For the HTTP version 1 transport, the
1696 case, the response type is ``stream``. For the HTTP version 1 transport, the
1652 response is zlib compressed.
1697 response is zlib compressed.
1653
1698
1654 The server may also respond with a generic error type, which contains a string
1699 The server may also respond with a generic error type, which contains a string
1655 indicating the failure.
1700 indicating the failure.
1656
1701
1657 Frame-Based Protocol Commands
1702 Frame-Based Protocol Commands
1658 =============================
1703 =============================
1659
1704
1660 **Experimental and under active development**
1705 **Experimental and under active development**
1661
1706
1662 This section documents the wire protocol commands exposed to transports
1707 This section documents the wire protocol commands exposed to transports
1663 using the frame-based protocol. The set of commands exposed through
1708 using the frame-based protocol. The set of commands exposed through
1664 these transports is distinct from the set of commands exposed to legacy
1709 these transports is distinct from the set of commands exposed to legacy
1665 transports.
1710 transports.
1666
1711
1667 The frame-based protocol uses CBOR to encode command execution requests.
1712 The frame-based protocol uses CBOR to encode command execution requests.
1668 All command arguments must be mapped to a specific or set of CBOR data
1713 All command arguments must be mapped to a specific or set of CBOR data
1669 types.
1714 types.
1670
1715
1671 The response to many commands is also CBOR. There is no common response
1716 The response to many commands is also CBOR. There is no common response
1672 format: each command defines its own response format.
1717 format: each command defines its own response format.
1673
1718
1674 TODO require node type be specified, as N bytes of binary node value
1719 TODO require node type be specified, as N bytes of binary node value
1675 could be ambiguous once SHA-1 is replaced.
1720 could be ambiguous once SHA-1 is replaced.
1676
1721
1677 branchmap
1722 branchmap
1678 ---------
1723 ---------
1679
1724
1680 Obtain heads in named branches.
1725 Obtain heads in named branches.
1681
1726
1682 Receives no arguments.
1727 Receives no arguments.
1683
1728
1684 The response is a map with bytestring keys defining the branch name.
1729 The response is a map with bytestring keys defining the branch name.
1685 Values are arrays of bytestring defining raw changeset nodes.
1730 Values are arrays of bytestring defining raw changeset nodes.
1686
1731
1687 capabilities
1732 capabilities
1688 ------------
1733 ------------
1689
1734
1690 Obtain the server's capabilities.
1735 Obtain the server's capabilities.
1691
1736
1692 Receives no arguments.
1737 Receives no arguments.
1693
1738
1694 This command is typically called only as part of the handshake during
1739 This command is typically called only as part of the handshake during
1695 initial connection establishment.
1740 initial connection establishment.
1696
1741
1697 The response is a map with bytestring keys defining server information.
1742 The response is a map with bytestring keys defining server information.
1698
1743
1699 The defined keys are:
1744 The defined keys are:
1700
1745
1701 commands
1746 commands
1702 A map defining available wire protocol commands on this server.
1747 A map defining available wire protocol commands on this server.
1703
1748
1704 Keys in the map are the names of commands that can be invoked. Values
1749 Keys in the map are the names of commands that can be invoked. Values
1705 are maps defining information about that command. The bytestring keys
1750 are maps defining information about that command. The bytestring keys
1706 are:
1751 are:
1707
1752
1708 args
1753 args
1709 A map of argument names and their expected types.
1754 A map of argument names and their expected types.
1710
1755
1711 Types are defined as a representative value for the expected type.
1756 Types are defined as a representative value for the expected type.
1712 e.g. an argument expecting a boolean type will have its value
1757 e.g. an argument expecting a boolean type will have its value
1713 set to true. An integer type will have its value set to 42. The
1758 set to true. An integer type will have its value set to 42. The
1714 actual values are arbitrary and may not have meaning.
1759 actual values are arbitrary and may not have meaning.
1715 permissions
1760 permissions
1716 An array of permissions required to execute this command.
1761 An array of permissions required to execute this command.
1717
1762
1718 compression
1763 compression
1719 An array of maps defining available compression format support.
1764 An array of maps defining available compression format support.
1720
1765
1721 The array is sorted from most preferred to least preferred.
1766 The array is sorted from most preferred to least preferred.
1722
1767
1723 Each entry has the following bytestring keys:
1768 Each entry has the following bytestring keys:
1724
1769
1725 name
1770 name
1726 Name of the compression engine. e.g. ``zstd`` or ``zlib``.
1771 Name of the compression engine. e.g. ``zstd`` or ``zlib``.
1727
1772
1728 heads
1773 heads
1729 -----
1774 -----
1730
1775
1731 Obtain DAG heads in the repository.
1776 Obtain DAG heads in the repository.
1732
1777
1733 The command accepts the following arguments:
1778 The command accepts the following arguments:
1734
1779
1735 publiconly (optional)
1780 publiconly (optional)
1736 (boolean) If set, operate on the DAG for public phase changesets only.
1781 (boolean) If set, operate on the DAG for public phase changesets only.
1737 Non-public (i.e. draft) phase DAG heads will not be returned.
1782 Non-public (i.e. draft) phase DAG heads will not be returned.
1738
1783
1739 The response is a CBOR array of bytestrings defining changeset nodes
1784 The response is a CBOR array of bytestrings defining changeset nodes
1740 of DAG heads. The array can be empty if the repository is empty or no
1785 of DAG heads. The array can be empty if the repository is empty or no
1741 changesets satisfied the request.
1786 changesets satisfied the request.
1742
1787
1743 TODO consider exposing phase of heads in response
1788 TODO consider exposing phase of heads in response
1744
1789
1745 known
1790 known
1746 -----
1791 -----
1747
1792
1748 Determine whether a series of changeset nodes is known to the server.
1793 Determine whether a series of changeset nodes is known to the server.
1749
1794
1750 The command accepts the following arguments:
1795 The command accepts the following arguments:
1751
1796
1752 nodes
1797 nodes
1753 (array of bytestrings) List of changeset nodes whose presence to
1798 (array of bytestrings) List of changeset nodes whose presence to
1754 query.
1799 query.
1755
1800
1756 The response is a bytestring where each byte contains a 0 or 1 for the
1801 The response is a bytestring where each byte contains a 0 or 1 for the
1757 corresponding requested node at the same index.
1802 corresponding requested node at the same index.
1758
1803
1759 TODO use a bit array for even more compact response
1804 TODO use a bit array for even more compact response
1760
1805
1761 listkeys
1806 listkeys
1762 --------
1807 --------
1763
1808
1764 List values in a specified ``pushkey`` namespace.
1809 List values in a specified ``pushkey`` namespace.
1765
1810
1766 The command receives the following arguments:
1811 The command receives the following arguments:
1767
1812
1768 namespace
1813 namespace
1769 (bytestring) Pushkey namespace to query.
1814 (bytestring) Pushkey namespace to query.
1770
1815
1771 The response is a map with bytestring keys and values.
1816 The response is a map with bytestring keys and values.
1772
1817
1773 TODO consider using binary to represent nodes in certain pushkey namespaces.
1818 TODO consider using binary to represent nodes in certain pushkey namespaces.
1774
1819
1775 lookup
1820 lookup
1776 ------
1821 ------
1777
1822
1778 Try to resolve a value to a changeset revision.
1823 Try to resolve a value to a changeset revision.
1779
1824
1780 Unlike ``known`` which operates on changeset nodes, lookup operates on
1825 Unlike ``known`` which operates on changeset nodes, lookup operates on
1781 node fragments and other names that a user may use.
1826 node fragments and other names that a user may use.
1782
1827
1783 The command receives the following arguments:
1828 The command receives the following arguments:
1784
1829
1785 key
1830 key
1786 (bytestring) Value to try to resolve.
1831 (bytestring) Value to try to resolve.
1787
1832
1788 On success, returns a bytestring containing the resolved node.
1833 On success, returns a bytestring containing the resolved node.
1789
1834
1790 pushkey
1835 pushkey
1791 -------
1836 -------
1792
1837
1793 Set a value using the ``pushkey`` protocol.
1838 Set a value using the ``pushkey`` protocol.
1794
1839
1795 The command receives the following arguments:
1840 The command receives the following arguments:
1796
1841
1797 namespace
1842 namespace
1798 (bytestring) Pushkey namespace to operate on.
1843 (bytestring) Pushkey namespace to operate on.
1799 key
1844 key
1800 (bytestring) The pushkey key to set.
1845 (bytestring) The pushkey key to set.
1801 old
1846 old
1802 (bytestring) Old value for this key.
1847 (bytestring) Old value for this key.
1803 new
1848 new
1804 (bytestring) New value for this key.
1849 (bytestring) New value for this key.
1805
1850
1806 TODO consider using binary to represent nodes is certain pushkey namespaces.
1851 TODO consider using binary to represent nodes is certain pushkey namespaces.
1807 TODO better define response type and meaning.
1852 TODO better define response type and meaning.
@@ -1,737 +1,811 b''
1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 #
3 #
4 # This software may be used and distributed according to the terms of the
4 # This software may be used and distributed according to the terms of the
5 # GNU General Public License version 2 or any later version.
5 # GNU General Public License version 2 or any later version.
6
6
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import contextlib
9 import contextlib
10 import struct
10 import struct
11 import sys
11 import sys
12 import threading
12 import threading
13
13
14 from .i18n import _
14 from .i18n import _
15 from .thirdparty import (
16 cbor,
17 )
15 from .thirdparty.zope import (
18 from .thirdparty.zope import (
16 interface as zi,
19 interface as zi,
17 )
20 )
18 from . import (
21 from . import (
19 encoding,
22 encoding,
20 error,
23 error,
21 hook,
24 hook,
22 pycompat,
25 pycompat,
23 util,
26 util,
24 wireproto,
27 wireproto,
25 wireprototypes,
28 wireprototypes,
26 wireprotov2server,
29 wireprotov2server,
27 )
30 )
28 from .utils import (
31 from .utils import (
29 procutil,
32 procutil,
30 )
33 )
31
34
32 stringio = util.stringio
35 stringio = util.stringio
33
36
34 urlerr = util.urlerr
37 urlerr = util.urlerr
35 urlreq = util.urlreq
38 urlreq = util.urlreq
36
39
37 HTTP_OK = 200
40 HTTP_OK = 200
38
41
39 HGTYPE = 'application/mercurial-0.1'
42 HGTYPE = 'application/mercurial-0.1'
40 HGTYPE2 = 'application/mercurial-0.2'
43 HGTYPE2 = 'application/mercurial-0.2'
41 HGERRTYPE = 'application/hg-error'
44 HGERRTYPE = 'application/hg-error'
42
45
43 SSHV1 = wireprototypes.SSHV1
46 SSHV1 = wireprototypes.SSHV1
44 SSHV2 = wireprototypes.SSHV2
47 SSHV2 = wireprototypes.SSHV2
45
48
46 def decodevaluefromheaders(req, headerprefix):
49 def decodevaluefromheaders(req, headerprefix):
47 """Decode a long value from multiple HTTP request headers.
50 """Decode a long value from multiple HTTP request headers.
48
51
49 Returns the value as a bytes, not a str.
52 Returns the value as a bytes, not a str.
50 """
53 """
51 chunks = []
54 chunks = []
52 i = 1
55 i = 1
53 while True:
56 while True:
54 v = req.headers.get(b'%s-%d' % (headerprefix, i))
57 v = req.headers.get(b'%s-%d' % (headerprefix, i))
55 if v is None:
58 if v is None:
56 break
59 break
57 chunks.append(pycompat.bytesurl(v))
60 chunks.append(pycompat.bytesurl(v))
58 i += 1
61 i += 1
59
62
60 return ''.join(chunks)
63 return ''.join(chunks)
61
64
62 @zi.implementer(wireprototypes.baseprotocolhandler)
65 @zi.implementer(wireprototypes.baseprotocolhandler)
63 class httpv1protocolhandler(object):
66 class httpv1protocolhandler(object):
64 def __init__(self, req, ui, checkperm):
67 def __init__(self, req, ui, checkperm):
65 self._req = req
68 self._req = req
66 self._ui = ui
69 self._ui = ui
67 self._checkperm = checkperm
70 self._checkperm = checkperm
68 self._protocaps = None
71 self._protocaps = None
69
72
70 @property
73 @property
71 def name(self):
74 def name(self):
72 return 'http-v1'
75 return 'http-v1'
73
76
74 def getargs(self, args):
77 def getargs(self, args):
75 knownargs = self._args()
78 knownargs = self._args()
76 data = {}
79 data = {}
77 keys = args.split()
80 keys = args.split()
78 for k in keys:
81 for k in keys:
79 if k == '*':
82 if k == '*':
80 star = {}
83 star = {}
81 for key in knownargs.keys():
84 for key in knownargs.keys():
82 if key != 'cmd' and key not in keys:
85 if key != 'cmd' and key not in keys:
83 star[key] = knownargs[key][0]
86 star[key] = knownargs[key][0]
84 data['*'] = star
87 data['*'] = star
85 else:
88 else:
86 data[k] = knownargs[k][0]
89 data[k] = knownargs[k][0]
87 return [data[k] for k in keys]
90 return [data[k] for k in keys]
88
91
89 def _args(self):
92 def _args(self):
90 args = self._req.qsparams.asdictoflists()
93 args = self._req.qsparams.asdictoflists()
91 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
94 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
92 if postlen:
95 if postlen:
93 args.update(urlreq.parseqs(
96 args.update(urlreq.parseqs(
94 self._req.bodyfh.read(postlen), keep_blank_values=True))
97 self._req.bodyfh.read(postlen), keep_blank_values=True))
95 return args
98 return args
96
99
97 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
100 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
98 args.update(urlreq.parseqs(argvalue, keep_blank_values=True))
101 args.update(urlreq.parseqs(argvalue, keep_blank_values=True))
99 return args
102 return args
100
103
101 def getprotocaps(self):
104 def getprotocaps(self):
102 if self._protocaps is None:
105 if self._protocaps is None:
103 value = decodevaluefromheaders(self._req, r'X-HgProto')
106 value = decodevaluefromheaders(self._req, r'X-HgProto')
104 self._protocaps = set(value.split(' '))
107 self._protocaps = set(value.split(' '))
105 return self._protocaps
108 return self._protocaps
106
109
107 def getpayload(self):
110 def getpayload(self):
108 # Existing clients *always* send Content-Length.
111 # Existing clients *always* send Content-Length.
109 length = int(self._req.headers[b'Content-Length'])
112 length = int(self._req.headers[b'Content-Length'])
110
113
111 # If httppostargs is used, we need to read Content-Length
114 # If httppostargs is used, we need to read Content-Length
112 # minus the amount that was consumed by args.
115 # minus the amount that was consumed by args.
113 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
116 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
114 return util.filechunkiter(self._req.bodyfh, limit=length)
117 return util.filechunkiter(self._req.bodyfh, limit=length)
115
118
116 @contextlib.contextmanager
119 @contextlib.contextmanager
117 def mayberedirectstdio(self):
120 def mayberedirectstdio(self):
118 oldout = self._ui.fout
121 oldout = self._ui.fout
119 olderr = self._ui.ferr
122 olderr = self._ui.ferr
120
123
121 out = util.stringio()
124 out = util.stringio()
122
125
123 try:
126 try:
124 self._ui.fout = out
127 self._ui.fout = out
125 self._ui.ferr = out
128 self._ui.ferr = out
126 yield out
129 yield out
127 finally:
130 finally:
128 self._ui.fout = oldout
131 self._ui.fout = oldout
129 self._ui.ferr = olderr
132 self._ui.ferr = olderr
130
133
131 def client(self):
134 def client(self):
132 return 'remote:%s:%s:%s' % (
135 return 'remote:%s:%s:%s' % (
133 self._req.urlscheme,
136 self._req.urlscheme,
134 urlreq.quote(self._req.remotehost or ''),
137 urlreq.quote(self._req.remotehost or ''),
135 urlreq.quote(self._req.remoteuser or ''))
138 urlreq.quote(self._req.remoteuser or ''))
136
139
137 def addcapabilities(self, repo, caps):
140 def addcapabilities(self, repo, caps):
138 caps.append(b'batch')
141 caps.append(b'batch')
139
142
140 caps.append('httpheader=%d' %
143 caps.append('httpheader=%d' %
141 repo.ui.configint('server', 'maxhttpheaderlen'))
144 repo.ui.configint('server', 'maxhttpheaderlen'))
142 if repo.ui.configbool('experimental', 'httppostargs'):
145 if repo.ui.configbool('experimental', 'httppostargs'):
143 caps.append('httppostargs')
146 caps.append('httppostargs')
144
147
145 # FUTURE advertise 0.2rx once support is implemented
148 # FUTURE advertise 0.2rx once support is implemented
146 # FUTURE advertise minrx and mintx after consulting config option
149 # FUTURE advertise minrx and mintx after consulting config option
147 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
150 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
148
151
149 compengines = wireproto.supportedcompengines(repo.ui, util.SERVERROLE)
152 compengines = wireproto.supportedcompengines(repo.ui, util.SERVERROLE)
150 if compengines:
153 if compengines:
151 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
154 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
152 for e in compengines)
155 for e in compengines)
153 caps.append('compression=%s' % comptypes)
156 caps.append('compression=%s' % comptypes)
154
157
155 return caps
158 return caps
156
159
157 def checkperm(self, perm):
160 def checkperm(self, perm):
158 return self._checkperm(perm)
161 return self._checkperm(perm)
159
162
160 # This method exists mostly so that extensions like remotefilelog can
163 # This method exists mostly so that extensions like remotefilelog can
161 # disable a kludgey legacy method only over http. As of early 2018,
164 # disable a kludgey legacy method only over http. As of early 2018,
162 # there are no other known users, so with any luck we can discard this
165 # there are no other known users, so with any luck we can discard this
163 # hook if remotefilelog becomes a first-party extension.
166 # hook if remotefilelog becomes a first-party extension.
164 def iscmd(cmd):
167 def iscmd(cmd):
165 return cmd in wireproto.commands
168 return cmd in wireproto.commands
166
169
167 def handlewsgirequest(rctx, req, res, checkperm):
170 def handlewsgirequest(rctx, req, res, checkperm):
168 """Possibly process a wire protocol request.
171 """Possibly process a wire protocol request.
169
172
170 If the current request is a wire protocol request, the request is
173 If the current request is a wire protocol request, the request is
171 processed by this function.
174 processed by this function.
172
175
173 ``req`` is a ``parsedrequest`` instance.
176 ``req`` is a ``parsedrequest`` instance.
174 ``res`` is a ``wsgiresponse`` instance.
177 ``res`` is a ``wsgiresponse`` instance.
175
178
176 Returns a bool indicating if the request was serviced. If set, the caller
179 Returns a bool indicating if the request was serviced. If set, the caller
177 should stop processing the request, as a response has already been issued.
180 should stop processing the request, as a response has already been issued.
178 """
181 """
179 # Avoid cycle involving hg module.
182 # Avoid cycle involving hg module.
180 from .hgweb import common as hgwebcommon
183 from .hgweb import common as hgwebcommon
181
184
182 repo = rctx.repo
185 repo = rctx.repo
183
186
184 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
187 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
185 # string parameter. If it isn't present, this isn't a wire protocol
188 # string parameter. If it isn't present, this isn't a wire protocol
186 # request.
189 # request.
187 if 'cmd' not in req.qsparams:
190 if 'cmd' not in req.qsparams:
188 return False
191 return False
189
192
190 cmd = req.qsparams['cmd']
193 cmd = req.qsparams['cmd']
191
194
192 # The "cmd" request parameter is used by both the wire protocol and hgweb.
195 # The "cmd" request parameter is used by both the wire protocol and hgweb.
193 # While not all wire protocol commands are available for all transports,
196 # While not all wire protocol commands are available for all transports,
194 # if we see a "cmd" value that resembles a known wire protocol command, we
197 # if we see a "cmd" value that resembles a known wire protocol command, we
195 # route it to a protocol handler. This is better than routing possible
198 # route it to a protocol handler. This is better than routing possible
196 # wire protocol requests to hgweb because it prevents hgweb from using
199 # wire protocol requests to hgweb because it prevents hgweb from using
197 # known wire protocol commands and it is less confusing for machine
200 # known wire protocol commands and it is less confusing for machine
198 # clients.
201 # clients.
199 if not iscmd(cmd):
202 if not iscmd(cmd):
200 return False
203 return False
201
204
202 # The "cmd" query string argument is only valid on the root path of the
205 # The "cmd" query string argument is only valid on the root path of the
203 # repo. e.g. ``/?cmd=foo``, ``/repo?cmd=foo``. URL paths within the repo
206 # repo. e.g. ``/?cmd=foo``, ``/repo?cmd=foo``. URL paths within the repo
204 # like ``/blah?cmd=foo`` are not allowed. So don't recognize the request
207 # like ``/blah?cmd=foo`` are not allowed. So don't recognize the request
205 # in this case. We send an HTTP 404 for backwards compatibility reasons.
208 # in this case. We send an HTTP 404 for backwards compatibility reasons.
206 if req.dispatchpath:
209 if req.dispatchpath:
207 res.status = hgwebcommon.statusmessage(404)
210 res.status = hgwebcommon.statusmessage(404)
208 res.headers['Content-Type'] = HGTYPE
211 res.headers['Content-Type'] = HGTYPE
209 # TODO This is not a good response to issue for this request. This
212 # TODO This is not a good response to issue for this request. This
210 # is mostly for BC for now.
213 # is mostly for BC for now.
211 res.setbodybytes('0\n%s\n' % b'Not Found')
214 res.setbodybytes('0\n%s\n' % b'Not Found')
212 return True
215 return True
213
216
214 proto = httpv1protocolhandler(req, repo.ui,
217 proto = httpv1protocolhandler(req, repo.ui,
215 lambda perm: checkperm(rctx, req, perm))
218 lambda perm: checkperm(rctx, req, perm))
216
219
217 # The permissions checker should be the only thing that can raise an
220 # The permissions checker should be the only thing that can raise an
218 # ErrorResponse. It is kind of a layer violation to catch an hgweb
221 # ErrorResponse. It is kind of a layer violation to catch an hgweb
219 # exception here. So consider refactoring into a exception type that
222 # exception here. So consider refactoring into a exception type that
220 # is associated with the wire protocol.
223 # is associated with the wire protocol.
221 try:
224 try:
222 _callhttp(repo, req, res, proto, cmd)
225 _callhttp(repo, req, res, proto, cmd)
223 except hgwebcommon.ErrorResponse as e:
226 except hgwebcommon.ErrorResponse as e:
224 for k, v in e.headers:
227 for k, v in e.headers:
225 res.headers[k] = v
228 res.headers[k] = v
226 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
229 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
227 # TODO This response body assumes the failed command was
230 # TODO This response body assumes the failed command was
228 # "unbundle." That assumption is not always valid.
231 # "unbundle." That assumption is not always valid.
229 res.setbodybytes('0\n%s\n' % pycompat.bytestr(e))
232 res.setbodybytes('0\n%s\n' % pycompat.bytestr(e))
230
233
231 return True
234 return True
232
235
236 def _availableapis(repo):
237 apis = set()
238
239 # Registered APIs are made available via config options of the name of
240 # the protocol.
241 for k, v in API_HANDLERS.items():
242 section, option = v['config']
243 if repo.ui.configbool(section, option):
244 apis.add(k)
245
246 return apis
247
233 def handlewsgiapirequest(rctx, req, res, checkperm):
248 def handlewsgiapirequest(rctx, req, res, checkperm):
234 """Handle requests to /api/*."""
249 """Handle requests to /api/*."""
235 assert req.dispatchparts[0] == b'api'
250 assert req.dispatchparts[0] == b'api'
236
251
237 repo = rctx.repo
252 repo = rctx.repo
238
253
239 # This whole URL space is experimental for now. But we want to
254 # This whole URL space is experimental for now. But we want to
240 # reserve the URL space. So, 404 all URLs if the feature isn't enabled.
255 # reserve the URL space. So, 404 all URLs if the feature isn't enabled.
241 if not repo.ui.configbool('experimental', 'web.apiserver'):
256 if not repo.ui.configbool('experimental', 'web.apiserver'):
242 res.status = b'404 Not Found'
257 res.status = b'404 Not Found'
243 res.headers[b'Content-Type'] = b'text/plain'
258 res.headers[b'Content-Type'] = b'text/plain'
244 res.setbodybytes(_('Experimental API server endpoint not enabled'))
259 res.setbodybytes(_('Experimental API server endpoint not enabled'))
245 return
260 return
246
261
247 # The URL space is /api/<protocol>/*. The structure of URLs under varies
262 # The URL space is /api/<protocol>/*. The structure of URLs under varies
248 # by <protocol>.
263 # by <protocol>.
249
264
250 # Registered APIs are made available via config options of the name of
265 availableapis = _availableapis(repo)
251 # the protocol.
252 availableapis = set()
253 for k, v in API_HANDLERS.items():
254 section, option = v['config']
255 if repo.ui.configbool(section, option):
256 availableapis.add(k)
257
266
258 # Requests to /api/ list available APIs.
267 # Requests to /api/ list available APIs.
259 if req.dispatchparts == [b'api']:
268 if req.dispatchparts == [b'api']:
260 res.status = b'200 OK'
269 res.status = b'200 OK'
261 res.headers[b'Content-Type'] = b'text/plain'
270 res.headers[b'Content-Type'] = b'text/plain'
262 lines = [_('APIs can be accessed at /api/<name>, where <name> can be '
271 lines = [_('APIs can be accessed at /api/<name>, where <name> can be '
263 'one of the following:\n')]
272 'one of the following:\n')]
264 if availableapis:
273 if availableapis:
265 lines.extend(sorted(availableapis))
274 lines.extend(sorted(availableapis))
266 else:
275 else:
267 lines.append(_('(no available APIs)\n'))
276 lines.append(_('(no available APIs)\n'))
268 res.setbodybytes(b'\n'.join(lines))
277 res.setbodybytes(b'\n'.join(lines))
269 return
278 return
270
279
271 proto = req.dispatchparts[1]
280 proto = req.dispatchparts[1]
272
281
273 if proto not in API_HANDLERS:
282 if proto not in API_HANDLERS:
274 res.status = b'404 Not Found'
283 res.status = b'404 Not Found'
275 res.headers[b'Content-Type'] = b'text/plain'
284 res.headers[b'Content-Type'] = b'text/plain'
276 res.setbodybytes(_('Unknown API: %s\nKnown APIs: %s') % (
285 res.setbodybytes(_('Unknown API: %s\nKnown APIs: %s') % (
277 proto, b', '.join(sorted(availableapis))))
286 proto, b', '.join(sorted(availableapis))))
278 return
287 return
279
288
280 if proto not in availableapis:
289 if proto not in availableapis:
281 res.status = b'404 Not Found'
290 res.status = b'404 Not Found'
282 res.headers[b'Content-Type'] = b'text/plain'
291 res.headers[b'Content-Type'] = b'text/plain'
283 res.setbodybytes(_('API %s not enabled\n') % proto)
292 res.setbodybytes(_('API %s not enabled\n') % proto)
284 return
293 return
285
294
286 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm,
295 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm,
287 req.dispatchparts[2:])
296 req.dispatchparts[2:])
288
297
289 # Maps API name to metadata so custom API can be registered.
298 # Maps API name to metadata so custom API can be registered.
299 # Keys are:
300 #
301 # config
302 # Config option that controls whether service is enabled.
303 # handler
304 # Callable receiving (rctx, req, res, checkperm, urlparts) that is called
305 # when a request to this API is received.
306 # apidescriptor
307 # Callable receiving (req, repo) that is called to obtain an API
308 # descriptor for this service. The response must be serializable to CBOR.
290 API_HANDLERS = {
309 API_HANDLERS = {
291 wireprotov2server.HTTPV2: {
310 wireprotov2server.HTTPV2: {
292 'config': ('experimental', 'web.api.http-v2'),
311 'config': ('experimental', 'web.api.http-v2'),
293 'handler': wireprotov2server.handlehttpv2request,
312 'handler': wireprotov2server.handlehttpv2request,
313 'apidescriptor': wireprotov2server.httpv2apidescriptor,
294 },
314 },
295 }
315 }
296
316
297 def _httpresponsetype(ui, proto, prefer_uncompressed):
317 def _httpresponsetype(ui, proto, prefer_uncompressed):
298 """Determine the appropriate response type and compression settings.
318 """Determine the appropriate response type and compression settings.
299
319
300 Returns a tuple of (mediatype, compengine, engineopts).
320 Returns a tuple of (mediatype, compengine, engineopts).
301 """
321 """
302 # Determine the response media type and compression engine based
322 # Determine the response media type and compression engine based
303 # on the request parameters.
323 # on the request parameters.
304
324
305 if '0.2' in proto.getprotocaps():
325 if '0.2' in proto.getprotocaps():
306 # All clients are expected to support uncompressed data.
326 # All clients are expected to support uncompressed data.
307 if prefer_uncompressed:
327 if prefer_uncompressed:
308 return HGTYPE2, util._noopengine(), {}
328 return HGTYPE2, util._noopengine(), {}
309
329
310 # Now find an agreed upon compression format.
330 # Now find an agreed upon compression format.
311 compformats = wireproto.clientcompressionsupport(proto)
331 compformats = wireproto.clientcompressionsupport(proto)
312 for engine in wireproto.supportedcompengines(ui, util.SERVERROLE):
332 for engine in wireproto.supportedcompengines(ui, util.SERVERROLE):
313 if engine.wireprotosupport().name in compformats:
333 if engine.wireprotosupport().name in compformats:
314 opts = {}
334 opts = {}
315 level = ui.configint('server', '%slevel' % engine.name())
335 level = ui.configint('server', '%slevel' % engine.name())
316 if level is not None:
336 if level is not None:
317 opts['level'] = level
337 opts['level'] = level
318
338
319 return HGTYPE2, engine, opts
339 return HGTYPE2, engine, opts
320
340
321 # No mutually supported compression format. Fall back to the
341 # No mutually supported compression format. Fall back to the
322 # legacy protocol.
342 # legacy protocol.
323
343
324 # Don't allow untrusted settings because disabling compression or
344 # Don't allow untrusted settings because disabling compression or
325 # setting a very high compression level could lead to flooding
345 # setting a very high compression level could lead to flooding
326 # the server's network or CPU.
346 # the server's network or CPU.
327 opts = {'level': ui.configint('server', 'zliblevel')}
347 opts = {'level': ui.configint('server', 'zliblevel')}
328 return HGTYPE, util.compengines['zlib'], opts
348 return HGTYPE, util.compengines['zlib'], opts
329
349
350 def processcapabilitieshandshake(repo, req, res, proto):
351 """Called during a ?cmd=capabilities request.
352
353 If the client is advertising support for a newer protocol, we send
354 a CBOR response with information about available services. If no
355 advertised services are available, we don't handle the request.
356 """
357 # Fall back to old behavior unless the API server is enabled.
358 if not repo.ui.configbool('experimental', 'web.apiserver'):
359 return False
360
361 clientapis = decodevaluefromheaders(req, b'X-HgUpgrade')
362 protocaps = decodevaluefromheaders(req, b'X-HgProto')
363 if not clientapis or not protocaps:
364 return False
365
366 # We currently only support CBOR responses.
367 protocaps = set(protocaps.split(' '))
368 if b'cbor' not in protocaps:
369 return False
370
371 descriptors = {}
372
373 for api in sorted(set(clientapis.split()) & _availableapis(repo)):
374 handler = API_HANDLERS[api]
375
376 descriptorfn = handler.get('apidescriptor')
377 if not descriptorfn:
378 continue
379
380 descriptors[api] = descriptorfn(req, repo)
381
382 v1caps = wireproto.dispatch(repo, proto, 'capabilities')
383 assert isinstance(v1caps, wireprototypes.bytesresponse)
384
385 m = {
386 # TODO allow this to be configurable.
387 'apibase': 'api/',
388 'apis': descriptors,
389 'v1capabilities': v1caps.data,
390 }
391
392 res.status = b'200 OK'
393 res.headers[b'Content-Type'] = b'application/mercurial-cbor'
394 res.setbodybytes(cbor.dumps(m, canonical=True))
395
396 return True
397
330 def _callhttp(repo, req, res, proto, cmd):
398 def _callhttp(repo, req, res, proto, cmd):
331 # Avoid cycle involving hg module.
399 # Avoid cycle involving hg module.
332 from .hgweb import common as hgwebcommon
400 from .hgweb import common as hgwebcommon
333
401
334 def genversion2(gen, engine, engineopts):
402 def genversion2(gen, engine, engineopts):
335 # application/mercurial-0.2 always sends a payload header
403 # application/mercurial-0.2 always sends a payload header
336 # identifying the compression engine.
404 # identifying the compression engine.
337 name = engine.wireprotosupport().name
405 name = engine.wireprotosupport().name
338 assert 0 < len(name) < 256
406 assert 0 < len(name) < 256
339 yield struct.pack('B', len(name))
407 yield struct.pack('B', len(name))
340 yield name
408 yield name
341
409
342 for chunk in gen:
410 for chunk in gen:
343 yield chunk
411 yield chunk
344
412
345 def setresponse(code, contenttype, bodybytes=None, bodygen=None):
413 def setresponse(code, contenttype, bodybytes=None, bodygen=None):
346 if code == HTTP_OK:
414 if code == HTTP_OK:
347 res.status = '200 Script output follows'
415 res.status = '200 Script output follows'
348 else:
416 else:
349 res.status = hgwebcommon.statusmessage(code)
417 res.status = hgwebcommon.statusmessage(code)
350
418
351 res.headers['Content-Type'] = contenttype
419 res.headers['Content-Type'] = contenttype
352
420
353 if bodybytes is not None:
421 if bodybytes is not None:
354 res.setbodybytes(bodybytes)
422 res.setbodybytes(bodybytes)
355 if bodygen is not None:
423 if bodygen is not None:
356 res.setbodygen(bodygen)
424 res.setbodygen(bodygen)
357
425
358 if not wireproto.commands.commandavailable(cmd, proto):
426 if not wireproto.commands.commandavailable(cmd, proto):
359 setresponse(HTTP_OK, HGERRTYPE,
427 setresponse(HTTP_OK, HGERRTYPE,
360 _('requested wire protocol command is not available over '
428 _('requested wire protocol command is not available over '
361 'HTTP'))
429 'HTTP'))
362 return
430 return
363
431
364 proto.checkperm(wireproto.commands[cmd].permission)
432 proto.checkperm(wireproto.commands[cmd].permission)
365
433
434 # Possibly handle a modern client wanting to switch protocols.
435 if (cmd == 'capabilities' and
436 processcapabilitieshandshake(repo, req, res, proto)):
437
438 return
439
366 rsp = wireproto.dispatch(repo, proto, cmd)
440 rsp = wireproto.dispatch(repo, proto, cmd)
367
441
368 if isinstance(rsp, bytes):
442 if isinstance(rsp, bytes):
369 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
443 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
370 elif isinstance(rsp, wireprototypes.bytesresponse):
444 elif isinstance(rsp, wireprototypes.bytesresponse):
371 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp.data)
445 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp.data)
372 elif isinstance(rsp, wireprototypes.streamreslegacy):
446 elif isinstance(rsp, wireprototypes.streamreslegacy):
373 setresponse(HTTP_OK, HGTYPE, bodygen=rsp.gen)
447 setresponse(HTTP_OK, HGTYPE, bodygen=rsp.gen)
374 elif isinstance(rsp, wireprototypes.streamres):
448 elif isinstance(rsp, wireprototypes.streamres):
375 gen = rsp.gen
449 gen = rsp.gen
376
450
377 # This code for compression should not be streamres specific. It
451 # This code for compression should not be streamres specific. It
378 # is here because we only compress streamres at the moment.
452 # is here because we only compress streamres at the moment.
379 mediatype, engine, engineopts = _httpresponsetype(
453 mediatype, engine, engineopts = _httpresponsetype(
380 repo.ui, proto, rsp.prefer_uncompressed)
454 repo.ui, proto, rsp.prefer_uncompressed)
381 gen = engine.compressstream(gen, engineopts)
455 gen = engine.compressstream(gen, engineopts)
382
456
383 if mediatype == HGTYPE2:
457 if mediatype == HGTYPE2:
384 gen = genversion2(gen, engine, engineopts)
458 gen = genversion2(gen, engine, engineopts)
385
459
386 setresponse(HTTP_OK, mediatype, bodygen=gen)
460 setresponse(HTTP_OK, mediatype, bodygen=gen)
387 elif isinstance(rsp, wireprototypes.pushres):
461 elif isinstance(rsp, wireprototypes.pushres):
388 rsp = '%d\n%s' % (rsp.res, rsp.output)
462 rsp = '%d\n%s' % (rsp.res, rsp.output)
389 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
463 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
390 elif isinstance(rsp, wireprototypes.pusherr):
464 elif isinstance(rsp, wireprototypes.pusherr):
391 rsp = '0\n%s\n' % rsp.res
465 rsp = '0\n%s\n' % rsp.res
392 res.drain = True
466 res.drain = True
393 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
467 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
394 elif isinstance(rsp, wireprototypes.ooberror):
468 elif isinstance(rsp, wireprototypes.ooberror):
395 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message)
469 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message)
396 else:
470 else:
397 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
471 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
398
472
399 def _sshv1respondbytes(fout, value):
473 def _sshv1respondbytes(fout, value):
400 """Send a bytes response for protocol version 1."""
474 """Send a bytes response for protocol version 1."""
401 fout.write('%d\n' % len(value))
475 fout.write('%d\n' % len(value))
402 fout.write(value)
476 fout.write(value)
403 fout.flush()
477 fout.flush()
404
478
405 def _sshv1respondstream(fout, source):
479 def _sshv1respondstream(fout, source):
406 write = fout.write
480 write = fout.write
407 for chunk in source.gen:
481 for chunk in source.gen:
408 write(chunk)
482 write(chunk)
409 fout.flush()
483 fout.flush()
410
484
411 def _sshv1respondooberror(fout, ferr, rsp):
485 def _sshv1respondooberror(fout, ferr, rsp):
412 ferr.write(b'%s\n-\n' % rsp)
486 ferr.write(b'%s\n-\n' % rsp)
413 ferr.flush()
487 ferr.flush()
414 fout.write(b'\n')
488 fout.write(b'\n')
415 fout.flush()
489 fout.flush()
416
490
417 @zi.implementer(wireprototypes.baseprotocolhandler)
491 @zi.implementer(wireprototypes.baseprotocolhandler)
418 class sshv1protocolhandler(object):
492 class sshv1protocolhandler(object):
419 """Handler for requests services via version 1 of SSH protocol."""
493 """Handler for requests services via version 1 of SSH protocol."""
420 def __init__(self, ui, fin, fout):
494 def __init__(self, ui, fin, fout):
421 self._ui = ui
495 self._ui = ui
422 self._fin = fin
496 self._fin = fin
423 self._fout = fout
497 self._fout = fout
424 self._protocaps = set()
498 self._protocaps = set()
425
499
426 @property
500 @property
427 def name(self):
501 def name(self):
428 return wireprototypes.SSHV1
502 return wireprototypes.SSHV1
429
503
430 def getargs(self, args):
504 def getargs(self, args):
431 data = {}
505 data = {}
432 keys = args.split()
506 keys = args.split()
433 for n in xrange(len(keys)):
507 for n in xrange(len(keys)):
434 argline = self._fin.readline()[:-1]
508 argline = self._fin.readline()[:-1]
435 arg, l = argline.split()
509 arg, l = argline.split()
436 if arg not in keys:
510 if arg not in keys:
437 raise error.Abort(_("unexpected parameter %r") % arg)
511 raise error.Abort(_("unexpected parameter %r") % arg)
438 if arg == '*':
512 if arg == '*':
439 star = {}
513 star = {}
440 for k in xrange(int(l)):
514 for k in xrange(int(l)):
441 argline = self._fin.readline()[:-1]
515 argline = self._fin.readline()[:-1]
442 arg, l = argline.split()
516 arg, l = argline.split()
443 val = self._fin.read(int(l))
517 val = self._fin.read(int(l))
444 star[arg] = val
518 star[arg] = val
445 data['*'] = star
519 data['*'] = star
446 else:
520 else:
447 val = self._fin.read(int(l))
521 val = self._fin.read(int(l))
448 data[arg] = val
522 data[arg] = val
449 return [data[k] for k in keys]
523 return [data[k] for k in keys]
450
524
451 def getprotocaps(self):
525 def getprotocaps(self):
452 return self._protocaps
526 return self._protocaps
453
527
454 def getpayload(self):
528 def getpayload(self):
455 # We initially send an empty response. This tells the client it is
529 # We initially send an empty response. This tells the client it is
456 # OK to start sending data. If a client sees any other response, it
530 # OK to start sending data. If a client sees any other response, it
457 # interprets it as an error.
531 # interprets it as an error.
458 _sshv1respondbytes(self._fout, b'')
532 _sshv1respondbytes(self._fout, b'')
459
533
460 # The file is in the form:
534 # The file is in the form:
461 #
535 #
462 # <chunk size>\n<chunk>
536 # <chunk size>\n<chunk>
463 # ...
537 # ...
464 # 0\n
538 # 0\n
465 count = int(self._fin.readline())
539 count = int(self._fin.readline())
466 while count:
540 while count:
467 yield self._fin.read(count)
541 yield self._fin.read(count)
468 count = int(self._fin.readline())
542 count = int(self._fin.readline())
469
543
470 @contextlib.contextmanager
544 @contextlib.contextmanager
471 def mayberedirectstdio(self):
545 def mayberedirectstdio(self):
472 yield None
546 yield None
473
547
474 def client(self):
548 def client(self):
475 client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
549 client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
476 return 'remote:ssh:' + client
550 return 'remote:ssh:' + client
477
551
478 def addcapabilities(self, repo, caps):
552 def addcapabilities(self, repo, caps):
479 if self.name == wireprototypes.SSHV1:
553 if self.name == wireprototypes.SSHV1:
480 caps.append(b'protocaps')
554 caps.append(b'protocaps')
481 caps.append(b'batch')
555 caps.append(b'batch')
482 return caps
556 return caps
483
557
484 def checkperm(self, perm):
558 def checkperm(self, perm):
485 pass
559 pass
486
560
487 class sshv2protocolhandler(sshv1protocolhandler):
561 class sshv2protocolhandler(sshv1protocolhandler):
488 """Protocol handler for version 2 of the SSH protocol."""
562 """Protocol handler for version 2 of the SSH protocol."""
489
563
490 @property
564 @property
491 def name(self):
565 def name(self):
492 return wireprototypes.SSHV2
566 return wireprototypes.SSHV2
493
567
494 def addcapabilities(self, repo, caps):
568 def addcapabilities(self, repo, caps):
495 return caps
569 return caps
496
570
497 def _runsshserver(ui, repo, fin, fout, ev):
571 def _runsshserver(ui, repo, fin, fout, ev):
498 # This function operates like a state machine of sorts. The following
572 # This function operates like a state machine of sorts. The following
499 # states are defined:
573 # states are defined:
500 #
574 #
501 # protov1-serving
575 # protov1-serving
502 # Server is in protocol version 1 serving mode. Commands arrive on
576 # Server is in protocol version 1 serving mode. Commands arrive on
503 # new lines. These commands are processed in this state, one command
577 # new lines. These commands are processed in this state, one command
504 # after the other.
578 # after the other.
505 #
579 #
506 # protov2-serving
580 # protov2-serving
507 # Server is in protocol version 2 serving mode.
581 # Server is in protocol version 2 serving mode.
508 #
582 #
509 # upgrade-initial
583 # upgrade-initial
510 # The server is going to process an upgrade request.
584 # The server is going to process an upgrade request.
511 #
585 #
512 # upgrade-v2-filter-legacy-handshake
586 # upgrade-v2-filter-legacy-handshake
513 # The protocol is being upgraded to version 2. The server is expecting
587 # The protocol is being upgraded to version 2. The server is expecting
514 # the legacy handshake from version 1.
588 # the legacy handshake from version 1.
515 #
589 #
516 # upgrade-v2-finish
590 # upgrade-v2-finish
517 # The upgrade to version 2 of the protocol is imminent.
591 # The upgrade to version 2 of the protocol is imminent.
518 #
592 #
519 # shutdown
593 # shutdown
520 # The server is shutting down, possibly in reaction to a client event.
594 # The server is shutting down, possibly in reaction to a client event.
521 #
595 #
522 # And here are their transitions:
596 # And here are their transitions:
523 #
597 #
524 # protov1-serving -> shutdown
598 # protov1-serving -> shutdown
525 # When server receives an empty request or encounters another
599 # When server receives an empty request or encounters another
526 # error.
600 # error.
527 #
601 #
528 # protov1-serving -> upgrade-initial
602 # protov1-serving -> upgrade-initial
529 # An upgrade request line was seen.
603 # An upgrade request line was seen.
530 #
604 #
531 # upgrade-initial -> upgrade-v2-filter-legacy-handshake
605 # upgrade-initial -> upgrade-v2-filter-legacy-handshake
532 # Upgrade to version 2 in progress. Server is expecting to
606 # Upgrade to version 2 in progress. Server is expecting to
533 # process a legacy handshake.
607 # process a legacy handshake.
534 #
608 #
535 # upgrade-v2-filter-legacy-handshake -> shutdown
609 # upgrade-v2-filter-legacy-handshake -> shutdown
536 # Client did not fulfill upgrade handshake requirements.
610 # Client did not fulfill upgrade handshake requirements.
537 #
611 #
538 # upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish
612 # upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish
539 # Client fulfilled version 2 upgrade requirements. Finishing that
613 # Client fulfilled version 2 upgrade requirements. Finishing that
540 # upgrade.
614 # upgrade.
541 #
615 #
542 # upgrade-v2-finish -> protov2-serving
616 # upgrade-v2-finish -> protov2-serving
543 # Protocol upgrade to version 2 complete. Server can now speak protocol
617 # Protocol upgrade to version 2 complete. Server can now speak protocol
544 # version 2.
618 # version 2.
545 #
619 #
546 # protov2-serving -> protov1-serving
620 # protov2-serving -> protov1-serving
547 # Ths happens by default since protocol version 2 is the same as
621 # Ths happens by default since protocol version 2 is the same as
548 # version 1 except for the handshake.
622 # version 1 except for the handshake.
549
623
550 state = 'protov1-serving'
624 state = 'protov1-serving'
551 proto = sshv1protocolhandler(ui, fin, fout)
625 proto = sshv1protocolhandler(ui, fin, fout)
552 protoswitched = False
626 protoswitched = False
553
627
554 while not ev.is_set():
628 while not ev.is_set():
555 if state == 'protov1-serving':
629 if state == 'protov1-serving':
556 # Commands are issued on new lines.
630 # Commands are issued on new lines.
557 request = fin.readline()[:-1]
631 request = fin.readline()[:-1]
558
632
559 # Empty lines signal to terminate the connection.
633 # Empty lines signal to terminate the connection.
560 if not request:
634 if not request:
561 state = 'shutdown'
635 state = 'shutdown'
562 continue
636 continue
563
637
564 # It looks like a protocol upgrade request. Transition state to
638 # It looks like a protocol upgrade request. Transition state to
565 # handle it.
639 # handle it.
566 if request.startswith(b'upgrade '):
640 if request.startswith(b'upgrade '):
567 if protoswitched:
641 if protoswitched:
568 _sshv1respondooberror(fout, ui.ferr,
642 _sshv1respondooberror(fout, ui.ferr,
569 b'cannot upgrade protocols multiple '
643 b'cannot upgrade protocols multiple '
570 b'times')
644 b'times')
571 state = 'shutdown'
645 state = 'shutdown'
572 continue
646 continue
573
647
574 state = 'upgrade-initial'
648 state = 'upgrade-initial'
575 continue
649 continue
576
650
577 available = wireproto.commands.commandavailable(request, proto)
651 available = wireproto.commands.commandavailable(request, proto)
578
652
579 # This command isn't available. Send an empty response and go
653 # This command isn't available. Send an empty response and go
580 # back to waiting for a new command.
654 # back to waiting for a new command.
581 if not available:
655 if not available:
582 _sshv1respondbytes(fout, b'')
656 _sshv1respondbytes(fout, b'')
583 continue
657 continue
584
658
585 rsp = wireproto.dispatch(repo, proto, request)
659 rsp = wireproto.dispatch(repo, proto, request)
586
660
587 if isinstance(rsp, bytes):
661 if isinstance(rsp, bytes):
588 _sshv1respondbytes(fout, rsp)
662 _sshv1respondbytes(fout, rsp)
589 elif isinstance(rsp, wireprototypes.bytesresponse):
663 elif isinstance(rsp, wireprototypes.bytesresponse):
590 _sshv1respondbytes(fout, rsp.data)
664 _sshv1respondbytes(fout, rsp.data)
591 elif isinstance(rsp, wireprototypes.streamres):
665 elif isinstance(rsp, wireprototypes.streamres):
592 _sshv1respondstream(fout, rsp)
666 _sshv1respondstream(fout, rsp)
593 elif isinstance(rsp, wireprototypes.streamreslegacy):
667 elif isinstance(rsp, wireprototypes.streamreslegacy):
594 _sshv1respondstream(fout, rsp)
668 _sshv1respondstream(fout, rsp)
595 elif isinstance(rsp, wireprototypes.pushres):
669 elif isinstance(rsp, wireprototypes.pushres):
596 _sshv1respondbytes(fout, b'')
670 _sshv1respondbytes(fout, b'')
597 _sshv1respondbytes(fout, b'%d' % rsp.res)
671 _sshv1respondbytes(fout, b'%d' % rsp.res)
598 elif isinstance(rsp, wireprototypes.pusherr):
672 elif isinstance(rsp, wireprototypes.pusherr):
599 _sshv1respondbytes(fout, rsp.res)
673 _sshv1respondbytes(fout, rsp.res)
600 elif isinstance(rsp, wireprototypes.ooberror):
674 elif isinstance(rsp, wireprototypes.ooberror):
601 _sshv1respondooberror(fout, ui.ferr, rsp.message)
675 _sshv1respondooberror(fout, ui.ferr, rsp.message)
602 else:
676 else:
603 raise error.ProgrammingError('unhandled response type from '
677 raise error.ProgrammingError('unhandled response type from '
604 'wire protocol command: %s' % rsp)
678 'wire protocol command: %s' % rsp)
605
679
606 # For now, protocol version 2 serving just goes back to version 1.
680 # For now, protocol version 2 serving just goes back to version 1.
607 elif state == 'protov2-serving':
681 elif state == 'protov2-serving':
608 state = 'protov1-serving'
682 state = 'protov1-serving'
609 continue
683 continue
610
684
611 elif state == 'upgrade-initial':
685 elif state == 'upgrade-initial':
612 # We should never transition into this state if we've switched
686 # We should never transition into this state if we've switched
613 # protocols.
687 # protocols.
614 assert not protoswitched
688 assert not protoswitched
615 assert proto.name == wireprototypes.SSHV1
689 assert proto.name == wireprototypes.SSHV1
616
690
617 # Expected: upgrade <token> <capabilities>
691 # Expected: upgrade <token> <capabilities>
618 # If we get something else, the request is malformed. It could be
692 # If we get something else, the request is malformed. It could be
619 # from a future client that has altered the upgrade line content.
693 # from a future client that has altered the upgrade line content.
620 # We treat this as an unknown command.
694 # We treat this as an unknown command.
621 try:
695 try:
622 token, caps = request.split(b' ')[1:]
696 token, caps = request.split(b' ')[1:]
623 except ValueError:
697 except ValueError:
624 _sshv1respondbytes(fout, b'')
698 _sshv1respondbytes(fout, b'')
625 state = 'protov1-serving'
699 state = 'protov1-serving'
626 continue
700 continue
627
701
628 # Send empty response if we don't support upgrading protocols.
702 # Send empty response if we don't support upgrading protocols.
629 if not ui.configbool('experimental', 'sshserver.support-v2'):
703 if not ui.configbool('experimental', 'sshserver.support-v2'):
630 _sshv1respondbytes(fout, b'')
704 _sshv1respondbytes(fout, b'')
631 state = 'protov1-serving'
705 state = 'protov1-serving'
632 continue
706 continue
633
707
634 try:
708 try:
635 caps = urlreq.parseqs(caps)
709 caps = urlreq.parseqs(caps)
636 except ValueError:
710 except ValueError:
637 _sshv1respondbytes(fout, b'')
711 _sshv1respondbytes(fout, b'')
638 state = 'protov1-serving'
712 state = 'protov1-serving'
639 continue
713 continue
640
714
641 # We don't see an upgrade request to protocol version 2. Ignore
715 # We don't see an upgrade request to protocol version 2. Ignore
642 # the upgrade request.
716 # the upgrade request.
643 wantedprotos = caps.get(b'proto', [b''])[0]
717 wantedprotos = caps.get(b'proto', [b''])[0]
644 if SSHV2 not in wantedprotos:
718 if SSHV2 not in wantedprotos:
645 _sshv1respondbytes(fout, b'')
719 _sshv1respondbytes(fout, b'')
646 state = 'protov1-serving'
720 state = 'protov1-serving'
647 continue
721 continue
648
722
649 # It looks like we can honor this upgrade request to protocol 2.
723 # It looks like we can honor this upgrade request to protocol 2.
650 # Filter the rest of the handshake protocol request lines.
724 # Filter the rest of the handshake protocol request lines.
651 state = 'upgrade-v2-filter-legacy-handshake'
725 state = 'upgrade-v2-filter-legacy-handshake'
652 continue
726 continue
653
727
654 elif state == 'upgrade-v2-filter-legacy-handshake':
728 elif state == 'upgrade-v2-filter-legacy-handshake':
655 # Client should have sent legacy handshake after an ``upgrade``
729 # Client should have sent legacy handshake after an ``upgrade``
656 # request. Expected lines:
730 # request. Expected lines:
657 #
731 #
658 # hello
732 # hello
659 # between
733 # between
660 # pairs 81
734 # pairs 81
661 # 0000...-0000...
735 # 0000...-0000...
662
736
663 ok = True
737 ok = True
664 for line in (b'hello', b'between', b'pairs 81'):
738 for line in (b'hello', b'between', b'pairs 81'):
665 request = fin.readline()[:-1]
739 request = fin.readline()[:-1]
666
740
667 if request != line:
741 if request != line:
668 _sshv1respondooberror(fout, ui.ferr,
742 _sshv1respondooberror(fout, ui.ferr,
669 b'malformed handshake protocol: '
743 b'malformed handshake protocol: '
670 b'missing %s' % line)
744 b'missing %s' % line)
671 ok = False
745 ok = False
672 state = 'shutdown'
746 state = 'shutdown'
673 break
747 break
674
748
675 if not ok:
749 if not ok:
676 continue
750 continue
677
751
678 request = fin.read(81)
752 request = fin.read(81)
679 if request != b'%s-%s' % (b'0' * 40, b'0' * 40):
753 if request != b'%s-%s' % (b'0' * 40, b'0' * 40):
680 _sshv1respondooberror(fout, ui.ferr,
754 _sshv1respondooberror(fout, ui.ferr,
681 b'malformed handshake protocol: '
755 b'malformed handshake protocol: '
682 b'missing between argument value')
756 b'missing between argument value')
683 state = 'shutdown'
757 state = 'shutdown'
684 continue
758 continue
685
759
686 state = 'upgrade-v2-finish'
760 state = 'upgrade-v2-finish'
687 continue
761 continue
688
762
689 elif state == 'upgrade-v2-finish':
763 elif state == 'upgrade-v2-finish':
690 # Send the upgrade response.
764 # Send the upgrade response.
691 fout.write(b'upgraded %s %s\n' % (token, SSHV2))
765 fout.write(b'upgraded %s %s\n' % (token, SSHV2))
692 servercaps = wireproto.capabilities(repo, proto)
766 servercaps = wireproto.capabilities(repo, proto)
693 rsp = b'capabilities: %s' % servercaps.data
767 rsp = b'capabilities: %s' % servercaps.data
694 fout.write(b'%d\n%s\n' % (len(rsp), rsp))
768 fout.write(b'%d\n%s\n' % (len(rsp), rsp))
695 fout.flush()
769 fout.flush()
696
770
697 proto = sshv2protocolhandler(ui, fin, fout)
771 proto = sshv2protocolhandler(ui, fin, fout)
698 protoswitched = True
772 protoswitched = True
699
773
700 state = 'protov2-serving'
774 state = 'protov2-serving'
701 continue
775 continue
702
776
703 elif state == 'shutdown':
777 elif state == 'shutdown':
704 break
778 break
705
779
706 else:
780 else:
707 raise error.ProgrammingError('unhandled ssh server state: %s' %
781 raise error.ProgrammingError('unhandled ssh server state: %s' %
708 state)
782 state)
709
783
710 class sshserver(object):
784 class sshserver(object):
711 def __init__(self, ui, repo, logfh=None):
785 def __init__(self, ui, repo, logfh=None):
712 self._ui = ui
786 self._ui = ui
713 self._repo = repo
787 self._repo = repo
714 self._fin = ui.fin
788 self._fin = ui.fin
715 self._fout = ui.fout
789 self._fout = ui.fout
716
790
717 # Log write I/O to stdout and stderr if configured.
791 # Log write I/O to stdout and stderr if configured.
718 if logfh:
792 if logfh:
719 self._fout = util.makeloggingfileobject(
793 self._fout = util.makeloggingfileobject(
720 logfh, self._fout, 'o', logdata=True)
794 logfh, self._fout, 'o', logdata=True)
721 ui.ferr = util.makeloggingfileobject(
795 ui.ferr = util.makeloggingfileobject(
722 logfh, ui.ferr, 'e', logdata=True)
796 logfh, ui.ferr, 'e', logdata=True)
723
797
724 hook.redirect(True)
798 hook.redirect(True)
725 ui.fout = repo.ui.fout = ui.ferr
799 ui.fout = repo.ui.fout = ui.ferr
726
800
727 # Prevent insertion/deletion of CRs
801 # Prevent insertion/deletion of CRs
728 procutil.setbinary(self._fin)
802 procutil.setbinary(self._fin)
729 procutil.setbinary(self._fout)
803 procutil.setbinary(self._fout)
730
804
731 def serve_forever(self):
805 def serve_forever(self):
732 self.serveuntil(threading.Event())
806 self.serveuntil(threading.Event())
733 sys.exit(0)
807 sys.exit(0)
734
808
735 def serveuntil(self, ev):
809 def serveuntil(self, ev):
736 """Serve until a threading.Event is set."""
810 """Serve until a threading.Event is set."""
737 _runsshserver(self._ui, self._repo, self._fin, self._fout, ev)
811 _runsshserver(self._ui, self._repo, self._fin, self._fout, ev)
@@ -1,474 +1,479 b''
1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 #
3 #
4 # This software may be used and distributed according to the terms of the
4 # This software may be used and distributed according to the terms of the
5 # GNU General Public License version 2 or any later version.
5 # GNU General Public License version 2 or any later version.
6
6
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import contextlib
9 import contextlib
10
10
11 from .i18n import _
11 from .i18n import _
12 from .thirdparty import (
12 from .thirdparty import (
13 cbor,
13 cbor,
14 )
14 )
15 from .thirdparty.zope import (
15 from .thirdparty.zope import (
16 interface as zi,
16 interface as zi,
17 )
17 )
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 error,
21 pycompat,
21 pycompat,
22 util,
22 util,
23 wireproto,
23 wireproto,
24 wireprotoframing,
24 wireprotoframing,
25 wireprototypes,
25 wireprototypes,
26 )
26 )
27
27
28 FRAMINGTYPE = b'application/mercurial-exp-framing-0003'
28 FRAMINGTYPE = b'application/mercurial-exp-framing-0003'
29
29
30 HTTPV2 = wireprototypes.HTTPV2
30 HTTPV2 = wireprototypes.HTTPV2
31
31
32 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
32 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
33 from .hgweb import common as hgwebcommon
33 from .hgweb import common as hgwebcommon
34
34
35 # URL space looks like: <permissions>/<command>, where <permission> can
35 # URL space looks like: <permissions>/<command>, where <permission> can
36 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
36 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
37
37
38 # Root URL does nothing meaningful... yet.
38 # Root URL does nothing meaningful... yet.
39 if not urlparts:
39 if not urlparts:
40 res.status = b'200 OK'
40 res.status = b'200 OK'
41 res.headers[b'Content-Type'] = b'text/plain'
41 res.headers[b'Content-Type'] = b'text/plain'
42 res.setbodybytes(_('HTTP version 2 API handler'))
42 res.setbodybytes(_('HTTP version 2 API handler'))
43 return
43 return
44
44
45 if len(urlparts) == 1:
45 if len(urlparts) == 1:
46 res.status = b'404 Not Found'
46 res.status = b'404 Not Found'
47 res.headers[b'Content-Type'] = b'text/plain'
47 res.headers[b'Content-Type'] = b'text/plain'
48 res.setbodybytes(_('do not know how to process %s\n') %
48 res.setbodybytes(_('do not know how to process %s\n') %
49 req.dispatchpath)
49 req.dispatchpath)
50 return
50 return
51
51
52 permission, command = urlparts[0:2]
52 permission, command = urlparts[0:2]
53
53
54 if permission not in (b'ro', b'rw'):
54 if permission not in (b'ro', b'rw'):
55 res.status = b'404 Not Found'
55 res.status = b'404 Not Found'
56 res.headers[b'Content-Type'] = b'text/plain'
56 res.headers[b'Content-Type'] = b'text/plain'
57 res.setbodybytes(_('unknown permission: %s') % permission)
57 res.setbodybytes(_('unknown permission: %s') % permission)
58 return
58 return
59
59
60 if req.method != 'POST':
60 if req.method != 'POST':
61 res.status = b'405 Method Not Allowed'
61 res.status = b'405 Method Not Allowed'
62 res.headers[b'Allow'] = b'POST'
62 res.headers[b'Allow'] = b'POST'
63 res.setbodybytes(_('commands require POST requests'))
63 res.setbodybytes(_('commands require POST requests'))
64 return
64 return
65
65
66 # At some point we'll want to use our own API instead of recycling the
66 # At some point we'll want to use our own API instead of recycling the
67 # behavior of version 1 of the wire protocol...
67 # behavior of version 1 of the wire protocol...
68 # TODO return reasonable responses - not responses that overload the
68 # TODO return reasonable responses - not responses that overload the
69 # HTTP status line message for error reporting.
69 # HTTP status line message for error reporting.
70 try:
70 try:
71 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
71 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
72 except hgwebcommon.ErrorResponse as e:
72 except hgwebcommon.ErrorResponse as e:
73 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
73 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
74 for k, v in e.headers:
74 for k, v in e.headers:
75 res.headers[k] = v
75 res.headers[k] = v
76 res.setbodybytes('permission denied')
76 res.setbodybytes('permission denied')
77 return
77 return
78
78
79 # We have a special endpoint to reflect the request back at the client.
79 # We have a special endpoint to reflect the request back at the client.
80 if command == b'debugreflect':
80 if command == b'debugreflect':
81 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
81 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
82 return
82 return
83
83
84 # Extra commands that we handle that aren't really wire protocol
84 # Extra commands that we handle that aren't really wire protocol
85 # commands. Think extra hard before making this hackery available to
85 # commands. Think extra hard before making this hackery available to
86 # extension.
86 # extension.
87 extracommands = {'multirequest'}
87 extracommands = {'multirequest'}
88
88
89 if command not in wireproto.commandsv2 and command not in extracommands:
89 if command not in wireproto.commandsv2 and command not in extracommands:
90 res.status = b'404 Not Found'
90 res.status = b'404 Not Found'
91 res.headers[b'Content-Type'] = b'text/plain'
91 res.headers[b'Content-Type'] = b'text/plain'
92 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
92 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
93 return
93 return
94
94
95 repo = rctx.repo
95 repo = rctx.repo
96 ui = repo.ui
96 ui = repo.ui
97
97
98 proto = httpv2protocolhandler(req, ui)
98 proto = httpv2protocolhandler(req, ui)
99
99
100 if (not wireproto.commandsv2.commandavailable(command, proto)
100 if (not wireproto.commandsv2.commandavailable(command, proto)
101 and command not in extracommands):
101 and command not in extracommands):
102 res.status = b'404 Not Found'
102 res.status = b'404 Not Found'
103 res.headers[b'Content-Type'] = b'text/plain'
103 res.headers[b'Content-Type'] = b'text/plain'
104 res.setbodybytes(_('invalid wire protocol command: %s') % command)
104 res.setbodybytes(_('invalid wire protocol command: %s') % command)
105 return
105 return
106
106
107 # TODO consider cases where proxies may add additional Accept headers.
107 # TODO consider cases where proxies may add additional Accept headers.
108 if req.headers.get(b'Accept') != FRAMINGTYPE:
108 if req.headers.get(b'Accept') != FRAMINGTYPE:
109 res.status = b'406 Not Acceptable'
109 res.status = b'406 Not Acceptable'
110 res.headers[b'Content-Type'] = b'text/plain'
110 res.headers[b'Content-Type'] = b'text/plain'
111 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
111 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
112 % FRAMINGTYPE)
112 % FRAMINGTYPE)
113 return
113 return
114
114
115 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
115 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
116 res.status = b'415 Unsupported Media Type'
116 res.status = b'415 Unsupported Media Type'
117 # TODO we should send a response with appropriate media type,
117 # TODO we should send a response with appropriate media type,
118 # since client does Accept it.
118 # since client does Accept it.
119 res.headers[b'Content-Type'] = b'text/plain'
119 res.headers[b'Content-Type'] = b'text/plain'
120 res.setbodybytes(_('client MUST send Content-Type header with '
120 res.setbodybytes(_('client MUST send Content-Type header with '
121 'value: %s\n') % FRAMINGTYPE)
121 'value: %s\n') % FRAMINGTYPE)
122 return
122 return
123
123
124 _processhttpv2request(ui, repo, req, res, permission, command, proto)
124 _processhttpv2request(ui, repo, req, res, permission, command, proto)
125
125
126 def _processhttpv2reflectrequest(ui, repo, req, res):
126 def _processhttpv2reflectrequest(ui, repo, req, res):
127 """Reads unified frame protocol request and dumps out state to client.
127 """Reads unified frame protocol request and dumps out state to client.
128
128
129 This special endpoint can be used to help debug the wire protocol.
129 This special endpoint can be used to help debug the wire protocol.
130
130
131 Instead of routing the request through the normal dispatch mechanism,
131 Instead of routing the request through the normal dispatch mechanism,
132 we instead read all frames, decode them, and feed them into our state
132 we instead read all frames, decode them, and feed them into our state
133 tracker. We then dump the log of all that activity back out to the
133 tracker. We then dump the log of all that activity back out to the
134 client.
134 client.
135 """
135 """
136 import json
136 import json
137
137
138 # Reflection APIs have a history of being abused, accidentally disclosing
138 # Reflection APIs have a history of being abused, accidentally disclosing
139 # sensitive data, etc. So we have a config knob.
139 # sensitive data, etc. So we have a config knob.
140 if not ui.configbool('experimental', 'web.api.debugreflect'):
140 if not ui.configbool('experimental', 'web.api.debugreflect'):
141 res.status = b'404 Not Found'
141 res.status = b'404 Not Found'
142 res.headers[b'Content-Type'] = b'text/plain'
142 res.headers[b'Content-Type'] = b'text/plain'
143 res.setbodybytes(_('debugreflect service not available'))
143 res.setbodybytes(_('debugreflect service not available'))
144 return
144 return
145
145
146 # We assume we have a unified framing protocol request body.
146 # We assume we have a unified framing protocol request body.
147
147
148 reactor = wireprotoframing.serverreactor()
148 reactor = wireprotoframing.serverreactor()
149 states = []
149 states = []
150
150
151 while True:
151 while True:
152 frame = wireprotoframing.readframe(req.bodyfh)
152 frame = wireprotoframing.readframe(req.bodyfh)
153
153
154 if not frame:
154 if not frame:
155 states.append(b'received: <no frame>')
155 states.append(b'received: <no frame>')
156 break
156 break
157
157
158 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
158 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
159 frame.requestid,
159 frame.requestid,
160 frame.payload))
160 frame.payload))
161
161
162 action, meta = reactor.onframerecv(frame)
162 action, meta = reactor.onframerecv(frame)
163 states.append(json.dumps((action, meta), sort_keys=True,
163 states.append(json.dumps((action, meta), sort_keys=True,
164 separators=(', ', ': ')))
164 separators=(', ', ': ')))
165
165
166 action, meta = reactor.oninputeof()
166 action, meta = reactor.oninputeof()
167 meta['action'] = action
167 meta['action'] = action
168 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
168 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
169
169
170 res.status = b'200 OK'
170 res.status = b'200 OK'
171 res.headers[b'Content-Type'] = b'text/plain'
171 res.headers[b'Content-Type'] = b'text/plain'
172 res.setbodybytes(b'\n'.join(states))
172 res.setbodybytes(b'\n'.join(states))
173
173
174 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
174 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
175 """Post-validation handler for HTTPv2 requests.
175 """Post-validation handler for HTTPv2 requests.
176
176
177 Called when the HTTP request contains unified frame-based protocol
177 Called when the HTTP request contains unified frame-based protocol
178 frames for evaluation.
178 frames for evaluation.
179 """
179 """
180 # TODO Some HTTP clients are full duplex and can receive data before
180 # TODO Some HTTP clients are full duplex and can receive data before
181 # the entire request is transmitted. Figure out a way to indicate support
181 # the entire request is transmitted. Figure out a way to indicate support
182 # for that so we can opt into full duplex mode.
182 # for that so we can opt into full duplex mode.
183 reactor = wireprotoframing.serverreactor(deferoutput=True)
183 reactor = wireprotoframing.serverreactor(deferoutput=True)
184 seencommand = False
184 seencommand = False
185
185
186 outstream = reactor.makeoutputstream()
186 outstream = reactor.makeoutputstream()
187
187
188 while True:
188 while True:
189 frame = wireprotoframing.readframe(req.bodyfh)
189 frame = wireprotoframing.readframe(req.bodyfh)
190 if not frame:
190 if not frame:
191 break
191 break
192
192
193 action, meta = reactor.onframerecv(frame)
193 action, meta = reactor.onframerecv(frame)
194
194
195 if action == 'wantframe':
195 if action == 'wantframe':
196 # Need more data before we can do anything.
196 # Need more data before we can do anything.
197 continue
197 continue
198 elif action == 'runcommand':
198 elif action == 'runcommand':
199 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
199 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
200 reqcommand, reactor, outstream,
200 reqcommand, reactor, outstream,
201 meta, issubsequent=seencommand)
201 meta, issubsequent=seencommand)
202
202
203 if sentoutput:
203 if sentoutput:
204 return
204 return
205
205
206 seencommand = True
206 seencommand = True
207
207
208 elif action == 'error':
208 elif action == 'error':
209 # TODO define proper error mechanism.
209 # TODO define proper error mechanism.
210 res.status = b'200 OK'
210 res.status = b'200 OK'
211 res.headers[b'Content-Type'] = b'text/plain'
211 res.headers[b'Content-Type'] = b'text/plain'
212 res.setbodybytes(meta['message'] + b'\n')
212 res.setbodybytes(meta['message'] + b'\n')
213 return
213 return
214 else:
214 else:
215 raise error.ProgrammingError(
215 raise error.ProgrammingError(
216 'unhandled action from frame processor: %s' % action)
216 'unhandled action from frame processor: %s' % action)
217
217
218 action, meta = reactor.oninputeof()
218 action, meta = reactor.oninputeof()
219 if action == 'sendframes':
219 if action == 'sendframes':
220 # We assume we haven't started sending the response yet. If we're
220 # We assume we haven't started sending the response yet. If we're
221 # wrong, the response type will raise an exception.
221 # wrong, the response type will raise an exception.
222 res.status = b'200 OK'
222 res.status = b'200 OK'
223 res.headers[b'Content-Type'] = FRAMINGTYPE
223 res.headers[b'Content-Type'] = FRAMINGTYPE
224 res.setbodygen(meta['framegen'])
224 res.setbodygen(meta['framegen'])
225 elif action == 'noop':
225 elif action == 'noop':
226 pass
226 pass
227 else:
227 else:
228 raise error.ProgrammingError('unhandled action from frame processor: %s'
228 raise error.ProgrammingError('unhandled action from frame processor: %s'
229 % action)
229 % action)
230
230
231 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
231 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
232 outstream, command, issubsequent):
232 outstream, command, issubsequent):
233 """Dispatch a wire protocol command made from HTTPv2 requests.
233 """Dispatch a wire protocol command made from HTTPv2 requests.
234
234
235 The authenticated permission (``authedperm``) along with the original
235 The authenticated permission (``authedperm``) along with the original
236 command from the URL (``reqcommand``) are passed in.
236 command from the URL (``reqcommand``) are passed in.
237 """
237 """
238 # We already validated that the session has permissions to perform the
238 # We already validated that the session has permissions to perform the
239 # actions in ``authedperm``. In the unified frame protocol, the canonical
239 # actions in ``authedperm``. In the unified frame protocol, the canonical
240 # command to run is expressed in a frame. However, the URL also requested
240 # command to run is expressed in a frame. However, the URL also requested
241 # to run a specific command. We need to be careful that the command we
241 # to run a specific command. We need to be careful that the command we
242 # run doesn't have permissions requirements greater than what was granted
242 # run doesn't have permissions requirements greater than what was granted
243 # by ``authedperm``.
243 # by ``authedperm``.
244 #
244 #
245 # Our rule for this is we only allow one command per HTTP request and
245 # Our rule for this is we only allow one command per HTTP request and
246 # that command must match the command in the URL. However, we make
246 # that command must match the command in the URL. However, we make
247 # an exception for the ``multirequest`` URL. This URL is allowed to
247 # an exception for the ``multirequest`` URL. This URL is allowed to
248 # execute multiple commands. We double check permissions of each command
248 # execute multiple commands. We double check permissions of each command
249 # as it is invoked to ensure there is no privilege escalation.
249 # as it is invoked to ensure there is no privilege escalation.
250 # TODO consider allowing multiple commands to regular command URLs
250 # TODO consider allowing multiple commands to regular command URLs
251 # iff each command is the same.
251 # iff each command is the same.
252
252
253 proto = httpv2protocolhandler(req, ui, args=command['args'])
253 proto = httpv2protocolhandler(req, ui, args=command['args'])
254
254
255 if reqcommand == b'multirequest':
255 if reqcommand == b'multirequest':
256 if not wireproto.commandsv2.commandavailable(command['command'], proto):
256 if not wireproto.commandsv2.commandavailable(command['command'], proto):
257 # TODO proper error mechanism
257 # TODO proper error mechanism
258 res.status = b'200 OK'
258 res.status = b'200 OK'
259 res.headers[b'Content-Type'] = b'text/plain'
259 res.headers[b'Content-Type'] = b'text/plain'
260 res.setbodybytes(_('wire protocol command not available: %s') %
260 res.setbodybytes(_('wire protocol command not available: %s') %
261 command['command'])
261 command['command'])
262 return True
262 return True
263
263
264 # TODO don't use assert here, since it may be elided by -O.
264 # TODO don't use assert here, since it may be elided by -O.
265 assert authedperm in (b'ro', b'rw')
265 assert authedperm in (b'ro', b'rw')
266 wirecommand = wireproto.commandsv2[command['command']]
266 wirecommand = wireproto.commandsv2[command['command']]
267 assert wirecommand.permission in ('push', 'pull')
267 assert wirecommand.permission in ('push', 'pull')
268
268
269 if authedperm == b'ro' and wirecommand.permission != 'pull':
269 if authedperm == b'ro' and wirecommand.permission != 'pull':
270 # TODO proper error mechanism
270 # TODO proper error mechanism
271 res.status = b'403 Forbidden'
271 res.status = b'403 Forbidden'
272 res.headers[b'Content-Type'] = b'text/plain'
272 res.headers[b'Content-Type'] = b'text/plain'
273 res.setbodybytes(_('insufficient permissions to execute '
273 res.setbodybytes(_('insufficient permissions to execute '
274 'command: %s') % command['command'])
274 'command: %s') % command['command'])
275 return True
275 return True
276
276
277 # TODO should we also call checkperm() here? Maybe not if we're going
277 # TODO should we also call checkperm() here? Maybe not if we're going
278 # to overhaul that API. The granted scope from the URL check should
278 # to overhaul that API. The granted scope from the URL check should
279 # be good enough.
279 # be good enough.
280
280
281 else:
281 else:
282 # Don't allow multiple commands outside of ``multirequest`` URL.
282 # Don't allow multiple commands outside of ``multirequest`` URL.
283 if issubsequent:
283 if issubsequent:
284 # TODO proper error mechanism
284 # TODO proper error mechanism
285 res.status = b'200 OK'
285 res.status = b'200 OK'
286 res.headers[b'Content-Type'] = b'text/plain'
286 res.headers[b'Content-Type'] = b'text/plain'
287 res.setbodybytes(_('multiple commands cannot be issued to this '
287 res.setbodybytes(_('multiple commands cannot be issued to this '
288 'URL'))
288 'URL'))
289 return True
289 return True
290
290
291 if reqcommand != command['command']:
291 if reqcommand != command['command']:
292 # TODO define proper error mechanism
292 # TODO define proper error mechanism
293 res.status = b'200 OK'
293 res.status = b'200 OK'
294 res.headers[b'Content-Type'] = b'text/plain'
294 res.headers[b'Content-Type'] = b'text/plain'
295 res.setbodybytes(_('command in frame must match command in URL'))
295 res.setbodybytes(_('command in frame must match command in URL'))
296 return True
296 return True
297
297
298 rsp = wireproto.dispatch(repo, proto, command['command'])
298 rsp = wireproto.dispatch(repo, proto, command['command'])
299
299
300 res.status = b'200 OK'
300 res.status = b'200 OK'
301 res.headers[b'Content-Type'] = FRAMINGTYPE
301 res.headers[b'Content-Type'] = FRAMINGTYPE
302
302
303 if isinstance(rsp, wireprototypes.bytesresponse):
303 if isinstance(rsp, wireprototypes.bytesresponse):
304 action, meta = reactor.onbytesresponseready(outstream,
304 action, meta = reactor.onbytesresponseready(outstream,
305 command['requestid'],
305 command['requestid'],
306 rsp.data)
306 rsp.data)
307 elif isinstance(rsp, wireprototypes.cborresponse):
307 elif isinstance(rsp, wireprototypes.cborresponse):
308 encoded = cbor.dumps(rsp.value, canonical=True)
308 encoded = cbor.dumps(rsp.value, canonical=True)
309 action, meta = reactor.onbytesresponseready(outstream,
309 action, meta = reactor.onbytesresponseready(outstream,
310 command['requestid'],
310 command['requestid'],
311 encoded,
311 encoded,
312 iscbor=True)
312 iscbor=True)
313 else:
313 else:
314 action, meta = reactor.onapplicationerror(
314 action, meta = reactor.onapplicationerror(
315 _('unhandled response type from wire proto command'))
315 _('unhandled response type from wire proto command'))
316
316
317 if action == 'sendframes':
317 if action == 'sendframes':
318 res.setbodygen(meta['framegen'])
318 res.setbodygen(meta['framegen'])
319 return True
319 return True
320 elif action == 'noop':
320 elif action == 'noop':
321 return False
321 return False
322 else:
322 else:
323 raise error.ProgrammingError('unhandled event from reactor: %s' %
323 raise error.ProgrammingError('unhandled event from reactor: %s' %
324 action)
324 action)
325
325
326 @zi.implementer(wireprototypes.baseprotocolhandler)
326 @zi.implementer(wireprototypes.baseprotocolhandler)
327 class httpv2protocolhandler(object):
327 class httpv2protocolhandler(object):
328 def __init__(self, req, ui, args=None):
328 def __init__(self, req, ui, args=None):
329 self._req = req
329 self._req = req
330 self._ui = ui
330 self._ui = ui
331 self._args = args
331 self._args = args
332
332
333 @property
333 @property
334 def name(self):
334 def name(self):
335 return HTTPV2
335 return HTTPV2
336
336
337 def getargs(self, args):
337 def getargs(self, args):
338 data = {}
338 data = {}
339 for k, typ in args.items():
339 for k, typ in args.items():
340 if k == '*':
340 if k == '*':
341 raise NotImplementedError('do not support * args')
341 raise NotImplementedError('do not support * args')
342 elif k in self._args:
342 elif k in self._args:
343 # TODO consider validating value types.
343 # TODO consider validating value types.
344 data[k] = self._args[k]
344 data[k] = self._args[k]
345
345
346 return data
346 return data
347
347
348 def getprotocaps(self):
348 def getprotocaps(self):
349 # Protocol capabilities are currently not implemented for HTTP V2.
349 # Protocol capabilities are currently not implemented for HTTP V2.
350 return set()
350 return set()
351
351
352 def getpayload(self):
352 def getpayload(self):
353 raise NotImplementedError
353 raise NotImplementedError
354
354
355 @contextlib.contextmanager
355 @contextlib.contextmanager
356 def mayberedirectstdio(self):
356 def mayberedirectstdio(self):
357 raise NotImplementedError
357 raise NotImplementedError
358
358
359 def client(self):
359 def client(self):
360 raise NotImplementedError
360 raise NotImplementedError
361
361
362 def addcapabilities(self, repo, caps):
362 def addcapabilities(self, repo, caps):
363 return caps
363 return caps
364
364
365 def checkperm(self, perm):
365 def checkperm(self, perm):
366 raise NotImplementedError
366 raise NotImplementedError
367
367
368 def httpv2apidescriptor(req, repo):
369 proto = httpv2protocolhandler(req, repo.ui)
370
371 return _capabilitiesv2(repo, proto)
372
368 def _capabilitiesv2(repo, proto):
373 def _capabilitiesv2(repo, proto):
369 """Obtain the set of capabilities for version 2 transports.
374 """Obtain the set of capabilities for version 2 transports.
370
375
371 These capabilities are distinct from the capabilities for version 1
376 These capabilities are distinct from the capabilities for version 1
372 transports.
377 transports.
373 """
378 """
374 compression = []
379 compression = []
375 for engine in wireproto.supportedcompengines(repo.ui, util.SERVERROLE):
380 for engine in wireproto.supportedcompengines(repo.ui, util.SERVERROLE):
376 compression.append({
381 compression.append({
377 b'name': engine.wireprotosupport().name,
382 b'name': engine.wireprotosupport().name,
378 })
383 })
379
384
380 caps = {
385 caps = {
381 'commands': {},
386 'commands': {},
382 'compression': compression,
387 'compression': compression,
383 }
388 }
384
389
385 for command, entry in wireproto.commandsv2.items():
390 for command, entry in wireproto.commandsv2.items():
386 caps['commands'][command] = {
391 caps['commands'][command] = {
387 'args': entry.args,
392 'args': entry.args,
388 'permissions': [entry.permission],
393 'permissions': [entry.permission],
389 }
394 }
390
395
391 return proto.addcapabilities(repo, caps)
396 return proto.addcapabilities(repo, caps)
392
397
393 def wireprotocommand(*args, **kwargs):
398 def wireprotocommand(*args, **kwargs):
394 def register(func):
399 def register(func):
395 return wireproto.wireprotocommand(
400 return wireproto.wireprotocommand(
396 *args, transportpolicy=wireproto.POLICY_V2_ONLY, **kwargs)(func)
401 *args, transportpolicy=wireproto.POLICY_V2_ONLY, **kwargs)(func)
397
402
398 return register
403 return register
399
404
400 @wireprotocommand('branchmap', permission='pull')
405 @wireprotocommand('branchmap', permission='pull')
401 def branchmapv2(repo, proto):
406 def branchmapv2(repo, proto):
402 branchmap = {encoding.fromlocal(k): v
407 branchmap = {encoding.fromlocal(k): v
403 for k, v in repo.branchmap().iteritems()}
408 for k, v in repo.branchmap().iteritems()}
404
409
405 return wireprototypes.cborresponse(branchmap)
410 return wireprototypes.cborresponse(branchmap)
406
411
407 @wireprotocommand('capabilities', permission='pull')
412 @wireprotocommand('capabilities', permission='pull')
408 def capabilitiesv2(repo, proto):
413 def capabilitiesv2(repo, proto):
409 caps = _capabilitiesv2(repo, proto)
414 caps = _capabilitiesv2(repo, proto)
410
415
411 return wireprototypes.cborresponse(caps)
416 return wireprototypes.cborresponse(caps)
412
417
413 @wireprotocommand('heads',
418 @wireprotocommand('heads',
414 args={
419 args={
415 'publiconly': False,
420 'publiconly': False,
416 },
421 },
417 permission='pull')
422 permission='pull')
418 def headsv2(repo, proto, publiconly=False):
423 def headsv2(repo, proto, publiconly=False):
419 if publiconly:
424 if publiconly:
420 repo = repo.filtered('immutable')
425 repo = repo.filtered('immutable')
421
426
422 return wireprototypes.cborresponse(repo.heads())
427 return wireprototypes.cborresponse(repo.heads())
423
428
424 @wireprotocommand('known',
429 @wireprotocommand('known',
425 args={
430 args={
426 'nodes': [b'deadbeef'],
431 'nodes': [b'deadbeef'],
427 },
432 },
428 permission='pull')
433 permission='pull')
429 def knownv2(repo, proto, nodes=None):
434 def knownv2(repo, proto, nodes=None):
430 nodes = nodes or []
435 nodes = nodes or []
431 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
436 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
432 return wireprototypes.cborresponse(result)
437 return wireprototypes.cborresponse(result)
433
438
434 @wireprotocommand('listkeys',
439 @wireprotocommand('listkeys',
435 args={
440 args={
436 'namespace': b'ns',
441 'namespace': b'ns',
437 },
442 },
438 permission='pull')
443 permission='pull')
439 def listkeysv2(repo, proto, namespace=None):
444 def listkeysv2(repo, proto, namespace=None):
440 keys = repo.listkeys(encoding.tolocal(namespace))
445 keys = repo.listkeys(encoding.tolocal(namespace))
441 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
446 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
442 for k, v in keys.iteritems()}
447 for k, v in keys.iteritems()}
443
448
444 return wireprototypes.cborresponse(keys)
449 return wireprototypes.cborresponse(keys)
445
450
446 @wireprotocommand('lookup',
451 @wireprotocommand('lookup',
447 args={
452 args={
448 'key': b'foo',
453 'key': b'foo',
449 },
454 },
450 permission='pull')
455 permission='pull')
451 def lookupv2(repo, proto, key):
456 def lookupv2(repo, proto, key):
452 key = encoding.tolocal(key)
457 key = encoding.tolocal(key)
453
458
454 # TODO handle exception.
459 # TODO handle exception.
455 node = repo.lookup(key)
460 node = repo.lookup(key)
456
461
457 return wireprototypes.cborresponse(node)
462 return wireprototypes.cborresponse(node)
458
463
459 @wireprotocommand('pushkey',
464 @wireprotocommand('pushkey',
460 args={
465 args={
461 'namespace': b'ns',
466 'namespace': b'ns',
462 'key': b'key',
467 'key': b'key',
463 'old': b'old',
468 'old': b'old',
464 'new': b'new',
469 'new': b'new',
465 },
470 },
466 permission='push')
471 permission='push')
467 def pushkeyv2(repo, proto, namespace, key, old, new):
472 def pushkeyv2(repo, proto, namespace, key, old, new):
468 # TODO handle ui output redirection
473 # TODO handle ui output redirection
469 r = repo.pushkey(encoding.tolocal(namespace),
474 r = repo.pushkey(encoding.tolocal(namespace),
470 encoding.tolocal(key),
475 encoding.tolocal(key),
471 encoding.tolocal(old),
476 encoding.tolocal(old),
472 encoding.tolocal(new))
477 encoding.tolocal(new))
473
478
474 return wireprototypes.cborresponse(r)
479 return wireprototypes.cborresponse(r)
@@ -1,40 +1,230 b''
1 $ . $TESTDIR/wireprotohelpers.sh
1 $ . $TESTDIR/wireprotohelpers.sh
2
2
3 $ hg init server
3 $ hg init server
4 $ enablehttpv2 server
4 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
5 $ cat hg.pid > $DAEMON_PIDS
6
7 A normal capabilities request is serviced for version 1
8
9 $ sendhttpraw << EOF
10 > httprequest GET ?cmd=capabilities
11 > user-agent: test
12 > EOF
13 using raw connection to peer
14 s> GET /?cmd=capabilities HTTP/1.1\r\n
15 s> Accept-Encoding: identity\r\n
16 s> user-agent: test\r\n
17 s> host: $LOCALIP:$HGPORT\r\n (glob)
18 s> \r\n
19 s> makefile('rb', None)
20 s> HTTP/1.1 200 Script output follows\r\n
21 s> Server: testing stub value\r\n
22 s> Date: $HTTP_DATE$\r\n
23 s> Content-Type: application/mercurial-0.1\r\n
24 s> Content-Length: 458\r\n
25 s> \r\n
26 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
27
28 A proper request without the API server enabled returns the legacy response
29
30 $ sendhttpraw << EOF
31 > httprequest GET ?cmd=capabilities
32 > user-agent: test
33 > x-hgupgrade-1: foo
34 > x-hgproto-1: cbor
35 > EOF
36 using raw connection to peer
37 s> GET /?cmd=capabilities HTTP/1.1\r\n
38 s> Accept-Encoding: identity\r\n
39 s> user-agent: test\r\n
40 s> x-hgproto-1: cbor\r\n
41 s> x-hgupgrade-1: foo\r\n
42 s> host: $LOCALIP:$HGPORT\r\n (glob)
43 s> \r\n
44 s> makefile('rb', None)
45 s> HTTP/1.1 200 Script output follows\r\n
46 s> Server: testing stub value\r\n
47 s> Date: $HTTP_DATE$\r\n
48 s> Content-Type: application/mercurial-0.1\r\n
49 s> Content-Length: 458\r\n
50 s> \r\n
51 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
52
53 Restart with just API server enabled. This enables serving the new format.
54
55 $ killdaemons.py
56 $ cat error.log
57
58 $ cat >> server/.hg/hgrc << EOF
59 > [experimental]
60 > web.apiserver = true
61 > EOF
62
5 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
63 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
6 $ cat hg.pid > $DAEMON_PIDS
64 $ cat hg.pid > $DAEMON_PIDS
7
65
8 capabilities request returns an array of capability strings
66 X-HgUpgrade-<N> without CBOR advertisement uses legacy response
67
68 $ sendhttpraw << EOF
69 > httprequest GET ?cmd=capabilities
70 > user-agent: test
71 > x-hgupgrade-1: foo bar
72 > EOF
73 using raw connection to peer
74 s> GET /?cmd=capabilities HTTP/1.1\r\n
75 s> Accept-Encoding: identity\r\n
76 s> user-agent: test\r\n
77 s> x-hgupgrade-1: foo bar\r\n
78 s> host: $LOCALIP:$HGPORT\r\n (glob)
79 s> \r\n
80 s> makefile('rb', None)
81 s> HTTP/1.1 200 Script output follows\r\n
82 s> Server: testing stub value\r\n
83 s> Date: $HTTP_DATE$\r\n
84 s> Content-Type: application/mercurial-0.1\r\n
85 s> Content-Length: 458\r\n
86 s> \r\n
87 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
88
89 X-HgUpgrade-<N> without known serialization in X-HgProto-<N> uses legacy response
90
91 $ sendhttpraw << EOF
92 > httprequest GET ?cmd=capabilities
93 > user-agent: test
94 > x-hgupgrade-1: foo bar
95 > x-hgproto-1: some value
96 > EOF
97 using raw connection to peer
98 s> GET /?cmd=capabilities HTTP/1.1\r\n
99 s> Accept-Encoding: identity\r\n
100 s> user-agent: test\r\n
101 s> x-hgproto-1: some value\r\n
102 s> x-hgupgrade-1: foo bar\r\n
103 s> host: $LOCALIP:$HGPORT\r\n (glob)
104 s> \r\n
105 s> makefile('rb', None)
106 s> HTTP/1.1 200 Script output follows\r\n
107 s> Server: testing stub value\r\n
108 s> Date: $HTTP_DATE$\r\n
109 s> Content-Type: application/mercurial-0.1\r\n
110 s> Content-Length: 458\r\n
111 s> \r\n
112 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
113
114 X-HgUpgrade-<N> + X-HgProto-<N> headers trigger new response format
115
116 $ sendhttpraw << EOF
117 > httprequest GET ?cmd=capabilities
118 > user-agent: test
119 > x-hgupgrade-1: foo bar
120 > x-hgproto-1: cbor
121 > EOF
122 using raw connection to peer
123 s> GET /?cmd=capabilities HTTP/1.1\r\n
124 s> Accept-Encoding: identity\r\n
125 s> user-agent: test\r\n
126 s> x-hgproto-1: cbor\r\n
127 s> x-hgupgrade-1: foo bar\r\n
128 s> host: $LOCALIP:$HGPORT\r\n (glob)
129 s> \r\n
130 s> makefile('rb', None)
131 s> HTTP/1.1 200 OK\r\n
132 s> Server: testing stub value\r\n
133 s> Date: $HTTP_DATE$\r\n
134 s> Content-Type: application/mercurial-cbor\r\n
135 s> Content-Length: 496\r\n
136 s> \r\n
137 s> \xa3Dapis\xa0GapibaseDapi/Nv1capabilitiesY\x01\xcabatch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
138 cbor> {b'apibase': b'api/', b'apis': {}, b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'}
139
140 Restart server to enable HTTPv2
141
142 $ killdaemons.py
143 $ enablehttpv2 server
144 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
145
146 Only requested API services are returned
147
148 $ sendhttpraw << EOF
149 > httprequest GET ?cmd=capabilities
150 > user-agent: test
151 > x-hgupgrade-1: foo bar
152 > x-hgproto-1: cbor
153 > EOF
154 using raw connection to peer
155 s> GET /?cmd=capabilities HTTP/1.1\r\n
156 s> Accept-Encoding: identity\r\n
157 s> user-agent: test\r\n
158 s> x-hgproto-1: cbor\r\n
159 s> x-hgupgrade-1: foo bar\r\n
160 s> host: $LOCALIP:$HGPORT\r\n (glob)
161 s> \r\n
162 s> makefile('rb', None)
163 s> HTTP/1.1 200 OK\r\n
164 s> Server: testing stub value\r\n
165 s> Date: $HTTP_DATE$\r\n
166 s> Content-Type: application/mercurial-cbor\r\n
167 s> Content-Length: 496\r\n
168 s> \r\n
169 s> \xa3Dapis\xa0GapibaseDapi/Nv1capabilitiesY\x01\xcabatch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
170 cbor> {b'apibase': b'api/', b'apis': {}, b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'}
171
172 Request for HTTPv2 service returns information about it
173
174 $ sendhttpraw << EOF
175 > httprequest GET ?cmd=capabilities
176 > user-agent: test
177 > x-hgupgrade-1: exp-http-v2-0001 foo bar
178 > x-hgproto-1: cbor
179 > EOF
180 using raw connection to peer
181 s> GET /?cmd=capabilities HTTP/1.1\r\n
182 s> Accept-Encoding: identity\r\n
183 s> user-agent: test\r\n
184 s> x-hgproto-1: cbor\r\n
185 s> x-hgupgrade-1: exp-http-v2-0001 foo bar\r\n
186 s> host: $LOCALIP:$HGPORT\r\n (glob)
187 s> \r\n
188 s> makefile('rb', None)
189 s> HTTP/1.1 200 OK\r\n
190 s> Server: testing stub value\r\n
191 s> Date: $HTTP_DATE$\r\n
192 s> Content-Type: application/mercurial-cbor\r\n
193 s> Content-Length: 879\r\n
194 s> \r\n
195 s> \xa3Dapis\xa1Pexp-http-v2-0001\xa2Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlibGapibaseDapi/Nv1capabilitiesY\x01\xcabatch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
196 cbor> {b'apibase': b'api/', b'apis': {b'exp-http-v2-0001': {b'commands': {b'branchmap': {b'args': {}, b'permissions': [b'pull']}, b'capabilities': {b'args': {}, b'permissions': [b'pull']}, b'heads': {b'args': {b'publiconly': False}, b'permissions': [b'pull']}, b'known': {b'args': {b'nodes': [b'deadbeef']}, b'permissions': [b'pull']}, b'listkeys': {b'args': {b'namespace': b'ns'}, b'permissions': [b'pull']}, b'lookup': {b'args': {b'key': b'foo'}, b'permissions': [b'pull']}, b'pushkey': {b'args': {b'key': b'key', b'namespace': b'ns', b'new': b'new', b'old': b'old'}, b'permissions': [b'push']}}, b'compression': [{b'name': b'zstd'}, {b'name': b'zlib'}]}}, b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'}
197
198 capabilities command returns expected info
9
199
10 $ sendhttpv2peer << EOF
200 $ sendhttpv2peer << EOF
11 > command capabilities
201 > command capabilities
12 > EOF
202 > EOF
13 creating http peer for wire protocol version 2
203 creating http peer for wire protocol version 2
14 sending capabilities command
204 sending capabilities command
15 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
205 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
16 s> Accept-Encoding: identity\r\n
206 s> Accept-Encoding: identity\r\n
17 s> accept: application/mercurial-exp-framing-0003\r\n
207 s> accept: application/mercurial-exp-framing-0003\r\n
18 s> content-type: application/mercurial-exp-framing-0003\r\n
208 s> content-type: application/mercurial-exp-framing-0003\r\n
19 s> content-length: 27\r\n
209 s> content-length: 27\r\n
20 s> host: $LOCALIP:$HGPORT\r\n (glob)
210 s> host: $LOCALIP:$HGPORT\r\n (glob)
21 s> user-agent: Mercurial debugwireproto\r\n
211 s> user-agent: Mercurial debugwireproto\r\n
22 s> \r\n
212 s> \r\n
23 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
213 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
24 s> makefile('rb', None)
214 s> makefile('rb', None)
25 s> HTTP/1.1 200 OK\r\n
215 s> HTTP/1.1 200 OK\r\n
26 s> Server: testing stub value\r\n
216 s> Server: testing stub value\r\n
27 s> Date: $HTTP_DATE$\r\n
217 s> Date: $HTTP_DATE$\r\n
28 s> Content-Type: application/mercurial-exp-framing-0003\r\n
218 s> Content-Type: application/mercurial-exp-framing-0003\r\n
29 s> Transfer-Encoding: chunked\r\n
219 s> Transfer-Encoding: chunked\r\n
30 s> \r\n
220 s> \r\n
31 s> *\r\n (glob)
221 s> *\r\n (glob)
32 s> *\x00\x01\x00\x02\x01F (glob)
222 s> *\x00\x01\x00\x02\x01F (glob)
33 s> \xa2Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlib
223 s> \xa2Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlib
34 s> \r\n
224 s> \r\n
35 received frame(size=*; request=1; stream=2; streamflags=stream-begin; type=bytes-response; flags=eos|cbor) (glob)
225 received frame(size=*; request=1; stream=2; streamflags=stream-begin; type=bytes-response; flags=eos|cbor) (glob)
36 s> 0\r\n
226 s> 0\r\n
37 s> \r\n
227 s> \r\n
38 response: [{b'commands': {b'branchmap': {b'args': {}, b'permissions': [b'pull']}, b'capabilities': {b'args': {}, b'permissions': [b'pull']}, b'heads': {b'args': {b'publiconly': False}, b'permissions': [b'pull']}, b'known': {b'args': {b'nodes': [b'deadbeef']}, b'permissions': [b'pull']}, b'listkeys': {b'args': {b'namespace': b'ns'}, b'permissions': [b'pull']}, b'lookup': {b'args': {b'key': b'foo'}, b'permissions': [b'pull']}, b'pushkey': {b'args': {b'key': b'key', b'namespace': b'ns', b'new': b'new', b'old': b'old'}, b'permissions': [b'push']}}, b'compression': [{b'name': b'zstd'}, {b'name': b'zlib'}]}]
228 response: [{b'commands': {b'branchmap': {b'args': {}, b'permissions': [b'pull']}, b'capabilities': {b'args': {}, b'permissions': [b'pull']}, b'heads': {b'args': {b'publiconly': False}, b'permissions': [b'pull']}, b'known': {b'args': {b'nodes': [b'deadbeef']}, b'permissions': [b'pull']}, b'listkeys': {b'args': {b'namespace': b'ns'}, b'permissions': [b'pull']}, b'lookup': {b'args': {b'key': b'foo'}, b'permissions': [b'pull']}, b'pushkey': {b'args': {b'key': b'key', b'namespace': b'ns', b'new': b'new', b'old': b'old'}, b'permissions': [b'push']}}, b'compression': [{b'name': b'zstd'}, {b'name': b'zlib'}]}]
39
229
40 $ cat error.log
230 $ cat error.log
General Comments 0
You need to be logged in to leave comments. Login now