##// END OF EJS Templates
hgweb_mod.archive(): Use 'key' instead of builtin 'id'.
Thomas Arendsen Hein -
r4669:96e096fe default
parent child Browse files
Show More
@@ -1,1179 +1,1179 b''
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-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 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, mimetypes, re, zlib, mimetools, cStringIO, sys
9 import os, mimetypes, re, zlib, mimetools, cStringIO, sys
10 import tempfile, urllib, bz2
10 import tempfile, urllib, bz2
11 from mercurial.node import *
11 from mercurial.node import *
12 from mercurial.i18n import gettext as _
12 from mercurial.i18n import gettext as _
13 from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
13 from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
14 from mercurial import revlog, templater
14 from mercurial import revlog, templater
15 from common import get_mtime, staticfile, style_map, paritygen
15 from common import get_mtime, staticfile, style_map, paritygen
16
16
17 def _up(p):
17 def _up(p):
18 if p[0] != "/":
18 if p[0] != "/":
19 p = "/" + p
19 p = "/" + p
20 if p[-1] == "/":
20 if p[-1] == "/":
21 p = p[:-1]
21 p = p[:-1]
22 up = os.path.dirname(p)
22 up = os.path.dirname(p)
23 if up == "/":
23 if up == "/":
24 return "/"
24 return "/"
25 return up + "/"
25 return up + "/"
26
26
27 def revnavgen(pos, pagelen, limit, nodefunc):
27 def revnavgen(pos, pagelen, limit, nodefunc):
28 def seq(factor, limit=None):
28 def seq(factor, limit=None):
29 if limit:
29 if limit:
30 yield limit
30 yield limit
31 if limit >= 20 and limit <= 40:
31 if limit >= 20 and limit <= 40:
32 yield 50
32 yield 50
33 else:
33 else:
34 yield 1 * factor
34 yield 1 * factor
35 yield 3 * factor
35 yield 3 * factor
36 for f in seq(factor * 10):
36 for f in seq(factor * 10):
37 yield f
37 yield f
38
38
39 def nav(**map):
39 def nav(**map):
40 l = []
40 l = []
41 last = 0
41 last = 0
42 for f in seq(1, pagelen):
42 for f in seq(1, pagelen):
43 if f < pagelen or f <= last:
43 if f < pagelen or f <= last:
44 continue
44 continue
45 if f > limit:
45 if f > limit:
46 break
46 break
47 last = f
47 last = f
48 if pos + f < limit:
48 if pos + f < limit:
49 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
49 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
50 if pos - f >= 0:
50 if pos - f >= 0:
51 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
51 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
52
52
53 try:
53 try:
54 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
54 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
55
55
56 for label, node in l:
56 for label, node in l:
57 yield {"label": label, "node": node}
57 yield {"label": label, "node": node}
58
58
59 yield {"label": "tip", "node": "tip"}
59 yield {"label": "tip", "node": "tip"}
60 except hg.RepoError:
60 except hg.RepoError:
61 pass
61 pass
62
62
63 return nav
63 return nav
64
64
65 class hgweb(object):
65 class hgweb(object):
66 def __init__(self, repo, name=None):
66 def __init__(self, repo, name=None):
67 if type(repo) == type(""):
67 if type(repo) == type(""):
68 self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
68 self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
69 else:
69 else:
70 self.repo = repo
70 self.repo = repo
71
71
72 self.mtime = -1
72 self.mtime = -1
73 self.reponame = name
73 self.reponame = name
74 self.archives = 'zip', 'gz', 'bz2'
74 self.archives = 'zip', 'gz', 'bz2'
75 self.stripecount = 1
75 self.stripecount = 1
76 # a repo owner may set web.templates in .hg/hgrc to get any file
76 # a repo owner may set web.templates in .hg/hgrc to get any file
77 # readable by the user running the CGI script
77 # readable by the user running the CGI script
78 self.templatepath = self.config("web", "templates",
78 self.templatepath = self.config("web", "templates",
79 templater.templatepath(),
79 templater.templatepath(),
80 untrusted=False)
80 untrusted=False)
81
81
82 # The CGI scripts are often run by a user different from the repo owner.
82 # The CGI scripts are often run by a user different from the repo owner.
83 # Trust the settings from the .hg/hgrc files by default.
83 # Trust the settings from the .hg/hgrc files by default.
84 def config(self, section, name, default=None, untrusted=True):
84 def config(self, section, name, default=None, untrusted=True):
85 return self.repo.ui.config(section, name, default,
85 return self.repo.ui.config(section, name, default,
86 untrusted=untrusted)
86 untrusted=untrusted)
87
87
88 def configbool(self, section, name, default=False, untrusted=True):
88 def configbool(self, section, name, default=False, untrusted=True):
89 return self.repo.ui.configbool(section, name, default,
89 return self.repo.ui.configbool(section, name, default,
90 untrusted=untrusted)
90 untrusted=untrusted)
91
91
92 def configlist(self, section, name, default=None, untrusted=True):
92 def configlist(self, section, name, default=None, untrusted=True):
93 return self.repo.ui.configlist(section, name, default,
93 return self.repo.ui.configlist(section, name, default,
94 untrusted=untrusted)
94 untrusted=untrusted)
95
95
96 def refresh(self):
96 def refresh(self):
97 mtime = get_mtime(self.repo.root)
97 mtime = get_mtime(self.repo.root)
98 if mtime != self.mtime:
98 if mtime != self.mtime:
99 self.mtime = mtime
99 self.mtime = mtime
100 self.repo = hg.repository(self.repo.ui, self.repo.root)
100 self.repo = hg.repository(self.repo.ui, self.repo.root)
101 self.maxchanges = int(self.config("web", "maxchanges", 10))
101 self.maxchanges = int(self.config("web", "maxchanges", 10))
102 self.stripecount = int(self.config("web", "stripes", 1))
102 self.stripecount = int(self.config("web", "stripes", 1))
103 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
103 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
104 self.maxfiles = int(self.config("web", "maxfiles", 10))
104 self.maxfiles = int(self.config("web", "maxfiles", 10))
105 self.allowpull = self.configbool("web", "allowpull", True)
105 self.allowpull = self.configbool("web", "allowpull", True)
106
106
107 def archivelist(self, nodeid):
107 def archivelist(self, nodeid):
108 allowed = self.configlist("web", "allow_archive")
108 allowed = self.configlist("web", "allow_archive")
109 for i, spec in self.archive_specs.iteritems():
109 for i, spec in self.archive_specs.iteritems():
110 if i in allowed or self.configbool("web", "allow" + i):
110 if i in allowed or self.configbool("web", "allow" + i):
111 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
111 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
112
112
113 def listfilediffs(self, files, changeset):
113 def listfilediffs(self, files, changeset):
114 for f in files[:self.maxfiles]:
114 for f in files[:self.maxfiles]:
115 yield self.t("filedifflink", node=hex(changeset), file=f)
115 yield self.t("filedifflink", node=hex(changeset), file=f)
116 if len(files) > self.maxfiles:
116 if len(files) > self.maxfiles:
117 yield self.t("fileellipses")
117 yield self.t("fileellipses")
118
118
119 def siblings(self, siblings=[], hiderev=None, **args):
119 def siblings(self, siblings=[], hiderev=None, **args):
120 siblings = [s for s in siblings if s.node() != nullid]
120 siblings = [s for s in siblings if s.node() != nullid]
121 if len(siblings) == 1 and siblings[0].rev() == hiderev:
121 if len(siblings) == 1 and siblings[0].rev() == hiderev:
122 return
122 return
123 for s in siblings:
123 for s in siblings:
124 d = {'node': hex(s.node()), 'rev': s.rev()}
124 d = {'node': hex(s.node()), 'rev': s.rev()}
125 if hasattr(s, 'path'):
125 if hasattr(s, 'path'):
126 d['file'] = s.path()
126 d['file'] = s.path()
127 d.update(args)
127 d.update(args)
128 yield d
128 yield d
129
129
130 def renamelink(self, fl, node):
130 def renamelink(self, fl, node):
131 r = fl.renamed(node)
131 r = fl.renamed(node)
132 if r:
132 if r:
133 return [dict(file=r[0], node=hex(r[1]))]
133 return [dict(file=r[0], node=hex(r[1]))]
134 return []
134 return []
135
135
136 def nodetagsdict(self, node):
136 def nodetagsdict(self, node):
137 return [{"name": i} for i in self.repo.nodetags(node)]
137 return [{"name": i} for i in self.repo.nodetags(node)]
138
138
139 def nodebranchdict(self, ctx):
139 def nodebranchdict(self, ctx):
140 branches = []
140 branches = []
141 branch = ctx.branch()
141 branch = ctx.branch()
142 if self.repo.branchtags()[branch] == ctx.node():
142 if self.repo.branchtags()[branch] == ctx.node():
143 branches.append({"name": branch})
143 branches.append({"name": branch})
144 return branches
144 return branches
145
145
146 def showtag(self, t1, node=nullid, **args):
146 def showtag(self, t1, node=nullid, **args):
147 for t in self.repo.nodetags(node):
147 for t in self.repo.nodetags(node):
148 yield self.t(t1, tag=t, **args)
148 yield self.t(t1, tag=t, **args)
149
149
150 def diff(self, node1, node2, files):
150 def diff(self, node1, node2, files):
151 def filterfiles(filters, files):
151 def filterfiles(filters, files):
152 l = [x for x in files if x in filters]
152 l = [x for x in files if x in filters]
153
153
154 for t in filters:
154 for t in filters:
155 if t and t[-1] != os.sep:
155 if t and t[-1] != os.sep:
156 t += os.sep
156 t += os.sep
157 l += [x for x in files if x.startswith(t)]
157 l += [x for x in files if x.startswith(t)]
158 return l
158 return l
159
159
160 parity = paritygen(self.stripecount)
160 parity = paritygen(self.stripecount)
161 def diffblock(diff, f, fn):
161 def diffblock(diff, f, fn):
162 yield self.t("diffblock",
162 yield self.t("diffblock",
163 lines=prettyprintlines(diff),
163 lines=prettyprintlines(diff),
164 parity=parity.next(),
164 parity=parity.next(),
165 file=f,
165 file=f,
166 filenode=hex(fn or nullid))
166 filenode=hex(fn or nullid))
167
167
168 def prettyprintlines(diff):
168 def prettyprintlines(diff):
169 for l in diff.splitlines(1):
169 for l in diff.splitlines(1):
170 if l.startswith('+'):
170 if l.startswith('+'):
171 yield self.t("difflineplus", line=l)
171 yield self.t("difflineplus", line=l)
172 elif l.startswith('-'):
172 elif l.startswith('-'):
173 yield self.t("difflineminus", line=l)
173 yield self.t("difflineminus", line=l)
174 elif l.startswith('@'):
174 elif l.startswith('@'):
175 yield self.t("difflineat", line=l)
175 yield self.t("difflineat", line=l)
176 else:
176 else:
177 yield self.t("diffline", line=l)
177 yield self.t("diffline", line=l)
178
178
179 r = self.repo
179 r = self.repo
180 c1 = r.changectx(node1)
180 c1 = r.changectx(node1)
181 c2 = r.changectx(node2)
181 c2 = r.changectx(node2)
182 date1 = util.datestr(c1.date())
182 date1 = util.datestr(c1.date())
183 date2 = util.datestr(c2.date())
183 date2 = util.datestr(c2.date())
184
184
185 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
185 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
186 if files:
186 if files:
187 modified, added, removed = map(lambda x: filterfiles(files, x),
187 modified, added, removed = map(lambda x: filterfiles(files, x),
188 (modified, added, removed))
188 (modified, added, removed))
189
189
190 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
190 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
191 for f in modified:
191 for f in modified:
192 to = c1.filectx(f).data()
192 to = c1.filectx(f).data()
193 tn = c2.filectx(f).data()
193 tn = c2.filectx(f).data()
194 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
194 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
195 opts=diffopts), f, tn)
195 opts=diffopts), f, tn)
196 for f in added:
196 for f in added:
197 to = None
197 to = None
198 tn = c2.filectx(f).data()
198 tn = c2.filectx(f).data()
199 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
199 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
200 opts=diffopts), f, tn)
200 opts=diffopts), f, tn)
201 for f in removed:
201 for f in removed:
202 to = c1.filectx(f).data()
202 to = c1.filectx(f).data()
203 tn = None
203 tn = None
204 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
204 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
205 opts=diffopts), f, tn)
205 opts=diffopts), f, tn)
206
206
207 def changelog(self, ctx, shortlog=False):
207 def changelog(self, ctx, shortlog=False):
208 def changelist(**map):
208 def changelist(**map):
209 cl = self.repo.changelog
209 cl = self.repo.changelog
210 l = [] # build a list in forward order for efficiency
210 l = [] # build a list in forward order for efficiency
211 for i in xrange(start, end):
211 for i in xrange(start, end):
212 ctx = self.repo.changectx(i)
212 ctx = self.repo.changectx(i)
213 n = ctx.node()
213 n = ctx.node()
214
214
215 l.insert(0, {"parity": parity.next(),
215 l.insert(0, {"parity": parity.next(),
216 "author": ctx.user(),
216 "author": ctx.user(),
217 "parent": self.siblings(ctx.parents(), i - 1),
217 "parent": self.siblings(ctx.parents(), i - 1),
218 "child": self.siblings(ctx.children(), i + 1),
218 "child": self.siblings(ctx.children(), i + 1),
219 "changelogtag": self.showtag("changelogtag",n),
219 "changelogtag": self.showtag("changelogtag",n),
220 "desc": ctx.description(),
220 "desc": ctx.description(),
221 "date": ctx.date(),
221 "date": ctx.date(),
222 "files": self.listfilediffs(ctx.files(), n),
222 "files": self.listfilediffs(ctx.files(), n),
223 "rev": i,
223 "rev": i,
224 "node": hex(n),
224 "node": hex(n),
225 "tags": self.nodetagsdict(n),
225 "tags": self.nodetagsdict(n),
226 "branches": self.nodebranchdict(ctx)})
226 "branches": self.nodebranchdict(ctx)})
227
227
228 for e in l:
228 for e in l:
229 yield e
229 yield e
230
230
231 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
231 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
232 cl = self.repo.changelog
232 cl = self.repo.changelog
233 count = cl.count()
233 count = cl.count()
234 pos = ctx.rev()
234 pos = ctx.rev()
235 start = max(0, pos - maxchanges + 1)
235 start = max(0, pos - maxchanges + 1)
236 end = min(count, start + maxchanges)
236 end = min(count, start + maxchanges)
237 pos = end - 1
237 pos = end - 1
238 parity = paritygen(self.stripecount, offset=start-end)
238 parity = paritygen(self.stripecount, offset=start-end)
239
239
240 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
240 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
241
241
242 yield self.t(shortlog and 'shortlog' or 'changelog',
242 yield self.t(shortlog and 'shortlog' or 'changelog',
243 changenav=changenav,
243 changenav=changenav,
244 node=hex(cl.tip()),
244 node=hex(cl.tip()),
245 rev=pos, changesets=count, entries=changelist,
245 rev=pos, changesets=count, entries=changelist,
246 archives=self.archivelist("tip"))
246 archives=self.archivelist("tip"))
247
247
248 def search(self, query):
248 def search(self, query):
249
249
250 def changelist(**map):
250 def changelist(**map):
251 cl = self.repo.changelog
251 cl = self.repo.changelog
252 count = 0
252 count = 0
253 qw = query.lower().split()
253 qw = query.lower().split()
254
254
255 def revgen():
255 def revgen():
256 for i in xrange(cl.count() - 1, 0, -100):
256 for i in xrange(cl.count() - 1, 0, -100):
257 l = []
257 l = []
258 for j in xrange(max(0, i - 100), i):
258 for j in xrange(max(0, i - 100), i):
259 ctx = self.repo.changectx(j)
259 ctx = self.repo.changectx(j)
260 l.append(ctx)
260 l.append(ctx)
261 l.reverse()
261 l.reverse()
262 for e in l:
262 for e in l:
263 yield e
263 yield e
264
264
265 for ctx in revgen():
265 for ctx in revgen():
266 miss = 0
266 miss = 0
267 for q in qw:
267 for q in qw:
268 if not (q in ctx.user().lower() or
268 if not (q in ctx.user().lower() or
269 q in ctx.description().lower() or
269 q in ctx.description().lower() or
270 q in " ".join(ctx.files()).lower()):
270 q in " ".join(ctx.files()).lower()):
271 miss = 1
271 miss = 1
272 break
272 break
273 if miss:
273 if miss:
274 continue
274 continue
275
275
276 count += 1
276 count += 1
277 n = ctx.node()
277 n = ctx.node()
278
278
279 yield self.t('searchentry',
279 yield self.t('searchentry',
280 parity=parity.next(),
280 parity=parity.next(),
281 author=ctx.user(),
281 author=ctx.user(),
282 parent=self.siblings(ctx.parents()),
282 parent=self.siblings(ctx.parents()),
283 child=self.siblings(ctx.children()),
283 child=self.siblings(ctx.children()),
284 changelogtag=self.showtag("changelogtag",n),
284 changelogtag=self.showtag("changelogtag",n),
285 desc=ctx.description(),
285 desc=ctx.description(),
286 date=ctx.date(),
286 date=ctx.date(),
287 files=self.listfilediffs(ctx.files(), n),
287 files=self.listfilediffs(ctx.files(), n),
288 rev=ctx.rev(),
288 rev=ctx.rev(),
289 node=hex(n),
289 node=hex(n),
290 tags=self.nodetagsdict(n),
290 tags=self.nodetagsdict(n),
291 branches=self.nodebranchdict(ctx))
291 branches=self.nodebranchdict(ctx))
292
292
293 if count >= self.maxchanges:
293 if count >= self.maxchanges:
294 break
294 break
295
295
296 cl = self.repo.changelog
296 cl = self.repo.changelog
297 parity = paritygen(self.stripecount)
297 parity = paritygen(self.stripecount)
298
298
299 yield self.t('search',
299 yield self.t('search',
300 query=query,
300 query=query,
301 node=hex(cl.tip()),
301 node=hex(cl.tip()),
302 entries=changelist,
302 entries=changelist,
303 archives=self.archivelist("tip"))
303 archives=self.archivelist("tip"))
304
304
305 def changeset(self, ctx):
305 def changeset(self, ctx):
306 n = ctx.node()
306 n = ctx.node()
307 parents = ctx.parents()
307 parents = ctx.parents()
308 p1 = parents[0].node()
308 p1 = parents[0].node()
309
309
310 files = []
310 files = []
311 parity = paritygen(self.stripecount)
311 parity = paritygen(self.stripecount)
312 for f in ctx.files():
312 for f in ctx.files():
313 files.append(self.t("filenodelink",
313 files.append(self.t("filenodelink",
314 node=hex(n), file=f,
314 node=hex(n), file=f,
315 parity=parity.next()))
315 parity=parity.next()))
316
316
317 def diff(**map):
317 def diff(**map):
318 yield self.diff(p1, n, None)
318 yield self.diff(p1, n, None)
319
319
320 yield self.t('changeset',
320 yield self.t('changeset',
321 diff=diff,
321 diff=diff,
322 rev=ctx.rev(),
322 rev=ctx.rev(),
323 node=hex(n),
323 node=hex(n),
324 parent=self.siblings(parents),
324 parent=self.siblings(parents),
325 child=self.siblings(ctx.children()),
325 child=self.siblings(ctx.children()),
326 changesettag=self.showtag("changesettag",n),
326 changesettag=self.showtag("changesettag",n),
327 author=ctx.user(),
327 author=ctx.user(),
328 desc=ctx.description(),
328 desc=ctx.description(),
329 date=ctx.date(),
329 date=ctx.date(),
330 files=files,
330 files=files,
331 archives=self.archivelist(hex(n)),
331 archives=self.archivelist(hex(n)),
332 tags=self.nodetagsdict(n),
332 tags=self.nodetagsdict(n),
333 branches=self.nodebranchdict(ctx))
333 branches=self.nodebranchdict(ctx))
334
334
335 def filelog(self, fctx):
335 def filelog(self, fctx):
336 f = fctx.path()
336 f = fctx.path()
337 fl = fctx.filelog()
337 fl = fctx.filelog()
338 count = fl.count()
338 count = fl.count()
339 pagelen = self.maxshortchanges
339 pagelen = self.maxshortchanges
340 pos = fctx.filerev()
340 pos = fctx.filerev()
341 start = max(0, pos - pagelen + 1)
341 start = max(0, pos - pagelen + 1)
342 end = min(count, start + pagelen)
342 end = min(count, start + pagelen)
343 pos = end - 1
343 pos = end - 1
344 parity = paritygen(self.stripecount, offset=start-end)
344 parity = paritygen(self.stripecount, offset=start-end)
345
345
346 def entries(**map):
346 def entries(**map):
347 l = []
347 l = []
348
348
349 for i in xrange(start, end):
349 for i in xrange(start, end):
350 ctx = fctx.filectx(i)
350 ctx = fctx.filectx(i)
351 n = fl.node(i)
351 n = fl.node(i)
352
352
353 l.insert(0, {"parity": parity.next(),
353 l.insert(0, {"parity": parity.next(),
354 "filerev": i,
354 "filerev": i,
355 "file": f,
355 "file": f,
356 "node": hex(ctx.node()),
356 "node": hex(ctx.node()),
357 "author": ctx.user(),
357 "author": ctx.user(),
358 "date": ctx.date(),
358 "date": ctx.date(),
359 "rename": self.renamelink(fl, n),
359 "rename": self.renamelink(fl, n),
360 "parent": self.siblings(fctx.parents()),
360 "parent": self.siblings(fctx.parents()),
361 "child": self.siblings(fctx.children()),
361 "child": self.siblings(fctx.children()),
362 "desc": ctx.description()})
362 "desc": ctx.description()})
363
363
364 for e in l:
364 for e in l:
365 yield e
365 yield e
366
366
367 nodefunc = lambda x: fctx.filectx(fileid=x)
367 nodefunc = lambda x: fctx.filectx(fileid=x)
368 nav = revnavgen(pos, pagelen, count, nodefunc)
368 nav = revnavgen(pos, pagelen, count, nodefunc)
369 yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
369 yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
370 entries=entries)
370 entries=entries)
371
371
372 def filerevision(self, fctx):
372 def filerevision(self, fctx):
373 f = fctx.path()
373 f = fctx.path()
374 text = fctx.data()
374 text = fctx.data()
375 fl = fctx.filelog()
375 fl = fctx.filelog()
376 n = fctx.filenode()
376 n = fctx.filenode()
377 parity = paritygen(self.stripecount)
377 parity = paritygen(self.stripecount)
378
378
379 mt = mimetypes.guess_type(f)[0]
379 mt = mimetypes.guess_type(f)[0]
380 rawtext = text
380 rawtext = text
381 if util.binary(text):
381 if util.binary(text):
382 mt = mt or 'application/octet-stream'
382 mt = mt or 'application/octet-stream'
383 text = "(binary:%s)" % mt
383 text = "(binary:%s)" % mt
384 mt = mt or 'text/plain'
384 mt = mt or 'text/plain'
385
385
386 def lines():
386 def lines():
387 for l, t in enumerate(text.splitlines(1)):
387 for l, t in enumerate(text.splitlines(1)):
388 yield {"line": t,
388 yield {"line": t,
389 "linenumber": "% 6d" % (l + 1),
389 "linenumber": "% 6d" % (l + 1),
390 "parity": parity.next()}
390 "parity": parity.next()}
391
391
392 yield self.t("filerevision",
392 yield self.t("filerevision",
393 file=f,
393 file=f,
394 path=_up(f),
394 path=_up(f),
395 text=lines(),
395 text=lines(),
396 raw=rawtext,
396 raw=rawtext,
397 mimetype=mt,
397 mimetype=mt,
398 rev=fctx.rev(),
398 rev=fctx.rev(),
399 node=hex(fctx.node()),
399 node=hex(fctx.node()),
400 author=fctx.user(),
400 author=fctx.user(),
401 date=fctx.date(),
401 date=fctx.date(),
402 desc=fctx.description(),
402 desc=fctx.description(),
403 parent=self.siblings(fctx.parents()),
403 parent=self.siblings(fctx.parents()),
404 child=self.siblings(fctx.children()),
404 child=self.siblings(fctx.children()),
405 rename=self.renamelink(fl, n),
405 rename=self.renamelink(fl, n),
406 permissions=fctx.manifest().execf(f))
406 permissions=fctx.manifest().execf(f))
407
407
408 def fileannotate(self, fctx):
408 def fileannotate(self, fctx):
409 f = fctx.path()
409 f = fctx.path()
410 n = fctx.filenode()
410 n = fctx.filenode()
411 fl = fctx.filelog()
411 fl = fctx.filelog()
412 parity = paritygen(self.stripecount)
412 parity = paritygen(self.stripecount)
413
413
414 def annotate(**map):
414 def annotate(**map):
415 last = None
415 last = None
416 for f, l in fctx.annotate(follow=True):
416 for f, l in fctx.annotate(follow=True):
417 fnode = f.filenode()
417 fnode = f.filenode()
418 name = self.repo.ui.shortuser(f.user())
418 name = self.repo.ui.shortuser(f.user())
419
419
420 if last != fnode:
420 if last != fnode:
421 last = fnode
421 last = fnode
422
422
423 yield {"parity": parity.next(),
423 yield {"parity": parity.next(),
424 "node": hex(f.node()),
424 "node": hex(f.node()),
425 "rev": f.rev(),
425 "rev": f.rev(),
426 "author": name,
426 "author": name,
427 "file": f.path(),
427 "file": f.path(),
428 "line": l}
428 "line": l}
429
429
430 yield self.t("fileannotate",
430 yield self.t("fileannotate",
431 file=f,
431 file=f,
432 annotate=annotate,
432 annotate=annotate,
433 path=_up(f),
433 path=_up(f),
434 rev=fctx.rev(),
434 rev=fctx.rev(),
435 node=hex(fctx.node()),
435 node=hex(fctx.node()),
436 author=fctx.user(),
436 author=fctx.user(),
437 date=fctx.date(),
437 date=fctx.date(),
438 desc=fctx.description(),
438 desc=fctx.description(),
439 rename=self.renamelink(fl, n),
439 rename=self.renamelink(fl, n),
440 parent=self.siblings(fctx.parents()),
440 parent=self.siblings(fctx.parents()),
441 child=self.siblings(fctx.children()),
441 child=self.siblings(fctx.children()),
442 permissions=fctx.manifest().execf(f))
442 permissions=fctx.manifest().execf(f))
443
443
444 def manifest(self, ctx, path):
444 def manifest(self, ctx, path):
445 mf = ctx.manifest()
445 mf = ctx.manifest()
446 node = ctx.node()
446 node = ctx.node()
447
447
448 files = {}
448 files = {}
449 parity = paritygen(self.stripecount)
449 parity = paritygen(self.stripecount)
450
450
451 if path and path[-1] != "/":
451 if path and path[-1] != "/":
452 path += "/"
452 path += "/"
453 l = len(path)
453 l = len(path)
454 abspath = "/" + path
454 abspath = "/" + path
455
455
456 for f, n in mf.items():
456 for f, n in mf.items():
457 if f[:l] != path:
457 if f[:l] != path:
458 continue
458 continue
459 remain = f[l:]
459 remain = f[l:]
460 if "/" in remain:
460 if "/" in remain:
461 short = remain[:remain.index("/") + 1] # bleah
461 short = remain[:remain.index("/") + 1] # bleah
462 files[short] = (f, None)
462 files[short] = (f, None)
463 else:
463 else:
464 short = os.path.basename(remain)
464 short = os.path.basename(remain)
465 files[short] = (f, n)
465 files[short] = (f, n)
466
466
467 def filelist(**map):
467 def filelist(**map):
468 fl = files.keys()
468 fl = files.keys()
469 fl.sort()
469 fl.sort()
470 for f in fl:
470 for f in fl:
471 full, fnode = files[f]
471 full, fnode = files[f]
472 if not fnode:
472 if not fnode:
473 continue
473 continue
474
474
475 yield {"file": full,
475 yield {"file": full,
476 "parity": parity.next(),
476 "parity": parity.next(),
477 "basename": f,
477 "basename": f,
478 "size": ctx.filectx(full).size(),
478 "size": ctx.filectx(full).size(),
479 "permissions": mf.execf(full)}
479 "permissions": mf.execf(full)}
480
480
481 def dirlist(**map):
481 def dirlist(**map):
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 fnode:
486 if fnode:
487 continue
487 continue
488
488
489 yield {"parity": parity.next(),
489 yield {"parity": parity.next(),
490 "path": os.path.join(abspath, f),
490 "path": os.path.join(abspath, f),
491 "basename": f[:-1]}
491 "basename": f[:-1]}
492
492
493 yield self.t("manifest",
493 yield self.t("manifest",
494 rev=ctx.rev(),
494 rev=ctx.rev(),
495 node=hex(node),
495 node=hex(node),
496 path=abspath,
496 path=abspath,
497 up=_up(abspath),
497 up=_up(abspath),
498 upparity=parity.next(),
498 upparity=parity.next(),
499 fentries=filelist,
499 fentries=filelist,
500 dentries=dirlist,
500 dentries=dirlist,
501 archives=self.archivelist(hex(node)),
501 archives=self.archivelist(hex(node)),
502 tags=self.nodetagsdict(node),
502 tags=self.nodetagsdict(node),
503 branches=self.nodebranchdict(ctx))
503 branches=self.nodebranchdict(ctx))
504
504
505 def tags(self):
505 def tags(self):
506 i = self.repo.tagslist()
506 i = self.repo.tagslist()
507 i.reverse()
507 i.reverse()
508 parity = paritygen(self.stripecount)
508 parity = paritygen(self.stripecount)
509
509
510 def entries(notip=False, **map):
510 def entries(notip=False, **map):
511 for k, n in i:
511 for k, n in i:
512 if notip and k == "tip":
512 if notip and k == "tip":
513 continue
513 continue
514 yield {"parity": parity.next(),
514 yield {"parity": parity.next(),
515 "tag": k,
515 "tag": k,
516 "date": self.repo.changectx(n).date(),
516 "date": self.repo.changectx(n).date(),
517 "node": hex(n)}
517 "node": hex(n)}
518
518
519 yield self.t("tags",
519 yield self.t("tags",
520 node=hex(self.repo.changelog.tip()),
520 node=hex(self.repo.changelog.tip()),
521 entries=lambda **x: entries(False, **x),
521 entries=lambda **x: entries(False, **x),
522 entriesnotip=lambda **x: entries(True, **x))
522 entriesnotip=lambda **x: entries(True, **x))
523
523
524 def summary(self):
524 def summary(self):
525 i = self.repo.tagslist()
525 i = self.repo.tagslist()
526 i.reverse()
526 i.reverse()
527
527
528 def tagentries(**map):
528 def tagentries(**map):
529 parity = paritygen(self.stripecount)
529 parity = paritygen(self.stripecount)
530 count = 0
530 count = 0
531 for k, n in i:
531 for k, n in i:
532 if k == "tip": # skip tip
532 if k == "tip": # skip tip
533 continue;
533 continue;
534
534
535 count += 1
535 count += 1
536 if count > 10: # limit to 10 tags
536 if count > 10: # limit to 10 tags
537 break;
537 break;
538
538
539 yield self.t("tagentry",
539 yield self.t("tagentry",
540 parity=parity.next(),
540 parity=parity.next(),
541 tag=k,
541 tag=k,
542 node=hex(n),
542 node=hex(n),
543 date=self.repo.changectx(n).date())
543 date=self.repo.changectx(n).date())
544
544
545
545
546 def branches(**map):
546 def branches(**map):
547 parity = paritygen(self.stripecount)
547 parity = paritygen(self.stripecount)
548
548
549 b = self.repo.branchtags()
549 b = self.repo.branchtags()
550 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
550 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
551 l.sort()
551 l.sort()
552
552
553 for r,n,t in l:
553 for r,n,t in l:
554 ctx = self.repo.changectx(n)
554 ctx = self.repo.changectx(n)
555
555
556 yield {'parity': parity.next(),
556 yield {'parity': parity.next(),
557 'branch': t,
557 'branch': t,
558 'node': hex(n),
558 'node': hex(n),
559 'date': ctx.date()}
559 'date': ctx.date()}
560
560
561 def changelist(**map):
561 def changelist(**map):
562 parity = paritygen(self.stripecount, offset=start-end)
562 parity = paritygen(self.stripecount, offset=start-end)
563 l = [] # build a list in forward order for efficiency
563 l = [] # build a list in forward order for efficiency
564 for i in xrange(start, end):
564 for i in xrange(start, end):
565 ctx = self.repo.changectx(i)
565 ctx = self.repo.changectx(i)
566 n = ctx.node()
566 n = ctx.node()
567 hn = hex(n)
567 hn = hex(n)
568
568
569 l.insert(0, self.t(
569 l.insert(0, self.t(
570 'shortlogentry',
570 'shortlogentry',
571 parity=parity.next(),
571 parity=parity.next(),
572 author=ctx.user(),
572 author=ctx.user(),
573 desc=ctx.description(),
573 desc=ctx.description(),
574 date=ctx.date(),
574 date=ctx.date(),
575 rev=i,
575 rev=i,
576 node=hn,
576 node=hn,
577 tags=self.nodetagsdict(n),
577 tags=self.nodetagsdict(n),
578 branches=self.nodebranchdict(ctx)))
578 branches=self.nodebranchdict(ctx)))
579
579
580 yield l
580 yield l
581
581
582 cl = self.repo.changelog
582 cl = self.repo.changelog
583 count = cl.count()
583 count = cl.count()
584 start = max(0, count - self.maxchanges)
584 start = max(0, count - self.maxchanges)
585 end = min(count, start + self.maxchanges)
585 end = min(count, start + self.maxchanges)
586
586
587 yield self.t("summary",
587 yield self.t("summary",
588 desc=self.config("web", "description", "unknown"),
588 desc=self.config("web", "description", "unknown"),
589 owner=(self.config("ui", "username") or # preferred
589 owner=(self.config("ui", "username") or # preferred
590 self.config("web", "contact") or # deprecated
590 self.config("web", "contact") or # deprecated
591 self.config("web", "author", "unknown")), # also
591 self.config("web", "author", "unknown")), # also
592 lastchange=cl.read(cl.tip())[2],
592 lastchange=cl.read(cl.tip())[2],
593 tags=tagentries,
593 tags=tagentries,
594 branches=branches,
594 branches=branches,
595 shortlog=changelist,
595 shortlog=changelist,
596 node=hex(cl.tip()),
596 node=hex(cl.tip()),
597 archives=self.archivelist("tip"))
597 archives=self.archivelist("tip"))
598
598
599 def filediff(self, fctx):
599 def filediff(self, fctx):
600 n = fctx.node()
600 n = fctx.node()
601 path = fctx.path()
601 path = fctx.path()
602 parents = fctx.parents()
602 parents = fctx.parents()
603 p1 = parents and parents[0].node() or nullid
603 p1 = parents and parents[0].node() or nullid
604
604
605 def diff(**map):
605 def diff(**map):
606 yield self.diff(p1, n, [path])
606 yield self.diff(p1, n, [path])
607
607
608 yield self.t("filediff",
608 yield self.t("filediff",
609 file=path,
609 file=path,
610 node=hex(n),
610 node=hex(n),
611 rev=fctx.rev(),
611 rev=fctx.rev(),
612 parent=self.siblings(parents),
612 parent=self.siblings(parents),
613 child=self.siblings(fctx.children()),
613 child=self.siblings(fctx.children()),
614 diff=diff)
614 diff=diff)
615
615
616 archive_specs = {
616 archive_specs = {
617 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
617 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
618 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
618 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
619 'zip': ('application/zip', 'zip', '.zip', None),
619 'zip': ('application/zip', 'zip', '.zip', None),
620 }
620 }
621
621
622 def archive(self, req, id, type_):
622 def archive(self, req, key, type_):
623 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
623 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
624 cnode = self.repo.lookup(id)
624 cnode = self.repo.lookup(key)
625 arch_version = id
625 arch_version = key
626 if cnode == id or id == 'tip':
626 if cnode == key or key == 'tip':
627 arch_version = short(cnode)
627 arch_version = short(cnode)
628 name = "%s-%s" % (reponame, arch_version)
628 name = "%s-%s" % (reponame, arch_version)
629 mimetype, artype, extension, encoding = self.archive_specs[type_]
629 mimetype, artype, extension, encoding = self.archive_specs[type_]
630 headers = [('Content-type', mimetype),
630 headers = [('Content-type', mimetype),
631 ('Content-disposition', 'attachment; filename=%s%s' %
631 ('Content-disposition', 'attachment; filename=%s%s' %
632 (name, extension))]
632 (name, extension))]
633 if encoding:
633 if encoding:
634 headers.append(('Content-encoding', encoding))
634 headers.append(('Content-encoding', encoding))
635 req.header(headers)
635 req.header(headers)
636 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
636 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
637
637
638 # add tags to things
638 # add tags to things
639 # tags -> list of changesets corresponding to tags
639 # tags -> list of changesets corresponding to tags
640 # find tag, changeset, file
640 # find tag, changeset, file
641
641
642 def cleanpath(self, path):
642 def cleanpath(self, path):
643 path = path.lstrip('/')
643 path = path.lstrip('/')
644 return util.canonpath(self.repo.root, '', path)
644 return util.canonpath(self.repo.root, '', path)
645
645
646 def run(self):
646 def run(self):
647 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
647 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
648 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
648 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
649 import mercurial.hgweb.wsgicgi as wsgicgi
649 import mercurial.hgweb.wsgicgi as wsgicgi
650 from request import wsgiapplication
650 from request import wsgiapplication
651 def make_web_app():
651 def make_web_app():
652 return self
652 return self
653 wsgicgi.launch(wsgiapplication(make_web_app))
653 wsgicgi.launch(wsgiapplication(make_web_app))
654
654
655 def run_wsgi(self, req):
655 def run_wsgi(self, req):
656 def header(**map):
656 def header(**map):
657 header_file = cStringIO.StringIO(
657 header_file = cStringIO.StringIO(
658 ''.join(self.t("header", encoding=util._encoding, **map)))
658 ''.join(self.t("header", encoding=util._encoding, **map)))
659 msg = mimetools.Message(header_file, 0)
659 msg = mimetools.Message(header_file, 0)
660 req.header(msg.items())
660 req.header(msg.items())
661 yield header_file.read()
661 yield header_file.read()
662
662
663 def rawfileheader(**map):
663 def rawfileheader(**map):
664 req.header([('Content-type', map['mimetype']),
664 req.header([('Content-type', map['mimetype']),
665 ('Content-disposition', 'filename=%s' % map['file']),
665 ('Content-disposition', 'filename=%s' % map['file']),
666 ('Content-length', str(len(map['raw'])))])
666 ('Content-length', str(len(map['raw'])))])
667 yield ''
667 yield ''
668
668
669 def footer(**map):
669 def footer(**map):
670 yield self.t("footer", **map)
670 yield self.t("footer", **map)
671
671
672 def motd(**map):
672 def motd(**map):
673 yield self.config("web", "motd", "")
673 yield self.config("web", "motd", "")
674
674
675 def expand_form(form):
675 def expand_form(form):
676 shortcuts = {
676 shortcuts = {
677 'cl': [('cmd', ['changelog']), ('rev', None)],
677 'cl': [('cmd', ['changelog']), ('rev', None)],
678 'sl': [('cmd', ['shortlog']), ('rev', None)],
678 'sl': [('cmd', ['shortlog']), ('rev', None)],
679 'cs': [('cmd', ['changeset']), ('node', None)],
679 'cs': [('cmd', ['changeset']), ('node', None)],
680 'f': [('cmd', ['file']), ('filenode', None)],
680 'f': [('cmd', ['file']), ('filenode', None)],
681 'fl': [('cmd', ['filelog']), ('filenode', None)],
681 'fl': [('cmd', ['filelog']), ('filenode', None)],
682 'fd': [('cmd', ['filediff']), ('node', None)],
682 'fd': [('cmd', ['filediff']), ('node', None)],
683 'fa': [('cmd', ['annotate']), ('filenode', None)],
683 'fa': [('cmd', ['annotate']), ('filenode', None)],
684 'mf': [('cmd', ['manifest']), ('manifest', None)],
684 'mf': [('cmd', ['manifest']), ('manifest', None)],
685 'ca': [('cmd', ['archive']), ('node', None)],
685 'ca': [('cmd', ['archive']), ('node', None)],
686 'tags': [('cmd', ['tags'])],
686 'tags': [('cmd', ['tags'])],
687 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
687 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
688 'static': [('cmd', ['static']), ('file', None)]
688 'static': [('cmd', ['static']), ('file', None)]
689 }
689 }
690
690
691 for k in shortcuts.iterkeys():
691 for k in shortcuts.iterkeys():
692 if form.has_key(k):
692 if form.has_key(k):
693 for name, value in shortcuts[k]:
693 for name, value in shortcuts[k]:
694 if value is None:
694 if value is None:
695 value = form[k]
695 value = form[k]
696 form[name] = value
696 form[name] = value
697 del form[k]
697 del form[k]
698
698
699 def rewrite_request(req):
699 def rewrite_request(req):
700 '''translate new web interface to traditional format'''
700 '''translate new web interface to traditional format'''
701
701
702 def spliturl(req):
702 def spliturl(req):
703 def firstitem(query):
703 def firstitem(query):
704 return query.split('&', 1)[0].split(';', 1)[0]
704 return query.split('&', 1)[0].split(';', 1)[0]
705
705
706 def normurl(url):
706 def normurl(url):
707 inner = '/'.join([x for x in url.split('/') if x])
707 inner = '/'.join([x for x in url.split('/') if x])
708 tl = len(url) > 1 and url.endswith('/') and '/' or ''
708 tl = len(url) > 1 and url.endswith('/') and '/' or ''
709
709
710 return '%s%s%s' % (url.startswith('/') and '/' or '',
710 return '%s%s%s' % (url.startswith('/') and '/' or '',
711 inner, tl)
711 inner, tl)
712
712
713 root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
713 root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
714 pi = normurl(req.env.get('PATH_INFO', ''))
714 pi = normurl(req.env.get('PATH_INFO', ''))
715 if pi:
715 if pi:
716 # strip leading /
716 # strip leading /
717 pi = pi[1:]
717 pi = pi[1:]
718 if pi:
718 if pi:
719 root = root[:root.rfind(pi)]
719 root = root[:root.rfind(pi)]
720 if req.env.has_key('REPO_NAME'):
720 if req.env.has_key('REPO_NAME'):
721 rn = req.env['REPO_NAME'] + '/'
721 rn = req.env['REPO_NAME'] + '/'
722 root += rn
722 root += rn
723 query = pi[len(rn):]
723 query = pi[len(rn):]
724 else:
724 else:
725 query = pi
725 query = pi
726 else:
726 else:
727 root += '?'
727 root += '?'
728 query = firstitem(req.env['QUERY_STRING'])
728 query = firstitem(req.env['QUERY_STRING'])
729
729
730 return (root, query)
730 return (root, query)
731
731
732 req.url, query = spliturl(req)
732 req.url, query = spliturl(req)
733
733
734 if req.form.has_key('cmd'):
734 if req.form.has_key('cmd'):
735 # old style
735 # old style
736 return
736 return
737
737
738 args = query.split('/', 2)
738 args = query.split('/', 2)
739 if not args or not args[0]:
739 if not args or not args[0]:
740 return
740 return
741
741
742 cmd = args.pop(0)
742 cmd = args.pop(0)
743 style = cmd.rfind('-')
743 style = cmd.rfind('-')
744 if style != -1:
744 if style != -1:
745 req.form['style'] = [cmd[:style]]
745 req.form['style'] = [cmd[:style]]
746 cmd = cmd[style+1:]
746 cmd = cmd[style+1:]
747 # avoid accepting e.g. style parameter as command
747 # avoid accepting e.g. style parameter as command
748 if hasattr(self, 'do_' + cmd):
748 if hasattr(self, 'do_' + cmd):
749 req.form['cmd'] = [cmd]
749 req.form['cmd'] = [cmd]
750
750
751 if args and args[0]:
751 if args and args[0]:
752 node = args.pop(0)
752 node = args.pop(0)
753 req.form['node'] = [node]
753 req.form['node'] = [node]
754 if args:
754 if args:
755 req.form['file'] = args
755 req.form['file'] = args
756
756
757 if cmd == 'static':
757 if cmd == 'static':
758 req.form['file'] = req.form['node']
758 req.form['file'] = req.form['node']
759 elif cmd == 'archive':
759 elif cmd == 'archive':
760 fn = req.form['node'][0]
760 fn = req.form['node'][0]
761 for type_, spec in self.archive_specs.iteritems():
761 for type_, spec in self.archive_specs.iteritems():
762 ext = spec[2]
762 ext = spec[2]
763 if fn.endswith(ext):
763 if fn.endswith(ext):
764 req.form['node'] = [fn[:-len(ext)]]
764 req.form['node'] = [fn[:-len(ext)]]
765 req.form['type'] = [type_]
765 req.form['type'] = [type_]
766
766
767 def sessionvars(**map):
767 def sessionvars(**map):
768 fields = []
768 fields = []
769 if req.form.has_key('style'):
769 if req.form.has_key('style'):
770 style = req.form['style'][0]
770 style = req.form['style'][0]
771 if style != self.config('web', 'style', ''):
771 if style != self.config('web', 'style', ''):
772 fields.append(('style', style))
772 fields.append(('style', style))
773
773
774 separator = req.url[-1] == '?' and ';' or '?'
774 separator = req.url[-1] == '?' and ';' or '?'
775 for name, value in fields:
775 for name, value in fields:
776 yield dict(name=name, value=value, separator=separator)
776 yield dict(name=name, value=value, separator=separator)
777 separator = ';'
777 separator = ';'
778
778
779 self.refresh()
779 self.refresh()
780
780
781 expand_form(req.form)
781 expand_form(req.form)
782 rewrite_request(req)
782 rewrite_request(req)
783
783
784 style = self.config("web", "style", "")
784 style = self.config("web", "style", "")
785 if req.form.has_key('style'):
785 if req.form.has_key('style'):
786 style = req.form['style'][0]
786 style = req.form['style'][0]
787 mapfile = style_map(self.templatepath, style)
787 mapfile = style_map(self.templatepath, style)
788
788
789 port = req.env["SERVER_PORT"]
789 port = req.env["SERVER_PORT"]
790 port = port != "80" and (":" + port) or ""
790 port = port != "80" and (":" + port) or ""
791 urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port)
791 urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port)
792 staticurl = self.config("web", "staticurl") or req.url + 'static/'
792 staticurl = self.config("web", "staticurl") or req.url + 'static/'
793 if not staticurl.endswith('/'):
793 if not staticurl.endswith('/'):
794 staticurl += '/'
794 staticurl += '/'
795
795
796 if not self.reponame:
796 if not self.reponame:
797 self.reponame = (self.config("web", "name")
797 self.reponame = (self.config("web", "name")
798 or req.env.get('REPO_NAME')
798 or req.env.get('REPO_NAME')
799 or req.url.strip('/') or self.repo.root)
799 or req.url.strip('/') or self.repo.root)
800
800
801 self.t = templater.templater(mapfile, templater.common_filters,
801 self.t = templater.templater(mapfile, templater.common_filters,
802 defaults={"url": req.url,
802 defaults={"url": req.url,
803 "staticurl": staticurl,
803 "staticurl": staticurl,
804 "urlbase": urlbase,
804 "urlbase": urlbase,
805 "repo": self.reponame,
805 "repo": self.reponame,
806 "header": header,
806 "header": header,
807 "footer": footer,
807 "footer": footer,
808 "motd": motd,
808 "motd": motd,
809 "rawfileheader": rawfileheader,
809 "rawfileheader": rawfileheader,
810 "sessionvars": sessionvars
810 "sessionvars": sessionvars
811 })
811 })
812
812
813 try:
813 try:
814 if not req.form.has_key('cmd'):
814 if not req.form.has_key('cmd'):
815 req.form['cmd'] = [self.t.cache['default']]
815 req.form['cmd'] = [self.t.cache['default']]
816
816
817 cmd = req.form['cmd'][0]
817 cmd = req.form['cmd'][0]
818
818
819 method = getattr(self, 'do_' + cmd, None)
819 method = getattr(self, 'do_' + cmd, None)
820 if method:
820 if method:
821 try:
821 try:
822 method(req)
822 method(req)
823 except (hg.RepoError, revlog.RevlogError), inst:
823 except (hg.RepoError, revlog.RevlogError), inst:
824 req.write(self.t("error", error=str(inst)))
824 req.write(self.t("error", error=str(inst)))
825 else:
825 else:
826 req.write(self.t("error", error='No such method: ' + cmd))
826 req.write(self.t("error", error='No such method: ' + cmd))
827 finally:
827 finally:
828 self.t = None
828 self.t = None
829
829
830 def changectx(self, req):
830 def changectx(self, req):
831 if req.form.has_key('node'):
831 if req.form.has_key('node'):
832 changeid = req.form['node'][0]
832 changeid = req.form['node'][0]
833 elif req.form.has_key('manifest'):
833 elif req.form.has_key('manifest'):
834 changeid = req.form['manifest'][0]
834 changeid = req.form['manifest'][0]
835 else:
835 else:
836 changeid = self.repo.changelog.count() - 1
836 changeid = self.repo.changelog.count() - 1
837
837
838 try:
838 try:
839 ctx = self.repo.changectx(changeid)
839 ctx = self.repo.changectx(changeid)
840 except hg.RepoError:
840 except hg.RepoError:
841 man = self.repo.manifest
841 man = self.repo.manifest
842 mn = man.lookup(changeid)
842 mn = man.lookup(changeid)
843 ctx = self.repo.changectx(man.linkrev(mn))
843 ctx = self.repo.changectx(man.linkrev(mn))
844
844
845 return ctx
845 return ctx
846
846
847 def filectx(self, req):
847 def filectx(self, req):
848 path = self.cleanpath(req.form['file'][0])
848 path = self.cleanpath(req.form['file'][0])
849 if req.form.has_key('node'):
849 if req.form.has_key('node'):
850 changeid = req.form['node'][0]
850 changeid = req.form['node'][0]
851 else:
851 else:
852 changeid = req.form['filenode'][0]
852 changeid = req.form['filenode'][0]
853 try:
853 try:
854 ctx = self.repo.changectx(changeid)
854 ctx = self.repo.changectx(changeid)
855 fctx = ctx.filectx(path)
855 fctx = ctx.filectx(path)
856 except hg.RepoError:
856 except hg.RepoError:
857 fctx = self.repo.filectx(path, fileid=changeid)
857 fctx = self.repo.filectx(path, fileid=changeid)
858
858
859 return fctx
859 return fctx
860
860
861 def do_log(self, req):
861 def do_log(self, req):
862 if req.form.has_key('file') and req.form['file'][0]:
862 if req.form.has_key('file') and req.form['file'][0]:
863 self.do_filelog(req)
863 self.do_filelog(req)
864 else:
864 else:
865 self.do_changelog(req)
865 self.do_changelog(req)
866
866
867 def do_rev(self, req):
867 def do_rev(self, req):
868 self.do_changeset(req)
868 self.do_changeset(req)
869
869
870 def do_file(self, req):
870 def do_file(self, req):
871 path = self.cleanpath(req.form.get('file', [''])[0])
871 path = self.cleanpath(req.form.get('file', [''])[0])
872 if path:
872 if path:
873 try:
873 try:
874 req.write(self.filerevision(self.filectx(req)))
874 req.write(self.filerevision(self.filectx(req)))
875 return
875 return
876 except revlog.LookupError:
876 except revlog.LookupError:
877 pass
877 pass
878
878
879 req.write(self.manifest(self.changectx(req), path))
879 req.write(self.manifest(self.changectx(req), path))
880
880
881 def do_diff(self, req):
881 def do_diff(self, req):
882 self.do_filediff(req)
882 self.do_filediff(req)
883
883
884 def do_changelog(self, req, shortlog = False):
884 def do_changelog(self, req, shortlog = False):
885 if req.form.has_key('node'):
885 if req.form.has_key('node'):
886 ctx = self.changectx(req)
886 ctx = self.changectx(req)
887 else:
887 else:
888 if req.form.has_key('rev'):
888 if req.form.has_key('rev'):
889 hi = req.form['rev'][0]
889 hi = req.form['rev'][0]
890 else:
890 else:
891 hi = self.repo.changelog.count() - 1
891 hi = self.repo.changelog.count() - 1
892 try:
892 try:
893 ctx = self.repo.changectx(hi)
893 ctx = self.repo.changectx(hi)
894 except hg.RepoError:
894 except hg.RepoError:
895 req.write(self.search(hi)) # XXX redirect to 404 page?
895 req.write(self.search(hi)) # XXX redirect to 404 page?
896 return
896 return
897
897
898 req.write(self.changelog(ctx, shortlog = shortlog))
898 req.write(self.changelog(ctx, shortlog = shortlog))
899
899
900 def do_shortlog(self, req):
900 def do_shortlog(self, req):
901 self.do_changelog(req, shortlog = True)
901 self.do_changelog(req, shortlog = True)
902
902
903 def do_changeset(self, req):
903 def do_changeset(self, req):
904 req.write(self.changeset(self.changectx(req)))
904 req.write(self.changeset(self.changectx(req)))
905
905
906 def do_manifest(self, req):
906 def do_manifest(self, req):
907 req.write(self.manifest(self.changectx(req),
907 req.write(self.manifest(self.changectx(req),
908 self.cleanpath(req.form['path'][0])))
908 self.cleanpath(req.form['path'][0])))
909
909
910 def do_tags(self, req):
910 def do_tags(self, req):
911 req.write(self.tags())
911 req.write(self.tags())
912
912
913 def do_summary(self, req):
913 def do_summary(self, req):
914 req.write(self.summary())
914 req.write(self.summary())
915
915
916 def do_filediff(self, req):
916 def do_filediff(self, req):
917 req.write(self.filediff(self.filectx(req)))
917 req.write(self.filediff(self.filectx(req)))
918
918
919 def do_annotate(self, req):
919 def do_annotate(self, req):
920 req.write(self.fileannotate(self.filectx(req)))
920 req.write(self.fileannotate(self.filectx(req)))
921
921
922 def do_filelog(self, req):
922 def do_filelog(self, req):
923 req.write(self.filelog(self.filectx(req)))
923 req.write(self.filelog(self.filectx(req)))
924
924
925 def do_lookup(self, req):
925 def do_lookup(self, req):
926 try:
926 try:
927 r = hex(self.repo.lookup(req.form['key'][0]))
927 r = hex(self.repo.lookup(req.form['key'][0]))
928 success = 1
928 success = 1
929 except Exception,inst:
929 except Exception,inst:
930 r = str(inst)
930 r = str(inst)
931 success = 0
931 success = 0
932 resp = "%s %s\n" % (success, r)
932 resp = "%s %s\n" % (success, r)
933 req.httphdr("application/mercurial-0.1", length=len(resp))
933 req.httphdr("application/mercurial-0.1", length=len(resp))
934 req.write(resp)
934 req.write(resp)
935
935
936 def do_heads(self, req):
936 def do_heads(self, req):
937 resp = " ".join(map(hex, self.repo.heads())) + "\n"
937 resp = " ".join(map(hex, self.repo.heads())) + "\n"
938 req.httphdr("application/mercurial-0.1", length=len(resp))
938 req.httphdr("application/mercurial-0.1", length=len(resp))
939 req.write(resp)
939 req.write(resp)
940
940
941 def do_branches(self, req):
941 def do_branches(self, req):
942 nodes = []
942 nodes = []
943 if req.form.has_key('nodes'):
943 if req.form.has_key('nodes'):
944 nodes = map(bin, req.form['nodes'][0].split(" "))
944 nodes = map(bin, req.form['nodes'][0].split(" "))
945 resp = cStringIO.StringIO()
945 resp = cStringIO.StringIO()
946 for b in self.repo.branches(nodes):
946 for b in self.repo.branches(nodes):
947 resp.write(" ".join(map(hex, b)) + "\n")
947 resp.write(" ".join(map(hex, b)) + "\n")
948 resp = resp.getvalue()
948 resp = resp.getvalue()
949 req.httphdr("application/mercurial-0.1", length=len(resp))
949 req.httphdr("application/mercurial-0.1", length=len(resp))
950 req.write(resp)
950 req.write(resp)
951
951
952 def do_between(self, req):
952 def do_between(self, req):
953 if req.form.has_key('pairs'):
953 if req.form.has_key('pairs'):
954 pairs = [map(bin, p.split("-"))
954 pairs = [map(bin, p.split("-"))
955 for p in req.form['pairs'][0].split(" ")]
955 for p in req.form['pairs'][0].split(" ")]
956 resp = cStringIO.StringIO()
956 resp = cStringIO.StringIO()
957 for b in self.repo.between(pairs):
957 for b in self.repo.between(pairs):
958 resp.write(" ".join(map(hex, b)) + "\n")
958 resp.write(" ".join(map(hex, b)) + "\n")
959 resp = resp.getvalue()
959 resp = resp.getvalue()
960 req.httphdr("application/mercurial-0.1", length=len(resp))
960 req.httphdr("application/mercurial-0.1", length=len(resp))
961 req.write(resp)
961 req.write(resp)
962
962
963 def do_changegroup(self, req):
963 def do_changegroup(self, req):
964 req.httphdr("application/mercurial-0.1")
964 req.httphdr("application/mercurial-0.1")
965 nodes = []
965 nodes = []
966 if not self.allowpull:
966 if not self.allowpull:
967 return
967 return
968
968
969 if req.form.has_key('roots'):
969 if req.form.has_key('roots'):
970 nodes = map(bin, req.form['roots'][0].split(" "))
970 nodes = map(bin, req.form['roots'][0].split(" "))
971
971
972 z = zlib.compressobj()
972 z = zlib.compressobj()
973 f = self.repo.changegroup(nodes, 'serve')
973 f = self.repo.changegroup(nodes, 'serve')
974 while 1:
974 while 1:
975 chunk = f.read(4096)
975 chunk = f.read(4096)
976 if not chunk:
976 if not chunk:
977 break
977 break
978 req.write(z.compress(chunk))
978 req.write(z.compress(chunk))
979
979
980 req.write(z.flush())
980 req.write(z.flush())
981
981
982 def do_changegroupsubset(self, req):
982 def do_changegroupsubset(self, req):
983 req.httphdr("application/mercurial-0.1")
983 req.httphdr("application/mercurial-0.1")
984 bases = []
984 bases = []
985 heads = []
985 heads = []
986 if not self.allowpull:
986 if not self.allowpull:
987 return
987 return
988
988
989 if req.form.has_key('bases'):
989 if req.form.has_key('bases'):
990 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
990 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
991 if req.form.has_key('heads'):
991 if req.form.has_key('heads'):
992 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
992 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
993
993
994 z = zlib.compressobj()
994 z = zlib.compressobj()
995 f = self.repo.changegroupsubset(bases, heads, 'serve')
995 f = self.repo.changegroupsubset(bases, heads, 'serve')
996 while 1:
996 while 1:
997 chunk = f.read(4096)
997 chunk = f.read(4096)
998 if not chunk:
998 if not chunk:
999 break
999 break
1000 req.write(z.compress(chunk))
1000 req.write(z.compress(chunk))
1001
1001
1002 req.write(z.flush())
1002 req.write(z.flush())
1003
1003
1004 def do_archive(self, req):
1004 def do_archive(self, req):
1005 type_ = req.form['type'][0]
1005 type_ = req.form['type'][0]
1006 allowed = self.configlist("web", "allow_archive")
1006 allowed = self.configlist("web", "allow_archive")
1007 if (type_ in self.archives and (type_ in allowed or
1007 if (type_ in self.archives and (type_ in allowed or
1008 self.configbool("web", "allow" + type_, False))):
1008 self.configbool("web", "allow" + type_, False))):
1009 self.archive(req, req.form['node'][0], type_)
1009 self.archive(req, req.form['node'][0], type_)
1010 return
1010 return
1011
1011
1012 req.write(self.t("error"))
1012 req.write(self.t("error"))
1013
1013
1014 def do_static(self, req):
1014 def do_static(self, req):
1015 fname = req.form['file'][0]
1015 fname = req.form['file'][0]
1016 # a repo owner may set web.static in .hg/hgrc to get any file
1016 # a repo owner may set web.static in .hg/hgrc to get any file
1017 # readable by the user running the CGI script
1017 # readable by the user running the CGI script
1018 static = self.config("web", "static",
1018 static = self.config("web", "static",
1019 os.path.join(self.templatepath, "static"),
1019 os.path.join(self.templatepath, "static"),
1020 untrusted=False)
1020 untrusted=False)
1021 req.write(staticfile(static, fname, req)
1021 req.write(staticfile(static, fname, req)
1022 or self.t("error", error="%r not found" % fname))
1022 or self.t("error", error="%r not found" % fname))
1023
1023
1024 def do_capabilities(self, req):
1024 def do_capabilities(self, req):
1025 caps = ['lookup', 'changegroupsubset']
1025 caps = ['lookup', 'changegroupsubset']
1026 if self.configbool('server', 'uncompressed'):
1026 if self.configbool('server', 'uncompressed'):
1027 caps.append('stream=%d' % self.repo.changelog.version)
1027 caps.append('stream=%d' % self.repo.changelog.version)
1028 # XXX: make configurable and/or share code with do_unbundle:
1028 # XXX: make configurable and/or share code with do_unbundle:
1029 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
1029 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
1030 if unbundleversions:
1030 if unbundleversions:
1031 caps.append('unbundle=%s' % ','.join(unbundleversions))
1031 caps.append('unbundle=%s' % ','.join(unbundleversions))
1032 resp = ' '.join(caps)
1032 resp = ' '.join(caps)
1033 req.httphdr("application/mercurial-0.1", length=len(resp))
1033 req.httphdr("application/mercurial-0.1", length=len(resp))
1034 req.write(resp)
1034 req.write(resp)
1035
1035
1036 def check_perm(self, req, op, default):
1036 def check_perm(self, req, op, default):
1037 '''check permission for operation based on user auth.
1037 '''check permission for operation based on user auth.
1038 return true if op allowed, else false.
1038 return true if op allowed, else false.
1039 default is policy to use if no config given.'''
1039 default is policy to use if no config given.'''
1040
1040
1041 user = req.env.get('REMOTE_USER')
1041 user = req.env.get('REMOTE_USER')
1042
1042
1043 deny = self.configlist('web', 'deny_' + op)
1043 deny = self.configlist('web', 'deny_' + op)
1044 if deny and (not user or deny == ['*'] or user in deny):
1044 if deny and (not user or deny == ['*'] or user in deny):
1045 return False
1045 return False
1046
1046
1047 allow = self.configlist('web', 'allow_' + op)
1047 allow = self.configlist('web', 'allow_' + op)
1048 return (allow and (allow == ['*'] or user in allow)) or default
1048 return (allow and (allow == ['*'] or user in allow)) or default
1049
1049
1050 def do_unbundle(self, req):
1050 def do_unbundle(self, req):
1051 def bail(response, headers={}):
1051 def bail(response, headers={}):
1052 length = int(req.env['CONTENT_LENGTH'])
1052 length = int(req.env['CONTENT_LENGTH'])
1053 for s in util.filechunkiter(req, limit=length):
1053 for s in util.filechunkiter(req, limit=length):
1054 # drain incoming bundle, else client will not see
1054 # drain incoming bundle, else client will not see
1055 # response when run outside cgi script
1055 # response when run outside cgi script
1056 pass
1056 pass
1057 req.httphdr("application/mercurial-0.1", headers=headers)
1057 req.httphdr("application/mercurial-0.1", headers=headers)
1058 req.write('0\n')
1058 req.write('0\n')
1059 req.write(response)
1059 req.write(response)
1060
1060
1061 # require ssl by default, auth info cannot be sniffed and
1061 # require ssl by default, auth info cannot be sniffed and
1062 # replayed
1062 # replayed
1063 ssl_req = self.configbool('web', 'push_ssl', True)
1063 ssl_req = self.configbool('web', 'push_ssl', True)
1064 if ssl_req:
1064 if ssl_req:
1065 if not req.env.get('HTTPS'):
1065 if not req.env.get('HTTPS'):
1066 bail(_('ssl required\n'))
1066 bail(_('ssl required\n'))
1067 return
1067 return
1068 proto = 'https'
1068 proto = 'https'
1069 else:
1069 else:
1070 proto = 'http'
1070 proto = 'http'
1071
1071
1072 # do not allow push unless explicitly allowed
1072 # do not allow push unless explicitly allowed
1073 if not self.check_perm(req, 'push', False):
1073 if not self.check_perm(req, 'push', False):
1074 bail(_('push not authorized\n'),
1074 bail(_('push not authorized\n'),
1075 headers={'status': '401 Unauthorized'})
1075 headers={'status': '401 Unauthorized'})
1076 return
1076 return
1077
1077
1078 their_heads = req.form['heads'][0].split(' ')
1078 their_heads = req.form['heads'][0].split(' ')
1079
1079
1080 def check_heads():
1080 def check_heads():
1081 heads = map(hex, self.repo.heads())
1081 heads = map(hex, self.repo.heads())
1082 return their_heads == [hex('force')] or their_heads == heads
1082 return their_heads == [hex('force')] or their_heads == heads
1083
1083
1084 # fail early if possible
1084 # fail early if possible
1085 if not check_heads():
1085 if not check_heads():
1086 bail(_('unsynced changes\n'))
1086 bail(_('unsynced changes\n'))
1087 return
1087 return
1088
1088
1089 req.httphdr("application/mercurial-0.1")
1089 req.httphdr("application/mercurial-0.1")
1090
1090
1091 # do not lock repo until all changegroup data is
1091 # do not lock repo until all changegroup data is
1092 # streamed. save to temporary file.
1092 # streamed. save to temporary file.
1093
1093
1094 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1094 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1095 fp = os.fdopen(fd, 'wb+')
1095 fp = os.fdopen(fd, 'wb+')
1096 try:
1096 try:
1097 length = int(req.env['CONTENT_LENGTH'])
1097 length = int(req.env['CONTENT_LENGTH'])
1098 for s in util.filechunkiter(req, limit=length):
1098 for s in util.filechunkiter(req, limit=length):
1099 fp.write(s)
1099 fp.write(s)
1100
1100
1101 try:
1101 try:
1102 lock = self.repo.lock()
1102 lock = self.repo.lock()
1103 try:
1103 try:
1104 if not check_heads():
1104 if not check_heads():
1105 req.write('0\n')
1105 req.write('0\n')
1106 req.write(_('unsynced changes\n'))
1106 req.write(_('unsynced changes\n'))
1107 return
1107 return
1108
1108
1109 fp.seek(0)
1109 fp.seek(0)
1110 header = fp.read(6)
1110 header = fp.read(6)
1111 if not header.startswith("HG"):
1111 if not header.startswith("HG"):
1112 # old client with uncompressed bundle
1112 # old client with uncompressed bundle
1113 def generator(f):
1113 def generator(f):
1114 yield header
1114 yield header
1115 for chunk in f:
1115 for chunk in f:
1116 yield chunk
1116 yield chunk
1117 elif not header.startswith("HG10"):
1117 elif not header.startswith("HG10"):
1118 req.write("0\n")
1118 req.write("0\n")
1119 req.write(_("unknown bundle version\n"))
1119 req.write(_("unknown bundle version\n"))
1120 return
1120 return
1121 elif header == "HG10GZ":
1121 elif header == "HG10GZ":
1122 def generator(f):
1122 def generator(f):
1123 zd = zlib.decompressobj()
1123 zd = zlib.decompressobj()
1124 for chunk in f:
1124 for chunk in f:
1125 yield zd.decompress(chunk)
1125 yield zd.decompress(chunk)
1126 elif header == "HG10BZ":
1126 elif header == "HG10BZ":
1127 def generator(f):
1127 def generator(f):
1128 zd = bz2.BZ2Decompressor()
1128 zd = bz2.BZ2Decompressor()
1129 zd.decompress("BZ")
1129 zd.decompress("BZ")
1130 for chunk in f:
1130 for chunk in f:
1131 yield zd.decompress(chunk)
1131 yield zd.decompress(chunk)
1132 elif header == "HG10UN":
1132 elif header == "HG10UN":
1133 def generator(f):
1133 def generator(f):
1134 for chunk in f:
1134 for chunk in f:
1135 yield chunk
1135 yield chunk
1136 else:
1136 else:
1137 req.write("0\n")
1137 req.write("0\n")
1138 req.write(_("unknown bundle compression type\n"))
1138 req.write(_("unknown bundle compression type\n"))
1139 return
1139 return
1140 gen = generator(util.filechunkiter(fp, 4096))
1140 gen = generator(util.filechunkiter(fp, 4096))
1141
1141
1142 # send addchangegroup output to client
1142 # send addchangegroup output to client
1143
1143
1144 old_stdout = sys.stdout
1144 old_stdout = sys.stdout
1145 sys.stdout = cStringIO.StringIO()
1145 sys.stdout = cStringIO.StringIO()
1146
1146
1147 try:
1147 try:
1148 url = 'remote:%s:%s' % (proto,
1148 url = 'remote:%s:%s' % (proto,
1149 req.env.get('REMOTE_HOST', ''))
1149 req.env.get('REMOTE_HOST', ''))
1150 try:
1150 try:
1151 ret = self.repo.addchangegroup(
1151 ret = self.repo.addchangegroup(
1152 util.chunkbuffer(gen), 'serve', url)
1152 util.chunkbuffer(gen), 'serve', url)
1153 except util.Abort, inst:
1153 except util.Abort, inst:
1154 sys.stdout.write("abort: %s\n" % inst)
1154 sys.stdout.write("abort: %s\n" % inst)
1155 ret = 0
1155 ret = 0
1156 finally:
1156 finally:
1157 val = sys.stdout.getvalue()
1157 val = sys.stdout.getvalue()
1158 sys.stdout = old_stdout
1158 sys.stdout = old_stdout
1159 req.write('%d\n' % ret)
1159 req.write('%d\n' % ret)
1160 req.write(val)
1160 req.write(val)
1161 finally:
1161 finally:
1162 lock.release()
1162 lock.release()
1163 except (OSError, IOError), inst:
1163 except (OSError, IOError), inst:
1164 req.write('0\n')
1164 req.write('0\n')
1165 filename = getattr(inst, 'filename', '')
1165 filename = getattr(inst, 'filename', '')
1166 # Don't send our filesystem layout to the client
1166 # Don't send our filesystem layout to the client
1167 if filename.startswith(self.repo.root):
1167 if filename.startswith(self.repo.root):
1168 filename = filename[len(self.repo.root)+1:]
1168 filename = filename[len(self.repo.root)+1:]
1169 else:
1169 else:
1170 filename = ''
1170 filename = ''
1171 error = getattr(inst, 'strerror', 'Unknown error')
1171 error = getattr(inst, 'strerror', 'Unknown error')
1172 req.write('%s: %s\n' % (error, filename))
1172 req.write('%s: %s\n' % (error, filename))
1173 finally:
1173 finally:
1174 fp.close()
1174 fp.close()
1175 os.unlink(tempname)
1175 os.unlink(tempname)
1176
1176
1177 def do_stream_out(self, req):
1177 def do_stream_out(self, req):
1178 req.httphdr("application/mercurial-0.1")
1178 req.httphdr("application/mercurial-0.1")
1179 streamclone.stream_out(self.repo, req)
1179 streamclone.stream_out(self.repo, req)
General Comments 0
You need to be logged in to leave comments. Login now