##// END OF EJS Templates
webcommands: fix increments lost by 894875eae49b
Andrew Beekhof -
r6659:bc553c6d default
parent child Browse files
Show More
@@ -1,576 +1,576 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
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import os, mimetypes, re
9 9 import webutil
10 10 from mercurial import revlog, archival
11 11 from mercurial.node import short, hex, nullid
12 12 from mercurial.util import binary
13 13 from mercurial.repo import RepoError
14 14 from common import paritygen, staticfile, get_contact, ErrorResponse
15 15 from common import HTTP_OK, HTTP_NOT_FOUND
16 16
17 17 # __all__ is populated with the allowed commands. Be sure to add to it if
18 18 # you're adding a new command, or the new command won't work.
19 19
20 20 __all__ = [
21 21 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
22 22 'manifest', 'tags', 'summary', 'filediff', 'diff', 'annotate', 'filelog',
23 23 'archive', 'static',
24 24 ]
25 25
26 26 def log(web, req, tmpl):
27 27 if 'file' in req.form and req.form['file'][0]:
28 28 return filelog(web, req, tmpl)
29 29 else:
30 30 return changelog(web, req, tmpl)
31 31
32 32 def rawfile(web, req, tmpl):
33 33 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
34 34 if not path:
35 35 content = manifest(web, req, tmpl)
36 36 req.respond(HTTP_OK, web.ctype)
37 37 return content
38 38
39 39 try:
40 40 fctx = webutil.filectx(web.repo, req)
41 41 except revlog.LookupError, inst:
42 42 try:
43 43 content = manifest(web, req, tmpl)
44 44 req.respond(HTTP_OK, web.ctype)
45 45 return content
46 46 except ErrorResponse:
47 47 raise inst
48 48
49 49 path = fctx.path()
50 50 text = fctx.data()
51 51 mt = mimetypes.guess_type(path)[0]
52 52 if mt is None or binary(text):
53 53 mt = mt or 'application/octet-stream'
54 54
55 55 req.respond(HTTP_OK, mt, path, len(text))
56 56 return [text]
57 57
58 58 def _filerevision(web, tmpl, fctx):
59 59 f = fctx.path()
60 60 text = fctx.data()
61 61 fl = fctx.filelog()
62 62 n = fctx.filenode()
63 63 parity = paritygen(web.stripecount)
64 64
65 65 if binary(text):
66 66 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
67 67 text = '(binary:%s)' % mt
68 68
69 69 def lines():
70 70 for lineno, t in enumerate(text.splitlines(1)):
71 71 yield {"line": t,
72 72 "lineid": "l%d" % (lineno + 1),
73 73 "linenumber": "% 6d" % (lineno + 1),
74 74 "parity": parity.next()}
75 75
76 76 return tmpl("filerevision",
77 77 file=f,
78 78 path=webutil.up(f),
79 79 text=lines(),
80 80 rev=fctx.rev(),
81 81 node=hex(fctx.node()),
82 82 author=fctx.user(),
83 83 date=fctx.date(),
84 84 desc=fctx.description(),
85 85 branch=webutil.nodebranchnodefault(fctx),
86 86 parent=webutil.siblings(fctx.parents()),
87 87 child=webutil.siblings(fctx.children()),
88 88 rename=webutil.renamelink(fctx),
89 89 permissions=fctx.manifest().flags(f))
90 90
91 91 def file(web, req, tmpl):
92 92 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
93 93 if path:
94 94 try:
95 95 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
96 96 except revlog.LookupError, inst:
97 97 pass
98 98
99 99 try:
100 100 return manifest(web, req, tmpl)
101 101 except ErrorResponse:
102 102 raise inst
103 103
104 104 def _search(web, tmpl, query):
105 105
106 106 def changelist(**map):
107 107 cl = web.repo.changelog
108 108 count = 0
109 109 qw = query.lower().split()
110 110
111 111 def revgen():
112 112 for i in xrange(cl.count() - 1, 0, -100):
113 113 l = []
114 114 for j in xrange(max(0, i - 100), i + 1):
115 115 ctx = web.repo.changectx(j)
116 116 l.append(ctx)
117 117 l.reverse()
118 118 for e in l:
119 119 yield e
120 120
121 121 for ctx in revgen():
122 122 miss = 0
123 123 for q in qw:
124 124 if not (q in ctx.user().lower() or
125 125 q in ctx.description().lower() or
126 126 q in " ".join(ctx.files()).lower()):
127 127 miss = 1
128 128 break
129 129 if miss:
130 130 continue
131 131
132 count = 1
132 count += 1
133 133 n = ctx.node()
134 134 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
135 135
136 136 yield tmpl('searchentry',
137 137 parity=parity.next(),
138 138 author=ctx.user(),
139 139 parent=webutil.siblings(ctx.parents()),
140 140 child=webutil.siblings(ctx.children()),
141 141 changelogtag=showtags,
142 142 desc=ctx.description(),
143 143 date=ctx.date(),
144 144 files=web.listfilediffs(tmpl, ctx.files(), n),
145 145 rev=ctx.rev(),
146 146 node=hex(n),
147 147 tags=webutil.nodetagsdict(web.repo, n),
148 148 inbranch=webutil.nodeinbranch(web.repo, ctx),
149 149 branches=webutil.nodebranchdict(web.repo, ctx))
150 150
151 151 if count >= web.maxchanges:
152 152 break
153 153
154 154 cl = web.repo.changelog
155 155 parity = paritygen(web.stripecount)
156 156
157 157 return tmpl('search',
158 158 query=query,
159 159 node=hex(cl.tip()),
160 160 entries=changelist,
161 161 archives=web.archivelist("tip"))
162 162
163 163 def changelog(web, req, tmpl, shortlog = False):
164 164 if 'node' in req.form:
165 165 ctx = webutil.changectx(web.repo, req)
166 166 else:
167 167 if 'rev' in req.form:
168 168 hi = req.form['rev'][0]
169 169 else:
170 170 hi = web.repo.changelog.count() - 1
171 171 try:
172 172 ctx = web.repo.changectx(hi)
173 173 except RepoError:
174 174 return _search(web, tmpl, hi) # XXX redirect to 404 page?
175 175
176 176 def changelist(limit=0, **map):
177 177 cl = web.repo.changelog
178 178 l = [] # build a list in forward order for efficiency
179 179 for i in xrange(start, end):
180 180 ctx = web.repo.changectx(i)
181 181 n = ctx.node()
182 182 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
183 183
184 184 l.insert(0, {"parity": parity.next(),
185 185 "author": ctx.user(),
186 186 "parent": webutil.siblings(ctx.parents(), i - 1),
187 187 "child": webutil.siblings(ctx.children(), i + 1),
188 188 "changelogtag": showtags,
189 189 "desc": ctx.description(),
190 190 "date": ctx.date(),
191 191 "files": web.listfilediffs(tmpl, ctx.files(), n),
192 192 "rev": i,
193 193 "node": hex(n),
194 194 "tags": webutil.nodetagsdict(web.repo, n),
195 195 "inbranch": webutil.nodeinbranch(web.repo, ctx),
196 196 "branches": webutil.nodebranchdict(web.repo, ctx)
197 197 })
198 198
199 199 if limit > 0:
200 200 l = l[:limit]
201 201
202 202 for e in l:
203 203 yield e
204 204
205 205 maxchanges = shortlog and web.maxshortchanges or web.maxchanges
206 206 cl = web.repo.changelog
207 207 count = cl.count()
208 208 pos = ctx.rev()
209 209 start = max(0, pos - maxchanges + 1)
210 210 end = min(count, start + maxchanges)
211 211 pos = end - 1
212 212 parity = paritygen(web.stripecount, offset=start-end)
213 213
214 214 changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx)
215 215
216 216 return tmpl(shortlog and 'shortlog' or 'changelog',
217 217 changenav=changenav,
218 218 node=hex(ctx.node()),
219 219 rev=pos, changesets=count,
220 220 entries=lambda **x: changelist(limit=0,**x),
221 221 latestentry=lambda **x: changelist(limit=1,**x),
222 222 archives=web.archivelist("tip"))
223 223
224 224 def shortlog(web, req, tmpl):
225 225 return changelog(web, req, tmpl, shortlog = True)
226 226
227 227 def changeset(web, req, tmpl):
228 228 ctx = webutil.changectx(web.repo, req)
229 229 n = ctx.node()
230 230 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', n)
231 231 parents = ctx.parents()
232 232 p1 = parents[0].node()
233 233
234 234 files = []
235 235 parity = paritygen(web.stripecount)
236 236 for f in ctx.files():
237 237 files.append(tmpl("filenodelink",
238 238 node=hex(n), file=f,
239 239 parity=parity.next()))
240 240
241 241 diffs = web.diff(tmpl, p1, n, None)
242 242 return tmpl('changeset',
243 243 diff=diffs,
244 244 rev=ctx.rev(),
245 245 node=hex(n),
246 246 parent=webutil.siblings(parents),
247 247 child=webutil.siblings(ctx.children()),
248 248 changesettag=showtags,
249 249 author=ctx.user(),
250 250 desc=ctx.description(),
251 251 date=ctx.date(),
252 252 files=files,
253 253 archives=web.archivelist(hex(n)),
254 254 tags=webutil.nodetagsdict(web.repo, n),
255 255 branch=webutil.nodebranchnodefault(ctx),
256 256 inbranch=webutil.nodeinbranch(web.repo, ctx),
257 257 branches=webutil.nodebranchdict(web.repo, ctx))
258 258
259 259 rev = changeset
260 260
261 261 def manifest(web, req, tmpl):
262 262 ctx = webutil.changectx(web.repo, req)
263 263 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
264 264 mf = ctx.manifest()
265 265 node = ctx.node()
266 266
267 267 files = {}
268 268 parity = paritygen(web.stripecount)
269 269
270 270 if path and path[-1] != "/":
271 271 path += "/"
272 272 l = len(path)
273 273 abspath = "/" + path
274 274
275 275 for f, n in mf.items():
276 276 if f[:l] != path:
277 277 continue
278 278 remain = f[l:]
279 279 if "/" in remain:
280 280 short = remain[:remain.index("/") + 1] # bleah
281 281 files[short] = (f, None)
282 282 else:
283 283 short = os.path.basename(remain)
284 284 files[short] = (f, n)
285 285
286 286 if not files:
287 287 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
288 288
289 289 def filelist(**map):
290 290 fl = files.keys()
291 291 fl.sort()
292 292 for f in fl:
293 293 full, fnode = files[f]
294 294 if not fnode:
295 295 continue
296 296
297 297 fctx = ctx.filectx(full)
298 298 yield {"file": full,
299 299 "parity": parity.next(),
300 300 "basename": f,
301 301 "date": fctx.changectx().date(),
302 302 "size": fctx.size(),
303 303 "permissions": mf.flags(full)}
304 304
305 305 def dirlist(**map):
306 306 fl = files.keys()
307 307 fl.sort()
308 308 for f in fl:
309 309 full, fnode = files[f]
310 310 if fnode:
311 311 continue
312 312
313 313 yield {"parity": parity.next(),
314 314 "path": "%s%s" % (abspath, f),
315 315 "basename": f[:-1]}
316 316
317 317 return tmpl("manifest",
318 318 rev=ctx.rev(),
319 319 node=hex(node),
320 320 path=abspath,
321 321 up=webutil.up(abspath),
322 322 upparity=parity.next(),
323 323 fentries=filelist,
324 324 dentries=dirlist,
325 325 archives=web.archivelist(hex(node)),
326 326 tags=webutil.nodetagsdict(web.repo, node),
327 327 inbranch=webutil.nodeinbranch(web.repo, ctx),
328 328 branches=webutil.nodebranchdict(web.repo, ctx))
329 329
330 330 def tags(web, req, tmpl):
331 331 i = web.repo.tagslist()
332 332 i.reverse()
333 333 parity = paritygen(web.stripecount)
334 334
335 335 def entries(notip=False,limit=0, **map):
336 336 count = 0
337 337 for k, n in i:
338 338 if notip and k == "tip":
339 339 continue
340 340 if limit > 0 and count >= limit:
341 341 continue
342 342 count = count + 1
343 343 yield {"parity": parity.next(),
344 344 "tag": k,
345 345 "date": web.repo.changectx(n).date(),
346 346 "node": hex(n)}
347 347
348 348 return tmpl("tags",
349 349 node=hex(web.repo.changelog.tip()),
350 350 entries=lambda **x: entries(False,0, **x),
351 351 entriesnotip=lambda **x: entries(True,0, **x),
352 352 latestentry=lambda **x: entries(True,1, **x))
353 353
354 354 def summary(web, req, tmpl):
355 355 i = web.repo.tagslist()
356 356 i.reverse()
357 357
358 358 def tagentries(**map):
359 359 parity = paritygen(web.stripecount)
360 360 count = 0
361 361 for k, n in i:
362 362 if k == "tip": # skip tip
363 363 continue
364 364
365 count = 1
365 count += 1
366 366 if count > 10: # limit to 10 tags
367 367 break
368 368
369 369 yield tmpl("tagentry",
370 370 parity=parity.next(),
371 371 tag=k,
372 372 node=hex(n),
373 373 date=web.repo.changectx(n).date())
374 374
375 375 def branches(**map):
376 376 parity = paritygen(web.stripecount)
377 377
378 378 b = web.repo.branchtags()
379 379 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
380 380 l.sort()
381 381
382 382 for r,n,t in l:
383 383 ctx = web.repo.changectx(n)
384 384 yield {'parity': parity.next(),
385 385 'branch': t,
386 386 'node': hex(n),
387 387 'date': ctx.date()}
388 388
389 389 def changelist(**map):
390 390 parity = paritygen(web.stripecount, offset=start-end)
391 391 l = [] # build a list in forward order for efficiency
392 392 for i in xrange(start, end):
393 393 ctx = web.repo.changectx(i)
394 394 n = ctx.node()
395 395 hn = hex(n)
396 396
397 397 l.insert(0, tmpl(
398 398 'shortlogentry',
399 399 parity=parity.next(),
400 400 author=ctx.user(),
401 401 desc=ctx.description(),
402 402 date=ctx.date(),
403 403 rev=i,
404 404 node=hn,
405 405 tags=webutil.nodetagsdict(web.repo, n),
406 406 inbranch=webutil.nodeinbranch(web.repo, ctx),
407 407 branches=webutil.nodebranchdict(web.repo, ctx)))
408 408
409 409 yield l
410 410
411 411 cl = web.repo.changelog
412 412 count = cl.count()
413 413 start = max(0, count - web.maxchanges)
414 414 end = min(count, start + web.maxchanges)
415 415
416 416 return tmpl("summary",
417 417 desc=web.config("web", "description", "unknown"),
418 418 owner=get_contact(web.config) or "unknown",
419 419 lastchange=cl.read(cl.tip())[2],
420 420 tags=tagentries,
421 421 branches=branches,
422 422 shortlog=changelist,
423 423 node=hex(cl.tip()),
424 424 archives=web.archivelist("tip"))
425 425
426 426 def filediff(web, req, tmpl):
427 427 fctx = webutil.filectx(web.repo, req)
428 428 n = fctx.node()
429 429 path = fctx.path()
430 430 parents = fctx.parents()
431 431 p1 = parents and parents[0].node() or nullid
432 432
433 433 diffs = web.diff(tmpl, p1, n, [path])
434 434 return tmpl("filediff",
435 435 file=path,
436 436 node=hex(n),
437 437 rev=fctx.rev(),
438 438 date=fctx.date(),
439 439 desc=fctx.description(),
440 440 author=fctx.user(),
441 441 rename=webutil.renamelink(fctx),
442 442 branch=webutil.nodebranchnodefault(fctx),
443 443 parent=webutil.siblings(parents),
444 444 child=webutil.siblings(fctx.children()),
445 445 diff=diffs)
446 446
447 447 diff = filediff
448 448
449 449 def annotate(web, req, tmpl):
450 450 fctx = webutil.filectx(web.repo, req)
451 451 f = fctx.path()
452 452 n = fctx.filenode()
453 453 fl = fctx.filelog()
454 454 parity = paritygen(web.stripecount)
455 455
456 456 def annotate(**map):
457 457 last = None
458 458 if binary(fctx.data()):
459 459 mt = (mimetypes.guess_type(fctx.path())[0]
460 460 or 'application/octet-stream')
461 461 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
462 462 '(binary:%s)' % mt)])
463 463 else:
464 464 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
465 465 for lineno, ((f, targetline), l) in lines:
466 466 fnode = f.filenode()
467 467
468 468 if last != fnode:
469 469 last = fnode
470 470
471 471 yield {"parity": parity.next(),
472 472 "node": hex(f.node()),
473 473 "rev": f.rev(),
474 474 "author": f.user(),
475 475 "desc": f.description(),
476 476 "file": f.path(),
477 477 "targetline": targetline,
478 478 "line": l,
479 479 "lineid": "l%d" % (lineno + 1),
480 480 "linenumber": "% 6d" % (lineno + 1)}
481 481
482 482 return tmpl("fileannotate",
483 483 file=f,
484 484 annotate=annotate,
485 485 path=webutil.up(f),
486 486 rev=fctx.rev(),
487 487 node=hex(fctx.node()),
488 488 author=fctx.user(),
489 489 date=fctx.date(),
490 490 desc=fctx.description(),
491 491 rename=webutil.renamelink(fctx),
492 492 branch=webutil.nodebranchnodefault(fctx),
493 493 parent=webutil.siblings(fctx.parents()),
494 494 child=webutil.siblings(fctx.children()),
495 495 permissions=fctx.manifest().flags(f))
496 496
497 497 def filelog(web, req, tmpl):
498 498 fctx = webutil.filectx(web.repo, req)
499 499 f = fctx.path()
500 500 fl = fctx.filelog()
501 501 count = fl.count()
502 502 pagelen = web.maxshortchanges
503 503 pos = fctx.filerev()
504 504 start = max(0, pos - pagelen + 1)
505 505 end = min(count, start + pagelen)
506 506 pos = end - 1
507 507 parity = paritygen(web.stripecount, offset=start-end)
508 508
509 509 def entries(limit=0, **map):
510 510 l = []
511 511
512 512 for i in xrange(start, end):
513 513 ctx = fctx.filectx(i)
514 514 n = fl.node(i)
515 515
516 516 l.insert(0, {"parity": parity.next(),
517 517 "filerev": i,
518 518 "file": f,
519 519 "node": hex(ctx.node()),
520 520 "author": ctx.user(),
521 521 "date": ctx.date(),
522 522 "rename": webutil.renamelink(fctx),
523 523 "parent": webutil.siblings(fctx.parents()),
524 524 "child": webutil.siblings(fctx.children()),
525 525 "desc": ctx.description()})
526 526
527 527 if limit > 0:
528 528 l = l[:limit]
529 529
530 530 for e in l:
531 531 yield e
532 532
533 533 nodefunc = lambda x: fctx.filectx(fileid=x)
534 534 nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
535 535 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
536 536 entries=lambda **x: entries(limit=0, **x),
537 537 latestentry=lambda **x: entries(limit=1, **x))
538 538
539 539
540 540 def archive(web, req, tmpl):
541 541 type_ = req.form['type'][0]
542 542 allowed = web.configlist("web", "allow_archive")
543 543 key = req.form['node'][0]
544 544
545 545 if not (type_ in web.archives and (type_ in allowed or
546 546 web.configbool("web", "allow" + type_, False))):
547 547 msg = 'Unsupported archive type: %s' % type_
548 548 raise ErrorResponse(HTTP_NOT_FOUND, msg)
549 549
550 550 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
551 551 cnode = web.repo.lookup(key)
552 552 arch_version = key
553 553 if cnode == key or key == 'tip':
554 554 arch_version = short(cnode)
555 555 name = "%s-%s" % (reponame, arch_version)
556 556 mimetype, artype, extension, encoding = web.archive_specs[type_]
557 557 headers = [
558 558 ('Content-Type', mimetype),
559 559 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
560 560 ]
561 561 if encoding:
562 562 headers.append(('Content-Encoding', encoding))
563 563 req.header(headers)
564 564 req.respond(HTTP_OK)
565 565 archival.archive(web.repo, req, cnode, artype, prefix=name)
566 566 return []
567 567
568 568
569 569 def static(web, req, tmpl):
570 570 fname = req.form['file'][0]
571 571 # a repo owner may set web.static in .hg/hgrc to get any file
572 572 # readable by the user running the CGI script
573 573 static = web.config("web", "static",
574 574 os.path.join(web.templatepath, "static"),
575 575 untrusted=False)
576 576 return [staticfile(static, fname, req)]
General Comments 0
You need to be logged in to leave comments. Login now