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