##// END OF EJS Templates
hgweb: don't blow up on search for unknown keys
mpm@selenic.com -
r166:39624c47 default
parent child Browse files
Show More
@@ -1,642 +1,645 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 def prettyprint(diff):
145 def prettyprint(diff):
146 for l in diff.splitlines(1):
146 for l in diff.splitlines(1):
147 line = cgi.escape(l)
147 line = cgi.escape(l)
148 if line.startswith('+'):
148 if line.startswith('+'):
149 yield self.t("difflineplus", line = line)
149 yield self.t("difflineplus", line = line)
150 elif line.startswith('-'):
150 elif line.startswith('-'):
151 yield self.t("difflineminus", line = line)
151 yield self.t("difflineminus", line = line)
152 elif line.startswith('@'):
152 elif line.startswith('@'):
153 yield self.t("difflineat", line = line)
153 yield self.t("difflineat", line = line)
154 else:
154 else:
155 yield self.t("diffline", line = line)
155 yield self.t("diffline", line = line)
156
156
157 r = self.repo
157 r = self.repo
158 cl = r.changelog
158 cl = r.changelog
159 mf = r.manifest
159 mf = r.manifest
160 change1 = cl.read(node1)
160 change1 = cl.read(node1)
161 change2 = cl.read(node2)
161 change2 = cl.read(node2)
162 mmap1 = mf.read(change1[0])
162 mmap1 = mf.read(change1[0])
163 mmap2 = mf.read(change2[0])
163 mmap2 = mf.read(change2[0])
164 date1 = self.date(change1)
164 date1 = self.date(change1)
165 date2 = self.date(change2)
165 date2 = self.date(change2)
166
166
167 c, a, d = r.diffrevs(node1, node2)
167 c, a, d = r.diffrevs(node1, node2)
168 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
168 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
169
169
170 for f in c:
170 for f in c:
171 to = r.file(f).read(mmap1[f])
171 to = r.file(f).read(mmap1[f])
172 tn = r.file(f).read(mmap2[f])
172 tn = r.file(f).read(mmap2[f])
173 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
173 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
174 for f in a:
174 for f in a:
175 to = ""
175 to = ""
176 tn = r.file(f).read(mmap2[f])
176 tn = r.file(f).read(mmap2[f])
177 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
177 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
178 for f in d:
178 for f in d:
179 to = r.file(f).read(mmap1[f])
179 to = r.file(f).read(mmap1[f])
180 tn = ""
180 tn = ""
181 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
181 yield prettyprint(mdiff.unidiff(to, date1, tn, date2, f))
182
182
183 def header(self):
183 def header(self):
184 yield self.t("header", repo = self.reponame)
184 yield self.t("header", repo = self.reponame)
185
185
186 def footer(self):
186 def footer(self):
187 yield self.t("footer", repo = self.reponame)
187 yield self.t("footer", repo = self.reponame)
188
188
189 def changelog(self, pos=None):
189 def changelog(self, pos=None):
190 def changenav():
190 def changenav():
191 def seq(factor = 1):
191 def seq(factor = 1):
192 yield 1 * factor
192 yield 1 * factor
193 yield 2 * factor
193 yield 2 * factor
194 yield 5 * factor
194 yield 5 * factor
195 for f in seq(factor * 10):
195 for f in seq(factor * 10):
196 yield f
196 yield f
197
197
198 linear = range(0, count - 2, self.maxchanges)[0:8]
198 linear = range(0, count - 2, self.maxchanges)[0:8]
199
199
200 for i in linear:
200 for i in linear:
201 yield self.t("naventry", rev = max(i, 1))
201 yield self.t("naventry", rev = max(i, 1))
202
202
203 for s in seq():
203 for s in seq():
204 if s > count - 2: break
204 if s > count - 2: break
205 if s > linear[-1]:
205 if s > linear[-1]:
206 yield self.t("naventry", rev = s)
206 yield self.t("naventry", rev = s)
207
207
208 yield self.t("naventry", rev = count - 1)
208 yield self.t("naventry", rev = count - 1)
209
209
210 def changelist():
210 def changelist():
211 parity = (start - end) & 1
211 parity = (start - end) & 1
212 cl = self.repo.changelog
212 cl = self.repo.changelog
213 l = [] # build a list in forward order for efficiency
213 l = [] # build a list in forward order for efficiency
214 for i in range(start, end + 1):
214 for i in range(start, end + 1):
215 n = cl.node(i)
215 n = cl.node(i)
216 changes = cl.read(n)
216 changes = cl.read(n)
217 hn = hex(n)
217 hn = hex(n)
218 p1, p2 = cl.parents(n)
218 p1, p2 = cl.parents(n)
219 t = float(changes[2].split(' ')[0])
219 t = float(changes[2].split(' ')[0])
220
220
221 l.insert(0, self.t(
221 l.insert(0, self.t(
222 'changelogentry',
222 'changelogentry',
223 parity = parity,
223 parity = parity,
224 author = obfuscate(changes[1]),
224 author = obfuscate(changes[1]),
225 shortdesc = cgi.escape(changes[4].splitlines()[0]),
225 shortdesc = cgi.escape(changes[4].splitlines()[0]),
226 age = age(t),
226 age = age(t),
227 parent1 = self.parent("changelogparent",
227 parent1 = self.parent("changelogparent",
228 hex(p1), cl.rev(p1)),
228 hex(p1), cl.rev(p1)),
229 parent2 = self.parent("changelogparent",
229 parent2 = self.parent("changelogparent",
230 hex(p2), cl.rev(p2)),
230 hex(p2), cl.rev(p2)),
231 p1 = hex(p1), p2 = hex(p2),
231 p1 = hex(p1), p2 = hex(p2),
232 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
232 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
233 manifest = hex(changes[0]),
233 manifest = hex(changes[0]),
234 desc = nl2br(cgi.escape(changes[4])),
234 desc = nl2br(cgi.escape(changes[4])),
235 date = time.asctime(time.gmtime(t)),
235 date = time.asctime(time.gmtime(t)),
236 files = self.listfilediffs(changes[3], n),
236 files = self.listfilediffs(changes[3], n),
237 rev = i,
237 rev = i,
238 node = hn))
238 node = hn))
239 parity = 1 - parity
239 parity = 1 - parity
240
240
241 yield l
241 yield l
242
242
243 count = self.repo.changelog.count()
243 count = self.repo.changelog.count()
244 pos = pos or count - 1
244 pos = pos or count - 1
245 end = min(pos, count - 1)
245 end = min(pos, count - 1)
246 start = max(0, pos - self.maxchanges)
246 start = max(0, pos - self.maxchanges)
247 end = min(count - 1, start + self.maxchanges)
247 end = min(count - 1, start + self.maxchanges)
248
248
249 yield self.t('changelog',
249 yield self.t('changelog',
250 header = self.header(),
250 header = self.header(),
251 footer = self.footer(),
251 footer = self.footer(),
252 repo = self.reponame,
252 repo = self.reponame,
253 changenav = changenav,
253 changenav = changenav,
254 rev = pos, changesets = count, entries = changelist)
254 rev = pos, changesets = count, entries = changelist)
255
255
256 def changeset(self, nodeid):
256 def changeset(self, nodeid):
257 n = bin(nodeid)
257 n = bin(nodeid)
258 cl = self.repo.changelog
258 cl = self.repo.changelog
259 changes = cl.read(n)
259 changes = cl.read(n)
260 p1, p2 = cl.parents(n)
260 p1, p2 = cl.parents(n)
261 p1rev, p2rev = cl.rev(p1), cl.rev(p2)
261 p1rev, p2rev = cl.rev(p1), cl.rev(p2)
262 t = float(changes[2].split(' ')[0])
262 t = float(changes[2].split(' ')[0])
263
263
264 files = []
264 files = []
265 mf = self.repo.manifest.read(changes[0])
265 mf = self.repo.manifest.read(changes[0])
266 for f in changes[3]:
266 for f in changes[3]:
267 files.append(self.t("filenodelink",
267 files.append(self.t("filenodelink",
268 filenode = hex(mf[f]), file = f))
268 filenode = hex(mf[f]), file = f))
269
269
270 def diff():
270 def diff():
271 yield self.diff(p1, n, changes[3])
271 yield self.diff(p1, n, changes[3])
272
272
273 yield self.t('changeset',
273 yield self.t('changeset',
274 header = self.header(),
274 header = self.header(),
275 footer = self.footer(),
275 footer = self.footer(),
276 repo = self.reponame,
276 repo = self.reponame,
277 diff = diff,
277 diff = diff,
278 rev = cl.rev(n),
278 rev = cl.rev(n),
279 node = nodeid,
279 node = nodeid,
280 shortdesc = cgi.escape(changes[4].splitlines()[0]),
280 shortdesc = cgi.escape(changes[4].splitlines()[0]),
281 parent1 = self.parent("changesetparent",
281 parent1 = self.parent("changesetparent",
282 hex(p1), cl.rev(p1)),
282 hex(p1), cl.rev(p1)),
283 parent2 = self.parent("changesetparent",
283 parent2 = self.parent("changesetparent",
284 hex(p2), cl.rev(p2)),
284 hex(p2), cl.rev(p2)),
285 p1 = hex(p1), p2 = hex(p2),
285 p1 = hex(p1), p2 = hex(p2),
286 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
286 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
287 manifest = hex(changes[0]),
287 manifest = hex(changes[0]),
288 author = obfuscate(changes[1]),
288 author = obfuscate(changes[1]),
289 desc = nl2br(cgi.escape(changes[4])),
289 desc = nl2br(cgi.escape(changes[4])),
290 date = time.asctime(time.gmtime(t)),
290 date = time.asctime(time.gmtime(t)),
291 files = files)
291 files = files)
292
292
293 def filelog(self, f, filenode):
293 def filelog(self, f, filenode):
294 cl = self.repo.changelog
294 cl = self.repo.changelog
295 fl = self.repo.file(f)
295 fl = self.repo.file(f)
296 count = fl.count()
296 count = fl.count()
297
297
298 def entries():
298 def entries():
299 l = []
299 l = []
300 parity = (count - 1) & 1
300 parity = (count - 1) & 1
301
301
302 for i in range(count):
302 for i in range(count):
303
303
304 n = fl.node(i)
304 n = fl.node(i)
305 lr = fl.linkrev(n)
305 lr = fl.linkrev(n)
306 cn = cl.node(lr)
306 cn = cl.node(lr)
307 cs = cl.read(cl.node(lr))
307 cs = cl.read(cl.node(lr))
308 p1, p2 = fl.parents(n)
308 p1, p2 = fl.parents(n)
309 t = float(cs[2].split(' ')[0])
309 t = float(cs[2].split(' ')[0])
310
310
311 l.insert(0, self.t("filelogentry",
311 l.insert(0, self.t("filelogentry",
312 parity = parity,
312 parity = parity,
313 filenode = hex(n),
313 filenode = hex(n),
314 filerev = i,
314 filerev = i,
315 file = f,
315 file = f,
316 node = hex(cn),
316 node = hex(cn),
317 author = obfuscate(cs[1]),
317 author = obfuscate(cs[1]),
318 age = age(t),
318 age = age(t),
319 date = time.asctime(time.gmtime(t)),
319 date = time.asctime(time.gmtime(t)),
320 shortdesc = cgi.escape(cs[4].splitlines()[0]),
320 shortdesc = cgi.escape(cs[4].splitlines()[0]),
321 p1 = hex(p1), p2 = hex(p2),
321 p1 = hex(p1), p2 = hex(p2),
322 p1rev = fl.rev(p1), p2rev = fl.rev(p2)))
322 p1rev = fl.rev(p1), p2rev = fl.rev(p2)))
323 parity = 1 - parity
323 parity = 1 - parity
324
324
325 yield l
325 yield l
326
326
327 yield self.t("filelog",
327 yield self.t("filelog",
328 header = self.header(),
328 header = self.header(),
329 footer = self.footer(),
329 footer = self.footer(),
330 repo = self.reponame,
330 repo = self.reponame,
331 file = f,
331 file = f,
332 filenode = filenode,
332 filenode = filenode,
333 entries = entries)
333 entries = entries)
334
334
335 def filerevision(self, f, node):
335 def filerevision(self, f, node):
336 fl = self.repo.file(f)
336 fl = self.repo.file(f)
337 n = bin(node)
337 n = bin(node)
338 text = cgi.escape(fl.read(n))
338 text = cgi.escape(fl.read(n))
339 changerev = fl.linkrev(n)
339 changerev = fl.linkrev(n)
340 cl = self.repo.changelog
340 cl = self.repo.changelog
341 cn = cl.node(changerev)
341 cn = cl.node(changerev)
342 cs = cl.read(cn)
342 cs = cl.read(cn)
343 p1, p2 = fl.parents(n)
343 p1, p2 = fl.parents(n)
344 t = float(cs[2].split(' ')[0])
344 t = float(cs[2].split(' ')[0])
345 mfn = cs[0]
345 mfn = cs[0]
346
346
347 def lines():
347 def lines():
348 for l, t in enumerate(text.splitlines(1)):
348 for l, t in enumerate(text.splitlines(1)):
349 yield self.t("fileline",
349 yield self.t("fileline",
350 line = t,
350 line = t,
351 linenumber = "% 6d" % (l + 1),
351 linenumber = "% 6d" % (l + 1),
352 parity = l & 1)
352 parity = l & 1)
353
353
354 yield self.t("filerevision", file = f,
354 yield self.t("filerevision", file = f,
355 header = self.header(),
355 header = self.header(),
356 footer = self.footer(),
356 footer = self.footer(),
357 repo = self.reponame,
357 repo = self.reponame,
358 filenode = node,
358 filenode = node,
359 path = up(f),
359 path = up(f),
360 text = lines(),
360 text = lines(),
361 rev = changerev,
361 rev = changerev,
362 node = hex(cn),
362 node = hex(cn),
363 manifest = hex(mfn),
363 manifest = hex(mfn),
364 author = obfuscate(cs[1]),
364 author = obfuscate(cs[1]),
365 age = age(t),
365 age = age(t),
366 date = time.asctime(time.gmtime(t)),
366 date = time.asctime(time.gmtime(t)),
367 shortdesc = cgi.escape(cs[4].splitlines()[0]),
367 shortdesc = cgi.escape(cs[4].splitlines()[0]),
368 parent1 = self.parent("filerevparent",
368 parent1 = self.parent("filerevparent",
369 hex(p1), fl.rev(p1), file=f),
369 hex(p1), fl.rev(p1), file=f),
370 parent2 = self.parent("filerevparent",
370 parent2 = self.parent("filerevparent",
371 hex(p2), fl.rev(p2), file=f),
371 hex(p2), fl.rev(p2), file=f),
372 p1 = hex(p1), p2 = hex(p2),
372 p1 = hex(p1), p2 = hex(p2),
373 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
373 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
374
374
375
375
376 def fileannotate(self, f, node):
376 def fileannotate(self, f, node):
377 bcache = {}
377 bcache = {}
378 ncache = {}
378 ncache = {}
379 fl = self.repo.file(f)
379 fl = self.repo.file(f)
380 n = bin(node)
380 n = bin(node)
381 changerev = fl.linkrev(n)
381 changerev = fl.linkrev(n)
382
382
383 cl = self.repo.changelog
383 cl = self.repo.changelog
384 cn = cl.node(changerev)
384 cn = cl.node(changerev)
385 cs = cl.read(cn)
385 cs = cl.read(cn)
386 p1, p2 = fl.parents(n)
386 p1, p2 = fl.parents(n)
387 t = float(cs[2].split(' ')[0])
387 t = float(cs[2].split(' ')[0])
388 mfn = cs[0]
388 mfn = cs[0]
389
389
390 def annotate():
390 def annotate():
391 parity = 1
391 parity = 1
392 last = None
392 last = None
393 for r, l in fl.annotate(n):
393 for r, l in fl.annotate(n):
394 try:
394 try:
395 cnode = ncache[r]
395 cnode = ncache[r]
396 except KeyError:
396 except KeyError:
397 cnode = ncache[r] = self.repo.changelog.node(r)
397 cnode = ncache[r] = self.repo.changelog.node(r)
398
398
399 try:
399 try:
400 name = bcache[r]
400 name = bcache[r]
401 except KeyError:
401 except KeyError:
402 cl = self.repo.changelog.read(cnode)
402 cl = self.repo.changelog.read(cnode)
403 name = cl[1]
403 name = cl[1]
404 f = name.find('@')
404 f = name.find('@')
405 if f >= 0:
405 if f >= 0:
406 name = name[:f]
406 name = name[:f]
407 bcache[r] = name
407 bcache[r] = name
408
408
409 if last != cnode:
409 if last != cnode:
410 parity = 1 - parity
410 parity = 1 - parity
411 last = cnode
411 last = cnode
412
412
413 yield self.t("annotateline",
413 yield self.t("annotateline",
414 parity = parity,
414 parity = parity,
415 node = hex(cnode),
415 node = hex(cnode),
416 rev = r,
416 rev = r,
417 author = name,
417 author = name,
418 file = f,
418 file = f,
419 line = cgi.escape(l))
419 line = cgi.escape(l))
420
420
421 yield self.t("fileannotate",
421 yield self.t("fileannotate",
422 header = self.header(),
422 header = self.header(),
423 footer = self.footer(),
423 footer = self.footer(),
424 repo = self.reponame,
424 repo = self.reponame,
425 file = f,
425 file = f,
426 filenode = node,
426 filenode = node,
427 annotate = annotate,
427 annotate = annotate,
428 path = up(f),
428 path = up(f),
429 rev = changerev,
429 rev = changerev,
430 node = hex(cn),
430 node = hex(cn),
431 manifest = hex(mfn),
431 manifest = hex(mfn),
432 author = obfuscate(cs[1]),
432 author = obfuscate(cs[1]),
433 age = age(t),
433 age = age(t),
434 date = time.asctime(time.gmtime(t)),
434 date = time.asctime(time.gmtime(t)),
435 shortdesc = cgi.escape(cs[4].splitlines()[0]),
435 shortdesc = cgi.escape(cs[4].splitlines()[0]),
436 parent1 = self.parent("fileannotateparent",
436 parent1 = self.parent("fileannotateparent",
437 hex(p1), fl.rev(p1), file=f),
437 hex(p1), fl.rev(p1), file=f),
438 parent2 = self.parent("fileannotateparent",
438 parent2 = self.parent("fileannotateparent",
439 hex(p2), fl.rev(p2), file=f),
439 hex(p2), fl.rev(p2), file=f),
440 p1 = hex(p1), p2 = hex(p2),
440 p1 = hex(p1), p2 = hex(p2),
441 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
441 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
442
442
443 def manifest(self, mnode, path):
443 def manifest(self, mnode, path):
444 mf = self.repo.manifest.read(bin(mnode))
444 mf = self.repo.manifest.read(bin(mnode))
445 rev = self.repo.manifest.rev(bin(mnode))
445 rev = self.repo.manifest.rev(bin(mnode))
446 node = self.repo.changelog.node(rev)
446 node = self.repo.changelog.node(rev)
447
447
448 files = {}
448 files = {}
449
449
450 p = path[1:]
450 p = path[1:]
451 l = len(p)
451 l = len(p)
452
452
453 for f,n in mf.items():
453 for f,n in mf.items():
454 if f[:l] != p:
454 if f[:l] != p:
455 continue
455 continue
456 remain = f[l:]
456 remain = f[l:]
457 if "/" in remain:
457 if "/" in remain:
458 short = remain[:remain.find("/") + 1] # bleah
458 short = remain[:remain.find("/") + 1] # bleah
459 files[short] = (f, None)
459 files[short] = (f, None)
460 else:
460 else:
461 short = os.path.basename(remain)
461 short = os.path.basename(remain)
462 files[short] = (f, n)
462 files[short] = (f, n)
463
463
464 def filelist():
464 def filelist():
465 parity = 0
465 parity = 0
466 fl = files.keys()
466 fl = files.keys()
467 fl.sort()
467 fl.sort()
468 for f in fl:
468 for f in fl:
469 full, fnode = files[f]
469 full, fnode = files[f]
470 if fnode:
470 if fnode:
471 yield self.t("manifestfileentry",
471 yield self.t("manifestfileentry",
472 file = full,
472 file = full,
473 manifest = mnode,
473 manifest = mnode,
474 filenode = hex(fnode),
474 filenode = hex(fnode),
475 parity = parity,
475 parity = parity,
476 basename = f)
476 basename = f)
477 else:
477 else:
478 yield self.t("manifestdirentry",
478 yield self.t("manifestdirentry",
479 parity = parity,
479 parity = parity,
480 path = os.path.join(path, f),
480 path = os.path.join(path, f),
481 manifest = mnode, basename = f[:-1])
481 manifest = mnode, basename = f[:-1])
482 parity = 1 - parity
482 parity = 1 - parity
483
483
484 yield self.t("manifest",
484 yield self.t("manifest",
485 header = self.header(),
485 header = self.header(),
486 footer = self.footer(),
486 footer = self.footer(),
487 repo = self.reponame,
487 repo = self.reponame,
488 manifest = mnode,
488 manifest = mnode,
489 rev = rev,
489 rev = rev,
490 node = hex(node),
490 node = hex(node),
491 path = path,
491 path = path,
492 up = up(path),
492 up = up(path),
493 entries = filelist)
493 entries = filelist)
494
494
495 def filediff(self, file, changeset):
495 def filediff(self, file, changeset):
496 n = bin(changeset)
496 n = bin(changeset)
497 cl = self.repo.changelog
497 cl = self.repo.changelog
498 p1 = cl.parents(n)[0]
498 p1 = cl.parents(n)[0]
499 cs = cl.read(n)
499 cs = cl.read(n)
500 mf = self.repo.manifest.read(cs[0])
500 mf = self.repo.manifest.read(cs[0])
501
501
502 def diff():
502 def diff():
503 yield self.diff(p1, n, file)
503 yield self.diff(p1, n, file)
504
504
505 yield self.t("filediff",
505 yield self.t("filediff",
506 header = self.header(),
506 header = self.header(),
507 footer = self.footer(),
507 footer = self.footer(),
508 repo = self.reponame,
508 repo = self.reponame,
509 file = file,
509 file = file,
510 filenode = hex(mf[file]),
510 filenode = hex(mf[file]),
511 node = changeset,
511 node = changeset,
512 rev = self.repo.changelog.rev(n),
512 rev = self.repo.changelog.rev(n),
513 p1 = hex(p1),
513 p1 = hex(p1),
514 p1rev = self.repo.changelog.rev(p1),
514 p1rev = self.repo.changelog.rev(p1),
515 diff = diff)
515 diff = diff)
516
516
517 # add tags to things
517 # add tags to things
518 # tags -> list of changesets corresponding to tags
518 # tags -> list of changesets corresponding to tags
519 # find tag, changeset, file
519 # find tag, changeset, file
520
520
521 def run(self):
521 def run(self):
522 args = cgi.parse()
522 args = cgi.parse()
523
523
524 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
524 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
525 hi = self.repo.changelog.count()
525 hi = self.repo.changelog.count()
526 if args.has_key('rev'):
526 if args.has_key('rev'):
527 hi = args['rev'][0]
527 hi = args['rev'][0]
528 try:
528 hi = self.repo.changelog.rev(self.repo.lookup(hi))
529 hi = self.repo.changelog.rev(self.repo.lookup(hi))
530 except KeyError:
531 hi = self.repo.changelog.count()
529
532
530 write(self.changelog(hi))
533 write(self.changelog(hi))
531
534
532 elif args['cmd'][0] == 'changeset':
535 elif args['cmd'][0] == 'changeset':
533 write(self.changeset(args['node'][0]))
536 write(self.changeset(args['node'][0]))
534
537
535 elif args['cmd'][0] == 'manifest':
538 elif args['cmd'][0] == 'manifest':
536 write(self.manifest(args['manifest'][0], args['path'][0]))
539 write(self.manifest(args['manifest'][0], args['path'][0]))
537
540
538 elif args['cmd'][0] == 'filediff':
541 elif args['cmd'][0] == 'filediff':
539 write(self.filediff(args['file'][0], args['node'][0]))
542 write(self.filediff(args['file'][0], args['node'][0]))
540
543
541 elif args['cmd'][0] == 'file':
544 elif args['cmd'][0] == 'file':
542 write(self.filerevision(args['file'][0], args['filenode'][0]))
545 write(self.filerevision(args['file'][0], args['filenode'][0]))
543
546
544 elif args['cmd'][0] == 'annotate':
547 elif args['cmd'][0] == 'annotate':
545 write(self.fileannotate(args['file'][0], args['filenode'][0]))
548 write(self.fileannotate(args['file'][0], args['filenode'][0]))
546
549
547 elif args['cmd'][0] == 'filelog':
550 elif args['cmd'][0] == 'filelog':
548 write(self.filelog(args['file'][0], args['filenode'][0]))
551 write(self.filelog(args['file'][0], args['filenode'][0]))
549
552
550 elif args['cmd'][0] == 'branches':
553 elif args['cmd'][0] == 'branches':
551 httphdr("text/plain")
554 httphdr("text/plain")
552 nodes = []
555 nodes = []
553 if args.has_key('nodes'):
556 if args.has_key('nodes'):
554 nodes = map(bin, args['nodes'][0].split(" "))
557 nodes = map(bin, args['nodes'][0].split(" "))
555 for b in self.repo.branches(nodes):
558 for b in self.repo.branches(nodes):
556 sys.stdout.write(" ".join(map(hex, b)) + "\n")
559 sys.stdout.write(" ".join(map(hex, b)) + "\n")
557
560
558 elif args['cmd'][0] == 'between':
561 elif args['cmd'][0] == 'between':
559 httphdr("text/plain")
562 httphdr("text/plain")
560 nodes = []
563 nodes = []
561 if args.has_key('pairs'):
564 if args.has_key('pairs'):
562 pairs = [ map(bin, p.split("-"))
565 pairs = [ map(bin, p.split("-"))
563 for p in args['pairs'][0].split(" ") ]
566 for p in args['pairs'][0].split(" ") ]
564 for b in self.repo.between(pairs):
567 for b in self.repo.between(pairs):
565 sys.stdout.write(" ".join(map(hex, b)) + "\n")
568 sys.stdout.write(" ".join(map(hex, b)) + "\n")
566
569
567 elif args['cmd'][0] == 'changegroup':
570 elif args['cmd'][0] == 'changegroup':
568 httphdr("application/hg-changegroup")
571 httphdr("application/hg-changegroup")
569 nodes = []
572 nodes = []
570 if args.has_key('roots'):
573 if args.has_key('roots'):
571 nodes = map(bin, args['roots'][0].split(" "))
574 nodes = map(bin, args['roots'][0].split(" "))
572
575
573 z = zlib.compressobj()
576 z = zlib.compressobj()
574 for chunk in self.repo.changegroup(nodes):
577 for chunk in self.repo.changegroup(nodes):
575 sys.stdout.write(z.compress(chunk))
578 sys.stdout.write(z.compress(chunk))
576
579
577 sys.stdout.write(z.flush())
580 sys.stdout.write(z.flush())
578
581
579 else:
582 else:
580 write(self.t("error"))
583 write(self.t("error"))
581
584
582 def server(path, name, templates, address, port):
585 def server(path, name, templates, address, port):
583
586
584 import BaseHTTPServer
587 import BaseHTTPServer
585 import sys, os
588 import sys, os
586
589
587 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
590 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
588 def do_POST(self):
591 def do_POST(self):
589 self.do_hgweb()
592 self.do_hgweb()
590
593
591 def do_GET(self):
594 def do_GET(self):
592 self.do_hgweb()
595 self.do_hgweb()
593
596
594 def do_hgweb(self):
597 def do_hgweb(self):
595 query = ""
598 query = ""
596 p = self.path.find("?")
599 p = self.path.find("?")
597 if p:
600 if p:
598 query = self.path[p + 1:]
601 query = self.path[p + 1:]
599 query = query.replace('+', ' ')
602 query = query.replace('+', ' ')
600
603
601 env = {}
604 env = {}
602 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
605 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
603 env['REQUEST_METHOD'] = self.command
606 env['REQUEST_METHOD'] = self.command
604 if query:
607 if query:
605 env['QUERY_STRING'] = query
608 env['QUERY_STRING'] = query
606 host = self.address_string()
609 host = self.address_string()
607 if host != self.client_address[0]:
610 if host != self.client_address[0]:
608 env['REMOTE_HOST'] = host
611 env['REMOTE_HOST'] = host
609 env['REMOTE_ADDR'] = self.client_address[0]
612 env['REMOTE_ADDR'] = self.client_address[0]
610
613
611 if self.headers.typeheader is None:
614 if self.headers.typeheader is None:
612 env['CONTENT_TYPE'] = self.headers.type
615 env['CONTENT_TYPE'] = self.headers.type
613 else:
616 else:
614 env['CONTENT_TYPE'] = self.headers.typeheader
617 env['CONTENT_TYPE'] = self.headers.typeheader
615 length = self.headers.getheader('content-length')
618 length = self.headers.getheader('content-length')
616 if length:
619 if length:
617 env['CONTENT_LENGTH'] = length
620 env['CONTENT_LENGTH'] = length
618 accept = []
621 accept = []
619 for line in self.headers.getallmatchingheaders('accept'):
622 for line in self.headers.getallmatchingheaders('accept'):
620 if line[:1] in "\t\n\r ":
623 if line[:1] in "\t\n\r ":
621 accept.append(line.strip())
624 accept.append(line.strip())
622 else:
625 else:
623 accept = accept + line[7:].split(',')
626 accept = accept + line[7:].split(',')
624 env['HTTP_ACCEPT'] = ','.join(accept)
627 env['HTTP_ACCEPT'] = ','.join(accept)
625
628
626 os.environ.update(env)
629 os.environ.update(env)
627
630
628 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
631 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
629 try:
632 try:
630 sys.stdin = self.rfile
633 sys.stdin = self.rfile
631 sys.stdout = self.wfile
634 sys.stdout = self.wfile
632 sys.argv = ["hgweb.py"]
635 sys.argv = ["hgweb.py"]
633 if '=' not in query:
636 if '=' not in query:
634 sys.argv.append(query)
637 sys.argv.append(query)
635 self.send_response(200, "Script output follows")
638 self.send_response(200, "Script output follows")
636 hg.run()
639 hg.run()
637 finally:
640 finally:
638 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
641 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
639
642
640 hg = hgweb(path, name, templates)
643 hg = hgweb(path, name, templates)
641 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
644 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
642 httpd.serve_forever()
645 httpd.serve_forever()
General Comments 0
You need to be logged in to leave comments. Login now