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