##// END OF EJS Templates
Revamped templated hgweb
mpm@selenic.com -
r138:c77a679e default
parent child Browse files
Show More
@@ -0,0 +1,16 b''
1 Content-Type: text/html
2
3 <html>
4 <body>
5 <h2>changelog for #repo#</h2>
6
7 navigate: #changenav#<br>
8
9 <table>
10 #changelist#
11 </table>
12
13 navigate: #changenav#<br>
14
15 </body>
16 </html> No newline at end of file
@@ -0,0 +1,26 b''
1 <tr>
2 <td align=right width="15%"><b>#age# ago:</b></td>
3 <td><b>#shortdesc#</b></td</tr>
4 <tr>
5 <td align=right>revision:</td>
6 <td><a href="?cmd=changeset;node=#node#">#rev#:#node#</a></td></tr>
7 <tr>
8 <td align=right>parent:</td>
9 <td><a href="?cmd=changeset;node=#p1#">#p1rev#:#p1#</a></td></tr>
10 <tr>
11 <td align=right>parent:</td>
12 <td><a href="?cmd=changeset;node=#p2#">#p2rev#:#p2#</a></td></tr>
13 <tr>
14 <td align=right>manifest:</td>
15 <td><a href="?cmd=manifest;manifest=#manifest#;path=/">#rev#:#manifest#</a></td></tr>
16 <tr>
17 <td align=right>author:</td>
18 <td>#author#</td></tr>
19 <tr>
20 <td align=right>date:</td>
21 <td>#date#</td></tr>
22 <tr>
23 <td align=right valign=top>files:</td>
24 <td>#files#</td></tr>
25
26
@@ -0,0 +1,46 b''
1 Content-type: text/html
2
3 <html>
4 <body>
5 <a href="?cmd=changelog&pos=#rev#">changelog</a>
6 <a href="?cmd=manifest;manifest=#manifest#;path=/">manifest</a>
7
8 <h2>changeset: #shortdesc#</h2>
9
10 <table>
11 <tr>
12 <td align=right>revision:</td>
13 <td><a href="?cmd=changeset;node=#node#">#rev#:#node#</a></td></tr>
14 <tr>
15 <td align=right>parent:</td>
16 <td><a href="?cmd=changeset;node=#p1#">#p1rev#:#p1#</a></td></tr>
17 <tr>
18 <td align=right>parent:</td>
19 <td><a href="?cmd=changeset;node=#p2#">#p2rev#:#p2#</a></td></tr>
20 <tr>
21 <td align=right>manifest:</td>
22 <td><a href="?cmd=manifest;manifest=#manifest#;path=/">#rev#:#manifest#</a></td></tr>
23 <tr>
24 <td align=right>author:</td>
25 <td>#author#</td></tr>
26 <tr>
27 <td align=right>date:</td>
28 <td>#date#</td></tr>
29 <tr>
30 <td align=right valign=top>files:</td>
31 <td>#files#</td></tr>
32 <tr>
33 <td align=right valign=top>description:</td>
34 <td>#desc#</td></tr>
35 </table>
36
37 <hr />
38
39 <pre>
40 #diff#
41 </pre>
42
43 </body>
44 </html
45
46
@@ -0,0 +1,42 b''
1 Content-type: text/html
2
3 <html>
4 <body>
5
6 <a href="?cmd=changelog&rev=#rev#">changelog</a>
7 <a href="?cmd=changeset&node=#node#">changeset</a>
8 <a href="?cmd=manifest&manifest=#manifest#;path=#path#">manifest</a>
9 <a href="?cmd=file&file=#file#&filenode=#filenode#">file</a>
10 <a href="?cmd=filelog&file=#file#;filenode=#filenode#">revisions</a>
11
12 <h2>Annotate #file# (#filenode#)</h2>
13
14 <table>
15 <tr>
16 <td align=right>changeset:</td>
17 <td><a href="?cmd=changeset;node=#node#">#rev#:#node#</a></td></tr>
18 <tr>
19 <td align=right>parent:</td>
20 <td><a href="?cmd=file;file=#file#;node=#p1#">#p1rev#:#p1#</a></td></tr>
21 <tr>
22 <td align=right>parent:</td>
23 <td><a href="?cmd=file;file=#file#;node=#p2#">#p2rev#:#p2#</a></td></tr>
24 <tr>
25 <td align=right>manifest:</td>
26 <td><a href="?cmd=manifest;manifest=#manifest#;path=/">#rev#:#manifest#</a></td></tr>
27 <tr>
28 <td align=right>author:</td>
29 <td>#author#</td></tr>
30 <tr>
31 <td align=right>date:</td>
32 <td>#date#</td></tr>
33 </table>
34
35 <hr />
36
37 <table>
38 #annotate#
39 </table>
40
41 </body>
42 </html> No newline at end of file
@@ -0,0 +1,30 b''
1 Content-type: text/html
2
3 <html>
4 <body>
5 <a href="?cmd=changelog&rev=#rev#">changelog</a>
6 <a href="?cmd=changeset&node=#node#">changeset</a>
7 <a href="?cmd=file&file=#file#&filenode=#filenode#">file</a>
8 <a href="?cmd=filelog&file=#file#&filenode=#filenode#">revisions</a>
9 <a href="?cmd=annotate&file=#file#&filenode=#filenode#">annotate</a>
10
11 <h2>#file#</h2>
12
13 <table>
14 <tr>
15 <td align=right>revision:</td>
16 <td><a href="?cmd=changeset;node=#node#">#rev#:#node#</a></td></tr>
17 <tr>
18 <td align=right>parent:</td>
19 <td><a href="?cmd=changeset;node=#p1#">#p1rev#:#p1#</a></td></tr>
20 </table>
21
22 <hr />
23 <pre>
24 #diff#
25 </pre>
26
27 </body>
28 </html
29
30
@@ -0,0 +1,17 b''
1 Content-type: text/html
2
3 <html>
4 <body>
5
6 <a href="?cmd=changelog">changelog</a>
7 <a href="?cmd=file&file=#file#&filenode=#filenode#">file</a>
8 <a href="?cmd=annotate&file=#file#&filenode=#filenode#">annotate</a>
9
10 <h2>#file# revision history</h2>
11
12 <table>
13 #entries#
14 </table>
15
16 </body>
17 </html> No newline at end of file
@@ -0,0 +1,18 b''
1 <tr>
2 <td align=right width="15%"><b>#age# ago:</b></td>
3 <td><b><a href="?cmd=changeset;node=#changeset#">#shortdesc#</a></b></td</tr>
4 <tr>
5 <td align=right>revision:</td>
6 <td><a href="?cmd=file;file=#file#;filenode=#filenode#">#filerev#:#filenode#</a>
7 <a href="?cmd=filediff;file=#file#;node=#node#">(diff)</a>
8 <a href="?cmd=annotate;file=#file#;filenode=#filenode#">(annotate)</a>
9 </td></tr>
10 <tr>
11 <td align=right>author:</td>
12 <td>#author#</td></tr>
13 <tr>
14 <td align=right>date:</td>
15 <td>#date#</td></tr>
16
17
18
@@ -0,0 +1,42 b''
1 Content-type: text/html
2
3 <html>
4 <body>
5
6 <a href="?cmd=changelog&rev=#rev#">changelog</a>
7 <a href="?cmd=changeset&node=#node#">changeset</a>
8 <a href="?cmd=manifest&manifest=#manifest#;path=#path#">manifest</a>
9 <a href="?cmd=filelog&file=#file#;filenode=#filenode#">revisions</a>
10 <a href="?cmd=annotate&file=#file#&filenode=#filenode#">annotate</a>
11
12 <h2>#file# (revision #filenode#)</h2>
13
14 <table>
15 <tr>
16 <td align=right>changeset:</td>
17 <td><a href="?cmd=changeset;node=#node#">#rev#:#node#</a></td></tr>
18 <tr>
19 <td align=right>parent:</td>
20 <td><a href="?cmd=file;file=#file#;node=#p1#">#p1rev#:#p1#</a></td></tr>
21 <tr>
22 <td align=right>parent:</td>
23 <td><a href="?cmd=file;file=#file#;node=#p2#">#p2rev#:#p2#</a></td></tr>
24 <tr>
25 <td align=right>manifest:</td>
26 <td><a href="?cmd=manifest;manifest=#manifest#;path=/">#rev#:#manifest#</a></td></tr>
27 <tr>
28 <td align=right>author:</td>
29 <td>#author#</td></tr>
30 <tr>
31 <td align=right>date:</td>
32 <td>#date#</td></tr>
33 </table>
34
35 <hr />
36
37 <pre>
38 #text#
39 </pre>
40
41 </body>
42 </html> No newline at end of file
@@ -0,0 +1,19 b''
1 Content-Type: text/html
2
3 <html>
4 <body>
5
6 <a href="?cmd=changelog&rev=#rev#">changelog</a>
7 <a href="?cmd=changeset&node=#node#">changeset</a>
8
9 <h2>manifest: #path#</h2>
10 <p>(#rev#:#manifest#)</p>
11
12 <p>
13 <a href="?cmd=manifest;manifest=#manifest#;path=#up#">[up]</a><br />
14 #dirs#</p>
15
16 <p>#files#</p>
17
18 </body>
19 </html> No newline at end of file
@@ -0,0 +1,20 b''
1 changelog = changelog.tmpl
2 naventry = "<a href="?cmd=changelog;pos=#rev#">#rev#</a> "
3 filedifflink = "<a href="?cmd=filediff;node=#node#;file=#file#">#file#</a> "
4 filenodelink = "<a href="?cmd=file;filenode=#filenode#;file=#file#">#file#</a> "
5 fileellipses = "..."
6 changelogentry = changelogentry.tmpl
7 changeset = changeset.tmpl
8 manifest = manifest.tmpl
9 manifestdirentry = "<a href="?cmd=manifest;manifest=#manifest#;path=#path#">#basename#/</a><br />"
10 manifestfileentry = "<a href="?cmd=file;filenode=#filenode#;file=#file#">#basename#</a><br /> "
11 filerevision = filerevision.tmpl
12 fileannotate = fileannotate.tmpl
13 filediff = filediff.tmpl
14 filelog = filelog.tmpl
15 filelogentry = filelogentry.tmpl
16 annotateline = "<tr><td align = right><a href="?cmd=changeset;node=#node#">#author#@#rev#</a>:</td><td><pre>#line#</pre></td></tr>"
17 difflineplus = "<span class=plusline>#line#</span>"
18 difflineminus = "<span class=minusline>#line#</span>"
19 difflineat = "<span class=atline>#line#</span>"
20 diffline = "#line#" No newline at end of file
@@ -0,0 +1,38 b''
1 repo the name of the repo
2 rev a changeset.manifest revision
3 node a changeset node
4 changesets total number of changesets
5 file a filename
6 filenode a file node
7 filerev a file revision
8 filerevs total number of file revisions
9 up the directory of the relevant file
10 path a path in the manifest, starting with "/"
11 basename a short pathname
12 manifest a manifest node
13 manifestrev a manifest revision
14 date a date string
15 age age in hours, days, etc
16 line a line of text (escaped)
17 desc a description (escaped, with breaks)
18 shortdesc a short description (escaped)
19 author a name or email addressv(obfuscated)
20 p1, p2 parent nodes
21 p1rev, p2rev parent revs
22
23 header the global page header
24 footer the global page footer
25
26 files a list of file links
27 dirs a set of directory links
28 diff a diff of one or more files
29 annotate an annotated file
30 entries the entries relevant to the page
31
32 Templates and commands:
33 changelog(rev) - a page for browsing changesets
34 naventry - a link for jumping to a changeset number
35 filenodelink - jump to file diff
36 fileellipses - printed after maxfiles
37 changelogentry - an entry in the log
38 manifest - browse a manifest as a directory tree No newline at end of file
This diff has been collapsed as it changes many lines, (759 lines changed) Show them Hide them
@@ -11,16 +11,45 b' import cgitb'
11 cgitb.enable()
11 cgitb.enable()
12
12
13 import os, cgi, time, re, difflib, sys, zlib
13 import os, cgi, time, re, difflib, sys, zlib
14 from mercurial import hg, mdiff
14 from mercurial.hg import *
15
16 def age(t):
17 def plural(t, c):
18 if c == 1: return t
19 return t + "s"
20 def fmt(t, c):
21 return "%d %s" % (c, plural(t, c))
22
23 now = time.time()
24 delta = max(1, int(now - t))
25
26 scales = [["second", 1],
27 ["minute", 60],
28 ["hour", 3600],
29 ["day", 3600 * 24],
30 ["week", 3600 * 24 * 7],
31 ["month", 3600 * 24 * 30],
32 ["year", 3600 * 24 * 365]]
33
34 scales.reverse()
35
36 for t, s in scales:
37 n = delta / s
38 if n >= 1: return fmt(t, n)
15
39
16 def nl2br(text):
40 def nl2br(text):
17 return re.sub('\n', '<br />', text)
41 return text.replace('\n', '<br/>')
18
42
19 def obfuscate(text):
43 def obfuscate(text):
20 l = []
44 return ''.join([ '&#%d' % ord(c) for c in text ])
21 for c in text:
45
22 l.append('&#%d;' % ord(c))
46 def up(p):
23 return ''.join(l)
47 if p[0] != "/": p = "/" + p
48 if p[-1] == "/": p = p[:-1]
49 up = os.path.dirname(p)
50 if up == "/":
51 return "/"
52 return up + "/"
24
53
25 def httphdr(type):
54 def httphdr(type):
26 print 'Content-type: %s\n' % type
55 print 'Content-type: %s\n' % type
@@ -33,365 +62,451 b' def write(*things):'
33 else:
62 else:
34 sys.stdout.write(str(thing))
63 sys.stdout.write(str(thing))
35
64
36 class template:
65 def template(tmpl, **map):
37 def __init__(self, tmpl_dir):
66 while tmpl:
38 self.tmpl_dir = tmpl_dir
67 m = re.search(r"#([a-zA-Z0-9]+)#", tmpl)
39 def do_page(self, tmpl_fn, **map):
40 txt = file(os.path.join(self.tmpl_dir, tmpl_fn)).read()
41 while txt:
42 m = re.search(r"#([a-zA-Z0-9]+)#", txt)
43 if m:
68 if m:
44 yield txt[:m.start(0)]
69 yield tmpl[:m.start(0)]
45 v = map.get(m.group(1), "")
70 v = map.get(m.group(1), "")
46 if callable(v):
71 yield callable(v) and v() or v
47 for y in v(**map): yield y
72 tmpl = tmpl[m.end(0):]
48 else:
49 yield v
50 txt = txt[m.end(0):]
51 else:
73 else:
52 yield txt
74 yield tmpl
53 txt = ''
75 return
54
55 class page:
56 def __init__(self, tmpl_dir = "", type="text/html", title="Mercurial Web",
57 charset="ISO-8859-1"):
58 self.tmpl = template(tmpl_dir)
59
60 print 'Content-type: %s; charset=%s\n' % (type, charset)
61 write(self.tmpl.do_page('htmlstart.tmpl', title = title))
62
63 def endpage(self):
64 print '</BODY>'
65 print '</HTML>'
66
67 def show_diff(self, a, b, fn):
68 a = a.splitlines(1)
69 b = b.splitlines(1)
70 l = difflib.unified_diff(a, b, fn, fn)
71 print '<pre>'
72 for line in l:
73 line = cgi.escape(line[:-1])
74 if line.startswith('+'):
75 print '<span class="plusline">%s</span>' % (line, )
76 elif line.startswith('-'):
77 print '<span class="minusline">%s</span>' % (line, )
78 elif line.startswith('@'):
79 print '<span class="atline">%s</span>' % (line, )
80 else:
81 print line
82 print '</pre>'
83
84 class errpage(page):
85 def __init__(self, tmpl_dir):
86 page.__init__(self, tmpl_dir, title="Mercurial Web Error Page")
87
88 class change_list(page):
89 def __init__(self, repo, tmpl_dir, reponame, numchanges = 50):
90 page.__init__(self, tmpl_dir)
91 self.repo = repo
92 self.numchanges = numchanges
93 write(self.tmpl.do_page('changestitle.tmpl', reponame=reponame))
94
76
95 def content(self, hi=None):
77 class templater:
96 cl = []
78 def __init__(self, mapfile):
97 count = self.repo.changelog.count()
79 self.cache = {}
98 if not hi:
80 self.map = {}
99 hi = count
81 self.base = os.path.dirname(mapfile)
100 elif hi < self.numchanges:
101 hi = self.numchanges
102
103 start = 0
104 if hi - self.numchanges >= 0:
105 start = hi - self.numchanges
106
107 nav = "Displaying Revisions: %d-%d" % (start, hi-1)
108 if start != 0:
109 nav = ('<a href="?cmd=changes;hi=%d">Previous %d</a>&nbsp;&nbsp;' \
110 % (start, self.numchanges)) + nav
111 if hi != count:
112 if hi + self.numchanges <= count:
113 nav += '&nbsp;&nbsp;<a href="?cmd=changes;hi=%d">Next %d</a>' \
114 % (hi + self.numchanges, self.numchanges)
115 else:
116 nav += '&nbsp;&nbsp;<a href="?cmd=changes">Next %d</a>' % \
117 self.numchanges
118
119 print '<center>%s</center>' % nav
120
121 for i in xrange(start, hi):
122 n = self.repo.changelog.node(i)
123 cl.append((n, self.repo.changelog.read(n)))
124 cl.reverse()
125
126 print '<table summary="" width="100%" align="center">'
127 for n, ch in cl:
128 print '<tr><td>'
129 self.change_table(n, ch)
130 print '</td></tr>'
131 print '</table>'
132
133 print '<center>%s</center>' % nav
134
135 def change_table(self, nodeid, changes):
136 hn = hg.hex(nodeid)
137 i = self.repo.changelog.rev(nodeid)
138 (h1, h2) = [ hg.hex(x) for x in self.repo.changelog.parents(nodeid) ]
139 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
140 files = []
141 for f in changes[3]:
142 files.append('<a href="?cmd=file;cs=%s;fn=%s">%s</a>&nbsp;&nbsp;' \
143 % (hn, f, cgi.escape(f)))
144 write(self.tmpl.do_page('change_table.tmpl',
145 author=obfuscate(changes[1]),
146 desc=nl2br(cgi.escape(changes[4])), date=datestr,
147 files=' '.join(files), revnum=i, revnode=hn))
148
149 class checkin(page):
150 def __init__(self, repo, tmpl_dir, nodestr):
151 page.__init__(self, tmpl_dir)
152 self.repo = repo
153 self.node = hg.bin(nodestr)
154 self.nodestr = nodestr
155 print '<h3>Checkin: %s</h3>' % nodestr
156
82
157 def content(self):
83 for l in file(mapfile):
158 changes = self.repo.changelog.read(self.node)
84 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
159 i = self.repo.changelog.rev(self.node)
85 if m:
160 parents = self.repo.changelog.parents(self.node)
86 self.cache[m.group(1)] = m.group(2)
161 (h1, h2) = [ hg.hex(x) for x in parents ]
162 (i1, i2) = [ self.repo.changelog.rev(x) for x in parents ]
163 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
164 mf = self.repo.manifest.read(changes[0])
165 files = []
166 for f in changes[3]:
167 files.append('<a href="?cmd=file;nd=%s;fn=%s">%s</a>&nbsp;&nbsp;' \
168 % (hg.hex(mf[f]), f, cgi.escape(f)))
169 p2link = h2
170 if i2 != -1:
171 p2link = '<a href="?cmd=chkin;nd=%s">%s</a>' % (h2, h2)
172
173 write(self.tmpl.do_page('checkin.tmpl', revnum=i, revnode=self.nodestr,
174 p1num=i1, p1node=h1, p2num=i2, p2node=h2, p2link=p2link,
175 mfnum=self.repo.manifest.rev(changes[0]),
176 mfnode=hg.hex(changes[0]), author=obfuscate(changes[1]),
177 desc=nl2br(cgi.escape(changes[4])), date=datestr,
178 files=' '.join(files)))
179
180 (c, a, d) = self.repo.diffrevs(parents[0], self.node)
181 change = self.repo.changelog.read(parents[0])
182 mf2 = self.repo.manifest.read(change[0])
183 for f in c:
184 self.show_diff(self.repo.file(f).read(mf2[f]), \
185 self.repo.file(f).read(mf[f]), f)
186 for f in a:
187 self.show_diff('', self.repo.file(f).read(mf[f]), f)
188 for f in d:
189 self.show_diff(self.repo.file(f).read(mf2[f]), '', f)
190
191 class filepage(page):
192 def __init__(self, repo, tmpl_dir, fn, node=None, cs=None):
193 page.__init__(self, tmpl_dir)
194 self.repo = repo
195 self.fn = fn
196 if cs:
197 chng = self.repo.changelog.read(hg.bin(cs))
198 mf = self.repo.manifest.read(chng[0])
199 self.node = mf[self.fn]
200 self.nodestr = hg.hex(self.node)
201 else:
87 else:
202 self.nodestr = node
88 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
203 self.node = hg.bin(node)
89 if m:
204 print '<div class="filename">%s (%s)</div>' % \
90 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
205 (cgi.escape(self.fn), self.nodestr, )
91 else:
206 print '<a href="?cmd=hist;fn=%s">history</a><br />' % self.fn
92 raise "unknown map entry '%s'" % l
207 print '<a href="?cmd=ann;fn=%s;nd=%s">annotate</a><br />' % \
208 (self.fn, self.nodestr)
209
210 def content(self):
211 print '<pre>'
212 print cgi.escape(self.repo.file(self.fn).read(self.node))
213 print '</pre>'
214
93
215 class annpage(page):
94 def __call__(self, t, **map):
216 def __init__(self, repo, tmpl_dir, fn, node):
95 try:
217 page.__init__(self, tmpl_dir)
96 tmpl = self.cache[t]
218 self.repo = repo
97 except KeyError:
219 self.fn = fn
98 tmpl = self.cache[t] = file(self.map[t]).read()
220 self.nodestr = node
99 return template(tmpl, **map)
221 self.node = hg.bin(node)
222 print '<div class="annotation">Annotated: %s (%s)</div>' % \
223 (cgi.escape(self.fn), self.nodestr, )
224
225 def content(self):
226 print '<pre>'
227 for n, l in self.repo.file(self.fn).annotate(self.node):
228 cnode = self.repo.changelog.lookup(n)
229 write(self.tmpl.do_page('annline.tmpl', cnode=hg.hex(cnode),
230 cnum='% 6s' % n, fn=self.fn, line=cgi.escape(l[:-1])))
231 print '</pre>'
232
233 class mfpage(page):
234 def __init__(self, repo, tmpl_dir, node):
235 page.__init__(self, tmpl_dir)
236 self.repo = repo
237 self.nodestr = node
238 self.node = hg.bin(node)
239
240 def content(self):
241 mf = self.repo.manifest.read(self.node)
242 fns = mf.keys()
243 fns.sort()
244 write(self.tmpl.do_page('mftitle.tmpl', node = self.nodestr))
245 for f in fns:
246 write(self.tmpl.do_page('mfentry.tmpl', fn=f, node=hg.hex(mf[f])))
247
248 class histpage(page):
249 def __init__(self, repo, tmpl_dir, fn):
250 page.__init__(self, tmpl_dir)
251 self.repo = repo
252 self.fn = fn
253
254 def content(self):
255 print '<div class="filehist">File History: %s</div>' % self.fn
256 r = self.repo.file(self.fn)
257 print '<br />'
258 print '<table summary="" width="100%" align="center">'
259 for i in xrange(r.count()-1, -1, -1):
260 print '<tr><td>'
261 self.hist_ent(i, r)
262 print '</tr></td>'
263 print '</table>'
264
265 def hist_ent(self, i, r):
266 n = r.node(i)
267 (p1, p2) = r.parents(n)
268 (h, h1, h2) = map(hg.hex, (n, p1, p2))
269 (i1, i2) = map(r.rev, (p1, p2))
270 ci = r.linkrev(n)
271 cn = self.repo.changelog.node(ci)
272 cs = hg.hex(cn)
273 changes = self.repo.changelog.read(cn)
274 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
275 p2entry = ''
276 if i2 != -1:
277 p2entry = '&nbsp;&nbsp;%d:<a href="?cmd=file;nd=%s;fn=%s">%s</a>' \
278 % (i2, h2, self.fn, h2 ),
279 write(self.tmpl.do_page('hist_ent.tmpl', author=obfuscate(changes[1]),
280 csnode=cs, desc=nl2br(cgi.escape(changes[4])),
281 date = datestr, fn=self.fn, revnode=h, p1num = i1,
282 p1node=h1, p2entry=p2entry))
283
100
284 class hgweb:
101 class hgweb:
285 repo_path = "."
102 maxchanges = 20
286 numchanges = 50
103 maxfiles = 10
287 tmpl_dir = "templates"
104
105 def __init__(self, path, name, templatemap):
106 self.reponame = name
107 self.repo = repository(ui(), path)
108 self.t = templater(templatemap)
109
110 def date(self, cs):
111 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
112
113 def listfiles(self, files, mf):
114 for f in files[:self.maxfiles]:
115 yield self.t("filenodelink", node = hex(mf[f]), file = f)
116 if len(files) > self.maxfiles:
117 yield self.t("fileellipses")
118
119 def listfilediffs(self, files, changeset):
120 for f in files[:self.maxfiles]:
121 yield self.t("filedifflink", node = hex(changeset), file = f)
122 if len(files) > self.maxfiles:
123 yield self.t("fileellipses")
124
125 def diff(self, node1, node2, files):
126 def filterfiles(list, files):
127 l = [ x for x in list if x in files ]
128
129 for f in files:
130 if f[-1] != os.sep: f += os.sep
131 l += [ x for x in list if x.startswith(f) ]
132 return l
133
134 def prettyprint(diff):
135 for l in diff.splitlines(1):
136 line = cgi.escape(l)
137 if line.startswith('+'):
138 yield self.t("difflineplus", line = line)
139 elif line.startswith('-'):
140 yield self.t("difflineminus", line = line)
141 elif line.startswith('@'):
142 yield self.t("difflineat", line = line)
143 else:
144 yield self.t("diffline", line = line)
145
146 r = self.repo
147 cl = r.changelog
148 mf = r.manifest
149 change1 = cl.read(node1)
150 change2 = cl.read(node2)
151 mmap1 = mf.read(change1[0])
152 mmap2 = mf.read(change2[0])
153 date1 = self.date(change1)
154 date2 = self.date(change2)
155
156 c, a, d = r.diffrevs(node1, node2)
157 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
158
159 for f in c:
160 to = r.file(f).read(mmap1[f])
161 tn = r.file(f).read(mmap2[f])
162 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
163 for f in a:
164 to = ""
165 tn = r.file(f).read(mmap2[f])
166 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
167 for f in d:
168 to = r.file(f).read(mmap1[f])
169 tn = ""
170 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
171
172 def changelog(self, pos=None):
173 def changenav():
174 def seq(factor = 1):
175 yield 1 * factor
176 yield 2 * factor
177 yield 5 * factor
178 for f in seq(factor * 10):
179 yield f
180
181 linear = range(0, count - 2, self.maxchanges)[0:8]
182
183 for i in linear:
184 yield self.t("naventry", rev = max(i, 1))
185
186 for s in seq():
187 if s > count - 2: break
188 if s > linear[-1]:
189 yield self.t("naventry", rev = s)
190
191 yield self.t("naventry", rev = count - 1)
192
193 def changelist():
194 cl = self.repo.changelog
195 l = [] # build a list in forward order for efficiency
196 for i in range(start, end + 1):
197 n = cl.node(i)
198 changes = cl.read(n)
199 hn = hex(n)
200 p1, p2 = cl.parents(n)
201 t = float(changes[2].split(' ')[0])
202
203 l.insert(0, self.t(
204 'changelogentry',
205 author = obfuscate(changes[1]),
206 shortdesc = cgi.escape(changes[4].splitlines()[0]),
207 age = age(t),
208 p1 = hex(p1), p2 = hex(p2),
209 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
210 manifest = hex(changes[0]),
211 desc = nl2br(cgi.escape(changes[4])),
212 date = time.asctime(time.gmtime(t)),
213 files = self.listfilediffs(changes[3], n),
214 rev = i,
215 node = hn))
216
217 yield l
218
219 count = self.repo.changelog.count()
220 pos = pos or count - 1
221 end = min(pos, count - 1)
222 start = max(0, pos - self.maxchanges)
223 end = min(count - 1, start + self.maxchanges)
224
225 yield self.t('changelog', repo = self.reponame, changenav = changenav,
226 rev = pos, changesets = count, changelist = changelist)
227
228 def changeset(self, nodeid):
229 n = bin(nodeid)
230 cl = self.repo.changelog
231 changes = cl.read(n)
232 p1, p2 = cl.parents(n)
233 p1rev, p2rev = cl.rev(p1), cl.rev(p2)
234 t = float(changes[2].split(' ')[0])
235
236 files = []
237 mf = self.repo.manifest.read(changes[0])
238 for f in changes[3]:
239 files.append(self.t("filenodelink",
240 filenode = hex(mf[f]), file = f))
241
242 def diff():
243 yield self.diff(p1, n, changes[3])
244
245 yield self.t('changeset',
246 diff = diff,
247 rev = cl.rev(n),
248 node = nodeid,
249 shortdesc = cgi.escape(changes[4].splitlines()[0]),
250 p1 = hex(p1), p2 = hex(p2),
251 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
252 manifest = hex(changes[0]),
253 author = obfuscate(changes[1]),
254 desc = nl2br(cgi.escape(changes[4])),
255 date = time.asctime(time.gmtime(t)),
256 files = files)
257
258 def filelog(self, f, filenode):
259 cl = self.repo.changelog
260 fl = self.repo.file(f)
261 count = fl.count()
262
263 def entries():
264 l = []
265 for i in range(count):
266
267 n = fl.node(i)
268 lr = fl.linkrev(n)
269 cn = cl.node(lr)
270 cs = cl.read(cl.node(lr))
271 p1, p2 = fl.parents(n)
272 t = float(cs[2].split(' ')[0])
288
273
289 def __init__(self):
274 l.insert(0, self.t("filelogentry",
290 pass
275 filenode = hex(n),
276 filerev = i,
277 file = f,
278 node = hex(cn),
279 author = obfuscate(cs[1]),
280 age = age(t),
281 date = time.asctime(time.gmtime(t)),
282 shortdesc = cgi.escape(cs[4].splitlines()[0]),
283 p1 = hex(p1), p2 = hex(p2),
284 p1rev = fl.rev(p1), p2rev = fl.rev(p2)))
285
286 yield l
287
288 yield self.t("filelog",
289 file = f,
290 filenode = filenode,
291 entries = entries)
292
293 def filerevision(self, f, node):
294 fl = self.repo.file(f)
295 n = bin(node)
296 text = cgi.escape(fl.read(n))
297 changerev = fl.linkrev(n)
298 cl = self.repo.changelog
299 cn = cl.node(changerev)
300 cs = cl.read(cn)
301 p1, p2 = fl.parents(n)
302 t = float(cs[2].split(' ')[0])
303 mfn = cs[0]
304
305 yield self.t("filerevision", file = f,
306 filenode = node,
307 path = up(f),
308 text = text,
309 rev = changerev,
310 node = hex(cn),
311 manifest = hex(mfn),
312 author = obfuscate(cs[1]),
313 age = age(t),
314 date = time.asctime(time.gmtime(t)),
315 shortdesc = cgi.escape(cs[4].splitlines()[0]),
316 p1 = hex(p1), p2 = hex(p2),
317 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
318
319
320 def fileannotate(self, f, node):
321 bcache = {}
322 ncache = {}
323 fl = self.repo.file(f)
324 n = bin(node)
325 changerev = fl.linkrev(n)
326
327 cl = self.repo.changelog
328 cn = cl.node(changerev)
329 cs = cl.read(cn)
330 p1, p2 = fl.parents(n)
331 t = float(cs[2].split(' ')[0])
332 mfn = cs[0]
333
334 def annotate():
335 for r, l in fl.annotate(n):
336 try:
337 cnode = ncache[r]
338 except KeyError:
339 cnode = ncache[r] = self.repo.changelog.node(r)
340
341 try:
342 name = bcache[r]
343 except KeyError:
344 cl = self.repo.changelog.read(cnode)
345 name = cl[1]
346 f = name.find('@')
347 if f >= 0:
348 name = name[:f]
349 bcache[r] = name
350
351 yield self.t("annotateline",
352 node = hex(cnode),
353 rev = r,
354 author = name,
355 file = f,
356 line = cgi.escape(l))
357
358 yield self.t("fileannotate",
359 file = f,
360 filenode = node,
361 annotate = annotate,
362 path = up(f),
363 rev = changerev,
364 node = hex(cn),
365 manifest = hex(mfn),
366 author = obfuscate(cs[1]),
367 age = age(t),
368 date = time.asctime(time.gmtime(t)),
369 shortdesc = cgi.escape(cs[4].splitlines()[0]),
370 p1 = hex(p1), p2 = hex(p2),
371 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
372
373 def manifest(self, mnode, path):
374 mf = self.repo.manifest.read(bin(mnode))
375 rev = self.repo.manifest.rev(bin(mnode))
376 node = self.repo.changelog.node(rev)
377
378 dirs = {}
379 files = {}
380 short = {}
381
382 p = path[1:]
383 l = len(p)
384
385 for f,n in mf.items():
386 if f[:l] != p:
387 continue
388 remain = f[l:]
389 if "/" in remain:
390 short = remain[:remain.find("/") + 1] # bleah
391 dirs[short] = 1
392 else:
393 short = os.path.basename(remain)
394 files[short] = (f, n)
395
396 def dirlist():
397 dl = dirs.keys()
398 dl.sort()
399
400 for d in dl:
401 yield self.t("manifestdirentry",
402 path = os.path.join(path, d),
403 manifest = mnode, basename = d[:-1])
404
405 def filelist():
406 fl = files.keys()
407 fl.sort()
408 for f in fl:
409 full, fnode = files[f]
410 yield self.t("manifestfileentry",
411 file = full, manifest = mnode, filenode = hex(fnode),
412 basename = f)
413
414 yield self.t("manifest",
415 manifest = mnode,
416 rev = rev,
417 node = hex(node),
418 path = path,
419 up = up(path),
420 dirs = dirlist,
421 files = filelist)
422
423 def filediff(self, file, changeset):
424 n = bin(changeset)
425 cl = self.repo.changelog
426 p1 = cl.parents(n)[0]
427 cs = cl.read(n)
428 mf = self.repo.manifest.read(cs[0])
429
430 def diff():
431 yield self.diff(p1, n, file)
432
433 yield self.t("filediff",
434 file = file,
435 filenode = hex(mf[file]),
436 node = changeset,
437 rev = self.repo.changelog.rev(n),
438 p1 = hex(p1),
439 p1rev = self.repo.changelog.rev(p1),
440 diff = diff)
441
442 # header and footer, css
443 # add tags to things
444 # show parents
445 # diff between rev and parent in changeset and file
446 # manifest links
447 # browse at top
448 # tags -> list of changesets corresponding to tags
449 # find tag, changeset, file
291
450
292 def run(self):
451 def run(self):
293
294 args = cgi.parse()
452 args = cgi.parse()
295
453
296 ui = hg.ui()
454 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
297 repo = hg.repository(ui, self.repo_path)
455 hi = self.repo.changelog.count()
456 if args.has_key('pos'):
457 hi = int(args['pos'][0])
458
459 write(self.changelog(hi))
298
460
299 if not args.has_key('cmd') or args['cmd'][0] == 'changes':
461 elif args['cmd'][0] == 'changeset':
300 page = change_list(repo, self.tmpl_dir, 'Mercurial',
462 write(self.changeset(args['node'][0]))
301 self.numchanges)
302 hi = args.get('hi', ( repo.changelog.count(), ))
303 page.content(hi = int(hi[0]))
304 page.endpage()
305
463
306 elif args['cmd'][0] == 'chkin':
464 elif args['cmd'][0] == 'manifest':
307 if not args.has_key('nd'):
465 write(self.manifest(args['manifest'][0], args['path'][0]))
308 page = errpage(self.tmpl_dir)
466
309 print '<div class="errmsg">No Node!</div>'
467 elif args['cmd'][0] == 'filediff':
310 else:
468 write(self.filediff(args['file'][0], args['node'][0]))
311 page = checkin(repo, self.tmpl_dir, args['nd'][0])
312 page.content()
313 page.endpage()
314
469
315 elif args['cmd'][0] == 'file':
470 elif args['cmd'][0] == 'file':
316 if not (args.has_key('nd') and args.has_key('fn')) and \
471 write(self.filerevision(args['file'][0], args['filenode'][0]))
317 not (args.has_key('cs') and args.has_key('fn')):
318 page = errpage(self.tmpl_dir)
319 print '<div class="errmsg">Invalid Args!</div>'
320 else:
321 if args.has_key('nd'):
322 page = filepage(repo, self.tmpl_dir,
323 args['fn'][0], node=args['nd'][0])
324 else:
325 page = filepage(repo, self.tmpl_dir,
326 args['fn'][0], cs=args['cs'][0])
327 page.content()
328 page.endpage()
329
472
330 elif args['cmd'][0] == 'mf':
473 elif args['cmd'][0] == 'annotate':
331 if not args.has_key('nd'):
474 write(self.fileannotate(args['file'][0], args['filenode'][0]))
332 page = errpage(self.tmpl_dir)
333 print '<div class="errmsg">No Node!</div>'
334 else:
335 page = mfpage(repo, self.tmpl_dir, args['nd'][0])
336 page.content()
337 page.endpage()
338
475
339 elif args['cmd'][0] == 'hist':
476 elif args['cmd'][0] == 'filelog':
340 if not args.has_key('fn'):
477 write(self.filelog(args['file'][0], args['filenode'][0]))
341 page = errpage(self.tmpl_dir)
342 print '<div class="errmsg">No Filename!</div>'
343 else:
344 page = histpage(repo, self.tmpl_dir, args['fn'][0])
345 page.content()
346 page.endpage()
347
348 elif args['cmd'][0] == 'ann':
349 if not args.has_key('fn'):
350 page = errpage(self.tmpl_dir)
351 print '<div class="errmsg">No Filename!</div>'
352 elif not args.has_key('nd'):
353 page = errpage(self.tmpl_dir)
354 print '<div class="errmsg">No Node!</div>'
355 else:
356 page = annpage(repo, self.tmpl_dir, args['fn'][0],
357 args['nd'][0])
358 page.content()
359 page.endpage()
360
478
361 elif args['cmd'][0] == 'branches':
479 elif args['cmd'][0] == 'branches':
362 httphdr("text/plain")
480 httphdr("text/plain")
363 nodes = []
481 nodes = []
364 if args.has_key('nodes'):
482 if args.has_key('nodes'):
365 nodes = map(hg.bin, args['nodes'][0].split(" "))
483 nodes = map(bin, args['nodes'][0].split(" "))
366 for b in repo.branches(nodes):
484 for b in self.repo.branches(nodes):
367 print " ".join(map(hg.hex, b))
485 sys.stdout.write(" ".join(map(hex, b)) + "\n")
368
486
369 elif args['cmd'][0] == 'between':
487 elif args['cmd'][0] == 'between':
370 httphdr("text/plain")
488 httphdr("text/plain")
371 nodes = []
489 nodes = []
372 if args.has_key('pairs'):
490 if args.has_key('pairs'):
373 pairs = [ map(hg.bin, p.split("-"))
491 pairs = [ map(bin, p.split("-"))
374 for p in args['pairs'][0].split(" ") ]
492 for p in args['pairs'][0].split(" ") ]
375 for b in repo.between(pairs):
493 for b in self.repo.between(pairs):
376 print " ".join(map(hg.hex, b))
494 sys.stdout.write(" ".join(map(hex, b)) + "\n")
377
495
378 elif args['cmd'][0] == 'changegroup':
496 elif args['cmd'][0] == 'changegroup':
379 httphdr("application/hg-changegroup")
497 httphdr("application/hg-changegroup")
380 nodes = []
498 nodes = []
381 if args.has_key('roots'):
499 if args.has_key('roots'):
382 nodes = map(hg.bin, args['roots'][0].split(" "))
500 nodes = map(bin, args['roots'][0].split(" "))
383
501
384 z = zlib.compressobj()
502 z = zlib.compressobj()
385 for chunk in repo.changegroup(nodes):
503 for chunk in self.repo.changegroup(nodes):
386 sys.stdout.write(z.compress(chunk))
504 sys.stdout.write(z.compress(chunk))
387
505
388 sys.stdout.write(z.flush())
506 sys.stdout.write(z.flush())
389
507
390 else:
508 else:
391 page = errpage(self.tmpl_dir)
509 write(self.t("error"))
392 print '<div class="errmsg">unknown command: %s</div>' % \
393 cgi.escape(args['cmd'][0])
394 page.endpage()
395
510
396 if __name__ == "__main__":
511 if __name__ == "__main__":
397 hgweb().run()
512 hgweb().run()
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now