##// END OF EJS Templates
Merge with http://hannibal.lr-s.tudelft.nl/~vincent/fcgi/mercurial/fcgi/
Thomas Arendsen Hein -
r1182:24d553b5 merge default
parent child Browse files
Show More
@@ -1,993 +1,989 b''
1 # hgweb.py - web interface to a mercurial repository
1 # hgweb.py - web interface to a mercurial repository
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
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 import os, cgi, time, re, socket, sys, zlib, errno
9 import os, cgi, time, re, socket, sys, zlib, errno
10 import mdiff
10 import mdiff
11 from hg import *
11 from hg import *
12 from ui import *
12 from ui import *
13
13
14
14
15 def templatepath():
15 def templatepath():
16 for f in "templates", "../templates":
16 for f in "templates", "../templates":
17 p = os.path.join(os.path.dirname(__file__), f)
17 p = os.path.join(os.path.dirname(__file__), f)
18 if os.path.isdir(p):
18 if os.path.isdir(p):
19 return p
19 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:
23 if c == 1:
24 return t
24 return t
25 return t + "s"
25 return t + "s"
26 def fmt(t, c):
26 def fmt(t, c):
27 return "%d %s" % (c, plural(t, c))
27 return "%d %s" % (c, plural(t, c))
28
28
29 now = time.time()
29 now = time.time()
30 delta = max(1, int(now - t))
30 delta = max(1, int(now - t))
31
31
32 scales = [["second", 1],
32 scales = [["second", 1],
33 ["minute", 60],
33 ["minute", 60],
34 ["hour", 3600],
34 ["hour", 3600],
35 ["day", 3600 * 24],
35 ["day", 3600 * 24],
36 ["week", 3600 * 24 * 7],
36 ["week", 3600 * 24 * 7],
37 ["month", 3600 * 24 * 30],
37 ["month", 3600 * 24 * 30],
38 ["year", 3600 * 24 * 365]]
38 ["year", 3600 * 24 * 365]]
39
39
40 scales.reverse()
40 scales.reverse()
41
41
42 for t, s in scales:
42 for t, s in scales:
43 n = delta / s
43 n = delta / s
44 if n >= 2 or s == 1:
44 if n >= 2 or s == 1:
45 return fmt(t, n)
45 return fmt(t, n)
46
46
47 def nl2br(text):
47 def nl2br(text):
48 return text.replace('\n', '<br/>\n')
48 return text.replace('\n', '<br/>\n')
49
49
50 def obfuscate(text):
50 def obfuscate(text):
51 return ''.join(['&#%d;' % ord(c) for c in text])
51 return ''.join(['&#%d;' % ord(c) for c in text])
52
52
53 def up(p):
53 def up(p):
54 if p[0] != "/":
54 if p[0] != "/":
55 p = "/" + p
55 p = "/" + p
56 if p[-1] == "/":
56 if p[-1] == "/":
57 p = p[:-1]
57 p = p[:-1]
58 up = os.path.dirname(p)
58 up = os.path.dirname(p)
59 if up == "/":
59 if up == "/":
60 return "/"
60 return "/"
61 return up + "/"
61 return up + "/"
62
62
63 class hgrequest:
63 class hgrequest:
64 def __init__(self, inp=None, out=None, env=None):
64 def __init__(self, inp=None, out=None, env=None):
65 self.inp = inp or sys.stdin
65 self.inp = inp or sys.stdin
66 self.out = out or sys.stdout
66 self.out = out or sys.stdout
67 self.env = env or os.environ
67 self.env = env or os.environ
68 self.form = cgi.parse(self.inp, self.env)
68 self.form = cgi.parse(self.inp, self.env)
69
69
70 def write(self, *things):
70 def write(self, *things):
71 for thing in things:
71 for thing in things:
72 if hasattr(thing, "__iter__"):
72 if hasattr(thing, "__iter__"):
73 for part in thing:
73 for part in thing:
74 self.write(part)
74 self.write(part)
75 else:
75 else:
76 try:
76 try:
77 self.out.write(thing)
77 self.out.write(thing)
78 except TypeError:
78 except TypeError:
79 self.out.write(str(thing))
79 self.out.write(str(thing))
80 except socket.error, inst:
80 except socket.error, inst:
81 if inst[0] != errno.ECONNRESET:
81 if inst[0] != errno.ECONNRESET:
82 raise
82 raise
83
83
84 def header(self, headers=[('Content-type','text/html')]):
84 def header(self, headers=[('Content-type','text/html')]):
85 for header in headers:
85 for header in headers:
86 self.out.write("%s: %s\r\n" % header)
86 self.out.write("%s: %s\r\n" % header)
87 self.out.write("\r\n")
87 self.out.write("\r\n")
88
88
89 def httphdr(self, type, file="", size=0):
89 def httphdr(self, type, file="", size=0):
90
90
91 headers = [('Content-type', type)]
91 headers = [('Content-type', type)]
92 if file:
92 if file:
93 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
93 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
94 if size > 0:
94 if size > 0:
95 headers.append(('Content-length', str(size)))
95 headers.append(('Content-length', str(size)))
96 self.header(headers)
96 self.header(headers)
97
97
98 class templater:
98 class templater:
99 def __init__(self, mapfile, filters={}, defaults={}):
99 def __init__(self, mapfile, filters={}, defaults={}):
100 self.cache = {}
100 self.cache = {}
101 self.map = {}
101 self.map = {}
102 self.base = os.path.dirname(mapfile)
102 self.base = os.path.dirname(mapfile)
103 self.filters = filters
103 self.filters = filters
104 self.defaults = defaults
104 self.defaults = defaults
105
105
106 for l in file(mapfile):
106 for l in file(mapfile):
107 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
107 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
108 if m:
108 if m:
109 self.cache[m.group(1)] = m.group(2)
109 self.cache[m.group(1)] = m.group(2)
110 else:
110 else:
111 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
111 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
112 if m:
112 if m:
113 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
113 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
114 else:
114 else:
115 raise LookupError("unknown map entry '%s'" % l)
115 raise LookupError("unknown map entry '%s'" % l)
116
116
117 def __call__(self, t, **map):
117 def __call__(self, t, **map):
118 m = self.defaults.copy()
118 m = self.defaults.copy()
119 m.update(map)
119 m.update(map)
120 try:
120 try:
121 tmpl = self.cache[t]
121 tmpl = self.cache[t]
122 except KeyError:
122 except KeyError:
123 tmpl = self.cache[t] = file(self.map[t]).read()
123 tmpl = self.cache[t] = file(self.map[t]).read()
124 return self.template(tmpl, self.filters, **m)
124 return self.template(tmpl, self.filters, **m)
125
125
126 def template(self, tmpl, filters={}, **map):
126 def template(self, tmpl, filters={}, **map):
127 while tmpl:
127 while tmpl:
128 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
128 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
129 if m:
129 if m:
130 yield tmpl[:m.start(0)]
130 yield tmpl[:m.start(0)]
131 v = map.get(m.group(1), "")
131 v = map.get(m.group(1), "")
132 v = callable(v) and v(**map) or v
132 v = callable(v) and v(**map) or v
133
133
134 format = m.group(2)
134 format = m.group(2)
135 fl = m.group(4)
135 fl = m.group(4)
136
136
137 if format:
137 if format:
138 q = v.__iter__
138 q = v.__iter__
139 for i in q():
139 for i in q():
140 lm = map.copy()
140 lm = map.copy()
141 lm.update(i)
141 lm.update(i)
142 yield self(format[1:], **lm)
142 yield self(format[1:], **lm)
143
143
144 v = ""
144 v = ""
145
145
146 elif fl:
146 elif fl:
147 for f in fl.split("|")[1:]:
147 for f in fl.split("|")[1:]:
148 v = filters[f](v)
148 v = filters[f](v)
149
149
150 yield v
150 yield v
151 tmpl = tmpl[m.end(0):]
151 tmpl = tmpl[m.end(0):]
152 else:
152 else:
153 yield tmpl
153 yield tmpl
154 return
154 return
155
155
156 def rfc822date(x):
156 def rfc822date(x):
157 return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
157 return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
158
158
159 common_filters = {
159 common_filters = {
160 "escape": cgi.escape,
160 "escape": cgi.escape,
161 "age": age,
161 "age": age,
162 "date": (lambda x: time.asctime(time.gmtime(x))),
162 "date": (lambda x: time.asctime(time.gmtime(x))),
163 "addbreaks": nl2br,
163 "addbreaks": nl2br,
164 "obfuscate": obfuscate,
164 "obfuscate": obfuscate,
165 "short": (lambda x: x[:12]),
165 "short": (lambda x: x[:12]),
166 "firstline": (lambda x: x.splitlines(1)[0]),
166 "firstline": (lambda x: x.splitlines(1)[0]),
167 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
167 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
168 "rfc822date": rfc822date,
168 "rfc822date": rfc822date,
169 }
169 }
170
170
171
171
172
172
173 class hgweb:
173 class hgweb:
174 def __init__(self, repo, name=None):
174 def __init__(self, repo, name=None):
175 if type(repo) == type(""):
175 if type(repo) == type(""):
176 self.repo = repository(ui(), repo)
176 self.repo = repository(ui(), repo)
177 else:
177 else:
178 self.repo = repo
178 self.repo = repo
179
179
180 self.mtime = -1
180 self.mtime = -1
181 self.reponame = name
181 self.reponame = name
182 self.archives = 'zip', 'gz', 'bz2'
182 self.archives = 'zip', 'gz', 'bz2'
183
183
184 def refresh(self):
184 def refresh(self):
185 s = os.stat(os.path.join(self.repo.root, ".hg", "00changelog.i"))
185 s = os.stat(os.path.join(self.repo.root, ".hg", "00changelog.i"))
186 if s.st_mtime != self.mtime:
186 if s.st_mtime != self.mtime:
187 self.mtime = s.st_mtime
187 self.mtime = s.st_mtime
188 self.repo = repository(self.repo.ui, self.repo.root)
188 self.repo = repository(self.repo.ui, self.repo.root)
189 self.maxchanges = self.repo.ui.config("web", "maxchanges", 10)
189 self.maxchanges = self.repo.ui.config("web", "maxchanges", 10)
190 self.maxfiles = self.repo.ui.config("web", "maxchanges", 10)
190 self.maxfiles = self.repo.ui.config("web", "maxchanges", 10)
191 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
191 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
192
192
193 def date(self, cs):
193 def date(self, cs):
194 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
194 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
195
195
196 def listfiles(self, files, mf):
196 def listfiles(self, files, mf):
197 for f in files[:self.maxfiles]:
197 for f in files[:self.maxfiles]:
198 yield self.t("filenodelink", node=hex(mf[f]), file=f)
198 yield self.t("filenodelink", node=hex(mf[f]), file=f)
199 if len(files) > self.maxfiles:
199 if len(files) > self.maxfiles:
200 yield self.t("fileellipses")
200 yield self.t("fileellipses")
201
201
202 def listfilediffs(self, files, changeset):
202 def listfilediffs(self, files, changeset):
203 for f in files[:self.maxfiles]:
203 for f in files[:self.maxfiles]:
204 yield self.t("filedifflink", node=hex(changeset), file=f)
204 yield self.t("filedifflink", node=hex(changeset), file=f)
205 if len(files) > self.maxfiles:
205 if len(files) > self.maxfiles:
206 yield self.t("fileellipses")
206 yield self.t("fileellipses")
207
207
208 def parents(self, t1, nodes=[], rev=None,**args):
208 def parents(self, t1, nodes=[], rev=None,**args):
209 if not rev:
209 if not rev:
210 rev = lambda x: ""
210 rev = lambda x: ""
211 for node in nodes:
211 for node in nodes:
212 if node != nullid:
212 if node != nullid:
213 yield self.t(t1, node=hex(node), rev=rev(node), **args)
213 yield self.t(t1, node=hex(node), rev=rev(node), **args)
214
214
215 def showtag(self, t1, node=nullid, **args):
215 def showtag(self, t1, node=nullid, **args):
216 for t in self.repo.nodetags(node):
216 for t in self.repo.nodetags(node):
217 yield self.t(t1, tag=t, **args)
217 yield self.t(t1, tag=t, **args)
218
218
219 def diff(self, node1, node2, files):
219 def diff(self, node1, node2, files):
220 def filterfiles(list, files):
220 def filterfiles(list, files):
221 l = [x for x in list if x in files]
221 l = [x for x in list if x in files]
222
222
223 for f in files:
223 for f in files:
224 if f[-1] != os.sep:
224 if f[-1] != os.sep:
225 f += os.sep
225 f += os.sep
226 l += [x for x in list if x.startswith(f)]
226 l += [x for x in list if x.startswith(f)]
227 return l
227 return l
228
228
229 parity = [0]
229 parity = [0]
230 def diffblock(diff, f, fn):
230 def diffblock(diff, f, fn):
231 yield self.t("diffblock",
231 yield self.t("diffblock",
232 lines=prettyprintlines(diff),
232 lines=prettyprintlines(diff),
233 parity=parity[0],
233 parity=parity[0],
234 file=f,
234 file=f,
235 filenode=hex(fn or nullid))
235 filenode=hex(fn or nullid))
236 parity[0] = 1 - parity[0]
236 parity[0] = 1 - parity[0]
237
237
238 def prettyprintlines(diff):
238 def prettyprintlines(diff):
239 for l in diff.splitlines(1):
239 for l in diff.splitlines(1):
240 if l.startswith('+'):
240 if l.startswith('+'):
241 yield self.t("difflineplus", line=l)
241 yield self.t("difflineplus", line=l)
242 elif l.startswith('-'):
242 elif l.startswith('-'):
243 yield self.t("difflineminus", line=l)
243 yield self.t("difflineminus", line=l)
244 elif l.startswith('@'):
244 elif l.startswith('@'):
245 yield self.t("difflineat", line=l)
245 yield self.t("difflineat", line=l)
246 else:
246 else:
247 yield self.t("diffline", line=l)
247 yield self.t("diffline", line=l)
248
248
249 r = self.repo
249 r = self.repo
250 cl = r.changelog
250 cl = r.changelog
251 mf = r.manifest
251 mf = r.manifest
252 change1 = cl.read(node1)
252 change1 = cl.read(node1)
253 change2 = cl.read(node2)
253 change2 = cl.read(node2)
254 mmap1 = mf.read(change1[0])
254 mmap1 = mf.read(change1[0])
255 mmap2 = mf.read(change2[0])
255 mmap2 = mf.read(change2[0])
256 date1 = self.date(change1)
256 date1 = self.date(change1)
257 date2 = self.date(change2)
257 date2 = self.date(change2)
258
258
259 c, a, d, u = r.changes(node1, node2)
259 c, a, d, u = r.changes(node1, node2)
260 if files:
260 if files:
261 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
261 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
262
262
263 for f in c:
263 for f in c:
264 to = r.file(f).read(mmap1[f])
264 to = r.file(f).read(mmap1[f])
265 tn = r.file(f).read(mmap2[f])
265 tn = r.file(f).read(mmap2[f])
266 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
266 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
267 for f in a:
267 for f in a:
268 to = None
268 to = None
269 tn = r.file(f).read(mmap2[f])
269 tn = r.file(f).read(mmap2[f])
270 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
270 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
271 for f in d:
271 for f in d:
272 to = r.file(f).read(mmap1[f])
272 to = r.file(f).read(mmap1[f])
273 tn = None
273 tn = None
274 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
274 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
275
275
276 def changelog(self, pos):
276 def changelog(self, pos):
277 def changenav(**map):
277 def changenav(**map):
278 def seq(factor=1):
278 def seq(factor=1):
279 yield 1 * factor
279 yield 1 * factor
280 yield 3 * factor
280 yield 3 * factor
281 #yield 5 * factor
281 #yield 5 * factor
282 for f in seq(factor * 10):
282 for f in seq(factor * 10):
283 yield f
283 yield f
284
284
285 l = []
285 l = []
286 for f in seq():
286 for f in seq():
287 if f < self.maxchanges / 2:
287 if f < self.maxchanges / 2:
288 continue
288 continue
289 if f > count:
289 if f > count:
290 break
290 break
291 r = "%d" % f
291 r = "%d" % f
292 if pos + f < count:
292 if pos + f < count:
293 l.append(("+" + r, pos + f))
293 l.append(("+" + r, pos + f))
294 if pos - f >= 0:
294 if pos - f >= 0:
295 l.insert(0, ("-" + r, pos - f))
295 l.insert(0, ("-" + r, pos - f))
296
296
297 yield {"rev": 0, "label": "(0)"}
297 yield {"rev": 0, "label": "(0)"}
298
298
299 for label, rev in l:
299 for label, rev in l:
300 yield {"label": label, "rev": rev}
300 yield {"label": label, "rev": rev}
301
301
302 yield {"label": "tip", "rev": ""}
302 yield {"label": "tip", "rev": ""}
303
303
304 def changelist(**map):
304 def changelist(**map):
305 parity = (start - end) & 1
305 parity = (start - end) & 1
306 cl = self.repo.changelog
306 cl = self.repo.changelog
307 l = [] # build a list in forward order for efficiency
307 l = [] # build a list in forward order for efficiency
308 for i in range(start, end):
308 for i in range(start, end):
309 n = cl.node(i)
309 n = cl.node(i)
310 changes = cl.read(n)
310 changes = cl.read(n)
311 hn = hex(n)
311 hn = hex(n)
312 t = float(changes[2].split(' ')[0])
312 t = float(changes[2].split(' ')[0])
313
313
314 l.insert(0, {"parity": parity,
314 l.insert(0, {"parity": parity,
315 "author": changes[1],
315 "author": changes[1],
316 "parent": self.parents("changelogparent",
316 "parent": self.parents("changelogparent",
317 cl.parents(n), cl.rev),
317 cl.parents(n), cl.rev),
318 "changelogtag": self.showtag("changelogtag",n),
318 "changelogtag": self.showtag("changelogtag",n),
319 "manifest": hex(changes[0]),
319 "manifest": hex(changes[0]),
320 "desc": changes[4],
320 "desc": changes[4],
321 "date": t,
321 "date": t,
322 "files": self.listfilediffs(changes[3], n),
322 "files": self.listfilediffs(changes[3], n),
323 "rev": i,
323 "rev": i,
324 "node": hn})
324 "node": hn})
325 parity = 1 - parity
325 parity = 1 - parity
326
326
327 for e in l:
327 for e in l:
328 yield e
328 yield e
329
329
330 cl = self.repo.changelog
330 cl = self.repo.changelog
331 mf = cl.read(cl.tip())[0]
331 mf = cl.read(cl.tip())[0]
332 count = cl.count()
332 count = cl.count()
333 start = max(0, pos - self.maxchanges + 1)
333 start = max(0, pos - self.maxchanges + 1)
334 end = min(count, start + self.maxchanges)
334 end = min(count, start + self.maxchanges)
335 pos = end - 1
335 pos = end - 1
336
336
337 yield self.t('changelog',
337 yield self.t('changelog',
338 changenav=changenav,
338 changenav=changenav,
339 manifest=hex(mf),
339 manifest=hex(mf),
340 rev=pos, changesets=count, entries=changelist)
340 rev=pos, changesets=count, entries=changelist)
341
341
342 def search(self, query):
342 def search(self, query):
343
343
344 def changelist(**map):
344 def changelist(**map):
345 cl = self.repo.changelog
345 cl = self.repo.changelog
346 count = 0
346 count = 0
347 qw = query.lower().split()
347 qw = query.lower().split()
348
348
349 def revgen():
349 def revgen():
350 for i in range(cl.count() - 1, 0, -100):
350 for i in range(cl.count() - 1, 0, -100):
351 l = []
351 l = []
352 for j in range(max(0, i - 100), i):
352 for j in range(max(0, i - 100), i):
353 n = cl.node(j)
353 n = cl.node(j)
354 changes = cl.read(n)
354 changes = cl.read(n)
355 l.append((n, j, changes))
355 l.append((n, j, changes))
356 l.reverse()
356 l.reverse()
357 for e in l:
357 for e in l:
358 yield e
358 yield e
359
359
360 for n, i, changes in revgen():
360 for n, i, changes in revgen():
361 miss = 0
361 miss = 0
362 for q in qw:
362 for q in qw:
363 if not (q in changes[1].lower() or
363 if not (q in changes[1].lower() or
364 q in changes[4].lower() or
364 q in changes[4].lower() or
365 q in " ".join(changes[3][:20]).lower()):
365 q in " ".join(changes[3][:20]).lower()):
366 miss = 1
366 miss = 1
367 break
367 break
368 if miss:
368 if miss:
369 continue
369 continue
370
370
371 count += 1
371 count += 1
372 hn = hex(n)
372 hn = hex(n)
373 t = float(changes[2].split(' ')[0])
373 t = float(changes[2].split(' ')[0])
374
374
375 yield self.t('searchentry',
375 yield self.t('searchentry',
376 parity=count & 1,
376 parity=count & 1,
377 author=changes[1],
377 author=changes[1],
378 parent=self.parents("changelogparent",
378 parent=self.parents("changelogparent",
379 cl.parents(n), cl.rev),
379 cl.parents(n), cl.rev),
380 changelogtag=self.showtag("changelogtag",n),
380 changelogtag=self.showtag("changelogtag",n),
381 manifest=hex(changes[0]),
381 manifest=hex(changes[0]),
382 desc=changes[4],
382 desc=changes[4],
383 date=t,
383 date=t,
384 files=self.listfilediffs(changes[3], n),
384 files=self.listfilediffs(changes[3], n),
385 rev=i,
385 rev=i,
386 node=hn)
386 node=hn)
387
387
388 if count >= self.maxchanges:
388 if count >= self.maxchanges:
389 break
389 break
390
390
391 cl = self.repo.changelog
391 cl = self.repo.changelog
392 mf = cl.read(cl.tip())[0]
392 mf = cl.read(cl.tip())[0]
393
393
394 yield self.t('search',
394 yield self.t('search',
395 query=query,
395 query=query,
396 manifest=hex(mf),
396 manifest=hex(mf),
397 entries=changelist)
397 entries=changelist)
398
398
399 def changeset(self, nodeid):
399 def changeset(self, nodeid):
400 n = bin(nodeid)
400 n = bin(nodeid)
401 cl = self.repo.changelog
401 cl = self.repo.changelog
402 changes = cl.read(n)
402 changes = cl.read(n)
403 p1 = cl.parents(n)[0]
403 p1 = cl.parents(n)[0]
404 t = float(changes[2].split(' ')[0])
404 t = float(changes[2].split(' ')[0])
405
405
406 files = []
406 files = []
407 mf = self.repo.manifest.read(changes[0])
407 mf = self.repo.manifest.read(changes[0])
408 for f in changes[3]:
408 for f in changes[3]:
409 files.append(self.t("filenodelink",
409 files.append(self.t("filenodelink",
410 filenode=hex(mf.get(f, nullid)), file=f))
410 filenode=hex(mf.get(f, nullid)), file=f))
411
411
412 def diff(**map):
412 def diff(**map):
413 yield self.diff(p1, n, None)
413 yield self.diff(p1, n, None)
414
414
415 def archivelist():
415 def archivelist():
416 for i in self.archives:
416 for i in self.archives:
417 if self.repo.ui.configbool("web", "allow" + i, False):
417 if self.repo.ui.configbool("web", "allow" + i, False):
418 yield {"type" : i, "node" : nodeid}
418 yield {"type" : i, "node" : nodeid}
419
419
420 yield self.t('changeset',
420 yield self.t('changeset',
421 diff=diff,
421 diff=diff,
422 rev=cl.rev(n),
422 rev=cl.rev(n),
423 node=nodeid,
423 node=nodeid,
424 parent=self.parents("changesetparent",
424 parent=self.parents("changesetparent",
425 cl.parents(n), cl.rev),
425 cl.parents(n), cl.rev),
426 changesettag=self.showtag("changesettag",n),
426 changesettag=self.showtag("changesettag",n),
427 manifest=hex(changes[0]),
427 manifest=hex(changes[0]),
428 author=changes[1],
428 author=changes[1],
429 desc=changes[4],
429 desc=changes[4],
430 date=t,
430 date=t,
431 files=files,
431 files=files,
432 archives=archivelist())
432 archives=archivelist())
433
433
434 def filelog(self, f, filenode):
434 def filelog(self, f, filenode):
435 cl = self.repo.changelog
435 cl = self.repo.changelog
436 fl = self.repo.file(f)
436 fl = self.repo.file(f)
437 count = fl.count()
437 count = fl.count()
438
438
439 def entries(**map):
439 def entries(**map):
440 l = []
440 l = []
441 parity = (count - 1) & 1
441 parity = (count - 1) & 1
442
442
443 for i in range(count):
443 for i in range(count):
444 n = fl.node(i)
444 n = fl.node(i)
445 lr = fl.linkrev(n)
445 lr = fl.linkrev(n)
446 cn = cl.node(lr)
446 cn = cl.node(lr)
447 cs = cl.read(cl.node(lr))
447 cs = cl.read(cl.node(lr))
448 t = float(cs[2].split(' ')[0])
448 t = float(cs[2].split(' ')[0])
449
449
450 l.insert(0, {"parity": parity,
450 l.insert(0, {"parity": parity,
451 "filenode": hex(n),
451 "filenode": hex(n),
452 "filerev": i,
452 "filerev": i,
453 "file": f,
453 "file": f,
454 "node": hex(cn),
454 "node": hex(cn),
455 "author": cs[1],
455 "author": cs[1],
456 "date": t,
456 "date": t,
457 "parent": self.parents("filelogparent",
457 "parent": self.parents("filelogparent",
458 fl.parents(n),
458 fl.parents(n),
459 fl.rev, file=f),
459 fl.rev, file=f),
460 "desc": cs[4]})
460 "desc": cs[4]})
461 parity = 1 - parity
461 parity = 1 - parity
462
462
463 for e in l:
463 for e in l:
464 yield e
464 yield e
465
465
466 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
466 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
467
467
468 def filerevision(self, f, node):
468 def filerevision(self, f, node):
469 fl = self.repo.file(f)
469 fl = self.repo.file(f)
470 n = bin(node)
470 n = bin(node)
471 text = fl.read(n)
471 text = fl.read(n)
472 changerev = fl.linkrev(n)
472 changerev = fl.linkrev(n)
473 cl = self.repo.changelog
473 cl = self.repo.changelog
474 cn = cl.node(changerev)
474 cn = cl.node(changerev)
475 cs = cl.read(cn)
475 cs = cl.read(cn)
476 t = float(cs[2].split(' ')[0])
476 t = float(cs[2].split(' ')[0])
477 mfn = cs[0]
477 mfn = cs[0]
478
478
479 def lines():
479 def lines():
480 for l, t in enumerate(text.splitlines(1)):
480 for l, t in enumerate(text.splitlines(1)):
481 yield {"line": t,
481 yield {"line": t,
482 "linenumber": "% 6d" % (l + 1),
482 "linenumber": "% 6d" % (l + 1),
483 "parity": l & 1}
483 "parity": l & 1}
484
484
485 yield self.t("filerevision",
485 yield self.t("filerevision",
486 file=f,
486 file=f,
487 filenode=node,
487 filenode=node,
488 path=up(f),
488 path=up(f),
489 text=lines(),
489 text=lines(),
490 rev=changerev,
490 rev=changerev,
491 node=hex(cn),
491 node=hex(cn),
492 manifest=hex(mfn),
492 manifest=hex(mfn),
493 author=cs[1],
493 author=cs[1],
494 date=t,
494 date=t,
495 parent=self.parents("filerevparent",
495 parent=self.parents("filerevparent",
496 fl.parents(n), fl.rev, file=f),
496 fl.parents(n), fl.rev, file=f),
497 permissions=self.repo.manifest.readflags(mfn)[f])
497 permissions=self.repo.manifest.readflags(mfn)[f])
498
498
499 def fileannotate(self, f, node):
499 def fileannotate(self, f, node):
500 bcache = {}
500 bcache = {}
501 ncache = {}
501 ncache = {}
502 fl = self.repo.file(f)
502 fl = self.repo.file(f)
503 n = bin(node)
503 n = bin(node)
504 changerev = fl.linkrev(n)
504 changerev = fl.linkrev(n)
505
505
506 cl = self.repo.changelog
506 cl = self.repo.changelog
507 cn = cl.node(changerev)
507 cn = cl.node(changerev)
508 cs = cl.read(cn)
508 cs = cl.read(cn)
509 t = float(cs[2].split(' ')[0])
509 t = float(cs[2].split(' ')[0])
510 mfn = cs[0]
510 mfn = cs[0]
511
511
512 def annotate(**map):
512 def annotate(**map):
513 parity = 1
513 parity = 1
514 last = None
514 last = None
515 for r, l in fl.annotate(n):
515 for r, l in fl.annotate(n):
516 try:
516 try:
517 cnode = ncache[r]
517 cnode = ncache[r]
518 except KeyError:
518 except KeyError:
519 cnode = ncache[r] = self.repo.changelog.node(r)
519 cnode = ncache[r] = self.repo.changelog.node(r)
520
520
521 try:
521 try:
522 name = bcache[r]
522 name = bcache[r]
523 except KeyError:
523 except KeyError:
524 cl = self.repo.changelog.read(cnode)
524 cl = self.repo.changelog.read(cnode)
525 bcache[r] = name = self.repo.ui.shortuser(cl[1])
525 bcache[r] = name = self.repo.ui.shortuser(cl[1])
526
526
527 if last != cnode:
527 if last != cnode:
528 parity = 1 - parity
528 parity = 1 - parity
529 last = cnode
529 last = cnode
530
530
531 yield {"parity": parity,
531 yield {"parity": parity,
532 "node": hex(cnode),
532 "node": hex(cnode),
533 "rev": r,
533 "rev": r,
534 "author": name,
534 "author": name,
535 "file": f,
535 "file": f,
536 "line": l}
536 "line": l}
537
537
538 yield self.t("fileannotate",
538 yield self.t("fileannotate",
539 file=f,
539 file=f,
540 filenode=node,
540 filenode=node,
541 annotate=annotate,
541 annotate=annotate,
542 path=up(f),
542 path=up(f),
543 rev=changerev,
543 rev=changerev,
544 node=hex(cn),
544 node=hex(cn),
545 manifest=hex(mfn),
545 manifest=hex(mfn),
546 author=cs[1],
546 author=cs[1],
547 date=t,
547 date=t,
548 parent=self.parents("fileannotateparent",
548 parent=self.parents("fileannotateparent",
549 fl.parents(n), fl.rev, file=f),
549 fl.parents(n), fl.rev, file=f),
550 permissions=self.repo.manifest.readflags(mfn)[f])
550 permissions=self.repo.manifest.readflags(mfn)[f])
551
551
552 def manifest(self, mnode, path):
552 def manifest(self, mnode, path):
553 mf = self.repo.manifest.read(bin(mnode))
553 mf = self.repo.manifest.read(bin(mnode))
554 rev = self.repo.manifest.rev(bin(mnode))
554 rev = self.repo.manifest.rev(bin(mnode))
555 node = self.repo.changelog.node(rev)
555 node = self.repo.changelog.node(rev)
556 mff=self.repo.manifest.readflags(bin(mnode))
556 mff=self.repo.manifest.readflags(bin(mnode))
557
557
558 files = {}
558 files = {}
559
559
560 p = path[1:]
560 p = path[1:]
561 l = len(p)
561 l = len(p)
562
562
563 for f,n in mf.items():
563 for f,n in mf.items():
564 if f[:l] != p:
564 if f[:l] != p:
565 continue
565 continue
566 remain = f[l:]
566 remain = f[l:]
567 if "/" in remain:
567 if "/" in remain:
568 short = remain[:remain.find("/") + 1] # bleah
568 short = remain[:remain.find("/") + 1] # bleah
569 files[short] = (f, None)
569 files[short] = (f, None)
570 else:
570 else:
571 short = os.path.basename(remain)
571 short = os.path.basename(remain)
572 files[short] = (f, n)
572 files[short] = (f, n)
573
573
574 def filelist(**map):
574 def filelist(**map):
575 parity = 0
575 parity = 0
576 fl = files.keys()
576 fl = files.keys()
577 fl.sort()
577 fl.sort()
578 for f in fl:
578 for f in fl:
579 full, fnode = files[f]
579 full, fnode = files[f]
580 if not fnode:
580 if not fnode:
581 continue
581 continue
582
582
583 yield {"file": full,
583 yield {"file": full,
584 "manifest": mnode,
584 "manifest": mnode,
585 "filenode": hex(fnode),
585 "filenode": hex(fnode),
586 "parity": parity,
586 "parity": parity,
587 "basename": f,
587 "basename": f,
588 "permissions": mff[full]}
588 "permissions": mff[full]}
589 parity = 1 - parity
589 parity = 1 - parity
590
590
591 def dirlist(**map):
591 def dirlist(**map):
592 parity = 0
592 parity = 0
593 fl = files.keys()
593 fl = files.keys()
594 fl.sort()
594 fl.sort()
595 for f in fl:
595 for f in fl:
596 full, fnode = files[f]
596 full, fnode = files[f]
597 if fnode:
597 if fnode:
598 continue
598 continue
599
599
600 yield {"parity": parity,
600 yield {"parity": parity,
601 "path": os.path.join(path, f),
601 "path": os.path.join(path, f),
602 "manifest": mnode,
602 "manifest": mnode,
603 "basename": f[:-1]}
603 "basename": f[:-1]}
604 parity = 1 - parity
604 parity = 1 - parity
605
605
606 yield self.t("manifest",
606 yield self.t("manifest",
607 manifest=mnode,
607 manifest=mnode,
608 rev=rev,
608 rev=rev,
609 node=hex(node),
609 node=hex(node),
610 path=path,
610 path=path,
611 up=up(path),
611 up=up(path),
612 fentries=filelist,
612 fentries=filelist,
613 dentries=dirlist)
613 dentries=dirlist)
614
614
615 def tags(self):
615 def tags(self):
616 cl = self.repo.changelog
616 cl = self.repo.changelog
617 mf = cl.read(cl.tip())[0]
617 mf = cl.read(cl.tip())[0]
618
618
619 i = self.repo.tagslist()
619 i = self.repo.tagslist()
620 i.reverse()
620 i.reverse()
621
621
622 def entries(**map):
622 def entries(**map):
623 parity = 0
623 parity = 0
624 for k,n in i:
624 for k,n in i:
625 yield {"parity": parity,
625 yield {"parity": parity,
626 "tag": k,
626 "tag": k,
627 "node": hex(n)}
627 "node": hex(n)}
628 parity = 1 - parity
628 parity = 1 - parity
629
629
630 yield self.t("tags",
630 yield self.t("tags",
631 manifest=hex(mf),
631 manifest=hex(mf),
632 entries=entries)
632 entries=entries)
633
633
634 def filediff(self, file, changeset):
634 def filediff(self, file, changeset):
635 n = bin(changeset)
635 n = bin(changeset)
636 cl = self.repo.changelog
636 cl = self.repo.changelog
637 p1 = cl.parents(n)[0]
637 p1 = cl.parents(n)[0]
638 cs = cl.read(n)
638 cs = cl.read(n)
639 mf = self.repo.manifest.read(cs[0])
639 mf = self.repo.manifest.read(cs[0])
640
640
641 def diff(**map):
641 def diff(**map):
642 yield self.diff(p1, n, file)
642 yield self.diff(p1, n, file)
643
643
644 yield self.t("filediff",
644 yield self.t("filediff",
645 file=file,
645 file=file,
646 filenode=hex(mf.get(file, nullid)),
646 filenode=hex(mf.get(file, nullid)),
647 node=changeset,
647 node=changeset,
648 rev=self.repo.changelog.rev(n),
648 rev=self.repo.changelog.rev(n),
649 parent=self.parents("filediffparent",
649 parent=self.parents("filediffparent",
650 cl.parents(n), cl.rev),
650 cl.parents(n), cl.rev),
651 diff=diff)
651 diff=diff)
652
652
653 def archive(self, req, cnode, type):
653 def archive(self, req, cnode, type):
654 cs = self.repo.changelog.read(cnode)
654 cs = self.repo.changelog.read(cnode)
655 mnode = cs[0]
655 mnode = cs[0]
656 mf = self.repo.manifest.read(mnode)
656 mf = self.repo.manifest.read(mnode)
657 rev = self.repo.manifest.rev(mnode)
657 rev = self.repo.manifest.rev(mnode)
658 reponame = re.sub(r"\W+", "-", self.reponame)
658 reponame = re.sub(r"\W+", "-", self.reponame)
659 name = "%s-%s/" % (reponame, short(cnode))
659 name = "%s-%s/" % (reponame, short(cnode))
660
660
661 files = mf.keys()
661 files = mf.keys()
662 files.sort()
662 files.sort()
663
663
664 if type == 'zip':
664 if type == 'zip':
665 import zipfile, tempfile
665 import zipfile, tempfile
666
666
667 tmp = tempfile.mkstemp()[1]
667 tmp = tempfile.mkstemp()[1]
668 try:
668 try:
669 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
669 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
670
670
671 for f in files:
671 for f in files:
672 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
672 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
673 zf.close()
673 zf.close()
674
674
675 f = open(tmp, 'r')
675 f = open(tmp, 'r')
676 req.httphdr('application/zip', name[:-1] + '.zip',
676 req.httphdr('application/zip', name[:-1] + '.zip',
677 os.path.getsize(tmp))
677 os.path.getsize(tmp))
678 req.write(f.read())
678 req.write(f.read())
679 f.close()
679 f.close()
680 finally:
680 finally:
681 os.unlink(tmp)
681 os.unlink(tmp)
682
682
683 else:
683 else:
684 import StringIO
684 import StringIO
685 import time
685 import time
686 import tarfile
686 import tarfile
687
687
688 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
688 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
689 mff = self.repo.manifest.readflags(mnode)
689 mff = self.repo.manifest.readflags(mnode)
690 mtime = int(time.time())
690 mtime = int(time.time())
691
691
692 req.httphdr('application/octet-stream', name[:-1] + '.tar.' + type)
692 req.httphdr('application/octet-stream', name[:-1] + '.tar.' + type)
693 for fname in files:
693 for fname in files:
694 rcont = self.repo.file(fname).read(mf[fname])
694 rcont = self.repo.file(fname).read(mf[fname])
695 finfo = tarfile.TarInfo(name + fname)
695 finfo = tarfile.TarInfo(name + fname)
696 finfo.mtime = mtime
696 finfo.mtime = mtime
697 finfo.size = len(rcont)
697 finfo.size = len(rcont)
698 finfo.mode = mff[fname] and 0755 or 0644
698 finfo.mode = mff[fname] and 0755 or 0644
699 tf.addfile(finfo, StringIO.StringIO(rcont))
699 tf.addfile(finfo, StringIO.StringIO(rcont))
700 tf.close()
700 tf.close()
701
701
702 # add tags to things
702 # add tags to things
703 # tags -> list of changesets corresponding to tags
703 # tags -> list of changesets corresponding to tags
704 # find tag, changeset, file
704 # find tag, changeset, file
705
705
706 def run(self, req=hgrequest()):
706 def run(self, req=hgrequest()):
707 def header(**map):
707 def header(**map):
708 yield self.t("header", **map)
708 yield self.t("header", **map)
709
709
710 def footer(**map):
710 def footer(**map):
711 yield self.t("footer", **map)
711 yield self.t("footer", **map)
712
712
713 self.refresh()
713 self.refresh()
714
714
715 t = self.repo.ui.config("web", "templates", templatepath())
715 t = self.repo.ui.config("web", "templates", templatepath())
716 m = os.path.join(t, "map")
716 m = os.path.join(t, "map")
717 style = self.repo.ui.config("web", "style", "")
717 style = self.repo.ui.config("web", "style", "")
718 if req.form.has_key('style'):
718 if req.form.has_key('style'):
719 style = req.form['style'][0]
719 style = req.form['style'][0]
720 if style:
720 if style:
721 b = os.path.basename("map-" + style)
721 b = os.path.basename("map-" + style)
722 p = os.path.join(t, b)
722 p = os.path.join(t, b)
723 if os.path.isfile(p):
723 if os.path.isfile(p):
724 m = p
724 m = p
725
725
726 port = req.env["SERVER_PORT"]
726 port = req.env["SERVER_PORT"]
727 port = port != "80" and (":" + port) or ""
727 port = port != "80" and (":" + port) or ""
728 uri = req.env["REQUEST_URI"]
728 uri = req.env["REQUEST_URI"]
729 if "?" in uri:
729 if "?" in uri:
730 uri = uri.split("?")[0]
730 uri = uri.split("?")[0]
731 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
731 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
732 if not self.reponame:
732 if not self.reponame:
733 self.reponame = (self.repo.ui.config("web", "name")
733 self.reponame = (self.repo.ui.config("web", "name")
734 or uri.strip('/') or self.repo.root)
734 or uri.strip('/') or self.repo.root)
735
735
736 self.t = templater(m, common_filters,
736 self.t = templater(m, common_filters,
737 {"url": url,
737 {"url": url,
738 "repo": self.reponame,
738 "repo": self.reponame,
739 "header": header,
739 "header": header,
740 "footer": footer,
740 "footer": footer,
741 })
741 })
742
742
743 if not req.form.has_key('cmd'):
743 if not req.form.has_key('cmd'):
744 req.form['cmd'] = [self.t.cache['default'],]
744 req.form['cmd'] = [self.t.cache['default'],]
745
745
746 if req.form['cmd'][0] == 'changelog':
746 if req.form['cmd'][0] == 'changelog':
747 c = self.repo.changelog.count() - 1
747 c = self.repo.changelog.count() - 1
748 hi = c
748 hi = c
749 if req.form.has_key('rev'):
749 if req.form.has_key('rev'):
750 hi = req.form['rev'][0]
750 hi = req.form['rev'][0]
751 try:
751 try:
752 hi = self.repo.changelog.rev(self.repo.lookup(hi))
752 hi = self.repo.changelog.rev(self.repo.lookup(hi))
753 except RepoError:
753 except RepoError:
754 req.write(self.search(hi))
754 req.write(self.search(hi))
755 return
755 return
756
756
757 req.write(self.changelog(hi))
757 req.write(self.changelog(hi))
758
758
759 elif req.form['cmd'][0] == 'changeset':
759 elif req.form['cmd'][0] == 'changeset':
760 req.write(self.changeset(req.form['node'][0]))
760 req.write(self.changeset(req.form['node'][0]))
761
761
762 elif req.form['cmd'][0] == 'manifest':
762 elif req.form['cmd'][0] == 'manifest':
763 req.write(self.manifest(req.form['manifest'][0], req.form['path'][0]))
763 req.write(self.manifest(req.form['manifest'][0], req.form['path'][0]))
764
764
765 elif req.form['cmd'][0] == 'tags':
765 elif req.form['cmd'][0] == 'tags':
766 req.write(self.tags())
766 req.write(self.tags())
767
767
768 elif req.form['cmd'][0] == 'filediff':
768 elif req.form['cmd'][0] == 'filediff':
769 req.write(self.filediff(req.form['file'][0], req.form['node'][0]))
769 req.write(self.filediff(req.form['file'][0], req.form['node'][0]))
770
770
771 elif req.form['cmd'][0] == 'file':
771 elif req.form['cmd'][0] == 'file':
772 req.write(self.filerevision(req.form['file'][0], req.form['filenode'][0]))
772 req.write(self.filerevision(req.form['file'][0], req.form['filenode'][0]))
773
773
774 elif req.form['cmd'][0] == 'annotate':
774 elif req.form['cmd'][0] == 'annotate':
775 req.write(self.fileannotate(req.form['file'][0], req.form['filenode'][0]))
775 req.write(self.fileannotate(req.form['file'][0], req.form['filenode'][0]))
776
776
777 elif req.form['cmd'][0] == 'filelog':
777 elif req.form['cmd'][0] == 'filelog':
778 req.write(self.filelog(req.form['file'][0], req.form['filenode'][0]))
778 req.write(self.filelog(req.form['file'][0], req.form['filenode'][0]))
779
779
780 elif req.form['cmd'][0] == 'heads':
780 elif req.form['cmd'][0] == 'heads':
781 req.httphdr("application/mercurial-0.1")
781 req.httphdr("application/mercurial-0.1")
782 h = self.repo.heads()
782 h = self.repo.heads()
783 req.write(" ".join(map(hex, h)) + "\n")
783 req.write(" ".join(map(hex, h)) + "\n")
784
784
785 elif req.form['cmd'][0] == 'branches':
785 elif req.form['cmd'][0] == 'branches':
786 req.httphdr("application/mercurial-0.1")
786 req.httphdr("application/mercurial-0.1")
787 nodes = []
787 nodes = []
788 if req.form.has_key('nodes'):
788 if req.form.has_key('nodes'):
789 nodes = map(bin, req.form['nodes'][0].split(" "))
789 nodes = map(bin, req.form['nodes'][0].split(" "))
790 for b in self.repo.branches(nodes):
790 for b in self.repo.branches(nodes):
791 req.write(" ".join(map(hex, b)) + "\n")
791 req.write(" ".join(map(hex, b)) + "\n")
792
792
793 elif req.form['cmd'][0] == 'between':
793 elif req.form['cmd'][0] == 'between':
794 req.httphdr("application/mercurial-0.1")
794 req.httphdr("application/mercurial-0.1")
795 nodes = []
795 nodes = []
796 if req.form.has_key('pairs'):
796 if req.form.has_key('pairs'):
797 pairs = [map(bin, p.split("-"))
797 pairs = [map(bin, p.split("-"))
798 for p in req.form['pairs'][0].split(" ")]
798 for p in req.form['pairs'][0].split(" ")]
799 for b in self.repo.between(pairs):
799 for b in self.repo.between(pairs):
800 req.write(" ".join(map(hex, b)) + "\n")
800 req.write(" ".join(map(hex, b)) + "\n")
801
801
802 elif req.form['cmd'][0] == 'changegroup':
802 elif req.form['cmd'][0] == 'changegroup':
803 req.httphdr("application/mercurial-0.1")
803 req.httphdr("application/mercurial-0.1")
804 nodes = []
804 nodes = []
805 if not self.allowpull:
805 if not self.allowpull:
806 return
806 return
807
807
808 if req.form.has_key('roots'):
808 if req.form.has_key('roots'):
809 nodes = map(bin, req.form['roots'][0].split(" "))
809 nodes = map(bin, req.form['roots'][0].split(" "))
810
810
811 z = zlib.compressobj()
811 z = zlib.compressobj()
812 f = self.repo.changegroup(nodes)
812 f = self.repo.changegroup(nodes)
813 while 1:
813 while 1:
814 chunk = f.read(4096)
814 chunk = f.read(4096)
815 if not chunk:
815 if not chunk:
816 break
816 break
817 req.write(z.compress(chunk))
817 req.write(z.compress(chunk))
818
818
819 req.write(z.flush())
819 req.write(z.flush())
820
820
821 elif req.form['cmd'][0] == 'archive':
821 elif req.form['cmd'][0] == 'archive':
822 changeset = bin(req.form['node'][0])
822 changeset = bin(req.form['node'][0])
823 type = req.form['type'][0]
823 type = req.form['type'][0]
824 if (type in self.archives and
824 if (type in self.archives and
825 self.repo.ui.configbool("web", "allow" + type, False)):
825 self.repo.ui.configbool("web", "allow" + type, False)):
826 self.archive(req, changeset, type)
826 self.archive(req, changeset, type)
827 return
827 return
828
828
829 req.write(self.t("error"))
829 req.write(self.t("error"))
830
830
831 else:
831 else:
832 req.write(self.t("error"))
832 req.write(self.t("error"))
833
833
834 def create_server(repo):
834 def create_server(repo):
835
835
836 def openlog(opt, default):
836 def openlog(opt, default):
837 if opt and opt != '-':
837 if opt and opt != '-':
838 return open(opt, 'w')
838 return open(opt, 'w')
839 return default
839 return default
840
840
841 address = repo.ui.config("web", "address", "")
841 address = repo.ui.config("web", "address", "")
842 port = int(repo.ui.config("web", "port", 8000))
842 port = int(repo.ui.config("web", "port", 8000))
843 use_ipv6 = repo.ui.configbool("web", "ipv6")
843 use_ipv6 = repo.ui.configbool("web", "ipv6")
844 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
844 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
845 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
845 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
846
846
847 import BaseHTTPServer
847 import BaseHTTPServer
848
848
849 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
849 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
850 address_family = getattr(socket, 'AF_INET6', None)
850 address_family = getattr(socket, 'AF_INET6', None)
851
851
852 def __init__(self, *args, **kwargs):
852 def __init__(self, *args, **kwargs):
853 if self.address_family is None:
853 if self.address_family is None:
854 raise RepoError('IPv6 not available on this system')
854 raise RepoError('IPv6 not available on this system')
855 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
855 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
856
856
857 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
857 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
858 def log_error(self, format, *args):
858 def log_error(self, format, *args):
859 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
859 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
860 self.log_date_time_string(),
860 self.log_date_time_string(),
861 format % args))
861 format % args))
862
862
863 def log_message(self, format, *args):
863 def log_message(self, format, *args):
864 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
864 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
865 self.log_date_time_string(),
865 self.log_date_time_string(),
866 format % args))
866 format % args))
867
867
868 def do_POST(self):
868 def do_POST(self):
869 try:
869 try:
870 self.do_hgweb()
870 self.do_hgweb()
871 except socket.error, inst:
871 except socket.error, inst:
872 if inst[0] != errno.EPIPE:
872 if inst[0] != errno.EPIPE:
873 raise
873 raise
874
874
875 def do_GET(self):
875 def do_GET(self):
876 self.do_POST()
876 self.do_POST()
877
877
878 def do_hgweb(self):
878 def do_hgweb(self):
879 query = ""
879 query = ""
880 p = self.path.find("?")
880 p = self.path.find("?")
881 if p:
881 if p:
882 query = self.path[p + 1:]
882 query = self.path[p + 1:]
883 query = query.replace('+', ' ')
883 query = query.replace('+', ' ')
884
884
885 env = {}
885 env = {}
886 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
886 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
887 env['REQUEST_METHOD'] = self.command
887 env['REQUEST_METHOD'] = self.command
888 env['SERVER_NAME'] = self.server.server_name
888 env['SERVER_NAME'] = self.server.server_name
889 env['SERVER_PORT'] = str(self.server.server_port)
889 env['SERVER_PORT'] = str(self.server.server_port)
890 env['REQUEST_URI'] = "/"
890 env['REQUEST_URI'] = "/"
891 if query:
891 if query:
892 env['QUERY_STRING'] = query
892 env['QUERY_STRING'] = query
893 host = self.address_string()
893 host = self.address_string()
894 if host != self.client_address[0]:
894 if host != self.client_address[0]:
895 env['REMOTE_HOST'] = host
895 env['REMOTE_HOST'] = host
896 env['REMOTE_ADDR'] = self.client_address[0]
896 env['REMOTE_ADDR'] = self.client_address[0]
897
897
898 if self.headers.typeheader is None:
898 if self.headers.typeheader is None:
899 env['CONTENT_TYPE'] = self.headers.type
899 env['CONTENT_TYPE'] = self.headers.type
900 else:
900 else:
901 env['CONTENT_TYPE'] = self.headers.typeheader
901 env['CONTENT_TYPE'] = self.headers.typeheader
902 length = self.headers.getheader('content-length')
902 length = self.headers.getheader('content-length')
903 if length:
903 if length:
904 env['CONTENT_LENGTH'] = length
904 env['CONTENT_LENGTH'] = length
905 accept = []
905 accept = []
906 for line in self.headers.getallmatchingheaders('accept'):
906 for line in self.headers.getallmatchingheaders('accept'):
907 if line[:1] in "\t\n\r ":
907 if line[:1] in "\t\n\r ":
908 accept.append(line.strip())
908 accept.append(line.strip())
909 else:
909 else:
910 accept = accept + line[7:].split(',')
910 accept = accept + line[7:].split(',')
911 env['HTTP_ACCEPT'] = ','.join(accept)
911 env['HTTP_ACCEPT'] = ','.join(accept)
912
912
913 save = sys.argv, sys.stderr
914 try:
915 req = hgrequest(self.rfile, self.wfile, env)
913 req = hgrequest(self.rfile, self.wfile, env)
916 sys.argv = ["hgweb.py"]
917 if '=' not in query:
918 sys.argv.append(query)
919 self.send_response(200, "Script output follows")
914 self.send_response(200, "Script output follows")
920 hg.run(req)
915 hg.run(req)
921 finally:
922 sys.argv, sys.stderr = save
923
916
924 hg = hgweb(repo)
917 hg = hgweb(repo)
925 if use_ipv6:
918 if use_ipv6:
926 return IPv6HTTPServer((address, port), hgwebhandler)
919 return IPv6HTTPServer((address, port), hgwebhandler)
927 else:
920 else:
928 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
921 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
929
922
930 def server(path, name, templates, address, port, use_ipv6=False,
923 def server(path, name, templates, address, port, use_ipv6=False,
931 accesslog=sys.stdout, errorlog=sys.stderr):
924 accesslog=sys.stdout, errorlog=sys.stderr):
932 httpd = create_server(path, name, templates, address, port, use_ipv6,
925 httpd = create_server(path, name, templates, address, port, use_ipv6,
933 accesslog, errorlog)
926 accesslog, errorlog)
934 httpd.serve_forever()
927 httpd.serve_forever()
935
928
936 # This is a stopgap
929 # This is a stopgap
937 class hgwebdir:
930 class hgwebdir:
938 def __init__(self, config):
931 def __init__(self, config):
932 def cleannames(items):
933 return [(name.strip('/'), path) for name, path in items]
934
939 if type(config) == type([]):
935 if type(config) == type([]):
940 self.repos = config
936 self.repos = cleannames(config)
941 elif type(config) == type({}):
937 elif type(config) == type({}):
942 self.repos = config.items()
938 self.repos = cleannames(config.items())
943 self.repos.sort()
939 self.repos.sort()
944 else:
940 else:
945 cp = ConfigParser.SafeConfigParser()
941 cp = ConfigParser.SafeConfigParser()
946 cp.read(config)
942 cp.read(config)
947 self.repos = cp.items("paths")
943 self.repos = cleannames(cp.items("paths"))
948 self.repos.sort()
944 self.repos.sort()
949
945
950 def run(self, req=hgrequest()):
946 def run(self, req=hgrequest()):
951 def header(**map):
947 def header(**map):
952 yield tmpl("header", **map)
948 yield tmpl("header", **map)
953
949
954 def footer(**map):
950 def footer(**map):
955 yield tmpl("footer", **map)
951 yield tmpl("footer", **map)
956
952
957 m = os.path.join(templatepath(), "map")
953 m = os.path.join(templatepath(), "map")
958 tmpl = templater(m, common_filters,
954 tmpl = templater(m, common_filters,
959 {"header": header, "footer": footer})
955 {"header": header, "footer": footer})
960
956
961 def entries(**map):
957 def entries(**map):
962 parity = 0
958 parity = 0
963 for name, path in self.repos:
959 for name, path in self.repos:
964 u = ui()
960 u = ui()
965 try:
961 try:
966 u.readconfig(file(os.path.join(path, '.hg', 'hgrc')))
962 u.readconfig(file(os.path.join(path, '.hg', 'hgrc')))
967 except IOError:
963 except IOError:
968 pass
964 pass
969 get = u.config
965 get = u.config
970
966
971 url = ('/'.join([req.env["REQUEST_URI"], name])
967 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
972 .replace("//", "/"))
968 .replace("//", "/"))
973
969
974 yield dict(contact=get("web", "contact") or
970 yield dict(contact=get("web", "contact") or
975 get("web", "author", "unknown"),
971 get("web", "author", "unknown"),
976 name=get("web", "name", name),
972 name=get("web", "name", name),
977 url=url,
973 url=url,
978 parity=parity,
974 parity=parity,
979 shortdesc=get("web", "description", "unknown"),
975 shortdesc=get("web", "description", "unknown"),
980 lastupdate=os.stat(os.path.join(path, ".hg",
976 lastupdate=os.stat(os.path.join(path, ".hg",
981 "00changelog.d")).st_mtime)
977 "00changelog.d")).st_mtime)
982
978
983 parity = 1 - parity
979 parity = 1 - parity
984
980
985 virtual = req.env.get("PATH_INFO", "").strip('/')
981 virtual = req.env.get("PATH_INFO", "").strip('/')
986 if virtual:
982 if virtual:
987 real = dict(self.repos).get(virtual)
983 real = dict(self.repos).get(virtual)
988 if real:
984 if real:
989 hgweb(real).run(req)
985 hgweb(real).run(req)
990 else:
986 else:
991 req.write(tmpl("notfound", repo=virtual))
987 req.write(tmpl("notfound", repo=virtual))
992 else:
988 else:
993 req.write(tmpl("index", entries=entries))
989 req.write(tmpl("index", entries=entries))
General Comments 0
You need to be logged in to leave comments. Login now