##// 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, (717 lines changed) Show them Hide them
@@ -11,16 +11,45 b' import cgitb'
11 11 cgitb.enable()
12 12
13 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 40 def nl2br(text):
17 return re.sub('\n', '<br />', text)
41 return text.replace('\n', '<br/>')
18 42
19 43 def obfuscate(text):
20 l = []
21 for c in text:
22 l.append('&#%d;' % ord(c))
23 return ''.join(l)
44 return ''.join([ '&#%d' % ord(c) for c in text ])
45
46 def up(p):
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 54 def httphdr(type):
26 55 print 'Content-type: %s\n' % type
@@ -33,365 +62,451 b' def write(*things):'
33 62 else:
34 63 sys.stdout.write(str(thing))
35 64
36 class template:
37 def __init__(self, tmpl_dir):
38 self.tmpl_dir = tmpl_dir
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)
65 def template(tmpl, **map):
66 while tmpl:
67 m = re.search(r"#([a-zA-Z0-9]+)#", tmpl)
68 if m:
69 yield tmpl[:m.start(0)]
70 v = map.get(m.group(1), "")
71 yield callable(v) and v() or v
72 tmpl = tmpl[m.end(0):]
73 else:
74 yield tmpl
75 return
76
77 class templater:
78 def __init__(self, mapfile):
79 self.cache = {}
80 self.map = {}
81 self.base = os.path.dirname(mapfile)
82
83 for l in file(mapfile):
84 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
43 85 if m:
44 yield txt[:m.start(0)]
45 v = map.get(m.group(1), "")
46 if callable(v):
47 for y in v(**map): yield y
86 self.cache[m.group(1)] = m.group(2)
87 else:
88 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
89 if m:
90 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
48 91 else:
49 yield v
50 txt = txt[m.end(0):]
51 else:
52 yield txt
53 txt = ''
92 raise "unknown map entry '%s'" % l
54 93
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)
94 def __call__(self, t, **map):
95 try:
96 tmpl = self.cache[t]
97 except KeyError:
98 tmpl = self.cache[t] = file(self.map[t]).read()
99 return template(tmpl, **map)
100
101 class hgweb:
102 maxchanges = 20
103 maxfiles = 10
59 104
60 print 'Content-type: %s; charset=%s\n' % (type, charset)
61 write(self.tmpl.do_page('htmlstart.tmpl', title = title))
105 def __init__(self, path, name, templatemap):
106 self.reponame = name
107 self.repo = repository(ui(), path)
108 self.t = templater(templatemap)
62 109
63 def endpage(self):
64 print '</BODY>'
65 print '</HTML>'
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
66 133
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>'
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)
83 145
84 class errpage(page):
85 def __init__(self, tmpl_dir):
86 page.__init__(self, tmpl_dir, title="Mercurial Web Error Page")
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)
87 155
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))
156 c, a, d = r.diffrevs(node1, node2)
157 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
94 158
95 def content(self, hi=None):
96 cl = []
97 count = self.repo.changelog.count()
98 if not hi:
99 hi = count
100 elif hi < self.numchanges:
101 hi = self.numchanges
102
103 start = 0
104 if hi - self.numchanges >= 0:
105 start = hi - self.numchanges
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))
106 171
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
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))
118 185
119 print '<center>%s</center>' % nav
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)
120 192
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()
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])
125 202
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>'
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
132 218
133 print '<center>%s</center>' % nav
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)
134 227
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])))
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
140 236 files = []
237 mf = self.repo.manifest.read(changes[0])
141 238 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))
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])
148 244
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
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)
156 257
157 def content(self):
158 changes = self.repo.changelog.read(self.node)
159 i = self.repo.changelog.rev(self.node)
160 parents = self.repo.changelog.parents(self.node)
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)
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])
172 273
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)))
274 l.insert(0, self.t("filelogentry",
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)
179 292
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)
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
190 319
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:
202 self.nodestr = node
203 self.node = hg.bin(node)
204 print '<div class="filename">%s (%s)</div>' % \
205 (cgi.escape(self.fn), self.nodestr, )
206 print '<a href="?cmd=hist;fn=%s">history</a><br />' % self.fn
207 print '<a href="?cmd=ann;fn=%s;nd=%s">annotate</a><br />' % \
208 (self.fn, self.nodestr)
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]
209 333
210 def content(self):
211 print '<pre>'
212 print cgi.escape(self.repo.file(self.fn).read(self.node))
213 print '</pre>'
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
214 350
215 class annpage(page):
216 def __init__(self, repo, tmpl_dir, fn, node):
217 page.__init__(self, tmpl_dir)
218 self.repo = repo
219 self.fn = fn
220 self.nodestr = node
221 self.node = hg.bin(node)
222 print '<div class="annotation">Annotated: %s (%s)</div>' % \
223 (cgi.escape(self.fn), self.nodestr, )
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))
224 372
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>'
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 = {}
232 381
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)
382 p = path[1:]
383 l = len(p)
239 384
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])))
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)
247 395
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
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])
253 404
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>'
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)
264 422
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
284 class hgweb:
285 repo_path = "."
286 numchanges = 50
287 tmpl_dir = "templates"
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)
288 432
289 def __init__(self):
290 pass
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 451 def run(self):
293
294 452 args = cgi.parse()
295 453
296 ui = hg.ui()
297 repo = hg.repository(ui, self.repo_path)
454 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
455 hi = self.repo.changelog.count()
456 if args.has_key('pos'):
457 hi = int(args['pos'][0])
298 458
299 if not args.has_key('cmd') or args['cmd'][0] == 'changes':
300 page = change_list(repo, self.tmpl_dir, 'Mercurial',
301 self.numchanges)
302 hi = args.get('hi', ( repo.changelog.count(), ))
303 page.content(hi = int(hi[0]))
304 page.endpage()
459 write(self.changelog(hi))
305 460
306 elif args['cmd'][0] == 'chkin':
307 if not args.has_key('nd'):
308 page = errpage(self.tmpl_dir)
309 print '<div class="errmsg">No Node!</div>'
310 else:
311 page = checkin(repo, self.tmpl_dir, args['nd'][0])
312 page.content()
313 page.endpage()
461 elif args['cmd'][0] == 'changeset':
462 write(self.changeset(args['node'][0]))
463
464 elif args['cmd'][0] == 'manifest':
465 write(self.manifest(args['manifest'][0], args['path'][0]))
466
467 elif args['cmd'][0] == 'filediff':
468 write(self.filediff(args['file'][0], args['node'][0]))
314 469
315 470 elif args['cmd'][0] == 'file':
316 if not (args.has_key('nd') and args.has_key('fn')) and \
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()
471 write(self.filerevision(args['file'][0], args['filenode'][0]))
329 472
330 elif args['cmd'][0] == 'mf':
331 if not args.has_key('nd'):
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()
473 elif args['cmd'][0] == 'annotate':
474 write(self.fileannotate(args['file'][0], args['filenode'][0]))
338 475
339 elif args['cmd'][0] == 'hist':
340 if not args.has_key('fn'):
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()
476 elif args['cmd'][0] == 'filelog':
477 write(self.filelog(args['file'][0], args['filenode'][0]))
360 478
361 479 elif args['cmd'][0] == 'branches':
362 480 httphdr("text/plain")
363 481 nodes = []
364 482 if args.has_key('nodes'):
365 nodes = map(hg.bin, args['nodes'][0].split(" "))
366 for b in repo.branches(nodes):
367 print " ".join(map(hg.hex, b))
483 nodes = map(bin, args['nodes'][0].split(" "))
484 for b in self.repo.branches(nodes):
485 sys.stdout.write(" ".join(map(hex, b)) + "\n")
368 486
369 487 elif args['cmd'][0] == 'between':
370 488 httphdr("text/plain")
371 489 nodes = []
372 490 if args.has_key('pairs'):
373 pairs = [ map(hg.bin, p.split("-"))
491 pairs = [ map(bin, p.split("-"))
374 492 for p in args['pairs'][0].split(" ") ]
375 for b in repo.between(pairs):
376 print " ".join(map(hg.hex, b))
493 for b in self.repo.between(pairs):
494 sys.stdout.write(" ".join(map(hex, b)) + "\n")
377 495
378 496 elif args['cmd'][0] == 'changegroup':
379 497 httphdr("application/hg-changegroup")
380 498 nodes = []
381 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 502 z = zlib.compressobj()
385 for chunk in repo.changegroup(nodes):
503 for chunk in self.repo.changegroup(nodes):
386 504 sys.stdout.write(z.compress(chunk))
387 505
388 506 sys.stdout.write(z.flush())
389 507
390 508 else:
391 page = errpage(self.tmpl_dir)
392 print '<div class="errmsg">unknown command: %s</div>' % \
393 cgi.escape(args['cmd'][0])
394 page.endpage()
509 write(self.t("error"))
395 510
396 511 if __name__ == "__main__":
397 512 hgweb().run()
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now