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