##// END OF EJS Templates
[hgweb] Fixed up gitweb templates...
Josef "Jeff" Sipek -
r2683:8a798185 default
parent child Browse files
Show More
@@ -1,960 +1,961
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a 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
9 import os
10 import os.path
10 import os.path
11 import mimetypes
11 import mimetypes
12 from mercurial.demandload import demandload
12 from mercurial.demandload import demandload
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone")
15 demandload(globals(), "mercurial:templater")
15 demandload(globals(), "mercurial:templater")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
17 from mercurial.node import *
17 from mercurial.node import *
18 from mercurial.i18n import gettext as _
18 from mercurial.i18n import gettext as _
19
19
20 def _up(p):
20 def _up(p):
21 if p[0] != "/":
21 if p[0] != "/":
22 p = "/" + p
22 p = "/" + p
23 if p[-1] == "/":
23 if p[-1] == "/":
24 p = p[:-1]
24 p = p[:-1]
25 up = os.path.dirname(p)
25 up = os.path.dirname(p)
26 if up == "/":
26 if up == "/":
27 return "/"
27 return "/"
28 return up + "/"
28 return up + "/"
29
29
30 class hgweb(object):
30 class hgweb(object):
31 def __init__(self, repo, name=None):
31 def __init__(self, repo, name=None):
32 if type(repo) == type(""):
32 if type(repo) == type(""):
33 self.repo = hg.repository(ui.ui(), repo)
33 self.repo = hg.repository(ui.ui(), repo)
34 else:
34 else:
35 self.repo = repo
35 self.repo = repo
36
36
37 self.mtime = -1
37 self.mtime = -1
38 self.reponame = name
38 self.reponame = name
39 self.archives = 'zip', 'gz', 'bz2'
39 self.archives = 'zip', 'gz', 'bz2'
40 self.templatepath = self.repo.ui.config("web", "templates",
40 self.templatepath = self.repo.ui.config("web", "templates",
41 templater.templatepath())
41 templater.templatepath())
42
42
43 def refresh(self):
43 def refresh(self):
44 mtime = get_mtime(self.repo.root)
44 mtime = get_mtime(self.repo.root)
45 if mtime != self.mtime:
45 if mtime != self.mtime:
46 self.mtime = mtime
46 self.mtime = mtime
47 self.repo = hg.repository(self.repo.ui, self.repo.root)
47 self.repo = hg.repository(self.repo.ui, self.repo.root)
48 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
48 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
49 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
49 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
50 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
50 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
51
51
52 def archivelist(self, nodeid):
52 def archivelist(self, nodeid):
53 allowed = self.repo.ui.configlist("web", "allow_archive")
53 allowed = self.repo.ui.configlist("web", "allow_archive")
54 for i in self.archives:
54 for i in self.archives:
55 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
55 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
56 yield {"type" : i, "node" : nodeid, "url": ""}
56 yield {"type" : i, "node" : nodeid, "url": ""}
57
57
58 def listfiles(self, files, mf):
58 def listfiles(self, files, mf):
59 for f in files[:self.maxfiles]:
59 for f in files[:self.maxfiles]:
60 yield self.t("filenodelink", node=hex(mf[f]), file=f)
60 yield self.t("filenodelink", node=hex(mf[f]), file=f)
61 if len(files) > self.maxfiles:
61 if len(files) > self.maxfiles:
62 yield self.t("fileellipses")
62 yield self.t("fileellipses")
63
63
64 def listfilediffs(self, files, changeset):
64 def listfilediffs(self, files, changeset):
65 for f in files[:self.maxfiles]:
65 for f in files[:self.maxfiles]:
66 yield self.t("filedifflink", node=hex(changeset), file=f)
66 yield self.t("filedifflink", node=hex(changeset), file=f)
67 if len(files) > self.maxfiles:
67 if len(files) > self.maxfiles:
68 yield self.t("fileellipses")
68 yield self.t("fileellipses")
69
69
70 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
70 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
71 if not rev:
71 if not rev:
72 rev = lambda x: ""
72 rev = lambda x: ""
73 siblings = [s for s in siblings if s != nullid]
73 siblings = [s for s in siblings if s != nullid]
74 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
74 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
75 return
75 return
76 for s in siblings:
76 for s in siblings:
77 yield dict(node=hex(s), rev=rev(s), **args)
77 yield dict(node=hex(s), rev=rev(s), **args)
78
78
79 def renamelink(self, fl, node):
79 def renamelink(self, fl, node):
80 r = fl.renamed(node)
80 r = fl.renamed(node)
81 if r:
81 if r:
82 return [dict(file=r[0], node=hex(r[1]))]
82 return [dict(file=r[0], node=hex(r[1]))]
83 return []
83 return []
84
84
85 def showtag(self, t1, node=nullid, **args):
85 def showtag(self, t1, node=nullid, **args):
86 for t in self.repo.nodetags(node):
86 for t in self.repo.nodetags(node):
87 yield self.t(t1, tag=t, **args)
87 yield self.t(t1, tag=t, **args)
88
88
89 def diff(self, node1, node2, files):
89 def diff(self, node1, node2, files):
90 def filterfiles(filters, files):
90 def filterfiles(filters, files):
91 l = [x for x in files if x in filters]
91 l = [x for x in files if x in filters]
92
92
93 for t in filters:
93 for t in filters:
94 if t and t[-1] != os.sep:
94 if t and t[-1] != os.sep:
95 t += os.sep
95 t += os.sep
96 l += [x for x in files if x.startswith(t)]
96 l += [x for x in files if x.startswith(t)]
97 return l
97 return l
98
98
99 parity = [0]
99 parity = [0]
100 def diffblock(diff, f, fn):
100 def diffblock(diff, f, fn):
101 yield self.t("diffblock",
101 yield self.t("diffblock",
102 lines=prettyprintlines(diff),
102 lines=prettyprintlines(diff),
103 parity=parity[0],
103 parity=parity[0],
104 file=f,
104 file=f,
105 filenode=hex(fn or nullid))
105 filenode=hex(fn or nullid))
106 parity[0] = 1 - parity[0]
106 parity[0] = 1 - parity[0]
107
107
108 def prettyprintlines(diff):
108 def prettyprintlines(diff):
109 for l in diff.splitlines(1):
109 for l in diff.splitlines(1):
110 if l.startswith('+'):
110 if l.startswith('+'):
111 yield self.t("difflineplus", line=l)
111 yield self.t("difflineplus", line=l)
112 elif l.startswith('-'):
112 elif l.startswith('-'):
113 yield self.t("difflineminus", line=l)
113 yield self.t("difflineminus", line=l)
114 elif l.startswith('@'):
114 elif l.startswith('@'):
115 yield self.t("difflineat", line=l)
115 yield self.t("difflineat", line=l)
116 else:
116 else:
117 yield self.t("diffline", line=l)
117 yield self.t("diffline", line=l)
118
118
119 r = self.repo
119 r = self.repo
120 cl = r.changelog
120 cl = r.changelog
121 mf = r.manifest
121 mf = r.manifest
122 change1 = cl.read(node1)
122 change1 = cl.read(node1)
123 change2 = cl.read(node2)
123 change2 = cl.read(node2)
124 mmap1 = mf.read(change1[0])
124 mmap1 = mf.read(change1[0])
125 mmap2 = mf.read(change2[0])
125 mmap2 = mf.read(change2[0])
126 date1 = util.datestr(change1[2])
126 date1 = util.datestr(change1[2])
127 date2 = util.datestr(change2[2])
127 date2 = util.datestr(change2[2])
128
128
129 modified, added, removed, deleted, unknown = r.changes(node1, node2)
129 modified, added, removed, deleted, unknown = r.changes(node1, node2)
130 if files:
130 if files:
131 modified, added, removed = map(lambda x: filterfiles(files, x),
131 modified, added, removed = map(lambda x: filterfiles(files, x),
132 (modified, added, removed))
132 (modified, added, removed))
133
133
134 diffopts = self.repo.ui.diffopts()
134 diffopts = self.repo.ui.diffopts()
135 showfunc = diffopts['showfunc']
135 showfunc = diffopts['showfunc']
136 ignorews = diffopts['ignorews']
136 ignorews = diffopts['ignorews']
137 ignorewsamount = diffopts['ignorewsamount']
137 ignorewsamount = diffopts['ignorewsamount']
138 ignoreblanklines = diffopts['ignoreblanklines']
138 ignoreblanklines = diffopts['ignoreblanklines']
139 for f in modified:
139 for f in modified:
140 to = r.file(f).read(mmap1[f])
140 to = r.file(f).read(mmap1[f])
141 tn = r.file(f).read(mmap2[f])
141 tn = r.file(f).read(mmap2[f])
142 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
142 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
143 showfunc=showfunc, ignorews=ignorews,
143 showfunc=showfunc, ignorews=ignorews,
144 ignorewsamount=ignorewsamount,
144 ignorewsamount=ignorewsamount,
145 ignoreblanklines=ignoreblanklines), f, tn)
145 ignoreblanklines=ignoreblanklines), f, tn)
146 for f in added:
146 for f in added:
147 to = None
147 to = None
148 tn = r.file(f).read(mmap2[f])
148 tn = r.file(f).read(mmap2[f])
149 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
149 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
150 showfunc=showfunc, ignorews=ignorews,
150 showfunc=showfunc, ignorews=ignorews,
151 ignorewsamount=ignorewsamount,
151 ignorewsamount=ignorewsamount,
152 ignoreblanklines=ignoreblanklines), f, tn)
152 ignoreblanklines=ignoreblanklines), f, tn)
153 for f in removed:
153 for f in removed:
154 to = r.file(f).read(mmap1[f])
154 to = r.file(f).read(mmap1[f])
155 tn = None
155 tn = None
156 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
156 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
157 showfunc=showfunc, ignorews=ignorews,
157 showfunc=showfunc, ignorews=ignorews,
158 ignorewsamount=ignorewsamount,
158 ignorewsamount=ignorewsamount,
159 ignoreblanklines=ignoreblanklines), f, tn)
159 ignoreblanklines=ignoreblanklines), f, tn)
160
160
161 def changelog(self, pos):
161 def changelog(self, pos):
162 def changenav(**map):
162 def changenav(**map):
163 def seq(factor, maxchanges=None):
163 def seq(factor, maxchanges=None):
164 if maxchanges:
164 if maxchanges:
165 yield maxchanges
165 yield maxchanges
166 if maxchanges >= 20 and maxchanges <= 40:
166 if maxchanges >= 20 and maxchanges <= 40:
167 yield 50
167 yield 50
168 else:
168 else:
169 yield 1 * factor
169 yield 1 * factor
170 yield 3 * factor
170 yield 3 * factor
171 for f in seq(factor * 10):
171 for f in seq(factor * 10):
172 yield f
172 yield f
173
173
174 l = []
174 l = []
175 last = 0
175 last = 0
176 for f in seq(1, self.maxchanges):
176 for f in seq(1, self.maxchanges):
177 if f < self.maxchanges or f <= last:
177 if f < self.maxchanges or f <= last:
178 continue
178 continue
179 if f > count:
179 if f > count:
180 break
180 break
181 last = f
181 last = f
182 r = "%d" % f
182 r = "%d" % f
183 if pos + f < count:
183 if pos + f < count:
184 l.append(("+" + r, pos + f))
184 l.append(("+" + r, pos + f))
185 if pos - f >= 0:
185 if pos - f >= 0:
186 l.insert(0, ("-" + r, pos - f))
186 l.insert(0, ("-" + r, pos - f))
187
187
188 yield {"rev": 0, "label": "(0)"}
188 yield {"rev": 0, "label": "(0)"}
189
189
190 for label, rev in l:
190 for label, rev in l:
191 yield {"label": label, "rev": rev}
191 yield {"label": label, "rev": rev}
192
192
193 yield {"label": "tip", "rev": "tip"}
193 yield {"label": "tip", "rev": "tip"}
194
194
195 def changelist(**map):
195 def changelist(**map):
196 parity = (start - end) & 1
196 parity = (start - end) & 1
197 cl = self.repo.changelog
197 cl = self.repo.changelog
198 l = [] # build a list in forward order for efficiency
198 l = [] # build a list in forward order for efficiency
199 for i in range(start, end):
199 for i in range(start, end):
200 n = cl.node(i)
200 n = cl.node(i)
201 changes = cl.read(n)
201 changes = cl.read(n)
202 hn = hex(n)
202 hn = hex(n)
203
203
204 l.insert(0, {"parity": parity,
204 l.insert(0, {"parity": parity,
205 "author": changes[1],
205 "author": changes[1],
206 "parent": self.siblings(cl.parents(n), cl.rev,
206 "parent": self.siblings(cl.parents(n), cl.rev,
207 cl.rev(n) - 1),
207 cl.rev(n) - 1),
208 "child": self.siblings(cl.children(n), cl.rev,
208 "child": self.siblings(cl.children(n), cl.rev,
209 cl.rev(n) + 1),
209 cl.rev(n) + 1),
210 "changelogtag": self.showtag("changelogtag",n),
210 "changelogtag": self.showtag("changelogtag",n),
211 "manifest": hex(changes[0]),
211 "manifest": hex(changes[0]),
212 "desc": changes[4],
212 "desc": changes[4],
213 "date": changes[2],
213 "date": changes[2],
214 "files": self.listfilediffs(changes[3], n),
214 "files": self.listfilediffs(changes[3], n),
215 "rev": i,
215 "rev": i,
216 "node": hn})
216 "node": hn})
217 parity = 1 - parity
217 parity = 1 - parity
218
218
219 for e in l:
219 for e in l:
220 yield e
220 yield e
221
221
222 cl = self.repo.changelog
222 cl = self.repo.changelog
223 mf = cl.read(cl.tip())[0]
223 mf = cl.read(cl.tip())[0]
224 count = cl.count()
224 count = cl.count()
225 start = max(0, pos - self.maxchanges + 1)
225 start = max(0, pos - self.maxchanges + 1)
226 end = min(count, start + self.maxchanges)
226 end = min(count, start + self.maxchanges)
227 pos = end - 1
227 pos = end - 1
228
228
229 yield self.t('changelog',
229 yield self.t('changelog',
230 changenav=changenav,
230 changenav=changenav,
231 manifest=hex(mf),
231 manifest=hex(mf),
232 rev=pos, changesets=count, entries=changelist,
232 rev=pos, changesets=count, entries=changelist,
233 archives=self.archivelist("tip"))
233 archives=self.archivelist("tip"))
234
234
235 def search(self, query):
235 def search(self, query):
236
236
237 def changelist(**map):
237 def changelist(**map):
238 cl = self.repo.changelog
238 cl = self.repo.changelog
239 count = 0
239 count = 0
240 qw = query.lower().split()
240 qw = query.lower().split()
241
241
242 def revgen():
242 def revgen():
243 for i in range(cl.count() - 1, 0, -100):
243 for i in range(cl.count() - 1, 0, -100):
244 l = []
244 l = []
245 for j in range(max(0, i - 100), i):
245 for j in range(max(0, i - 100), i):
246 n = cl.node(j)
246 n = cl.node(j)
247 changes = cl.read(n)
247 changes = cl.read(n)
248 l.append((n, j, changes))
248 l.append((n, j, changes))
249 l.reverse()
249 l.reverse()
250 for e in l:
250 for e in l:
251 yield e
251 yield e
252
252
253 for n, i, changes in revgen():
253 for n, i, changes in revgen():
254 miss = 0
254 miss = 0
255 for q in qw:
255 for q in qw:
256 if not (q in changes[1].lower() or
256 if not (q in changes[1].lower() or
257 q in changes[4].lower() or
257 q in changes[4].lower() or
258 q in " ".join(changes[3][:20]).lower()):
258 q in " ".join(changes[3][:20]).lower()):
259 miss = 1
259 miss = 1
260 break
260 break
261 if miss:
261 if miss:
262 continue
262 continue
263
263
264 count += 1
264 count += 1
265 hn = hex(n)
265 hn = hex(n)
266
266
267 yield self.t('searchentry',
267 yield self.t('searchentry',
268 parity=count & 1,
268 parity=count & 1,
269 author=changes[1],
269 author=changes[1],
270 parent=self.siblings(cl.parents(n), cl.rev),
270 parent=self.siblings(cl.parents(n), cl.rev),
271 child=self.siblings(cl.children(n), cl.rev),
271 child=self.siblings(cl.children(n), cl.rev),
272 changelogtag=self.showtag("changelogtag",n),
272 changelogtag=self.showtag("changelogtag",n),
273 manifest=hex(changes[0]),
273 manifest=hex(changes[0]),
274 desc=changes[4],
274 desc=changes[4],
275 date=changes[2],
275 date=changes[2],
276 files=self.listfilediffs(changes[3], n),
276 files=self.listfilediffs(changes[3], n),
277 rev=i,
277 rev=i,
278 node=hn)
278 node=hn)
279
279
280 if count >= self.maxchanges:
280 if count >= self.maxchanges:
281 break
281 break
282
282
283 cl = self.repo.changelog
283 cl = self.repo.changelog
284 mf = cl.read(cl.tip())[0]
284 mf = cl.read(cl.tip())[0]
285
285
286 yield self.t('search',
286 yield self.t('search',
287 query=query,
287 query=query,
288 manifest=hex(mf),
288 manifest=hex(mf),
289 entries=changelist)
289 entries=changelist)
290
290
291 def changeset(self, nodeid):
291 def changeset(self, nodeid):
292 cl = self.repo.changelog
292 cl = self.repo.changelog
293 n = self.repo.lookup(nodeid)
293 n = self.repo.lookup(nodeid)
294 nodeid = hex(n)
294 nodeid = hex(n)
295 changes = cl.read(n)
295 changes = cl.read(n)
296 p1 = cl.parents(n)[0]
296 p1 = cl.parents(n)[0]
297
297
298 files = []
298 files = []
299 mf = self.repo.manifest.read(changes[0])
299 mf = self.repo.manifest.read(changes[0])
300 for f in changes[3]:
300 for f in changes[3]:
301 files.append(self.t("filenodelink",
301 files.append(self.t("filenodelink",
302 filenode=hex(mf.get(f, nullid)), file=f))
302 filenode=hex(mf.get(f, nullid)), file=f))
303
303
304 def diff(**map):
304 def diff(**map):
305 yield self.diff(p1, n, None)
305 yield self.diff(p1, n, None)
306
306
307 yield self.t('changeset',
307 yield self.t('changeset',
308 diff=diff,
308 diff=diff,
309 rev=cl.rev(n),
309 rev=cl.rev(n),
310 node=nodeid,
310 node=nodeid,
311 parent=self.siblings(cl.parents(n), cl.rev),
311 parent=self.siblings(cl.parents(n), cl.rev),
312 child=self.siblings(cl.children(n), cl.rev),
312 child=self.siblings(cl.children(n), cl.rev),
313 changesettag=self.showtag("changesettag",n),
313 changesettag=self.showtag("changesettag",n),
314 manifest=hex(changes[0]),
314 manifest=hex(changes[0]),
315 author=changes[1],
315 author=changes[1],
316 desc=changes[4],
316 desc=changes[4],
317 date=changes[2],
317 date=changes[2],
318 files=files,
318 files=files,
319 archives=self.archivelist(nodeid))
319 archives=self.archivelist(nodeid))
320
320
321 def filelog(self, f, filenode):
321 def filelog(self, f, filenode):
322 cl = self.repo.changelog
322 cl = self.repo.changelog
323 fl = self.repo.file(f)
323 fl = self.repo.file(f)
324 filenode = hex(fl.lookup(filenode))
324 filenode = hex(fl.lookup(filenode))
325 count = fl.count()
325 count = fl.count()
326
326
327 def entries(**map):
327 def entries(**map):
328 l = []
328 l = []
329 parity = (count - 1) & 1
329 parity = (count - 1) & 1
330
330
331 for i in range(count):
331 for i in range(count):
332 n = fl.node(i)
332 n = fl.node(i)
333 lr = fl.linkrev(n)
333 lr = fl.linkrev(n)
334 cn = cl.node(lr)
334 cn = cl.node(lr)
335 cs = cl.read(cl.node(lr))
335 cs = cl.read(cl.node(lr))
336
336
337 l.insert(0, {"parity": parity,
337 l.insert(0, {"parity": parity,
338 "filenode": hex(n),
338 "filenode": hex(n),
339 "filerev": i,
339 "filerev": i,
340 "file": f,
340 "file": f,
341 "node": hex(cn),
341 "node": hex(cn),
342 "author": cs[1],
342 "author": cs[1],
343 "date": cs[2],
343 "date": cs[2],
344 "rename": self.renamelink(fl, n),
344 "rename": self.renamelink(fl, n),
345 "parent": self.siblings(fl.parents(n),
345 "parent": self.siblings(fl.parents(n),
346 fl.rev, file=f),
346 fl.rev, file=f),
347 "child": self.siblings(fl.children(n),
347 "child": self.siblings(fl.children(n),
348 fl.rev, file=f),
348 fl.rev, file=f),
349 "desc": cs[4]})
349 "desc": cs[4]})
350 parity = 1 - parity
350 parity = 1 - parity
351
351
352 for e in l:
352 for e in l:
353 yield e
353 yield e
354
354
355 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
355 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
356
356
357 def filerevision(self, f, node):
357 def filerevision(self, f, node):
358 fl = self.repo.file(f)
358 fl = self.repo.file(f)
359 n = fl.lookup(node)
359 n = fl.lookup(node)
360 node = hex(n)
360 node = hex(n)
361 text = fl.read(n)
361 text = fl.read(n)
362 changerev = fl.linkrev(n)
362 changerev = fl.linkrev(n)
363 cl = self.repo.changelog
363 cl = self.repo.changelog
364 cn = cl.node(changerev)
364 cn = cl.node(changerev)
365 cs = cl.read(cn)
365 cs = cl.read(cn)
366 mfn = cs[0]
366 mfn = cs[0]
367
367
368 mt = mimetypes.guess_type(f)[0]
368 mt = mimetypes.guess_type(f)[0]
369 rawtext = text
369 rawtext = text
370 if util.binary(text):
370 if util.binary(text):
371 mt = mt or 'application/octet-stream'
371 mt = mt or 'application/octet-stream'
372 text = "(binary:%s)" % mt
372 text = "(binary:%s)" % mt
373 mt = mt or 'text/plain'
373 mt = mt or 'text/plain'
374
374
375 def lines():
375 def lines():
376 for l, t in enumerate(text.splitlines(1)):
376 for l, t in enumerate(text.splitlines(1)):
377 yield {"line": t,
377 yield {"line": t,
378 "linenumber": "% 6d" % (l + 1),
378 "linenumber": "% 6d" % (l + 1),
379 "parity": l & 1}
379 "parity": l & 1}
380
380
381 yield self.t("filerevision",
381 yield self.t("filerevision",
382 file=f,
382 file=f,
383 filenode=node,
383 filenode=node,
384 path=_up(f),
384 path=_up(f),
385 text=lines(),
385 text=lines(),
386 raw=rawtext,
386 raw=rawtext,
387 mimetype=mt,
387 mimetype=mt,
388 rev=changerev,
388 rev=changerev,
389 node=hex(cn),
389 node=hex(cn),
390 manifest=hex(mfn),
390 manifest=hex(mfn),
391 author=cs[1],
391 author=cs[1],
392 date=cs[2],
392 date=cs[2],
393 parent=self.siblings(fl.parents(n), fl.rev, file=f),
393 parent=self.siblings(fl.parents(n), fl.rev, file=f),
394 child=self.siblings(fl.children(n), fl.rev, file=f),
394 child=self.siblings(fl.children(n), fl.rev, file=f),
395 rename=self.renamelink(fl, n),
395 rename=self.renamelink(fl, n),
396 permissions=self.repo.manifest.readflags(mfn)[f])
396 permissions=self.repo.manifest.readflags(mfn)[f])
397
397
398 def fileannotate(self, f, node):
398 def fileannotate(self, f, node):
399 bcache = {}
399 bcache = {}
400 ncache = {}
400 ncache = {}
401 fl = self.repo.file(f)
401 fl = self.repo.file(f)
402 n = fl.lookup(node)
402 n = fl.lookup(node)
403 node = hex(n)
403 node = hex(n)
404 changerev = fl.linkrev(n)
404 changerev = fl.linkrev(n)
405
405
406 cl = self.repo.changelog
406 cl = self.repo.changelog
407 cn = cl.node(changerev)
407 cn = cl.node(changerev)
408 cs = cl.read(cn)
408 cs = cl.read(cn)
409 mfn = cs[0]
409 mfn = cs[0]
410
410
411 def annotate(**map):
411 def annotate(**map):
412 parity = 1
412 parity = 1
413 last = None
413 last = None
414 for r, l in fl.annotate(n):
414 for r, l in fl.annotate(n):
415 try:
415 try:
416 cnode = ncache[r]
416 cnode = ncache[r]
417 except KeyError:
417 except KeyError:
418 cnode = ncache[r] = self.repo.changelog.node(r)
418 cnode = ncache[r] = self.repo.changelog.node(r)
419
419
420 try:
420 try:
421 name = bcache[r]
421 name = bcache[r]
422 except KeyError:
422 except KeyError:
423 cl = self.repo.changelog.read(cnode)
423 cl = self.repo.changelog.read(cnode)
424 bcache[r] = name = self.repo.ui.shortuser(cl[1])
424 bcache[r] = name = self.repo.ui.shortuser(cl[1])
425
425
426 if last != cnode:
426 if last != cnode:
427 parity = 1 - parity
427 parity = 1 - parity
428 last = cnode
428 last = cnode
429
429
430 yield {"parity": parity,
430 yield {"parity": parity,
431 "node": hex(cnode),
431 "node": hex(cnode),
432 "rev": r,
432 "rev": r,
433 "author": name,
433 "author": name,
434 "file": f,
434 "file": f,
435 "line": l}
435 "line": l}
436
436
437 yield self.t("fileannotate",
437 yield self.t("fileannotate",
438 file=f,
438 file=f,
439 filenode=node,
439 filenode=node,
440 annotate=annotate,
440 annotate=annotate,
441 path=_up(f),
441 path=_up(f),
442 rev=changerev,
442 rev=changerev,
443 node=hex(cn),
443 node=hex(cn),
444 manifest=hex(mfn),
444 manifest=hex(mfn),
445 author=cs[1],
445 author=cs[1],
446 date=cs[2],
446 date=cs[2],
447 rename=self.renamelink(fl, n),
447 rename=self.renamelink(fl, n),
448 parent=self.siblings(fl.parents(n), fl.rev, file=f),
448 parent=self.siblings(fl.parents(n), fl.rev, file=f),
449 child=self.siblings(fl.children(n), fl.rev, file=f),
449 child=self.siblings(fl.children(n), fl.rev, file=f),
450 permissions=self.repo.manifest.readflags(mfn)[f])
450 permissions=self.repo.manifest.readflags(mfn)[f])
451
451
452 def manifest(self, mnode, path):
452 def manifest(self, mnode, path):
453 man = self.repo.manifest
453 man = self.repo.manifest
454 mn = man.lookup(mnode)
454 mn = man.lookup(mnode)
455 mnode = hex(mn)
455 mnode = hex(mn)
456 mf = man.read(mn)
456 mf = man.read(mn)
457 rev = man.rev(mn)
457 rev = man.rev(mn)
458 changerev = man.linkrev(mn)
458 changerev = man.linkrev(mn)
459 node = self.repo.changelog.node(changerev)
459 node = self.repo.changelog.node(changerev)
460 mff = man.readflags(mn)
460 mff = man.readflags(mn)
461
461
462 files = {}
462 files = {}
463
463
464 p = path[1:]
464 p = path[1:]
465 if p and p[-1] != "/":
465 if p and p[-1] != "/":
466 p += "/"
466 p += "/"
467 l = len(p)
467 l = len(p)
468
468
469 for f,n in mf.items():
469 for f,n in mf.items():
470 if f[:l] != p:
470 if f[:l] != p:
471 continue
471 continue
472 remain = f[l:]
472 remain = f[l:]
473 if "/" in remain:
473 if "/" in remain:
474 short = remain[:remain.index("/") + 1] # bleah
474 short = remain[:remain.index("/") + 1] # bleah
475 files[short] = (f, None)
475 files[short] = (f, None)
476 else:
476 else:
477 short = os.path.basename(remain)
477 short = os.path.basename(remain)
478 files[short] = (f, n)
478 files[short] = (f, n)
479
479
480 def filelist(**map):
480 def filelist(**map):
481 parity = 0
481 parity = 0
482 fl = files.keys()
482 fl = files.keys()
483 fl.sort()
483 fl.sort()
484 for f in fl:
484 for f in fl:
485 full, fnode = files[f]
485 full, fnode = files[f]
486 if not fnode:
486 if not fnode:
487 continue
487 continue
488
488
489 yield {"file": full,
489 yield {"file": full,
490 "manifest": mnode,
490 "manifest": mnode,
491 "filenode": hex(fnode),
491 "filenode": hex(fnode),
492 "parity": parity,
492 "parity": parity,
493 "basename": f,
493 "basename": f,
494 "permissions": mff[full]}
494 "permissions": mff[full]}
495 parity = 1 - parity
495 parity = 1 - parity
496
496
497 def dirlist(**map):
497 def dirlist(**map):
498 parity = 0
498 parity = 0
499 fl = files.keys()
499 fl = files.keys()
500 fl.sort()
500 fl.sort()
501 for f in fl:
501 for f in fl:
502 full, fnode = files[f]
502 full, fnode = files[f]
503 if fnode:
503 if fnode:
504 continue
504 continue
505
505
506 yield {"parity": parity,
506 yield {"parity": parity,
507 "path": os.path.join(path, f),
507 "path": os.path.join(path, f),
508 "manifest": mnode,
508 "manifest": mnode,
509 "basename": f[:-1]}
509 "basename": f[:-1]}
510 parity = 1 - parity
510 parity = 1 - parity
511
511
512 yield self.t("manifest",
512 yield self.t("manifest",
513 manifest=mnode,
513 manifest=mnode,
514 rev=rev,
514 rev=rev,
515 node=hex(node),
515 node=hex(node),
516 path=path,
516 path=path,
517 up=_up(path),
517 up=_up(path),
518 fentries=filelist,
518 fentries=filelist,
519 dentries=dirlist,
519 dentries=dirlist,
520 archives=self.archivelist(hex(node)))
520 archives=self.archivelist(hex(node)))
521
521
522 def tags(self):
522 def tags(self):
523 cl = self.repo.changelog
523 cl = self.repo.changelog
524 mf = cl.read(cl.tip())[0]
524 mf = cl.read(cl.tip())[0]
525
525
526 i = self.repo.tagslist()
526 i = self.repo.tagslist()
527 i.reverse()
527 i.reverse()
528
528
529 def entries(notip=False, **map):
529 def entries(notip=False, **map):
530 parity = 0
530 parity = 0
531 for k,n in i:
531 for k,n in i:
532 if notip and k == "tip": continue
532 if notip and k == "tip": continue
533 yield {"parity": parity,
533 yield {"parity": parity,
534 "tag": k,
534 "tag": k,
535 "tagmanifest": hex(cl.read(n)[0]),
535 "tagmanifest": hex(cl.read(n)[0]),
536 "date": cl.read(n)[2],
536 "date": cl.read(n)[2],
537 "node": hex(n)}
537 "node": hex(n)}
538 parity = 1 - parity
538 parity = 1 - parity
539
539
540 yield self.t("tags",
540 yield self.t("tags",
541 manifest=hex(mf),
541 manifest=hex(mf),
542 entries=lambda **x: entries(False, **x),
542 entries=lambda **x: entries(False, **x),
543 entriesnotip=lambda **x: entries(True, **x))
543 entriesnotip=lambda **x: entries(True, **x))
544
544
545 def summary(self):
545 def summary(self):
546 cl = self.repo.changelog
546 cl = self.repo.changelog
547 mf = cl.read(cl.tip())[0]
547 mf = cl.read(cl.tip())[0]
548
548
549 i = self.repo.tagslist()
549 i = self.repo.tagslist()
550 i.reverse()
550 i.reverse()
551
551
552 def tagentries(**map):
552 def tagentries(**map):
553 parity = 0
553 parity = 0
554 count = 0
554 count = 0
555 for k,n in i:
555 for k,n in i:
556 if k == "tip": # skip tip
556 if k == "tip": # skip tip
557 continue;
557 continue;
558
558
559 count += 1
559 count += 1
560 if count > 10: # limit to 10 tags
560 if count > 10: # limit to 10 tags
561 break;
561 break;
562
562
563 c = cl.read(n)
563 c = cl.read(n)
564 m = c[0]
564 m = c[0]
565 t = c[2]
565 t = c[2]
566
566
567 yield self.t("tagentry",
567 yield self.t("tagentry",
568 parity = parity,
568 parity = parity,
569 tag = k,
569 tag = k,
570 node = hex(n),
570 node = hex(n),
571 date = t,
571 date = t,
572 tagmanifest = hex(m))
572 tagmanifest = hex(m))
573 parity = 1 - parity
573 parity = 1 - parity
574
574
575 def changelist(**map):
575 def changelist(**map):
576 parity = 0
576 parity = 0
577 cl = self.repo.changelog
577 cl = self.repo.changelog
578 l = [] # build a list in forward order for efficiency
578 l = [] # build a list in forward order for efficiency
579 for i in range(start, end):
579 for i in range(start, end):
580 n = cl.node(i)
580 n = cl.node(i)
581 changes = cl.read(n)
581 changes = cl.read(n)
582 hn = hex(n)
582 hn = hex(n)
583 t = changes[2]
583 t = changes[2]
584
584
585 l.insert(0, self.t(
585 l.insert(0, self.t(
586 'shortlogentry',
586 'shortlogentry',
587 parity = parity,
587 parity = parity,
588 author = changes[1],
588 author = changes[1],
589 manifest = hex(changes[0]),
589 manifest = hex(changes[0]),
590 desc = changes[4],
590 desc = changes[4],
591 date = t,
591 date = t,
592 rev = i,
592 rev = i,
593 node = hn))
593 node = hn))
594 parity = 1 - parity
594 parity = 1 - parity
595
595
596 yield l
596 yield l
597
597
598 cl = self.repo.changelog
598 cl = self.repo.changelog
599 mf = cl.read(cl.tip())[0]
599 mf = cl.read(cl.tip())[0]
600 count = cl.count()
600 count = cl.count()
601 start = max(0, count - self.maxchanges)
601 start = max(0, count - self.maxchanges)
602 end = min(count, start + self.maxchanges)
602 end = min(count, start + self.maxchanges)
603
603
604 yield self.t("summary",
604 yield self.t("summary",
605 desc = self.repo.ui.config("web", "description", "unknown"),
605 desc = self.repo.ui.config("web", "description", "unknown"),
606 owner = (self.repo.ui.config("ui", "username") or # preferred
606 owner = (self.repo.ui.config("ui", "username") or # preferred
607 self.repo.ui.config("web", "contact") or # deprecated
607 self.repo.ui.config("web", "contact") or # deprecated
608 self.repo.ui.config("web", "author", "unknown")), # also
608 self.repo.ui.config("web", "author", "unknown")), # also
609 lastchange = (0, 0), # FIXME
609 lastchange = (0, 0), # FIXME
610 manifest = hex(mf),
610 manifest = hex(mf),
611 tags = tagentries,
611 tags = tagentries,
612 shortlog = changelist)
612 shortlog = changelist,
613 archives=self.archivelist("tip"))
613
614
614 def filediff(self, file, changeset):
615 def filediff(self, file, changeset):
615 cl = self.repo.changelog
616 cl = self.repo.changelog
616 n = self.repo.lookup(changeset)
617 n = self.repo.lookup(changeset)
617 changeset = hex(n)
618 changeset = hex(n)
618 p1 = cl.parents(n)[0]
619 p1 = cl.parents(n)[0]
619 cs = cl.read(n)
620 cs = cl.read(n)
620 mf = self.repo.manifest.read(cs[0])
621 mf = self.repo.manifest.read(cs[0])
621
622
622 def diff(**map):
623 def diff(**map):
623 yield self.diff(p1, n, [file])
624 yield self.diff(p1, n, [file])
624
625
625 yield self.t("filediff",
626 yield self.t("filediff",
626 file=file,
627 file=file,
627 filenode=hex(mf.get(file, nullid)),
628 filenode=hex(mf.get(file, nullid)),
628 node=changeset,
629 node=changeset,
629 rev=self.repo.changelog.rev(n),
630 rev=self.repo.changelog.rev(n),
630 parent=self.siblings(cl.parents(n), cl.rev),
631 parent=self.siblings(cl.parents(n), cl.rev),
631 child=self.siblings(cl.children(n), cl.rev),
632 child=self.siblings(cl.children(n), cl.rev),
632 diff=diff)
633 diff=diff)
633
634
634 archive_specs = {
635 archive_specs = {
635 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
636 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
636 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
637 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
637 'zip': ('application/zip', 'zip', '.zip', None),
638 'zip': ('application/zip', 'zip', '.zip', None),
638 }
639 }
639
640
640 def archive(self, req, cnode, type_):
641 def archive(self, req, cnode, type_):
641 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
642 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
642 name = "%s-%s" % (reponame, short(cnode))
643 name = "%s-%s" % (reponame, short(cnode))
643 mimetype, artype, extension, encoding = self.archive_specs[type_]
644 mimetype, artype, extension, encoding = self.archive_specs[type_]
644 headers = [('Content-type', mimetype),
645 headers = [('Content-type', mimetype),
645 ('Content-disposition', 'attachment; filename=%s%s' %
646 ('Content-disposition', 'attachment; filename=%s%s' %
646 (name, extension))]
647 (name, extension))]
647 if encoding:
648 if encoding:
648 headers.append(('Content-encoding', encoding))
649 headers.append(('Content-encoding', encoding))
649 req.header(headers)
650 req.header(headers)
650 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
651 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
651
652
652 # add tags to things
653 # add tags to things
653 # tags -> list of changesets corresponding to tags
654 # tags -> list of changesets corresponding to tags
654 # find tag, changeset, file
655 # find tag, changeset, file
655
656
656 def cleanpath(self, path):
657 def cleanpath(self, path):
657 p = util.normpath(path)
658 p = util.normpath(path)
658 if p[:2] == "..":
659 if p[:2] == "..":
659 raise Exception("suspicious path")
660 raise Exception("suspicious path")
660 return p
661 return p
661
662
662 def run(self):
663 def run(self):
663 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
664 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
664 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
665 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
665 import mercurial.hgweb.wsgicgi as wsgicgi
666 import mercurial.hgweb.wsgicgi as wsgicgi
666 from request import wsgiapplication
667 from request import wsgiapplication
667 def make_web_app():
668 def make_web_app():
668 return self
669 return self
669 wsgicgi.launch(wsgiapplication(make_web_app))
670 wsgicgi.launch(wsgiapplication(make_web_app))
670
671
671 def run_wsgi(self, req):
672 def run_wsgi(self, req):
672 def header(**map):
673 def header(**map):
673 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
674 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
674 msg = mimetools.Message(header_file, 0)
675 msg = mimetools.Message(header_file, 0)
675 req.header(msg.items())
676 req.header(msg.items())
676 yield header_file.read()
677 yield header_file.read()
677
678
678 def rawfileheader(**map):
679 def rawfileheader(**map):
679 req.header([('Content-type', map['mimetype']),
680 req.header([('Content-type', map['mimetype']),
680 ('Content-disposition', 'filename=%s' % map['file']),
681 ('Content-disposition', 'filename=%s' % map['file']),
681 ('Content-length', str(len(map['raw'])))])
682 ('Content-length', str(len(map['raw'])))])
682 yield ''
683 yield ''
683
684
684 def footer(**map):
685 def footer(**map):
685 yield self.t("footer",
686 yield self.t("footer",
686 motd=self.repo.ui.config("web", "motd", ""),
687 motd=self.repo.ui.config("web", "motd", ""),
687 **map)
688 **map)
688
689
689 def expand_form(form):
690 def expand_form(form):
690 shortcuts = {
691 shortcuts = {
691 'cl': [('cmd', ['changelog']), ('rev', None)],
692 'cl': [('cmd', ['changelog']), ('rev', None)],
692 'cs': [('cmd', ['changeset']), ('node', None)],
693 'cs': [('cmd', ['changeset']), ('node', None)],
693 'f': [('cmd', ['file']), ('filenode', None)],
694 'f': [('cmd', ['file']), ('filenode', None)],
694 'fl': [('cmd', ['filelog']), ('filenode', None)],
695 'fl': [('cmd', ['filelog']), ('filenode', None)],
695 'fd': [('cmd', ['filediff']), ('node', None)],
696 'fd': [('cmd', ['filediff']), ('node', None)],
696 'fa': [('cmd', ['annotate']), ('filenode', None)],
697 'fa': [('cmd', ['annotate']), ('filenode', None)],
697 'mf': [('cmd', ['manifest']), ('manifest', None)],
698 'mf': [('cmd', ['manifest']), ('manifest', None)],
698 'ca': [('cmd', ['archive']), ('node', None)],
699 'ca': [('cmd', ['archive']), ('node', None)],
699 'tags': [('cmd', ['tags'])],
700 'tags': [('cmd', ['tags'])],
700 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
701 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
701 'static': [('cmd', ['static']), ('file', None)]
702 'static': [('cmd', ['static']), ('file', None)]
702 }
703 }
703
704
704 for k in shortcuts.iterkeys():
705 for k in shortcuts.iterkeys():
705 if form.has_key(k):
706 if form.has_key(k):
706 for name, value in shortcuts[k]:
707 for name, value in shortcuts[k]:
707 if value is None:
708 if value is None:
708 value = form[k]
709 value = form[k]
709 form[name] = value
710 form[name] = value
710 del form[k]
711 del form[k]
711
712
712 self.refresh()
713 self.refresh()
713
714
714 expand_form(req.form)
715 expand_form(req.form)
715
716
716 m = os.path.join(self.templatepath, "map")
717 m = os.path.join(self.templatepath, "map")
717 style = self.repo.ui.config("web", "style", "")
718 style = self.repo.ui.config("web", "style", "")
718 if req.form.has_key('style'):
719 if req.form.has_key('style'):
719 style = req.form['style'][0]
720 style = req.form['style'][0]
720 if style:
721 if style:
721 b = os.path.basename("map-" + style)
722 b = os.path.basename("map-" + style)
722 p = os.path.join(self.templatepath, b)
723 p = os.path.join(self.templatepath, b)
723 if os.path.isfile(p):
724 if os.path.isfile(p):
724 m = p
725 m = p
725
726
726 port = req.env["SERVER_PORT"]
727 port = req.env["SERVER_PORT"]
727 port = port != "80" and (":" + port) or ""
728 port = port != "80" and (":" + port) or ""
728 uri = req.env["REQUEST_URI"]
729 uri = req.env["REQUEST_URI"]
729 if "?" in uri:
730 if "?" in uri:
730 uri = uri.split("?")[0]
731 uri = uri.split("?")[0]
731 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
732 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
732 if not self.reponame:
733 if not self.reponame:
733 self.reponame = (self.repo.ui.config("web", "name")
734 self.reponame = (self.repo.ui.config("web", "name")
734 or uri.strip('/') or self.repo.root)
735 or uri.strip('/') or self.repo.root)
735
736
736 self.t = templater.templater(m, templater.common_filters,
737 self.t = templater.templater(m, templater.common_filters,
737 defaults={"url": url,
738 defaults={"url": url,
738 "repo": self.reponame,
739 "repo": self.reponame,
739 "header": header,
740 "header": header,
740 "footer": footer,
741 "footer": footer,
741 "rawfileheader": rawfileheader,
742 "rawfileheader": rawfileheader,
742 })
743 })
743
744
744 if not req.form.has_key('cmd'):
745 if not req.form.has_key('cmd'):
745 req.form['cmd'] = [self.t.cache['default'],]
746 req.form['cmd'] = [self.t.cache['default'],]
746
747
747 cmd = req.form['cmd'][0]
748 cmd = req.form['cmd'][0]
748
749
749 method = getattr(self, 'do_' + cmd, None)
750 method = getattr(self, 'do_' + cmd, None)
750 if method:
751 if method:
751 method(req)
752 method(req)
752 else:
753 else:
753 req.write(self.t("error"))
754 req.write(self.t("error"))
754
755
755 def do_changelog(self, req):
756 def do_changelog(self, req):
756 hi = self.repo.changelog.count() - 1
757 hi = self.repo.changelog.count() - 1
757 if req.form.has_key('rev'):
758 if req.form.has_key('rev'):
758 hi = req.form['rev'][0]
759 hi = req.form['rev'][0]
759 try:
760 try:
760 hi = self.repo.changelog.rev(self.repo.lookup(hi))
761 hi = self.repo.changelog.rev(self.repo.lookup(hi))
761 except hg.RepoError:
762 except hg.RepoError:
762 req.write(self.search(hi)) # XXX redirect to 404 page?
763 req.write(self.search(hi)) # XXX redirect to 404 page?
763 return
764 return
764
765
765 req.write(self.changelog(hi))
766 req.write(self.changelog(hi))
766
767
767 def do_changeset(self, req):
768 def do_changeset(self, req):
768 req.write(self.changeset(req.form['node'][0]))
769 req.write(self.changeset(req.form['node'][0]))
769
770
770 def do_manifest(self, req):
771 def do_manifest(self, req):
771 req.write(self.manifest(req.form['manifest'][0],
772 req.write(self.manifest(req.form['manifest'][0],
772 self.cleanpath(req.form['path'][0])))
773 self.cleanpath(req.form['path'][0])))
773
774
774 def do_tags(self, req):
775 def do_tags(self, req):
775 req.write(self.tags())
776 req.write(self.tags())
776
777
777 def do_summary(self, req):
778 def do_summary(self, req):
778 req.write(self.summary())
779 req.write(self.summary())
779
780
780 def do_filediff(self, req):
781 def do_filediff(self, req):
781 req.write(self.filediff(self.cleanpath(req.form['file'][0]),
782 req.write(self.filediff(self.cleanpath(req.form['file'][0]),
782 req.form['node'][0]))
783 req.form['node'][0]))
783
784
784 def do_file(self, req):
785 def do_file(self, req):
785 req.write(self.filerevision(self.cleanpath(req.form['file'][0]),
786 req.write(self.filerevision(self.cleanpath(req.form['file'][0]),
786 req.form['filenode'][0]))
787 req.form['filenode'][0]))
787
788
788 def do_annotate(self, req):
789 def do_annotate(self, req):
789 req.write(self.fileannotate(self.cleanpath(req.form['file'][0]),
790 req.write(self.fileannotate(self.cleanpath(req.form['file'][0]),
790 req.form['filenode'][0]))
791 req.form['filenode'][0]))
791
792
792 def do_filelog(self, req):
793 def do_filelog(self, req):
793 req.write(self.filelog(self.cleanpath(req.form['file'][0]),
794 req.write(self.filelog(self.cleanpath(req.form['file'][0]),
794 req.form['filenode'][0]))
795 req.form['filenode'][0]))
795
796
796 def do_heads(self, req):
797 def do_heads(self, req):
797 resp = " ".join(map(hex, self.repo.heads())) + "\n"
798 resp = " ".join(map(hex, self.repo.heads())) + "\n"
798 req.httphdr("application/mercurial-0.1", length=len(resp))
799 req.httphdr("application/mercurial-0.1", length=len(resp))
799 req.write(resp)
800 req.write(resp)
800
801
801 def do_branches(self, req):
802 def do_branches(self, req):
802 nodes = []
803 nodes = []
803 if req.form.has_key('nodes'):
804 if req.form.has_key('nodes'):
804 nodes = map(bin, req.form['nodes'][0].split(" "))
805 nodes = map(bin, req.form['nodes'][0].split(" "))
805 resp = cStringIO.StringIO()
806 resp = cStringIO.StringIO()
806 for b in self.repo.branches(nodes):
807 for b in self.repo.branches(nodes):
807 resp.write(" ".join(map(hex, b)) + "\n")
808 resp.write(" ".join(map(hex, b)) + "\n")
808 resp = resp.getvalue()
809 resp = resp.getvalue()
809 req.httphdr("application/mercurial-0.1", length=len(resp))
810 req.httphdr("application/mercurial-0.1", length=len(resp))
810 req.write(resp)
811 req.write(resp)
811
812
812 def do_between(self, req):
813 def do_between(self, req):
813 nodes = []
814 nodes = []
814 if req.form.has_key('pairs'):
815 if req.form.has_key('pairs'):
815 pairs = [map(bin, p.split("-"))
816 pairs = [map(bin, p.split("-"))
816 for p in req.form['pairs'][0].split(" ")]
817 for p in req.form['pairs'][0].split(" ")]
817 resp = cStringIO.StringIO()
818 resp = cStringIO.StringIO()
818 for b in self.repo.between(pairs):
819 for b in self.repo.between(pairs):
819 resp.write(" ".join(map(hex, b)) + "\n")
820 resp.write(" ".join(map(hex, b)) + "\n")
820 resp = resp.getvalue()
821 resp = resp.getvalue()
821 req.httphdr("application/mercurial-0.1", length=len(resp))
822 req.httphdr("application/mercurial-0.1", length=len(resp))
822 req.write(resp)
823 req.write(resp)
823
824
824 def do_changegroup(self, req):
825 def do_changegroup(self, req):
825 req.httphdr("application/mercurial-0.1")
826 req.httphdr("application/mercurial-0.1")
826 nodes = []
827 nodes = []
827 if not self.allowpull:
828 if not self.allowpull:
828 return
829 return
829
830
830 if req.form.has_key('roots'):
831 if req.form.has_key('roots'):
831 nodes = map(bin, req.form['roots'][0].split(" "))
832 nodes = map(bin, req.form['roots'][0].split(" "))
832
833
833 z = zlib.compressobj()
834 z = zlib.compressobj()
834 f = self.repo.changegroup(nodes, 'serve')
835 f = self.repo.changegroup(nodes, 'serve')
835 while 1:
836 while 1:
836 chunk = f.read(4096)
837 chunk = f.read(4096)
837 if not chunk:
838 if not chunk:
838 break
839 break
839 req.write(z.compress(chunk))
840 req.write(z.compress(chunk))
840
841
841 req.write(z.flush())
842 req.write(z.flush())
842
843
843 def do_archive(self, req):
844 def do_archive(self, req):
844 changeset = self.repo.lookup(req.form['node'][0])
845 changeset = self.repo.lookup(req.form['node'][0])
845 type_ = req.form['type'][0]
846 type_ = req.form['type'][0]
846 allowed = self.repo.ui.configlist("web", "allow_archive")
847 allowed = self.repo.ui.configlist("web", "allow_archive")
847 if (type_ in self.archives and (type_ in allowed or
848 if (type_ in self.archives and (type_ in allowed or
848 self.repo.ui.configbool("web", "allow" + type_, False))):
849 self.repo.ui.configbool("web", "allow" + type_, False))):
849 self.archive(req, changeset, type_)
850 self.archive(req, changeset, type_)
850 return
851 return
851
852
852 req.write(self.t("error"))
853 req.write(self.t("error"))
853
854
854 def do_static(self, req):
855 def do_static(self, req):
855 fname = req.form['file'][0]
856 fname = req.form['file'][0]
856 static = self.repo.ui.config("web", "static",
857 static = self.repo.ui.config("web", "static",
857 os.path.join(self.templatepath,
858 os.path.join(self.templatepath,
858 "static"))
859 "static"))
859 req.write(staticfile(static, fname, req)
860 req.write(staticfile(static, fname, req)
860 or self.t("error", error="%r not found" % fname))
861 or self.t("error", error="%r not found" % fname))
861
862
862 def do_capabilities(self, req):
863 def do_capabilities(self, req):
863 caps = ['unbundle']
864 caps = ['unbundle']
864 if self.repo.ui.configbool('server', 'uncompressed'):
865 if self.repo.ui.configbool('server', 'uncompressed'):
865 caps.append('stream=%d' % self.repo.revlogversion)
866 caps.append('stream=%d' % self.repo.revlogversion)
866 resp = ' '.join(caps)
867 resp = ' '.join(caps)
867 req.httphdr("application/mercurial-0.1", length=len(resp))
868 req.httphdr("application/mercurial-0.1", length=len(resp))
868 req.write(resp)
869 req.write(resp)
869
870
870 def check_perm(self, req, op, default):
871 def check_perm(self, req, op, default):
871 '''check permission for operation based on user auth.
872 '''check permission for operation based on user auth.
872 return true if op allowed, else false.
873 return true if op allowed, else false.
873 default is policy to use if no config given.'''
874 default is policy to use if no config given.'''
874
875
875 user = req.env.get('REMOTE_USER')
876 user = req.env.get('REMOTE_USER')
876
877
877 deny = self.repo.ui.configlist('web', 'deny_' + op)
878 deny = self.repo.ui.configlist('web', 'deny_' + op)
878 if deny and (not user or deny == ['*'] or user in deny):
879 if deny and (not user or deny == ['*'] or user in deny):
879 return False
880 return False
880
881
881 allow = self.repo.ui.configlist('web', 'allow_' + op)
882 allow = self.repo.ui.configlist('web', 'allow_' + op)
882 return (allow and (allow == ['*'] or user in allow)) or default
883 return (allow and (allow == ['*'] or user in allow)) or default
883
884
884 def do_unbundle(self, req):
885 def do_unbundle(self, req):
885 def bail(response, headers={}):
886 def bail(response, headers={}):
886 length = int(req.env['CONTENT_LENGTH'])
887 length = int(req.env['CONTENT_LENGTH'])
887 for s in util.filechunkiter(req, limit=length):
888 for s in util.filechunkiter(req, limit=length):
888 # drain incoming bundle, else client will not see
889 # drain incoming bundle, else client will not see
889 # response when run outside cgi script
890 # response when run outside cgi script
890 pass
891 pass
891 req.httphdr("application/mercurial-0.1", headers=headers)
892 req.httphdr("application/mercurial-0.1", headers=headers)
892 req.write('0\n')
893 req.write('0\n')
893 req.write(response)
894 req.write(response)
894
895
895 # require ssl by default, auth info cannot be sniffed and
896 # require ssl by default, auth info cannot be sniffed and
896 # replayed
897 # replayed
897 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
898 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
898 if ssl_req and not req.env.get('HTTPS'):
899 if ssl_req and not req.env.get('HTTPS'):
899 bail(_('ssl required\n'))
900 bail(_('ssl required\n'))
900 return
901 return
901
902
902 # do not allow push unless explicitly allowed
903 # do not allow push unless explicitly allowed
903 if not self.check_perm(req, 'push', False):
904 if not self.check_perm(req, 'push', False):
904 bail(_('push not authorized\n'),
905 bail(_('push not authorized\n'),
905 headers={'status': '401 Unauthorized'})
906 headers={'status': '401 Unauthorized'})
906 return
907 return
907
908
908 req.httphdr("application/mercurial-0.1")
909 req.httphdr("application/mercurial-0.1")
909
910
910 their_heads = req.form['heads'][0].split(' ')
911 their_heads = req.form['heads'][0].split(' ')
911
912
912 def check_heads():
913 def check_heads():
913 heads = map(hex, self.repo.heads())
914 heads = map(hex, self.repo.heads())
914 return their_heads == [hex('force')] or their_heads == heads
915 return their_heads == [hex('force')] or their_heads == heads
915
916
916 # fail early if possible
917 # fail early if possible
917 if not check_heads():
918 if not check_heads():
918 bail(_('unsynced changes\n'))
919 bail(_('unsynced changes\n'))
919 return
920 return
920
921
921 # do not lock repo until all changegroup data is
922 # do not lock repo until all changegroup data is
922 # streamed. save to temporary file.
923 # streamed. save to temporary file.
923
924
924 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
925 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
925 fp = os.fdopen(fd, 'wb+')
926 fp = os.fdopen(fd, 'wb+')
926 try:
927 try:
927 length = int(req.env['CONTENT_LENGTH'])
928 length = int(req.env['CONTENT_LENGTH'])
928 for s in util.filechunkiter(req, limit=length):
929 for s in util.filechunkiter(req, limit=length):
929 fp.write(s)
930 fp.write(s)
930
931
931 lock = self.repo.lock()
932 lock = self.repo.lock()
932 try:
933 try:
933 if not check_heads():
934 if not check_heads():
934 req.write('0\n')
935 req.write('0\n')
935 req.write(_('unsynced changes\n'))
936 req.write(_('unsynced changes\n'))
936 return
937 return
937
938
938 fp.seek(0)
939 fp.seek(0)
939
940
940 # send addchangegroup output to client
941 # send addchangegroup output to client
941
942
942 old_stdout = sys.stdout
943 old_stdout = sys.stdout
943 sys.stdout = cStringIO.StringIO()
944 sys.stdout = cStringIO.StringIO()
944
945
945 try:
946 try:
946 ret = self.repo.addchangegroup(fp, 'serve')
947 ret = self.repo.addchangegroup(fp, 'serve')
947 finally:
948 finally:
948 val = sys.stdout.getvalue()
949 val = sys.stdout.getvalue()
949 sys.stdout = old_stdout
950 sys.stdout = old_stdout
950 req.write('%d\n' % ret)
951 req.write('%d\n' % ret)
951 req.write(val)
952 req.write(val)
952 finally:
953 finally:
953 lock.release()
954 lock.release()
954 finally:
955 finally:
955 fp.close()
956 fp.close()
956 os.unlink(tempname)
957 os.unlink(tempname)
957
958
958 def do_stream_out(self, req):
959 def do_stream_out(self, req):
959 req.httphdr("application/mercurial-0.1")
960 req.httphdr("application/mercurial-0.1")
960 streamclone.stream_out(self.repo, req)
961 streamclone.stream_out(self.repo, req)
@@ -1,34 +1,34
1 #header#
1 #header#
2 <title>#repo|escape#: Changelog</title>
2 <title>#repo|escape#: Changelog</title>
3 <link rel="alternate" type="application/rss+xml"
3 <link rel="alternate" type="application/rss+xml"
4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 </head>
5 </head>
6 <body>
6 <body>
7
7
8 <div class="page_header">
8 <div class="page_header">
9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / changelog
9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / changelog
10 </div>
10 </div>
11
11
12 <form action="#">
12 <form action="#">
13 <div class="search">
13 <div class="search">
14 <input type="hidden" name="repo" value="#repo|escape#" />
14 <input type="hidden" name="repo" value="#repo|escape#" />
15 <input type="hidden" name="style" value="gitweb" />
15 <input type="hidden" name="style" value="gitweb" />
16 <input type="hidden" name="cmd" value="changelog" />
16 <input type="hidden" name="cmd" value="changelog" />
17 <input type="text" name="rev" />
17 <input type="text" name="rev" />
18 </div>
18 </div>
19 </form>
19 </form>
20 </div>
20 </div>
21
21
22 <div class="page_nav">
22 <div class="page_nav">
23 <a href="?cmd=summary;style=gitweb">summary</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
23 <a href="?cmd=summary;style=gitweb">summary</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#<br/>
24 <br/>
24 <br/>
25 #changenav%naventry#<br/>
25 #changenav%naventry#<br/>
26 </div>
26 </div>
27
27
28 #entries%changelogentry#
28 #entries%changelogentry#
29
29
30 <div class="page_nav">
30 <div class="page_nav">
31 #changenav%naventry#<br/>
31 #changenav%naventry#<br/>
32 </div>
32 </div>
33
33
34 #footer#
34 #footer#
@@ -1,13 +1,14
1 #header#
1 #header#
2
2
3 <div class="page_nav">
3 <div class="page_nav">
4 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">log</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
4 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">log</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#
5 <br/>
5
6
6 #changenav%naventry#<br/>
7 #changenav%naventry#<br/>
7 </div>
8 </div>
8
9
9 <table cellspacing="0">
10 <table cellspacing="0">
10 #entries#
11 #entries#
11 </table>
12 </table>
12
13
13 #footer#
14 #footer#
@@ -1,34 +1,35
1 #header#
1 #header#
2 <title>#repo|escape#: Summary</title>
2 <title>#repo|escape#: Summary</title>
3 <link rel="alternate" type="application/rss+xml"
3 <link rel="alternate" type="application/rss+xml"
4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 </head>
5 </head>
6 <body>
6 <body>
7
7
8 <div class="page_header">
8 <div class="page_header">
9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / summary
9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / summary
10 </div>
10 </div>
11 <div class="page_nav">
11 <div class="page_nav">
12 summary | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
12 summary | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#
13 <br/>
13 </div>
14 </div>
14
15
15 <div class="title">&nbsp;</div>
16 <div class="title">&nbsp;</div>
16 <table cellspacing="0">
17 <table cellspacing="0">
17 <tr><td>description</td><td>#desc#</td></tr>
18 <tr><td>description</td><td>#desc#</td></tr>
18 <tr><td>owner</td><td>#owner|escape#</td></tr>
19 <tr><td>owner</td><td>#owner|escape#</td></tr>
19 <!-- <tr><td>last change</td><td>#lastchange|rfc822date#</td></tr> -->
20 <!-- <tr><td>last change</td><td>#lastchange|rfc822date#</td></tr> -->
20 </table>
21 </table>
21
22
22 <div><a class="title" href="?cmd=changelog;style=gitweb">changes</a></div>
23 <div><a class="title" href="?cmd=changelog;style=gitweb">changes</a></div>
23 <table cellspacing="0">
24 <table cellspacing="0">
24 #shortlog#
25 #shortlog#
25 <tr class="light"><td colspan="3"><a class="list" href="?cmd=changelog;style=gitweb">...</a></td></tr>
26 <tr class="light"><td colspan="3"><a class="list" href="?cmd=changelog;style=gitweb">...</a></td></tr>
26 </table>
27 </table>
27
28
28 <div><a class="title" href="?cmd=tags;style=gitweb">tags</a></div>
29 <div><a class="title" href="?cmd=tags;style=gitweb">tags</a></div>
29 <table cellspacing="0">
30 <table cellspacing="0">
30 #tags#
31 #tags#
31 <tr class="light"><td colspan="3"><a class="list" href="?cmd=tags;style=gitweb">...</a></td></tr>
32 <tr class="light"><td colspan="3"><a class="list" href="?cmd=tags;style=gitweb">...</a></td></tr>
32 </table>
33 </table>
33
34
34 #footer#
35 #footer#
General Comments 0
You need to be logged in to leave comments. Login now