##// 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 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):
68 if m:
40 txt = file(os.path.join(self.tmpl_dir, tmpl_fn)).read()
69 yield tmpl[:m.start(0)]
41 while txt:
70 v = map.get(m.group(1), "")
42 m = re.search(r"#([a-zA-Z0-9]+)#", txt)
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 if m:
85 if m:
44 yield txt[:m.start(0)]
86 self.cache[m.group(1)] = m.group(2)
45 v = map.get(m.group(1), "")
87 else:
46 if callable(v):
88 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
47 for y in v(**map): yield y
89 if m:
90 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
48 else:
91 else:
49 yield v
92 raise "unknown map entry '%s'" % l
50 txt = txt[m.end(0):]
51 else:
52 yield txt
53 txt = ''
54
93
55 class page:
94 def __call__(self, t, **map):
56 def __init__(self, tmpl_dir = "", type="text/html", title="Mercurial Web",
95 try:
57 charset="ISO-8859-1"):
96 tmpl = self.cache[t]
58 self.tmpl = template(tmpl_dir)
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)
105 def __init__(self, path, name, templatemap):
61 write(self.tmpl.do_page('htmlstart.tmpl', title = title))
106 self.reponame = name
107 self.repo = repository(ui(), path)
108 self.t = templater(templatemap)
62
109
63 def endpage(self):
110 def date(self, cs):
64 print '</BODY>'
111 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
65 print '</HTML>'
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):
134 def prettyprint(diff):
68 a = a.splitlines(1)
135 for l in diff.splitlines(1):
69 b = b.splitlines(1)
136 line = cgi.escape(l)
70 l = difflib.unified_diff(a, b, fn, fn)
137 if line.startswith('+'):
71 print '<pre>'
138 yield self.t("difflineplus", line = line)
72 for line in l:
139 elif line.startswith('-'):
73 line = cgi.escape(line[:-1])
140 yield self.t("difflineminus", line = line)
74 if line.startswith('+'):
141 elif line.startswith('@'):
75 print '<span class="plusline">%s</span>' % (line, )
142 yield self.t("difflineat", line = line)
76 elif line.startswith('-'):
143 else:
77 print '<span class="minusline">%s</span>' % (line, )
144 yield self.t("diffline", line = line)
78 elif line.startswith('@'):
79 print '<span class="atline">%s</span>' % (line, )
80 else:
81 print line
82 print '</pre>'
83
145
84 class errpage(page):
146 r = self.repo
85 def __init__(self, tmpl_dir):
147 cl = r.changelog
86 page.__init__(self, tmpl_dir, title="Mercurial Web Error Page")
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):
156 c, a, d = r.diffrevs(node1, node2)
89 def __init__(self, repo, tmpl_dir, reponame, numchanges = 50):
157 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
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
158
95 def content(self, hi=None):
159 for f in c:
96 cl = []
160 to = r.file(f).read(mmap1[f])
97 count = self.repo.changelog.count()
161 tn = r.file(f).read(mmap2[f])
98 if not hi:
162 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
99 hi = count
163 for f in a:
100 elif hi < self.numchanges:
164 to = ""
101 hi = self.numchanges
165 tn = r.file(f).read(mmap2[f])
102
166 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
103 start = 0
167 for f in d:
104 if hi - self.numchanges >= 0:
168 to = r.file(f).read(mmap1[f])
105 start = hi - self.numchanges
169 tn = ""
170 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
106
171
107 nav = "Displaying Revisions: %d-%d" % (start, hi-1)
172 def changelog(self, pos=None):
108 if start != 0:
173 def changenav():
109 nav = ('<a href="?cmd=changes;hi=%d">Previous %d</a>&nbsp;&nbsp;' \
174 def seq(factor = 1):
110 % (start, self.numchanges)) + nav
175 yield 1 * factor
111 if hi != count:
176 yield 2 * factor
112 if hi + self.numchanges <= count:
177 yield 5 * factor
113 nav += '&nbsp;&nbsp;<a href="?cmd=changes;hi=%d">Next %d</a>' \
178 for f in seq(factor * 10):
114 % (hi + self.numchanges, self.numchanges)
179 yield f
115 else:
180
116 nav += '&nbsp;&nbsp;<a href="?cmd=changes">Next %d</a>' % \
181 linear = range(0, count - 2, self.maxchanges)[0:8]
117 self.numchanges
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):
193 def changelist():
122 n = self.repo.changelog.node(i)
194 cl = self.repo.changelog
123 cl.append((n, self.repo.changelog.read(n)))
195 l = [] # build a list in forward order for efficiency
124 cl.reverse()
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">'
203 l.insert(0, self.t(
127 for n, ch in cl:
204 'changelogentry',
128 print '<tr><td>'
205 author = obfuscate(changes[1]),
129 self.change_table(n, ch)
206 shortdesc = cgi.escape(changes[4].splitlines()[0]),
130 print '</td></tr>'
207 age = age(t),
131 print '</table>'
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):
228 def changeset(self, nodeid):
136 hn = hg.hex(nodeid)
229 n = bin(nodeid)
137 i = self.repo.changelog.rev(nodeid)
230 cl = self.repo.changelog
138 (h1, h2) = [ hg.hex(x) for x in self.repo.changelog.parents(nodeid) ]
231 changes = cl.read(n)
139 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
232 p1, p2 = cl.parents(n)
233 p1rev, p2rev = cl.rev(p1), cl.rev(p2)
234 t = float(changes[2].split(' ')[0])
235
140 files = []
236 files = []
237 mf = self.repo.manifest.read(changes[0])
141 for f in changes[3]:
238 for f in changes[3]:
142 files.append('<a href="?cmd=file;cs=%s;fn=%s">%s</a>&nbsp;&nbsp;' \
239 files.append(self.t("filenodelink",
143 % (hn, f, cgi.escape(f)))
240 filenode = hex(mf[f]), file = f))
144 write(self.tmpl.do_page('change_table.tmpl',
241
145 author=obfuscate(changes[1]),
242 def diff():
146 desc=nl2br(cgi.escape(changes[4])), date=datestr,
243 yield self.diff(p1, n, changes[3])
147 files=' '.join(files), revnum=i, revnode=hn))
148
244
149 class checkin(page):
245 yield self.t('changeset',
150 def __init__(self, repo, tmpl_dir, nodestr):
246 diff = diff,
151 page.__init__(self, tmpl_dir)
247 rev = cl.rev(n),
152 self.repo = repo
248 node = nodeid,
153 self.node = hg.bin(nodestr)
249 shortdesc = cgi.escape(changes[4].splitlines()[0]),
154 self.nodestr = nodestr
250 p1 = hex(p1), p2 = hex(p2),
155 print '<h3>Checkin: %s</h3>' % nodestr
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):
258 def filelog(self, f, filenode):
158 changes = self.repo.changelog.read(self.node)
259 cl = self.repo.changelog
159 i = self.repo.changelog.rev(self.node)
260 fl = self.repo.file(f)
160 parents = self.repo.changelog.parents(self.node)
261 count = fl.count()
161 (h1, h2) = [ hg.hex(x) for x in parents ]
262
162 (i1, i2) = [ self.repo.changelog.rev(x) for x in parents ]
263 def entries():
163 datestr = time.asctime(time.gmtime(float(changes[2].split(' ')[0])))
264 l = []
164 mf = self.repo.manifest.read(changes[0])
265 for i in range(count):
165 files = []
266
166 for f in changes[3]:
267 n = fl.node(i)
167 files.append('<a href="?cmd=file;nd=%s;fn=%s">%s</a>&nbsp;&nbsp;' \
268 lr = fl.linkrev(n)
168 % (hg.hex(mf[f]), f, cgi.escape(f)))
269 cn = cl.node(lr)
169 p2link = h2
270 cs = cl.read(cl.node(lr))
170 if i2 != -1:
271 p1, p2 = fl.parents(n)
171 p2link = '<a href="?cmd=chkin;nd=%s">%s</a>' % (h2, h2)
272 t = float(cs[2].split(' ')[0])
172
273
173 write(self.tmpl.do_page('checkin.tmpl', revnum=i, revnode=self.nodestr,
274 l.insert(0, self.t("filelogentry",
174 p1num=i1, p1node=h1, p2num=i2, p2node=h2, p2link=p2link,
275 filenode = hex(n),
175 mfnum=self.repo.manifest.rev(changes[0]),
276 filerev = i,
176 mfnode=hg.hex(changes[0]), author=obfuscate(changes[1]),
277 file = f,
177 desc=nl2br(cgi.escape(changes[4])), date=datestr,
278 node = hex(cn),
178 files=' '.join(files)))
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)
293 def filerevision(self, f, node):
181 change = self.repo.changelog.read(parents[0])
294 fl = self.repo.file(f)
182 mf2 = self.repo.manifest.read(change[0])
295 n = bin(node)
183 for f in c:
296 text = cgi.escape(fl.read(n))
184 self.show_diff(self.repo.file(f).read(mf2[f]), \
297 changerev = fl.linkrev(n)
185 self.repo.file(f).read(mf[f]), f)
298 cl = self.repo.changelog
186 for f in a:
299 cn = cl.node(changerev)
187 self.show_diff('', self.repo.file(f).read(mf[f]), f)
300 cs = cl.read(cn)
188 for f in d:
301 p1, p2 = fl.parents(n)
189 self.show_diff(self.repo.file(f).read(mf2[f]), '', f)
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):
320 def fileannotate(self, f, node):
192 def __init__(self, repo, tmpl_dir, fn, node=None, cs=None):
321 bcache = {}
193 page.__init__(self, tmpl_dir)
322 ncache = {}
194 self.repo = repo
323 fl = self.repo.file(f)
195 self.fn = fn
324 n = bin(node)
196 if cs:
325 changerev = fl.linkrev(n)
197 chng = self.repo.changelog.read(hg.bin(cs))
326
198 mf = self.repo.manifest.read(chng[0])
327 cl = self.repo.changelog
199 self.node = mf[self.fn]
328 cn = cl.node(changerev)
200 self.nodestr = hg.hex(self.node)
329 cs = cl.read(cn)
201 else:
330 p1, p2 = fl.parents(n)
202 self.nodestr = node
331 t = float(cs[2].split(' ')[0])
203 self.node = hg.bin(node)
332 mfn = cs[0]
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)
209
333
210 def content(self):
334 def annotate():
211 print '<pre>'
335 for r, l in fl.annotate(n):
212 print cgi.escape(self.repo.file(self.fn).read(self.node))
336 try:
213 print '</pre>'
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):
351 yield self.t("annotateline",
216 def __init__(self, repo, tmpl_dir, fn, node):
352 node = hex(cnode),
217 page.__init__(self, tmpl_dir)
353 rev = r,
218 self.repo = repo
354 author = name,
219 self.fn = fn
355 file = f,
220 self.nodestr = node
356 line = cgi.escape(l))
221 self.node = hg.bin(node)
357
222 print '<div class="annotation">Annotated: %s (%s)</div>' % \
358 yield self.t("fileannotate",
223 (cgi.escape(self.fn), self.nodestr, )
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):
373 def manifest(self, mnode, path):
226 print '<pre>'
374 mf = self.repo.manifest.read(bin(mnode))
227 for n, l in self.repo.file(self.fn).annotate(self.node):
375 rev = self.repo.manifest.rev(bin(mnode))
228 cnode = self.repo.changelog.lookup(n)
376 node = self.repo.changelog.node(rev)
229 write(self.tmpl.do_page('annline.tmpl', cnode=hg.hex(cnode),
377
230 cnum='% 6s' % n, fn=self.fn, line=cgi.escape(l[:-1])))
378 dirs = {}
231 print '</pre>'
379 files = {}
380 short = {}
232
381
233 class mfpage(page):
382 p = path[1:]
234 def __init__(self, repo, tmpl_dir, node):
383 l = len(p)
235 page.__init__(self, tmpl_dir)
236 self.repo = repo
237 self.nodestr = node
238 self.node = hg.bin(node)
239
384
240 def content(self):
385 for f,n in mf.items():
241 mf = self.repo.manifest.read(self.node)
386 if f[:l] != p:
242 fns = mf.keys()
387 continue
243 fns.sort()
388 remain = f[l:]
244 write(self.tmpl.do_page('mftitle.tmpl', node = self.nodestr))
389 if "/" in remain:
245 for f in fns:
390 short = remain[:remain.find("/") + 1] # bleah
246 write(self.tmpl.do_page('mfentry.tmpl', fn=f, node=hg.hex(mf[f])))
391 dirs[short] = 1
392 else:
393 short = os.path.basename(remain)
394 files[short] = (f, n)
247
395
248 class histpage(page):
396 def dirlist():
249 def __init__(self, repo, tmpl_dir, fn):
397 dl = dirs.keys()
250 page.__init__(self, tmpl_dir)
398 dl.sort()
251 self.repo = repo
399
252 self.fn = fn
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):
405 def filelist():
255 print '<div class="filehist">File History: %s</div>' % self.fn
406 fl = files.keys()
256 r = self.repo.file(self.fn)
407 fl.sort()
257 print '<br />'
408 for f in fl:
258 print '<table summary="" width="100%" align="center">'
409 full, fnode = files[f]
259 for i in xrange(r.count()-1, -1, -1):
410 yield self.t("manifestfileentry",
260 print '<tr><td>'
411 file = full, manifest = mnode, filenode = hex(fnode),
261 self.hist_ent(i, r)
412 basename = f)
262 print '</tr></td>'
413
263 print '</table>'
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):
423 def filediff(self, file, changeset):
266 n = r.node(i)
424 n = bin(changeset)
267 (p1, p2) = r.parents(n)
425 cl = self.repo.changelog
268 (h, h1, h2) = map(hg.hex, (n, p1, p2))
426 p1 = cl.parents(n)[0]
269 (i1, i2) = map(r.rev, (p1, p2))
427 cs = cl.read(n)
270 ci = r.linkrev(n)
428 mf = self.repo.manifest.read(cs[0])
271 cn = self.repo.changelog.node(ci)
429
272 cs = hg.hex(cn)
430 def diff():
273 changes = self.repo.changelog.read(cn)
431 yield self.diff(p1, n, file)
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"
288
432
289 def __init__(self):
433 yield self.t("filediff",
290 pass
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])
298
458
299 if not args.has_key('cmd') or args['cmd'][0] == 'changes':
459 write(self.changelog(hi))
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()
305
460
306 elif args['cmd'][0] == 'chkin':
461 elif args['cmd'][0] == 'changeset':
307 if not args.has_key('nd'):
462 write(self.changeset(args['node'][0]))
308 page = errpage(self.tmpl_dir)
463
309 print '<div class="errmsg">No Node!</div>'
464 elif args['cmd'][0] == 'manifest':
310 else:
465 write(self.manifest(args['manifest'][0], args['path'][0]))
311 page = checkin(repo, self.tmpl_dir, args['nd'][0])
466
312 page.content()
467 elif args['cmd'][0] == 'filediff':
313 page.endpage()
468 write(self.filediff(args['file'][0], args['node'][0]))
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