##// END OF EJS Templates
configitems: register the 'web.maxchanges' config
Boris Feld -
r34591:95f4e5b1 default
parent child Browse files
Show More
@@ -1,785 +1,788 b''
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', 'charsets',
194 coreconfigitem('email', 'charsets',
195 default=list,
195 default=list,
196 )
196 )
197 coreconfigitem('email', 'from',
197 coreconfigitem('email', 'from',
198 default=None,
198 default=None,
199 )
199 )
200 coreconfigitem('email', 'method',
200 coreconfigitem('email', 'method',
201 default='smtp',
201 default='smtp',
202 )
202 )
203 coreconfigitem('experimental', 'allowdivergence',
203 coreconfigitem('experimental', 'allowdivergence',
204 default=False,
204 default=False,
205 )
205 )
206 coreconfigitem('experimental', 'bundle-phases',
206 coreconfigitem('experimental', 'bundle-phases',
207 default=False,
207 default=False,
208 )
208 )
209 coreconfigitem('experimental', 'bundle2-advertise',
209 coreconfigitem('experimental', 'bundle2-advertise',
210 default=True,
210 default=True,
211 )
211 )
212 coreconfigitem('experimental', 'bundle2-output-capture',
212 coreconfigitem('experimental', 'bundle2-output-capture',
213 default=False,
213 default=False,
214 )
214 )
215 coreconfigitem('experimental', 'bundle2.pushback',
215 coreconfigitem('experimental', 'bundle2.pushback',
216 default=False,
216 default=False,
217 )
217 )
218 coreconfigitem('experimental', 'bundle2lazylocking',
218 coreconfigitem('experimental', 'bundle2lazylocking',
219 default=False,
219 default=False,
220 )
220 )
221 coreconfigitem('experimental', 'bundlecomplevel',
221 coreconfigitem('experimental', 'bundlecomplevel',
222 default=None,
222 default=None,
223 )
223 )
224 coreconfigitem('experimental', 'changegroup3',
224 coreconfigitem('experimental', 'changegroup3',
225 default=False,
225 default=False,
226 )
226 )
227 coreconfigitem('experimental', 'clientcompressionengines',
227 coreconfigitem('experimental', 'clientcompressionengines',
228 default=list,
228 default=list,
229 )
229 )
230 coreconfigitem('experimental', 'copytrace',
230 coreconfigitem('experimental', 'copytrace',
231 default='on',
231 default='on',
232 )
232 )
233 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
233 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
234 default=100,
234 default=100,
235 )
235 )
236 coreconfigitem('experimental', 'crecordtest',
236 coreconfigitem('experimental', 'crecordtest',
237 default=None,
237 default=None,
238 )
238 )
239 coreconfigitem('experimental', 'editortmpinhg',
239 coreconfigitem('experimental', 'editortmpinhg',
240 default=False,
240 default=False,
241 )
241 )
242 coreconfigitem('experimental', 'maxdeltachainspan',
242 coreconfigitem('experimental', 'maxdeltachainspan',
243 default=-1,
243 default=-1,
244 )
244 )
245 coreconfigitem('experimental', 'mmapindexthreshold',
245 coreconfigitem('experimental', 'mmapindexthreshold',
246 default=None,
246 default=None,
247 )
247 )
248 coreconfigitem('experimental', 'nonnormalparanoidcheck',
248 coreconfigitem('experimental', 'nonnormalparanoidcheck',
249 default=False,
249 default=False,
250 )
250 )
251 coreconfigitem('experimental', 'stabilization',
251 coreconfigitem('experimental', 'stabilization',
252 default=list,
252 default=list,
253 alias=[('experimental', 'evolution')],
253 alias=[('experimental', 'evolution')],
254 )
254 )
255 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
255 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
256 default=False,
256 default=False,
257 alias=[('experimental', 'evolution.bundle-obsmarker')],
257 alias=[('experimental', 'evolution.bundle-obsmarker')],
258 )
258 )
259 coreconfigitem('experimental', 'stabilization.track-operation',
259 coreconfigitem('experimental', 'stabilization.track-operation',
260 default=True,
260 default=True,
261 alias=[('experimental', 'evolution.track-operation')]
261 alias=[('experimental', 'evolution.track-operation')]
262 )
262 )
263 coreconfigitem('experimental', 'exportableenviron',
263 coreconfigitem('experimental', 'exportableenviron',
264 default=list,
264 default=list,
265 )
265 )
266 coreconfigitem('experimental', 'extendedheader.index',
266 coreconfigitem('experimental', 'extendedheader.index',
267 default=None,
267 default=None,
268 )
268 )
269 coreconfigitem('experimental', 'extendedheader.similarity',
269 coreconfigitem('experimental', 'extendedheader.similarity',
270 default=False,
270 default=False,
271 )
271 )
272 coreconfigitem('experimental', 'format.compression',
272 coreconfigitem('experimental', 'format.compression',
273 default='zlib',
273 default='zlib',
274 )
274 )
275 coreconfigitem('experimental', 'graphshorten',
275 coreconfigitem('experimental', 'graphshorten',
276 default=False,
276 default=False,
277 )
277 )
278 coreconfigitem('experimental', 'graphstyle.parent',
278 coreconfigitem('experimental', 'graphstyle.parent',
279 default=dynamicdefault,
279 default=dynamicdefault,
280 )
280 )
281 coreconfigitem('experimental', 'graphstyle.missing',
281 coreconfigitem('experimental', 'graphstyle.missing',
282 default=dynamicdefault,
282 default=dynamicdefault,
283 )
283 )
284 coreconfigitem('experimental', 'graphstyle.grandparent',
284 coreconfigitem('experimental', 'graphstyle.grandparent',
285 default=dynamicdefault,
285 default=dynamicdefault,
286 )
286 )
287 coreconfigitem('experimental', 'hook-track-tags',
287 coreconfigitem('experimental', 'hook-track-tags',
288 default=False,
288 default=False,
289 )
289 )
290 coreconfigitem('experimental', 'httppostargs',
290 coreconfigitem('experimental', 'httppostargs',
291 default=False,
291 default=False,
292 )
292 )
293 coreconfigitem('experimental', 'manifestv2',
293 coreconfigitem('experimental', 'manifestv2',
294 default=False,
294 default=False,
295 )
295 )
296 coreconfigitem('experimental', 'mergedriver',
296 coreconfigitem('experimental', 'mergedriver',
297 default=None,
297 default=None,
298 )
298 )
299 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
299 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
300 default=False,
300 default=False,
301 )
301 )
302 coreconfigitem('experimental', 'rebase.multidest',
302 coreconfigitem('experimental', 'rebase.multidest',
303 default=False,
303 default=False,
304 )
304 )
305 coreconfigitem('experimental', 'revertalternateinteractivemode',
305 coreconfigitem('experimental', 'revertalternateinteractivemode',
306 default=True,
306 default=True,
307 )
307 )
308 coreconfigitem('experimental', 'revlogv2',
308 coreconfigitem('experimental', 'revlogv2',
309 default=None,
309 default=None,
310 )
310 )
311 coreconfigitem('experimental', 'spacemovesdown',
311 coreconfigitem('experimental', 'spacemovesdown',
312 default=False,
312 default=False,
313 )
313 )
314 coreconfigitem('experimental', 'treemanifest',
314 coreconfigitem('experimental', 'treemanifest',
315 default=False,
315 default=False,
316 )
316 )
317 coreconfigitem('experimental', 'updatecheck',
317 coreconfigitem('experimental', 'updatecheck',
318 default=None,
318 default=None,
319 )
319 )
320 coreconfigitem('format', 'aggressivemergedeltas',
320 coreconfigitem('format', 'aggressivemergedeltas',
321 default=False,
321 default=False,
322 )
322 )
323 coreconfigitem('format', 'chunkcachesize',
323 coreconfigitem('format', 'chunkcachesize',
324 default=None,
324 default=None,
325 )
325 )
326 coreconfigitem('format', 'dotencode',
326 coreconfigitem('format', 'dotencode',
327 default=True,
327 default=True,
328 )
328 )
329 coreconfigitem('format', 'generaldelta',
329 coreconfigitem('format', 'generaldelta',
330 default=False,
330 default=False,
331 )
331 )
332 coreconfigitem('format', 'manifestcachesize',
332 coreconfigitem('format', 'manifestcachesize',
333 default=None,
333 default=None,
334 )
334 )
335 coreconfigitem('format', 'maxchainlen',
335 coreconfigitem('format', 'maxchainlen',
336 default=None,
336 default=None,
337 )
337 )
338 coreconfigitem('format', 'obsstore-version',
338 coreconfigitem('format', 'obsstore-version',
339 default=None,
339 default=None,
340 )
340 )
341 coreconfigitem('format', 'usefncache',
341 coreconfigitem('format', 'usefncache',
342 default=True,
342 default=True,
343 )
343 )
344 coreconfigitem('format', 'usegeneraldelta',
344 coreconfigitem('format', 'usegeneraldelta',
345 default=True,
345 default=True,
346 )
346 )
347 coreconfigitem('format', 'usestore',
347 coreconfigitem('format', 'usestore',
348 default=True,
348 default=True,
349 )
349 )
350 coreconfigitem('hostsecurity', 'ciphers',
350 coreconfigitem('hostsecurity', 'ciphers',
351 default=None,
351 default=None,
352 )
352 )
353 coreconfigitem('hostsecurity', 'disabletls10warning',
353 coreconfigitem('hostsecurity', 'disabletls10warning',
354 default=False,
354 default=False,
355 )
355 )
356 coreconfigitem('http_proxy', 'always',
356 coreconfigitem('http_proxy', 'always',
357 default=False,
357 default=False,
358 )
358 )
359 coreconfigitem('http_proxy', 'host',
359 coreconfigitem('http_proxy', 'host',
360 default=None,
360 default=None,
361 )
361 )
362 coreconfigitem('http_proxy', 'no',
362 coreconfigitem('http_proxy', 'no',
363 default=list,
363 default=list,
364 )
364 )
365 coreconfigitem('http_proxy', 'passwd',
365 coreconfigitem('http_proxy', 'passwd',
366 default=None,
366 default=None,
367 )
367 )
368 coreconfigitem('http_proxy', 'user',
368 coreconfigitem('http_proxy', 'user',
369 default=None,
369 default=None,
370 )
370 )
371 coreconfigitem('merge', 'checkunknown',
371 coreconfigitem('merge', 'checkunknown',
372 default='abort',
372 default='abort',
373 )
373 )
374 coreconfigitem('merge', 'checkignored',
374 coreconfigitem('merge', 'checkignored',
375 default='abort',
375 default='abort',
376 )
376 )
377 coreconfigitem('merge', 'followcopies',
377 coreconfigitem('merge', 'followcopies',
378 default=True,
378 default=True,
379 )
379 )
380 coreconfigitem('merge', 'preferancestor',
380 coreconfigitem('merge', 'preferancestor',
381 default=lambda: ['*'],
381 default=lambda: ['*'],
382 )
382 )
383 coreconfigitem('pager', 'ignore',
383 coreconfigitem('pager', 'ignore',
384 default=list,
384 default=list,
385 )
385 )
386 coreconfigitem('patch', 'eol',
386 coreconfigitem('patch', 'eol',
387 default='strict',
387 default='strict',
388 )
388 )
389 coreconfigitem('patch', 'fuzz',
389 coreconfigitem('patch', 'fuzz',
390 default=2,
390 default=2,
391 )
391 )
392 coreconfigitem('paths', 'default',
392 coreconfigitem('paths', 'default',
393 default=None,
393 default=None,
394 )
394 )
395 coreconfigitem('paths', 'default-push',
395 coreconfigitem('paths', 'default-push',
396 default=None,
396 default=None,
397 )
397 )
398 coreconfigitem('phases', 'checksubrepos',
398 coreconfigitem('phases', 'checksubrepos',
399 default='follow',
399 default='follow',
400 )
400 )
401 coreconfigitem('phases', 'new-commit',
401 coreconfigitem('phases', 'new-commit',
402 default='draft',
402 default='draft',
403 )
403 )
404 coreconfigitem('phases', 'publish',
404 coreconfigitem('phases', 'publish',
405 default=True,
405 default=True,
406 )
406 )
407 coreconfigitem('profiling', 'enabled',
407 coreconfigitem('profiling', 'enabled',
408 default=False,
408 default=False,
409 )
409 )
410 coreconfigitem('profiling', 'format',
410 coreconfigitem('profiling', 'format',
411 default='text',
411 default='text',
412 )
412 )
413 coreconfigitem('profiling', 'freq',
413 coreconfigitem('profiling', 'freq',
414 default=1000,
414 default=1000,
415 )
415 )
416 coreconfigitem('profiling', 'limit',
416 coreconfigitem('profiling', 'limit',
417 default=30,
417 default=30,
418 )
418 )
419 coreconfigitem('profiling', 'nested',
419 coreconfigitem('profiling', 'nested',
420 default=0,
420 default=0,
421 )
421 )
422 coreconfigitem('profiling', 'output',
422 coreconfigitem('profiling', 'output',
423 default=None,
423 default=None,
424 )
424 )
425 coreconfigitem('profiling', 'showmax',
425 coreconfigitem('profiling', 'showmax',
426 default=0.999,
426 default=0.999,
427 )
427 )
428 coreconfigitem('profiling', 'showmin',
428 coreconfigitem('profiling', 'showmin',
429 default=dynamicdefault,
429 default=dynamicdefault,
430 )
430 )
431 coreconfigitem('profiling', 'sort',
431 coreconfigitem('profiling', 'sort',
432 default='inlinetime',
432 default='inlinetime',
433 )
433 )
434 coreconfigitem('profiling', 'statformat',
434 coreconfigitem('profiling', 'statformat',
435 default='hotpath',
435 default='hotpath',
436 )
436 )
437 coreconfigitem('profiling', 'type',
437 coreconfigitem('profiling', 'type',
438 default='stat',
438 default='stat',
439 )
439 )
440 coreconfigitem('progress', 'assume-tty',
440 coreconfigitem('progress', 'assume-tty',
441 default=False,
441 default=False,
442 )
442 )
443 coreconfigitem('progress', 'changedelay',
443 coreconfigitem('progress', 'changedelay',
444 default=1,
444 default=1,
445 )
445 )
446 coreconfigitem('progress', 'clear-complete',
446 coreconfigitem('progress', 'clear-complete',
447 default=True,
447 default=True,
448 )
448 )
449 coreconfigitem('progress', 'debug',
449 coreconfigitem('progress', 'debug',
450 default=False,
450 default=False,
451 )
451 )
452 coreconfigitem('progress', 'delay',
452 coreconfigitem('progress', 'delay',
453 default=3,
453 default=3,
454 )
454 )
455 coreconfigitem('progress', 'disable',
455 coreconfigitem('progress', 'disable',
456 default=False,
456 default=False,
457 )
457 )
458 coreconfigitem('progress', 'estimateinterval',
458 coreconfigitem('progress', 'estimateinterval',
459 default=60.0,
459 default=60.0,
460 )
460 )
461 coreconfigitem('progress', 'refresh',
461 coreconfigitem('progress', 'refresh',
462 default=0.1,
462 default=0.1,
463 )
463 )
464 coreconfigitem('progress', 'width',
464 coreconfigitem('progress', 'width',
465 default=dynamicdefault,
465 default=dynamicdefault,
466 )
466 )
467 coreconfigitem('push', 'pushvars.server',
467 coreconfigitem('push', 'pushvars.server',
468 default=False,
468 default=False,
469 )
469 )
470 coreconfigitem('server', 'bundle1',
470 coreconfigitem('server', 'bundle1',
471 default=True,
471 default=True,
472 )
472 )
473 coreconfigitem('server', 'bundle1gd',
473 coreconfigitem('server', 'bundle1gd',
474 default=None,
474 default=None,
475 )
475 )
476 coreconfigitem('server', 'compressionengines',
476 coreconfigitem('server', 'compressionengines',
477 default=list,
477 default=list,
478 )
478 )
479 coreconfigitem('server', 'concurrent-push-mode',
479 coreconfigitem('server', 'concurrent-push-mode',
480 default='strict',
480 default='strict',
481 )
481 )
482 coreconfigitem('server', 'disablefullbundle',
482 coreconfigitem('server', 'disablefullbundle',
483 default=False,
483 default=False,
484 )
484 )
485 coreconfigitem('server', 'maxhttpheaderlen',
485 coreconfigitem('server', 'maxhttpheaderlen',
486 default=1024,
486 default=1024,
487 )
487 )
488 coreconfigitem('server', 'preferuncompressed',
488 coreconfigitem('server', 'preferuncompressed',
489 default=False,
489 default=False,
490 )
490 )
491 coreconfigitem('server', 'uncompressed',
491 coreconfigitem('server', 'uncompressed',
492 default=True,
492 default=True,
493 )
493 )
494 coreconfigitem('server', 'uncompressedallowsecret',
494 coreconfigitem('server', 'uncompressedallowsecret',
495 default=False,
495 default=False,
496 )
496 )
497 coreconfigitem('server', 'validate',
497 coreconfigitem('server', 'validate',
498 default=False,
498 default=False,
499 )
499 )
500 coreconfigitem('server', 'zliblevel',
500 coreconfigitem('server', 'zliblevel',
501 default=-1,
501 default=-1,
502 )
502 )
503 coreconfigitem('smtp', 'host',
503 coreconfigitem('smtp', 'host',
504 default=None,
504 default=None,
505 )
505 )
506 coreconfigitem('smtp', 'local_hostname',
506 coreconfigitem('smtp', 'local_hostname',
507 default=None,
507 default=None,
508 )
508 )
509 coreconfigitem('smtp', 'password',
509 coreconfigitem('smtp', 'password',
510 default=None,
510 default=None,
511 )
511 )
512 coreconfigitem('smtp', 'port',
512 coreconfigitem('smtp', 'port',
513 default=dynamicdefault,
513 default=dynamicdefault,
514 )
514 )
515 coreconfigitem('smtp', 'tls',
515 coreconfigitem('smtp', 'tls',
516 default='none',
516 default='none',
517 )
517 )
518 coreconfigitem('smtp', 'username',
518 coreconfigitem('smtp', 'username',
519 default=None,
519 default=None,
520 )
520 )
521 coreconfigitem('sparse', 'missingwarning',
521 coreconfigitem('sparse', 'missingwarning',
522 default=True,
522 default=True,
523 )
523 )
524 coreconfigitem('trusted', 'groups',
524 coreconfigitem('trusted', 'groups',
525 default=list,
525 default=list,
526 )
526 )
527 coreconfigitem('trusted', 'users',
527 coreconfigitem('trusted', 'users',
528 default=list,
528 default=list,
529 )
529 )
530 coreconfigitem('ui', '_usedassubrepo',
530 coreconfigitem('ui', '_usedassubrepo',
531 default=False,
531 default=False,
532 )
532 )
533 coreconfigitem('ui', 'allowemptycommit',
533 coreconfigitem('ui', 'allowemptycommit',
534 default=False,
534 default=False,
535 )
535 )
536 coreconfigitem('ui', 'archivemeta',
536 coreconfigitem('ui', 'archivemeta',
537 default=True,
537 default=True,
538 )
538 )
539 coreconfigitem('ui', 'askusername',
539 coreconfigitem('ui', 'askusername',
540 default=False,
540 default=False,
541 )
541 )
542 coreconfigitem('ui', 'clonebundlefallback',
542 coreconfigitem('ui', 'clonebundlefallback',
543 default=False,
543 default=False,
544 )
544 )
545 coreconfigitem('ui', 'clonebundleprefers',
545 coreconfigitem('ui', 'clonebundleprefers',
546 default=list,
546 default=list,
547 )
547 )
548 coreconfigitem('ui', 'clonebundles',
548 coreconfigitem('ui', 'clonebundles',
549 default=True,
549 default=True,
550 )
550 )
551 coreconfigitem('ui', 'color',
551 coreconfigitem('ui', 'color',
552 default='auto',
552 default='auto',
553 )
553 )
554 coreconfigitem('ui', 'commitsubrepos',
554 coreconfigitem('ui', 'commitsubrepos',
555 default=False,
555 default=False,
556 )
556 )
557 coreconfigitem('ui', 'debug',
557 coreconfigitem('ui', 'debug',
558 default=False,
558 default=False,
559 )
559 )
560 coreconfigitem('ui', 'debugger',
560 coreconfigitem('ui', 'debugger',
561 default=None,
561 default=None,
562 )
562 )
563 coreconfigitem('ui', 'fallbackencoding',
563 coreconfigitem('ui', 'fallbackencoding',
564 default=None,
564 default=None,
565 )
565 )
566 coreconfigitem('ui', 'forcecwd',
566 coreconfigitem('ui', 'forcecwd',
567 default=None,
567 default=None,
568 )
568 )
569 coreconfigitem('ui', 'forcemerge',
569 coreconfigitem('ui', 'forcemerge',
570 default=None,
570 default=None,
571 )
571 )
572 coreconfigitem('ui', 'formatdebug',
572 coreconfigitem('ui', 'formatdebug',
573 default=False,
573 default=False,
574 )
574 )
575 coreconfigitem('ui', 'formatjson',
575 coreconfigitem('ui', 'formatjson',
576 default=False,
576 default=False,
577 )
577 )
578 coreconfigitem('ui', 'formatted',
578 coreconfigitem('ui', 'formatted',
579 default=None,
579 default=None,
580 )
580 )
581 coreconfigitem('ui', 'graphnodetemplate',
581 coreconfigitem('ui', 'graphnodetemplate',
582 default=None,
582 default=None,
583 )
583 )
584 coreconfigitem('ui', 'http2debuglevel',
584 coreconfigitem('ui', 'http2debuglevel',
585 default=None,
585 default=None,
586 )
586 )
587 coreconfigitem('ui', 'interactive',
587 coreconfigitem('ui', 'interactive',
588 default=None,
588 default=None,
589 )
589 )
590 coreconfigitem('ui', 'interface',
590 coreconfigitem('ui', 'interface',
591 default=None,
591 default=None,
592 )
592 )
593 coreconfigitem('ui', 'logblockedtimes',
593 coreconfigitem('ui', 'logblockedtimes',
594 default=False,
594 default=False,
595 )
595 )
596 coreconfigitem('ui', 'logtemplate',
596 coreconfigitem('ui', 'logtemplate',
597 default=None,
597 default=None,
598 )
598 )
599 coreconfigitem('ui', 'merge',
599 coreconfigitem('ui', 'merge',
600 default=None,
600 default=None,
601 )
601 )
602 coreconfigitem('ui', 'mergemarkers',
602 coreconfigitem('ui', 'mergemarkers',
603 default='basic',
603 default='basic',
604 )
604 )
605 coreconfigitem('ui', 'mergemarkertemplate',
605 coreconfigitem('ui', 'mergemarkertemplate',
606 default=('{node|short} '
606 default=('{node|short} '
607 '{ifeq(tags, "tip", "", '
607 '{ifeq(tags, "tip", "", '
608 'ifeq(tags, "", "", "{tags} "))}'
608 'ifeq(tags, "", "", "{tags} "))}'
609 '{if(bookmarks, "{bookmarks} ")}'
609 '{if(bookmarks, "{bookmarks} ")}'
610 '{ifeq(branch, "default", "", "{branch} ")}'
610 '{ifeq(branch, "default", "", "{branch} ")}'
611 '- {author|user}: {desc|firstline}')
611 '- {author|user}: {desc|firstline}')
612 )
612 )
613 coreconfigitem('ui', 'nontty',
613 coreconfigitem('ui', 'nontty',
614 default=False,
614 default=False,
615 )
615 )
616 coreconfigitem('ui', 'origbackuppath',
616 coreconfigitem('ui', 'origbackuppath',
617 default=None,
617 default=None,
618 )
618 )
619 coreconfigitem('ui', 'paginate',
619 coreconfigitem('ui', 'paginate',
620 default=True,
620 default=True,
621 )
621 )
622 coreconfigitem('ui', 'patch',
622 coreconfigitem('ui', 'patch',
623 default=None,
623 default=None,
624 )
624 )
625 coreconfigitem('ui', 'portablefilenames',
625 coreconfigitem('ui', 'portablefilenames',
626 default='warn',
626 default='warn',
627 )
627 )
628 coreconfigitem('ui', 'promptecho',
628 coreconfigitem('ui', 'promptecho',
629 default=False,
629 default=False,
630 )
630 )
631 coreconfigitem('ui', 'quiet',
631 coreconfigitem('ui', 'quiet',
632 default=False,
632 default=False,
633 )
633 )
634 coreconfigitem('ui', 'quietbookmarkmove',
634 coreconfigitem('ui', 'quietbookmarkmove',
635 default=False,
635 default=False,
636 )
636 )
637 coreconfigitem('ui', 'remotecmd',
637 coreconfigitem('ui', 'remotecmd',
638 default='hg',
638 default='hg',
639 )
639 )
640 coreconfigitem('ui', 'report_untrusted',
640 coreconfigitem('ui', 'report_untrusted',
641 default=True,
641 default=True,
642 )
642 )
643 coreconfigitem('ui', 'rollback',
643 coreconfigitem('ui', 'rollback',
644 default=True,
644 default=True,
645 )
645 )
646 coreconfigitem('ui', 'slash',
646 coreconfigitem('ui', 'slash',
647 default=False,
647 default=False,
648 )
648 )
649 coreconfigitem('ui', 'ssh',
649 coreconfigitem('ui', 'ssh',
650 default='ssh',
650 default='ssh',
651 )
651 )
652 coreconfigitem('ui', 'statuscopies',
652 coreconfigitem('ui', 'statuscopies',
653 default=False,
653 default=False,
654 )
654 )
655 coreconfigitem('ui', 'strict',
655 coreconfigitem('ui', 'strict',
656 default=False,
656 default=False,
657 )
657 )
658 coreconfigitem('ui', 'style',
658 coreconfigitem('ui', 'style',
659 default='',
659 default='',
660 )
660 )
661 coreconfigitem('ui', 'supportcontact',
661 coreconfigitem('ui', 'supportcontact',
662 default=None,
662 default=None,
663 )
663 )
664 coreconfigitem('ui', 'textwidth',
664 coreconfigitem('ui', 'textwidth',
665 default=78,
665 default=78,
666 )
666 )
667 coreconfigitem('ui', 'timeout',
667 coreconfigitem('ui', 'timeout',
668 default='600',
668 default='600',
669 )
669 )
670 coreconfigitem('ui', 'traceback',
670 coreconfigitem('ui', 'traceback',
671 default=False,
671 default=False,
672 )
672 )
673 coreconfigitem('ui', 'tweakdefaults',
673 coreconfigitem('ui', 'tweakdefaults',
674 default=False,
674 default=False,
675 )
675 )
676 coreconfigitem('ui', 'usehttp2',
676 coreconfigitem('ui', 'usehttp2',
677 default=False,
677 default=False,
678 )
678 )
679 coreconfigitem('ui', 'username',
679 coreconfigitem('ui', 'username',
680 alias=[('ui', 'user')]
680 alias=[('ui', 'user')]
681 )
681 )
682 coreconfigitem('ui', 'verbose',
682 coreconfigitem('ui', 'verbose',
683 default=False,
683 default=False,
684 )
684 )
685 coreconfigitem('verify', 'skipflags',
685 coreconfigitem('verify', 'skipflags',
686 default=None,
686 default=None,
687 )
687 )
688 coreconfigitem('web', 'accesslog',
688 coreconfigitem('web', 'accesslog',
689 default='-',
689 default='-',
690 )
690 )
691 coreconfigitem('web', 'address',
691 coreconfigitem('web', 'address',
692 default='',
692 default='',
693 )
693 )
694 coreconfigitem('web', 'allow_archive',
694 coreconfigitem('web', 'allow_archive',
695 default=list,
695 default=list,
696 )
696 )
697 coreconfigitem('web', 'allow_read',
697 coreconfigitem('web', 'allow_read',
698 default=list,
698 default=list,
699 )
699 )
700 coreconfigitem('web', 'baseurl',
700 coreconfigitem('web', 'baseurl',
701 default=None,
701 default=None,
702 )
702 )
703 coreconfigitem('web', 'cacerts',
703 coreconfigitem('web', 'cacerts',
704 default=None,
704 default=None,
705 )
705 )
706 coreconfigitem('web', 'certificate',
706 coreconfigitem('web', 'certificate',
707 default=None,
707 default=None,
708 )
708 )
709 coreconfigitem('web', 'collapse',
709 coreconfigitem('web', 'collapse',
710 default=False,
710 default=False,
711 )
711 )
712 coreconfigitem('web', 'csp',
712 coreconfigitem('web', 'csp',
713 default=None,
713 default=None,
714 )
714 )
715 coreconfigitem('web', 'deny_read',
715 coreconfigitem('web', 'deny_read',
716 default=list,
716 default=list,
717 )
717 )
718 coreconfigitem('web', 'descend',
718 coreconfigitem('web', 'descend',
719 default=True,
719 default=True,
720 )
720 )
721 coreconfigitem('web', 'description',
721 coreconfigitem('web', 'description',
722 default="",
722 default="",
723 )
723 )
724 coreconfigitem('web', 'encoding',
724 coreconfigitem('web', 'encoding',
725 default=lambda: encoding.encoding,
725 default=lambda: encoding.encoding,
726 )
726 )
727 coreconfigitem('web', 'errorlog',
727 coreconfigitem('web', 'errorlog',
728 default='-',
728 default='-',
729 )
729 )
730 coreconfigitem('web', 'ipv6',
730 coreconfigitem('web', 'ipv6',
731 default=False,
731 default=False,
732 )
732 )
733 coreconfigitem('web', 'maxchanges',
734 default=10,
735 )
733 coreconfigitem('web', 'maxfiles',
736 coreconfigitem('web', 'maxfiles',
734 default=10,
737 default=10,
735 )
738 )
736 coreconfigitem('web', 'maxshortchanges',
739 coreconfigitem('web', 'maxshortchanges',
737 default=60,
740 default=60,
738 )
741 )
739 coreconfigitem('web', 'motd',
742 coreconfigitem('web', 'motd',
740 default='',
743 default='',
741 )
744 )
742 coreconfigitem('web', 'name',
745 coreconfigitem('web', 'name',
743 default=dynamicdefault,
746 default=dynamicdefault,
744 )
747 )
745 coreconfigitem('web', 'port',
748 coreconfigitem('web', 'port',
746 default=8000,
749 default=8000,
747 )
750 )
748 coreconfigitem('web', 'prefix',
751 coreconfigitem('web', 'prefix',
749 default='',
752 default='',
750 )
753 )
751 coreconfigitem('web', 'push_ssl',
754 coreconfigitem('web', 'push_ssl',
752 default=True,
755 default=True,
753 )
756 )
754 coreconfigitem('web', 'refreshinterval',
757 coreconfigitem('web', 'refreshinterval',
755 default=20,
758 default=20,
756 )
759 )
757 coreconfigitem('web', 'stripes',
760 coreconfigitem('web', 'stripes',
758 default=1,
761 default=1,
759 )
762 )
760 coreconfigitem('web', 'style',
763 coreconfigitem('web', 'style',
761 default='paper',
764 default='paper',
762 )
765 )
763 coreconfigitem('web', 'templates',
766 coreconfigitem('web', 'templates',
764 default=None,
767 default=None,
765 )
768 )
766 coreconfigitem('web', 'view',
769 coreconfigitem('web', 'view',
767 default='served',
770 default='served',
768 )
771 )
769 coreconfigitem('worker', 'backgroundclose',
772 coreconfigitem('worker', 'backgroundclose',
770 default=dynamicdefault,
773 default=dynamicdefault,
771 )
774 )
772 # Windows defaults to a limit of 512 open files. A buffer of 128
775 # Windows defaults to a limit of 512 open files. A buffer of 128
773 # should give us enough headway.
776 # should give us enough headway.
774 coreconfigitem('worker', 'backgroundclosemaxqueue',
777 coreconfigitem('worker', 'backgroundclosemaxqueue',
775 default=384,
778 default=384,
776 )
779 )
777 coreconfigitem('worker', 'backgroundcloseminfilecount',
780 coreconfigitem('worker', 'backgroundcloseminfilecount',
778 default=2048,
781 default=2048,
779 )
782 )
780 coreconfigitem('worker', 'backgroundclosethreadcount',
783 coreconfigitem('worker', 'backgroundclosethreadcount',
781 default=4,
784 default=4,
782 )
785 )
783 coreconfigitem('worker', 'numcpus',
786 coreconfigitem('worker', 'numcpus',
784 default=None,
787 default=None,
785 )
788 )
@@ -1,492 +1,492 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import contextlib
11 import contextlib
12 import os
12 import os
13
13
14 from .common import (
14 from .common import (
15 ErrorResponse,
15 ErrorResponse,
16 HTTP_BAD_REQUEST,
16 HTTP_BAD_REQUEST,
17 HTTP_NOT_FOUND,
17 HTTP_NOT_FOUND,
18 HTTP_NOT_MODIFIED,
18 HTTP_NOT_MODIFIED,
19 HTTP_OK,
19 HTTP_OK,
20 HTTP_SERVER_ERROR,
20 HTTP_SERVER_ERROR,
21 caching,
21 caching,
22 cspvalues,
22 cspvalues,
23 permhooks,
23 permhooks,
24 )
24 )
25 from .request import wsgirequest
25 from .request import wsgirequest
26
26
27 from .. import (
27 from .. import (
28 encoding,
28 encoding,
29 error,
29 error,
30 hg,
30 hg,
31 hook,
31 hook,
32 profiling,
32 profiling,
33 pycompat,
33 pycompat,
34 repoview,
34 repoview,
35 templatefilters,
35 templatefilters,
36 templater,
36 templater,
37 ui as uimod,
37 ui as uimod,
38 util,
38 util,
39 )
39 )
40
40
41 from . import (
41 from . import (
42 protocol,
42 protocol,
43 webcommands,
43 webcommands,
44 webutil,
44 webutil,
45 wsgicgi,
45 wsgicgi,
46 )
46 )
47
47
48 perms = {
48 perms = {
49 'changegroup': 'pull',
49 'changegroup': 'pull',
50 'changegroupsubset': 'pull',
50 'changegroupsubset': 'pull',
51 'getbundle': 'pull',
51 'getbundle': 'pull',
52 'stream_out': 'pull',
52 'stream_out': 'pull',
53 'listkeys': 'pull',
53 'listkeys': 'pull',
54 'unbundle': 'push',
54 'unbundle': 'push',
55 'pushkey': 'push',
55 'pushkey': 'push',
56 }
56 }
57
57
58 archivespecs = util.sortdict((
58 archivespecs = util.sortdict((
59 ('zip', ('application/zip', 'zip', '.zip', None)),
59 ('zip', ('application/zip', 'zip', '.zip', None)),
60 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
60 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
61 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
61 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
62 ))
62 ))
63
63
64 def getstyle(req, configfn, templatepath):
64 def getstyle(req, configfn, templatepath):
65 fromreq = req.form.get('style', [None])[0]
65 fromreq = req.form.get('style', [None])[0]
66 if fromreq is not None:
66 if fromreq is not None:
67 fromreq = pycompat.sysbytes(fromreq)
67 fromreq = pycompat.sysbytes(fromreq)
68 styles = (
68 styles = (
69 fromreq,
69 fromreq,
70 configfn('web', 'style'),
70 configfn('web', 'style'),
71 'paper',
71 'paper',
72 )
72 )
73 return styles, templater.stylemap(styles, templatepath)
73 return styles, templater.stylemap(styles, templatepath)
74
74
75 def makebreadcrumb(url, prefix=''):
75 def makebreadcrumb(url, prefix=''):
76 '''Return a 'URL breadcrumb' list
76 '''Return a 'URL breadcrumb' list
77
77
78 A 'URL breadcrumb' is a list of URL-name pairs,
78 A 'URL breadcrumb' is a list of URL-name pairs,
79 corresponding to each of the path items on a URL.
79 corresponding to each of the path items on a URL.
80 This can be used to create path navigation entries.
80 This can be used to create path navigation entries.
81 '''
81 '''
82 if url.endswith('/'):
82 if url.endswith('/'):
83 url = url[:-1]
83 url = url[:-1]
84 if prefix:
84 if prefix:
85 url = '/' + prefix + url
85 url = '/' + prefix + url
86 relpath = url
86 relpath = url
87 if relpath.startswith('/'):
87 if relpath.startswith('/'):
88 relpath = relpath[1:]
88 relpath = relpath[1:]
89
89
90 breadcrumb = []
90 breadcrumb = []
91 urlel = url
91 urlel = url
92 pathitems = [''] + relpath.split('/')
92 pathitems = [''] + relpath.split('/')
93 for pathel in reversed(pathitems):
93 for pathel in reversed(pathitems):
94 if not pathel or not urlel:
94 if not pathel or not urlel:
95 break
95 break
96 breadcrumb.append({'url': urlel, 'name': pathel})
96 breadcrumb.append({'url': urlel, 'name': pathel})
97 urlel = os.path.dirname(urlel)
97 urlel = os.path.dirname(urlel)
98 return reversed(breadcrumb)
98 return reversed(breadcrumb)
99
99
100 class requestcontext(object):
100 class requestcontext(object):
101 """Holds state/context for an individual request.
101 """Holds state/context for an individual request.
102
102
103 Servers can be multi-threaded. Holding state on the WSGI application
103 Servers can be multi-threaded. Holding state on the WSGI application
104 is prone to race conditions. Instances of this class exist to hold
104 is prone to race conditions. Instances of this class exist to hold
105 mutable and race-free state for requests.
105 mutable and race-free state for requests.
106 """
106 """
107 def __init__(self, app, repo):
107 def __init__(self, app, repo):
108 self.repo = repo
108 self.repo = repo
109 self.reponame = app.reponame
109 self.reponame = app.reponame
110
110
111 self.archivespecs = archivespecs
111 self.archivespecs = archivespecs
112
112
113 self.maxchanges = self.configint('web', 'maxchanges', 10)
113 self.maxchanges = self.configint('web', 'maxchanges')
114 self.stripecount = self.configint('web', 'stripes')
114 self.stripecount = self.configint('web', 'stripes')
115 self.maxshortchanges = self.configint('web', 'maxshortchanges')
115 self.maxshortchanges = self.configint('web', 'maxshortchanges')
116 self.maxfiles = self.configint('web', 'maxfiles')
116 self.maxfiles = self.configint('web', 'maxfiles')
117 self.allowpull = self.configbool('web', 'allowpull', True)
117 self.allowpull = self.configbool('web', 'allowpull', True)
118
118
119 # we use untrusted=False to prevent a repo owner from using
119 # we use untrusted=False to prevent a repo owner from using
120 # web.templates in .hg/hgrc to get access to any file readable
120 # web.templates in .hg/hgrc to get access to any file readable
121 # by the user running the CGI script
121 # by the user running the CGI script
122 self.templatepath = self.config('web', 'templates', untrusted=False)
122 self.templatepath = self.config('web', 'templates', untrusted=False)
123
123
124 # This object is more expensive to build than simple config values.
124 # This object is more expensive to build than simple config values.
125 # It is shared across requests. The app will replace the object
125 # It is shared across requests. The app will replace the object
126 # if it is updated. Since this is a reference and nothing should
126 # if it is updated. Since this is a reference and nothing should
127 # modify the underlying object, it should be constant for the lifetime
127 # modify the underlying object, it should be constant for the lifetime
128 # of the request.
128 # of the request.
129 self.websubtable = app.websubtable
129 self.websubtable = app.websubtable
130
130
131 self.csp, self.nonce = cspvalues(self.repo.ui)
131 self.csp, self.nonce = cspvalues(self.repo.ui)
132
132
133 # Trust the settings from the .hg/hgrc files by default.
133 # Trust the settings from the .hg/hgrc files by default.
134 def config(self, section, name, default=uimod._unset, untrusted=True):
134 def config(self, section, name, default=uimod._unset, untrusted=True):
135 return self.repo.ui.config(section, name, default,
135 return self.repo.ui.config(section, name, default,
136 untrusted=untrusted)
136 untrusted=untrusted)
137
137
138 def configbool(self, section, name, default=uimod._unset, untrusted=True):
138 def configbool(self, section, name, default=uimod._unset, untrusted=True):
139 return self.repo.ui.configbool(section, name, default,
139 return self.repo.ui.configbool(section, name, default,
140 untrusted=untrusted)
140 untrusted=untrusted)
141
141
142 def configint(self, section, name, default=uimod._unset, untrusted=True):
142 def configint(self, section, name, default=uimod._unset, untrusted=True):
143 return self.repo.ui.configint(section, name, default,
143 return self.repo.ui.configint(section, name, default,
144 untrusted=untrusted)
144 untrusted=untrusted)
145
145
146 def configlist(self, section, name, default=uimod._unset, untrusted=True):
146 def configlist(self, section, name, default=uimod._unset, untrusted=True):
147 return self.repo.ui.configlist(section, name, default,
147 return self.repo.ui.configlist(section, name, default,
148 untrusted=untrusted)
148 untrusted=untrusted)
149
149
150 def archivelist(self, nodeid):
150 def archivelist(self, nodeid):
151 allowed = self.configlist('web', 'allow_archive')
151 allowed = self.configlist('web', 'allow_archive')
152 for typ, spec in self.archivespecs.iteritems():
152 for typ, spec in self.archivespecs.iteritems():
153 if typ in allowed or self.configbool('web', 'allow%s' % typ):
153 if typ in allowed or self.configbool('web', 'allow%s' % typ):
154 yield {'type': typ, 'extension': spec[2], 'node': nodeid}
154 yield {'type': typ, 'extension': spec[2], 'node': nodeid}
155
155
156 def templater(self, req):
156 def templater(self, req):
157 # determine scheme, port and server name
157 # determine scheme, port and server name
158 # this is needed to create absolute urls
158 # this is needed to create absolute urls
159
159
160 proto = req.env.get('wsgi.url_scheme')
160 proto = req.env.get('wsgi.url_scheme')
161 if proto == 'https':
161 if proto == 'https':
162 proto = 'https'
162 proto = 'https'
163 default_port = '443'
163 default_port = '443'
164 else:
164 else:
165 proto = 'http'
165 proto = 'http'
166 default_port = '80'
166 default_port = '80'
167
167
168 port = req.env['SERVER_PORT']
168 port = req.env['SERVER_PORT']
169 port = port != default_port and (':' + port) or ''
169 port = port != default_port and (':' + port) or ''
170 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
170 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
171 logourl = self.config('web', 'logourl', 'https://mercurial-scm.org/')
171 logourl = self.config('web', 'logourl', 'https://mercurial-scm.org/')
172 logoimg = self.config('web', 'logoimg', 'hglogo.png')
172 logoimg = self.config('web', 'logoimg', 'hglogo.png')
173 staticurl = self.config('web', 'staticurl') or req.url + 'static/'
173 staticurl = self.config('web', 'staticurl') or req.url + 'static/'
174 if not staticurl.endswith('/'):
174 if not staticurl.endswith('/'):
175 staticurl += '/'
175 staticurl += '/'
176
176
177 # some functions for the templater
177 # some functions for the templater
178
178
179 def motd(**map):
179 def motd(**map):
180 yield self.config('web', 'motd')
180 yield self.config('web', 'motd')
181
181
182 # figure out which style to use
182 # figure out which style to use
183
183
184 vars = {}
184 vars = {}
185 styles, (style, mapfile) = getstyle(req, self.config,
185 styles, (style, mapfile) = getstyle(req, self.config,
186 self.templatepath)
186 self.templatepath)
187 if style == styles[0]:
187 if style == styles[0]:
188 vars['style'] = style
188 vars['style'] = style
189
189
190 start = req.url[-1] == '?' and '&' or '?'
190 start = req.url[-1] == '?' and '&' or '?'
191 sessionvars = webutil.sessionvars(vars, start)
191 sessionvars = webutil.sessionvars(vars, start)
192
192
193 if not self.reponame:
193 if not self.reponame:
194 self.reponame = (self.config('web', 'name', '')
194 self.reponame = (self.config('web', 'name', '')
195 or req.env.get('REPO_NAME')
195 or req.env.get('REPO_NAME')
196 or req.url.strip('/') or self.repo.root)
196 or req.url.strip('/') or self.repo.root)
197
197
198 def websubfilter(text):
198 def websubfilter(text):
199 return templatefilters.websub(text, self.websubtable)
199 return templatefilters.websub(text, self.websubtable)
200
200
201 # create the templater
201 # create the templater
202
202
203 defaults = {
203 defaults = {
204 'url': req.url,
204 'url': req.url,
205 'logourl': logourl,
205 'logourl': logourl,
206 'logoimg': logoimg,
206 'logoimg': logoimg,
207 'staticurl': staticurl,
207 'staticurl': staticurl,
208 'urlbase': urlbase,
208 'urlbase': urlbase,
209 'repo': self.reponame,
209 'repo': self.reponame,
210 'encoding': encoding.encoding,
210 'encoding': encoding.encoding,
211 'motd': motd,
211 'motd': motd,
212 'sessionvars': sessionvars,
212 'sessionvars': sessionvars,
213 'pathdef': makebreadcrumb(req.url),
213 'pathdef': makebreadcrumb(req.url),
214 'style': style,
214 'style': style,
215 'nonce': self.nonce,
215 'nonce': self.nonce,
216 }
216 }
217 tmpl = templater.templater.frommapfile(mapfile,
217 tmpl = templater.templater.frommapfile(mapfile,
218 filters={'websub': websubfilter},
218 filters={'websub': websubfilter},
219 defaults=defaults)
219 defaults=defaults)
220 return tmpl
220 return tmpl
221
221
222
222
223 class hgweb(object):
223 class hgweb(object):
224 """HTTP server for individual repositories.
224 """HTTP server for individual repositories.
225
225
226 Instances of this class serve HTTP responses for a particular
226 Instances of this class serve HTTP responses for a particular
227 repository.
227 repository.
228
228
229 Instances are typically used as WSGI applications.
229 Instances are typically used as WSGI applications.
230
230
231 Some servers are multi-threaded. On these servers, there may
231 Some servers are multi-threaded. On these servers, there may
232 be multiple active threads inside __call__.
232 be multiple active threads inside __call__.
233 """
233 """
234 def __init__(self, repo, name=None, baseui=None):
234 def __init__(self, repo, name=None, baseui=None):
235 if isinstance(repo, str):
235 if isinstance(repo, str):
236 if baseui:
236 if baseui:
237 u = baseui.copy()
237 u = baseui.copy()
238 else:
238 else:
239 u = uimod.ui.load()
239 u = uimod.ui.load()
240 r = hg.repository(u, repo)
240 r = hg.repository(u, repo)
241 else:
241 else:
242 # we trust caller to give us a private copy
242 # we trust caller to give us a private copy
243 r = repo
243 r = repo
244
244
245 r.ui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
245 r.ui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
246 r.baseui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
246 r.baseui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
247 r.ui.setconfig('ui', 'nontty', 'true', 'hgweb')
247 r.ui.setconfig('ui', 'nontty', 'true', 'hgweb')
248 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb')
248 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb')
249 # resolve file patterns relative to repo root
249 # resolve file patterns relative to repo root
250 r.ui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
250 r.ui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
251 r.baseui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
251 r.baseui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
252 # displaying bundling progress bar while serving feel wrong and may
252 # displaying bundling progress bar while serving feel wrong and may
253 # break some wsgi implementation.
253 # break some wsgi implementation.
254 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
254 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
255 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
255 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
256 self._repos = [hg.cachedlocalrepo(self._webifyrepo(r))]
256 self._repos = [hg.cachedlocalrepo(self._webifyrepo(r))]
257 self._lastrepo = self._repos[0]
257 self._lastrepo = self._repos[0]
258 hook.redirect(True)
258 hook.redirect(True)
259 self.reponame = name
259 self.reponame = name
260
260
261 def _webifyrepo(self, repo):
261 def _webifyrepo(self, repo):
262 repo = getwebview(repo)
262 repo = getwebview(repo)
263 self.websubtable = webutil.getwebsubs(repo)
263 self.websubtable = webutil.getwebsubs(repo)
264 return repo
264 return repo
265
265
266 @contextlib.contextmanager
266 @contextlib.contextmanager
267 def _obtainrepo(self):
267 def _obtainrepo(self):
268 """Obtain a repo unique to the caller.
268 """Obtain a repo unique to the caller.
269
269
270 Internally we maintain a stack of cachedlocalrepo instances
270 Internally we maintain a stack of cachedlocalrepo instances
271 to be handed out. If one is available, we pop it and return it,
271 to be handed out. If one is available, we pop it and return it,
272 ensuring it is up to date in the process. If one is not available,
272 ensuring it is up to date in the process. If one is not available,
273 we clone the most recently used repo instance and return it.
273 we clone the most recently used repo instance and return it.
274
274
275 It is currently possible for the stack to grow without bounds
275 It is currently possible for the stack to grow without bounds
276 if the server allows infinite threads. However, servers should
276 if the server allows infinite threads. However, servers should
277 have a thread limit, thus establishing our limit.
277 have a thread limit, thus establishing our limit.
278 """
278 """
279 if self._repos:
279 if self._repos:
280 cached = self._repos.pop()
280 cached = self._repos.pop()
281 r, created = cached.fetch()
281 r, created = cached.fetch()
282 else:
282 else:
283 cached = self._lastrepo.copy()
283 cached = self._lastrepo.copy()
284 r, created = cached.fetch()
284 r, created = cached.fetch()
285 if created:
285 if created:
286 r = self._webifyrepo(r)
286 r = self._webifyrepo(r)
287
287
288 self._lastrepo = cached
288 self._lastrepo = cached
289 self.mtime = cached.mtime
289 self.mtime = cached.mtime
290 try:
290 try:
291 yield r
291 yield r
292 finally:
292 finally:
293 self._repos.append(cached)
293 self._repos.append(cached)
294
294
295 def run(self):
295 def run(self):
296 """Start a server from CGI environment.
296 """Start a server from CGI environment.
297
297
298 Modern servers should be using WSGI and should avoid this
298 Modern servers should be using WSGI and should avoid this
299 method, if possible.
299 method, if possible.
300 """
300 """
301 if not encoding.environ.get('GATEWAY_INTERFACE',
301 if not encoding.environ.get('GATEWAY_INTERFACE',
302 '').startswith("CGI/1."):
302 '').startswith("CGI/1."):
303 raise RuntimeError("This function is only intended to be "
303 raise RuntimeError("This function is only intended to be "
304 "called while running as a CGI script.")
304 "called while running as a CGI script.")
305 wsgicgi.launch(self)
305 wsgicgi.launch(self)
306
306
307 def __call__(self, env, respond):
307 def __call__(self, env, respond):
308 """Run the WSGI application.
308 """Run the WSGI application.
309
309
310 This may be called by multiple threads.
310 This may be called by multiple threads.
311 """
311 """
312 req = wsgirequest(env, respond)
312 req = wsgirequest(env, respond)
313 return self.run_wsgi(req)
313 return self.run_wsgi(req)
314
314
315 def run_wsgi(self, req):
315 def run_wsgi(self, req):
316 """Internal method to run the WSGI application.
316 """Internal method to run the WSGI application.
317
317
318 This is typically only called by Mercurial. External consumers
318 This is typically only called by Mercurial. External consumers
319 should be using instances of this class as the WSGI application.
319 should be using instances of this class as the WSGI application.
320 """
320 """
321 with self._obtainrepo() as repo:
321 with self._obtainrepo() as repo:
322 profile = repo.ui.configbool('profiling', 'enabled')
322 profile = repo.ui.configbool('profiling', 'enabled')
323 with profiling.profile(repo.ui, enabled=profile):
323 with profiling.profile(repo.ui, enabled=profile):
324 for r in self._runwsgi(req, repo):
324 for r in self._runwsgi(req, repo):
325 yield r
325 yield r
326
326
327 def _runwsgi(self, req, repo):
327 def _runwsgi(self, req, repo):
328 rctx = requestcontext(self, repo)
328 rctx = requestcontext(self, repo)
329
329
330 # This state is global across all threads.
330 # This state is global across all threads.
331 encoding.encoding = rctx.config('web', 'encoding')
331 encoding.encoding = rctx.config('web', 'encoding')
332 rctx.repo.ui.environ = req.env
332 rctx.repo.ui.environ = req.env
333
333
334 if rctx.csp:
334 if rctx.csp:
335 # hgwebdir may have added CSP header. Since we generate our own,
335 # hgwebdir may have added CSP header. Since we generate our own,
336 # replace it.
336 # replace it.
337 req.headers = [h for h in req.headers
337 req.headers = [h for h in req.headers
338 if h[0] != 'Content-Security-Policy']
338 if h[0] != 'Content-Security-Policy']
339 req.headers.append(('Content-Security-Policy', rctx.csp))
339 req.headers.append(('Content-Security-Policy', rctx.csp))
340
340
341 # work with CGI variables to create coherent structure
341 # work with CGI variables to create coherent structure
342 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
342 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
343
343
344 req.url = req.env['SCRIPT_NAME']
344 req.url = req.env['SCRIPT_NAME']
345 if not req.url.endswith('/'):
345 if not req.url.endswith('/'):
346 req.url += '/'
346 req.url += '/'
347 if req.env.get('REPO_NAME'):
347 if req.env.get('REPO_NAME'):
348 req.url += req.env['REPO_NAME'] + '/'
348 req.url += req.env['REPO_NAME'] + '/'
349
349
350 if 'PATH_INFO' in req.env:
350 if 'PATH_INFO' in req.env:
351 parts = req.env['PATH_INFO'].strip('/').split('/')
351 parts = req.env['PATH_INFO'].strip('/').split('/')
352 repo_parts = req.env.get('REPO_NAME', '').split('/')
352 repo_parts = req.env.get('REPO_NAME', '').split('/')
353 if parts[:len(repo_parts)] == repo_parts:
353 if parts[:len(repo_parts)] == repo_parts:
354 parts = parts[len(repo_parts):]
354 parts = parts[len(repo_parts):]
355 query = '/'.join(parts)
355 query = '/'.join(parts)
356 else:
356 else:
357 query = req.env['QUERY_STRING'].partition('&')[0]
357 query = req.env['QUERY_STRING'].partition('&')[0]
358 query = query.partition(';')[0]
358 query = query.partition(';')[0]
359
359
360 # process this if it's a protocol request
360 # process this if it's a protocol request
361 # protocol bits don't need to create any URLs
361 # protocol bits don't need to create any URLs
362 # and the clients always use the old URL structure
362 # and the clients always use the old URL structure
363
363
364 cmd = req.form.get('cmd', [''])[0]
364 cmd = req.form.get('cmd', [''])[0]
365 if protocol.iscmd(cmd):
365 if protocol.iscmd(cmd):
366 try:
366 try:
367 if query:
367 if query:
368 raise ErrorResponse(HTTP_NOT_FOUND)
368 raise ErrorResponse(HTTP_NOT_FOUND)
369 if cmd in perms:
369 if cmd in perms:
370 self.check_perm(rctx, req, perms[cmd])
370 self.check_perm(rctx, req, perms[cmd])
371 return protocol.call(rctx.repo, req, cmd)
371 return protocol.call(rctx.repo, req, cmd)
372 except ErrorResponse as inst:
372 except ErrorResponse as inst:
373 # A client that sends unbundle without 100-continue will
373 # A client that sends unbundle without 100-continue will
374 # break if we respond early.
374 # break if we respond early.
375 if (cmd == 'unbundle' and
375 if (cmd == 'unbundle' and
376 (req.env.get('HTTP_EXPECT',
376 (req.env.get('HTTP_EXPECT',
377 '').lower() != '100-continue') or
377 '').lower() != '100-continue') or
378 req.env.get('X-HgHttp2', '')):
378 req.env.get('X-HgHttp2', '')):
379 req.drain()
379 req.drain()
380 else:
380 else:
381 req.headers.append(('Connection', 'Close'))
381 req.headers.append(('Connection', 'Close'))
382 req.respond(inst, protocol.HGTYPE,
382 req.respond(inst, protocol.HGTYPE,
383 body='0\n%s\n' % inst)
383 body='0\n%s\n' % inst)
384 return ''
384 return ''
385
385
386 # translate user-visible url structure to internal structure
386 # translate user-visible url structure to internal structure
387
387
388 args = query.split('/', 2)
388 args = query.split('/', 2)
389 if 'cmd' not in req.form and args and args[0]:
389 if 'cmd' not in req.form and args and args[0]:
390
390
391 cmd = args.pop(0)
391 cmd = args.pop(0)
392 style = cmd.rfind('-')
392 style = cmd.rfind('-')
393 if style != -1:
393 if style != -1:
394 req.form['style'] = [cmd[:style]]
394 req.form['style'] = [cmd[:style]]
395 cmd = cmd[style + 1:]
395 cmd = cmd[style + 1:]
396
396
397 # avoid accepting e.g. style parameter as command
397 # avoid accepting e.g. style parameter as command
398 if util.safehasattr(webcommands, cmd):
398 if util.safehasattr(webcommands, cmd):
399 req.form['cmd'] = [cmd]
399 req.form['cmd'] = [cmd]
400
400
401 if cmd == 'static':
401 if cmd == 'static':
402 req.form['file'] = ['/'.join(args)]
402 req.form['file'] = ['/'.join(args)]
403 else:
403 else:
404 if args and args[0]:
404 if args and args[0]:
405 node = args.pop(0).replace('%2F', '/')
405 node = args.pop(0).replace('%2F', '/')
406 req.form['node'] = [node]
406 req.form['node'] = [node]
407 if args:
407 if args:
408 req.form['file'] = args
408 req.form['file'] = args
409
409
410 ua = req.env.get('HTTP_USER_AGENT', '')
410 ua = req.env.get('HTTP_USER_AGENT', '')
411 if cmd == 'rev' and 'mercurial' in ua:
411 if cmd == 'rev' and 'mercurial' in ua:
412 req.form['style'] = ['raw']
412 req.form['style'] = ['raw']
413
413
414 if cmd == 'archive':
414 if cmd == 'archive':
415 fn = req.form['node'][0]
415 fn = req.form['node'][0]
416 for type_, spec in rctx.archivespecs.iteritems():
416 for type_, spec in rctx.archivespecs.iteritems():
417 ext = spec[2]
417 ext = spec[2]
418 if fn.endswith(ext):
418 if fn.endswith(ext):
419 req.form['node'] = [fn[:-len(ext)]]
419 req.form['node'] = [fn[:-len(ext)]]
420 req.form['type'] = [type_]
420 req.form['type'] = [type_]
421
421
422 # process the web interface request
422 # process the web interface request
423
423
424 try:
424 try:
425 tmpl = rctx.templater(req)
425 tmpl = rctx.templater(req)
426 ctype = tmpl('mimetype', encoding=encoding.encoding)
426 ctype = tmpl('mimetype', encoding=encoding.encoding)
427 ctype = templater.stringify(ctype)
427 ctype = templater.stringify(ctype)
428
428
429 # check read permissions non-static content
429 # check read permissions non-static content
430 if cmd != 'static':
430 if cmd != 'static':
431 self.check_perm(rctx, req, None)
431 self.check_perm(rctx, req, None)
432
432
433 if cmd == '':
433 if cmd == '':
434 req.form['cmd'] = [tmpl.cache['default']]
434 req.form['cmd'] = [tmpl.cache['default']]
435 cmd = req.form['cmd'][0]
435 cmd = req.form['cmd'][0]
436
436
437 # Don't enable caching if using a CSP nonce because then it wouldn't
437 # Don't enable caching if using a CSP nonce because then it wouldn't
438 # be a nonce.
438 # be a nonce.
439 if rctx.configbool('web', 'cache', True) and not rctx.nonce:
439 if rctx.configbool('web', 'cache', True) and not rctx.nonce:
440 caching(self, req) # sets ETag header or raises NOT_MODIFIED
440 caching(self, req) # sets ETag header or raises NOT_MODIFIED
441 if cmd not in webcommands.__all__:
441 if cmd not in webcommands.__all__:
442 msg = 'no such method: %s' % cmd
442 msg = 'no such method: %s' % cmd
443 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
443 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
444 elif cmd == 'file' and 'raw' in req.form.get('style', []):
444 elif cmd == 'file' and 'raw' in req.form.get('style', []):
445 rctx.ctype = ctype
445 rctx.ctype = ctype
446 content = webcommands.rawfile(rctx, req, tmpl)
446 content = webcommands.rawfile(rctx, req, tmpl)
447 else:
447 else:
448 content = getattr(webcommands, cmd)(rctx, req, tmpl)
448 content = getattr(webcommands, cmd)(rctx, req, tmpl)
449 req.respond(HTTP_OK, ctype)
449 req.respond(HTTP_OK, ctype)
450
450
451 return content
451 return content
452
452
453 except (error.LookupError, error.RepoLookupError) as err:
453 except (error.LookupError, error.RepoLookupError) as err:
454 req.respond(HTTP_NOT_FOUND, ctype)
454 req.respond(HTTP_NOT_FOUND, ctype)
455 msg = str(err)
455 msg = str(err)
456 if (util.safehasattr(err, 'name') and
456 if (util.safehasattr(err, 'name') and
457 not isinstance(err, error.ManifestLookupError)):
457 not isinstance(err, error.ManifestLookupError)):
458 msg = 'revision not found: %s' % err.name
458 msg = 'revision not found: %s' % err.name
459 return tmpl('error', error=msg)
459 return tmpl('error', error=msg)
460 except (error.RepoError, error.RevlogError) as inst:
460 except (error.RepoError, error.RevlogError) as inst:
461 req.respond(HTTP_SERVER_ERROR, ctype)
461 req.respond(HTTP_SERVER_ERROR, ctype)
462 return tmpl('error', error=str(inst))
462 return tmpl('error', error=str(inst))
463 except ErrorResponse as inst:
463 except ErrorResponse as inst:
464 req.respond(inst, ctype)
464 req.respond(inst, ctype)
465 if inst.code == HTTP_NOT_MODIFIED:
465 if inst.code == HTTP_NOT_MODIFIED:
466 # Not allowed to return a body on a 304
466 # Not allowed to return a body on a 304
467 return ['']
467 return ['']
468 return tmpl('error', error=str(inst))
468 return tmpl('error', error=str(inst))
469
469
470 def check_perm(self, rctx, req, op):
470 def check_perm(self, rctx, req, op):
471 for permhook in permhooks:
471 for permhook in permhooks:
472 permhook(rctx, req, op)
472 permhook(rctx, req, op)
473
473
474 def getwebview(repo):
474 def getwebview(repo):
475 """The 'web.view' config controls changeset filter to hgweb. Possible
475 """The 'web.view' config controls changeset filter to hgweb. Possible
476 values are ``served``, ``visible`` and ``all``. Default is ``served``.
476 values are ``served``, ``visible`` and ``all``. Default is ``served``.
477 The ``served`` filter only shows changesets that can be pulled from the
477 The ``served`` filter only shows changesets that can be pulled from the
478 hgweb instance. The``visible`` filter includes secret changesets but
478 hgweb instance. The``visible`` filter includes secret changesets but
479 still excludes "hidden" one.
479 still excludes "hidden" one.
480
480
481 See the repoview module for details.
481 See the repoview module for details.
482
482
483 The option has been around undocumented since Mercurial 2.5, but no
483 The option has been around undocumented since Mercurial 2.5, but no
484 user ever asked about it. So we better keep it undocumented for now."""
484 user ever asked about it. So we better keep it undocumented for now."""
485 # experimental config: web.view
485 # experimental config: web.view
486 viewconfig = repo.ui.config('web', 'view', untrusted=True)
486 viewconfig = repo.ui.config('web', 'view', untrusted=True)
487 if viewconfig == 'all':
487 if viewconfig == 'all':
488 return repo.unfiltered()
488 return repo.unfiltered()
489 elif viewconfig in repoview.filtertable:
489 elif viewconfig in repoview.filtertable:
490 return repo.filtered(viewconfig)
490 return repo.filtered(viewconfig)
491 else:
491 else:
492 return repo.filtered('served')
492 return repo.filtered('served')
General Comments 0
You need to be logged in to leave comments. Login now