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