##// END OF EJS Templates
configitems: register the 'server.bundle*' family of config...
Boris Feld -
r34614:5e61cd5f default
parent child Browse files
Show More
@@ -1,854 +1,866
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 functools
10 import functools
11
11
12 from . import (
12 from . import (
13 encoding,
13 encoding,
14 error,
14 error,
15 )
15 )
16
16
17 def loadconfigtable(ui, extname, configtable):
17 def loadconfigtable(ui, extname, configtable):
18 """update config item known to the ui with the extension ones"""
18 """update config item known to the ui with the extension ones"""
19 for section, items in configtable.items():
19 for section, items in configtable.items():
20 knownitems = ui._knownconfig.setdefault(section, {})
20 knownitems = ui._knownconfig.setdefault(section, {})
21 knownkeys = set(knownitems)
21 knownkeys = set(knownitems)
22 newkeys = set(items)
22 newkeys = set(items)
23 for key in sorted(knownkeys & newkeys):
23 for key in sorted(knownkeys & newkeys):
24 msg = "extension '%s' overwrite config item '%s.%s'"
24 msg = "extension '%s' overwrite config item '%s.%s'"
25 msg %= (extname, section, key)
25 msg %= (extname, section, key)
26 ui.develwarn(msg, config='warn-config')
26 ui.develwarn(msg, config='warn-config')
27
27
28 knownitems.update(items)
28 knownitems.update(items)
29
29
30 class configitem(object):
30 class configitem(object):
31 """represent a known config item
31 """represent a known config item
32
32
33 :section: the official config section where to find this item,
33 :section: the official config section where to find this item,
34 :name: the official name within the section,
34 :name: the official name within the section,
35 :default: default value for this item,
35 :default: default value for this item,
36 :alias: optional list of tuples as alternatives.
36 :alias: optional list of tuples as alternatives.
37 """
37 """
38
38
39 def __init__(self, section, name, default=None, alias=()):
39 def __init__(self, section, name, default=None, alias=()):
40 self.section = section
40 self.section = section
41 self.name = name
41 self.name = name
42 self.default = default
42 self.default = default
43 self.alias = list(alias)
43 self.alias = list(alias)
44
44
45 coreitems = {}
45 coreitems = {}
46
46
47 def _register(configtable, *args, **kwargs):
47 def _register(configtable, *args, **kwargs):
48 item = configitem(*args, **kwargs)
48 item = configitem(*args, **kwargs)
49 section = configtable.setdefault(item.section, {})
49 section = configtable.setdefault(item.section, {})
50 if item.name in section:
50 if item.name in section:
51 msg = "duplicated config item registration for '%s.%s'"
51 msg = "duplicated config item registration for '%s.%s'"
52 raise error.ProgrammingError(msg % (item.section, item.name))
52 raise error.ProgrammingError(msg % (item.section, item.name))
53 section[item.name] = item
53 section[item.name] = item
54
54
55 # special value for case where the default is derived from other values
55 # special value for case where the default is derived from other values
56 dynamicdefault = object()
56 dynamicdefault = object()
57
57
58 # Registering actual config items
58 # Registering actual config items
59
59
60 def getitemregister(configtable):
60 def getitemregister(configtable):
61 return functools.partial(_register, configtable)
61 return functools.partial(_register, configtable)
62
62
63 coreconfigitem = getitemregister(coreitems)
63 coreconfigitem = getitemregister(coreitems)
64
64
65 coreconfigitem('auth', 'cookiefile',
65 coreconfigitem('auth', 'cookiefile',
66 default=None,
66 default=None,
67 )
67 )
68 # bookmarks.pushing: internal hack for discovery
68 # bookmarks.pushing: internal hack for discovery
69 coreconfigitem('bookmarks', 'pushing',
69 coreconfigitem('bookmarks', 'pushing',
70 default=list,
70 default=list,
71 )
71 )
72 # bundle.mainreporoot: internal hack for bundlerepo
72 # bundle.mainreporoot: internal hack for bundlerepo
73 coreconfigitem('bundle', 'mainreporoot',
73 coreconfigitem('bundle', 'mainreporoot',
74 default='',
74 default='',
75 )
75 )
76 # bundle.reorder: experimental config
76 # bundle.reorder: experimental config
77 coreconfigitem('bundle', 'reorder',
77 coreconfigitem('bundle', 'reorder',
78 default='auto',
78 default='auto',
79 )
79 )
80 coreconfigitem('censor', 'policy',
80 coreconfigitem('censor', 'policy',
81 default='abort',
81 default='abort',
82 )
82 )
83 coreconfigitem('chgserver', 'idletimeout',
83 coreconfigitem('chgserver', 'idletimeout',
84 default=3600,
84 default=3600,
85 )
85 )
86 coreconfigitem('chgserver', 'skiphash',
86 coreconfigitem('chgserver', 'skiphash',
87 default=False,
87 default=False,
88 )
88 )
89 coreconfigitem('cmdserver', 'log',
89 coreconfigitem('cmdserver', 'log',
90 default=None,
90 default=None,
91 )
91 )
92 coreconfigitem('color', 'mode',
92 coreconfigitem('color', 'mode',
93 default='auto',
93 default='auto',
94 )
94 )
95 coreconfigitem('color', 'pagermode',
95 coreconfigitem('color', 'pagermode',
96 default=dynamicdefault,
96 default=dynamicdefault,
97 )
97 )
98 coreconfigitem('commands', 'status.relative',
98 coreconfigitem('commands', 'status.relative',
99 default=False,
99 default=False,
100 )
100 )
101 coreconfigitem('commands', 'status.skipstates',
101 coreconfigitem('commands', 'status.skipstates',
102 default=[],
102 default=[],
103 )
103 )
104 coreconfigitem('commands', 'status.verbose',
104 coreconfigitem('commands', 'status.verbose',
105 default=False,
105 default=False,
106 )
106 )
107 coreconfigitem('commands', 'update.requiredest',
107 coreconfigitem('commands', 'update.requiredest',
108 default=False,
108 default=False,
109 )
109 )
110 coreconfigitem('debug', 'dirstate.delaywrite',
110 coreconfigitem('debug', 'dirstate.delaywrite',
111 default=0,
111 default=0,
112 )
112 )
113 coreconfigitem('devel', 'all-warnings',
113 coreconfigitem('devel', 'all-warnings',
114 default=False,
114 default=False,
115 )
115 )
116 coreconfigitem('devel', 'bundle2.debug',
116 coreconfigitem('devel', 'bundle2.debug',
117 default=False,
117 default=False,
118 )
118 )
119 coreconfigitem('devel', 'cache-vfs',
119 coreconfigitem('devel', 'cache-vfs',
120 default=None,
120 default=None,
121 )
121 )
122 coreconfigitem('devel', 'check-locks',
122 coreconfigitem('devel', 'check-locks',
123 default=False,
123 default=False,
124 )
124 )
125 coreconfigitem('devel', 'check-relroot',
125 coreconfigitem('devel', 'check-relroot',
126 default=False,
126 default=False,
127 )
127 )
128 coreconfigitem('devel', 'default-date',
128 coreconfigitem('devel', 'default-date',
129 default=None,
129 default=None,
130 )
130 )
131 coreconfigitem('devel', 'deprec-warn',
131 coreconfigitem('devel', 'deprec-warn',
132 default=False,
132 default=False,
133 )
133 )
134 coreconfigitem('devel', 'disableloaddefaultcerts',
134 coreconfigitem('devel', 'disableloaddefaultcerts',
135 default=False,
135 default=False,
136 )
136 )
137 coreconfigitem('devel', 'empty-changegroup',
137 coreconfigitem('devel', 'empty-changegroup',
138 default=False,
138 default=False,
139 )
139 )
140 coreconfigitem('devel', 'legacy.exchange',
140 coreconfigitem('devel', 'legacy.exchange',
141 default=list,
141 default=list,
142 )
142 )
143 coreconfigitem('devel', 'servercafile',
143 coreconfigitem('devel', 'servercafile',
144 default='',
144 default='',
145 )
145 )
146 coreconfigitem('devel', 'serverexactprotocol',
146 coreconfigitem('devel', 'serverexactprotocol',
147 default='',
147 default='',
148 )
148 )
149 coreconfigitem('devel', 'serverrequirecert',
149 coreconfigitem('devel', 'serverrequirecert',
150 default=False,
150 default=False,
151 )
151 )
152 coreconfigitem('devel', 'strip-obsmarkers',
152 coreconfigitem('devel', 'strip-obsmarkers',
153 default=True,
153 default=True,
154 )
154 )
155 coreconfigitem('devel', 'warn-config',
155 coreconfigitem('devel', 'warn-config',
156 default=None,
156 default=None,
157 )
157 )
158 coreconfigitem('devel', 'warn-config-default',
158 coreconfigitem('devel', 'warn-config-default',
159 default=None,
159 default=None,
160 )
160 )
161 coreconfigitem('devel', 'user.obsmarker',
161 coreconfigitem('devel', 'user.obsmarker',
162 default=None,
162 default=None,
163 )
163 )
164 coreconfigitem('diff', 'nodates',
164 coreconfigitem('diff', 'nodates',
165 default=None,
165 default=None,
166 )
166 )
167 coreconfigitem('diff', 'showfunc',
167 coreconfigitem('diff', 'showfunc',
168 default=None,
168 default=None,
169 )
169 )
170 coreconfigitem('diff', 'unified',
170 coreconfigitem('diff', 'unified',
171 default=None,
171 default=None,
172 )
172 )
173 coreconfigitem('diff', 'git',
173 coreconfigitem('diff', 'git',
174 default=None,
174 default=None,
175 )
175 )
176 coreconfigitem('diff', 'ignorews',
176 coreconfigitem('diff', 'ignorews',
177 default=None,
177 default=None,
178 )
178 )
179 coreconfigitem('diff', 'ignorewsamount',
179 coreconfigitem('diff', 'ignorewsamount',
180 default=None,
180 default=None,
181 )
181 )
182 coreconfigitem('diff', 'ignoreblanklines',
182 coreconfigitem('diff', 'ignoreblanklines',
183 default=None,
183 default=None,
184 )
184 )
185 coreconfigitem('diff', 'ignorewseol',
185 coreconfigitem('diff', 'ignorewseol',
186 default=None,
186 default=None,
187 )
187 )
188 coreconfigitem('diff', 'nobinary',
188 coreconfigitem('diff', 'nobinary',
189 default=None,
189 default=None,
190 )
190 )
191 coreconfigitem('diff', 'noprefix',
191 coreconfigitem('diff', 'noprefix',
192 default=None,
192 default=None,
193 )
193 )
194 coreconfigitem('email', 'bcc',
194 coreconfigitem('email', 'bcc',
195 default=None,
195 default=None,
196 )
196 )
197 coreconfigitem('email', 'cc',
197 coreconfigitem('email', 'cc',
198 default=None,
198 default=None,
199 )
199 )
200 coreconfigitem('email', 'charsets',
200 coreconfigitem('email', 'charsets',
201 default=list,
201 default=list,
202 )
202 )
203 coreconfigitem('email', 'from',
203 coreconfigitem('email', 'from',
204 default=None,
204 default=None,
205 )
205 )
206 coreconfigitem('email', 'method',
206 coreconfigitem('email', 'method',
207 default='smtp',
207 default='smtp',
208 )
208 )
209 coreconfigitem('email', 'reply-to',
209 coreconfigitem('email', 'reply-to',
210 default=None,
210 default=None,
211 )
211 )
212 coreconfigitem('experimental', 'allowdivergence',
212 coreconfigitem('experimental', 'allowdivergence',
213 default=False,
213 default=False,
214 )
214 )
215 coreconfigitem('experimental', 'bundle-phases',
215 coreconfigitem('experimental', 'bundle-phases',
216 default=False,
216 default=False,
217 )
217 )
218 coreconfigitem('experimental', 'bundle2-advertise',
218 coreconfigitem('experimental', 'bundle2-advertise',
219 default=True,
219 default=True,
220 )
220 )
221 coreconfigitem('experimental', 'bundle2-output-capture',
221 coreconfigitem('experimental', 'bundle2-output-capture',
222 default=False,
222 default=False,
223 )
223 )
224 coreconfigitem('experimental', 'bundle2.pushback',
224 coreconfigitem('experimental', 'bundle2.pushback',
225 default=False,
225 default=False,
226 )
226 )
227 coreconfigitem('experimental', 'bundle2lazylocking',
227 coreconfigitem('experimental', 'bundle2lazylocking',
228 default=False,
228 default=False,
229 )
229 )
230 coreconfigitem('experimental', 'bundlecomplevel',
230 coreconfigitem('experimental', 'bundlecomplevel',
231 default=None,
231 default=None,
232 )
232 )
233 coreconfigitem('experimental', 'changegroup3',
233 coreconfigitem('experimental', 'changegroup3',
234 default=False,
234 default=False,
235 )
235 )
236 coreconfigitem('experimental', 'clientcompressionengines',
236 coreconfigitem('experimental', 'clientcompressionengines',
237 default=list,
237 default=list,
238 )
238 )
239 coreconfigitem('experimental', 'copytrace',
239 coreconfigitem('experimental', 'copytrace',
240 default='on',
240 default='on',
241 )
241 )
242 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
242 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
243 default=100,
243 default=100,
244 )
244 )
245 coreconfigitem('experimental', 'crecordtest',
245 coreconfigitem('experimental', 'crecordtest',
246 default=None,
246 default=None,
247 )
247 )
248 coreconfigitem('experimental', 'editortmpinhg',
248 coreconfigitem('experimental', 'editortmpinhg',
249 default=False,
249 default=False,
250 )
250 )
251 coreconfigitem('experimental', 'maxdeltachainspan',
251 coreconfigitem('experimental', 'maxdeltachainspan',
252 default=-1,
252 default=-1,
253 )
253 )
254 coreconfigitem('experimental', 'mmapindexthreshold',
254 coreconfigitem('experimental', 'mmapindexthreshold',
255 default=None,
255 default=None,
256 )
256 )
257 coreconfigitem('experimental', 'nonnormalparanoidcheck',
257 coreconfigitem('experimental', 'nonnormalparanoidcheck',
258 default=False,
258 default=False,
259 )
259 )
260 coreconfigitem('experimental', 'stabilization',
260 coreconfigitem('experimental', 'stabilization',
261 default=list,
261 default=list,
262 alias=[('experimental', 'evolution')],
262 alias=[('experimental', 'evolution')],
263 )
263 )
264 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
264 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
265 default=False,
265 default=False,
266 alias=[('experimental', 'evolution.bundle-obsmarker')],
266 alias=[('experimental', 'evolution.bundle-obsmarker')],
267 )
267 )
268 coreconfigitem('experimental', 'stabilization.track-operation',
268 coreconfigitem('experimental', 'stabilization.track-operation',
269 default=True,
269 default=True,
270 alias=[('experimental', 'evolution.track-operation')]
270 alias=[('experimental', 'evolution.track-operation')]
271 )
271 )
272 coreconfigitem('experimental', 'exportableenviron',
272 coreconfigitem('experimental', 'exportableenviron',
273 default=list,
273 default=list,
274 )
274 )
275 coreconfigitem('experimental', 'extendedheader.index',
275 coreconfigitem('experimental', 'extendedheader.index',
276 default=None,
276 default=None,
277 )
277 )
278 coreconfigitem('experimental', 'extendedheader.similarity',
278 coreconfigitem('experimental', 'extendedheader.similarity',
279 default=False,
279 default=False,
280 )
280 )
281 coreconfigitem('experimental', 'format.compression',
281 coreconfigitem('experimental', 'format.compression',
282 default='zlib',
282 default='zlib',
283 )
283 )
284 coreconfigitem('experimental', 'graphshorten',
284 coreconfigitem('experimental', 'graphshorten',
285 default=False,
285 default=False,
286 )
286 )
287 coreconfigitem('experimental', 'graphstyle.parent',
287 coreconfigitem('experimental', 'graphstyle.parent',
288 default=dynamicdefault,
288 default=dynamicdefault,
289 )
289 )
290 coreconfigitem('experimental', 'graphstyle.missing',
290 coreconfigitem('experimental', 'graphstyle.missing',
291 default=dynamicdefault,
291 default=dynamicdefault,
292 )
292 )
293 coreconfigitem('experimental', 'graphstyle.grandparent',
293 coreconfigitem('experimental', 'graphstyle.grandparent',
294 default=dynamicdefault,
294 default=dynamicdefault,
295 )
295 )
296 coreconfigitem('experimental', 'hook-track-tags',
296 coreconfigitem('experimental', 'hook-track-tags',
297 default=False,
297 default=False,
298 )
298 )
299 coreconfigitem('experimental', 'httppostargs',
299 coreconfigitem('experimental', 'httppostargs',
300 default=False,
300 default=False,
301 )
301 )
302 coreconfigitem('experimental', 'manifestv2',
302 coreconfigitem('experimental', 'manifestv2',
303 default=False,
303 default=False,
304 )
304 )
305 coreconfigitem('experimental', 'mergedriver',
305 coreconfigitem('experimental', 'mergedriver',
306 default=None,
306 default=None,
307 )
307 )
308 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
308 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
309 default=False,
309 default=False,
310 )
310 )
311 coreconfigitem('experimental', 'rebase.multidest',
311 coreconfigitem('experimental', 'rebase.multidest',
312 default=False,
312 default=False,
313 )
313 )
314 coreconfigitem('experimental', 'revertalternateinteractivemode',
314 coreconfigitem('experimental', 'revertalternateinteractivemode',
315 default=True,
315 default=True,
316 )
316 )
317 coreconfigitem('experimental', 'revlogv2',
317 coreconfigitem('experimental', 'revlogv2',
318 default=None,
318 default=None,
319 )
319 )
320 coreconfigitem('experimental', 'spacemovesdown',
320 coreconfigitem('experimental', 'spacemovesdown',
321 default=False,
321 default=False,
322 )
322 )
323 coreconfigitem('experimental', 'treemanifest',
323 coreconfigitem('experimental', 'treemanifest',
324 default=False,
324 default=False,
325 )
325 )
326 coreconfigitem('experimental', 'updatecheck',
326 coreconfigitem('experimental', 'updatecheck',
327 default=None,
327 default=None,
328 )
328 )
329 coreconfigitem('format', 'aggressivemergedeltas',
329 coreconfigitem('format', 'aggressivemergedeltas',
330 default=False,
330 default=False,
331 )
331 )
332 coreconfigitem('format', 'chunkcachesize',
332 coreconfigitem('format', 'chunkcachesize',
333 default=None,
333 default=None,
334 )
334 )
335 coreconfigitem('format', 'dotencode',
335 coreconfigitem('format', 'dotencode',
336 default=True,
336 default=True,
337 )
337 )
338 coreconfigitem('format', 'generaldelta',
338 coreconfigitem('format', 'generaldelta',
339 default=False,
339 default=False,
340 )
340 )
341 coreconfigitem('format', 'manifestcachesize',
341 coreconfigitem('format', 'manifestcachesize',
342 default=None,
342 default=None,
343 )
343 )
344 coreconfigitem('format', 'maxchainlen',
344 coreconfigitem('format', 'maxchainlen',
345 default=None,
345 default=None,
346 )
346 )
347 coreconfigitem('format', 'obsstore-version',
347 coreconfigitem('format', 'obsstore-version',
348 default=None,
348 default=None,
349 )
349 )
350 coreconfigitem('format', 'usefncache',
350 coreconfigitem('format', 'usefncache',
351 default=True,
351 default=True,
352 )
352 )
353 coreconfigitem('format', 'usegeneraldelta',
353 coreconfigitem('format', 'usegeneraldelta',
354 default=True,
354 default=True,
355 )
355 )
356 coreconfigitem('format', 'usestore',
356 coreconfigitem('format', 'usestore',
357 default=True,
357 default=True,
358 )
358 )
359 coreconfigitem('hostsecurity', 'ciphers',
359 coreconfigitem('hostsecurity', 'ciphers',
360 default=None,
360 default=None,
361 )
361 )
362 coreconfigitem('hostsecurity', 'disabletls10warning',
362 coreconfigitem('hostsecurity', 'disabletls10warning',
363 default=False,
363 default=False,
364 )
364 )
365 coreconfigitem('http_proxy', 'always',
365 coreconfigitem('http_proxy', 'always',
366 default=False,
366 default=False,
367 )
367 )
368 coreconfigitem('http_proxy', 'host',
368 coreconfigitem('http_proxy', 'host',
369 default=None,
369 default=None,
370 )
370 )
371 coreconfigitem('http_proxy', 'no',
371 coreconfigitem('http_proxy', 'no',
372 default=list,
372 default=list,
373 )
373 )
374 coreconfigitem('http_proxy', 'passwd',
374 coreconfigitem('http_proxy', 'passwd',
375 default=None,
375 default=None,
376 )
376 )
377 coreconfigitem('http_proxy', 'user',
377 coreconfigitem('http_proxy', 'user',
378 default=None,
378 default=None,
379 )
379 )
380 coreconfigitem('logtoprocess', 'commandexception',
380 coreconfigitem('logtoprocess', 'commandexception',
381 default=None,
381 default=None,
382 )
382 )
383 coreconfigitem('logtoprocess', 'commandfinish',
383 coreconfigitem('logtoprocess', 'commandfinish',
384 default=None,
384 default=None,
385 )
385 )
386 coreconfigitem('logtoprocess', 'command',
386 coreconfigitem('logtoprocess', 'command',
387 default=None,
387 default=None,
388 )
388 )
389 coreconfigitem('logtoprocess', 'develwarn',
389 coreconfigitem('logtoprocess', 'develwarn',
390 default=None,
390 default=None,
391 )
391 )
392 coreconfigitem('logtoprocess', 'uiblocked',
392 coreconfigitem('logtoprocess', 'uiblocked',
393 default=None,
393 default=None,
394 )
394 )
395 coreconfigitem('merge', 'checkunknown',
395 coreconfigitem('merge', 'checkunknown',
396 default='abort',
396 default='abort',
397 )
397 )
398 coreconfigitem('merge', 'checkignored',
398 coreconfigitem('merge', 'checkignored',
399 default='abort',
399 default='abort',
400 )
400 )
401 coreconfigitem('merge', 'followcopies',
401 coreconfigitem('merge', 'followcopies',
402 default=True,
402 default=True,
403 )
403 )
404 coreconfigitem('merge', 'preferancestor',
404 coreconfigitem('merge', 'preferancestor',
405 default=lambda: ['*'],
405 default=lambda: ['*'],
406 )
406 )
407 coreconfigitem('pager', 'ignore',
407 coreconfigitem('pager', 'ignore',
408 default=list,
408 default=list,
409 )
409 )
410 coreconfigitem('pager', 'pager',
410 coreconfigitem('pager', 'pager',
411 default=dynamicdefault,
411 default=dynamicdefault,
412 )
412 )
413 coreconfigitem('patch', 'eol',
413 coreconfigitem('patch', 'eol',
414 default='strict',
414 default='strict',
415 )
415 )
416 coreconfigitem('patch', 'fuzz',
416 coreconfigitem('patch', 'fuzz',
417 default=2,
417 default=2,
418 )
418 )
419 coreconfigitem('paths', 'default',
419 coreconfigitem('paths', 'default',
420 default=None,
420 default=None,
421 )
421 )
422 coreconfigitem('paths', 'default-push',
422 coreconfigitem('paths', 'default-push',
423 default=None,
423 default=None,
424 )
424 )
425 coreconfigitem('phases', 'checksubrepos',
425 coreconfigitem('phases', 'checksubrepos',
426 default='follow',
426 default='follow',
427 )
427 )
428 coreconfigitem('phases', 'new-commit',
428 coreconfigitem('phases', 'new-commit',
429 default='draft',
429 default='draft',
430 )
430 )
431 coreconfigitem('phases', 'publish',
431 coreconfigitem('phases', 'publish',
432 default=True,
432 default=True,
433 )
433 )
434 coreconfigitem('profiling', 'enabled',
434 coreconfigitem('profiling', 'enabled',
435 default=False,
435 default=False,
436 )
436 )
437 coreconfigitem('profiling', 'format',
437 coreconfigitem('profiling', 'format',
438 default='text',
438 default='text',
439 )
439 )
440 coreconfigitem('profiling', 'freq',
440 coreconfigitem('profiling', 'freq',
441 default=1000,
441 default=1000,
442 )
442 )
443 coreconfigitem('profiling', 'limit',
443 coreconfigitem('profiling', 'limit',
444 default=30,
444 default=30,
445 )
445 )
446 coreconfigitem('profiling', 'nested',
446 coreconfigitem('profiling', 'nested',
447 default=0,
447 default=0,
448 )
448 )
449 coreconfigitem('profiling', 'output',
449 coreconfigitem('profiling', 'output',
450 default=None,
450 default=None,
451 )
451 )
452 coreconfigitem('profiling', 'showmax',
452 coreconfigitem('profiling', 'showmax',
453 default=0.999,
453 default=0.999,
454 )
454 )
455 coreconfigitem('profiling', 'showmin',
455 coreconfigitem('profiling', 'showmin',
456 default=dynamicdefault,
456 default=dynamicdefault,
457 )
457 )
458 coreconfigitem('profiling', 'sort',
458 coreconfigitem('profiling', 'sort',
459 default='inlinetime',
459 default='inlinetime',
460 )
460 )
461 coreconfigitem('profiling', 'statformat',
461 coreconfigitem('profiling', 'statformat',
462 default='hotpath',
462 default='hotpath',
463 )
463 )
464 coreconfigitem('profiling', 'type',
464 coreconfigitem('profiling', 'type',
465 default='stat',
465 default='stat',
466 )
466 )
467 coreconfigitem('progress', 'assume-tty',
467 coreconfigitem('progress', 'assume-tty',
468 default=False,
468 default=False,
469 )
469 )
470 coreconfigitem('progress', 'changedelay',
470 coreconfigitem('progress', 'changedelay',
471 default=1,
471 default=1,
472 )
472 )
473 coreconfigitem('progress', 'clear-complete',
473 coreconfigitem('progress', 'clear-complete',
474 default=True,
474 default=True,
475 )
475 )
476 coreconfigitem('progress', 'debug',
476 coreconfigitem('progress', 'debug',
477 default=False,
477 default=False,
478 )
478 )
479 coreconfigitem('progress', 'delay',
479 coreconfigitem('progress', 'delay',
480 default=3,
480 default=3,
481 )
481 )
482 coreconfigitem('progress', 'disable',
482 coreconfigitem('progress', 'disable',
483 default=False,
483 default=False,
484 )
484 )
485 coreconfigitem('progress', 'estimateinterval',
485 coreconfigitem('progress', 'estimateinterval',
486 default=60.0,
486 default=60.0,
487 )
487 )
488 coreconfigitem('progress', 'refresh',
488 coreconfigitem('progress', 'refresh',
489 default=0.1,
489 default=0.1,
490 )
490 )
491 coreconfigitem('progress', 'width',
491 coreconfigitem('progress', 'width',
492 default=dynamicdefault,
492 default=dynamicdefault,
493 )
493 )
494 coreconfigitem('push', 'pushvars.server',
494 coreconfigitem('push', 'pushvars.server',
495 default=False,
495 default=False,
496 )
496 )
497 coreconfigitem('server', 'bundle1',
497 coreconfigitem('server', 'bundle1',
498 default=True,
498 default=True,
499 )
499 )
500 coreconfigitem('server', 'bundle1gd',
500 coreconfigitem('server', 'bundle1gd',
501 default=None,
501 default=None,
502 )
502 )
503 coreconfigitem('server', 'bundle1.pull',
504 default=None,
505 )
506 coreconfigitem('server', 'bundle1gd.pull',
507 default=None,
508 )
509 coreconfigitem('server', 'bundle1.push',
510 default=None,
511 )
512 coreconfigitem('server', 'bundle1gd.push',
513 default=None,
514 )
503 coreconfigitem('server', 'compressionengines',
515 coreconfigitem('server', 'compressionengines',
504 default=list,
516 default=list,
505 )
517 )
506 coreconfigitem('server', 'concurrent-push-mode',
518 coreconfigitem('server', 'concurrent-push-mode',
507 default='strict',
519 default='strict',
508 )
520 )
509 coreconfigitem('server', 'disablefullbundle',
521 coreconfigitem('server', 'disablefullbundle',
510 default=False,
522 default=False,
511 )
523 )
512 coreconfigitem('server', 'maxhttpheaderlen',
524 coreconfigitem('server', 'maxhttpheaderlen',
513 default=1024,
525 default=1024,
514 )
526 )
515 coreconfigitem('server', 'preferuncompressed',
527 coreconfigitem('server', 'preferuncompressed',
516 default=False,
528 default=False,
517 )
529 )
518 coreconfigitem('server', 'uncompressed',
530 coreconfigitem('server', 'uncompressed',
519 default=True,
531 default=True,
520 )
532 )
521 coreconfigitem('server', 'uncompressedallowsecret',
533 coreconfigitem('server', 'uncompressedallowsecret',
522 default=False,
534 default=False,
523 )
535 )
524 coreconfigitem('server', 'validate',
536 coreconfigitem('server', 'validate',
525 default=False,
537 default=False,
526 )
538 )
527 coreconfigitem('server', 'zliblevel',
539 coreconfigitem('server', 'zliblevel',
528 default=-1,
540 default=-1,
529 )
541 )
530 coreconfigitem('smtp', 'host',
542 coreconfigitem('smtp', 'host',
531 default=None,
543 default=None,
532 )
544 )
533 coreconfigitem('smtp', 'local_hostname',
545 coreconfigitem('smtp', 'local_hostname',
534 default=None,
546 default=None,
535 )
547 )
536 coreconfigitem('smtp', 'password',
548 coreconfigitem('smtp', 'password',
537 default=None,
549 default=None,
538 )
550 )
539 coreconfigitem('smtp', 'port',
551 coreconfigitem('smtp', 'port',
540 default=dynamicdefault,
552 default=dynamicdefault,
541 )
553 )
542 coreconfigitem('smtp', 'tls',
554 coreconfigitem('smtp', 'tls',
543 default='none',
555 default='none',
544 )
556 )
545 coreconfigitem('smtp', 'username',
557 coreconfigitem('smtp', 'username',
546 default=None,
558 default=None,
547 )
559 )
548 coreconfigitem('sparse', 'missingwarning',
560 coreconfigitem('sparse', 'missingwarning',
549 default=True,
561 default=True,
550 )
562 )
551 coreconfigitem('trusted', 'groups',
563 coreconfigitem('trusted', 'groups',
552 default=list,
564 default=list,
553 )
565 )
554 coreconfigitem('trusted', 'users',
566 coreconfigitem('trusted', 'users',
555 default=list,
567 default=list,
556 )
568 )
557 coreconfigitem('ui', '_usedassubrepo',
569 coreconfigitem('ui', '_usedassubrepo',
558 default=False,
570 default=False,
559 )
571 )
560 coreconfigitem('ui', 'allowemptycommit',
572 coreconfigitem('ui', 'allowemptycommit',
561 default=False,
573 default=False,
562 )
574 )
563 coreconfigitem('ui', 'archivemeta',
575 coreconfigitem('ui', 'archivemeta',
564 default=True,
576 default=True,
565 )
577 )
566 coreconfigitem('ui', 'askusername',
578 coreconfigitem('ui', 'askusername',
567 default=False,
579 default=False,
568 )
580 )
569 coreconfigitem('ui', 'clonebundlefallback',
581 coreconfigitem('ui', 'clonebundlefallback',
570 default=False,
582 default=False,
571 )
583 )
572 coreconfigitem('ui', 'clonebundleprefers',
584 coreconfigitem('ui', 'clonebundleprefers',
573 default=list,
585 default=list,
574 )
586 )
575 coreconfigitem('ui', 'clonebundles',
587 coreconfigitem('ui', 'clonebundles',
576 default=True,
588 default=True,
577 )
589 )
578 coreconfigitem('ui', 'color',
590 coreconfigitem('ui', 'color',
579 default='auto',
591 default='auto',
580 )
592 )
581 coreconfigitem('ui', 'commitsubrepos',
593 coreconfigitem('ui', 'commitsubrepos',
582 default=False,
594 default=False,
583 )
595 )
584 coreconfigitem('ui', 'debug',
596 coreconfigitem('ui', 'debug',
585 default=False,
597 default=False,
586 )
598 )
587 coreconfigitem('ui', 'debugger',
599 coreconfigitem('ui', 'debugger',
588 default=None,
600 default=None,
589 )
601 )
590 coreconfigitem('ui', 'fallbackencoding',
602 coreconfigitem('ui', 'fallbackencoding',
591 default=None,
603 default=None,
592 )
604 )
593 coreconfigitem('ui', 'forcecwd',
605 coreconfigitem('ui', 'forcecwd',
594 default=None,
606 default=None,
595 )
607 )
596 coreconfigitem('ui', 'forcemerge',
608 coreconfigitem('ui', 'forcemerge',
597 default=None,
609 default=None,
598 )
610 )
599 coreconfigitem('ui', 'formatdebug',
611 coreconfigitem('ui', 'formatdebug',
600 default=False,
612 default=False,
601 )
613 )
602 coreconfigitem('ui', 'formatjson',
614 coreconfigitem('ui', 'formatjson',
603 default=False,
615 default=False,
604 )
616 )
605 coreconfigitem('ui', 'formatted',
617 coreconfigitem('ui', 'formatted',
606 default=None,
618 default=None,
607 )
619 )
608 coreconfigitem('ui', 'graphnodetemplate',
620 coreconfigitem('ui', 'graphnodetemplate',
609 default=None,
621 default=None,
610 )
622 )
611 coreconfigitem('ui', 'http2debuglevel',
623 coreconfigitem('ui', 'http2debuglevel',
612 default=None,
624 default=None,
613 )
625 )
614 coreconfigitem('ui', 'interactive',
626 coreconfigitem('ui', 'interactive',
615 default=None,
627 default=None,
616 )
628 )
617 coreconfigitem('ui', 'interface',
629 coreconfigitem('ui', 'interface',
618 default=None,
630 default=None,
619 )
631 )
620 coreconfigitem('ui', 'logblockedtimes',
632 coreconfigitem('ui', 'logblockedtimes',
621 default=False,
633 default=False,
622 )
634 )
623 coreconfigitem('ui', 'logtemplate',
635 coreconfigitem('ui', 'logtemplate',
624 default=None,
636 default=None,
625 )
637 )
626 coreconfigitem('ui', 'merge',
638 coreconfigitem('ui', 'merge',
627 default=None,
639 default=None,
628 )
640 )
629 coreconfigitem('ui', 'mergemarkers',
641 coreconfigitem('ui', 'mergemarkers',
630 default='basic',
642 default='basic',
631 )
643 )
632 coreconfigitem('ui', 'mergemarkertemplate',
644 coreconfigitem('ui', 'mergemarkertemplate',
633 default=('{node|short} '
645 default=('{node|short} '
634 '{ifeq(tags, "tip", "", '
646 '{ifeq(tags, "tip", "", '
635 'ifeq(tags, "", "", "{tags} "))}'
647 'ifeq(tags, "", "", "{tags} "))}'
636 '{if(bookmarks, "{bookmarks} ")}'
648 '{if(bookmarks, "{bookmarks} ")}'
637 '{ifeq(branch, "default", "", "{branch} ")}'
649 '{ifeq(branch, "default", "", "{branch} ")}'
638 '- {author|user}: {desc|firstline}')
650 '- {author|user}: {desc|firstline}')
639 )
651 )
640 coreconfigitem('ui', 'nontty',
652 coreconfigitem('ui', 'nontty',
641 default=False,
653 default=False,
642 )
654 )
643 coreconfigitem('ui', 'origbackuppath',
655 coreconfigitem('ui', 'origbackuppath',
644 default=None,
656 default=None,
645 )
657 )
646 coreconfigitem('ui', 'paginate',
658 coreconfigitem('ui', 'paginate',
647 default=True,
659 default=True,
648 )
660 )
649 coreconfigitem('ui', 'patch',
661 coreconfigitem('ui', 'patch',
650 default=None,
662 default=None,
651 )
663 )
652 coreconfigitem('ui', 'portablefilenames',
664 coreconfigitem('ui', 'portablefilenames',
653 default='warn',
665 default='warn',
654 )
666 )
655 coreconfigitem('ui', 'promptecho',
667 coreconfigitem('ui', 'promptecho',
656 default=False,
668 default=False,
657 )
669 )
658 coreconfigitem('ui', 'quiet',
670 coreconfigitem('ui', 'quiet',
659 default=False,
671 default=False,
660 )
672 )
661 coreconfigitem('ui', 'quietbookmarkmove',
673 coreconfigitem('ui', 'quietbookmarkmove',
662 default=False,
674 default=False,
663 )
675 )
664 coreconfigitem('ui', 'remotecmd',
676 coreconfigitem('ui', 'remotecmd',
665 default='hg',
677 default='hg',
666 )
678 )
667 coreconfigitem('ui', 'report_untrusted',
679 coreconfigitem('ui', 'report_untrusted',
668 default=True,
680 default=True,
669 )
681 )
670 coreconfigitem('ui', 'rollback',
682 coreconfigitem('ui', 'rollback',
671 default=True,
683 default=True,
672 )
684 )
673 coreconfigitem('ui', 'slash',
685 coreconfigitem('ui', 'slash',
674 default=False,
686 default=False,
675 )
687 )
676 coreconfigitem('ui', 'ssh',
688 coreconfigitem('ui', 'ssh',
677 default='ssh',
689 default='ssh',
678 )
690 )
679 coreconfigitem('ui', 'statuscopies',
691 coreconfigitem('ui', 'statuscopies',
680 default=False,
692 default=False,
681 )
693 )
682 coreconfigitem('ui', 'strict',
694 coreconfigitem('ui', 'strict',
683 default=False,
695 default=False,
684 )
696 )
685 coreconfigitem('ui', 'style',
697 coreconfigitem('ui', 'style',
686 default='',
698 default='',
687 )
699 )
688 coreconfigitem('ui', 'supportcontact',
700 coreconfigitem('ui', 'supportcontact',
689 default=None,
701 default=None,
690 )
702 )
691 coreconfigitem('ui', 'textwidth',
703 coreconfigitem('ui', 'textwidth',
692 default=78,
704 default=78,
693 )
705 )
694 coreconfigitem('ui', 'timeout',
706 coreconfigitem('ui', 'timeout',
695 default='600',
707 default='600',
696 )
708 )
697 coreconfigitem('ui', 'traceback',
709 coreconfigitem('ui', 'traceback',
698 default=False,
710 default=False,
699 )
711 )
700 coreconfigitem('ui', 'tweakdefaults',
712 coreconfigitem('ui', 'tweakdefaults',
701 default=False,
713 default=False,
702 )
714 )
703 coreconfigitem('ui', 'usehttp2',
715 coreconfigitem('ui', 'usehttp2',
704 default=False,
716 default=False,
705 )
717 )
706 coreconfigitem('ui', 'username',
718 coreconfigitem('ui', 'username',
707 alias=[('ui', 'user')]
719 alias=[('ui', 'user')]
708 )
720 )
709 coreconfigitem('ui', 'verbose',
721 coreconfigitem('ui', 'verbose',
710 default=False,
722 default=False,
711 )
723 )
712 coreconfigitem('verify', 'skipflags',
724 coreconfigitem('verify', 'skipflags',
713 default=None,
725 default=None,
714 )
726 )
715 coreconfigitem('web', 'allowbz2',
727 coreconfigitem('web', 'allowbz2',
716 default=None,
728 default=None,
717 )
729 )
718 coreconfigitem('web', 'allowgz',
730 coreconfigitem('web', 'allowgz',
719 default=None,
731 default=None,
720 )
732 )
721 coreconfigitem('web', 'allowpull',
733 coreconfigitem('web', 'allowpull',
722 default=True,
734 default=True,
723 )
735 )
724 coreconfigitem('web', 'allow_push',
736 coreconfigitem('web', 'allow_push',
725 default=list,
737 default=list,
726 )
738 )
727 coreconfigitem('web', 'allowzip',
739 coreconfigitem('web', 'allowzip',
728 default=None,
740 default=None,
729 )
741 )
730 coreconfigitem('web', 'cache',
742 coreconfigitem('web', 'cache',
731 default=True,
743 default=True,
732 )
744 )
733 coreconfigitem('web', 'contact',
745 coreconfigitem('web', 'contact',
734 default=None,
746 default=None,
735 )
747 )
736 coreconfigitem('web', 'deny_push',
748 coreconfigitem('web', 'deny_push',
737 default=list,
749 default=list,
738 )
750 )
739 coreconfigitem('web', 'guessmime',
751 coreconfigitem('web', 'guessmime',
740 default=False,
752 default=False,
741 )
753 )
742 coreconfigitem('web', 'hidden',
754 coreconfigitem('web', 'hidden',
743 default=None,
755 default=None,
744 )
756 )
745 coreconfigitem('web', 'labels',
757 coreconfigitem('web', 'labels',
746 default=list,
758 default=list,
747 )
759 )
748 coreconfigitem('web', 'logoimg',
760 coreconfigitem('web', 'logoimg',
749 default='hglogo.png',
761 default='hglogo.png',
750 )
762 )
751 coreconfigitem('web', 'logourl',
763 coreconfigitem('web', 'logourl',
752 default='https://mercurial-scm.org/',
764 default='https://mercurial-scm.org/',
753 )
765 )
754 coreconfigitem('web', 'accesslog',
766 coreconfigitem('web', 'accesslog',
755 default='-',
767 default='-',
756 )
768 )
757 coreconfigitem('web', 'address',
769 coreconfigitem('web', 'address',
758 default='',
770 default='',
759 )
771 )
760 coreconfigitem('web', 'allow_archive',
772 coreconfigitem('web', 'allow_archive',
761 default=list,
773 default=list,
762 )
774 )
763 coreconfigitem('web', 'allow_read',
775 coreconfigitem('web', 'allow_read',
764 default=list,
776 default=list,
765 )
777 )
766 coreconfigitem('web', 'baseurl',
778 coreconfigitem('web', 'baseurl',
767 default=None,
779 default=None,
768 )
780 )
769 coreconfigitem('web', 'cacerts',
781 coreconfigitem('web', 'cacerts',
770 default=None,
782 default=None,
771 )
783 )
772 coreconfigitem('web', 'certificate',
784 coreconfigitem('web', 'certificate',
773 default=None,
785 default=None,
774 )
786 )
775 coreconfigitem('web', 'collapse',
787 coreconfigitem('web', 'collapse',
776 default=False,
788 default=False,
777 )
789 )
778 coreconfigitem('web', 'csp',
790 coreconfigitem('web', 'csp',
779 default=None,
791 default=None,
780 )
792 )
781 coreconfigitem('web', 'deny_read',
793 coreconfigitem('web', 'deny_read',
782 default=list,
794 default=list,
783 )
795 )
784 coreconfigitem('web', 'descend',
796 coreconfigitem('web', 'descend',
785 default=True,
797 default=True,
786 )
798 )
787 coreconfigitem('web', 'description',
799 coreconfigitem('web', 'description',
788 default="",
800 default="",
789 )
801 )
790 coreconfigitem('web', 'encoding',
802 coreconfigitem('web', 'encoding',
791 default=lambda: encoding.encoding,
803 default=lambda: encoding.encoding,
792 )
804 )
793 coreconfigitem('web', 'errorlog',
805 coreconfigitem('web', 'errorlog',
794 default='-',
806 default='-',
795 )
807 )
796 coreconfigitem('web', 'ipv6',
808 coreconfigitem('web', 'ipv6',
797 default=False,
809 default=False,
798 )
810 )
799 coreconfigitem('web', 'maxchanges',
811 coreconfigitem('web', 'maxchanges',
800 default=10,
812 default=10,
801 )
813 )
802 coreconfigitem('web', 'maxfiles',
814 coreconfigitem('web', 'maxfiles',
803 default=10,
815 default=10,
804 )
816 )
805 coreconfigitem('web', 'maxshortchanges',
817 coreconfigitem('web', 'maxshortchanges',
806 default=60,
818 default=60,
807 )
819 )
808 coreconfigitem('web', 'motd',
820 coreconfigitem('web', 'motd',
809 default='',
821 default='',
810 )
822 )
811 coreconfigitem('web', 'name',
823 coreconfigitem('web', 'name',
812 default=dynamicdefault,
824 default=dynamicdefault,
813 )
825 )
814 coreconfigitem('web', 'port',
826 coreconfigitem('web', 'port',
815 default=8000,
827 default=8000,
816 )
828 )
817 coreconfigitem('web', 'prefix',
829 coreconfigitem('web', 'prefix',
818 default='',
830 default='',
819 )
831 )
820 coreconfigitem('web', 'push_ssl',
832 coreconfigitem('web', 'push_ssl',
821 default=True,
833 default=True,
822 )
834 )
823 coreconfigitem('web', 'refreshinterval',
835 coreconfigitem('web', 'refreshinterval',
824 default=20,
836 default=20,
825 )
837 )
826 coreconfigitem('web', 'stripes',
838 coreconfigitem('web', 'stripes',
827 default=1,
839 default=1,
828 )
840 )
829 coreconfigitem('web', 'style',
841 coreconfigitem('web', 'style',
830 default='paper',
842 default='paper',
831 )
843 )
832 coreconfigitem('web', 'templates',
844 coreconfigitem('web', 'templates',
833 default=None,
845 default=None,
834 )
846 )
835 coreconfigitem('web', 'view',
847 coreconfigitem('web', 'view',
836 default='served',
848 default='served',
837 )
849 )
838 coreconfigitem('worker', 'backgroundclose',
850 coreconfigitem('worker', 'backgroundclose',
839 default=dynamicdefault,
851 default=dynamicdefault,
840 )
852 )
841 # Windows defaults to a limit of 512 open files. A buffer of 128
853 # Windows defaults to a limit of 512 open files. A buffer of 128
842 # should give us enough headway.
854 # should give us enough headway.
843 coreconfigitem('worker', 'backgroundclosemaxqueue',
855 coreconfigitem('worker', 'backgroundclosemaxqueue',
844 default=384,
856 default=384,
845 )
857 )
846 coreconfigitem('worker', 'backgroundcloseminfilecount',
858 coreconfigitem('worker', 'backgroundcloseminfilecount',
847 default=2048,
859 default=2048,
848 )
860 )
849 coreconfigitem('worker', 'backgroundclosethreadcount',
861 coreconfigitem('worker', 'backgroundclosethreadcount',
850 default=4,
862 default=4,
851 )
863 )
852 coreconfigitem('worker', 'numcpus',
864 coreconfigitem('worker', 'numcpus',
853 default=None,
865 default=None,
854 )
866 )
@@ -1,1066 +1,1066
1 # wireproto.py - generic wire protocol support functions
1 # wireproto.py - generic wire protocol support functions
2 #
2 #
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2010 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 hashlib
10 import hashlib
11 import os
11 import os
12 import tempfile
12 import tempfile
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 bin,
16 bin,
17 hex,
17 hex,
18 nullid,
18 nullid,
19 )
19 )
20
20
21 from . import (
21 from . import (
22 bundle2,
22 bundle2,
23 changegroup as changegroupmod,
23 changegroup as changegroupmod,
24 discovery,
24 discovery,
25 encoding,
25 encoding,
26 error,
26 error,
27 exchange,
27 exchange,
28 peer,
28 peer,
29 pushkey as pushkeymod,
29 pushkey as pushkeymod,
30 pycompat,
30 pycompat,
31 repository,
31 repository,
32 streamclone,
32 streamclone,
33 util,
33 util,
34 )
34 )
35
35
36 urlerr = util.urlerr
36 urlerr = util.urlerr
37 urlreq = util.urlreq
37 urlreq = util.urlreq
38
38
39 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required')
39 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required')
40 bundle2requiredhint = _('see https://www.mercurial-scm.org/wiki/'
40 bundle2requiredhint = _('see https://www.mercurial-scm.org/wiki/'
41 'IncompatibleClient')
41 'IncompatibleClient')
42 bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint)
42 bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint)
43
43
44 class abstractserverproto(object):
44 class abstractserverproto(object):
45 """abstract class that summarizes the protocol API
45 """abstract class that summarizes the protocol API
46
46
47 Used as reference and documentation.
47 Used as reference and documentation.
48 """
48 """
49
49
50 def getargs(self, args):
50 def getargs(self, args):
51 """return the value for arguments in <args>
51 """return the value for arguments in <args>
52
52
53 returns a list of values (same order as <args>)"""
53 returns a list of values (same order as <args>)"""
54 raise NotImplementedError()
54 raise NotImplementedError()
55
55
56 def getfile(self, fp):
56 def getfile(self, fp):
57 """write the whole content of a file into a file like object
57 """write the whole content of a file into a file like object
58
58
59 The file is in the form::
59 The file is in the form::
60
60
61 (<chunk-size>\n<chunk>)+0\n
61 (<chunk-size>\n<chunk>)+0\n
62
62
63 chunk size is the ascii version of the int.
63 chunk size is the ascii version of the int.
64 """
64 """
65 raise NotImplementedError()
65 raise NotImplementedError()
66
66
67 def redirect(self):
67 def redirect(self):
68 """may setup interception for stdout and stderr
68 """may setup interception for stdout and stderr
69
69
70 See also the `restore` method."""
70 See also the `restore` method."""
71 raise NotImplementedError()
71 raise NotImplementedError()
72
72
73 # If the `redirect` function does install interception, the `restore`
73 # If the `redirect` function does install interception, the `restore`
74 # function MUST be defined. If interception is not used, this function
74 # function MUST be defined. If interception is not used, this function
75 # MUST NOT be defined.
75 # MUST NOT be defined.
76 #
76 #
77 # left commented here on purpose
77 # left commented here on purpose
78 #
78 #
79 #def restore(self):
79 #def restore(self):
80 # """reinstall previous stdout and stderr and return intercepted stdout
80 # """reinstall previous stdout and stderr and return intercepted stdout
81 # """
81 # """
82 # raise NotImplementedError()
82 # raise NotImplementedError()
83
83
84 class remoteiterbatcher(peer.iterbatcher):
84 class remoteiterbatcher(peer.iterbatcher):
85 def __init__(self, remote):
85 def __init__(self, remote):
86 super(remoteiterbatcher, self).__init__()
86 super(remoteiterbatcher, self).__init__()
87 self._remote = remote
87 self._remote = remote
88
88
89 def __getattr__(self, name):
89 def __getattr__(self, name):
90 # Validate this method is batchable, since submit() only supports
90 # Validate this method is batchable, since submit() only supports
91 # batchable methods.
91 # batchable methods.
92 fn = getattr(self._remote, name)
92 fn = getattr(self._remote, name)
93 if not getattr(fn, 'batchable', None):
93 if not getattr(fn, 'batchable', None):
94 raise error.ProgrammingError('Attempted to batch a non-batchable '
94 raise error.ProgrammingError('Attempted to batch a non-batchable '
95 'call to %r' % name)
95 'call to %r' % name)
96
96
97 return super(remoteiterbatcher, self).__getattr__(name)
97 return super(remoteiterbatcher, self).__getattr__(name)
98
98
99 def submit(self):
99 def submit(self):
100 """Break the batch request into many patch calls and pipeline them.
100 """Break the batch request into many patch calls and pipeline them.
101
101
102 This is mostly valuable over http where request sizes can be
102 This is mostly valuable over http where request sizes can be
103 limited, but can be used in other places as well.
103 limited, but can be used in other places as well.
104 """
104 """
105 # 2-tuple of (command, arguments) that represents what will be
105 # 2-tuple of (command, arguments) that represents what will be
106 # sent over the wire.
106 # sent over the wire.
107 requests = []
107 requests = []
108
108
109 # 4-tuple of (command, final future, @batchable generator, remote
109 # 4-tuple of (command, final future, @batchable generator, remote
110 # future).
110 # future).
111 results = []
111 results = []
112
112
113 for command, args, opts, finalfuture in self.calls:
113 for command, args, opts, finalfuture in self.calls:
114 mtd = getattr(self._remote, command)
114 mtd = getattr(self._remote, command)
115 batchable = mtd.batchable(mtd.im_self, *args, **opts)
115 batchable = mtd.batchable(mtd.im_self, *args, **opts)
116
116
117 commandargs, fremote = next(batchable)
117 commandargs, fremote = next(batchable)
118 assert fremote
118 assert fremote
119 requests.append((command, commandargs))
119 requests.append((command, commandargs))
120 results.append((command, finalfuture, batchable, fremote))
120 results.append((command, finalfuture, batchable, fremote))
121
121
122 if requests:
122 if requests:
123 self._resultiter = self._remote._submitbatch(requests)
123 self._resultiter = self._remote._submitbatch(requests)
124
124
125 self._results = results
125 self._results = results
126
126
127 def results(self):
127 def results(self):
128 for command, finalfuture, batchable, remotefuture in self._results:
128 for command, finalfuture, batchable, remotefuture in self._results:
129 # Get the raw result, set it in the remote future, feed it
129 # Get the raw result, set it in the remote future, feed it
130 # back into the @batchable generator so it can be decoded, and
130 # back into the @batchable generator so it can be decoded, and
131 # set the result on the final future to this value.
131 # set the result on the final future to this value.
132 remoteresult = next(self._resultiter)
132 remoteresult = next(self._resultiter)
133 remotefuture.set(remoteresult)
133 remotefuture.set(remoteresult)
134 finalfuture.set(next(batchable))
134 finalfuture.set(next(batchable))
135
135
136 # Verify our @batchable generators only emit 2 values.
136 # Verify our @batchable generators only emit 2 values.
137 try:
137 try:
138 next(batchable)
138 next(batchable)
139 except StopIteration:
139 except StopIteration:
140 pass
140 pass
141 else:
141 else:
142 raise error.ProgrammingError('%s @batchable generator emitted '
142 raise error.ProgrammingError('%s @batchable generator emitted '
143 'unexpected value count' % command)
143 'unexpected value count' % command)
144
144
145 yield finalfuture.value
145 yield finalfuture.value
146
146
147 # Forward a couple of names from peer to make wireproto interactions
147 # Forward a couple of names from peer to make wireproto interactions
148 # slightly more sensible.
148 # slightly more sensible.
149 batchable = peer.batchable
149 batchable = peer.batchable
150 future = peer.future
150 future = peer.future
151
151
152 # list of nodes encoding / decoding
152 # list of nodes encoding / decoding
153
153
154 def decodelist(l, sep=' '):
154 def decodelist(l, sep=' '):
155 if l:
155 if l:
156 return map(bin, l.split(sep))
156 return map(bin, l.split(sep))
157 return []
157 return []
158
158
159 def encodelist(l, sep=' '):
159 def encodelist(l, sep=' '):
160 try:
160 try:
161 return sep.join(map(hex, l))
161 return sep.join(map(hex, l))
162 except TypeError:
162 except TypeError:
163 raise
163 raise
164
164
165 # batched call argument encoding
165 # batched call argument encoding
166
166
167 def escapearg(plain):
167 def escapearg(plain):
168 return (plain
168 return (plain
169 .replace(':', ':c')
169 .replace(':', ':c')
170 .replace(',', ':o')
170 .replace(',', ':o')
171 .replace(';', ':s')
171 .replace(';', ':s')
172 .replace('=', ':e'))
172 .replace('=', ':e'))
173
173
174 def unescapearg(escaped):
174 def unescapearg(escaped):
175 return (escaped
175 return (escaped
176 .replace(':e', '=')
176 .replace(':e', '=')
177 .replace(':s', ';')
177 .replace(':s', ';')
178 .replace(':o', ',')
178 .replace(':o', ',')
179 .replace(':c', ':'))
179 .replace(':c', ':'))
180
180
181 def encodebatchcmds(req):
181 def encodebatchcmds(req):
182 """Return a ``cmds`` argument value for the ``batch`` command."""
182 """Return a ``cmds`` argument value for the ``batch`` command."""
183 cmds = []
183 cmds = []
184 for op, argsdict in req:
184 for op, argsdict in req:
185 # Old servers didn't properly unescape argument names. So prevent
185 # Old servers didn't properly unescape argument names. So prevent
186 # the sending of argument names that may not be decoded properly by
186 # the sending of argument names that may not be decoded properly by
187 # servers.
187 # servers.
188 assert all(escapearg(k) == k for k in argsdict)
188 assert all(escapearg(k) == k for k in argsdict)
189
189
190 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
190 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
191 for k, v in argsdict.iteritems())
191 for k, v in argsdict.iteritems())
192 cmds.append('%s %s' % (op, args))
192 cmds.append('%s %s' % (op, args))
193
193
194 return ';'.join(cmds)
194 return ';'.join(cmds)
195
195
196 # mapping of options accepted by getbundle and their types
196 # mapping of options accepted by getbundle and their types
197 #
197 #
198 # Meant to be extended by extensions. It is extensions responsibility to ensure
198 # Meant to be extended by extensions. It is extensions responsibility to ensure
199 # such options are properly processed in exchange.getbundle.
199 # such options are properly processed in exchange.getbundle.
200 #
200 #
201 # supported types are:
201 # supported types are:
202 #
202 #
203 # :nodes: list of binary nodes
203 # :nodes: list of binary nodes
204 # :csv: list of comma-separated values
204 # :csv: list of comma-separated values
205 # :scsv: list of comma-separated values return as set
205 # :scsv: list of comma-separated values return as set
206 # :plain: string with no transformation needed.
206 # :plain: string with no transformation needed.
207 gboptsmap = {'heads': 'nodes',
207 gboptsmap = {'heads': 'nodes',
208 'common': 'nodes',
208 'common': 'nodes',
209 'obsmarkers': 'boolean',
209 'obsmarkers': 'boolean',
210 'phases': 'boolean',
210 'phases': 'boolean',
211 'bundlecaps': 'scsv',
211 'bundlecaps': 'scsv',
212 'listkeys': 'csv',
212 'listkeys': 'csv',
213 'cg': 'boolean',
213 'cg': 'boolean',
214 'cbattempted': 'boolean'}
214 'cbattempted': 'boolean'}
215
215
216 # client side
216 # client side
217
217
218 class wirepeer(repository.legacypeer):
218 class wirepeer(repository.legacypeer):
219 """Client-side interface for communicating with a peer repository.
219 """Client-side interface for communicating with a peer repository.
220
220
221 Methods commonly call wire protocol commands of the same name.
221 Methods commonly call wire protocol commands of the same name.
222
222
223 See also httppeer.py and sshpeer.py for protocol-specific
223 See also httppeer.py and sshpeer.py for protocol-specific
224 implementations of this interface.
224 implementations of this interface.
225 """
225 """
226 # Begin of basewirepeer interface.
226 # Begin of basewirepeer interface.
227
227
228 def iterbatch(self):
228 def iterbatch(self):
229 return remoteiterbatcher(self)
229 return remoteiterbatcher(self)
230
230
231 @batchable
231 @batchable
232 def lookup(self, key):
232 def lookup(self, key):
233 self.requirecap('lookup', _('look up remote revision'))
233 self.requirecap('lookup', _('look up remote revision'))
234 f = future()
234 f = future()
235 yield {'key': encoding.fromlocal(key)}, f
235 yield {'key': encoding.fromlocal(key)}, f
236 d = f.value
236 d = f.value
237 success, data = d[:-1].split(" ", 1)
237 success, data = d[:-1].split(" ", 1)
238 if int(success):
238 if int(success):
239 yield bin(data)
239 yield bin(data)
240 else:
240 else:
241 self._abort(error.RepoError(data))
241 self._abort(error.RepoError(data))
242
242
243 @batchable
243 @batchable
244 def heads(self):
244 def heads(self):
245 f = future()
245 f = future()
246 yield {}, f
246 yield {}, f
247 d = f.value
247 d = f.value
248 try:
248 try:
249 yield decodelist(d[:-1])
249 yield decodelist(d[:-1])
250 except ValueError:
250 except ValueError:
251 self._abort(error.ResponseError(_("unexpected response:"), d))
251 self._abort(error.ResponseError(_("unexpected response:"), d))
252
252
253 @batchable
253 @batchable
254 def known(self, nodes):
254 def known(self, nodes):
255 f = future()
255 f = future()
256 yield {'nodes': encodelist(nodes)}, f
256 yield {'nodes': encodelist(nodes)}, f
257 d = f.value
257 d = f.value
258 try:
258 try:
259 yield [bool(int(b)) for b in d]
259 yield [bool(int(b)) for b in d]
260 except ValueError:
260 except ValueError:
261 self._abort(error.ResponseError(_("unexpected response:"), d))
261 self._abort(error.ResponseError(_("unexpected response:"), d))
262
262
263 @batchable
263 @batchable
264 def branchmap(self):
264 def branchmap(self):
265 f = future()
265 f = future()
266 yield {}, f
266 yield {}, f
267 d = f.value
267 d = f.value
268 try:
268 try:
269 branchmap = {}
269 branchmap = {}
270 for branchpart in d.splitlines():
270 for branchpart in d.splitlines():
271 branchname, branchheads = branchpart.split(' ', 1)
271 branchname, branchheads = branchpart.split(' ', 1)
272 branchname = encoding.tolocal(urlreq.unquote(branchname))
272 branchname = encoding.tolocal(urlreq.unquote(branchname))
273 branchheads = decodelist(branchheads)
273 branchheads = decodelist(branchheads)
274 branchmap[branchname] = branchheads
274 branchmap[branchname] = branchheads
275 yield branchmap
275 yield branchmap
276 except TypeError:
276 except TypeError:
277 self._abort(error.ResponseError(_("unexpected response:"), d))
277 self._abort(error.ResponseError(_("unexpected response:"), d))
278
278
279 @batchable
279 @batchable
280 def listkeys(self, namespace):
280 def listkeys(self, namespace):
281 if not self.capable('pushkey'):
281 if not self.capable('pushkey'):
282 yield {}, None
282 yield {}, None
283 f = future()
283 f = future()
284 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
284 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
285 yield {'namespace': encoding.fromlocal(namespace)}, f
285 yield {'namespace': encoding.fromlocal(namespace)}, f
286 d = f.value
286 d = f.value
287 self.ui.debug('received listkey for "%s": %i bytes\n'
287 self.ui.debug('received listkey for "%s": %i bytes\n'
288 % (namespace, len(d)))
288 % (namespace, len(d)))
289 yield pushkeymod.decodekeys(d)
289 yield pushkeymod.decodekeys(d)
290
290
291 @batchable
291 @batchable
292 def pushkey(self, namespace, key, old, new):
292 def pushkey(self, namespace, key, old, new):
293 if not self.capable('pushkey'):
293 if not self.capable('pushkey'):
294 yield False, None
294 yield False, None
295 f = future()
295 f = future()
296 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
296 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
297 yield {'namespace': encoding.fromlocal(namespace),
297 yield {'namespace': encoding.fromlocal(namespace),
298 'key': encoding.fromlocal(key),
298 'key': encoding.fromlocal(key),
299 'old': encoding.fromlocal(old),
299 'old': encoding.fromlocal(old),
300 'new': encoding.fromlocal(new)}, f
300 'new': encoding.fromlocal(new)}, f
301 d = f.value
301 d = f.value
302 d, output = d.split('\n', 1)
302 d, output = d.split('\n', 1)
303 try:
303 try:
304 d = bool(int(d))
304 d = bool(int(d))
305 except ValueError:
305 except ValueError:
306 raise error.ResponseError(
306 raise error.ResponseError(
307 _('push failed (unexpected response):'), d)
307 _('push failed (unexpected response):'), d)
308 for l in output.splitlines(True):
308 for l in output.splitlines(True):
309 self.ui.status(_('remote: '), l)
309 self.ui.status(_('remote: '), l)
310 yield d
310 yield d
311
311
312 def stream_out(self):
312 def stream_out(self):
313 return self._callstream('stream_out')
313 return self._callstream('stream_out')
314
314
315 def getbundle(self, source, **kwargs):
315 def getbundle(self, source, **kwargs):
316 self.requirecap('getbundle', _('look up remote changes'))
316 self.requirecap('getbundle', _('look up remote changes'))
317 opts = {}
317 opts = {}
318 bundlecaps = kwargs.get('bundlecaps')
318 bundlecaps = kwargs.get('bundlecaps')
319 if bundlecaps is not None:
319 if bundlecaps is not None:
320 kwargs['bundlecaps'] = sorted(bundlecaps)
320 kwargs['bundlecaps'] = sorted(bundlecaps)
321 else:
321 else:
322 bundlecaps = () # kwargs could have it to None
322 bundlecaps = () # kwargs could have it to None
323 for key, value in kwargs.iteritems():
323 for key, value in kwargs.iteritems():
324 if value is None:
324 if value is None:
325 continue
325 continue
326 keytype = gboptsmap.get(key)
326 keytype = gboptsmap.get(key)
327 if keytype is None:
327 if keytype is None:
328 assert False, 'unexpected'
328 assert False, 'unexpected'
329 elif keytype == 'nodes':
329 elif keytype == 'nodes':
330 value = encodelist(value)
330 value = encodelist(value)
331 elif keytype in ('csv', 'scsv'):
331 elif keytype in ('csv', 'scsv'):
332 value = ','.join(value)
332 value = ','.join(value)
333 elif keytype == 'boolean':
333 elif keytype == 'boolean':
334 value = '%i' % bool(value)
334 value = '%i' % bool(value)
335 elif keytype != 'plain':
335 elif keytype != 'plain':
336 raise KeyError('unknown getbundle option type %s'
336 raise KeyError('unknown getbundle option type %s'
337 % keytype)
337 % keytype)
338 opts[key] = value
338 opts[key] = value
339 f = self._callcompressable("getbundle", **opts)
339 f = self._callcompressable("getbundle", **opts)
340 if any((cap.startswith('HG2') for cap in bundlecaps)):
340 if any((cap.startswith('HG2') for cap in bundlecaps)):
341 return bundle2.getunbundler(self.ui, f)
341 return bundle2.getunbundler(self.ui, f)
342 else:
342 else:
343 return changegroupmod.cg1unpacker(f, 'UN')
343 return changegroupmod.cg1unpacker(f, 'UN')
344
344
345 def unbundle(self, cg, heads, url):
345 def unbundle(self, cg, heads, url):
346 '''Send cg (a readable file-like object representing the
346 '''Send cg (a readable file-like object representing the
347 changegroup to push, typically a chunkbuffer object) to the
347 changegroup to push, typically a chunkbuffer object) to the
348 remote server as a bundle.
348 remote server as a bundle.
349
349
350 When pushing a bundle10 stream, return an integer indicating the
350 When pushing a bundle10 stream, return an integer indicating the
351 result of the push (see changegroup.apply()).
351 result of the push (see changegroup.apply()).
352
352
353 When pushing a bundle20 stream, return a bundle20 stream.
353 When pushing a bundle20 stream, return a bundle20 stream.
354
354
355 `url` is the url the client thinks it's pushing to, which is
355 `url` is the url the client thinks it's pushing to, which is
356 visible to hooks.
356 visible to hooks.
357 '''
357 '''
358
358
359 if heads != ['force'] and self.capable('unbundlehash'):
359 if heads != ['force'] and self.capable('unbundlehash'):
360 heads = encodelist(['hashed',
360 heads = encodelist(['hashed',
361 hashlib.sha1(''.join(sorted(heads))).digest()])
361 hashlib.sha1(''.join(sorted(heads))).digest()])
362 else:
362 else:
363 heads = encodelist(heads)
363 heads = encodelist(heads)
364
364
365 if util.safehasattr(cg, 'deltaheader'):
365 if util.safehasattr(cg, 'deltaheader'):
366 # this a bundle10, do the old style call sequence
366 # this a bundle10, do the old style call sequence
367 ret, output = self._callpush("unbundle", cg, heads=heads)
367 ret, output = self._callpush("unbundle", cg, heads=heads)
368 if ret == "":
368 if ret == "":
369 raise error.ResponseError(
369 raise error.ResponseError(
370 _('push failed:'), output)
370 _('push failed:'), output)
371 try:
371 try:
372 ret = int(ret)
372 ret = int(ret)
373 except ValueError:
373 except ValueError:
374 raise error.ResponseError(
374 raise error.ResponseError(
375 _('push failed (unexpected response):'), ret)
375 _('push failed (unexpected response):'), ret)
376
376
377 for l in output.splitlines(True):
377 for l in output.splitlines(True):
378 self.ui.status(_('remote: '), l)
378 self.ui.status(_('remote: '), l)
379 else:
379 else:
380 # bundle2 push. Send a stream, fetch a stream.
380 # bundle2 push. Send a stream, fetch a stream.
381 stream = self._calltwowaystream('unbundle', cg, heads=heads)
381 stream = self._calltwowaystream('unbundle', cg, heads=heads)
382 ret = bundle2.getunbundler(self.ui, stream)
382 ret = bundle2.getunbundler(self.ui, stream)
383 return ret
383 return ret
384
384
385 # End of basewirepeer interface.
385 # End of basewirepeer interface.
386
386
387 # Begin of baselegacywirepeer interface.
387 # Begin of baselegacywirepeer interface.
388
388
389 def branches(self, nodes):
389 def branches(self, nodes):
390 n = encodelist(nodes)
390 n = encodelist(nodes)
391 d = self._call("branches", nodes=n)
391 d = self._call("branches", nodes=n)
392 try:
392 try:
393 br = [tuple(decodelist(b)) for b in d.splitlines()]
393 br = [tuple(decodelist(b)) for b in d.splitlines()]
394 return br
394 return br
395 except ValueError:
395 except ValueError:
396 self._abort(error.ResponseError(_("unexpected response:"), d))
396 self._abort(error.ResponseError(_("unexpected response:"), d))
397
397
398 def between(self, pairs):
398 def between(self, pairs):
399 batch = 8 # avoid giant requests
399 batch = 8 # avoid giant requests
400 r = []
400 r = []
401 for i in xrange(0, len(pairs), batch):
401 for i in xrange(0, len(pairs), batch):
402 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
402 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
403 d = self._call("between", pairs=n)
403 d = self._call("between", pairs=n)
404 try:
404 try:
405 r.extend(l and decodelist(l) or [] for l in d.splitlines())
405 r.extend(l and decodelist(l) or [] for l in d.splitlines())
406 except ValueError:
406 except ValueError:
407 self._abort(error.ResponseError(_("unexpected response:"), d))
407 self._abort(error.ResponseError(_("unexpected response:"), d))
408 return r
408 return r
409
409
410 def changegroup(self, nodes, kind):
410 def changegroup(self, nodes, kind):
411 n = encodelist(nodes)
411 n = encodelist(nodes)
412 f = self._callcompressable("changegroup", roots=n)
412 f = self._callcompressable("changegroup", roots=n)
413 return changegroupmod.cg1unpacker(f, 'UN')
413 return changegroupmod.cg1unpacker(f, 'UN')
414
414
415 def changegroupsubset(self, bases, heads, kind):
415 def changegroupsubset(self, bases, heads, kind):
416 self.requirecap('changegroupsubset', _('look up remote changes'))
416 self.requirecap('changegroupsubset', _('look up remote changes'))
417 bases = encodelist(bases)
417 bases = encodelist(bases)
418 heads = encodelist(heads)
418 heads = encodelist(heads)
419 f = self._callcompressable("changegroupsubset",
419 f = self._callcompressable("changegroupsubset",
420 bases=bases, heads=heads)
420 bases=bases, heads=heads)
421 return changegroupmod.cg1unpacker(f, 'UN')
421 return changegroupmod.cg1unpacker(f, 'UN')
422
422
423 # End of baselegacywirepeer interface.
423 # End of baselegacywirepeer interface.
424
424
425 def _submitbatch(self, req):
425 def _submitbatch(self, req):
426 """run batch request <req> on the server
426 """run batch request <req> on the server
427
427
428 Returns an iterator of the raw responses from the server.
428 Returns an iterator of the raw responses from the server.
429 """
429 """
430 rsp = self._callstream("batch", cmds=encodebatchcmds(req))
430 rsp = self._callstream("batch", cmds=encodebatchcmds(req))
431 chunk = rsp.read(1024)
431 chunk = rsp.read(1024)
432 work = [chunk]
432 work = [chunk]
433 while chunk:
433 while chunk:
434 while ';' not in chunk and chunk:
434 while ';' not in chunk and chunk:
435 chunk = rsp.read(1024)
435 chunk = rsp.read(1024)
436 work.append(chunk)
436 work.append(chunk)
437 merged = ''.join(work)
437 merged = ''.join(work)
438 while ';' in merged:
438 while ';' in merged:
439 one, merged = merged.split(';', 1)
439 one, merged = merged.split(';', 1)
440 yield unescapearg(one)
440 yield unescapearg(one)
441 chunk = rsp.read(1024)
441 chunk = rsp.read(1024)
442 work = [merged, chunk]
442 work = [merged, chunk]
443 yield unescapearg(''.join(work))
443 yield unescapearg(''.join(work))
444
444
445 def _submitone(self, op, args):
445 def _submitone(self, op, args):
446 return self._call(op, **args)
446 return self._call(op, **args)
447
447
448 def debugwireargs(self, one, two, three=None, four=None, five=None):
448 def debugwireargs(self, one, two, three=None, four=None, five=None):
449 # don't pass optional arguments left at their default value
449 # don't pass optional arguments left at their default value
450 opts = {}
450 opts = {}
451 if three is not None:
451 if three is not None:
452 opts['three'] = three
452 opts['three'] = three
453 if four is not None:
453 if four is not None:
454 opts['four'] = four
454 opts['four'] = four
455 return self._call('debugwireargs', one=one, two=two, **opts)
455 return self._call('debugwireargs', one=one, two=two, **opts)
456
456
457 def _call(self, cmd, **args):
457 def _call(self, cmd, **args):
458 """execute <cmd> on the server
458 """execute <cmd> on the server
459
459
460 The command is expected to return a simple string.
460 The command is expected to return a simple string.
461
461
462 returns the server reply as a string."""
462 returns the server reply as a string."""
463 raise NotImplementedError()
463 raise NotImplementedError()
464
464
465 def _callstream(self, cmd, **args):
465 def _callstream(self, cmd, **args):
466 """execute <cmd> on the server
466 """execute <cmd> on the server
467
467
468 The command is expected to return a stream. Note that if the
468 The command is expected to return a stream. Note that if the
469 command doesn't return a stream, _callstream behaves
469 command doesn't return a stream, _callstream behaves
470 differently for ssh and http peers.
470 differently for ssh and http peers.
471
471
472 returns the server reply as a file like object.
472 returns the server reply as a file like object.
473 """
473 """
474 raise NotImplementedError()
474 raise NotImplementedError()
475
475
476 def _callcompressable(self, cmd, **args):
476 def _callcompressable(self, cmd, **args):
477 """execute <cmd> on the server
477 """execute <cmd> on the server
478
478
479 The command is expected to return a stream.
479 The command is expected to return a stream.
480
480
481 The stream may have been compressed in some implementations. This
481 The stream may have been compressed in some implementations. This
482 function takes care of the decompression. This is the only difference
482 function takes care of the decompression. This is the only difference
483 with _callstream.
483 with _callstream.
484
484
485 returns the server reply as a file like object.
485 returns the server reply as a file like object.
486 """
486 """
487 raise NotImplementedError()
487 raise NotImplementedError()
488
488
489 def _callpush(self, cmd, fp, **args):
489 def _callpush(self, cmd, fp, **args):
490 """execute a <cmd> on server
490 """execute a <cmd> on server
491
491
492 The command is expected to be related to a push. Push has a special
492 The command is expected to be related to a push. Push has a special
493 return method.
493 return method.
494
494
495 returns the server reply as a (ret, output) tuple. ret is either
495 returns the server reply as a (ret, output) tuple. ret is either
496 empty (error) or a stringified int.
496 empty (error) or a stringified int.
497 """
497 """
498 raise NotImplementedError()
498 raise NotImplementedError()
499
499
500 def _calltwowaystream(self, cmd, fp, **args):
500 def _calltwowaystream(self, cmd, fp, **args):
501 """execute <cmd> on server
501 """execute <cmd> on server
502
502
503 The command will send a stream to the server and get a stream in reply.
503 The command will send a stream to the server and get a stream in reply.
504 """
504 """
505 raise NotImplementedError()
505 raise NotImplementedError()
506
506
507 def _abort(self, exception):
507 def _abort(self, exception):
508 """clearly abort the wire protocol connection and raise the exception
508 """clearly abort the wire protocol connection and raise the exception
509 """
509 """
510 raise NotImplementedError()
510 raise NotImplementedError()
511
511
512 # server side
512 # server side
513
513
514 # wire protocol command can either return a string or one of these classes.
514 # wire protocol command can either return a string or one of these classes.
515 class streamres(object):
515 class streamres(object):
516 """wireproto reply: binary stream
516 """wireproto reply: binary stream
517
517
518 The call was successful and the result is a stream.
518 The call was successful and the result is a stream.
519
519
520 Accepts either a generator or an object with a ``read(size)`` method.
520 Accepts either a generator or an object with a ``read(size)`` method.
521
521
522 ``v1compressible`` indicates whether this data can be compressed to
522 ``v1compressible`` indicates whether this data can be compressed to
523 "version 1" clients (technically: HTTP peers using
523 "version 1" clients (technically: HTTP peers using
524 application/mercurial-0.1 media type). This flag should NOT be used on
524 application/mercurial-0.1 media type). This flag should NOT be used on
525 new commands because new clients should support a more modern compression
525 new commands because new clients should support a more modern compression
526 mechanism.
526 mechanism.
527 """
527 """
528 def __init__(self, gen=None, reader=None, v1compressible=False):
528 def __init__(self, gen=None, reader=None, v1compressible=False):
529 self.gen = gen
529 self.gen = gen
530 self.reader = reader
530 self.reader = reader
531 self.v1compressible = v1compressible
531 self.v1compressible = v1compressible
532
532
533 class pushres(object):
533 class pushres(object):
534 """wireproto reply: success with simple integer return
534 """wireproto reply: success with simple integer return
535
535
536 The call was successful and returned an integer contained in `self.res`.
536 The call was successful and returned an integer contained in `self.res`.
537 """
537 """
538 def __init__(self, res):
538 def __init__(self, res):
539 self.res = res
539 self.res = res
540
540
541 class pusherr(object):
541 class pusherr(object):
542 """wireproto reply: failure
542 """wireproto reply: failure
543
543
544 The call failed. The `self.res` attribute contains the error message.
544 The call failed. The `self.res` attribute contains the error message.
545 """
545 """
546 def __init__(self, res):
546 def __init__(self, res):
547 self.res = res
547 self.res = res
548
548
549 class ooberror(object):
549 class ooberror(object):
550 """wireproto reply: failure of a batch of operation
550 """wireproto reply: failure of a batch of operation
551
551
552 Something failed during a batch call. The error message is stored in
552 Something failed during a batch call. The error message is stored in
553 `self.message`.
553 `self.message`.
554 """
554 """
555 def __init__(self, message):
555 def __init__(self, message):
556 self.message = message
556 self.message = message
557
557
558 def getdispatchrepo(repo, proto, command):
558 def getdispatchrepo(repo, proto, command):
559 """Obtain the repo used for processing wire protocol commands.
559 """Obtain the repo used for processing wire protocol commands.
560
560
561 The intent of this function is to serve as a monkeypatch point for
561 The intent of this function is to serve as a monkeypatch point for
562 extensions that need commands to operate on different repo views under
562 extensions that need commands to operate on different repo views under
563 specialized circumstances.
563 specialized circumstances.
564 """
564 """
565 return repo.filtered('served')
565 return repo.filtered('served')
566
566
567 def dispatch(repo, proto, command):
567 def dispatch(repo, proto, command):
568 repo = getdispatchrepo(repo, proto, command)
568 repo = getdispatchrepo(repo, proto, command)
569 func, spec = commands[command]
569 func, spec = commands[command]
570 args = proto.getargs(spec)
570 args = proto.getargs(spec)
571 return func(repo, proto, *args)
571 return func(repo, proto, *args)
572
572
573 def options(cmd, keys, others):
573 def options(cmd, keys, others):
574 opts = {}
574 opts = {}
575 for k in keys:
575 for k in keys:
576 if k in others:
576 if k in others:
577 opts[k] = others[k]
577 opts[k] = others[k]
578 del others[k]
578 del others[k]
579 if others:
579 if others:
580 util.stderr.write("warning: %s ignored unexpected arguments %s\n"
580 util.stderr.write("warning: %s ignored unexpected arguments %s\n"
581 % (cmd, ",".join(others)))
581 % (cmd, ",".join(others)))
582 return opts
582 return opts
583
583
584 def bundle1allowed(repo, action):
584 def bundle1allowed(repo, action):
585 """Whether a bundle1 operation is allowed from the server.
585 """Whether a bundle1 operation is allowed from the server.
586
586
587 Priority is:
587 Priority is:
588
588
589 1. server.bundle1gd.<action> (if generaldelta active)
589 1. server.bundle1gd.<action> (if generaldelta active)
590 2. server.bundle1.<action>
590 2. server.bundle1.<action>
591 3. server.bundle1gd (if generaldelta active)
591 3. server.bundle1gd (if generaldelta active)
592 4. server.bundle1
592 4. server.bundle1
593 """
593 """
594 ui = repo.ui
594 ui = repo.ui
595 gd = 'generaldelta' in repo.requirements
595 gd = 'generaldelta' in repo.requirements
596
596
597 if gd:
597 if gd:
598 v = ui.configbool('server', 'bundle1gd.%s' % action, None)
598 v = ui.configbool('server', 'bundle1gd.%s' % action)
599 if v is not None:
599 if v is not None:
600 return v
600 return v
601
601
602 v = ui.configbool('server', 'bundle1.%s' % action, None)
602 v = ui.configbool('server', 'bundle1.%s' % action)
603 if v is not None:
603 if v is not None:
604 return v
604 return v
605
605
606 if gd:
606 if gd:
607 v = ui.configbool('server', 'bundle1gd')
607 v = ui.configbool('server', 'bundle1gd')
608 if v is not None:
608 if v is not None:
609 return v
609 return v
610
610
611 return ui.configbool('server', 'bundle1')
611 return ui.configbool('server', 'bundle1')
612
612
613 def supportedcompengines(ui, proto, role):
613 def supportedcompengines(ui, proto, role):
614 """Obtain the list of supported compression engines for a request."""
614 """Obtain the list of supported compression engines for a request."""
615 assert role in (util.CLIENTROLE, util.SERVERROLE)
615 assert role in (util.CLIENTROLE, util.SERVERROLE)
616
616
617 compengines = util.compengines.supportedwireengines(role)
617 compengines = util.compengines.supportedwireengines(role)
618
618
619 # Allow config to override default list and ordering.
619 # Allow config to override default list and ordering.
620 if role == util.SERVERROLE:
620 if role == util.SERVERROLE:
621 configengines = ui.configlist('server', 'compressionengines')
621 configengines = ui.configlist('server', 'compressionengines')
622 config = 'server.compressionengines'
622 config = 'server.compressionengines'
623 else:
623 else:
624 # This is currently implemented mainly to facilitate testing. In most
624 # This is currently implemented mainly to facilitate testing. In most
625 # cases, the server should be in charge of choosing a compression engine
625 # cases, the server should be in charge of choosing a compression engine
626 # because a server has the most to lose from a sub-optimal choice. (e.g.
626 # because a server has the most to lose from a sub-optimal choice. (e.g.
627 # CPU DoS due to an expensive engine or a network DoS due to poor
627 # CPU DoS due to an expensive engine or a network DoS due to poor
628 # compression ratio).
628 # compression ratio).
629 configengines = ui.configlist('experimental',
629 configengines = ui.configlist('experimental',
630 'clientcompressionengines')
630 'clientcompressionengines')
631 config = 'experimental.clientcompressionengines'
631 config = 'experimental.clientcompressionengines'
632
632
633 # No explicit config. Filter out the ones that aren't supposed to be
633 # No explicit config. Filter out the ones that aren't supposed to be
634 # advertised and return default ordering.
634 # advertised and return default ordering.
635 if not configengines:
635 if not configengines:
636 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
636 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
637 return [e for e in compengines
637 return [e for e in compengines
638 if getattr(e.wireprotosupport(), attr) > 0]
638 if getattr(e.wireprotosupport(), attr) > 0]
639
639
640 # If compression engines are listed in the config, assume there is a good
640 # If compression engines are listed in the config, assume there is a good
641 # reason for it (like server operators wanting to achieve specific
641 # reason for it (like server operators wanting to achieve specific
642 # performance characteristics). So fail fast if the config references
642 # performance characteristics). So fail fast if the config references
643 # unusable compression engines.
643 # unusable compression engines.
644 validnames = set(e.name() for e in compengines)
644 validnames = set(e.name() for e in compengines)
645 invalidnames = set(e for e in configengines if e not in validnames)
645 invalidnames = set(e for e in configengines if e not in validnames)
646 if invalidnames:
646 if invalidnames:
647 raise error.Abort(_('invalid compression engine defined in %s: %s') %
647 raise error.Abort(_('invalid compression engine defined in %s: %s') %
648 (config, ', '.join(sorted(invalidnames))))
648 (config, ', '.join(sorted(invalidnames))))
649
649
650 compengines = [e for e in compengines if e.name() in configengines]
650 compengines = [e for e in compengines if e.name() in configengines]
651 compengines = sorted(compengines,
651 compengines = sorted(compengines,
652 key=lambda e: configengines.index(e.name()))
652 key=lambda e: configengines.index(e.name()))
653
653
654 if not compengines:
654 if not compengines:
655 raise error.Abort(_('%s config option does not specify any known '
655 raise error.Abort(_('%s config option does not specify any known '
656 'compression engines') % config,
656 'compression engines') % config,
657 hint=_('usable compression engines: %s') %
657 hint=_('usable compression engines: %s') %
658 ', '.sorted(validnames))
658 ', '.sorted(validnames))
659
659
660 return compengines
660 return compengines
661
661
662 # list of commands
662 # list of commands
663 commands = {}
663 commands = {}
664
664
665 def wireprotocommand(name, args=''):
665 def wireprotocommand(name, args=''):
666 """decorator for wire protocol command"""
666 """decorator for wire protocol command"""
667 def register(func):
667 def register(func):
668 commands[name] = (func, args)
668 commands[name] = (func, args)
669 return func
669 return func
670 return register
670 return register
671
671
672 @wireprotocommand('batch', 'cmds *')
672 @wireprotocommand('batch', 'cmds *')
673 def batch(repo, proto, cmds, others):
673 def batch(repo, proto, cmds, others):
674 repo = repo.filtered("served")
674 repo = repo.filtered("served")
675 res = []
675 res = []
676 for pair in cmds.split(';'):
676 for pair in cmds.split(';'):
677 op, args = pair.split(' ', 1)
677 op, args = pair.split(' ', 1)
678 vals = {}
678 vals = {}
679 for a in args.split(','):
679 for a in args.split(','):
680 if a:
680 if a:
681 n, v = a.split('=')
681 n, v = a.split('=')
682 vals[unescapearg(n)] = unescapearg(v)
682 vals[unescapearg(n)] = unescapearg(v)
683 func, spec = commands[op]
683 func, spec = commands[op]
684 if spec:
684 if spec:
685 keys = spec.split()
685 keys = spec.split()
686 data = {}
686 data = {}
687 for k in keys:
687 for k in keys:
688 if k == '*':
688 if k == '*':
689 star = {}
689 star = {}
690 for key in vals.keys():
690 for key in vals.keys():
691 if key not in keys:
691 if key not in keys:
692 star[key] = vals[key]
692 star[key] = vals[key]
693 data['*'] = star
693 data['*'] = star
694 else:
694 else:
695 data[k] = vals[k]
695 data[k] = vals[k]
696 result = func(repo, proto, *[data[k] for k in keys])
696 result = func(repo, proto, *[data[k] for k in keys])
697 else:
697 else:
698 result = func(repo, proto)
698 result = func(repo, proto)
699 if isinstance(result, ooberror):
699 if isinstance(result, ooberror):
700 return result
700 return result
701 res.append(escapearg(result))
701 res.append(escapearg(result))
702 return ';'.join(res)
702 return ';'.join(res)
703
703
704 @wireprotocommand('between', 'pairs')
704 @wireprotocommand('between', 'pairs')
705 def between(repo, proto, pairs):
705 def between(repo, proto, pairs):
706 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
706 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
707 r = []
707 r = []
708 for b in repo.between(pairs):
708 for b in repo.between(pairs):
709 r.append(encodelist(b) + "\n")
709 r.append(encodelist(b) + "\n")
710 return "".join(r)
710 return "".join(r)
711
711
712 @wireprotocommand('branchmap')
712 @wireprotocommand('branchmap')
713 def branchmap(repo, proto):
713 def branchmap(repo, proto):
714 branchmap = repo.branchmap()
714 branchmap = repo.branchmap()
715 heads = []
715 heads = []
716 for branch, nodes in branchmap.iteritems():
716 for branch, nodes in branchmap.iteritems():
717 branchname = urlreq.quote(encoding.fromlocal(branch))
717 branchname = urlreq.quote(encoding.fromlocal(branch))
718 branchnodes = encodelist(nodes)
718 branchnodes = encodelist(nodes)
719 heads.append('%s %s' % (branchname, branchnodes))
719 heads.append('%s %s' % (branchname, branchnodes))
720 return '\n'.join(heads)
720 return '\n'.join(heads)
721
721
722 @wireprotocommand('branches', 'nodes')
722 @wireprotocommand('branches', 'nodes')
723 def branches(repo, proto, nodes):
723 def branches(repo, proto, nodes):
724 nodes = decodelist(nodes)
724 nodes = decodelist(nodes)
725 r = []
725 r = []
726 for b in repo.branches(nodes):
726 for b in repo.branches(nodes):
727 r.append(encodelist(b) + "\n")
727 r.append(encodelist(b) + "\n")
728 return "".join(r)
728 return "".join(r)
729
729
730 @wireprotocommand('clonebundles', '')
730 @wireprotocommand('clonebundles', '')
731 def clonebundles(repo, proto):
731 def clonebundles(repo, proto):
732 """Server command for returning info for available bundles to seed clones.
732 """Server command for returning info for available bundles to seed clones.
733
733
734 Clients will parse this response and determine what bundle to fetch.
734 Clients will parse this response and determine what bundle to fetch.
735
735
736 Extensions may wrap this command to filter or dynamically emit data
736 Extensions may wrap this command to filter or dynamically emit data
737 depending on the request. e.g. you could advertise URLs for the closest
737 depending on the request. e.g. you could advertise URLs for the closest
738 data center given the client's IP address.
738 data center given the client's IP address.
739 """
739 """
740 return repo.vfs.tryread('clonebundles.manifest')
740 return repo.vfs.tryread('clonebundles.manifest')
741
741
742 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
742 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
743 'known', 'getbundle', 'unbundlehash', 'batch']
743 'known', 'getbundle', 'unbundlehash', 'batch']
744
744
745 def _capabilities(repo, proto):
745 def _capabilities(repo, proto):
746 """return a list of capabilities for a repo
746 """return a list of capabilities for a repo
747
747
748 This function exists to allow extensions to easily wrap capabilities
748 This function exists to allow extensions to easily wrap capabilities
749 computation
749 computation
750
750
751 - returns a lists: easy to alter
751 - returns a lists: easy to alter
752 - change done here will be propagated to both `capabilities` and `hello`
752 - change done here will be propagated to both `capabilities` and `hello`
753 command without any other action needed.
753 command without any other action needed.
754 """
754 """
755 # copy to prevent modification of the global list
755 # copy to prevent modification of the global list
756 caps = list(wireprotocaps)
756 caps = list(wireprotocaps)
757 if streamclone.allowservergeneration(repo):
757 if streamclone.allowservergeneration(repo):
758 if repo.ui.configbool('server', 'preferuncompressed'):
758 if repo.ui.configbool('server', 'preferuncompressed'):
759 caps.append('stream-preferred')
759 caps.append('stream-preferred')
760 requiredformats = repo.requirements & repo.supportedformats
760 requiredformats = repo.requirements & repo.supportedformats
761 # if our local revlogs are just revlogv1, add 'stream' cap
761 # if our local revlogs are just revlogv1, add 'stream' cap
762 if not requiredformats - {'revlogv1'}:
762 if not requiredformats - {'revlogv1'}:
763 caps.append('stream')
763 caps.append('stream')
764 # otherwise, add 'streamreqs' detailing our local revlog format
764 # otherwise, add 'streamreqs' detailing our local revlog format
765 else:
765 else:
766 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
766 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
767 if repo.ui.configbool('experimental', 'bundle2-advertise'):
767 if repo.ui.configbool('experimental', 'bundle2-advertise'):
768 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
768 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
769 caps.append('bundle2=' + urlreq.quote(capsblob))
769 caps.append('bundle2=' + urlreq.quote(capsblob))
770 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
770 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
771
771
772 if proto.name == 'http':
772 if proto.name == 'http':
773 caps.append('httpheader=%d' %
773 caps.append('httpheader=%d' %
774 repo.ui.configint('server', 'maxhttpheaderlen'))
774 repo.ui.configint('server', 'maxhttpheaderlen'))
775 if repo.ui.configbool('experimental', 'httppostargs'):
775 if repo.ui.configbool('experimental', 'httppostargs'):
776 caps.append('httppostargs')
776 caps.append('httppostargs')
777
777
778 # FUTURE advertise 0.2rx once support is implemented
778 # FUTURE advertise 0.2rx once support is implemented
779 # FUTURE advertise minrx and mintx after consulting config option
779 # FUTURE advertise minrx and mintx after consulting config option
780 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
780 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
781
781
782 compengines = supportedcompengines(repo.ui, proto, util.SERVERROLE)
782 compengines = supportedcompengines(repo.ui, proto, util.SERVERROLE)
783 if compengines:
783 if compengines:
784 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
784 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
785 for e in compengines)
785 for e in compengines)
786 caps.append('compression=%s' % comptypes)
786 caps.append('compression=%s' % comptypes)
787
787
788 return caps
788 return caps
789
789
790 # If you are writing an extension and consider wrapping this function. Wrap
790 # If you are writing an extension and consider wrapping this function. Wrap
791 # `_capabilities` instead.
791 # `_capabilities` instead.
792 @wireprotocommand('capabilities')
792 @wireprotocommand('capabilities')
793 def capabilities(repo, proto):
793 def capabilities(repo, proto):
794 return ' '.join(_capabilities(repo, proto))
794 return ' '.join(_capabilities(repo, proto))
795
795
796 @wireprotocommand('changegroup', 'roots')
796 @wireprotocommand('changegroup', 'roots')
797 def changegroup(repo, proto, roots):
797 def changegroup(repo, proto, roots):
798 nodes = decodelist(roots)
798 nodes = decodelist(roots)
799 outgoing = discovery.outgoing(repo, missingroots=nodes,
799 outgoing = discovery.outgoing(repo, missingroots=nodes,
800 missingheads=repo.heads())
800 missingheads=repo.heads())
801 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
801 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
802 return streamres(reader=cg, v1compressible=True)
802 return streamres(reader=cg, v1compressible=True)
803
803
804 @wireprotocommand('changegroupsubset', 'bases heads')
804 @wireprotocommand('changegroupsubset', 'bases heads')
805 def changegroupsubset(repo, proto, bases, heads):
805 def changegroupsubset(repo, proto, bases, heads):
806 bases = decodelist(bases)
806 bases = decodelist(bases)
807 heads = decodelist(heads)
807 heads = decodelist(heads)
808 outgoing = discovery.outgoing(repo, missingroots=bases,
808 outgoing = discovery.outgoing(repo, missingroots=bases,
809 missingheads=heads)
809 missingheads=heads)
810 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
810 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
811 return streamres(reader=cg, v1compressible=True)
811 return streamres(reader=cg, v1compressible=True)
812
812
813 @wireprotocommand('debugwireargs', 'one two *')
813 @wireprotocommand('debugwireargs', 'one two *')
814 def debugwireargs(repo, proto, one, two, others):
814 def debugwireargs(repo, proto, one, two, others):
815 # only accept optional args from the known set
815 # only accept optional args from the known set
816 opts = options('debugwireargs', ['three', 'four'], others)
816 opts = options('debugwireargs', ['three', 'four'], others)
817 return repo.debugwireargs(one, two, **opts)
817 return repo.debugwireargs(one, two, **opts)
818
818
819 @wireprotocommand('getbundle', '*')
819 @wireprotocommand('getbundle', '*')
820 def getbundle(repo, proto, others):
820 def getbundle(repo, proto, others):
821 opts = options('getbundle', gboptsmap.keys(), others)
821 opts = options('getbundle', gboptsmap.keys(), others)
822 for k, v in opts.iteritems():
822 for k, v in opts.iteritems():
823 keytype = gboptsmap[k]
823 keytype = gboptsmap[k]
824 if keytype == 'nodes':
824 if keytype == 'nodes':
825 opts[k] = decodelist(v)
825 opts[k] = decodelist(v)
826 elif keytype == 'csv':
826 elif keytype == 'csv':
827 opts[k] = list(v.split(','))
827 opts[k] = list(v.split(','))
828 elif keytype == 'scsv':
828 elif keytype == 'scsv':
829 opts[k] = set(v.split(','))
829 opts[k] = set(v.split(','))
830 elif keytype == 'boolean':
830 elif keytype == 'boolean':
831 # Client should serialize False as '0', which is a non-empty string
831 # Client should serialize False as '0', which is a non-empty string
832 # so it evaluates as a True bool.
832 # so it evaluates as a True bool.
833 if v == '0':
833 if v == '0':
834 opts[k] = False
834 opts[k] = False
835 else:
835 else:
836 opts[k] = bool(v)
836 opts[k] = bool(v)
837 elif keytype != 'plain':
837 elif keytype != 'plain':
838 raise KeyError('unknown getbundle option type %s'
838 raise KeyError('unknown getbundle option type %s'
839 % keytype)
839 % keytype)
840
840
841 if not bundle1allowed(repo, 'pull'):
841 if not bundle1allowed(repo, 'pull'):
842 if not exchange.bundle2requested(opts.get('bundlecaps')):
842 if not exchange.bundle2requested(opts.get('bundlecaps')):
843 if proto.name == 'http':
843 if proto.name == 'http':
844 return ooberror(bundle2required)
844 return ooberror(bundle2required)
845 raise error.Abort(bundle2requiredmain,
845 raise error.Abort(bundle2requiredmain,
846 hint=bundle2requiredhint)
846 hint=bundle2requiredhint)
847
847
848 try:
848 try:
849 if repo.ui.configbool('server', 'disablefullbundle'):
849 if repo.ui.configbool('server', 'disablefullbundle'):
850 # Check to see if this is a full clone.
850 # Check to see if this is a full clone.
851 clheads = set(repo.changelog.heads())
851 clheads = set(repo.changelog.heads())
852 heads = set(opts.get('heads', set()))
852 heads = set(opts.get('heads', set()))
853 common = set(opts.get('common', set()))
853 common = set(opts.get('common', set()))
854 common.discard(nullid)
854 common.discard(nullid)
855 if not common and clheads == heads:
855 if not common and clheads == heads:
856 raise error.Abort(
856 raise error.Abort(
857 _('server has pull-based clones disabled'),
857 _('server has pull-based clones disabled'),
858 hint=_('remove --pull if specified or upgrade Mercurial'))
858 hint=_('remove --pull if specified or upgrade Mercurial'))
859
859
860 chunks = exchange.getbundlechunks(repo, 'serve', **opts)
860 chunks = exchange.getbundlechunks(repo, 'serve', **opts)
861 except error.Abort as exc:
861 except error.Abort as exc:
862 # cleanly forward Abort error to the client
862 # cleanly forward Abort error to the client
863 if not exchange.bundle2requested(opts.get('bundlecaps')):
863 if not exchange.bundle2requested(opts.get('bundlecaps')):
864 if proto.name == 'http':
864 if proto.name == 'http':
865 return ooberror(str(exc) + '\n')
865 return ooberror(str(exc) + '\n')
866 raise # cannot do better for bundle1 + ssh
866 raise # cannot do better for bundle1 + ssh
867 # bundle2 request expect a bundle2 reply
867 # bundle2 request expect a bundle2 reply
868 bundler = bundle2.bundle20(repo.ui)
868 bundler = bundle2.bundle20(repo.ui)
869 manargs = [('message', str(exc))]
869 manargs = [('message', str(exc))]
870 advargs = []
870 advargs = []
871 if exc.hint is not None:
871 if exc.hint is not None:
872 advargs.append(('hint', exc.hint))
872 advargs.append(('hint', exc.hint))
873 bundler.addpart(bundle2.bundlepart('error:abort',
873 bundler.addpart(bundle2.bundlepart('error:abort',
874 manargs, advargs))
874 manargs, advargs))
875 return streamres(gen=bundler.getchunks(), v1compressible=True)
875 return streamres(gen=bundler.getchunks(), v1compressible=True)
876 return streamres(gen=chunks, v1compressible=True)
876 return streamres(gen=chunks, v1compressible=True)
877
877
878 @wireprotocommand('heads')
878 @wireprotocommand('heads')
879 def heads(repo, proto):
879 def heads(repo, proto):
880 h = repo.heads()
880 h = repo.heads()
881 return encodelist(h) + "\n"
881 return encodelist(h) + "\n"
882
882
883 @wireprotocommand('hello')
883 @wireprotocommand('hello')
884 def hello(repo, proto):
884 def hello(repo, proto):
885 '''the hello command returns a set of lines describing various
885 '''the hello command returns a set of lines describing various
886 interesting things about the server, in an RFC822-like format.
886 interesting things about the server, in an RFC822-like format.
887 Currently the only one defined is "capabilities", which
887 Currently the only one defined is "capabilities", which
888 consists of a line in the form:
888 consists of a line in the form:
889
889
890 capabilities: space separated list of tokens
890 capabilities: space separated list of tokens
891 '''
891 '''
892 return "capabilities: %s\n" % (capabilities(repo, proto))
892 return "capabilities: %s\n" % (capabilities(repo, proto))
893
893
894 @wireprotocommand('listkeys', 'namespace')
894 @wireprotocommand('listkeys', 'namespace')
895 def listkeys(repo, proto, namespace):
895 def listkeys(repo, proto, namespace):
896 d = repo.listkeys(encoding.tolocal(namespace)).items()
896 d = repo.listkeys(encoding.tolocal(namespace)).items()
897 return pushkeymod.encodekeys(d)
897 return pushkeymod.encodekeys(d)
898
898
899 @wireprotocommand('lookup', 'key')
899 @wireprotocommand('lookup', 'key')
900 def lookup(repo, proto, key):
900 def lookup(repo, proto, key):
901 try:
901 try:
902 k = encoding.tolocal(key)
902 k = encoding.tolocal(key)
903 c = repo[k]
903 c = repo[k]
904 r = c.hex()
904 r = c.hex()
905 success = 1
905 success = 1
906 except Exception as inst:
906 except Exception as inst:
907 r = str(inst)
907 r = str(inst)
908 success = 0
908 success = 0
909 return "%s %s\n" % (success, r)
909 return "%s %s\n" % (success, r)
910
910
911 @wireprotocommand('known', 'nodes *')
911 @wireprotocommand('known', 'nodes *')
912 def known(repo, proto, nodes, others):
912 def known(repo, proto, nodes, others):
913 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
913 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
914
914
915 @wireprotocommand('pushkey', 'namespace key old new')
915 @wireprotocommand('pushkey', 'namespace key old new')
916 def pushkey(repo, proto, namespace, key, old, new):
916 def pushkey(repo, proto, namespace, key, old, new):
917 # compatibility with pre-1.8 clients which were accidentally
917 # compatibility with pre-1.8 clients which were accidentally
918 # sending raw binary nodes rather than utf-8-encoded hex
918 # sending raw binary nodes rather than utf-8-encoded hex
919 if len(new) == 20 and util.escapestr(new) != new:
919 if len(new) == 20 and util.escapestr(new) != new:
920 # looks like it could be a binary node
920 # looks like it could be a binary node
921 try:
921 try:
922 new.decode('utf-8')
922 new.decode('utf-8')
923 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
923 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
924 except UnicodeDecodeError:
924 except UnicodeDecodeError:
925 pass # binary, leave unmodified
925 pass # binary, leave unmodified
926 else:
926 else:
927 new = encoding.tolocal(new) # normal path
927 new = encoding.tolocal(new) # normal path
928
928
929 if util.safehasattr(proto, 'restore'):
929 if util.safehasattr(proto, 'restore'):
930
930
931 proto.redirect()
931 proto.redirect()
932
932
933 try:
933 try:
934 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
934 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
935 encoding.tolocal(old), new) or False
935 encoding.tolocal(old), new) or False
936 except error.Abort:
936 except error.Abort:
937 r = False
937 r = False
938
938
939 output = proto.restore()
939 output = proto.restore()
940
940
941 return '%s\n%s' % (int(r), output)
941 return '%s\n%s' % (int(r), output)
942
942
943 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
943 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
944 encoding.tolocal(old), new)
944 encoding.tolocal(old), new)
945 return '%s\n' % int(r)
945 return '%s\n' % int(r)
946
946
947 @wireprotocommand('stream_out')
947 @wireprotocommand('stream_out')
948 def stream(repo, proto):
948 def stream(repo, proto):
949 '''If the server supports streaming clone, it advertises the "stream"
949 '''If the server supports streaming clone, it advertises the "stream"
950 capability with a value representing the version and flags of the repo
950 capability with a value representing the version and flags of the repo
951 it is serving. Client checks to see if it understands the format.
951 it is serving. Client checks to see if it understands the format.
952 '''
952 '''
953 if not streamclone.allowservergeneration(repo):
953 if not streamclone.allowservergeneration(repo):
954 return '1\n'
954 return '1\n'
955
955
956 def getstream(it):
956 def getstream(it):
957 yield '0\n'
957 yield '0\n'
958 for chunk in it:
958 for chunk in it:
959 yield chunk
959 yield chunk
960
960
961 try:
961 try:
962 # LockError may be raised before the first result is yielded. Don't
962 # LockError may be raised before the first result is yielded. Don't
963 # emit output until we're sure we got the lock successfully.
963 # emit output until we're sure we got the lock successfully.
964 it = streamclone.generatev1wireproto(repo)
964 it = streamclone.generatev1wireproto(repo)
965 return streamres(gen=getstream(it))
965 return streamres(gen=getstream(it))
966 except error.LockError:
966 except error.LockError:
967 return '2\n'
967 return '2\n'
968
968
969 @wireprotocommand('unbundle', 'heads')
969 @wireprotocommand('unbundle', 'heads')
970 def unbundle(repo, proto, heads):
970 def unbundle(repo, proto, heads):
971 their_heads = decodelist(heads)
971 their_heads = decodelist(heads)
972
972
973 try:
973 try:
974 proto.redirect()
974 proto.redirect()
975
975
976 exchange.check_heads(repo, their_heads, 'preparing changes')
976 exchange.check_heads(repo, their_heads, 'preparing changes')
977
977
978 # write bundle data to temporary file because it can be big
978 # write bundle data to temporary file because it can be big
979 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
979 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
980 fp = os.fdopen(fd, pycompat.sysstr('wb+'))
980 fp = os.fdopen(fd, pycompat.sysstr('wb+'))
981 r = 0
981 r = 0
982 try:
982 try:
983 proto.getfile(fp)
983 proto.getfile(fp)
984 fp.seek(0)
984 fp.seek(0)
985 gen = exchange.readbundle(repo.ui, fp, None)
985 gen = exchange.readbundle(repo.ui, fp, None)
986 if (isinstance(gen, changegroupmod.cg1unpacker)
986 if (isinstance(gen, changegroupmod.cg1unpacker)
987 and not bundle1allowed(repo, 'push')):
987 and not bundle1allowed(repo, 'push')):
988 if proto.name == 'http':
988 if proto.name == 'http':
989 # need to special case http because stderr do not get to
989 # need to special case http because stderr do not get to
990 # the http client on failed push so we need to abuse some
990 # the http client on failed push so we need to abuse some
991 # other error type to make sure the message get to the
991 # other error type to make sure the message get to the
992 # user.
992 # user.
993 return ooberror(bundle2required)
993 return ooberror(bundle2required)
994 raise error.Abort(bundle2requiredmain,
994 raise error.Abort(bundle2requiredmain,
995 hint=bundle2requiredhint)
995 hint=bundle2requiredhint)
996
996
997 r = exchange.unbundle(repo, gen, their_heads, 'serve',
997 r = exchange.unbundle(repo, gen, their_heads, 'serve',
998 proto._client())
998 proto._client())
999 if util.safehasattr(r, 'addpart'):
999 if util.safehasattr(r, 'addpart'):
1000 # The return looks streamable, we are in the bundle2 case and
1000 # The return looks streamable, we are in the bundle2 case and
1001 # should return a stream.
1001 # should return a stream.
1002 return streamres(gen=r.getchunks())
1002 return streamres(gen=r.getchunks())
1003 return pushres(r)
1003 return pushres(r)
1004
1004
1005 finally:
1005 finally:
1006 fp.close()
1006 fp.close()
1007 os.unlink(tempname)
1007 os.unlink(tempname)
1008
1008
1009 except (error.BundleValueError, error.Abort, error.PushRaced) as exc:
1009 except (error.BundleValueError, error.Abort, error.PushRaced) as exc:
1010 # handle non-bundle2 case first
1010 # handle non-bundle2 case first
1011 if not getattr(exc, 'duringunbundle2', False):
1011 if not getattr(exc, 'duringunbundle2', False):
1012 try:
1012 try:
1013 raise
1013 raise
1014 except error.Abort:
1014 except error.Abort:
1015 # The old code we moved used util.stderr directly.
1015 # The old code we moved used util.stderr directly.
1016 # We did not change it to minimise code change.
1016 # We did not change it to minimise code change.
1017 # This need to be moved to something proper.
1017 # This need to be moved to something proper.
1018 # Feel free to do it.
1018 # Feel free to do it.
1019 util.stderr.write("abort: %s\n" % exc)
1019 util.stderr.write("abort: %s\n" % exc)
1020 if exc.hint is not None:
1020 if exc.hint is not None:
1021 util.stderr.write("(%s)\n" % exc.hint)
1021 util.stderr.write("(%s)\n" % exc.hint)
1022 return pushres(0)
1022 return pushres(0)
1023 except error.PushRaced:
1023 except error.PushRaced:
1024 return pusherr(str(exc))
1024 return pusherr(str(exc))
1025
1025
1026 bundler = bundle2.bundle20(repo.ui)
1026 bundler = bundle2.bundle20(repo.ui)
1027 for out in getattr(exc, '_bundle2salvagedoutput', ()):
1027 for out in getattr(exc, '_bundle2salvagedoutput', ()):
1028 bundler.addpart(out)
1028 bundler.addpart(out)
1029 try:
1029 try:
1030 try:
1030 try:
1031 raise
1031 raise
1032 except error.PushkeyFailed as exc:
1032 except error.PushkeyFailed as exc:
1033 # check client caps
1033 # check client caps
1034 remotecaps = getattr(exc, '_replycaps', None)
1034 remotecaps = getattr(exc, '_replycaps', None)
1035 if (remotecaps is not None
1035 if (remotecaps is not None
1036 and 'pushkey' not in remotecaps.get('error', ())):
1036 and 'pushkey' not in remotecaps.get('error', ())):
1037 # no support remote side, fallback to Abort handler.
1037 # no support remote side, fallback to Abort handler.
1038 raise
1038 raise
1039 part = bundler.newpart('error:pushkey')
1039 part = bundler.newpart('error:pushkey')
1040 part.addparam('in-reply-to', exc.partid)
1040 part.addparam('in-reply-to', exc.partid)
1041 if exc.namespace is not None:
1041 if exc.namespace is not None:
1042 part.addparam('namespace', exc.namespace, mandatory=False)
1042 part.addparam('namespace', exc.namespace, mandatory=False)
1043 if exc.key is not None:
1043 if exc.key is not None:
1044 part.addparam('key', exc.key, mandatory=False)
1044 part.addparam('key', exc.key, mandatory=False)
1045 if exc.new is not None:
1045 if exc.new is not None:
1046 part.addparam('new', exc.new, mandatory=False)
1046 part.addparam('new', exc.new, mandatory=False)
1047 if exc.old is not None:
1047 if exc.old is not None:
1048 part.addparam('old', exc.old, mandatory=False)
1048 part.addparam('old', exc.old, mandatory=False)
1049 if exc.ret is not None:
1049 if exc.ret is not None:
1050 part.addparam('ret', exc.ret, mandatory=False)
1050 part.addparam('ret', exc.ret, mandatory=False)
1051 except error.BundleValueError as exc:
1051 except error.BundleValueError as exc:
1052 errpart = bundler.newpart('error:unsupportedcontent')
1052 errpart = bundler.newpart('error:unsupportedcontent')
1053 if exc.parttype is not None:
1053 if exc.parttype is not None:
1054 errpart.addparam('parttype', exc.parttype)
1054 errpart.addparam('parttype', exc.parttype)
1055 if exc.params:
1055 if exc.params:
1056 errpart.addparam('params', '\0'.join(exc.params))
1056 errpart.addparam('params', '\0'.join(exc.params))
1057 except error.Abort as exc:
1057 except error.Abort as exc:
1058 manargs = [('message', str(exc))]
1058 manargs = [('message', str(exc))]
1059 advargs = []
1059 advargs = []
1060 if exc.hint is not None:
1060 if exc.hint is not None:
1061 advargs.append(('hint', exc.hint))
1061 advargs.append(('hint', exc.hint))
1062 bundler.addpart(bundle2.bundlepart('error:abort',
1062 bundler.addpart(bundle2.bundlepart('error:abort',
1063 manargs, advargs))
1063 manargs, advargs))
1064 except error.PushRaced as exc:
1064 except error.PushRaced as exc:
1065 bundler.newpart('error:pushraced', [('message', str(exc))])
1065 bundler.newpart('error:pushraced', [('message', str(exc))])
1066 return streamres(gen=bundler.getchunks())
1066 return streamres(gen=bundler.getchunks())
General Comments 0
You need to be logged in to leave comments. Login now