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