##// END OF EJS Templates
hgweb: Sort tags by revision number
mpm@selenic.com -
r183:76791667 default
parent child Browse files
Show More
@@ -1,686 +1,689 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # hgweb.py - 0.2 - 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # hgweb.py - 0.2 - 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # - web interface to a mercurial repository
4 # - web interface to a mercurial repository
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
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.hg import *
14 from mercurial.hg import *
15
15
16 def templatepath():
16 def templatepath():
17 for f in "templates/map", "../templates/map":
17 for f in "templates/map", "../templates/map":
18 p = os.path.join(os.path.dirname(__file__), f)
18 p = os.path.join(os.path.dirname(__file__), f)
19 if os.path.isfile(p): return p
19 if os.path.isfile(p): return p
20
20
21 def age(t):
21 def age(t):
22 def plural(t, c):
22 def plural(t, c):
23 if c == 1: return t
23 if c == 1: return t
24 return t + "s"
24 return t + "s"
25 def fmt(t, c):
25 def fmt(t, c):
26 return "%d %s" % (c, plural(t, c))
26 return "%d %s" % (c, plural(t, c))
27
27
28 now = time.time()
28 now = time.time()
29 delta = max(1, int(now - t))
29 delta = max(1, int(now - t))
30
30
31 scales = [["second", 1],
31 scales = [["second", 1],
32 ["minute", 60],
32 ["minute", 60],
33 ["hour", 3600],
33 ["hour", 3600],
34 ["day", 3600 * 24],
34 ["day", 3600 * 24],
35 ["week", 3600 * 24 * 7],
35 ["week", 3600 * 24 * 7],
36 ["month", 3600 * 24 * 30],
36 ["month", 3600 * 24 * 30],
37 ["year", 3600 * 24 * 365]]
37 ["year", 3600 * 24 * 365]]
38
38
39 scales.reverse()
39 scales.reverse()
40
40
41 for t, s in scales:
41 for t, s in scales:
42 n = delta / s
42 n = delta / s
43 if n >= 1: return fmt(t, n)
43 if n >= 1: return fmt(t, n)
44
44
45 def nl2br(text):
45 def nl2br(text):
46 return text.replace('\n', '<br/>')
46 return text.replace('\n', '<br/>')
47
47
48 def obfuscate(text):
48 def obfuscate(text):
49 return ''.join([ '&#%d' % ord(c) for c in text ])
49 return ''.join([ '&#%d' % ord(c) for c in text ])
50
50
51 def up(p):
51 def up(p):
52 if p[0] != "/": p = "/" + p
52 if p[0] != "/": p = "/" + p
53 if p[-1] == "/": p = p[:-1]
53 if p[-1] == "/": p = p[:-1]
54 up = os.path.dirname(p)
54 up = os.path.dirname(p)
55 if up == "/":
55 if up == "/":
56 return "/"
56 return "/"
57 return up + "/"
57 return up + "/"
58
58
59 def httphdr(type):
59 def httphdr(type):
60 print 'Content-type: %s\n' % type
60 print 'Content-type: %s\n' % type
61
61
62 def write(*things):
62 def write(*things):
63 for thing in things:
63 for thing in things:
64 if hasattr(thing, "__iter__"):
64 if hasattr(thing, "__iter__"):
65 for part in thing:
65 for part in thing:
66 write(part)
66 write(part)
67 else:
67 else:
68 sys.stdout.write(str(thing))
68 sys.stdout.write(str(thing))
69
69
70 def template(tmpl, **map):
70 def template(tmpl, **map):
71 while tmpl:
71 while tmpl:
72 m = re.search(r"#([a-zA-Z0-9]+)#", tmpl)
72 m = re.search(r"#([a-zA-Z0-9]+)#", tmpl)
73 if m:
73 if m:
74 yield tmpl[:m.start(0)]
74 yield tmpl[:m.start(0)]
75 v = map.get(m.group(1), "")
75 v = map.get(m.group(1), "")
76 yield callable(v) and v() or v
76 yield callable(v) and v() or v
77 tmpl = tmpl[m.end(0):]
77 tmpl = tmpl[m.end(0):]
78 else:
78 else:
79 yield tmpl
79 yield tmpl
80 return
80 return
81
81
82 class templater:
82 class templater:
83 def __init__(self, mapfile):
83 def __init__(self, mapfile):
84 self.cache = {}
84 self.cache = {}
85 self.map = {}
85 self.map = {}
86 self.base = os.path.dirname(mapfile)
86 self.base = os.path.dirname(mapfile)
87
87
88 for l in file(mapfile):
88 for l in file(mapfile):
89 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
89 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
90 if m:
90 if m:
91 self.cache[m.group(1)] = m.group(2)
91 self.cache[m.group(1)] = m.group(2)
92 else:
92 else:
93 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
93 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
94 if m:
94 if m:
95 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
95 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
96 else:
96 else:
97 raise "unknown map entry '%s'" % l
97 raise "unknown map entry '%s'" % l
98
98
99 def __call__(self, t, **map):
99 def __call__(self, t, **map):
100 try:
100 try:
101 tmpl = self.cache[t]
101 tmpl = self.cache[t]
102 except KeyError:
102 except KeyError:
103 tmpl = self.cache[t] = file(self.map[t]).read()
103 tmpl = self.cache[t] = file(self.map[t]).read()
104 return template(tmpl, **map)
104 return template(tmpl, **map)
105
105
106 class hgweb:
106 class hgweb:
107 maxchanges = 20
107 maxchanges = 20
108 maxfiles = 10
108 maxfiles = 10
109
109
110 def __init__(self, path, name, templatemap = ""):
110 def __init__(self, path, name, templatemap = ""):
111 templatemap = templatemap or templatepath()
111 templatemap = templatemap or templatepath()
112
112
113 self.reponame = name
113 self.reponame = name
114 self.repo = repository(ui(), path)
114 self.repo = repository(ui(), path)
115 self.t = templater(templatemap)
115 self.t = templater(templatemap)
116
116
117 def date(self, cs):
117 def date(self, cs):
118 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
118 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
119
119
120 def listfiles(self, files, mf):
120 def listfiles(self, files, mf):
121 for f in files[:self.maxfiles]:
121 for f in files[:self.maxfiles]:
122 yield self.t("filenodelink", node = hex(mf[f]), file = f)
122 yield self.t("filenodelink", node = hex(mf[f]), file = f)
123 if len(files) > self.maxfiles:
123 if len(files) > self.maxfiles:
124 yield self.t("fileellipses")
124 yield self.t("fileellipses")
125
125
126 def listfilediffs(self, files, changeset):
126 def listfilediffs(self, files, changeset):
127 for f in files[:self.maxfiles]:
127 for f in files[:self.maxfiles]:
128 yield self.t("filedifflink", node = hex(changeset), file = f)
128 yield self.t("filedifflink", node = hex(changeset), file = f)
129 if len(files) > self.maxfiles:
129 if len(files) > self.maxfiles:
130 yield self.t("fileellipses")
130 yield self.t("fileellipses")
131
131
132 def parent(self, t1, node=nullid, rev=-1, **args):
132 def parent(self, t1, node=nullid, rev=-1, **args):
133 if node != hex(nullid):
133 if node != hex(nullid):
134 yield self.t(t1, node = node, rev = rev, **args)
134 yield self.t(t1, node = node, rev = rev, **args)
135
135
136 def diff(self, node1, node2, files):
136 def diff(self, node1, node2, files):
137 def filterfiles(list, files):
137 def filterfiles(list, files):
138 l = [ x for x in list if x in files ]
138 l = [ x for x in list if x in files ]
139
139
140 for f in files:
140 for f in files:
141 if f[-1] != os.sep: f += os.sep
141 if f[-1] != os.sep: f += os.sep
142 l += [ x for x in list if x.startswith(f) ]
142 l += [ x for x in list if x.startswith(f) ]
143 return l
143 return l
144
144
145 parity = [0]
145 parity = [0]
146 def diffblock(diff, f, fn):
146 def diffblock(diff, f, fn):
147 yield self.t("diffblock",
147 yield self.t("diffblock",
148 lines = prettyprintlines(diff),
148 lines = prettyprintlines(diff),
149 parity = parity[0],
149 parity = parity[0],
150 file = f,
150 file = f,
151 filenode = hex(fn))
151 filenode = hex(fn))
152 parity[0] = 1 - parity[0]
152 parity[0] = 1 - parity[0]
153
153
154 def prettyprintlines(diff):
154 def prettyprintlines(diff):
155 for l in diff.splitlines(1):
155 for l in diff.splitlines(1):
156 line = cgi.escape(l)
156 line = cgi.escape(l)
157 if line.startswith('+'):
157 if line.startswith('+'):
158 yield self.t("difflineplus", line = line)
158 yield self.t("difflineplus", line = line)
159 elif line.startswith('-'):
159 elif line.startswith('-'):
160 yield self.t("difflineminus", line = line)
160 yield self.t("difflineminus", line = line)
161 elif line.startswith('@'):
161 elif line.startswith('@'):
162 yield self.t("difflineat", line = line)
162 yield self.t("difflineat", line = line)
163 else:
163 else:
164 yield self.t("diffline", line = line)
164 yield self.t("diffline", line = line)
165
165
166 r = self.repo
166 r = self.repo
167 cl = r.changelog
167 cl = r.changelog
168 mf = r.manifest
168 mf = r.manifest
169 change1 = cl.read(node1)
169 change1 = cl.read(node1)
170 change2 = cl.read(node2)
170 change2 = cl.read(node2)
171 mmap1 = mf.read(change1[0])
171 mmap1 = mf.read(change1[0])
172 mmap2 = mf.read(change2[0])
172 mmap2 = mf.read(change2[0])
173 date1 = self.date(change1)
173 date1 = self.date(change1)
174 date2 = self.date(change2)
174 date2 = self.date(change2)
175
175
176 c, a, d = r.diffrevs(node1, node2)
176 c, a, d = r.diffrevs(node1, node2)
177 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
177 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
178
178
179 for f in c:
179 for f in c:
180 to = r.file(f).read(mmap1[f])
180 to = r.file(f).read(mmap1[f])
181 tn = r.file(f).read(mmap2[f])
181 tn = r.file(f).read(mmap2[f])
182 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
182 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
183 for f in a:
183 for f in a:
184 to = ""
184 to = ""
185 tn = r.file(f).read(mmap2[f])
185 tn = r.file(f).read(mmap2[f])
186 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
186 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
187 for f in d:
187 for f in d:
188 to = r.file(f).read(mmap1[f])
188 to = r.file(f).read(mmap1[f])
189 tn = ""
189 tn = ""
190 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
190 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
191
191
192 def header(self):
192 def header(self):
193 yield self.t("header", repo = self.reponame)
193 yield self.t("header", repo = self.reponame)
194
194
195 def footer(self):
195 def footer(self):
196 yield self.t("footer", repo = self.reponame)
196 yield self.t("footer", repo = self.reponame)
197
197
198 def changelog(self, pos):
198 def changelog(self, pos):
199 def changenav():
199 def changenav():
200 def seq(factor = 1):
200 def seq(factor = 1):
201 yield 1 * factor
201 yield 1 * factor
202 yield 3 * factor
202 yield 3 * factor
203 #yield 5 * factor
203 #yield 5 * factor
204 for f in seq(factor * 10):
204 for f in seq(factor * 10):
205 yield f
205 yield f
206
206
207 l = []
207 l = []
208 for f in seq():
208 for f in seq():
209 if f < self.maxchanges / 2: continue
209 if f < self.maxchanges / 2: continue
210 if f > count: break
210 if f > count: break
211 r = "%d" % f
211 r = "%d" % f
212 if pos + f < count - (f/2): l.append(("+" + r, pos + f))
212 if pos + f < count - (f/2): l.append(("+" + r, pos + f))
213 if pos - f >= 0 + (f/2): l.insert(0, ("-" + r, pos - f))
213 if pos - f >= 0 + (f/2): l.insert(0, ("-" + r, pos - f))
214
214
215 yield self.t("naventry", rev = 0, label="(0)")
215 yield self.t("naventry", rev = 0, label="(0)")
216
216
217 for label, rev in l:
217 for label, rev in l:
218 yield self.t("naventry", label = label, rev = rev)
218 yield self.t("naventry", label = label, rev = rev)
219
219
220 yield self.t("naventry", rev = count - 1, label="tip")
220 yield self.t("naventry", rev = count - 1, label="tip")
221
221
222 def changelist():
222 def changelist():
223 parity = (start - end) & 1
223 parity = (start - end) & 1
224 cl = self.repo.changelog
224 cl = self.repo.changelog
225 l = [] # build a list in forward order for efficiency
225 l = [] # build a list in forward order for efficiency
226 for i in range(start, end + 1):
226 for i in range(start, end + 1):
227 n = cl.node(i)
227 n = cl.node(i)
228 changes = cl.read(n)
228 changes = cl.read(n)
229 hn = hex(n)
229 hn = hex(n)
230 p1, p2 = cl.parents(n)
230 p1, p2 = cl.parents(n)
231 t = float(changes[2].split(' ')[0])
231 t = float(changes[2].split(' ')[0])
232
232
233 l.insert(0, self.t(
233 l.insert(0, self.t(
234 'changelogentry',
234 'changelogentry',
235 parity = parity,
235 parity = parity,
236 author = obfuscate(changes[1]),
236 author = obfuscate(changes[1]),
237 shortdesc = cgi.escape(changes[4].splitlines()[0]),
237 shortdesc = cgi.escape(changes[4].splitlines()[0]),
238 age = age(t),
238 age = age(t),
239 parent1 = self.parent("changelogparent",
239 parent1 = self.parent("changelogparent",
240 hex(p1), cl.rev(p1)),
240 hex(p1), cl.rev(p1)),
241 parent2 = self.parent("changelogparent",
241 parent2 = self.parent("changelogparent",
242 hex(p2), cl.rev(p2)),
242 hex(p2), cl.rev(p2)),
243 p1 = hex(p1), p2 = hex(p2),
243 p1 = hex(p1), p2 = hex(p2),
244 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
244 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
245 manifest = hex(changes[0]),
245 manifest = hex(changes[0]),
246 desc = nl2br(cgi.escape(changes[4])),
246 desc = nl2br(cgi.escape(changes[4])),
247 date = time.asctime(time.gmtime(t)),
247 date = time.asctime(time.gmtime(t)),
248 files = self.listfilediffs(changes[3], n),
248 files = self.listfilediffs(changes[3], n),
249 rev = i,
249 rev = i,
250 node = hn))
250 node = hn))
251 parity = 1 - parity
251 parity = 1 - parity
252
252
253 yield l
253 yield l
254
254
255 cl = self.repo.changelog
255 cl = self.repo.changelog
256 mf = cl.read(cl.tip())[0]
256 mf = cl.read(cl.tip())[0]
257 count = cl.count()
257 count = cl.count()
258 end = min(pos, count - 1)
258 end = min(pos, count - 1)
259 start = max(0, pos - self.maxchanges)
259 start = max(0, pos - self.maxchanges)
260 end = min(count - 1, start + self.maxchanges)
260 end = min(count - 1, start + self.maxchanges)
261
261
262 yield self.t('changelog',
262 yield self.t('changelog',
263 header = self.header(),
263 header = self.header(),
264 footer = self.footer(),
264 footer = self.footer(),
265 repo = self.reponame,
265 repo = self.reponame,
266 changenav = changenav,
266 changenav = changenav,
267 manifest = hex(mf),
267 manifest = hex(mf),
268 rev = pos, changesets = count, entries = changelist)
268 rev = pos, changesets = count, entries = changelist)
269
269
270 def changeset(self, nodeid):
270 def changeset(self, nodeid):
271 n = bin(nodeid)
271 n = bin(nodeid)
272 cl = self.repo.changelog
272 cl = self.repo.changelog
273 changes = cl.read(n)
273 changes = cl.read(n)
274 p1, p2 = cl.parents(n)
274 p1, p2 = cl.parents(n)
275 p1rev, p2rev = cl.rev(p1), cl.rev(p2)
275 p1rev, p2rev = cl.rev(p1), cl.rev(p2)
276 t = float(changes[2].split(' ')[0])
276 t = float(changes[2].split(' ')[0])
277
277
278 files = []
278 files = []
279 mf = self.repo.manifest.read(changes[0])
279 mf = self.repo.manifest.read(changes[0])
280 for f in changes[3]:
280 for f in changes[3]:
281 files.append(self.t("filenodelink",
281 files.append(self.t("filenodelink",
282 filenode = hex(mf[f]), file = f))
282 filenode = hex(mf[f]), file = f))
283
283
284 def diff():
284 def diff():
285 yield self.diff(p1, n, changes[3])
285 yield self.diff(p1, n, changes[3])
286
286
287 yield self.t('changeset',
287 yield self.t('changeset',
288 header = self.header(),
288 header = self.header(),
289 footer = self.footer(),
289 footer = self.footer(),
290 repo = self.reponame,
290 repo = self.reponame,
291 diff = diff,
291 diff = diff,
292 rev = cl.rev(n),
292 rev = cl.rev(n),
293 node = nodeid,
293 node = nodeid,
294 shortdesc = cgi.escape(changes[4].splitlines()[0]),
294 shortdesc = cgi.escape(changes[4].splitlines()[0]),
295 parent1 = self.parent("changesetparent",
295 parent1 = self.parent("changesetparent",
296 hex(p1), cl.rev(p1)),
296 hex(p1), cl.rev(p1)),
297 parent2 = self.parent("changesetparent",
297 parent2 = self.parent("changesetparent",
298 hex(p2), cl.rev(p2)),
298 hex(p2), cl.rev(p2)),
299 p1 = hex(p1), p2 = hex(p2),
299 p1 = hex(p1), p2 = hex(p2),
300 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
300 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
301 manifest = hex(changes[0]),
301 manifest = hex(changes[0]),
302 author = obfuscate(changes[1]),
302 author = obfuscate(changes[1]),
303 desc = nl2br(cgi.escape(changes[4])),
303 desc = nl2br(cgi.escape(changes[4])),
304 date = time.asctime(time.gmtime(t)),
304 date = time.asctime(time.gmtime(t)),
305 files = files)
305 files = files)
306
306
307 def filelog(self, f, filenode):
307 def filelog(self, f, filenode):
308 cl = self.repo.changelog
308 cl = self.repo.changelog
309 fl = self.repo.file(f)
309 fl = self.repo.file(f)
310 count = fl.count()
310 count = fl.count()
311
311
312 def entries():
312 def entries():
313 l = []
313 l = []
314 parity = (count - 1) & 1
314 parity = (count - 1) & 1
315
315
316 for i in range(count):
316 for i in range(count):
317
317
318 n = fl.node(i)
318 n = fl.node(i)
319 lr = fl.linkrev(n)
319 lr = fl.linkrev(n)
320 cn = cl.node(lr)
320 cn = cl.node(lr)
321 cs = cl.read(cl.node(lr))
321 cs = cl.read(cl.node(lr))
322 p1, p2 = fl.parents(n)
322 p1, p2 = fl.parents(n)
323 t = float(cs[2].split(' ')[0])
323 t = float(cs[2].split(' ')[0])
324
324
325 l.insert(0, self.t("filelogentry",
325 l.insert(0, self.t("filelogentry",
326 parity = parity,
326 parity = parity,
327 filenode = hex(n),
327 filenode = hex(n),
328 filerev = i,
328 filerev = i,
329 file = f,
329 file = f,
330 node = hex(cn),
330 node = hex(cn),
331 author = obfuscate(cs[1]),
331 author = obfuscate(cs[1]),
332 age = age(t),
332 age = age(t),
333 date = time.asctime(time.gmtime(t)),
333 date = time.asctime(time.gmtime(t)),
334 shortdesc = cgi.escape(cs[4].splitlines()[0]),
334 shortdesc = cgi.escape(cs[4].splitlines()[0]),
335 p1 = hex(p1), p2 = hex(p2),
335 p1 = hex(p1), p2 = hex(p2),
336 p1rev = fl.rev(p1), p2rev = fl.rev(p2)))
336 p1rev = fl.rev(p1), p2rev = fl.rev(p2)))
337 parity = 1 - parity
337 parity = 1 - parity
338
338
339 yield l
339 yield l
340
340
341 yield self.t("filelog",
341 yield self.t("filelog",
342 header = self.header(),
342 header = self.header(),
343 footer = self.footer(),
343 footer = self.footer(),
344 repo = self.reponame,
344 repo = self.reponame,
345 file = f,
345 file = f,
346 filenode = filenode,
346 filenode = filenode,
347 entries = entries)
347 entries = entries)
348
348
349 def filerevision(self, f, node):
349 def filerevision(self, f, node):
350 fl = self.repo.file(f)
350 fl = self.repo.file(f)
351 n = bin(node)
351 n = bin(node)
352 text = cgi.escape(fl.read(n))
352 text = cgi.escape(fl.read(n))
353 changerev = fl.linkrev(n)
353 changerev = fl.linkrev(n)
354 cl = self.repo.changelog
354 cl = self.repo.changelog
355 cn = cl.node(changerev)
355 cn = cl.node(changerev)
356 cs = cl.read(cn)
356 cs = cl.read(cn)
357 p1, p2 = fl.parents(n)
357 p1, p2 = fl.parents(n)
358 t = float(cs[2].split(' ')[0])
358 t = float(cs[2].split(' ')[0])
359 mfn = cs[0]
359 mfn = cs[0]
360
360
361 def lines():
361 def lines():
362 for l, t in enumerate(text.splitlines(1)):
362 for l, t in enumerate(text.splitlines(1)):
363 yield self.t("fileline",
363 yield self.t("fileline",
364 line = t,
364 line = t,
365 linenumber = "% 6d" % (l + 1),
365 linenumber = "% 6d" % (l + 1),
366 parity = l & 1)
366 parity = l & 1)
367
367
368 yield self.t("filerevision", file = f,
368 yield self.t("filerevision", file = f,
369 header = self.header(),
369 header = self.header(),
370 footer = self.footer(),
370 footer = self.footer(),
371 repo = self.reponame,
371 repo = self.reponame,
372 filenode = node,
372 filenode = node,
373 path = up(f),
373 path = up(f),
374 text = lines(),
374 text = lines(),
375 rev = changerev,
375 rev = changerev,
376 node = hex(cn),
376 node = hex(cn),
377 manifest = hex(mfn),
377 manifest = hex(mfn),
378 author = obfuscate(cs[1]),
378 author = obfuscate(cs[1]),
379 age = age(t),
379 age = age(t),
380 date = time.asctime(time.gmtime(t)),
380 date = time.asctime(time.gmtime(t)),
381 shortdesc = cgi.escape(cs[4].splitlines()[0]),
381 shortdesc = cgi.escape(cs[4].splitlines()[0]),
382 parent1 = self.parent("filerevparent",
382 parent1 = self.parent("filerevparent",
383 hex(p1), fl.rev(p1), file=f),
383 hex(p1), fl.rev(p1), file=f),
384 parent2 = self.parent("filerevparent",
384 parent2 = self.parent("filerevparent",
385 hex(p2), fl.rev(p2), file=f),
385 hex(p2), fl.rev(p2), file=f),
386 p1 = hex(p1), p2 = hex(p2),
386 p1 = hex(p1), p2 = hex(p2),
387 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
387 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
388
388
389
389
390 def fileannotate(self, f, node):
390 def fileannotate(self, f, node):
391 bcache = {}
391 bcache = {}
392 ncache = {}
392 ncache = {}
393 fl = self.repo.file(f)
393 fl = self.repo.file(f)
394 n = bin(node)
394 n = bin(node)
395 changerev = fl.linkrev(n)
395 changerev = fl.linkrev(n)
396
396
397 cl = self.repo.changelog
397 cl = self.repo.changelog
398 cn = cl.node(changerev)
398 cn = cl.node(changerev)
399 cs = cl.read(cn)
399 cs = cl.read(cn)
400 p1, p2 = fl.parents(n)
400 p1, p2 = fl.parents(n)
401 t = float(cs[2].split(' ')[0])
401 t = float(cs[2].split(' ')[0])
402 mfn = cs[0]
402 mfn = cs[0]
403
403
404 def annotate():
404 def annotate():
405 parity = 1
405 parity = 1
406 last = None
406 last = None
407 for r, l in fl.annotate(n):
407 for r, l in fl.annotate(n):
408 try:
408 try:
409 cnode = ncache[r]
409 cnode = ncache[r]
410 except KeyError:
410 except KeyError:
411 cnode = ncache[r] = self.repo.changelog.node(r)
411 cnode = ncache[r] = self.repo.changelog.node(r)
412
412
413 try:
413 try:
414 name = bcache[r]
414 name = bcache[r]
415 except KeyError:
415 except KeyError:
416 cl = self.repo.changelog.read(cnode)
416 cl = self.repo.changelog.read(cnode)
417 name = cl[1]
417 name = cl[1]
418 f = name.find('@')
418 f = name.find('@')
419 if f >= 0:
419 if f >= 0:
420 name = name[:f]
420 name = name[:f]
421 bcache[r] = name
421 bcache[r] = name
422
422
423 if last != cnode:
423 if last != cnode:
424 parity = 1 - parity
424 parity = 1 - parity
425 last = cnode
425 last = cnode
426
426
427 yield self.t("annotateline",
427 yield self.t("annotateline",
428 parity = parity,
428 parity = parity,
429 node = hex(cnode),
429 node = hex(cnode),
430 rev = r,
430 rev = r,
431 author = name,
431 author = name,
432 file = f,
432 file = f,
433 line = cgi.escape(l))
433 line = cgi.escape(l))
434
434
435 yield self.t("fileannotate",
435 yield self.t("fileannotate",
436 header = self.header(),
436 header = self.header(),
437 footer = self.footer(),
437 footer = self.footer(),
438 repo = self.reponame,
438 repo = self.reponame,
439 file = f,
439 file = f,
440 filenode = node,
440 filenode = node,
441 annotate = annotate,
441 annotate = annotate,
442 path = up(f),
442 path = up(f),
443 rev = changerev,
443 rev = changerev,
444 node = hex(cn),
444 node = hex(cn),
445 manifest = hex(mfn),
445 manifest = hex(mfn),
446 author = obfuscate(cs[1]),
446 author = obfuscate(cs[1]),
447 age = age(t),
447 age = age(t),
448 date = time.asctime(time.gmtime(t)),
448 date = time.asctime(time.gmtime(t)),
449 shortdesc = cgi.escape(cs[4].splitlines()[0]),
449 shortdesc = cgi.escape(cs[4].splitlines()[0]),
450 parent1 = self.parent("fileannotateparent",
450 parent1 = self.parent("fileannotateparent",
451 hex(p1), fl.rev(p1), file=f),
451 hex(p1), fl.rev(p1), file=f),
452 parent2 = self.parent("fileannotateparent",
452 parent2 = self.parent("fileannotateparent",
453 hex(p2), fl.rev(p2), file=f),
453 hex(p2), fl.rev(p2), file=f),
454 p1 = hex(p1), p2 = hex(p2),
454 p1 = hex(p1), p2 = hex(p2),
455 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
455 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
456
456
457 def manifest(self, mnode, path):
457 def manifest(self, mnode, path):
458 mf = self.repo.manifest.read(bin(mnode))
458 mf = self.repo.manifest.read(bin(mnode))
459 rev = self.repo.manifest.rev(bin(mnode))
459 rev = self.repo.manifest.rev(bin(mnode))
460 node = self.repo.changelog.node(rev)
460 node = self.repo.changelog.node(rev)
461
461
462 files = {}
462 files = {}
463
463
464 p = path[1:]
464 p = path[1:]
465 l = len(p)
465 l = len(p)
466
466
467 for f,n in mf.items():
467 for f,n in mf.items():
468 if f[:l] != p:
468 if f[:l] != p:
469 continue
469 continue
470 remain = f[l:]
470 remain = f[l:]
471 if "/" in remain:
471 if "/" in remain:
472 short = remain[:remain.find("/") + 1] # bleah
472 short = remain[:remain.find("/") + 1] # bleah
473 files[short] = (f, None)
473 files[short] = (f, None)
474 else:
474 else:
475 short = os.path.basename(remain)
475 short = os.path.basename(remain)
476 files[short] = (f, n)
476 files[short] = (f, n)
477
477
478 def filelist():
478 def filelist():
479 parity = 0
479 parity = 0
480 fl = files.keys()
480 fl = files.keys()
481 fl.sort()
481 fl.sort()
482 for f in fl:
482 for f in fl:
483 full, fnode = files[f]
483 full, fnode = files[f]
484 if fnode:
484 if fnode:
485 yield self.t("manifestfileentry",
485 yield self.t("manifestfileentry",
486 file = full,
486 file = full,
487 manifest = mnode,
487 manifest = mnode,
488 filenode = hex(fnode),
488 filenode = hex(fnode),
489 parity = parity,
489 parity = parity,
490 basename = f)
490 basename = f)
491 else:
491 else:
492 yield self.t("manifestdirentry",
492 yield self.t("manifestdirentry",
493 parity = parity,
493 parity = parity,
494 path = os.path.join(path, f),
494 path = os.path.join(path, f),
495 manifest = mnode, basename = f[:-1])
495 manifest = mnode, basename = f[:-1])
496 parity = 1 - parity
496 parity = 1 - parity
497
497
498 yield self.t("manifest",
498 yield self.t("manifest",
499 header = self.header(),
499 header = self.header(),
500 footer = self.footer(),
500 footer = self.footer(),
501 repo = self.reponame,
501 repo = self.reponame,
502 manifest = mnode,
502 manifest = mnode,
503 rev = rev,
503 rev = rev,
504 node = hex(node),
504 node = hex(node),
505 path = path,
505 path = path,
506 up = up(path),
506 up = up(path),
507 entries = filelist)
507 entries = filelist)
508
508
509 def tags(self):
509 def tags(self):
510 cl = self.repo.changelog
510 cl = self.repo.changelog
511 mf = cl.read(cl.tip())[0]
511 mf = cl.read(cl.tip())[0]
512
512
513 self.repo.lookup(0) # prime the cache
513 self.repo.lookup(0) # prime the cache
514 i = self.repo.tags.items()
514 i = self.repo.tags.items()
515 i.sort()
515 n = [ (cl.rev(e[1]), e) for e in i ] # sort by revision
516 n.sort()
517 n.reverse()
518 i = [ e[1] for e in n ]
516
519
517 def entries():
520 def entries():
518 parity = 0
521 parity = 0
519 for k,n in i:
522 for k,n in i:
520 yield self.t("tagentry",
523 yield self.t("tagentry",
521 parity = parity,
524 parity = parity,
522 tag = k,
525 tag = k,
523 node = hex(n))
526 node = hex(n))
524 parity = 1 - parity
527 parity = 1 - parity
525
528
526 yield self.t("tags",
529 yield self.t("tags",
527 header = self.header(),
530 header = self.header(),
528 footer = self.footer(),
531 footer = self.footer(),
529 repo = self.reponame,
532 repo = self.reponame,
530 manifest = hex(mf),
533 manifest = hex(mf),
531 entries = entries)
534 entries = entries)
532
535
533 def filediff(self, file, changeset):
536 def filediff(self, file, changeset):
534 n = bin(changeset)
537 n = bin(changeset)
535 cl = self.repo.changelog
538 cl = self.repo.changelog
536 p1 = cl.parents(n)[0]
539 p1 = cl.parents(n)[0]
537 cs = cl.read(n)
540 cs = cl.read(n)
538 mf = self.repo.manifest.read(cs[0])
541 mf = self.repo.manifest.read(cs[0])
539
542
540 def diff():
543 def diff():
541 yield self.diff(p1, n, file)
544 yield self.diff(p1, n, file)
542
545
543 yield self.t("filediff",
546 yield self.t("filediff",
544 header = self.header(),
547 header = self.header(),
545 footer = self.footer(),
548 footer = self.footer(),
546 repo = self.reponame,
549 repo = self.reponame,
547 file = file,
550 file = file,
548 filenode = hex(mf[file]),
551 filenode = hex(mf[file]),
549 node = changeset,
552 node = changeset,
550 rev = self.repo.changelog.rev(n),
553 rev = self.repo.changelog.rev(n),
551 p1 = hex(p1),
554 p1 = hex(p1),
552 p1rev = self.repo.changelog.rev(p1),
555 p1rev = self.repo.changelog.rev(p1),
553 diff = diff)
556 diff = diff)
554
557
555 # add tags to things
558 # add tags to things
556 # tags -> list of changesets corresponding to tags
559 # tags -> list of changesets corresponding to tags
557 # find tag, changeset, file
560 # find tag, changeset, file
558
561
559 def run(self):
562 def run(self):
560 args = cgi.parse()
563 args = cgi.parse()
561
564
562 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
565 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
563 hi = self.repo.changelog.count()
566 hi = self.repo.changelog.count()
564 if args.has_key('rev'):
567 if args.has_key('rev'):
565 hi = args['rev'][0]
568 hi = args['rev'][0]
566 try:
569 try:
567 hi = self.repo.changelog.rev(self.repo.lookup(hi))
570 hi = self.repo.changelog.rev(self.repo.lookup(hi))
568 except KeyError:
571 except KeyError:
569 hi = self.repo.changelog.count()
572 hi = self.repo.changelog.count()
570
573
571 write(self.changelog(hi))
574 write(self.changelog(hi))
572
575
573 elif args['cmd'][0] == 'changeset':
576 elif args['cmd'][0] == 'changeset':
574 write(self.changeset(args['node'][0]))
577 write(self.changeset(args['node'][0]))
575
578
576 elif args['cmd'][0] == 'manifest':
579 elif args['cmd'][0] == 'manifest':
577 write(self.manifest(args['manifest'][0], args['path'][0]))
580 write(self.manifest(args['manifest'][0], args['path'][0]))
578
581
579 elif args['cmd'][0] == 'tags':
582 elif args['cmd'][0] == 'tags':
580 write(self.tags())
583 write(self.tags())
581
584
582 elif args['cmd'][0] == 'filediff':
585 elif args['cmd'][0] == 'filediff':
583 write(self.filediff(args['file'][0], args['node'][0]))
586 write(self.filediff(args['file'][0], args['node'][0]))
584
587
585 elif args['cmd'][0] == 'file':
588 elif args['cmd'][0] == 'file':
586 write(self.filerevision(args['file'][0], args['filenode'][0]))
589 write(self.filerevision(args['file'][0], args['filenode'][0]))
587
590
588 elif args['cmd'][0] == 'annotate':
591 elif args['cmd'][0] == 'annotate':
589 write(self.fileannotate(args['file'][0], args['filenode'][0]))
592 write(self.fileannotate(args['file'][0], args['filenode'][0]))
590
593
591 elif args['cmd'][0] == 'filelog':
594 elif args['cmd'][0] == 'filelog':
592 write(self.filelog(args['file'][0], args['filenode'][0]))
595 write(self.filelog(args['file'][0], args['filenode'][0]))
593
596
594 elif args['cmd'][0] == 'branches':
597 elif args['cmd'][0] == 'branches':
595 httphdr("text/plain")
598 httphdr("text/plain")
596 nodes = []
599 nodes = []
597 if args.has_key('nodes'):
600 if args.has_key('nodes'):
598 nodes = map(bin, args['nodes'][0].split(" "))
601 nodes = map(bin, args['nodes'][0].split(" "))
599 for b in self.repo.branches(nodes):
602 for b in self.repo.branches(nodes):
600 sys.stdout.write(" ".join(map(hex, b)) + "\n")
603 sys.stdout.write(" ".join(map(hex, b)) + "\n")
601
604
602 elif args['cmd'][0] == 'between':
605 elif args['cmd'][0] == 'between':
603 httphdr("text/plain")
606 httphdr("text/plain")
604 nodes = []
607 nodes = []
605 if args.has_key('pairs'):
608 if args.has_key('pairs'):
606 pairs = [ map(bin, p.split("-"))
609 pairs = [ map(bin, p.split("-"))
607 for p in args['pairs'][0].split(" ") ]
610 for p in args['pairs'][0].split(" ") ]
608 for b in self.repo.between(pairs):
611 for b in self.repo.between(pairs):
609 sys.stdout.write(" ".join(map(hex, b)) + "\n")
612 sys.stdout.write(" ".join(map(hex, b)) + "\n")
610
613
611 elif args['cmd'][0] == 'changegroup':
614 elif args['cmd'][0] == 'changegroup':
612 httphdr("application/hg-changegroup")
615 httphdr("application/hg-changegroup")
613 nodes = []
616 nodes = []
614 if args.has_key('roots'):
617 if args.has_key('roots'):
615 nodes = map(bin, args['roots'][0].split(" "))
618 nodes = map(bin, args['roots'][0].split(" "))
616
619
617 z = zlib.compressobj()
620 z = zlib.compressobj()
618 for chunk in self.repo.changegroup(nodes):
621 for chunk in self.repo.changegroup(nodes):
619 sys.stdout.write(z.compress(chunk))
622 sys.stdout.write(z.compress(chunk))
620
623
621 sys.stdout.write(z.flush())
624 sys.stdout.write(z.flush())
622
625
623 else:
626 else:
624 write(self.t("error"))
627 write(self.t("error"))
625
628
626 def server(path, name, templates, address, port):
629 def server(path, name, templates, address, port):
627
630
628 import BaseHTTPServer
631 import BaseHTTPServer
629 import sys, os
632 import sys, os
630
633
631 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
634 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
632 def do_POST(self):
635 def do_POST(self):
633 self.do_hgweb()
636 self.do_hgweb()
634
637
635 def do_GET(self):
638 def do_GET(self):
636 self.do_hgweb()
639 self.do_hgweb()
637
640
638 def do_hgweb(self):
641 def do_hgweb(self):
639 query = ""
642 query = ""
640 p = self.path.find("?")
643 p = self.path.find("?")
641 if p:
644 if p:
642 query = self.path[p + 1:]
645 query = self.path[p + 1:]
643 query = query.replace('+', ' ')
646 query = query.replace('+', ' ')
644
647
645 env = {}
648 env = {}
646 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
649 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
647 env['REQUEST_METHOD'] = self.command
650 env['REQUEST_METHOD'] = self.command
648 if query:
651 if query:
649 env['QUERY_STRING'] = query
652 env['QUERY_STRING'] = query
650 host = self.address_string()
653 host = self.address_string()
651 if host != self.client_address[0]:
654 if host != self.client_address[0]:
652 env['REMOTE_HOST'] = host
655 env['REMOTE_HOST'] = host
653 env['REMOTE_ADDR'] = self.client_address[0]
656 env['REMOTE_ADDR'] = self.client_address[0]
654
657
655 if self.headers.typeheader is None:
658 if self.headers.typeheader is None:
656 env['CONTENT_TYPE'] = self.headers.type
659 env['CONTENT_TYPE'] = self.headers.type
657 else:
660 else:
658 env['CONTENT_TYPE'] = self.headers.typeheader
661 env['CONTENT_TYPE'] = self.headers.typeheader
659 length = self.headers.getheader('content-length')
662 length = self.headers.getheader('content-length')
660 if length:
663 if length:
661 env['CONTENT_LENGTH'] = length
664 env['CONTENT_LENGTH'] = length
662 accept = []
665 accept = []
663 for line in self.headers.getallmatchingheaders('accept'):
666 for line in self.headers.getallmatchingheaders('accept'):
664 if line[:1] in "\t\n\r ":
667 if line[:1] in "\t\n\r ":
665 accept.append(line.strip())
668 accept.append(line.strip())
666 else:
669 else:
667 accept = accept + line[7:].split(',')
670 accept = accept + line[7:].split(',')
668 env['HTTP_ACCEPT'] = ','.join(accept)
671 env['HTTP_ACCEPT'] = ','.join(accept)
669
672
670 os.environ.update(env)
673 os.environ.update(env)
671
674
672 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
675 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
673 try:
676 try:
674 sys.stdin = self.rfile
677 sys.stdin = self.rfile
675 sys.stdout = self.wfile
678 sys.stdout = self.wfile
676 sys.argv = ["hgweb.py"]
679 sys.argv = ["hgweb.py"]
677 if '=' not in query:
680 if '=' not in query:
678 sys.argv.append(query)
681 sys.argv.append(query)
679 self.send_response(200, "Script output follows")
682 self.send_response(200, "Script output follows")
680 hg.run()
683 hg.run()
681 finally:
684 finally:
682 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
685 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
683
686
684 hg = hgweb(path, name, templates)
687 hg = hgweb(path, name, templates)
685 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
688 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
686 httpd.serve_forever()
689 httpd.serve_forever()
General Comments 0
You need to be logged in to leave comments. Login now