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