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