##// END OF EJS Templates
hgweb: added revision date to annotate line data...
Oli Thissen -
r13199:a38df125 default
parent child Browse files
Show More
@@ -1,783 +1,784 b''
1 1 #
2 2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import os, mimetypes, re, cgi, copy
9 9 import webutil
10 10 from mercurial import error, encoding, archival, templater, templatefilters
11 11 from mercurial.node import short, hex
12 12 from mercurial.util import binary
13 13 from common import paritygen, staticfile, get_contact, ErrorResponse
14 14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
15 15 from mercurial import graphmod
16 16 from mercurial import help as helpmod
17 17 from mercurial.i18n import _
18 18
19 19 # __all__ is populated with the allowed commands. Be sure to add to it if
20 20 # you're adding a new command, or the new command won't work.
21 21
22 22 __all__ = [
23 23 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
24 24 'manifest', 'tags', 'branches', 'summary', 'filediff', 'diff', 'annotate',
25 25 'filelog', 'archive', 'static', 'graph', 'help',
26 26 ]
27 27
28 28 def log(web, req, tmpl):
29 29 if 'file' in req.form and req.form['file'][0]:
30 30 return filelog(web, req, tmpl)
31 31 else:
32 32 return changelog(web, req, tmpl)
33 33
34 34 def rawfile(web, req, tmpl):
35 35 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
36 36 if not path:
37 37 content = manifest(web, req, tmpl)
38 38 req.respond(HTTP_OK, web.ctype)
39 39 return content
40 40
41 41 try:
42 42 fctx = webutil.filectx(web.repo, req)
43 43 except error.LookupError, inst:
44 44 try:
45 45 content = manifest(web, req, tmpl)
46 46 req.respond(HTTP_OK, web.ctype)
47 47 return content
48 48 except ErrorResponse:
49 49 raise inst
50 50
51 51 path = fctx.path()
52 52 text = fctx.data()
53 53 mt = mimetypes.guess_type(path)[0]
54 54 if mt is None:
55 55 mt = binary(text) and 'application/octet-stream' or 'text/plain'
56 56 if mt.startswith('text/'):
57 57 mt += '; charset="%s"' % encoding.encoding
58 58
59 59 req.respond(HTTP_OK, mt, path, len(text))
60 60 return [text]
61 61
62 62 def _filerevision(web, tmpl, fctx):
63 63 f = fctx.path()
64 64 text = fctx.data()
65 65 parity = paritygen(web.stripecount)
66 66
67 67 if binary(text):
68 68 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
69 69 text = '(binary:%s)' % mt
70 70
71 71 def lines():
72 72 for lineno, t in enumerate(text.splitlines(True)):
73 73 yield {"line": t,
74 74 "lineid": "l%d" % (lineno + 1),
75 75 "linenumber": "% 6d" % (lineno + 1),
76 76 "parity": parity.next()}
77 77
78 78 return tmpl("filerevision",
79 79 file=f,
80 80 path=webutil.up(f),
81 81 text=lines(),
82 82 rev=fctx.rev(),
83 83 node=hex(fctx.node()),
84 84 author=fctx.user(),
85 85 date=fctx.date(),
86 86 desc=fctx.description(),
87 87 branch=webutil.nodebranchnodefault(fctx),
88 88 parent=webutil.parents(fctx),
89 89 child=webutil.children(fctx),
90 90 rename=webutil.renamelink(fctx),
91 91 permissions=fctx.manifest().flags(f))
92 92
93 93 def file(web, req, tmpl):
94 94 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
95 95 if not path:
96 96 return manifest(web, req, tmpl)
97 97 try:
98 98 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
99 99 except error.LookupError, inst:
100 100 try:
101 101 return manifest(web, req, tmpl)
102 102 except ErrorResponse:
103 103 raise inst
104 104
105 105 def _search(web, req, tmpl):
106 106
107 107 query = req.form['rev'][0]
108 108 revcount = web.maxchanges
109 109 if 'revcount' in req.form:
110 110 revcount = int(req.form.get('revcount', [revcount])[0])
111 111 tmpl.defaults['sessionvars']['revcount'] = revcount
112 112
113 113 lessvars = copy.copy(tmpl.defaults['sessionvars'])
114 114 lessvars['revcount'] = revcount / 2
115 115 lessvars['rev'] = query
116 116 morevars = copy.copy(tmpl.defaults['sessionvars'])
117 117 morevars['revcount'] = revcount * 2
118 118 morevars['rev'] = query
119 119
120 120 def changelist(**map):
121 121 count = 0
122 122 qw = query.lower().split()
123 123
124 124 def revgen():
125 125 for i in xrange(len(web.repo) - 1, 0, -100):
126 126 l = []
127 127 for j in xrange(max(0, i - 100), i + 1):
128 128 ctx = web.repo[j]
129 129 l.append(ctx)
130 130 l.reverse()
131 131 for e in l:
132 132 yield e
133 133
134 134 for ctx in revgen():
135 135 miss = 0
136 136 for q in qw:
137 137 if not (q in ctx.user().lower() or
138 138 q in ctx.description().lower() or
139 139 q in " ".join(ctx.files()).lower()):
140 140 miss = 1
141 141 break
142 142 if miss:
143 143 continue
144 144
145 145 count += 1
146 146 n = ctx.node()
147 147 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
148 148 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
149 149
150 150 yield tmpl('searchentry',
151 151 parity=parity.next(),
152 152 author=ctx.user(),
153 153 parent=webutil.parents(ctx),
154 154 child=webutil.children(ctx),
155 155 changelogtag=showtags,
156 156 desc=ctx.description(),
157 157 date=ctx.date(),
158 158 files=files,
159 159 rev=ctx.rev(),
160 160 node=hex(n),
161 161 tags=webutil.nodetagsdict(web.repo, n),
162 162 inbranch=webutil.nodeinbranch(web.repo, ctx),
163 163 branches=webutil.nodebranchdict(web.repo, ctx))
164 164
165 165 if count >= revcount:
166 166 break
167 167
168 168 tip = web.repo['tip']
169 169 parity = paritygen(web.stripecount)
170 170
171 171 return tmpl('search', query=query, node=tip.hex(),
172 172 entries=changelist, archives=web.archivelist("tip"),
173 173 morevars=morevars, lessvars=lessvars)
174 174
175 175 def changelog(web, req, tmpl, shortlog=False):
176 176
177 177 if 'node' in req.form:
178 178 ctx = webutil.changectx(web.repo, req)
179 179 else:
180 180 if 'rev' in req.form:
181 181 hi = req.form['rev'][0]
182 182 else:
183 183 hi = len(web.repo) - 1
184 184 try:
185 185 ctx = web.repo[hi]
186 186 except error.RepoError:
187 187 return _search(web, req, tmpl) # XXX redirect to 404 page?
188 188
189 189 def changelist(limit=0, **map):
190 190 l = [] # build a list in forward order for efficiency
191 191 for i in xrange(start, end):
192 192 ctx = web.repo[i]
193 193 n = ctx.node()
194 194 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
195 195 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
196 196
197 197 l.insert(0, {"parity": parity.next(),
198 198 "author": ctx.user(),
199 199 "parent": webutil.parents(ctx, i - 1),
200 200 "child": webutil.children(ctx, i + 1),
201 201 "changelogtag": showtags,
202 202 "desc": ctx.description(),
203 203 "date": ctx.date(),
204 204 "files": files,
205 205 "rev": i,
206 206 "node": hex(n),
207 207 "tags": webutil.nodetagsdict(web.repo, n),
208 208 "inbranch": webutil.nodeinbranch(web.repo, ctx),
209 209 "branches": webutil.nodebranchdict(web.repo, ctx)
210 210 })
211 211
212 212 if limit > 0:
213 213 l = l[:limit]
214 214
215 215 for e in l:
216 216 yield e
217 217
218 218 revcount = shortlog and web.maxshortchanges or web.maxchanges
219 219 if 'revcount' in req.form:
220 220 revcount = int(req.form.get('revcount', [revcount])[0])
221 221 tmpl.defaults['sessionvars']['revcount'] = revcount
222 222
223 223 lessvars = copy.copy(tmpl.defaults['sessionvars'])
224 224 lessvars['revcount'] = revcount / 2
225 225 morevars = copy.copy(tmpl.defaults['sessionvars'])
226 226 morevars['revcount'] = revcount * 2
227 227
228 228 count = len(web.repo)
229 229 pos = ctx.rev()
230 230 start = max(0, pos - revcount + 1)
231 231 end = min(count, start + revcount)
232 232 pos = end - 1
233 233 parity = paritygen(web.stripecount, offset=start - end)
234 234
235 235 changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx)
236 236
237 237 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
238 238 node=hex(ctx.node()), rev=pos, changesets=count,
239 239 entries=lambda **x: changelist(limit=0,**x),
240 240 latestentry=lambda **x: changelist(limit=1,**x),
241 241 archives=web.archivelist("tip"), revcount=revcount,
242 242 morevars=morevars, lessvars=lessvars)
243 243
244 244 def shortlog(web, req, tmpl):
245 245 return changelog(web, req, tmpl, shortlog = True)
246 246
247 247 def changeset(web, req, tmpl):
248 248 ctx = webutil.changectx(web.repo, req)
249 249 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
250 250 showbranch = webutil.nodebranchnodefault(ctx)
251 251
252 252 files = []
253 253 parity = paritygen(web.stripecount)
254 254 for f in ctx.files():
255 255 template = f in ctx and 'filenodelink' or 'filenolink'
256 256 files.append(tmpl(template,
257 257 node=ctx.hex(), file=f,
258 258 parity=parity.next()))
259 259
260 260 parity = paritygen(web.stripecount)
261 261 style = web.config('web', 'style', 'paper')
262 262 if 'style' in req.form:
263 263 style = req.form['style'][0]
264 264
265 265 diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style)
266 266 return tmpl('changeset',
267 267 diff=diffs,
268 268 rev=ctx.rev(),
269 269 node=ctx.hex(),
270 270 parent=webutil.parents(ctx),
271 271 child=webutil.children(ctx),
272 272 changesettag=showtags,
273 273 changesetbranch=showbranch,
274 274 author=ctx.user(),
275 275 desc=ctx.description(),
276 276 date=ctx.date(),
277 277 files=files,
278 278 archives=web.archivelist(ctx.hex()),
279 279 tags=webutil.nodetagsdict(web.repo, ctx.node()),
280 280 branch=webutil.nodebranchnodefault(ctx),
281 281 inbranch=webutil.nodeinbranch(web.repo, ctx),
282 282 branches=webutil.nodebranchdict(web.repo, ctx))
283 283
284 284 rev = changeset
285 285
286 286 def manifest(web, req, tmpl):
287 287 ctx = webutil.changectx(web.repo, req)
288 288 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
289 289 mf = ctx.manifest()
290 290 node = ctx.node()
291 291
292 292 files = {}
293 293 dirs = {}
294 294 parity = paritygen(web.stripecount)
295 295
296 296 if path and path[-1] != "/":
297 297 path += "/"
298 298 l = len(path)
299 299 abspath = "/" + path
300 300
301 301 for f, n in mf.iteritems():
302 302 if f[:l] != path:
303 303 continue
304 304 remain = f[l:]
305 305 elements = remain.split('/')
306 306 if len(elements) == 1:
307 307 files[remain] = f
308 308 else:
309 309 h = dirs # need to retain ref to dirs (root)
310 310 for elem in elements[0:-1]:
311 311 if elem not in h:
312 312 h[elem] = {}
313 313 h = h[elem]
314 314 if len(h) > 1:
315 315 break
316 316 h[None] = None # denotes files present
317 317
318 318 if mf and not files and not dirs:
319 319 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
320 320
321 321 def filelist(**map):
322 322 for f in sorted(files):
323 323 full = files[f]
324 324
325 325 fctx = ctx.filectx(full)
326 326 yield {"file": full,
327 327 "parity": parity.next(),
328 328 "basename": f,
329 329 "date": fctx.date(),
330 330 "size": fctx.size(),
331 331 "permissions": mf.flags(full)}
332 332
333 333 def dirlist(**map):
334 334 for d in sorted(dirs):
335 335
336 336 emptydirs = []
337 337 h = dirs[d]
338 338 while isinstance(h, dict) and len(h) == 1:
339 339 k, v = h.items()[0]
340 340 if v:
341 341 emptydirs.append(k)
342 342 h = v
343 343
344 344 path = "%s%s" % (abspath, d)
345 345 yield {"parity": parity.next(),
346 346 "path": path,
347 347 "emptydirs": "/".join(emptydirs),
348 348 "basename": d}
349 349
350 350 return tmpl("manifest",
351 351 rev=ctx.rev(),
352 352 node=hex(node),
353 353 path=abspath,
354 354 up=webutil.up(abspath),
355 355 upparity=parity.next(),
356 356 fentries=filelist,
357 357 dentries=dirlist,
358 358 archives=web.archivelist(hex(node)),
359 359 tags=webutil.nodetagsdict(web.repo, node),
360 360 inbranch=webutil.nodeinbranch(web.repo, ctx),
361 361 branches=webutil.nodebranchdict(web.repo, ctx))
362 362
363 363 def tags(web, req, tmpl):
364 364 i = web.repo.tagslist()
365 365 i.reverse()
366 366 parity = paritygen(web.stripecount)
367 367
368 368 def entries(notip=False, limit=0, **map):
369 369 count = 0
370 370 for k, n in i:
371 371 if notip and k == "tip":
372 372 continue
373 373 if limit > 0 and count >= limit:
374 374 continue
375 375 count = count + 1
376 376 yield {"parity": parity.next(),
377 377 "tag": k,
378 378 "date": web.repo[n].date(),
379 379 "node": hex(n)}
380 380
381 381 return tmpl("tags",
382 382 node=hex(web.repo.changelog.tip()),
383 383 entries=lambda **x: entries(False, 0, **x),
384 384 entriesnotip=lambda **x: entries(True, 0, **x),
385 385 latestentry=lambda **x: entries(True, 1, **x))
386 386
387 387 def branches(web, req, tmpl):
388 388 tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
389 389 heads = web.repo.heads()
390 390 parity = paritygen(web.stripecount)
391 391 sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev())
392 392
393 393 def entries(limit, **map):
394 394 count = 0
395 395 for ctx in sorted(tips, key=sortkey, reverse=True):
396 396 if limit > 0 and count >= limit:
397 397 return
398 398 count += 1
399 399 if ctx.node() not in heads:
400 400 status = 'inactive'
401 401 elif not web.repo.branchheads(ctx.branch()):
402 402 status = 'closed'
403 403 else:
404 404 status = 'open'
405 405 yield {'parity': parity.next(),
406 406 'branch': ctx.branch(),
407 407 'status': status,
408 408 'node': ctx.hex(),
409 409 'date': ctx.date()}
410 410
411 411 return tmpl('branches', node=hex(web.repo.changelog.tip()),
412 412 entries=lambda **x: entries(0, **x),
413 413 latestentry=lambda **x: entries(1, **x))
414 414
415 415 def summary(web, req, tmpl):
416 416 i = web.repo.tagslist()
417 417 i.reverse()
418 418
419 419 def tagentries(**map):
420 420 parity = paritygen(web.stripecount)
421 421 count = 0
422 422 for k, n in i:
423 423 if k == "tip": # skip tip
424 424 continue
425 425
426 426 count += 1
427 427 if count > 10: # limit to 10 tags
428 428 break
429 429
430 430 yield tmpl("tagentry",
431 431 parity=parity.next(),
432 432 tag=k,
433 433 node=hex(n),
434 434 date=web.repo[n].date())
435 435
436 436 def branches(**map):
437 437 parity = paritygen(web.stripecount)
438 438
439 439 b = web.repo.branchtags()
440 440 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
441 441 for r, n, t in sorted(l):
442 442 yield {'parity': parity.next(),
443 443 'branch': t,
444 444 'node': hex(n),
445 445 'date': web.repo[n].date()}
446 446
447 447 def changelist(**map):
448 448 parity = paritygen(web.stripecount, offset=start - end)
449 449 l = [] # build a list in forward order for efficiency
450 450 for i in xrange(start, end):
451 451 ctx = web.repo[i]
452 452 n = ctx.node()
453 453 hn = hex(n)
454 454
455 455 l.insert(0, tmpl(
456 456 'shortlogentry',
457 457 parity=parity.next(),
458 458 author=ctx.user(),
459 459 desc=ctx.description(),
460 460 date=ctx.date(),
461 461 rev=i,
462 462 node=hn,
463 463 tags=webutil.nodetagsdict(web.repo, n),
464 464 inbranch=webutil.nodeinbranch(web.repo, ctx),
465 465 branches=webutil.nodebranchdict(web.repo, ctx)))
466 466
467 467 yield l
468 468
469 469 tip = web.repo['tip']
470 470 count = len(web.repo)
471 471 start = max(0, count - web.maxchanges)
472 472 end = min(count, start + web.maxchanges)
473 473
474 474 return tmpl("summary",
475 475 desc=web.config("web", "description", "unknown"),
476 476 owner=get_contact(web.config) or "unknown",
477 477 lastchange=tip.date(),
478 478 tags=tagentries,
479 479 branches=branches,
480 480 shortlog=changelist,
481 481 node=tip.hex(),
482 482 archives=web.archivelist("tip"))
483 483
484 484 def filediff(web, req, tmpl):
485 485 fctx, ctx = None, None
486 486 try:
487 487 fctx = webutil.filectx(web.repo, req)
488 488 except LookupError:
489 489 ctx = webutil.changectx(web.repo, req)
490 490 path = webutil.cleanpath(web.repo, req.form['file'][0])
491 491 if path not in ctx.files():
492 492 raise
493 493
494 494 if fctx is not None:
495 495 n = fctx.node()
496 496 path = fctx.path()
497 497 else:
498 498 n = ctx.node()
499 499 # path already defined in except clause
500 500
501 501 parity = paritygen(web.stripecount)
502 502 style = web.config('web', 'style', 'paper')
503 503 if 'style' in req.form:
504 504 style = req.form['style'][0]
505 505
506 506 diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity, style)
507 507 rename = fctx and webutil.renamelink(fctx) or []
508 508 ctx = fctx and fctx or ctx
509 509 return tmpl("filediff",
510 510 file=path,
511 511 node=hex(n),
512 512 rev=ctx.rev(),
513 513 date=ctx.date(),
514 514 desc=ctx.description(),
515 515 author=ctx.user(),
516 516 rename=rename,
517 517 branch=webutil.nodebranchnodefault(ctx),
518 518 parent=webutil.parents(ctx),
519 519 child=webutil.children(ctx),
520 520 diff=diffs)
521 521
522 522 diff = filediff
523 523
524 524 def annotate(web, req, tmpl):
525 525 fctx = webutil.filectx(web.repo, req)
526 526 f = fctx.path()
527 527 parity = paritygen(web.stripecount)
528 528
529 529 def annotate(**map):
530 530 last = None
531 531 if binary(fctx.data()):
532 532 mt = (mimetypes.guess_type(fctx.path())[0]
533 533 or 'application/octet-stream')
534 534 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
535 535 '(binary:%s)' % mt)])
536 536 else:
537 537 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
538 538 for lineno, ((f, targetline), l) in lines:
539 539 fnode = f.filenode()
540 540
541 541 if last != fnode:
542 542 last = fnode
543 543
544 544 yield {"parity": parity.next(),
545 545 "node": hex(f.node()),
546 546 "rev": f.rev(),
547 547 "author": f.user(),
548 548 "desc": f.description(),
549 549 "file": f.path(),
550 550 "targetline": targetline,
551 551 "line": l,
552 552 "lineid": "l%d" % (lineno + 1),
553 "linenumber": "% 6d" % (lineno + 1)}
553 "linenumber": "% 6d" % (lineno + 1),
554 "revdate": f.date()}
554 555
555 556 return tmpl("fileannotate",
556 557 file=f,
557 558 annotate=annotate,
558 559 path=webutil.up(f),
559 560 rev=fctx.rev(),
560 561 node=hex(fctx.node()),
561 562 author=fctx.user(),
562 563 date=fctx.date(),
563 564 desc=fctx.description(),
564 565 rename=webutil.renamelink(fctx),
565 566 branch=webutil.nodebranchnodefault(fctx),
566 567 parent=webutil.parents(fctx),
567 568 child=webutil.children(fctx),
568 569 permissions=fctx.manifest().flags(f))
569 570
570 571 def filelog(web, req, tmpl):
571 572
572 573 try:
573 574 fctx = webutil.filectx(web.repo, req)
574 575 f = fctx.path()
575 576 fl = fctx.filelog()
576 577 except error.LookupError:
577 578 f = webutil.cleanpath(web.repo, req.form['file'][0])
578 579 fl = web.repo.file(f)
579 580 numrevs = len(fl)
580 581 if not numrevs: # file doesn't exist at all
581 582 raise
582 583 rev = webutil.changectx(web.repo, req).rev()
583 584 first = fl.linkrev(0)
584 585 if rev < first: # current rev is from before file existed
585 586 raise
586 587 frev = numrevs - 1
587 588 while fl.linkrev(frev) > rev:
588 589 frev -= 1
589 590 fctx = web.repo.filectx(f, fl.linkrev(frev))
590 591
591 592 revcount = web.maxshortchanges
592 593 if 'revcount' in req.form:
593 594 revcount = int(req.form.get('revcount', [revcount])[0])
594 595 tmpl.defaults['sessionvars']['revcount'] = revcount
595 596
596 597 lessvars = copy.copy(tmpl.defaults['sessionvars'])
597 598 lessvars['revcount'] = revcount / 2
598 599 morevars = copy.copy(tmpl.defaults['sessionvars'])
599 600 morevars['revcount'] = revcount * 2
600 601
601 602 count = fctx.filerev() + 1
602 603 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
603 604 end = min(count, start + revcount) # last rev on this page
604 605 parity = paritygen(web.stripecount, offset=start - end)
605 606
606 607 def entries(limit=0, **map):
607 608 l = []
608 609
609 610 repo = web.repo
610 611 for i in xrange(start, end):
611 612 iterfctx = fctx.filectx(i)
612 613
613 614 l.insert(0, {"parity": parity.next(),
614 615 "filerev": i,
615 616 "file": f,
616 617 "node": hex(iterfctx.node()),
617 618 "author": iterfctx.user(),
618 619 "date": iterfctx.date(),
619 620 "rename": webutil.renamelink(iterfctx),
620 621 "parent": webutil.parents(iterfctx),
621 622 "child": webutil.children(iterfctx),
622 623 "desc": iterfctx.description(),
623 624 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
624 625 "branch": webutil.nodebranchnodefault(iterfctx),
625 626 "inbranch": webutil.nodeinbranch(repo, iterfctx),
626 627 "branches": webutil.nodebranchdict(repo, iterfctx)})
627 628
628 629 if limit > 0:
629 630 l = l[:limit]
630 631
631 632 for e in l:
632 633 yield e
633 634
634 635 nodefunc = lambda x: fctx.filectx(fileid=x)
635 636 nav = webutil.revnavgen(end - 1, revcount, count, nodefunc)
636 637 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
637 638 entries=lambda **x: entries(limit=0, **x),
638 639 latestentry=lambda **x: entries(limit=1, **x),
639 640 revcount=revcount, morevars=morevars, lessvars=lessvars)
640 641
641 642 def archive(web, req, tmpl):
642 643 type_ = req.form.get('type', [None])[0]
643 644 allowed = web.configlist("web", "allow_archive")
644 645 key = req.form['node'][0]
645 646
646 647 if type_ not in web.archives:
647 648 msg = 'Unsupported archive type: %s' % type_
648 649 raise ErrorResponse(HTTP_NOT_FOUND, msg)
649 650
650 651 if not ((type_ in allowed or
651 652 web.configbool("web", "allow" + type_, False))):
652 653 msg = 'Archive type not allowed: %s' % type_
653 654 raise ErrorResponse(HTTP_FORBIDDEN, msg)
654 655
655 656 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
656 657 cnode = web.repo.lookup(key)
657 658 arch_version = key
658 659 if cnode == key or key == 'tip':
659 660 arch_version = short(cnode)
660 661 name = "%s-%s" % (reponame, arch_version)
661 662 mimetype, artype, extension, encoding = web.archive_specs[type_]
662 663 headers = [
663 664 ('Content-Type', mimetype),
664 665 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
665 666 ]
666 667 if encoding:
667 668 headers.append(('Content-Encoding', encoding))
668 669 req.header(headers)
669 670 req.respond(HTTP_OK)
670 671 archival.archive(web.repo, req, cnode, artype, prefix=name)
671 672 return []
672 673
673 674
674 675 def static(web, req, tmpl):
675 676 fname = req.form['file'][0]
676 677 # a repo owner may set web.static in .hg/hgrc to get any file
677 678 # readable by the user running the CGI script
678 679 static = web.config("web", "static", None, untrusted=False)
679 680 if not static:
680 681 tp = web.templatepath or templater.templatepath()
681 682 if isinstance(tp, str):
682 683 tp = [tp]
683 684 static = [os.path.join(p, 'static') for p in tp]
684 685 return [staticfile(static, fname, req)]
685 686
686 687 def graph(web, req, tmpl):
687 688
688 689 rev = webutil.changectx(web.repo, req).rev()
689 690 bg_height = 39
690 691 revcount = web.maxshortchanges
691 692 if 'revcount' in req.form:
692 693 revcount = int(req.form.get('revcount', [revcount])[0])
693 694 tmpl.defaults['sessionvars']['revcount'] = revcount
694 695
695 696 lessvars = copy.copy(tmpl.defaults['sessionvars'])
696 697 lessvars['revcount'] = revcount / 2
697 698 morevars = copy.copy(tmpl.defaults['sessionvars'])
698 699 morevars['revcount'] = revcount * 2
699 700
700 701 max_rev = len(web.repo) - 1
701 702 revcount = min(max_rev, revcount)
702 703 revnode = web.repo.changelog.node(rev)
703 704 revnode_hex = hex(revnode)
704 705 uprev = min(max_rev, rev + revcount)
705 706 downrev = max(0, rev - revcount)
706 707 count = len(web.repo)
707 708 changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
708 709
709 710 dag = graphmod.revisions(web.repo, rev, downrev)
710 711 tree = list(graphmod.colored(dag))
711 712 canvasheight = (len(tree) + 1) * bg_height - 27
712 713 data = []
713 714 for (id, type, ctx, vtx, edges) in tree:
714 715 if type != graphmod.CHANGESET:
715 716 continue
716 717 node = short(ctx.node())
717 718 age = templatefilters.age(ctx.date())
718 719 desc = templatefilters.firstline(ctx.description())
719 720 desc = cgi.escape(templatefilters.nonempty(desc))
720 721 user = cgi.escape(templatefilters.person(ctx.user()))
721 722 branch = ctx.branch()
722 723 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
723 724 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
724 725
725 726 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
726 727 lessvars=lessvars, morevars=morevars, downrev=downrev,
727 728 canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
728 729 node=revnode_hex, changenav=changenav)
729 730
730 731 def _getdoc(e):
731 732 doc = e[0].__doc__
732 733 if doc:
733 734 doc = doc.split('\n')[0]
734 735 else:
735 736 doc = _('(no help text available)')
736 737 return doc
737 738
738 739 def help(web, req, tmpl):
739 740 from mercurial import commands # avoid cycle
740 741
741 742 topicname = req.form.get('node', [None])[0]
742 743 if not topicname:
743 744 topic = []
744 745
745 746 def topics(**map):
746 747 for entries, summary, _ in helpmod.helptable:
747 748 entries = sorted(entries, key=len)
748 749 yield {'topic': entries[-1], 'summary': summary}
749 750
750 751 early, other = [], []
751 752 primary = lambda s: s.split('|')[0]
752 753 for c, e in commands.table.iteritems():
753 754 doc = _getdoc(e)
754 755 if 'DEPRECATED' in doc or c.startswith('debug'):
755 756 continue
756 757 cmd = primary(c)
757 758 if cmd.startswith('^'):
758 759 early.append((cmd[1:], doc))
759 760 else:
760 761 other.append((cmd, doc))
761 762
762 763 early.sort()
763 764 other.sort()
764 765
765 766 def earlycommands(**map):
766 767 for c, doc in early:
767 768 yield {'topic': c, 'summary': doc}
768 769
769 770 def othercommands(**map):
770 771 for c, doc in other:
771 772 yield {'topic': c, 'summary': doc}
772 773
773 774 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
774 775 othercommands=othercommands, title='Index')
775 776
776 777 u = webutil.wsgiui()
777 778 u.pushbuffer()
778 779 try:
779 780 commands.help_(u, topicname)
780 781 except error.UnknownCommand:
781 782 raise ErrorResponse(HTTP_NOT_FOUND)
782 783 doc = u.popbuffer()
783 784 return tmpl('help', topic=topicname, doc=doc)
General Comments 0
You need to be logged in to leave comments. Login now