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