##// END OF EJS Templates
hgweb: refactor hgweb code
Dirkjan Ochtman -
r6393:894875ea default
parent child Browse files
Show More
@@ -31,7 +31,7 b" demandimport.ignore.extend(['pkgutil',"
31 31 'pkg_resources',
32 32 '__main__',])
33 33
34 from mercurial.hgweb.hgweb_mod import hgweb
34 from mercurial.hgweb import webcommands, webutil
35 35 from mercurial import util
36 36 from mercurial.templatefilters import filters
37 37
@@ -79,20 +79,19 b' def pygmentize(self, tmpl, fctx, field):'
79 79 newl = oldl.replace('line|escape', 'line|colorize')
80 80 tmpl.cache[field] = newl
81 81
82 def filerevision_highlight(self, tmpl, fctx):
83 pygmentize(self, tmpl, fctx, 'fileline')
84
85 return realrevision(self, tmpl, fctx)
82 web_filerevision = webcommands._filerevision
83 web_annotate = webcommands.annotate
86 84
87 def fileannotate_highlight(self, tmpl, fctx):
88 pygmentize(self, tmpl, fctx, 'annotateline')
85 def filerevision_highlight(web, tmpl, fctx):
86 pygmentize(web, tmpl, fctx, 'fileline')
87 return web_filerevision(web, tmpl, fctx)
89 88
90 return realannotate(self, tmpl, fctx)
89 def annotate_highlight(web, req, tmpl):
90 fctx = webutil.filectx(web.repo, req)
91 pygmentize(web, tmpl, fctx, 'annotateline')
92 return web_annotate(web, req, tmpl)
91 93
92 94 # monkeypatch in the new version
93 # should be safer than overriding the method in a derived class
94 # and then patching the class
95 realrevision = hgweb.filerevision
96 hgweb.filerevision = filerevision_highlight
97 realannotate = hgweb.fileannotate
98 hgweb.fileannotate = fileannotate_highlight
95
96 webcommands._filerevision = filerevision_highlight
97 webcommands.annotate = annotate_highlight
@@ -128,15 +128,21 b' def _kw_diff(repo, node1=None, node2=Non'
128 128 _patch_diff(repo, node1=node1, node2=node2, files=files, match=match,
129 129 fp=fp, changes=changes, opts=opts)
130 130
131 # monkeypatching hgweb functions changeset and filediff
132 # actual monkeypatching is done at the bottom of reposetup()
133
134 web_changeset = webcommands.changeset
135 web_filediff = webcommands.filediff
136
131 137 def _kwweb_changeset(web, req, tmpl):
132 138 '''Wraps webcommands.changeset turning off keyword expansion.'''
133 139 kwtools['templater'].matcher = util.never
134 return web.changeset(tmpl, web.changectx(req))
140 return web_changeset(web, req, tmpl)
135 141
136 142 def _kwweb_filediff(web, req, tmpl):
137 143 '''Wraps webcommands.filediff turning off keyword expansion.'''
138 144 kwtools['templater'].matcher = util.never
139 return web.filediff(tmpl, web.filectx(req))
145 return web_filediff(web, req, tmpl)
140 146
141 147 def _kwdispatch_parse(ui, args):
142 148 '''Monkeypatch dispatch._parse to obtain running hg command.'''
This diff has been collapsed as it changes many lines, (521 lines changed) Show them Hide them
@@ -6,13 +6,12 b''
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 import os, mimetypes, re
10 from mercurial.node import hex, nullid, short
9 import os, mimetypes
10 from mercurial.node import hex, nullid
11 11 from mercurial.repo import RepoError
12 from mercurial import mdiff, ui, hg, util, archival, patch, hook
12 from mercurial import mdiff, ui, hg, util, patch, hook
13 13 from mercurial import revlog, templater, templatefilters, changegroup
14 from common import get_mtime, style_map, paritygen, countgen, get_contact
15 from common import ErrorResponse
14 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse
16 15 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
17 16 from request import wsgirequest
18 17 import webcommands, protocol, webutil
@@ -32,54 +31,6 b' shortcuts = {'
32 31 'static': [('cmd', ['static']), ('file', None)]
33 32 }
34 33
35 def _up(p):
36 if p[0] != "/":
37 p = "/" + p
38 if p[-1] == "/":
39 p = p[:-1]
40 up = os.path.dirname(p)
41 if up == "/":
42 return "/"
43 return up + "/"
44
45 def revnavgen(pos, pagelen, limit, nodefunc):
46 def seq(factor, limit=None):
47 if limit:
48 yield limit
49 if limit >= 20 and limit <= 40:
50 yield 50
51 else:
52 yield 1 * factor
53 yield 3 * factor
54 for f in seq(factor * 10):
55 yield f
56
57 def nav(**map):
58 l = []
59 last = 0
60 for f in seq(1, pagelen):
61 if f < pagelen or f <= last:
62 continue
63 if f > limit:
64 break
65 last = f
66 if pos + f < limit:
67 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
68 if pos - f >= 0:
69 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
70
71 try:
72 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
73
74 for label, node in l:
75 yield {"label": label, "node": node}
76
77 yield {"label": "tip", "node": "tip"}
78 except RepoError:
79 pass
80
81 return nav
82
83 34 class hgweb(object):
84 35 def __init__(self, repo, name=None):
85 36 if isinstance(repo, str):
@@ -407,476 +358,12 b' class hgweb(object):'
407 358 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
408 359 opts=diffopts), f, tn)
409 360
410 def changelog(self, tmpl, ctx, shortlog=False):
411 def changelist(limit=0,**map):
412 cl = self.repo.changelog
413 l = [] # build a list in forward order for efficiency
414 for i in xrange(start, end):
415 ctx = self.repo.changectx(i)
416 n = ctx.node()
417 showtags = webutil.showtag(self.repo, tmpl, 'changelogtag', n)
418
419 l.insert(0, {"parity": parity.next(),
420 "author": ctx.user(),
421 "parent": webutil.siblings(ctx.parents(), i - 1),
422 "child": webutil.siblings(ctx.children(), i + 1),
423 "changelogtag": showtags,
424 "desc": ctx.description(),
425 "date": ctx.date(),
426 "files": self.listfilediffs(tmpl, ctx.files(), n),
427 "rev": i,
428 "node": hex(n),
429 "tags": webutil.nodetagsdict(self.repo, n),
430 "inbranch": webutil.nodeinbranch(self.repo, ctx),
431 "branches": webutil.nodebranchdict(self.repo, ctx)
432 })
433
434 if limit > 0:
435 l = l[:limit]
436
437 for e in l:
438 yield e
439
440 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
441 cl = self.repo.changelog
442 count = cl.count()
443 pos = ctx.rev()
444 start = max(0, pos - maxchanges + 1)
445 end = min(count, start + maxchanges)
446 pos = end - 1
447 parity = paritygen(self.stripecount, offset=start-end)
448
449 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
450
451 return tmpl(shortlog and 'shortlog' or 'changelog',
452 changenav=changenav,
453 node=hex(cl.tip()),
454 rev=pos, changesets=count,
455 entries=lambda **x: changelist(limit=0,**x),
456 latestentry=lambda **x: changelist(limit=1,**x),
457 archives=self.archivelist("tip"))
458
459 def search(self, tmpl, query):
460
461 def changelist(**map):
462 cl = self.repo.changelog
463 count = 0
464 qw = query.lower().split()
465
466 def revgen():
467 for i in xrange(cl.count() - 1, 0, -100):
468 l = []
469 for j in xrange(max(0, i - 100), i + 1):
470 ctx = self.repo.changectx(j)
471 l.append(ctx)
472 l.reverse()
473 for e in l:
474 yield e
475
476 for ctx in revgen():
477 miss = 0
478 for q in qw:
479 if not (q in ctx.user().lower() or
480 q in ctx.description().lower() or
481 q in " ".join(ctx.files()).lower()):
482 miss = 1
483 break
484 if miss:
485 continue
486
487 count += 1
488 n = ctx.node()
489 showtags = webutil.showtag(self.repo, tmpl, 'changelogtag', n)
490
491 yield tmpl('searchentry',
492 parity=parity.next(),
493 author=ctx.user(),
494 parent=webutil.siblings(ctx.parents()),
495 child=webutil.siblings(ctx.children()),
496 changelogtag=showtags,
497 desc=ctx.description(),
498 date=ctx.date(),
499 files=self.listfilediffs(tmpl, ctx.files(), n),
500 rev=ctx.rev(),
501 node=hex(n),
502 tags=webutil.nodetagsdict(self.repo, n),
503 inbranch=webutil.nodeinbranch(self.repo, ctx),
504 branches=webutil.nodebranchdict(self.repo, ctx))
505
506 if count >= self.maxchanges:
507 break
508
509 cl = self.repo.changelog
510 parity = paritygen(self.stripecount)
511
512 return tmpl('search',
513 query=query,
514 node=hex(cl.tip()),
515 entries=changelist,
516 archives=self.archivelist("tip"))
517
518 def changeset(self, tmpl, ctx):
519 n = ctx.node()
520 showtags = webutil.showtag(self.repo, tmpl, 'changesettag', n)
521 parents = ctx.parents()
522 p1 = parents[0].node()
523
524 files = []
525 parity = paritygen(self.stripecount)
526 for f in ctx.files():
527 files.append(tmpl("filenodelink",
528 node=hex(n), file=f,
529 parity=parity.next()))
530
531 def diff(**map):
532 yield self.diff(tmpl, p1, n, None)
533
534 return tmpl('changeset',
535 diff=diff,
536 rev=ctx.rev(),
537 node=hex(n),
538 parent=webutil.siblings(parents),
539 child=webutil.siblings(ctx.children()),
540 changesettag=showtags,
541 author=ctx.user(),
542 desc=ctx.description(),
543 date=ctx.date(),
544 files=files,
545 archives=self.archivelist(hex(n)),
546 tags=webutil.nodetagsdict(self.repo, n),
547 branch=webutil.nodebranchnodefault(ctx),
548 inbranch=webutil.nodeinbranch(self.repo, ctx),
549 branches=webutil.nodebranchdict(self.repo, ctx))
550
551 def filelog(self, tmpl, fctx):
552 f = fctx.path()
553 fl = fctx.filelog()
554 count = fl.count()
555 pagelen = self.maxshortchanges
556 pos = fctx.filerev()
557 start = max(0, pos - pagelen + 1)
558 end = min(count, start + pagelen)
559 pos = end - 1
560 parity = paritygen(self.stripecount, offset=start-end)
561
562 def entries(limit=0, **map):
563 l = []
564
565 for i in xrange(start, end):
566 ctx = fctx.filectx(i)
567 n = fl.node(i)
568
569 l.insert(0, {"parity": parity.next(),
570 "filerev": i,
571 "file": f,
572 "node": hex(ctx.node()),
573 "author": ctx.user(),
574 "date": ctx.date(),
575 "rename": webutil.renamelink(fl, n),
576 "parent": webutil.siblings(fctx.parents()),
577 "child": webutil.siblings(fctx.children()),
578 "desc": ctx.description()})
579
580 if limit > 0:
581 l = l[:limit]
582
583 for e in l:
584 yield e
585
586 nodefunc = lambda x: fctx.filectx(fileid=x)
587 nav = revnavgen(pos, pagelen, count, nodefunc)
588 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
589 entries=lambda **x: entries(limit=0, **x),
590 latestentry=lambda **x: entries(limit=1, **x))
591
592 def filerevision(self, tmpl, fctx):
593 f = fctx.path()
594 text = fctx.data()
595 fl = fctx.filelog()
596 n = fctx.filenode()
597 parity = paritygen(self.stripecount)
598
599 if util.binary(text):
600 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
601 text = '(binary:%s)' % mt
602
603 def lines():
604 for lineno, t in enumerate(text.splitlines(1)):
605 yield {"line": t,
606 "lineid": "l%d" % (lineno + 1),
607 "linenumber": "% 6d" % (lineno + 1),
608 "parity": parity.next()}
609
610 return tmpl("filerevision",
611 file=f,
612 path=_up(f),
613 text=lines(),
614 rev=fctx.rev(),
615 node=hex(fctx.node()),
616 author=fctx.user(),
617 date=fctx.date(),
618 desc=fctx.description(),
619 branch=webutil.nodebranchnodefault(fctx),
620 parent=webutil.siblings(fctx.parents()),
621 child=webutil.siblings(fctx.children()),
622 rename=webutil.renamelink(fl, n),
623 permissions=fctx.manifest().flags(f))
624
625 def fileannotate(self, tmpl, fctx):
626 f = fctx.path()
627 n = fctx.filenode()
628 fl = fctx.filelog()
629 parity = paritygen(self.stripecount)
630
631 def annotate(**map):
632 last = None
633 if util.binary(fctx.data()):
634 mt = (mimetypes.guess_type(fctx.path())[0]
635 or 'application/octet-stream')
636 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
637 '(binary:%s)' % mt)])
638 else:
639 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
640 for lineno, ((f, targetline), l) in lines:
641 fnode = f.filenode()
642 name = self.repo.ui.shortuser(f.user())
643
644 if last != fnode:
645 last = fnode
646
647 yield {"parity": parity.next(),
648 "node": hex(f.node()),
649 "rev": f.rev(),
650 "author": name,
651 "file": f.path(),
652 "targetline": targetline,
653 "line": l,
654 "lineid": "l%d" % (lineno + 1),
655 "linenumber": "% 6d" % (lineno + 1)}
656
657 return tmpl("fileannotate",
658 file=f,
659 annotate=annotate,
660 path=_up(f),
661 rev=fctx.rev(),
662 node=hex(fctx.node()),
663 author=fctx.user(),
664 date=fctx.date(),
665 desc=fctx.description(),
666 rename=webutil.renamelink(fl, n),
667 branch=webutil.nodebranchnodefault(fctx),
668 parent=webutil.siblings(fctx.parents()),
669 child=webutil.siblings(fctx.children()),
670 permissions=fctx.manifest().flags(f))
671
672 def manifest(self, tmpl, ctx, path):
673 mf = ctx.manifest()
674 node = ctx.node()
675
676 files = {}
677 parity = paritygen(self.stripecount)
678
679 if path and path[-1] != "/":
680 path += "/"
681 l = len(path)
682 abspath = "/" + path
683
684 for f, n in mf.items():
685 if f[:l] != path:
686 continue
687 remain = f[l:]
688 if "/" in remain:
689 short = remain[:remain.index("/") + 1] # bleah
690 files[short] = (f, None)
691 else:
692 short = os.path.basename(remain)
693 files[short] = (f, n)
694
695 if not files:
696 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
697
698 def filelist(**map):
699 fl = files.keys()
700 fl.sort()
701 for f in fl:
702 full, fnode = files[f]
703 if not fnode:
704 continue
705
706 fctx = ctx.filectx(full)
707 yield {"file": full,
708 "parity": parity.next(),
709 "basename": f,
710 "date": fctx.changectx().date(),
711 "size": fctx.size(),
712 "permissions": mf.flags(full)}
713
714 def dirlist(**map):
715 fl = files.keys()
716 fl.sort()
717 for f in fl:
718 full, fnode = files[f]
719 if fnode:
720 continue
721
722 yield {"parity": parity.next(),
723 "path": "%s%s" % (abspath, f),
724 "basename": f[:-1]}
725
726 return tmpl("manifest",
727 rev=ctx.rev(),
728 node=hex(node),
729 path=abspath,
730 up=_up(abspath),
731 upparity=parity.next(),
732 fentries=filelist,
733 dentries=dirlist,
734 archives=self.archivelist(hex(node)),
735 tags=webutil.nodetagsdict(self.repo, node),
736 inbranch=webutil.nodeinbranch(self.repo, ctx),
737 branches=webutil.nodebranchdict(self.repo, ctx))
738
739 def tags(self, tmpl):
740 i = self.repo.tagslist()
741 i.reverse()
742 parity = paritygen(self.stripecount)
743
744 def entries(notip=False,limit=0, **map):
745 count = 0
746 for k, n in i:
747 if notip and k == "tip":
748 continue
749 if limit > 0 and count >= limit:
750 continue
751 count = count + 1
752 yield {"parity": parity.next(),
753 "tag": k,
754 "date": self.repo.changectx(n).date(),
755 "node": hex(n)}
756
757 return tmpl("tags",
758 node=hex(self.repo.changelog.tip()),
759 entries=lambda **x: entries(False,0, **x),
760 entriesnotip=lambda **x: entries(True,0, **x),
761 latestentry=lambda **x: entries(True,1, **x))
762
763 def summary(self, tmpl):
764 i = self.repo.tagslist()
765 i.reverse()
766
767 def tagentries(**map):
768 parity = paritygen(self.stripecount)
769 count = 0
770 for k, n in i:
771 if k == "tip": # skip tip
772 continue;
773
774 count += 1
775 if count > 10: # limit to 10 tags
776 break;
777
778 yield tmpl("tagentry",
779 parity=parity.next(),
780 tag=k,
781 node=hex(n),
782 date=self.repo.changectx(n).date())
783
784
785 def branches(**map):
786 parity = paritygen(self.stripecount)
787
788 b = self.repo.branchtags()
789 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
790 l.sort()
791
792 for r,n,t in l:
793 ctx = self.repo.changectx(n)
794
795 yield {'parity': parity.next(),
796 'branch': t,
797 'node': hex(n),
798 'date': ctx.date()}
799
800 def changelist(**map):
801 parity = paritygen(self.stripecount, offset=start-end)
802 l = [] # build a list in forward order for efficiency
803 for i in xrange(start, end):
804 ctx = self.repo.changectx(i)
805 n = ctx.node()
806 hn = hex(n)
807
808 l.insert(0, tmpl(
809 'shortlogentry',
810 parity=parity.next(),
811 author=ctx.user(),
812 desc=ctx.description(),
813 date=ctx.date(),
814 rev=i,
815 node=hn,
816 tags=webutil.nodetagsdict(self.repo, n),
817 inbranch=webutil.nodeinbranch(self.repo, ctx),
818 branches=webutil.nodebranchdict(self.repo, ctx)))
819
820 yield l
821
822 cl = self.repo.changelog
823 count = cl.count()
824 start = max(0, count - self.maxchanges)
825 end = min(count, start + self.maxchanges)
826
827 return tmpl("summary",
828 desc=self.config("web", "description", "unknown"),
829 owner=get_contact(self.config) or "unknown",
830 lastchange=cl.read(cl.tip())[2],
831 tags=tagentries,
832 branches=branches,
833 shortlog=changelist,
834 node=hex(cl.tip()),
835 archives=self.archivelist("tip"))
836
837 def filediff(self, tmpl, fctx):
838 n = fctx.node()
839 path = fctx.path()
840 parents = fctx.parents()
841 p1 = parents and parents[0].node() or nullid
842
843 def diff(**map):
844 yield self.diff(tmpl, p1, n, [path])
845
846 return tmpl("filediff",
847 file=path,
848 node=hex(n),
849 rev=fctx.rev(),
850 branch=webutil.nodebranchnodefault(fctx),
851 parent=webutil.siblings(parents),
852 child=webutil.siblings(fctx.children()),
853 diff=diff)
854
855 361 archive_specs = {
856 362 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
857 363 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
858 364 'zip': ('application/zip', 'zip', '.zip', None),
859 365 }
860 366
861 def archive(self, tmpl, req, key, type_):
862 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
863 cnode = self.repo.lookup(key)
864 arch_version = key
865 if cnode == key or key == 'tip':
866 arch_version = short(cnode)
867 name = "%s-%s" % (reponame, arch_version)
868 mimetype, artype, extension, encoding = self.archive_specs[type_]
869 headers = [
870 ('Content-Type', mimetype),
871 ('Content-Disposition', 'attachment; filename=%s%s' %
872 (name, extension))
873 ]
874 if encoding:
875 headers.append(('Content-Encoding', encoding))
876 req.header(headers)
877 req.respond(HTTP_OK)
878 archival.archive(self.repo, req, cnode, artype, prefix=name)
879
880 367 def check_perm(self, req, op, default):
881 368 '''check permission for operation based on user auth.
882 369 return true if op allowed, else false.
@@ -5,12 +5,14 b''
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 import os, mimetypes
8 import os, mimetypes, re
9 9 import webutil
10 from mercurial import revlog
10 from mercurial import revlog, archival
11 from mercurial.node import hex, nullid
11 12 from mercurial.util import binary
12 13 from mercurial.repo import RepoError
13 from common import staticfile, ErrorResponse, HTTP_OK, HTTP_NOT_FOUND
14 from common import paritygen, staticfile, get_contact, ErrorResponse
15 from common import HTTP_OK, HTTP_NOT_FOUND
14 16
15 17 # __all__ is populated with the allowed commands. Be sure to add to it if
16 18 # you're adding a new command, or the new command won't work.
@@ -30,7 +32,7 b' def log(web, req, tmpl):'
30 32 def rawfile(web, req, tmpl):
31 33 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
32 34 if not path:
33 content = web.manifest(tmpl, webutil.changectx(web.repo, req), path)
35 content = manifest(web, req, tmpl)
34 36 req.respond(HTTP_OK, web.ctype)
35 37 return content
36 38
@@ -38,7 +40,7 b' def rawfile(web, req, tmpl):'
38 40 fctx = webutil.filectx(web.repo, req)
39 41 except revlog.LookupError, inst:
40 42 try:
41 content = web.manifest(tmpl, webutil.changectx(web.repo, req), path)
43 content = manifest(web, req, tmpl)
42 44 req.respond(HTTP_OK, web.ctype)
43 45 return content
44 46 except ErrorResponse:
@@ -53,19 +55,111 b' def rawfile(web, req, tmpl):'
53 55 req.respond(HTTP_OK, mt, path, len(text))
54 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 91 def file(web, req, tmpl):
57 92 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
58 93 if path:
59 94 try:
60 return web.filerevision(tmpl, webutil.filectx(web.repo, req))
95 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
61 96 except revlog.LookupError, inst:
62 97 pass
63 98
64 99 try:
65 return web.manifest(tmpl, webutil.changectx(web.repo, req), path)
100 return manifest(web, req, tmpl)
66 101 except ErrorResponse:
67 102 raise inst
68 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"))
162
69 163 def changelog(web, req, tmpl, shortlog = False):
70 164 if 'node' in req.form:
71 165 ctx = webutil.changectx(web.repo, req)
@@ -77,47 +171,396 b' def changelog(web, req, tmpl, shortlog ='
77 171 try:
78 172 ctx = web.repo.changectx(hi)
79 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?
175
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 })
81 198
82 return web.changelog(tmpl, ctx, shortlog = shortlog)
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 224 def shortlog(web, req, tmpl):
85 225 return changelog(web, req, tmpl, shortlog = True)
86 226
87 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 259 rev = changeset
91 260
92 261 def manifest(web, req, tmpl):
93 return web.manifest(tmpl, webutil.changectx(web.repo, req),
94 webutil.cleanpath(web.repo, req.form['path'][0]))
262 ctx = webutil.changectx(web.repo, req)
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 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 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 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 443 diff = filediff
106 444
107 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 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 536 def archive(web, req, tmpl):
114 537 type_ = req.form['type'][0]
115 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 542 web.configbool("web", "allow" + type_, False))):
118 web.archive(tmpl, req, req.form['node'][0], type_)
543 msg = 'Unsupported archive type: %s' % type_
544 raise ErrorResponse(HTTP_NOT_FOUND, msg)
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)
119 562 return []
120 raise ErrorResponse(HTTP_NOT_FOUND, 'unsupported archive type: %s' % type_)
563
121 564
122 565 def static(web, req, tmpl):
123 566 fname = req.form['file'][0]
@@ -6,10 +6,59 b''
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 import os
9 10 from mercurial.node import hex, nullid
10 11 from mercurial.repo import RepoError
11 12 from mercurial import util
12 13
14 def up(p):
15 if p[0] != "/":
16 p = "/" + p
17 if p[-1] == "/":
18 p = p[:-1]
19 up = os.path.dirname(p)
20 if up == "/":
21 return "/"
22 return up + "/"
23
24 def revnavgen(pos, pagelen, limit, nodefunc):
25 def seq(factor, limit=None):
26 if limit:
27 yield limit
28 if limit >= 20 and limit <= 40:
29 yield 50
30 else:
31 yield 1 * factor
32 yield 3 * factor
33 for f in seq(factor * 10):
34 yield f
35
36 def nav(**map):
37 l = []
38 last = 0
39 for f in seq(1, pagelen):
40 if f < pagelen or f <= last:
41 continue
42 if f > limit:
43 break
44 last = f
45 if pos + f < limit:
46 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
47 if pos - f >= 0:
48 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
49
50 try:
51 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
52
53 for label, node in l:
54 yield {"label": label, "node": node}
55
56 yield {"label": "tip", "node": "tip"}
57 except RepoError:
58 pass
59
60 return nav
61
13 62 def siblings(siblings=[], hiderev=None, **args):
14 63 siblings = [s for s in siblings if s.node() != nullid]
15 64 if len(siblings) == 1 and siblings[0].rev() == hiderev:
General Comments 0
You need to be logged in to leave comments. Login now