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