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