Mercurial > public > mercurial-scm > hg
comparison mercurial/hgweb.py @ 138:c77a679e9cfa
Revamped templated hgweb
author | mpm@selenic.com |
---|---|
date | Mon, 23 May 2005 16:00:02 -0800 |
parents | b45b1b00fc9e |
children | 529bf610092e |
comparison
equal
deleted
inserted
replaced
137:b45b1b00fc9e | 138:c77a679e9cfa |
---|---|
9 # useful for debugging | 9 # useful for debugging |
10 import cgitb | 10 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 |
27 | 56 |
28 def write(*things): | 57 def write(*things): |
31 for part in thing: | 60 for part in thing: |
32 write(part) | 61 write(part) |
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):] | 93 |
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 | |
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]) | |
273 | |
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) | |
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 | |
51 else: | 392 else: |
52 yield txt | 393 short = os.path.basename(remain) |
53 txt = '' | 394 files[short] = (f, n) |
54 | 395 |
55 class page: | 396 def dirlist(): |
56 def __init__(self, tmpl_dir = "", type="text/html", title="Mercurial Web", | 397 dl = dirs.keys() |
57 charset="ISO-8859-1"): | 398 dl.sort() |
58 self.tmpl = template(tmpl_dir) | 399 |
59 | 400 for d in dl: |
60 print 'Content-type: %s; charset=%s\n' % (type, charset) | 401 yield self.t("manifestdirentry", |
61 write(self.tmpl.do_page('htmlstart.tmpl', title = title)) | 402 path = os.path.join(path, d), |
62 | 403 manifest = mnode, basename = d[:-1]) |
63 def endpage(self): | 404 |
64 print '</BODY>' | 405 def filelist(): |
65 print '</HTML>' | 406 fl = files.keys() |
66 | 407 fl.sort() |
67 def show_diff(self, a, b, fn): | 408 for f in fl: |
68 a = a.splitlines(1) | 409 full, fnode = files[f] |
69 b = b.splitlines(1) | 410 yield self.t("manifestfileentry", |
70 l = difflib.unified_diff(a, b, fn, fn) | 411 file = full, manifest = mnode, filenode = hex(fnode), |
71 print '<pre>' | 412 basename = f) |
72 for line in l: | 413 |
73 line = cgi.escape(line[:-1]) | 414 yield self.t("manifest", |
74 if line.startswith('+'): | 415 manifest = mnode, |
75 print '<span class="plusline">%s</span>' % (line, ) | 416 rev = rev, |
76 elif line.startswith('-'): | 417 node = hex(node), |
77 print '<span class="minusline">%s</span>' % (line, ) | 418 path = path, |
78 elif line.startswith('@'): | 419 up = up(path), |
79 print '<span class="atline">%s</span>' % (line, ) | 420 dirs = dirlist, |
80 else: | 421 files = filelist) |
81 print line | 422 |
82 print '</pre>' | 423 def filediff(self, file, changeset): |
83 | 424 n = bin(changeset) |
84 class errpage(page): | 425 cl = self.repo.changelog |
85 def __init__(self, tmpl_dir): | 426 p1 = cl.parents(n)[0] |
86 page.__init__(self, tmpl_dir, title="Mercurial Web Error Page") | 427 cs = cl.read(n) |
87 | 428 mf = self.repo.manifest.read(cs[0]) |
88 class change_list(page): | 429 |
89 def __init__(self, repo, tmpl_dir, reponame, numchanges = 50): | 430 def diff(): |
90 page.__init__(self, tmpl_dir) | 431 yield self.diff(p1, n, file) |
91 self.repo = repo | 432 |
92 self.numchanges = numchanges | 433 yield self.t("filediff", |
93 write(self.tmpl.do_page('changestitle.tmpl', reponame=reponame)) | 434 file = file, |
94 | 435 filenode = hex(mf[file]), |
95 def content(self, hi=None): | 436 node = changeset, |
96 cl = [] | 437 rev = self.repo.changelog.rev(n), |
97 count = self.repo.changelog.count() | 438 p1 = hex(p1), |
98 if not hi: | 439 p1rev = self.repo.changelog.rev(p1), |
99 hi = count | 440 diff = diff) |
100 elif hi < self.numchanges: | 441 |
101 hi = self.numchanges | 442 # header and footer, css |
102 | 443 # add tags to things |
103 start = 0 | 444 # show parents |
104 if hi - self.numchanges >= 0: | 445 # diff between rev and parent in changeset and file |
105 start = hi - self.numchanges | 446 # manifest links |
106 | 447 # browse at top |
107 nav = "Displaying Revisions: %d-%d" % (start, hi-1) | 448 # tags -> list of changesets corresponding to tags |
108 if start != 0: | 449 # find tag, changeset, file |
109 nav = ('<a href="?cmd=changes;hi=%d">Previous %d</a> ' \ | |
110 % (start, self.numchanges)) + nav | |
111 if hi != count: | |
112 if hi + self.numchanges <= count: | |
113 nav += ' <a href="?cmd=changes;hi=%d">Next %d</a>' \ | |
114 % (hi + self.numchanges, self.numchanges) | |
115 else: | |
116 nav += ' <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> ' \ | |
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 | |
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> ' \ | |
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: | |
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) | |
209 | |
210 def content(self): | |
211 print '<pre>' | |
212 print cgi.escape(self.repo.file(self.fn).read(self.node)) | |
213 print '</pre>' | |
214 | |
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, ) | |
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 = ' %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 | |
289 def __init__(self): | |
290 pass | |
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() |
298 | 456 if args.has_key('pos'): |
299 if not args.has_key('cmd') or args['cmd'][0] == 'changes': | 457 hi = int(args['pos'][0]) |
300 page = change_list(repo, self.tmpl_dir, 'Mercurial', | 458 |
301 self.numchanges) | 459 write(self.changelog(hi)) |
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')): | 472 |
318 page = errpage(self.tmpl_dir) | 473 elif args['cmd'][0] == 'annotate': |
319 print '<div class="errmsg">Invalid Args!</div>' | 474 write(self.fileannotate(args['file'][0], args['filenode'][0])) |
320 else: | 475 |
321 if args.has_key('nd'): | 476 elif args['cmd'][0] == 'filelog': |
322 page = filepage(repo, self.tmpl_dir, | 477 write(self.filelog(args['file'][0], args['filenode'][0])) |
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 | |
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() | |
338 | |
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() | |
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() |