##// END OF EJS Templates
templates: export extra as a dict to templates...
Benoit Boissinot -
r18581:3490c91a default
parent child Browse files
Show More
@@ -1,992 +1,1002 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, nullid
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, patch
16 16 from mercurial import help as helpmod
17 17 from mercurial import scmutil
18 18 from mercurial.i18n import _
19 19
20 20 # __all__ is populated with the allowed commands. Be sure to add to it if
21 21 # you're adding a new command, or the new command won't work.
22 22
23 23 __all__ = [
24 24 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
25 25 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
26 26 'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
27 27 ]
28 28
29 29 def log(web, req, tmpl):
30 30 if 'file' in req.form and req.form['file'][0]:
31 31 return filelog(web, req, tmpl)
32 32 else:
33 33 return changelog(web, req, tmpl)
34 34
35 35 def rawfile(web, req, tmpl):
36 36 guessmime = web.configbool('web', 'guessmime', False)
37 37
38 38 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
39 39 if not path:
40 40 content = manifest(web, req, tmpl)
41 41 req.respond(HTTP_OK, web.ctype)
42 42 return content
43 43
44 44 try:
45 45 fctx = webutil.filectx(web.repo, req)
46 46 except error.LookupError, inst:
47 47 try:
48 48 content = manifest(web, req, tmpl)
49 49 req.respond(HTTP_OK, web.ctype)
50 50 return content
51 51 except ErrorResponse:
52 52 raise inst
53 53
54 54 path = fctx.path()
55 55 text = fctx.data()
56 56 mt = 'application/binary'
57 57 if guessmime:
58 58 mt = mimetypes.guess_type(path)[0]
59 59 if mt is None:
60 60 mt = binary(text) and 'application/binary' or 'text/plain'
61 61 if mt.startswith('text/'):
62 62 mt += '; charset="%s"' % encoding.encoding
63 63
64 64 req.respond(HTTP_OK, mt, path, body=text)
65 65 return []
66 66
67 67 def _filerevision(web, tmpl, fctx):
68 68 f = fctx.path()
69 69 text = fctx.data()
70 70 parity = paritygen(web.stripecount)
71 71
72 72 if binary(text):
73 73 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
74 74 text = '(binary:%s)' % mt
75 75
76 76 def lines():
77 77 for lineno, t in enumerate(text.splitlines(True)):
78 78 yield {"line": t,
79 79 "lineid": "l%d" % (lineno + 1),
80 80 "linenumber": "% 6d" % (lineno + 1),
81 81 "parity": parity.next()}
82 82
83 83 return tmpl("filerevision",
84 84 file=f,
85 85 path=webutil.up(f),
86 86 text=lines(),
87 87 rev=fctx.rev(),
88 88 node=fctx.hex(),
89 89 author=fctx.user(),
90 90 date=fctx.date(),
91 91 desc=fctx.description(),
92 extra=fctx.extra(),
92 93 branch=webutil.nodebranchnodefault(fctx),
93 94 parent=webutil.parents(fctx),
94 95 child=webutil.children(fctx),
95 96 rename=webutil.renamelink(fctx),
96 97 permissions=fctx.manifest().flags(f))
97 98
98 99 def file(web, req, tmpl):
99 100 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
100 101 if not path:
101 102 return manifest(web, req, tmpl)
102 103 try:
103 104 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
104 105 except error.LookupError, inst:
105 106 try:
106 107 return manifest(web, req, tmpl)
107 108 except ErrorResponse:
108 109 raise inst
109 110
110 111 def _search(web, req, tmpl):
111 112
112 113 query = req.form['rev'][0]
113 114 revcount = web.maxchanges
114 115 if 'revcount' in req.form:
115 116 revcount = int(req.form.get('revcount', [revcount])[0])
116 117 revcount = max(revcount, 1)
117 118 tmpl.defaults['sessionvars']['revcount'] = revcount
118 119
119 120 lessvars = copy.copy(tmpl.defaults['sessionvars'])
120 121 lessvars['revcount'] = max(revcount / 2, 1)
121 122 lessvars['rev'] = query
122 123 morevars = copy.copy(tmpl.defaults['sessionvars'])
123 124 morevars['revcount'] = revcount * 2
124 125 morevars['rev'] = query
125 126
126 127 def changelist(**map):
127 128 count = 0
128 129 lower = encoding.lower
129 130 qw = lower(query).split()
130 131
131 132 def revgen():
132 133 cl = web.repo.changelog
133 134 for i in xrange(len(web.repo) - 1, 0, -100):
134 135 l = []
135 136 for j in cl.revs(max(0, i - 100), i + 1):
136 137 ctx = web.repo[j]
137 138 l.append(ctx)
138 139 l.reverse()
139 140 for e in l:
140 141 yield e
141 142
142 143 for ctx in revgen():
143 144 miss = 0
144 145 for q in qw:
145 146 if not (q in lower(ctx.user()) or
146 147 q in lower(ctx.description()) or
147 148 q in lower(" ".join(ctx.files()))):
148 149 miss = 1
149 150 break
150 151 if miss:
151 152 continue
152 153
153 154 count += 1
154 155 n = ctx.node()
155 156 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
156 157 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
157 158
158 159 yield tmpl('searchentry',
159 160 parity=parity.next(),
160 161 author=ctx.user(),
161 162 parent=webutil.parents(ctx),
162 163 child=webutil.children(ctx),
163 164 changelogtag=showtags,
164 165 desc=ctx.description(),
166 extra=ctx.extra(),
165 167 date=ctx.date(),
166 168 files=files,
167 169 rev=ctx.rev(),
168 170 node=hex(n),
169 171 tags=webutil.nodetagsdict(web.repo, n),
170 172 bookmarks=webutil.nodebookmarksdict(web.repo, n),
171 173 inbranch=webutil.nodeinbranch(web.repo, ctx),
172 174 branches=webutil.nodebranchdict(web.repo, ctx))
173 175
174 176 if count >= revcount:
175 177 break
176 178
177 179 tip = web.repo['tip']
178 180 parity = paritygen(web.stripecount)
179 181
180 182 return tmpl('search', query=query, node=tip.hex(),
181 183 entries=changelist, archives=web.archivelist("tip"),
182 184 morevars=morevars, lessvars=lessvars)
183 185
184 186 def changelog(web, req, tmpl, shortlog=False):
185 187
186 188 if 'node' in req.form:
187 189 ctx = webutil.changectx(web.repo, req)
188 190 else:
189 191 if 'rev' in req.form:
190 192 hi = req.form['rev'][0]
191 193 else:
192 194 hi = 'tip'
193 195 try:
194 196 ctx = web.repo[hi]
195 197 except error.RepoError:
196 198 return _search(web, req, tmpl) # XXX redirect to 404 page?
197 199
198 200 def changelist(latestonly, **map):
199 201 l = [] # build a list in forward order for efficiency
200 202 revs = []
201 203 if start < end:
202 204 revs = web.repo.changelog.revs(start, end - 1)
203 205 if latestonly:
204 206 for r in revs:
205 207 pass
206 208 revs = (r,)
207 209 for i in revs:
208 210 ctx = web.repo[i]
209 211 n = ctx.node()
210 212 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
211 213 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
212 214
213 215 l.append({"parity": parity.next(),
214 216 "author": ctx.user(),
215 217 "parent": webutil.parents(ctx, i - 1),
216 218 "child": webutil.children(ctx, i + 1),
217 219 "changelogtag": showtags,
218 220 "desc": ctx.description(),
221 "extra": ctx.extra(),
219 222 "date": ctx.date(),
220 223 "files": files,
221 224 "rev": i,
222 225 "node": hex(n),
223 226 "tags": webutil.nodetagsdict(web.repo, n),
224 227 "bookmarks": webutil.nodebookmarksdict(web.repo, n),
225 228 "inbranch": webutil.nodeinbranch(web.repo, ctx),
226 229 "branches": webutil.nodebranchdict(web.repo, ctx)
227 230 })
228 231 for e in reversed(l):
229 232 yield e
230 233
231 234 revcount = shortlog and web.maxshortchanges or web.maxchanges
232 235 if 'revcount' in req.form:
233 236 revcount = int(req.form.get('revcount', [revcount])[0])
234 237 revcount = max(revcount, 1)
235 238 tmpl.defaults['sessionvars']['revcount'] = revcount
236 239
237 240 lessvars = copy.copy(tmpl.defaults['sessionvars'])
238 241 lessvars['revcount'] = max(revcount / 2, 1)
239 242 morevars = copy.copy(tmpl.defaults['sessionvars'])
240 243 morevars['revcount'] = revcount * 2
241 244
242 245 count = len(web.repo)
243 246 pos = ctx.rev()
244 247 start = max(0, pos - revcount + 1)
245 248 end = min(count, start + revcount)
246 249 pos = end - 1
247 250 parity = paritygen(web.stripecount, offset=start - end)
248 251
249 252 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
250 253
251 254 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
252 255 node=ctx.hex(), rev=pos, changesets=count,
253 256 entries=lambda **x: changelist(latestonly=False, **x),
254 257 latestentry=lambda **x: changelist(latestonly=True, **x),
255 258 archives=web.archivelist("tip"), revcount=revcount,
256 259 morevars=morevars, lessvars=lessvars)
257 260
258 261 def shortlog(web, req, tmpl):
259 262 return changelog(web, req, tmpl, shortlog = True)
260 263
261 264 def changeset(web, req, tmpl):
262 265 ctx = webutil.changectx(web.repo, req)
263 266 basectx = webutil.basechangectx(web.repo, req)
264 267 if basectx is None:
265 268 basectx = ctx.p1()
266 269 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
267 270 showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
268 271 ctx.node())
269 272 showbranch = webutil.nodebranchnodefault(ctx)
270 273
271 274 files = []
272 275 parity = paritygen(web.stripecount)
273 276 for blockno, f in enumerate(ctx.files()):
274 277 template = f in ctx and 'filenodelink' or 'filenolink'
275 278 files.append(tmpl(template,
276 279 node=ctx.hex(), file=f, blockno=blockno + 1,
277 280 parity=parity.next()))
278 281
279 282 style = web.config('web', 'style', 'paper')
280 283 if 'style' in req.form:
281 284 style = req.form['style'][0]
282 285
283 286 parity = paritygen(web.stripecount)
284 287 diffs = webutil.diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
285 288
286 289 parity = paritygen(web.stripecount)
287 290 diffstatgen = webutil.diffstatgen(ctx, basectx)
288 291 diffstat = webutil.diffstat(tmpl, ctx, diffstatgen, parity)
289 292
290 293 return tmpl('changeset',
291 294 diff=diffs,
292 295 rev=ctx.rev(),
293 296 node=ctx.hex(),
294 297 parent=webutil.parents(ctx),
295 298 child=webutil.children(ctx),
296 299 basenode=basectx.hex(),
297 300 changesettag=showtags,
298 301 changesetbookmark=showbookmarks,
299 302 changesetbranch=showbranch,
300 303 author=ctx.user(),
301 304 desc=ctx.description(),
305 extra=ctx.extra(),
302 306 date=ctx.date(),
303 307 files=files,
304 308 diffsummary=lambda **x: webutil.diffsummary(diffstatgen),
305 309 diffstat=diffstat,
306 310 archives=web.archivelist(ctx.hex()),
307 311 tags=webutil.nodetagsdict(web.repo, ctx.node()),
308 312 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
309 313 branch=webutil.nodebranchnodefault(ctx),
310 314 inbranch=webutil.nodeinbranch(web.repo, ctx),
311 315 branches=webutil.nodebranchdict(web.repo, ctx))
312 316
313 317 rev = changeset
314 318
315 319 def decodepath(path):
316 320 """Hook for mapping a path in the repository to a path in the
317 321 working copy.
318 322
319 323 Extensions (e.g., largefiles) can override this to remap files in
320 324 the virtual file system presented by the manifest command below."""
321 325 return path
322 326
323 327 def manifest(web, req, tmpl):
324 328 ctx = webutil.changectx(web.repo, req)
325 329 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
326 330 mf = ctx.manifest()
327 331 node = ctx.node()
328 332
329 333 files = {}
330 334 dirs = {}
331 335 parity = paritygen(web.stripecount)
332 336
333 337 if path and path[-1] != "/":
334 338 path += "/"
335 339 l = len(path)
336 340 abspath = "/" + path
337 341
338 342 for full, n in mf.iteritems():
339 343 # the virtual path (working copy path) used for the full
340 344 # (repository) path
341 345 f = decodepath(full)
342 346
343 347 if f[:l] != path:
344 348 continue
345 349 remain = f[l:]
346 350 elements = remain.split('/')
347 351 if len(elements) == 1:
348 352 files[remain] = full
349 353 else:
350 354 h = dirs # need to retain ref to dirs (root)
351 355 for elem in elements[0:-1]:
352 356 if elem not in h:
353 357 h[elem] = {}
354 358 h = h[elem]
355 359 if len(h) > 1:
356 360 break
357 361 h[None] = None # denotes files present
358 362
359 363 if mf and not files and not dirs:
360 364 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
361 365
362 366 def filelist(**map):
363 367 for f in sorted(files):
364 368 full = files[f]
365 369
366 370 fctx = ctx.filectx(full)
367 371 yield {"file": full,
368 372 "parity": parity.next(),
369 373 "basename": f,
370 374 "date": fctx.date(),
371 375 "size": fctx.size(),
372 376 "permissions": mf.flags(full)}
373 377
374 378 def dirlist(**map):
375 379 for d in sorted(dirs):
376 380
377 381 emptydirs = []
378 382 h = dirs[d]
379 383 while isinstance(h, dict) and len(h) == 1:
380 384 k, v = h.items()[0]
381 385 if v:
382 386 emptydirs.append(k)
383 387 h = v
384 388
385 389 path = "%s%s" % (abspath, d)
386 390 yield {"parity": parity.next(),
387 391 "path": path,
388 392 "emptydirs": "/".join(emptydirs),
389 393 "basename": d}
390 394
391 395 return tmpl("manifest",
392 396 rev=ctx.rev(),
393 397 node=hex(node),
394 398 path=abspath,
395 399 up=webutil.up(abspath),
396 400 upparity=parity.next(),
397 401 fentries=filelist,
398 402 dentries=dirlist,
399 403 archives=web.archivelist(hex(node)),
400 404 tags=webutil.nodetagsdict(web.repo, node),
401 405 bookmarks=webutil.nodebookmarksdict(web.repo, node),
402 406 inbranch=webutil.nodeinbranch(web.repo, ctx),
403 407 branches=webutil.nodebranchdict(web.repo, ctx))
404 408
405 409 def tags(web, req, tmpl):
406 410 i = list(reversed(web.repo.tagslist()))
407 411 parity = paritygen(web.stripecount)
408 412
409 413 def entries(notip, latestonly, **map):
410 414 t = i
411 415 if notip:
412 416 t = [(k, n) for k, n in i if k != "tip"]
413 417 if latestonly:
414 418 t = t[:1]
415 419 for k, n in t:
416 420 yield {"parity": parity.next(),
417 421 "tag": k,
418 422 "date": web.repo[n].date(),
419 423 "node": hex(n)}
420 424
421 425 return tmpl("tags",
422 426 node=hex(web.repo.changelog.tip()),
423 427 entries=lambda **x: entries(False, False, **x),
424 428 entriesnotip=lambda **x: entries(True, False, **x),
425 429 latestentry=lambda **x: entries(True, True, **x))
426 430
427 431 def bookmarks(web, req, tmpl):
428 432 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
429 433 parity = paritygen(web.stripecount)
430 434
431 435 def entries(latestonly, **map):
432 436 if latestonly:
433 437 t = [min(i)]
434 438 else:
435 439 t = sorted(i)
436 440 for k, n in t:
437 441 yield {"parity": parity.next(),
438 442 "bookmark": k,
439 443 "date": web.repo[n].date(),
440 444 "node": hex(n)}
441 445
442 446 return tmpl("bookmarks",
443 447 node=hex(web.repo.changelog.tip()),
444 448 entries=lambda **x: entries(latestonly=False, **x),
445 449 latestentry=lambda **x: entries(latestonly=True, **x))
446 450
447 451 def branches(web, req, tmpl):
448 452 tips = []
449 453 heads = web.repo.heads()
450 454 parity = paritygen(web.stripecount)
451 455 sortkey = lambda ctx: (not ctx.closesbranch(), ctx.rev())
452 456
453 457 def entries(limit, **map):
454 458 count = 0
455 459 if not tips:
456 460 for t, n in web.repo.branchtags().iteritems():
457 461 tips.append(web.repo[n])
458 462 for ctx in sorted(tips, key=sortkey, reverse=True):
459 463 if limit > 0 and count >= limit:
460 464 return
461 465 count += 1
462 466 if not web.repo.branchheads(ctx.branch()):
463 467 status = 'closed'
464 468 elif ctx.node() not in heads:
465 469 status = 'inactive'
466 470 else:
467 471 status = 'open'
468 472 yield {'parity': parity.next(),
469 473 'branch': ctx.branch(),
470 474 'status': status,
471 475 'node': ctx.hex(),
472 476 'date': ctx.date()}
473 477
474 478 return tmpl('branches', node=hex(web.repo.changelog.tip()),
475 479 entries=lambda **x: entries(0, **x),
476 480 latestentry=lambda **x: entries(1, **x))
477 481
478 482 def summary(web, req, tmpl):
479 483 i = reversed(web.repo.tagslist())
480 484
481 485 def tagentries(**map):
482 486 parity = paritygen(web.stripecount)
483 487 count = 0
484 488 for k, n in i:
485 489 if k == "tip": # skip tip
486 490 continue
487 491
488 492 count += 1
489 493 if count > 10: # limit to 10 tags
490 494 break
491 495
492 496 yield tmpl("tagentry",
493 497 parity=parity.next(),
494 498 tag=k,
495 499 node=hex(n),
496 500 date=web.repo[n].date())
497 501
498 502 def bookmarks(**map):
499 503 parity = paritygen(web.stripecount)
500 504 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
501 505 for k, n in sorted(marks)[:10]: # limit to 10 bookmarks
502 506 yield {'parity': parity.next(),
503 507 'bookmark': k,
504 508 'date': web.repo[n].date(),
505 509 'node': hex(n)}
506 510
507 511 def branches(**map):
508 512 parity = paritygen(web.stripecount)
509 513
510 514 b = web.repo.branchtags()
511 515 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
512 516 for r, n, t in sorted(l):
513 517 yield {'parity': parity.next(),
514 518 'branch': t,
515 519 'node': hex(n),
516 520 'date': web.repo[n].date()}
517 521
518 522 def changelist(**map):
519 523 parity = paritygen(web.stripecount, offset=start - end)
520 524 l = [] # build a list in forward order for efficiency
521 525 revs = []
522 526 if start < end:
523 527 revs = web.repo.changelog.revs(start, end - 1)
524 528 for i in revs:
525 529 ctx = web.repo[i]
526 530 n = ctx.node()
527 531 hn = hex(n)
528 532
529 533 l.append(tmpl(
530 534 'shortlogentry',
531 535 parity=parity.next(),
532 536 author=ctx.user(),
533 537 desc=ctx.description(),
538 extra=ctx.extra(),
534 539 date=ctx.date(),
535 540 rev=i,
536 541 node=hn,
537 542 tags=webutil.nodetagsdict(web.repo, n),
538 543 bookmarks=webutil.nodebookmarksdict(web.repo, n),
539 544 inbranch=webutil.nodeinbranch(web.repo, ctx),
540 545 branches=webutil.nodebranchdict(web.repo, ctx)))
541 546
542 547 l.reverse()
543 548 yield l
544 549
545 550 tip = web.repo['tip']
546 551 count = len(web.repo)
547 552 start = max(0, count - web.maxchanges)
548 553 end = min(count, start + web.maxchanges)
549 554
550 555 return tmpl("summary",
551 556 desc=web.config("web", "description", "unknown"),
552 557 owner=get_contact(web.config) or "unknown",
553 558 lastchange=tip.date(),
554 559 tags=tagentries,
555 560 bookmarks=bookmarks,
556 561 branches=branches,
557 562 shortlog=changelist,
558 563 node=tip.hex(),
559 564 archives=web.archivelist("tip"))
560 565
561 566 def filediff(web, req, tmpl):
562 567 fctx, ctx = None, None
563 568 try:
564 569 fctx = webutil.filectx(web.repo, req)
565 570 except LookupError:
566 571 ctx = webutil.changectx(web.repo, req)
567 572 path = webutil.cleanpath(web.repo, req.form['file'][0])
568 573 if path not in ctx.files():
569 574 raise
570 575
571 576 if fctx is not None:
572 577 n = fctx.node()
573 578 path = fctx.path()
574 579 ctx = fctx.changectx()
575 580 else:
576 581 n = ctx.node()
577 582 # path already defined in except clause
578 583
579 584 parity = paritygen(web.stripecount)
580 585 style = web.config('web', 'style', 'paper')
581 586 if 'style' in req.form:
582 587 style = req.form['style'][0]
583 588
584 589 diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
585 590 rename = fctx and webutil.renamelink(fctx) or []
586 591 ctx = fctx and fctx or ctx
587 592 return tmpl("filediff",
588 593 file=path,
589 594 node=hex(n),
590 595 rev=ctx.rev(),
591 596 date=ctx.date(),
592 597 desc=ctx.description(),
598 extra=ctx.extra(),
593 599 author=ctx.user(),
594 600 rename=rename,
595 601 branch=webutil.nodebranchnodefault(ctx),
596 602 parent=webutil.parents(ctx),
597 603 child=webutil.children(ctx),
598 604 diff=diffs)
599 605
600 606 diff = filediff
601 607
602 608 def comparison(web, req, tmpl):
603 609 ctx = webutil.changectx(web.repo, req)
604 610 if 'file' not in req.form:
605 611 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
606 612 path = webutil.cleanpath(web.repo, req.form['file'][0])
607 613 rename = path in ctx and webutil.renamelink(ctx[path]) or []
608 614
609 615 parsecontext = lambda v: v == 'full' and -1 or int(v)
610 616 if 'context' in req.form:
611 617 context = parsecontext(req.form['context'][0])
612 618 else:
613 619 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
614 620
615 621 def filelines(f):
616 622 if binary(f.data()):
617 623 mt = mimetypes.guess_type(f.path())[0]
618 624 if not mt:
619 625 mt = 'application/octet-stream'
620 626 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
621 627 return f.data().splitlines()
622 628
623 629 if path in ctx:
624 630 fctx = ctx[path]
625 631 rightrev = fctx.filerev()
626 632 rightnode = fctx.filenode()
627 633 rightlines = filelines(fctx)
628 634 parents = fctx.parents()
629 635 if not parents:
630 636 leftrev = -1
631 637 leftnode = nullid
632 638 leftlines = ()
633 639 else:
634 640 pfctx = parents[0]
635 641 leftrev = pfctx.filerev()
636 642 leftnode = pfctx.filenode()
637 643 leftlines = filelines(pfctx)
638 644 else:
639 645 rightrev = -1
640 646 rightnode = nullid
641 647 rightlines = ()
642 648 fctx = ctx.parents()[0][path]
643 649 leftrev = fctx.filerev()
644 650 leftnode = fctx.filenode()
645 651 leftlines = filelines(fctx)
646 652
647 653 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
648 654 return tmpl('filecomparison',
649 655 file=path,
650 656 node=hex(ctx.node()),
651 657 rev=ctx.rev(),
652 658 date=ctx.date(),
653 659 desc=ctx.description(),
660 extra=ctx.extra(),
654 661 author=ctx.user(),
655 662 rename=rename,
656 663 branch=webutil.nodebranchnodefault(ctx),
657 664 parent=webutil.parents(fctx),
658 665 child=webutil.children(fctx),
659 666 leftrev=leftrev,
660 667 leftnode=hex(leftnode),
661 668 rightrev=rightrev,
662 669 rightnode=hex(rightnode),
663 670 comparison=comparison)
664 671
665 672 def annotate(web, req, tmpl):
666 673 fctx = webutil.filectx(web.repo, req)
667 674 f = fctx.path()
668 675 parity = paritygen(web.stripecount)
669 676 diffopts = patch.diffopts(web.repo.ui, untrusted=True, section='annotate')
670 677
671 678 def annotate(**map):
672 679 last = None
673 680 if binary(fctx.data()):
674 681 mt = (mimetypes.guess_type(fctx.path())[0]
675 682 or 'application/octet-stream')
676 683 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
677 684 '(binary:%s)' % mt)])
678 685 else:
679 686 lines = enumerate(fctx.annotate(follow=True, linenumber=True,
680 687 diffopts=diffopts))
681 688 for lineno, ((f, targetline), l) in lines:
682 689 fnode = f.filenode()
683 690
684 691 if last != fnode:
685 692 last = fnode
686 693
687 694 yield {"parity": parity.next(),
688 695 "node": f.hex(),
689 696 "rev": f.rev(),
690 697 "author": f.user(),
691 698 "desc": f.description(),
699 "extra": f.extra(),
692 700 "file": f.path(),
693 701 "targetline": targetline,
694 702 "line": l,
695 703 "lineid": "l%d" % (lineno + 1),
696 704 "linenumber": "% 6d" % (lineno + 1),
697 705 "revdate": f.date()}
698 706
699 707 return tmpl("fileannotate",
700 708 file=f,
701 709 annotate=annotate,
702 710 path=webutil.up(f),
703 711 rev=fctx.rev(),
704 712 node=fctx.hex(),
705 713 author=fctx.user(),
706 714 date=fctx.date(),
707 715 desc=fctx.description(),
716 extra=fctx.extra(),
708 717 rename=webutil.renamelink(fctx),
709 718 branch=webutil.nodebranchnodefault(fctx),
710 719 parent=webutil.parents(fctx),
711 720 child=webutil.children(fctx),
712 721 permissions=fctx.manifest().flags(f))
713 722
714 723 def filelog(web, req, tmpl):
715 724
716 725 try:
717 726 fctx = webutil.filectx(web.repo, req)
718 727 f = fctx.path()
719 728 fl = fctx.filelog()
720 729 except error.LookupError:
721 730 f = webutil.cleanpath(web.repo, req.form['file'][0])
722 731 fl = web.repo.file(f)
723 732 numrevs = len(fl)
724 733 if not numrevs: # file doesn't exist at all
725 734 raise
726 735 rev = webutil.changectx(web.repo, req).rev()
727 736 first = fl.linkrev(0)
728 737 if rev < first: # current rev is from before file existed
729 738 raise
730 739 frev = numrevs - 1
731 740 while fl.linkrev(frev) > rev:
732 741 frev -= 1
733 742 fctx = web.repo.filectx(f, fl.linkrev(frev))
734 743
735 744 revcount = web.maxshortchanges
736 745 if 'revcount' in req.form:
737 746 revcount = int(req.form.get('revcount', [revcount])[0])
738 747 revcount = max(revcount, 1)
739 748 tmpl.defaults['sessionvars']['revcount'] = revcount
740 749
741 750 lessvars = copy.copy(tmpl.defaults['sessionvars'])
742 751 lessvars['revcount'] = max(revcount / 2, 1)
743 752 morevars = copy.copy(tmpl.defaults['sessionvars'])
744 753 morevars['revcount'] = revcount * 2
745 754
746 755 count = fctx.filerev() + 1
747 756 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
748 757 end = min(count, start + revcount) # last rev on this page
749 758 parity = paritygen(web.stripecount, offset=start - end)
750 759
751 760 def entries(latestonly, **map):
752 761 l = []
753 762
754 763 repo = web.repo
755 764 revs = repo.changelog.revs(start, end - 1)
756 765 if latestonly:
757 766 for r in revs:
758 767 pass
759 768 revs = (r,)
760 769 for i in revs:
761 770 iterfctx = fctx.filectx(i)
762 771
763 772 l.append({"parity": parity.next(),
764 773 "filerev": i,
765 774 "file": f,
766 775 "node": iterfctx.hex(),
767 776 "author": iterfctx.user(),
768 777 "date": iterfctx.date(),
769 778 "rename": webutil.renamelink(iterfctx),
770 779 "parent": webutil.parents(iterfctx),
771 780 "child": webutil.children(iterfctx),
772 781 "desc": iterfctx.description(),
782 "extra": iterfctx.extra(),
773 783 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
774 784 "bookmarks": webutil.nodebookmarksdict(
775 785 repo, iterfctx.node()),
776 786 "branch": webutil.nodebranchnodefault(iterfctx),
777 787 "inbranch": webutil.nodeinbranch(repo, iterfctx),
778 788 "branches": webutil.nodebranchdict(repo, iterfctx)})
779 789 for e in reversed(l):
780 790 yield e
781 791
782 792 revnav = webutil.filerevnav(web.repo, fctx.path())
783 793 nav = revnav.gen(end - 1, revcount, count)
784 794 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
785 795 entries=lambda **x: entries(latestonly=False, **x),
786 796 latestentry=lambda **x: entries(latestonly=True, **x),
787 797 revcount=revcount, morevars=morevars, lessvars=lessvars)
788 798
789 799 def archive(web, req, tmpl):
790 800 type_ = req.form.get('type', [None])[0]
791 801 allowed = web.configlist("web", "allow_archive")
792 802 key = req.form['node'][0]
793 803
794 804 if type_ not in web.archives:
795 805 msg = 'Unsupported archive type: %s' % type_
796 806 raise ErrorResponse(HTTP_NOT_FOUND, msg)
797 807
798 808 if not ((type_ in allowed or
799 809 web.configbool("web", "allow" + type_, False))):
800 810 msg = 'Archive type not allowed: %s' % type_
801 811 raise ErrorResponse(HTTP_FORBIDDEN, msg)
802 812
803 813 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
804 814 cnode = web.repo.lookup(key)
805 815 arch_version = key
806 816 if cnode == key or key == 'tip':
807 817 arch_version = short(cnode)
808 818 name = "%s-%s" % (reponame, arch_version)
809 819 mimetype, artype, extension, encoding = web.archive_specs[type_]
810 820 headers = [
811 821 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
812 822 ]
813 823 if encoding:
814 824 headers.append(('Content-Encoding', encoding))
815 825 req.headers.extend(headers)
816 826 req.respond(HTTP_OK, mimetype)
817 827
818 828 ctx = webutil.changectx(web.repo, req)
819 829 archival.archive(web.repo, req, cnode, artype, prefix=name,
820 830 matchfn=scmutil.match(ctx, []),
821 831 subrepos=web.configbool("web", "archivesubrepos"))
822 832 return []
823 833
824 834
825 835 def static(web, req, tmpl):
826 836 fname = req.form['file'][0]
827 837 # a repo owner may set web.static in .hg/hgrc to get any file
828 838 # readable by the user running the CGI script
829 839 static = web.config("web", "static", None, untrusted=False)
830 840 if not static:
831 841 tp = web.templatepath or templater.templatepath()
832 842 if isinstance(tp, str):
833 843 tp = [tp]
834 844 static = [os.path.join(p, 'static') for p in tp]
835 845 return [staticfile(static, fname, req)]
836 846
837 847 def graph(web, req, tmpl):
838 848
839 849 ctx = webutil.changectx(web.repo, req)
840 850 rev = ctx.rev()
841 851
842 852 bg_height = 39
843 853 revcount = web.maxshortchanges
844 854 if 'revcount' in req.form:
845 855 revcount = int(req.form.get('revcount', [revcount])[0])
846 856 revcount = max(revcount, 1)
847 857 tmpl.defaults['sessionvars']['revcount'] = revcount
848 858
849 859 lessvars = copy.copy(tmpl.defaults['sessionvars'])
850 860 lessvars['revcount'] = max(revcount / 2, 1)
851 861 morevars = copy.copy(tmpl.defaults['sessionvars'])
852 862 morevars['revcount'] = revcount * 2
853 863
854 864 count = len(web.repo)
855 865 pos = rev
856 866 start = max(0, pos - revcount + 1)
857 867 end = min(count, start + revcount)
858 868 pos = end - 1
859 869
860 870 uprev = min(max(0, count - 1), rev + revcount)
861 871 downrev = max(0, rev - revcount)
862 872 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
863 873
864 874 tree = []
865 875 if start < end:
866 876 revs = list(web.repo.changelog.revs(end - 1, start))
867 877 dag = graphmod.dagwalker(web.repo, revs)
868 878 tree = list(graphmod.colored(dag, web.repo))
869 879
870 880 def getcolumns(tree):
871 881 cols = 0
872 882 for (id, type, ctx, vtx, edges) in tree:
873 883 if type != graphmod.CHANGESET:
874 884 continue
875 885 cols = max(cols, max([edge[0] for edge in edges] or [0]),
876 886 max([edge[1] for edge in edges] or [0]))
877 887 return cols
878 888
879 889 def graphdata(usetuples, **map):
880 890 data = []
881 891
882 892 row = 0
883 893 for (id, type, ctx, vtx, edges) in tree:
884 894 if type != graphmod.CHANGESET:
885 895 continue
886 896 node = str(ctx)
887 897 age = templatefilters.age(ctx.date())
888 898 desc = templatefilters.firstline(ctx.description())
889 899 desc = cgi.escape(templatefilters.nonempty(desc))
890 900 user = cgi.escape(templatefilters.person(ctx.user()))
891 901 branch = ctx.branch()
892 902 try:
893 903 branchnode = web.repo.branchtip(branch)
894 904 except error.RepoLookupError:
895 905 branchnode = None
896 906 branch = branch, branchnode == ctx.node()
897 907
898 908 if usetuples:
899 909 data.append((node, vtx, edges, desc, user, age, branch,
900 910 ctx.tags(), ctx.bookmarks()))
901 911 else:
902 912 edgedata = [dict(col=edge[0], nextcol=edge[1],
903 913 color=(edge[2] - 1) % 6 + 1,
904 914 width=edge[3], bcolor=edge[4])
905 915 for edge in edges]
906 916
907 917 data.append(
908 918 dict(node=node,
909 919 col=vtx[0],
910 920 color=(vtx[1] - 1) % 6 + 1,
911 921 edges=edgedata,
912 922 row=row,
913 923 nextrow=row + 1,
914 924 desc=desc,
915 925 user=user,
916 926 age=age,
917 927 bookmarks=webutil.nodebookmarksdict(
918 928 web.repo, ctx.node()),
919 929 branches=webutil.nodebranchdict(web.repo, ctx),
920 930 inbranch=webutil.nodeinbranch(web.repo, ctx),
921 931 tags=webutil.nodetagsdict(web.repo, ctx.node())))
922 932
923 933 row += 1
924 934
925 935 return data
926 936
927 937 cols = getcolumns(tree)
928 938 rows = len(tree)
929 939 canvasheight = (rows + 1) * bg_height - 27
930 940
931 941 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
932 942 lessvars=lessvars, morevars=morevars, downrev=downrev,
933 943 cols=cols, rows=rows,
934 944 canvaswidth=(cols + 1) * bg_height,
935 945 truecanvasheight=rows * bg_height,
936 946 canvasheight=canvasheight, bg_height=bg_height,
937 947 jsdata=lambda **x: graphdata(True, **x),
938 948 nodes=lambda **x: graphdata(False, **x),
939 949 node=ctx.hex(), changenav=changenav)
940 950
941 951 def _getdoc(e):
942 952 doc = e[0].__doc__
943 953 if doc:
944 954 doc = _(doc).split('\n')[0]
945 955 else:
946 956 doc = _('(no help text available)')
947 957 return doc
948 958
949 959 def help(web, req, tmpl):
950 960 from mercurial import commands # avoid cycle
951 961
952 962 topicname = req.form.get('node', [None])[0]
953 963 if not topicname:
954 964 def topics(**map):
955 965 for entries, summary, _ in helpmod.helptable:
956 966 yield {'topic': entries[0], 'summary': summary}
957 967
958 968 early, other = [], []
959 969 primary = lambda s: s.split('|')[0]
960 970 for c, e in commands.table.iteritems():
961 971 doc = _getdoc(e)
962 972 if 'DEPRECATED' in doc or c.startswith('debug'):
963 973 continue
964 974 cmd = primary(c)
965 975 if cmd.startswith('^'):
966 976 early.append((cmd[1:], doc))
967 977 else:
968 978 other.append((cmd, doc))
969 979
970 980 early.sort()
971 981 other.sort()
972 982
973 983 def earlycommands(**map):
974 984 for c, doc in early:
975 985 yield {'topic': c, 'summary': doc}
976 986
977 987 def othercommands(**map):
978 988 for c, doc in other:
979 989 yield {'topic': c, 'summary': doc}
980 990
981 991 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
982 992 othercommands=othercommands, title='Index')
983 993
984 994 u = webutil.wsgiui()
985 995 u.pushbuffer()
986 996 u.verbose = True
987 997 try:
988 998 commands.help_(u, topicname)
989 999 except error.UnknownCommand:
990 1000 raise ErrorResponse(HTTP_NOT_FOUND)
991 1001 doc = u.popbuffer()
992 1002 return tmpl('help', topic=topicname, doc=doc)
General Comments 0
You need to be logged in to leave comments. Login now