Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/hgweb/webcommands.py @ 43077:687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Done with
python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py')
black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**')
# skip-blame mass-reformatting only
Differential Revision: https://phab.mercurial-scm.org/D6972
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:48:39 -0400 |
parents | 2372284d9457 |
children | c59eb1560c44 |
comparison
equal
deleted
inserted
replaced
43076:2372284d9457 | 43077:687b865b95ad |
---|---|
77 __all__.append(self.name) | 77 __all__.append(self.name) |
78 commands[self.name] = func | 78 commands[self.name] = func |
79 return func | 79 return func |
80 | 80 |
81 | 81 |
82 @webcommand('log') | 82 @webcommand(b'log') |
83 def log(web): | 83 def log(web): |
84 """ | 84 """ |
85 /log[/{revision}[/{path}]] | 85 /log[/{revision}[/{path}]] |
86 -------------------------- | 86 -------------------------- |
87 | 87 |
94 | 94 |
95 For URLs of the form ``/log/{revision}/{file}``, the history for a specific | 95 For URLs of the form ``/log/{revision}/{file}``, the history for a specific |
96 file will be shown. This form is equivalent to the ``filelog`` handler. | 96 file will be shown. This form is equivalent to the ``filelog`` handler. |
97 """ | 97 """ |
98 | 98 |
99 if web.req.qsparams.get('file'): | 99 if web.req.qsparams.get(b'file'): |
100 return filelog(web) | 100 return filelog(web) |
101 else: | 101 else: |
102 return changelog(web) | 102 return changelog(web) |
103 | 103 |
104 | 104 |
105 @webcommand('rawfile') | 105 @webcommand(b'rawfile') |
106 def rawfile(web): | 106 def rawfile(web): |
107 guessmime = web.configbool('web', 'guessmime') | 107 guessmime = web.configbool(b'web', b'guessmime') |
108 | 108 |
109 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', '')) | 109 path = webutil.cleanpath(web.repo, web.req.qsparams.get(b'file', b'')) |
110 if not path: | 110 if not path: |
111 return manifest(web) | 111 return manifest(web) |
112 | 112 |
113 try: | 113 try: |
114 fctx = webutil.filectx(web.repo, web.req) | 114 fctx = webutil.filectx(web.repo, web.req) |
118 except ErrorResponse: | 118 except ErrorResponse: |
119 raise inst | 119 raise inst |
120 | 120 |
121 path = fctx.path() | 121 path = fctx.path() |
122 text = fctx.data() | 122 text = fctx.data() |
123 mt = 'application/binary' | 123 mt = b'application/binary' |
124 if guessmime: | 124 if guessmime: |
125 mt = mimetypes.guess_type(pycompat.fsdecode(path))[0] | 125 mt = mimetypes.guess_type(pycompat.fsdecode(path))[0] |
126 if mt is None: | 126 if mt is None: |
127 if stringutil.binary(text): | 127 if stringutil.binary(text): |
128 mt = 'application/binary' | 128 mt = b'application/binary' |
129 else: | 129 else: |
130 mt = 'text/plain' | 130 mt = b'text/plain' |
131 else: | 131 else: |
132 mt = pycompat.sysbytes(mt) | 132 mt = pycompat.sysbytes(mt) |
133 | 133 |
134 if mt.startswith('text/'): | 134 if mt.startswith(b'text/'): |
135 mt += '; charset="%s"' % encoding.encoding | 135 mt += b'; charset="%s"' % encoding.encoding |
136 | 136 |
137 web.res.headers['Content-Type'] = mt | 137 web.res.headers[b'Content-Type'] = mt |
138 filename = ( | 138 filename = ( |
139 path.rpartition('/')[-1].replace('\\', '\\\\').replace('"', '\\"') | 139 path.rpartition(b'/')[-1].replace(b'\\', b'\\\\').replace(b'"', b'\\"') |
140 ) | 140 ) |
141 web.res.headers['Content-Disposition'] = 'inline; filename="%s"' % filename | 141 web.res.headers[b'Content-Disposition'] = ( |
142 b'inline; filename="%s"' % filename | |
143 ) | |
142 web.res.setbodybytes(text) | 144 web.res.setbodybytes(text) |
143 return web.res.sendresponse() | 145 return web.res.sendresponse() |
144 | 146 |
145 | 147 |
146 def _filerevision(web, fctx): | 148 def _filerevision(web, fctx): |
152 if stringutil.binary(text): | 154 if stringutil.binary(text): |
153 mt = pycompat.sysbytes( | 155 mt = pycompat.sysbytes( |
154 mimetypes.guess_type(pycompat.fsdecode(f))[0] | 156 mimetypes.guess_type(pycompat.fsdecode(f))[0] |
155 or r'application/octet-stream' | 157 or r'application/octet-stream' |
156 ) | 158 ) |
157 text = '(binary:%s)' % mt | 159 text = b'(binary:%s)' % mt |
158 | 160 |
159 def lines(context): | 161 def lines(context): |
160 for lineno, t in enumerate(text.splitlines(True)): | 162 for lineno, t in enumerate(text.splitlines(True)): |
161 yield { | 163 yield { |
162 "line": t, | 164 b"line": t, |
163 "lineid": "l%d" % (lineno + 1), | 165 b"lineid": b"l%d" % (lineno + 1), |
164 "linenumber": "% 6d" % (lineno + 1), | 166 b"linenumber": b"% 6d" % (lineno + 1), |
165 "parity": next(parity), | 167 b"parity": next(parity), |
166 } | 168 } |
167 | 169 |
168 return web.sendtemplate( | 170 return web.sendtemplate( |
169 'filerevision', | 171 b'filerevision', |
170 file=f, | 172 file=f, |
171 path=webutil.up(f), | 173 path=webutil.up(f), |
172 text=templateutil.mappinggenerator(lines), | 174 text=templateutil.mappinggenerator(lines), |
173 symrev=webutil.symrevorshortnode(web.req, fctx), | 175 symrev=webutil.symrevorshortnode(web.req, fctx), |
174 rename=webutil.renamelink(fctx), | 176 rename=webutil.renamelink(fctx), |
176 ishead=int(ishead), | 178 ishead=int(ishead), |
177 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)) | 179 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)) |
178 ) | 180 ) |
179 | 181 |
180 | 182 |
181 @webcommand('file') | 183 @webcommand(b'file') |
182 def file(web): | 184 def file(web): |
183 """ | 185 """ |
184 /file/{revision}[/{path}] | 186 /file/{revision}[/{path}] |
185 ------------------------- | 187 ------------------------- |
186 | 188 |
196 the ``filerevision`` template. | 198 the ``filerevision`` template. |
197 | 199 |
198 If ``path`` is not defined, information about the root directory will | 200 If ``path`` is not defined, information about the root directory will |
199 be rendered. | 201 be rendered. |
200 """ | 202 """ |
201 if web.req.qsparams.get('style') == 'raw': | 203 if web.req.qsparams.get(b'style') == b'raw': |
202 return rawfile(web) | 204 return rawfile(web) |
203 | 205 |
204 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', '')) | 206 path = webutil.cleanpath(web.repo, web.req.qsparams.get(b'file', b'')) |
205 if not path: | 207 if not path: |
206 return manifest(web) | 208 return manifest(web) |
207 try: | 209 try: |
208 return _filerevision(web, webutil.filectx(web.repo, web.req)) | 210 return _filerevision(web, webutil.filectx(web.repo, web.req)) |
209 except error.LookupError as inst: | 211 except error.LookupError as inst: |
212 except ErrorResponse: | 214 except ErrorResponse: |
213 raise inst | 215 raise inst |
214 | 216 |
215 | 217 |
216 def _search(web): | 218 def _search(web): |
217 MODE_REVISION = 'rev' | 219 MODE_REVISION = b'rev' |
218 MODE_KEYWORD = 'keyword' | 220 MODE_KEYWORD = b'keyword' |
219 MODE_REVSET = 'revset' | 221 MODE_REVSET = b'revset' |
220 | 222 |
221 def revsearch(ctx): | 223 def revsearch(ctx): |
222 yield ctx | 224 yield ctx |
223 | 225 |
224 def keywordsearch(query): | 226 def keywordsearch(query): |
240 miss = 0 | 242 miss = 0 |
241 for q in qw: | 243 for q in qw: |
242 if not ( | 244 if not ( |
243 q in lower(ctx.user()) | 245 q in lower(ctx.user()) |
244 or q in lower(ctx.description()) | 246 or q in lower(ctx.description()) |
245 or q in lower(" ".join(ctx.files())) | 247 or q in lower(b" ".join(ctx.files())) |
246 ): | 248 ): |
247 miss = 1 | 249 miss = 1 |
248 break | 250 break |
249 if miss: | 251 if miss: |
250 continue | 252 continue |
254 def revsetsearch(revs): | 256 def revsetsearch(revs): |
255 for r in revs: | 257 for r in revs: |
256 yield web.repo[r] | 258 yield web.repo[r] |
257 | 259 |
258 searchfuncs = { | 260 searchfuncs = { |
259 MODE_REVISION: (revsearch, 'exact revision search'), | 261 MODE_REVISION: (revsearch, b'exact revision search'), |
260 MODE_KEYWORD: (keywordsearch, 'literal keyword search'), | 262 MODE_KEYWORD: (keywordsearch, b'literal keyword search'), |
261 MODE_REVSET: (revsetsearch, 'revset expression search'), | 263 MODE_REVSET: (revsetsearch, b'revset expression search'), |
262 } | 264 } |
263 | 265 |
264 def getsearchmode(query): | 266 def getsearchmode(query): |
265 try: | 267 try: |
266 ctx = scmutil.revsymbol(web.repo, query) | 268 ctx = scmutil.revsymbol(web.repo, query) |
269 # decide if it's a revset expression or keywords | 271 # decide if it's a revset expression or keywords |
270 pass | 272 pass |
271 else: | 273 else: |
272 return MODE_REVISION, ctx | 274 return MODE_REVISION, ctx |
273 | 275 |
274 revdef = 'reverse(%s)' % query | 276 revdef = b'reverse(%s)' % query |
275 try: | 277 try: |
276 tree = revsetlang.parse(revdef) | 278 tree = revsetlang.parse(revdef) |
277 except error.ParseError: | 279 except error.ParseError: |
278 # can't parse to a revset tree | 280 # can't parse to a revset tree |
279 return MODE_KEYWORD, query | 281 return MODE_KEYWORD, query |
281 if revsetlang.depth(tree) <= 2: | 283 if revsetlang.depth(tree) <= 2: |
282 # no revset syntax used | 284 # no revset syntax used |
283 return MODE_KEYWORD, query | 285 return MODE_KEYWORD, query |
284 | 286 |
285 if any( | 287 if any( |
286 (token, (value or '')[:3]) == ('string', 're:') | 288 (token, (value or b'')[:3]) == (b'string', b're:') |
287 for token, value, pos in revsetlang.tokenize(revdef) | 289 for token, value, pos in revsetlang.tokenize(revdef) |
288 ): | 290 ): |
289 return MODE_KEYWORD, query | 291 return MODE_KEYWORD, query |
290 | 292 |
291 funcsused = revsetlang.funcsused(tree) | 293 funcsused = revsetlang.funcsused(tree) |
314 count = 0 | 316 count = 0 |
315 | 317 |
316 for ctx in searchfunc[0](funcarg): | 318 for ctx in searchfunc[0](funcarg): |
317 count += 1 | 319 count += 1 |
318 n = scmutil.binnode(ctx) | 320 n = scmutil.binnode(ctx) |
319 showtags = webutil.showtag(web.repo, 'changelogtag', n) | 321 showtags = webutil.showtag(web.repo, b'changelogtag', n) |
320 files = webutil.listfilediffs(ctx.files(), n, web.maxfiles) | 322 files = webutil.listfilediffs(ctx.files(), n, web.maxfiles) |
321 | 323 |
322 lm = webutil.commonentry(web.repo, ctx) | 324 lm = webutil.commonentry(web.repo, ctx) |
323 lm.update( | 325 lm.update( |
324 { | 326 { |
325 'parity': next(parity), | 327 b'parity': next(parity), |
326 'changelogtag': showtags, | 328 b'changelogtag': showtags, |
327 'files': files, | 329 b'files': files, |
328 } | 330 } |
329 ) | 331 ) |
330 yield lm | 332 yield lm |
331 | 333 |
332 if count >= revcount: | 334 if count >= revcount: |
333 break | 335 break |
334 | 336 |
335 query = web.req.qsparams['rev'] | 337 query = web.req.qsparams[b'rev'] |
336 revcount = web.maxchanges | 338 revcount = web.maxchanges |
337 if 'revcount' in web.req.qsparams: | 339 if b'revcount' in web.req.qsparams: |
338 try: | 340 try: |
339 revcount = int(web.req.qsparams.get('revcount', revcount)) | 341 revcount = int(web.req.qsparams.get(b'revcount', revcount)) |
340 revcount = max(revcount, 1) | 342 revcount = max(revcount, 1) |
341 web.tmpl.defaults['sessionvars']['revcount'] = revcount | 343 web.tmpl.defaults[b'sessionvars'][b'revcount'] = revcount |
342 except ValueError: | 344 except ValueError: |
343 pass | 345 pass |
344 | 346 |
345 lessvars = copy.copy(web.tmpl.defaults['sessionvars']) | 347 lessvars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
346 lessvars['revcount'] = max(revcount // 2, 1) | 348 lessvars[b'revcount'] = max(revcount // 2, 1) |
347 lessvars['rev'] = query | 349 lessvars[b'rev'] = query |
348 morevars = copy.copy(web.tmpl.defaults['sessionvars']) | 350 morevars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
349 morevars['revcount'] = revcount * 2 | 351 morevars[b'revcount'] = revcount * 2 |
350 morevars['rev'] = query | 352 morevars[b'rev'] = query |
351 | 353 |
352 mode, funcarg = getsearchmode(query) | 354 mode, funcarg = getsearchmode(query) |
353 | 355 |
354 if 'forcekw' in web.req.qsparams: | 356 if b'forcekw' in web.req.qsparams: |
355 showforcekw = '' | 357 showforcekw = b'' |
356 showunforcekw = searchfuncs[mode][1] | 358 showunforcekw = searchfuncs[mode][1] |
357 mode = MODE_KEYWORD | 359 mode = MODE_KEYWORD |
358 funcarg = query | 360 funcarg = query |
359 else: | 361 else: |
360 if mode != MODE_KEYWORD: | 362 if mode != MODE_KEYWORD: |
361 showforcekw = searchfuncs[MODE_KEYWORD][1] | 363 showforcekw = searchfuncs[MODE_KEYWORD][1] |
362 else: | 364 else: |
363 showforcekw = '' | 365 showforcekw = b'' |
364 showunforcekw = '' | 366 showunforcekw = b'' |
365 | 367 |
366 searchfunc = searchfuncs[mode] | 368 searchfunc = searchfuncs[mode] |
367 | 369 |
368 tip = web.repo['tip'] | 370 tip = web.repo[b'tip'] |
369 parity = paritygen(web.stripecount) | 371 parity = paritygen(web.stripecount) |
370 | 372 |
371 return web.sendtemplate( | 373 return web.sendtemplate( |
372 'search', | 374 b'search', |
373 query=query, | 375 query=query, |
374 node=tip.hex(), | 376 node=tip.hex(), |
375 symrev='tip', | 377 symrev=b'tip', |
376 entries=templateutil.mappinggenerator(changelist, name='searchentry'), | 378 entries=templateutil.mappinggenerator(changelist, name=b'searchentry'), |
377 archives=web.archivelist('tip'), | 379 archives=web.archivelist(b'tip'), |
378 morevars=morevars, | 380 morevars=morevars, |
379 lessvars=lessvars, | 381 lessvars=lessvars, |
380 modedesc=searchfunc[1], | 382 modedesc=searchfunc[1], |
381 showforcekw=showforcekw, | 383 showforcekw=showforcekw, |
382 showunforcekw=showunforcekw, | 384 showunforcekw=showunforcekw, |
383 ) | 385 ) |
384 | 386 |
385 | 387 |
386 @webcommand('changelog') | 388 @webcommand(b'changelog') |
387 def changelog(web, shortlog=False): | 389 def changelog(web, shortlog=False): |
388 """ | 390 """ |
389 /changelog[/{revision}] | 391 /changelog[/{revision}] |
390 ----------------------- | 392 ----------------------- |
391 | 393 |
407 changesets to render. | 409 changesets to render. |
408 | 410 |
409 For non-searches, the ``changelog`` template will be rendered. | 411 For non-searches, the ``changelog`` template will be rendered. |
410 """ | 412 """ |
411 | 413 |
412 query = '' | 414 query = b'' |
413 if 'node' in web.req.qsparams: | 415 if b'node' in web.req.qsparams: |
414 ctx = webutil.changectx(web.repo, web.req) | 416 ctx = webutil.changectx(web.repo, web.req) |
415 symrev = webutil.symrevorshortnode(web.req, ctx) | 417 symrev = webutil.symrevorshortnode(web.req, ctx) |
416 elif 'rev' in web.req.qsparams: | 418 elif b'rev' in web.req.qsparams: |
417 return _search(web) | 419 return _search(web) |
418 else: | 420 else: |
419 ctx = web.repo['tip'] | 421 ctx = web.repo[b'tip'] |
420 symrev = 'tip' | 422 symrev = b'tip' |
421 | 423 |
422 def changelist(maxcount): | 424 def changelist(maxcount): |
423 revs = [] | 425 revs = [] |
424 if pos != -1: | 426 if pos != -1: |
425 revs = web.repo.changelog.revs(pos, 0) | 427 revs = web.repo.changelog.revs(pos, 0) |
430 if shortlog: | 432 if shortlog: |
431 revcount = web.maxshortchanges | 433 revcount = web.maxshortchanges |
432 else: | 434 else: |
433 revcount = web.maxchanges | 435 revcount = web.maxchanges |
434 | 436 |
435 if 'revcount' in web.req.qsparams: | 437 if b'revcount' in web.req.qsparams: |
436 try: | 438 try: |
437 revcount = int(web.req.qsparams.get('revcount', revcount)) | 439 revcount = int(web.req.qsparams.get(b'revcount', revcount)) |
438 revcount = max(revcount, 1) | 440 revcount = max(revcount, 1) |
439 web.tmpl.defaults['sessionvars']['revcount'] = revcount | 441 web.tmpl.defaults[b'sessionvars'][b'revcount'] = revcount |
440 except ValueError: | 442 except ValueError: |
441 pass | 443 pass |
442 | 444 |
443 lessvars = copy.copy(web.tmpl.defaults['sessionvars']) | 445 lessvars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
444 lessvars['revcount'] = max(revcount // 2, 1) | 446 lessvars[b'revcount'] = max(revcount // 2, 1) |
445 morevars = copy.copy(web.tmpl.defaults['sessionvars']) | 447 morevars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
446 morevars['revcount'] = revcount * 2 | 448 morevars[b'revcount'] = revcount * 2 |
447 | 449 |
448 count = len(web.repo) | 450 count = len(web.repo) |
449 pos = ctx.rev() | 451 pos = ctx.rev() |
450 parity = paritygen(web.stripecount) | 452 parity = paritygen(web.stripecount) |
451 | 453 |
458 entries = entries[:-1] | 460 entries = entries[:-1] |
459 else: | 461 else: |
460 nextentry = [] | 462 nextentry = [] |
461 | 463 |
462 return web.sendtemplate( | 464 return web.sendtemplate( |
463 'shortlog' if shortlog else 'changelog', | 465 b'shortlog' if shortlog else b'changelog', |
464 changenav=changenav, | 466 changenav=changenav, |
465 node=ctx.hex(), | 467 node=ctx.hex(), |
466 rev=pos, | 468 rev=pos, |
467 symrev=symrev, | 469 symrev=symrev, |
468 changesets=count, | 470 changesets=count, |
469 entries=templateutil.mappinglist(entries), | 471 entries=templateutil.mappinglist(entries), |
470 latestentry=templateutil.mappinglist(latestentry), | 472 latestentry=templateutil.mappinglist(latestentry), |
471 nextentry=templateutil.mappinglist(nextentry), | 473 nextentry=templateutil.mappinglist(nextentry), |
472 archives=web.archivelist('tip'), | 474 archives=web.archivelist(b'tip'), |
473 revcount=revcount, | 475 revcount=revcount, |
474 morevars=morevars, | 476 morevars=morevars, |
475 lessvars=lessvars, | 477 lessvars=lessvars, |
476 query=query, | 478 query=query, |
477 ) | 479 ) |
478 | 480 |
479 | 481 |
480 @webcommand('shortlog') | 482 @webcommand(b'shortlog') |
481 def shortlog(web): | 483 def shortlog(web): |
482 """ | 484 """ |
483 /shortlog | 485 /shortlog |
484 --------- | 486 --------- |
485 | 487 |
490 ``changelog`` template. | 492 ``changelog`` template. |
491 """ | 493 """ |
492 return changelog(web, shortlog=True) | 494 return changelog(web, shortlog=True) |
493 | 495 |
494 | 496 |
495 @webcommand('changeset') | 497 @webcommand(b'changeset') |
496 def changeset(web): | 498 def changeset(web): |
497 """ | 499 """ |
498 /changeset[/{revision}] | 500 /changeset[/{revision}] |
499 ----------------------- | 501 ----------------------- |
500 | 502 |
508 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many | 510 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many |
509 templates related to diffs may all be used to produce the output. | 511 templates related to diffs may all be used to produce the output. |
510 """ | 512 """ |
511 ctx = webutil.changectx(web.repo, web.req) | 513 ctx = webutil.changectx(web.repo, web.req) |
512 | 514 |
513 return web.sendtemplate('changeset', **webutil.changesetentry(web, ctx)) | 515 return web.sendtemplate(b'changeset', **webutil.changesetentry(web, ctx)) |
514 | 516 |
515 | 517 |
516 rev = webcommand('rev')(changeset) | 518 rev = webcommand(b'rev')(changeset) |
517 | 519 |
518 | 520 |
519 def decodepath(path): | 521 def decodepath(path): |
520 """Hook for mapping a path in the repository to a path in the | 522 """Hook for mapping a path in the repository to a path in the |
521 working copy. | 523 working copy. |
523 Extensions (e.g., largefiles) can override this to remap files in | 525 Extensions (e.g., largefiles) can override this to remap files in |
524 the virtual file system presented by the manifest command below.""" | 526 the virtual file system presented by the manifest command below.""" |
525 return path | 527 return path |
526 | 528 |
527 | 529 |
528 @webcommand('manifest') | 530 @webcommand(b'manifest') |
529 def manifest(web): | 531 def manifest(web): |
530 """ | 532 """ |
531 /manifest[/{revision}[/{path}]] | 533 /manifest[/{revision}[/{path}]] |
532 ------------------------------- | 534 ------------------------------- |
533 | 535 |
540 is recommended to use the ``file`` handler instead, as it can handle both | 542 is recommended to use the ``file`` handler instead, as it can handle both |
541 directories and files. | 543 directories and files. |
542 | 544 |
543 The ``manifest`` template will be rendered for this handler. | 545 The ``manifest`` template will be rendered for this handler. |
544 """ | 546 """ |
545 if 'node' in web.req.qsparams: | 547 if b'node' in web.req.qsparams: |
546 ctx = webutil.changectx(web.repo, web.req) | 548 ctx = webutil.changectx(web.repo, web.req) |
547 symrev = webutil.symrevorshortnode(web.req, ctx) | 549 symrev = webutil.symrevorshortnode(web.req, ctx) |
548 else: | 550 else: |
549 ctx = web.repo['tip'] | 551 ctx = web.repo[b'tip'] |
550 symrev = 'tip' | 552 symrev = b'tip' |
551 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', '')) | 553 path = webutil.cleanpath(web.repo, web.req.qsparams.get(b'file', b'')) |
552 mf = ctx.manifest() | 554 mf = ctx.manifest() |
553 node = scmutil.binnode(ctx) | 555 node = scmutil.binnode(ctx) |
554 | 556 |
555 files = {} | 557 files = {} |
556 dirs = {} | 558 dirs = {} |
557 parity = paritygen(web.stripecount) | 559 parity = paritygen(web.stripecount) |
558 | 560 |
559 if path and path[-1:] != "/": | 561 if path and path[-1:] != b"/": |
560 path += "/" | 562 path += b"/" |
561 l = len(path) | 563 l = len(path) |
562 abspath = "/" + path | 564 abspath = b"/" + path |
563 | 565 |
564 for full, n in mf.iteritems(): | 566 for full, n in mf.iteritems(): |
565 # the virtual path (working copy path) used for the full | 567 # the virtual path (working copy path) used for the full |
566 # (repository) path | 568 # (repository) path |
567 f = decodepath(full) | 569 f = decodepath(full) |
568 | 570 |
569 if f[:l] != path: | 571 if f[:l] != path: |
570 continue | 572 continue |
571 remain = f[l:] | 573 remain = f[l:] |
572 elements = remain.split('/') | 574 elements = remain.split(b'/') |
573 if len(elements) == 1: | 575 if len(elements) == 1: |
574 files[remain] = full | 576 files[remain] = full |
575 else: | 577 else: |
576 h = dirs # need to retain ref to dirs (root) | 578 h = dirs # need to retain ref to dirs (root) |
577 for elem in elements[0:-1]: | 579 for elem in elements[0:-1]: |
581 if len(h) > 1: | 583 if len(h) > 1: |
582 break | 584 break |
583 h[None] = None # denotes files present | 585 h[None] = None # denotes files present |
584 | 586 |
585 if mf and not files and not dirs: | 587 if mf and not files and not dirs: |
586 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path) | 588 raise ErrorResponse(HTTP_NOT_FOUND, b'path not found: ' + path) |
587 | 589 |
588 def filelist(context): | 590 def filelist(context): |
589 for f in sorted(files): | 591 for f in sorted(files): |
590 full = files[f] | 592 full = files[f] |
591 | 593 |
592 fctx = ctx.filectx(full) | 594 fctx = ctx.filectx(full) |
593 yield { | 595 yield { |
594 "file": full, | 596 b"file": full, |
595 "parity": next(parity), | 597 b"parity": next(parity), |
596 "basename": f, | 598 b"basename": f, |
597 "date": fctx.date(), | 599 b"date": fctx.date(), |
598 "size": fctx.size(), | 600 b"size": fctx.size(), |
599 "permissions": mf.flags(full), | 601 b"permissions": mf.flags(full), |
600 } | 602 } |
601 | 603 |
602 def dirlist(context): | 604 def dirlist(context): |
603 for d in sorted(dirs): | 605 for d in sorted(dirs): |
604 | 606 |
608 k, v = next(iter(h.items())) | 610 k, v = next(iter(h.items())) |
609 if v: | 611 if v: |
610 emptydirs.append(k) | 612 emptydirs.append(k) |
611 h = v | 613 h = v |
612 | 614 |
613 path = "%s%s" % (abspath, d) | 615 path = b"%s%s" % (abspath, d) |
614 yield { | 616 yield { |
615 "parity": next(parity), | 617 b"parity": next(parity), |
616 "path": path, | 618 b"path": path, |
617 "emptydirs": "/".join(emptydirs), | 619 b"emptydirs": b"/".join(emptydirs), |
618 "basename": d, | 620 b"basename": d, |
619 } | 621 } |
620 | 622 |
621 return web.sendtemplate( | 623 return web.sendtemplate( |
622 'manifest', | 624 b'manifest', |
623 symrev=symrev, | 625 symrev=symrev, |
624 path=abspath, | 626 path=abspath, |
625 up=webutil.up(abspath), | 627 up=webutil.up(abspath), |
626 upparity=next(parity), | 628 upparity=next(parity), |
627 fentries=templateutil.mappinggenerator(filelist), | 629 fentries=templateutil.mappinggenerator(filelist), |
629 archives=web.archivelist(hex(node)), | 631 archives=web.archivelist(hex(node)), |
630 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)) | 632 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)) |
631 ) | 633 ) |
632 | 634 |
633 | 635 |
634 @webcommand('tags') | 636 @webcommand(b'tags') |
635 def tags(web): | 637 def tags(web): |
636 """ | 638 """ |
637 /tags | 639 /tags |
638 ----- | 640 ----- |
639 | 641 |
647 parity = paritygen(web.stripecount) | 649 parity = paritygen(web.stripecount) |
648 | 650 |
649 def entries(context, notip, latestonly): | 651 def entries(context, notip, latestonly): |
650 t = i | 652 t = i |
651 if notip: | 653 if notip: |
652 t = [(k, n) for k, n in i if k != "tip"] | 654 t = [(k, n) for k, n in i if k != b"tip"] |
653 if latestonly: | 655 if latestonly: |
654 t = t[:1] | 656 t = t[:1] |
655 for k, n in t: | 657 for k, n in t: |
656 yield { | 658 yield { |
657 "parity": next(parity), | 659 b"parity": next(parity), |
658 "tag": k, | 660 b"tag": k, |
659 "date": web.repo[n].date(), | 661 b"date": web.repo[n].date(), |
660 "node": hex(n), | 662 b"node": hex(n), |
661 } | 663 } |
662 | 664 |
663 return web.sendtemplate( | 665 return web.sendtemplate( |
664 'tags', | 666 b'tags', |
665 node=hex(web.repo.changelog.tip()), | 667 node=hex(web.repo.changelog.tip()), |
666 entries=templateutil.mappinggenerator(entries, args=(False, False)), | 668 entries=templateutil.mappinggenerator(entries, args=(False, False)), |
667 entriesnotip=templateutil.mappinggenerator(entries, args=(True, False)), | 669 entriesnotip=templateutil.mappinggenerator(entries, args=(True, False)), |
668 latestentry=templateutil.mappinggenerator(entries, args=(True, True)), | 670 latestentry=templateutil.mappinggenerator(entries, args=(True, True)), |
669 ) | 671 ) |
670 | 672 |
671 | 673 |
672 @webcommand('bookmarks') | 674 @webcommand(b'bookmarks') |
673 def bookmarks(web): | 675 def bookmarks(web): |
674 """ | 676 """ |
675 /bookmarks | 677 /bookmarks |
676 ---------- | 678 ---------- |
677 | 679 |
690 t = i | 692 t = i |
691 if latestonly: | 693 if latestonly: |
692 t = i[:1] | 694 t = i[:1] |
693 for k, n in t: | 695 for k, n in t: |
694 yield { | 696 yield { |
695 "parity": next(parity), | 697 b"parity": next(parity), |
696 "bookmark": k, | 698 b"bookmark": k, |
697 "date": web.repo[n].date(), | 699 b"date": web.repo[n].date(), |
698 "node": hex(n), | 700 b"node": hex(n), |
699 } | 701 } |
700 | 702 |
701 if i: | 703 if i: |
702 latestrev = i[0][1] | 704 latestrev = i[0][1] |
703 else: | 705 else: |
704 latestrev = -1 | 706 latestrev = -1 |
705 lastdate = web.repo[latestrev].date() | 707 lastdate = web.repo[latestrev].date() |
706 | 708 |
707 return web.sendtemplate( | 709 return web.sendtemplate( |
708 'bookmarks', | 710 b'bookmarks', |
709 node=hex(web.repo.changelog.tip()), | 711 node=hex(web.repo.changelog.tip()), |
710 lastchange=templateutil.mappinglist([{'date': lastdate}]), | 712 lastchange=templateutil.mappinglist([{b'date': lastdate}]), |
711 entries=templateutil.mappinggenerator(entries, args=(False,)), | 713 entries=templateutil.mappinggenerator(entries, args=(False,)), |
712 latestentry=templateutil.mappinggenerator(entries, args=(True,)), | 714 latestentry=templateutil.mappinggenerator(entries, args=(True,)), |
713 ) | 715 ) |
714 | 716 |
715 | 717 |
716 @webcommand('branches') | 718 @webcommand(b'branches') |
717 def branches(web): | 719 def branches(web): |
718 """ | 720 """ |
719 /branches | 721 /branches |
720 --------- | 722 --------- |
721 | 723 |
729 """ | 731 """ |
730 entries = webutil.branchentries(web.repo, web.stripecount) | 732 entries = webutil.branchentries(web.repo, web.stripecount) |
731 latestentry = webutil.branchentries(web.repo, web.stripecount, 1) | 733 latestentry = webutil.branchentries(web.repo, web.stripecount, 1) |
732 | 734 |
733 return web.sendtemplate( | 735 return web.sendtemplate( |
734 'branches', | 736 b'branches', |
735 node=hex(web.repo.changelog.tip()), | 737 node=hex(web.repo.changelog.tip()), |
736 entries=entries, | 738 entries=entries, |
737 latestentry=latestentry, | 739 latestentry=latestentry, |
738 ) | 740 ) |
739 | 741 |
740 | 742 |
741 @webcommand('summary') | 743 @webcommand(b'summary') |
742 def summary(web): | 744 def summary(web): |
743 """ | 745 """ |
744 /summary | 746 /summary |
745 -------- | 747 -------- |
746 | 748 |
755 | 757 |
756 def tagentries(context): | 758 def tagentries(context): |
757 parity = paritygen(web.stripecount) | 759 parity = paritygen(web.stripecount) |
758 count = 0 | 760 count = 0 |
759 for k, n in i: | 761 for k, n in i: |
760 if k == "tip": # skip tip | 762 if k == b"tip": # skip tip |
761 continue | 763 continue |
762 | 764 |
763 count += 1 | 765 count += 1 |
764 if count > 10: # limit to 10 tags | 766 if count > 10: # limit to 10 tags |
765 break | 767 break |
766 | 768 |
767 yield { | 769 yield { |
768 'parity': next(parity), | 770 b'parity': next(parity), |
769 'tag': k, | 771 b'tag': k, |
770 'node': hex(n), | 772 b'node': hex(n), |
771 'date': web.repo[n].date(), | 773 b'date': web.repo[n].date(), |
772 } | 774 } |
773 | 775 |
774 def bookmarks(context): | 776 def bookmarks(context): |
775 parity = paritygen(web.stripecount) | 777 parity = paritygen(web.stripecount) |
776 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo] | 778 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo] |
777 sortkey = lambda b: (web.repo[b[1]].rev(), b[0]) | 779 sortkey = lambda b: (web.repo[b[1]].rev(), b[0]) |
778 marks = sorted(marks, key=sortkey, reverse=True) | 780 marks = sorted(marks, key=sortkey, reverse=True) |
779 for k, n in marks[:10]: # limit to 10 bookmarks | 781 for k, n in marks[:10]: # limit to 10 bookmarks |
780 yield { | 782 yield { |
781 'parity': next(parity), | 783 b'parity': next(parity), |
782 'bookmark': k, | 784 b'bookmark': k, |
783 'date': web.repo[n].date(), | 785 b'date': web.repo[n].date(), |
784 'node': hex(n), | 786 b'node': hex(n), |
785 } | 787 } |
786 | 788 |
787 def changelist(context): | 789 def changelist(context): |
788 parity = paritygen(web.stripecount, offset=start - end) | 790 parity = paritygen(web.stripecount, offset=start - end) |
789 l = [] # build a list in forward order for efficiency | 791 l = [] # build a list in forward order for efficiency |
791 if start < end: | 793 if start < end: |
792 revs = web.repo.changelog.revs(start, end - 1) | 794 revs = web.repo.changelog.revs(start, end - 1) |
793 for i in revs: | 795 for i in revs: |
794 ctx = web.repo[i] | 796 ctx = web.repo[i] |
795 lm = webutil.commonentry(web.repo, ctx) | 797 lm = webutil.commonentry(web.repo, ctx) |
796 lm['parity'] = next(parity) | 798 lm[b'parity'] = next(parity) |
797 l.append(lm) | 799 l.append(lm) |
798 | 800 |
799 for entry in reversed(l): | 801 for entry in reversed(l): |
800 yield entry | 802 yield entry |
801 | 803 |
802 tip = web.repo['tip'] | 804 tip = web.repo[b'tip'] |
803 count = len(web.repo) | 805 count = len(web.repo) |
804 start = max(0, count - web.maxchanges) | 806 start = max(0, count - web.maxchanges) |
805 end = min(count, start + web.maxchanges) | 807 end = min(count, start + web.maxchanges) |
806 | 808 |
807 desc = web.config("web", "description") | 809 desc = web.config(b"web", b"description") |
808 if not desc: | 810 if not desc: |
809 desc = 'unknown' | 811 desc = b'unknown' |
810 labels = web.configlist('web', 'labels') | 812 labels = web.configlist(b'web', b'labels') |
811 | 813 |
812 return web.sendtemplate( | 814 return web.sendtemplate( |
813 'summary', | 815 b'summary', |
814 desc=desc, | 816 desc=desc, |
815 owner=get_contact(web.config) or 'unknown', | 817 owner=get_contact(web.config) or b'unknown', |
816 lastchange=tip.date(), | 818 lastchange=tip.date(), |
817 tags=templateutil.mappinggenerator(tagentries, name='tagentry'), | 819 tags=templateutil.mappinggenerator(tagentries, name=b'tagentry'), |
818 bookmarks=templateutil.mappinggenerator(bookmarks), | 820 bookmarks=templateutil.mappinggenerator(bookmarks), |
819 branches=webutil.branchentries(web.repo, web.stripecount, 10), | 821 branches=webutil.branchentries(web.repo, web.stripecount, 10), |
820 shortlog=templateutil.mappinggenerator( | 822 shortlog=templateutil.mappinggenerator( |
821 changelist, name='shortlogentry' | 823 changelist, name=b'shortlogentry' |
822 ), | 824 ), |
823 node=tip.hex(), | 825 node=tip.hex(), |
824 symrev='tip', | 826 symrev=b'tip', |
825 archives=web.archivelist('tip'), | 827 archives=web.archivelist(b'tip'), |
826 labels=templateutil.hybridlist(labels, name='label'), | 828 labels=templateutil.hybridlist(labels, name=b'label'), |
827 ) | 829 ) |
828 | 830 |
829 | 831 |
830 @webcommand('filediff') | 832 @webcommand(b'filediff') |
831 def filediff(web): | 833 def filediff(web): |
832 """ | 834 """ |
833 /diff/{revision}/{path} | 835 /diff/{revision}/{path} |
834 ----------------------- | 836 ----------------------- |
835 | 837 |
843 fctx, ctx = None, None | 845 fctx, ctx = None, None |
844 try: | 846 try: |
845 fctx = webutil.filectx(web.repo, web.req) | 847 fctx = webutil.filectx(web.repo, web.req) |
846 except LookupError: | 848 except LookupError: |
847 ctx = webutil.changectx(web.repo, web.req) | 849 ctx = webutil.changectx(web.repo, web.req) |
848 path = webutil.cleanpath(web.repo, web.req.qsparams['file']) | 850 path = webutil.cleanpath(web.repo, web.req.qsparams[b'file']) |
849 if path not in ctx.files(): | 851 if path not in ctx.files(): |
850 raise | 852 raise |
851 | 853 |
852 if fctx is not None: | 854 if fctx is not None: |
853 path = fctx.path() | 855 path = fctx.path() |
854 ctx = fctx.changectx() | 856 ctx = fctx.changectx() |
855 basectx = ctx.p1() | 857 basectx = ctx.p1() |
856 | 858 |
857 style = web.config('web', 'style') | 859 style = web.config(b'web', b'style') |
858 if 'style' in web.req.qsparams: | 860 if b'style' in web.req.qsparams: |
859 style = web.req.qsparams['style'] | 861 style = web.req.qsparams[b'style'] |
860 | 862 |
861 diffs = webutil.diffs(web, ctx, basectx, [path], style) | 863 diffs = webutil.diffs(web, ctx, basectx, [path], style) |
862 if fctx is not None: | 864 if fctx is not None: |
863 rename = webutil.renamelink(fctx) | 865 rename = webutil.renamelink(fctx) |
864 ctx = fctx | 866 ctx = fctx |
865 else: | 867 else: |
866 rename = templateutil.mappinglist([]) | 868 rename = templateutil.mappinglist([]) |
867 ctx = ctx | 869 ctx = ctx |
868 | 870 |
869 return web.sendtemplate( | 871 return web.sendtemplate( |
870 'filediff', | 872 b'filediff', |
871 file=path, | 873 file=path, |
872 symrev=webutil.symrevorshortnode(web.req, ctx), | 874 symrev=webutil.symrevorshortnode(web.req, ctx), |
873 rename=rename, | 875 rename=rename, |
874 diff=diffs, | 876 diff=diffs, |
875 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)) | 877 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)) |
876 ) | 878 ) |
877 | 879 |
878 | 880 |
879 diff = webcommand('diff')(filediff) | 881 diff = webcommand(b'diff')(filediff) |
880 | 882 |
881 | 883 |
882 @webcommand('comparison') | 884 @webcommand(b'comparison') |
883 def comparison(web): | 885 def comparison(web): |
884 """ | 886 """ |
885 /comparison/{revision}/{path} | 887 /comparison/{revision}/{path} |
886 ----------------------------- | 888 ----------------------------- |
887 | 889 |
895 context in the diff. | 897 context in the diff. |
896 | 898 |
897 The ``filecomparison`` template is rendered. | 899 The ``filecomparison`` template is rendered. |
898 """ | 900 """ |
899 ctx = webutil.changectx(web.repo, web.req) | 901 ctx = webutil.changectx(web.repo, web.req) |
900 if 'file' not in web.req.qsparams: | 902 if b'file' not in web.req.qsparams: |
901 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given') | 903 raise ErrorResponse(HTTP_NOT_FOUND, b'file not given') |
902 path = webutil.cleanpath(web.repo, web.req.qsparams['file']) | 904 path = webutil.cleanpath(web.repo, web.req.qsparams[b'file']) |
903 | 905 |
904 parsecontext = lambda v: v == 'full' and -1 or int(v) | 906 parsecontext = lambda v: v == b'full' and -1 or int(v) |
905 if 'context' in web.req.qsparams: | 907 if b'context' in web.req.qsparams: |
906 context = parsecontext(web.req.qsparams['context']) | 908 context = parsecontext(web.req.qsparams[b'context']) |
907 else: | 909 else: |
908 context = parsecontext(web.config('web', 'comparisoncontext')) | 910 context = parsecontext(web.config(b'web', b'comparisoncontext')) |
909 | 911 |
910 def filelines(f): | 912 def filelines(f): |
911 if f.isbinary(): | 913 if f.isbinary(): |
912 mt = pycompat.sysbytes( | 914 mt = pycompat.sysbytes( |
913 mimetypes.guess_type(pycompat.fsdecode(f.path()))[0] | 915 mimetypes.guess_type(pycompat.fsdecode(f.path()))[0] |
914 or r'application/octet-stream' | 916 or r'application/octet-stream' |
915 ) | 917 ) |
916 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))] | 918 return [_(b'(binary file %s, hash: %s)') % (mt, hex(f.filenode()))] |
917 return f.data().splitlines() | 919 return f.data().splitlines() |
918 | 920 |
919 fctx = None | 921 fctx = None |
920 parent = ctx.p1() | 922 parent = ctx.p1() |
921 leftrev = parent.rev() | 923 leftrev = parent.rev() |
942 else: | 944 else: |
943 rename = templateutil.mappinglist([]) | 945 rename = templateutil.mappinglist([]) |
944 ctx = ctx | 946 ctx = ctx |
945 | 947 |
946 return web.sendtemplate( | 948 return web.sendtemplate( |
947 'filecomparison', | 949 b'filecomparison', |
948 file=path, | 950 file=path, |
949 symrev=webutil.symrevorshortnode(web.req, ctx), | 951 symrev=webutil.symrevorshortnode(web.req, ctx), |
950 rename=rename, | 952 rename=rename, |
951 leftrev=leftrev, | 953 leftrev=leftrev, |
952 leftnode=hex(leftnode), | 954 leftnode=hex(leftnode), |
955 comparison=comparison, | 957 comparison=comparison, |
956 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)) | 958 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)) |
957 ) | 959 ) |
958 | 960 |
959 | 961 |
960 @webcommand('annotate') | 962 @webcommand(b'annotate') |
961 def annotate(web): | 963 def annotate(web): |
962 """ | 964 """ |
963 /annotate/{revision}/{path} | 965 /annotate/{revision}/{path} |
964 --------------------------- | 966 --------------------------- |
965 | 967 |
989 rev = f.rev() | 991 rev = f.rev() |
990 if rev not in parentscache: | 992 if rev not in parentscache: |
991 parentscache[rev] = [] | 993 parentscache[rev] = [] |
992 for p in f.parents(): | 994 for p in f.parents(): |
993 entry = { | 995 entry = { |
994 'node': p.hex(), | 996 b'node': p.hex(), |
995 'rev': p.rev(), | 997 b'rev': p.rev(), |
996 } | 998 } |
997 parentscache[rev].append(entry) | 999 parentscache[rev].append(entry) |
998 | 1000 |
999 for p in parentscache[rev]: | 1001 for p in parentscache[rev]: |
1000 yield p | 1002 yield p |
1007 ) | 1009 ) |
1008 lines = [ | 1010 lines = [ |
1009 dagop.annotateline( | 1011 dagop.annotateline( |
1010 fctx=fctx.filectx(fctx.filerev()), | 1012 fctx=fctx.filectx(fctx.filerev()), |
1011 lineno=1, | 1013 lineno=1, |
1012 text='(binary:%s)' % mt, | 1014 text=b'(binary:%s)' % mt, |
1013 ) | 1015 ) |
1014 ] | 1016 ] |
1015 else: | 1017 else: |
1016 lines = webutil.annotate(web.req, fctx, web.repo.ui) | 1018 lines = webutil.annotate(web.req, fctx, web.repo.ui) |
1017 | 1019 |
1025 blockparity = next(blockparitygen) | 1027 blockparity = next(blockparitygen) |
1026 else: | 1028 else: |
1027 blockhead = None | 1029 blockhead = None |
1028 previousrev = rev | 1030 previousrev = rev |
1029 yield { | 1031 yield { |
1030 "parity": next(parity), | 1032 b"parity": next(parity), |
1031 "node": f.hex(), | 1033 b"node": f.hex(), |
1032 "rev": rev, | 1034 b"rev": rev, |
1033 "author": f.user(), | 1035 b"author": f.user(), |
1034 "parents": templateutil.mappinggenerator(parents, args=(f,)), | 1036 b"parents": templateutil.mappinggenerator(parents, args=(f,)), |
1035 "desc": f.description(), | 1037 b"desc": f.description(), |
1036 "extra": f.extra(), | 1038 b"extra": f.extra(), |
1037 "file": f.path(), | 1039 b"file": f.path(), |
1038 "blockhead": blockhead, | 1040 b"blockhead": blockhead, |
1039 "blockparity": blockparity, | 1041 b"blockparity": blockparity, |
1040 "targetline": aline.lineno, | 1042 b"targetline": aline.lineno, |
1041 "line": aline.text, | 1043 b"line": aline.text, |
1042 "lineno": lineno + 1, | 1044 b"lineno": lineno + 1, |
1043 "lineid": "l%d" % (lineno + 1), | 1045 b"lineid": b"l%d" % (lineno + 1), |
1044 "linenumber": "% 6d" % (lineno + 1), | 1046 b"linenumber": b"% 6d" % (lineno + 1), |
1045 "revdate": f.date(), | 1047 b"revdate": f.date(), |
1046 } | 1048 } |
1047 | 1049 |
1048 diffopts = webutil.difffeatureopts(web.req, web.repo.ui, 'annotate') | 1050 diffopts = webutil.difffeatureopts(web.req, web.repo.ui, b'annotate') |
1049 diffopts = {k: getattr(diffopts, k) for k in diffopts.defaults} | 1051 diffopts = {k: getattr(diffopts, k) for k in diffopts.defaults} |
1050 | 1052 |
1051 return web.sendtemplate( | 1053 return web.sendtemplate( |
1052 'fileannotate', | 1054 b'fileannotate', |
1053 file=f, | 1055 file=f, |
1054 annotate=templateutil.mappinggenerator(annotate), | 1056 annotate=templateutil.mappinggenerator(annotate), |
1055 path=webutil.up(f), | 1057 path=webutil.up(f), |
1056 symrev=webutil.symrevorshortnode(web.req, fctx), | 1058 symrev=webutil.symrevorshortnode(web.req, fctx), |
1057 rename=webutil.renamelink(fctx), | 1059 rename=webutil.renamelink(fctx), |
1060 diffopts=templateutil.hybriddict(diffopts), | 1062 diffopts=templateutil.hybriddict(diffopts), |
1061 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)) | 1063 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)) |
1062 ) | 1064 ) |
1063 | 1065 |
1064 | 1066 |
1065 @webcommand('filelog') | 1067 @webcommand(b'filelog') |
1066 def filelog(web): | 1068 def filelog(web): |
1067 """ | 1069 """ |
1068 /filelog/{revision}/{path} | 1070 /filelog/{revision}/{path} |
1069 -------------------------- | 1071 -------------------------- |
1070 | 1072 |
1079 try: | 1081 try: |
1080 fctx = webutil.filectx(web.repo, web.req) | 1082 fctx = webutil.filectx(web.repo, web.req) |
1081 f = fctx.path() | 1083 f = fctx.path() |
1082 fl = fctx.filelog() | 1084 fl = fctx.filelog() |
1083 except error.LookupError: | 1085 except error.LookupError: |
1084 f = webutil.cleanpath(web.repo, web.req.qsparams['file']) | 1086 f = webutil.cleanpath(web.repo, web.req.qsparams[b'file']) |
1085 fl = web.repo.file(f) | 1087 fl = web.repo.file(f) |
1086 numrevs = len(fl) | 1088 numrevs = len(fl) |
1087 if not numrevs: # file doesn't exist at all | 1089 if not numrevs: # file doesn't exist at all |
1088 raise | 1090 raise |
1089 rev = webutil.changectx(web.repo, web.req).rev() | 1091 rev = webutil.changectx(web.repo, web.req).rev() |
1094 while fl.linkrev(frev) > rev: | 1096 while fl.linkrev(frev) > rev: |
1095 frev -= 1 | 1097 frev -= 1 |
1096 fctx = web.repo.filectx(f, fl.linkrev(frev)) | 1098 fctx = web.repo.filectx(f, fl.linkrev(frev)) |
1097 | 1099 |
1098 revcount = web.maxshortchanges | 1100 revcount = web.maxshortchanges |
1099 if 'revcount' in web.req.qsparams: | 1101 if b'revcount' in web.req.qsparams: |
1100 try: | 1102 try: |
1101 revcount = int(web.req.qsparams.get('revcount', revcount)) | 1103 revcount = int(web.req.qsparams.get(b'revcount', revcount)) |
1102 revcount = max(revcount, 1) | 1104 revcount = max(revcount, 1) |
1103 web.tmpl.defaults['sessionvars']['revcount'] = revcount | 1105 web.tmpl.defaults[b'sessionvars'][b'revcount'] = revcount |
1104 except ValueError: | 1106 except ValueError: |
1105 pass | 1107 pass |
1106 | 1108 |
1107 lrange = webutil.linerange(web.req) | 1109 lrange = webutil.linerange(web.req) |
1108 | 1110 |
1109 lessvars = copy.copy(web.tmpl.defaults['sessionvars']) | 1111 lessvars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
1110 lessvars['revcount'] = max(revcount // 2, 1) | 1112 lessvars[b'revcount'] = max(revcount // 2, 1) |
1111 morevars = copy.copy(web.tmpl.defaults['sessionvars']) | 1113 morevars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
1112 morevars['revcount'] = revcount * 2 | 1114 morevars[b'revcount'] = revcount * 2 |
1113 | 1115 |
1114 patch = 'patch' in web.req.qsparams | 1116 patch = b'patch' in web.req.qsparams |
1115 if patch: | 1117 if patch: |
1116 lessvars['patch'] = morevars['patch'] = web.req.qsparams['patch'] | 1118 lessvars[b'patch'] = morevars[b'patch'] = web.req.qsparams[b'patch'] |
1117 descend = 'descend' in web.req.qsparams | 1119 descend = b'descend' in web.req.qsparams |
1118 if descend: | 1120 if descend: |
1119 lessvars['descend'] = morevars['descend'] = web.req.qsparams['descend'] | 1121 lessvars[b'descend'] = morevars[b'descend'] = web.req.qsparams[ |
1122 b'descend' | |
1123 ] | |
1120 | 1124 |
1121 count = fctx.filerev() + 1 | 1125 count = fctx.filerev() + 1 |
1122 start = max(0, count - revcount) # first rev on this page | 1126 start = max(0, count - revcount) # first rev on this page |
1123 end = min(count, start + revcount) # last rev on this page | 1127 end = min(count, start + revcount) # last rev on this page |
1124 parity = paritygen(web.stripecount, offset=start - end) | 1128 parity = paritygen(web.stripecount, offset=start - end) |
1130 for filerev in filelog.revs(start, end - 1) | 1134 for filerev in filelog.revs(start, end - 1) |
1131 if filelog.linkrev(filerev) in repo | 1135 if filelog.linkrev(filerev) in repo |
1132 ] | 1136 ] |
1133 entries = [] | 1137 entries = [] |
1134 | 1138 |
1135 diffstyle = web.config('web', 'style') | 1139 diffstyle = web.config(b'web', b'style') |
1136 if 'style' in web.req.qsparams: | 1140 if b'style' in web.req.qsparams: |
1137 diffstyle = web.req.qsparams['style'] | 1141 diffstyle = web.req.qsparams[b'style'] |
1138 | 1142 |
1139 def diff(fctx, linerange=None): | 1143 def diff(fctx, linerange=None): |
1140 ctx = fctx.changectx() | 1144 ctx = fctx.changectx() |
1141 basectx = ctx.p1() | 1145 basectx = ctx.p1() |
1142 path = fctx.path() | 1146 path = fctx.path() |
1145 ctx, | 1149 ctx, |
1146 basectx, | 1150 basectx, |
1147 [path], | 1151 [path], |
1148 diffstyle, | 1152 diffstyle, |
1149 linerange=linerange, | 1153 linerange=linerange, |
1150 lineidprefix='%s-' % ctx.hex()[:12], | 1154 lineidprefix=b'%s-' % ctx.hex()[:12], |
1151 ) | 1155 ) |
1152 | 1156 |
1153 linerange = None | 1157 linerange = None |
1154 if lrange is not None: | 1158 if lrange is not None: |
1155 linerange = webutil.formatlinerange(*lrange) | 1159 linerange = webutil.formatlinerange(*lrange) |
1167 # follow renames accross filtered (not in range) revisions | 1171 # follow renames accross filtered (not in range) revisions |
1168 path = c.path() | 1172 path = c.path() |
1169 lm = webutil.commonentry(repo, c) | 1173 lm = webutil.commonentry(repo, c) |
1170 lm.update( | 1174 lm.update( |
1171 { | 1175 { |
1172 'parity': next(parity), | 1176 b'parity': next(parity), |
1173 'filerev': c.rev(), | 1177 b'filerev': c.rev(), |
1174 'file': path, | 1178 b'file': path, |
1175 'diff': diffs, | 1179 b'diff': diffs, |
1176 'linerange': webutil.formatlinerange(*lr), | 1180 b'linerange': webutil.formatlinerange(*lr), |
1177 'rename': templateutil.mappinglist([]), | 1181 b'rename': templateutil.mappinglist([]), |
1178 } | 1182 } |
1179 ) | 1183 ) |
1180 entries.append(lm) | 1184 entries.append(lm) |
1181 if i == revcount: | 1185 if i == revcount: |
1182 break | 1186 break |
1183 lessvars['linerange'] = webutil.formatlinerange(*lrange) | 1187 lessvars[b'linerange'] = webutil.formatlinerange(*lrange) |
1184 morevars['linerange'] = lessvars['linerange'] | 1188 morevars[b'linerange'] = lessvars[b'linerange'] |
1185 else: | 1189 else: |
1186 for i in revs: | 1190 for i in revs: |
1187 iterfctx = fctx.filectx(i) | 1191 iterfctx = fctx.filectx(i) |
1188 diffs = None | 1192 diffs = None |
1189 if patch: | 1193 if patch: |
1190 diffs = diff(iterfctx) | 1194 diffs = diff(iterfctx) |
1191 lm = webutil.commonentry(repo, iterfctx) | 1195 lm = webutil.commonentry(repo, iterfctx) |
1192 lm.update( | 1196 lm.update( |
1193 { | 1197 { |
1194 'parity': next(parity), | 1198 b'parity': next(parity), |
1195 'filerev': i, | 1199 b'filerev': i, |
1196 'file': f, | 1200 b'file': f, |
1197 'diff': diffs, | 1201 b'diff': diffs, |
1198 'rename': webutil.renamelink(iterfctx), | 1202 b'rename': webutil.renamelink(iterfctx), |
1199 } | 1203 } |
1200 ) | 1204 ) |
1201 entries.append(lm) | 1205 entries.append(lm) |
1202 entries.reverse() | 1206 entries.reverse() |
1203 revnav = webutil.filerevnav(web.repo, fctx.path()) | 1207 revnav = webutil.filerevnav(web.repo, fctx.path()) |
1204 nav = revnav.gen(end - 1, revcount, count) | 1208 nav = revnav.gen(end - 1, revcount, count) |
1205 | 1209 |
1206 latestentry = entries[:1] | 1210 latestentry = entries[:1] |
1207 | 1211 |
1208 return web.sendtemplate( | 1212 return web.sendtemplate( |
1209 'filelog', | 1213 b'filelog', |
1210 file=f, | 1214 file=f, |
1211 nav=nav, | 1215 nav=nav, |
1212 symrev=webutil.symrevorshortnode(web.req, fctx), | 1216 symrev=webutil.symrevorshortnode(web.req, fctx), |
1213 entries=templateutil.mappinglist(entries), | 1217 entries=templateutil.mappinglist(entries), |
1214 descend=descend, | 1218 descend=descend, |
1220 lessvars=lessvars, | 1224 lessvars=lessvars, |
1221 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)) | 1225 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)) |
1222 ) | 1226 ) |
1223 | 1227 |
1224 | 1228 |
1225 @webcommand('archive') | 1229 @webcommand(b'archive') |
1226 def archive(web): | 1230 def archive(web): |
1227 """ | 1231 """ |
1228 /archive/{revision}.{format}[/{path}] | 1232 /archive/{revision}.{format}[/{path}] |
1229 ------------------------------------- | 1233 ------------------------------------- |
1230 | 1234 |
1241 directory will be included in the archive. | 1245 directory will be included in the archive. |
1242 | 1246 |
1243 No template is used for this handler. Raw, binary content is generated. | 1247 No template is used for this handler. Raw, binary content is generated. |
1244 """ | 1248 """ |
1245 | 1249 |
1246 type_ = web.req.qsparams.get('type') | 1250 type_ = web.req.qsparams.get(b'type') |
1247 allowed = web.configlist("web", "allow-archive") | 1251 allowed = web.configlist(b"web", b"allow-archive") |
1248 key = web.req.qsparams['node'] | 1252 key = web.req.qsparams[b'node'] |
1249 | 1253 |
1250 if type_ not in webutil.archivespecs: | 1254 if type_ not in webutil.archivespecs: |
1251 msg = 'Unsupported archive type: %s' % stringutil.pprint(type_) | 1255 msg = b'Unsupported archive type: %s' % stringutil.pprint(type_) |
1252 raise ErrorResponse(HTTP_NOT_FOUND, msg) | 1256 raise ErrorResponse(HTTP_NOT_FOUND, msg) |
1253 | 1257 |
1254 if not ((type_ in allowed or web.configbool("web", "allow" + type_))): | 1258 if not ((type_ in allowed or web.configbool(b"web", b"allow" + type_))): |
1255 msg = 'Archive type not allowed: %s' % type_ | 1259 msg = b'Archive type not allowed: %s' % type_ |
1256 raise ErrorResponse(HTTP_FORBIDDEN, msg) | 1260 raise ErrorResponse(HTTP_FORBIDDEN, msg) |
1257 | 1261 |
1258 reponame = re.sub(br"\W+", "-", os.path.basename(web.reponame)) | 1262 reponame = re.sub(br"\W+", b"-", os.path.basename(web.reponame)) |
1259 cnode = web.repo.lookup(key) | 1263 cnode = web.repo.lookup(key) |
1260 arch_version = key | 1264 arch_version = key |
1261 if cnode == key or key == 'tip': | 1265 if cnode == key or key == b'tip': |
1262 arch_version = short(cnode) | 1266 arch_version = short(cnode) |
1263 name = "%s-%s" % (reponame, arch_version) | 1267 name = b"%s-%s" % (reponame, arch_version) |
1264 | 1268 |
1265 ctx = webutil.changectx(web.repo, web.req) | 1269 ctx = webutil.changectx(web.repo, web.req) |
1266 pats = [] | 1270 pats = [] |
1267 match = scmutil.match(ctx, []) | 1271 match = scmutil.match(ctx, []) |
1268 file = web.req.qsparams.get('file') | 1272 file = web.req.qsparams.get(b'file') |
1269 if file: | 1273 if file: |
1270 pats = ['path:' + file] | 1274 pats = [b'path:' + file] |
1271 match = scmutil.match(ctx, pats, default='path') | 1275 match = scmutil.match(ctx, pats, default=b'path') |
1272 if pats: | 1276 if pats: |
1273 files = [f for f in ctx.manifest().keys() if match(f)] | 1277 files = [f for f in ctx.manifest().keys() if match(f)] |
1274 if not files: | 1278 if not files: |
1275 raise ErrorResponse( | 1279 raise ErrorResponse( |
1276 HTTP_NOT_FOUND, 'file(s) not found: %s' % file | 1280 HTTP_NOT_FOUND, b'file(s) not found: %s' % file |
1277 ) | 1281 ) |
1278 | 1282 |
1279 mimetype, artype, extension, encoding = webutil.archivespecs[type_] | 1283 mimetype, artype, extension, encoding = webutil.archivespecs[type_] |
1280 | 1284 |
1281 web.res.headers['Content-Type'] = mimetype | 1285 web.res.headers[b'Content-Type'] = mimetype |
1282 web.res.headers['Content-Disposition'] = 'attachment; filename=%s%s' % ( | 1286 web.res.headers[b'Content-Disposition'] = b'attachment; filename=%s%s' % ( |
1283 name, | 1287 name, |
1284 extension, | 1288 extension, |
1285 ) | 1289 ) |
1286 | 1290 |
1287 if encoding: | 1291 if encoding: |
1288 web.res.headers['Content-Encoding'] = encoding | 1292 web.res.headers[b'Content-Encoding'] = encoding |
1289 | 1293 |
1290 web.res.setbodywillwrite() | 1294 web.res.setbodywillwrite() |
1291 if list(web.res.sendresponse()): | 1295 if list(web.res.sendresponse()): |
1292 raise error.ProgrammingError( | 1296 raise error.ProgrammingError( |
1293 'sendresponse() should not emit data ' 'if writing later' | 1297 b'sendresponse() should not emit data ' b'if writing later' |
1294 ) | 1298 ) |
1295 | 1299 |
1296 bodyfh = web.res.getbodyfile() | 1300 bodyfh = web.res.getbodyfile() |
1297 | 1301 |
1298 archival.archive( | 1302 archival.archive( |
1300 bodyfh, | 1304 bodyfh, |
1301 cnode, | 1305 cnode, |
1302 artype, | 1306 artype, |
1303 prefix=name, | 1307 prefix=name, |
1304 match=match, | 1308 match=match, |
1305 subrepos=web.configbool("web", "archivesubrepos"), | 1309 subrepos=web.configbool(b"web", b"archivesubrepos"), |
1306 ) | 1310 ) |
1307 | 1311 |
1308 return [] | 1312 return [] |
1309 | 1313 |
1310 | 1314 |
1311 @webcommand('static') | 1315 @webcommand(b'static') |
1312 def static(web): | 1316 def static(web): |
1313 fname = web.req.qsparams['file'] | 1317 fname = web.req.qsparams[b'file'] |
1314 # a repo owner may set web.static in .hg/hgrc to get any file | 1318 # a repo owner may set web.static in .hg/hgrc to get any file |
1315 # readable by the user running the CGI script | 1319 # readable by the user running the CGI script |
1316 static = web.config("web", "static", untrusted=False) | 1320 static = web.config(b"web", b"static", untrusted=False) |
1317 if not static: | 1321 if not static: |
1318 tp = web.templatepath or templater.templatepaths() | 1322 tp = web.templatepath or templater.templatepaths() |
1319 if isinstance(tp, str): | 1323 if isinstance(tp, str): |
1320 tp = [tp] | 1324 tp = [tp] |
1321 static = [os.path.join(p, 'static') for p in tp] | 1325 static = [os.path.join(p, b'static') for p in tp] |
1322 | 1326 |
1323 staticfile(static, fname, web.res) | 1327 staticfile(static, fname, web.res) |
1324 return web.res.sendresponse() | 1328 return web.res.sendresponse() |
1325 | 1329 |
1326 | 1330 |
1327 @webcommand('graph') | 1331 @webcommand(b'graph') |
1328 def graph(web): | 1332 def graph(web): |
1329 """ | 1333 """ |
1330 /graph[/{revision}] | 1334 /graph[/{revision}] |
1331 ------------------- | 1335 ------------------- |
1332 | 1336 |
1346 JavaScript. By default it has the same value as ``revision``. | 1350 JavaScript. By default it has the same value as ``revision``. |
1347 | 1351 |
1348 This handler will render the ``graph`` template. | 1352 This handler will render the ``graph`` template. |
1349 """ | 1353 """ |
1350 | 1354 |
1351 if 'node' in web.req.qsparams: | 1355 if b'node' in web.req.qsparams: |
1352 ctx = webutil.changectx(web.repo, web.req) | 1356 ctx = webutil.changectx(web.repo, web.req) |
1353 symrev = webutil.symrevorshortnode(web.req, ctx) | 1357 symrev = webutil.symrevorshortnode(web.req, ctx) |
1354 else: | 1358 else: |
1355 ctx = web.repo['tip'] | 1359 ctx = web.repo[b'tip'] |
1356 symrev = 'tip' | 1360 symrev = b'tip' |
1357 rev = ctx.rev() | 1361 rev = ctx.rev() |
1358 | 1362 |
1359 bg_height = 39 | 1363 bg_height = 39 |
1360 revcount = web.maxshortchanges | 1364 revcount = web.maxshortchanges |
1361 if 'revcount' in web.req.qsparams: | 1365 if b'revcount' in web.req.qsparams: |
1362 try: | 1366 try: |
1363 revcount = int(web.req.qsparams.get('revcount', revcount)) | 1367 revcount = int(web.req.qsparams.get(b'revcount', revcount)) |
1364 revcount = max(revcount, 1) | 1368 revcount = max(revcount, 1) |
1365 web.tmpl.defaults['sessionvars']['revcount'] = revcount | 1369 web.tmpl.defaults[b'sessionvars'][b'revcount'] = revcount |
1366 except ValueError: | 1370 except ValueError: |
1367 pass | 1371 pass |
1368 | 1372 |
1369 lessvars = copy.copy(web.tmpl.defaults['sessionvars']) | 1373 lessvars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
1370 lessvars['revcount'] = max(revcount // 2, 1) | 1374 lessvars[b'revcount'] = max(revcount // 2, 1) |
1371 morevars = copy.copy(web.tmpl.defaults['sessionvars']) | 1375 morevars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
1372 morevars['revcount'] = revcount * 2 | 1376 morevars[b'revcount'] = revcount * 2 |
1373 | 1377 |
1374 graphtop = web.req.qsparams.get('graphtop', ctx.hex()) | 1378 graphtop = web.req.qsparams.get(b'graphtop', ctx.hex()) |
1375 graphvars = copy.copy(web.tmpl.defaults['sessionvars']) | 1379 graphvars = copy.copy(web.tmpl.defaults[b'sessionvars']) |
1376 graphvars['graphtop'] = graphtop | 1380 graphvars[b'graphtop'] = graphtop |
1377 | 1381 |
1378 count = len(web.repo) | 1382 count = len(web.repo) |
1379 pos = rev | 1383 pos = rev |
1380 | 1384 |
1381 uprev = min(max(0, count - 1), rev + revcount) | 1385 uprev = min(max(0, count - 1), rev + revcount) |
1424 return tree | 1428 return tree |
1425 | 1429 |
1426 def jsdata(context): | 1430 def jsdata(context): |
1427 for (id, type, ctx, vtx, edges) in fulltree(): | 1431 for (id, type, ctx, vtx, edges) in fulltree(): |
1428 yield { | 1432 yield { |
1429 'node': pycompat.bytestr(ctx), | 1433 b'node': pycompat.bytestr(ctx), |
1430 'graphnode': webutil.getgraphnode(web.repo, ctx), | 1434 b'graphnode': webutil.getgraphnode(web.repo, ctx), |
1431 'vertex': vtx, | 1435 b'vertex': vtx, |
1432 'edges': edges, | 1436 b'edges': edges, |
1433 } | 1437 } |
1434 | 1438 |
1435 def nodes(context): | 1439 def nodes(context): |
1436 parity = paritygen(web.stripecount) | 1440 parity = paritygen(web.stripecount) |
1437 for row, (id, type, ctx, vtx, edges) in enumerate(tree): | 1441 for row, (id, type, ctx, vtx, edges) in enumerate(tree): |
1438 entry = webutil.commonentry(web.repo, ctx) | 1442 entry = webutil.commonentry(web.repo, ctx) |
1439 edgedata = [ | 1443 edgedata = [ |
1440 { | 1444 { |
1441 'col': edge[0], | 1445 b'col': edge[0], |
1442 'nextcol': edge[1], | 1446 b'nextcol': edge[1], |
1443 'color': (edge[2] - 1) % 6 + 1, | 1447 b'color': (edge[2] - 1) % 6 + 1, |
1444 'width': edge[3], | 1448 b'width': edge[3], |
1445 'bcolor': edge[4], | 1449 b'bcolor': edge[4], |
1446 } | 1450 } |
1447 for edge in edges | 1451 for edge in edges |
1448 ] | 1452 ] |
1449 | 1453 |
1450 entry.update( | 1454 entry.update( |
1451 { | 1455 { |
1452 'col': vtx[0], | 1456 b'col': vtx[0], |
1453 'color': (vtx[1] - 1) % 6 + 1, | 1457 b'color': (vtx[1] - 1) % 6 + 1, |
1454 'parity': next(parity), | 1458 b'parity': next(parity), |
1455 'edges': templateutil.mappinglist(edgedata), | 1459 b'edges': templateutil.mappinglist(edgedata), |
1456 'row': row, | 1460 b'row': row, |
1457 'nextrow': row + 1, | 1461 b'nextrow': row + 1, |
1458 } | 1462 } |
1459 ) | 1463 ) |
1460 | 1464 |
1461 yield entry | 1465 yield entry |
1462 | 1466 |
1463 rows = len(tree) | 1467 rows = len(tree) |
1464 | 1468 |
1465 return web.sendtemplate( | 1469 return web.sendtemplate( |
1466 'graph', | 1470 b'graph', |
1467 rev=rev, | 1471 rev=rev, |
1468 symrev=symrev, | 1472 symrev=symrev, |
1469 revcount=revcount, | 1473 revcount=revcount, |
1470 uprev=uprev, | 1474 uprev=uprev, |
1471 lessvars=lessvars, | 1475 lessvars=lessvars, |
1477 changesets=count, | 1481 changesets=count, |
1478 nextentry=templateutil.mappinglist(nextentry), | 1482 nextentry=templateutil.mappinglist(nextentry), |
1479 jsdata=templateutil.mappinggenerator(jsdata), | 1483 jsdata=templateutil.mappinggenerator(jsdata), |
1480 nodes=templateutil.mappinggenerator(nodes), | 1484 nodes=templateutil.mappinggenerator(nodes), |
1481 node=ctx.hex(), | 1485 node=ctx.hex(), |
1482 archives=web.archivelist('tip'), | 1486 archives=web.archivelist(b'tip'), |
1483 changenav=changenav, | 1487 changenav=changenav, |
1484 ) | 1488 ) |
1485 | 1489 |
1486 | 1490 |
1487 def _getdoc(e): | 1491 def _getdoc(e): |
1488 doc = e[0].__doc__ | 1492 doc = e[0].__doc__ |
1489 if doc: | 1493 if doc: |
1490 doc = _(doc).partition('\n')[0] | 1494 doc = _(doc).partition(b'\n')[0] |
1491 else: | 1495 else: |
1492 doc = _('(no help text available)') | 1496 doc = _(b'(no help text available)') |
1493 return doc | 1497 return doc |
1494 | 1498 |
1495 | 1499 |
1496 @webcommand('help') | 1500 @webcommand(b'help') |
1497 def help(web): | 1501 def help(web): |
1498 """ | 1502 """ |
1499 /help[/{topic}] | 1503 /help[/{topic}] |
1500 --------------- | 1504 --------------- |
1501 | 1505 |
1508 The ``help`` template will be rendered when requesting help for a topic. | 1512 The ``help`` template will be rendered when requesting help for a topic. |
1509 ``helptopics`` will be rendered for the index of help topics. | 1513 ``helptopics`` will be rendered for the index of help topics. |
1510 """ | 1514 """ |
1511 from .. import commands, help as helpmod # avoid cycle | 1515 from .. import commands, help as helpmod # avoid cycle |
1512 | 1516 |
1513 topicname = web.req.qsparams.get('node') | 1517 topicname = web.req.qsparams.get(b'node') |
1514 if not topicname: | 1518 if not topicname: |
1515 | 1519 |
1516 def topics(context): | 1520 def topics(context): |
1517 for h in helpmod.helptable: | 1521 for h in helpmod.helptable: |
1518 entries, summary, _doc = h[0:3] | 1522 entries, summary, _doc = h[0:3] |
1519 yield {'topic': entries[0], 'summary': summary} | 1523 yield {b'topic': entries[0], b'summary': summary} |
1520 | 1524 |
1521 early, other = [], [] | 1525 early, other = [], [] |
1522 primary = lambda s: s.partition('|')[0] | 1526 primary = lambda s: s.partition(b'|')[0] |
1523 for c, e in commands.table.iteritems(): | 1527 for c, e in commands.table.iteritems(): |
1524 doc = _getdoc(e) | 1528 doc = _getdoc(e) |
1525 if 'DEPRECATED' in doc or c.startswith('debug'): | 1529 if b'DEPRECATED' in doc or c.startswith(b'debug'): |
1526 continue | 1530 continue |
1527 cmd = primary(c) | 1531 cmd = primary(c) |
1528 if getattr(e[0], 'helpbasic', False): | 1532 if getattr(e[0], 'helpbasic', False): |
1529 early.append((cmd, doc)) | 1533 early.append((cmd, doc)) |
1530 else: | 1534 else: |
1533 early.sort() | 1537 early.sort() |
1534 other.sort() | 1538 other.sort() |
1535 | 1539 |
1536 def earlycommands(context): | 1540 def earlycommands(context): |
1537 for c, doc in early: | 1541 for c, doc in early: |
1538 yield {'topic': c, 'summary': doc} | 1542 yield {b'topic': c, b'summary': doc} |
1539 | 1543 |
1540 def othercommands(context): | 1544 def othercommands(context): |
1541 for c, doc in other: | 1545 for c, doc in other: |
1542 yield {'topic': c, 'summary': doc} | 1546 yield {b'topic': c, b'summary': doc} |
1543 | 1547 |
1544 return web.sendtemplate( | 1548 return web.sendtemplate( |
1545 'helptopics', | 1549 b'helptopics', |
1546 topics=templateutil.mappinggenerator(topics), | 1550 topics=templateutil.mappinggenerator(topics), |
1547 earlycommands=templateutil.mappinggenerator(earlycommands), | 1551 earlycommands=templateutil.mappinggenerator(earlycommands), |
1548 othercommands=templateutil.mappinggenerator(othercommands), | 1552 othercommands=templateutil.mappinggenerator(othercommands), |
1549 title='Index', | 1553 title=b'Index', |
1550 ) | 1554 ) |
1551 | 1555 |
1552 # Render an index of sub-topics. | 1556 # Render an index of sub-topics. |
1553 if topicname in helpmod.subtopics: | 1557 if topicname in helpmod.subtopics: |
1554 topics = [] | 1558 topics = [] |
1555 for entries, summary, _doc in helpmod.subtopics[topicname]: | 1559 for entries, summary, _doc in helpmod.subtopics[topicname]: |
1556 topics.append( | 1560 topics.append( |
1557 { | 1561 { |
1558 'topic': '%s.%s' % (topicname, entries[0]), | 1562 b'topic': b'%s.%s' % (topicname, entries[0]), |
1559 'basename': entries[0], | 1563 b'basename': entries[0], |
1560 'summary': summary, | 1564 b'summary': summary, |
1561 } | 1565 } |
1562 ) | 1566 ) |
1563 | 1567 |
1564 return web.sendtemplate( | 1568 return web.sendtemplate( |
1565 'helptopics', | 1569 b'helptopics', |
1566 topics=templateutil.mappinglist(topics), | 1570 topics=templateutil.mappinglist(topics), |
1567 title=topicname, | 1571 title=topicname, |
1568 subindex=True, | 1572 subindex=True, |
1569 ) | 1573 ) |
1570 | 1574 |
1571 u = webutil.wsgiui.load() | 1575 u = webutil.wsgiui.load() |
1572 u.verbose = True | 1576 u.verbose = True |
1573 | 1577 |
1574 # Render a page from a sub-topic. | 1578 # Render a page from a sub-topic. |
1575 if '.' in topicname: | 1579 if b'.' in topicname: |
1576 # TODO implement support for rendering sections, like | 1580 # TODO implement support for rendering sections, like |
1577 # `hg help` works. | 1581 # `hg help` works. |
1578 topic, subtopic = topicname.split('.', 1) | 1582 topic, subtopic = topicname.split(b'.', 1) |
1579 if topic not in helpmod.subtopics: | 1583 if topic not in helpmod.subtopics: |
1580 raise ErrorResponse(HTTP_NOT_FOUND) | 1584 raise ErrorResponse(HTTP_NOT_FOUND) |
1581 else: | 1585 else: |
1582 topic = topicname | 1586 topic = topicname |
1583 subtopic = None | 1587 subtopic = None |
1585 try: | 1589 try: |
1586 doc = helpmod.help_(u, commands, topic, subtopic=subtopic) | 1590 doc = helpmod.help_(u, commands, topic, subtopic=subtopic) |
1587 except error.Abort: | 1591 except error.Abort: |
1588 raise ErrorResponse(HTTP_NOT_FOUND) | 1592 raise ErrorResponse(HTTP_NOT_FOUND) |
1589 | 1593 |
1590 return web.sendtemplate('help', topic=topicname, doc=doc) | 1594 return web.sendtemplate(b'help', topic=topicname, doc=doc) |
1591 | 1595 |
1592 | 1596 |
1593 # tell hggettext to extract docstrings from these functions: | 1597 # tell hggettext to extract docstrings from these functions: |
1594 i18nfunctions = commands.values() | 1598 i18nfunctions = commands.values() |