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