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