##// END OF EJS Templates
fix some imports; pushing bz2 bundles over http is currently untested
Dirkjan Ochtman -
r5595:b95b2525 default
parent child Browse files
Show More
@@ -1,897 +1,897 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, mimetools, cStringIO, sys, urllib, bz2
9 import os, mimetypes, re, mimetools, cStringIO
10 from mercurial.node import *
10 from mercurial.node import *
11 from mercurial import mdiff, ui, hg, util, archival, patch
11 from mercurial import mdiff, ui, hg, util, archival, patch
12 from mercurial import revlog, templater
12 from mercurial import revlog, templater
13 from common import ErrorResponse, get_mtime, style_map, paritygen
13 from common import ErrorResponse, get_mtime, style_map, paritygen
14 from request import wsgirequest
14 from request import wsgirequest
15 import webcommands
15 import webcommands
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 isinstance(repo, str):
67 if isinstance(repo, str):
68 parentui = ui.ui(report_untrusted=False, interactive=False)
68 parentui = ui.ui(report_untrusted=False, interactive=False)
69 self.repo = hg.repository(parentui, repo)
69 self.repo = hg.repository(parentui, repo)
70 else:
70 else:
71 self.repo = repo
71 self.repo = repo
72
72
73 self.mtime = -1
73 self.mtime = -1
74 self.reponame = name
74 self.reponame = name
75 self.archives = 'zip', 'gz', 'bz2'
75 self.archives = 'zip', 'gz', 'bz2'
76 self.stripecount = 1
76 self.stripecount = 1
77 # a repo owner may set web.templates in .hg/hgrc to get any file
77 # a repo owner may set web.templates in .hg/hgrc to get any file
78 # readable by the user running the CGI script
78 # readable by the user running the CGI script
79 self.templatepath = self.config("web", "templates",
79 self.templatepath = self.config("web", "templates",
80 templater.templatepath(),
80 templater.templatepath(),
81 untrusted=False)
81 untrusted=False)
82
82
83 # The CGI scripts are often run by a user different from the repo owner.
83 # The CGI scripts are often run by a user different from the repo owner.
84 # Trust the settings from the .hg/hgrc files by default.
84 # Trust the settings from the .hg/hgrc files by default.
85 def config(self, section, name, default=None, untrusted=True):
85 def config(self, section, name, default=None, untrusted=True):
86 return self.repo.ui.config(section, name, default,
86 return self.repo.ui.config(section, name, default,
87 untrusted=untrusted)
87 untrusted=untrusted)
88
88
89 def configbool(self, section, name, default=False, untrusted=True):
89 def configbool(self, section, name, default=False, untrusted=True):
90 return self.repo.ui.configbool(section, name, default,
90 return self.repo.ui.configbool(section, name, default,
91 untrusted=untrusted)
91 untrusted=untrusted)
92
92
93 def configlist(self, section, name, default=None, untrusted=True):
93 def configlist(self, section, name, default=None, untrusted=True):
94 return self.repo.ui.configlist(section, name, default,
94 return self.repo.ui.configlist(section, name, default,
95 untrusted=untrusted)
95 untrusted=untrusted)
96
96
97 def refresh(self):
97 def refresh(self):
98 mtime = get_mtime(self.repo.root)
98 mtime = get_mtime(self.repo.root)
99 if mtime != self.mtime:
99 if mtime != self.mtime:
100 self.mtime = mtime
100 self.mtime = mtime
101 self.repo = hg.repository(self.repo.ui, self.repo.root)
101 self.repo = hg.repository(self.repo.ui, self.repo.root)
102 self.maxchanges = int(self.config("web", "maxchanges", 10))
102 self.maxchanges = int(self.config("web", "maxchanges", 10))
103 self.stripecount = int(self.config("web", "stripes", 1))
103 self.stripecount = int(self.config("web", "stripes", 1))
104 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
104 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
105 self.maxfiles = int(self.config("web", "maxfiles", 10))
105 self.maxfiles = int(self.config("web", "maxfiles", 10))
106 self.allowpull = self.configbool("web", "allowpull", True)
106 self.allowpull = self.configbool("web", "allowpull", True)
107 self.encoding = self.config("web", "encoding", util._encoding)
107 self.encoding = self.config("web", "encoding", util._encoding)
108
108
109 def run(self):
109 def run(self):
110 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
110 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
111 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
111 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
112 import mercurial.hgweb.wsgicgi as wsgicgi
112 import mercurial.hgweb.wsgicgi as wsgicgi
113 wsgicgi.launch(self)
113 wsgicgi.launch(self)
114
114
115 def __call__(self, env, respond):
115 def __call__(self, env, respond):
116 req = wsgirequest(env, respond)
116 req = wsgirequest(env, respond)
117 self.run_wsgi(req)
117 self.run_wsgi(req)
118 return req
118 return req
119
119
120 def run_wsgi(self, req):
120 def run_wsgi(self, req):
121 def header(**map):
121 def header(**map):
122 header_file = cStringIO.StringIO(
122 header_file = cStringIO.StringIO(
123 ''.join(self.t("header", encoding=self.encoding, **map)))
123 ''.join(self.t("header", encoding=self.encoding, **map)))
124 msg = mimetools.Message(header_file, 0)
124 msg = mimetools.Message(header_file, 0)
125 req.header(msg.items())
125 req.header(msg.items())
126 yield header_file.read()
126 yield header_file.read()
127
127
128 def rawfileheader(**map):
128 def rawfileheader(**map):
129 req.header([('Content-type', map['mimetype']),
129 req.header([('Content-type', map['mimetype']),
130 ('Content-disposition', 'filename=%s' % map['file']),
130 ('Content-disposition', 'filename=%s' % map['file']),
131 ('Content-length', str(len(map['raw'])))])
131 ('Content-length', str(len(map['raw'])))])
132 yield ''
132 yield ''
133
133
134 def footer(**map):
134 def footer(**map):
135 yield self.t("footer", **map)
135 yield self.t("footer", **map)
136
136
137 def motd(**map):
137 def motd(**map):
138 yield self.config("web", "motd", "")
138 yield self.config("web", "motd", "")
139
139
140 def expand_form(form):
140 def expand_form(form):
141 shortcuts = {
141 shortcuts = {
142 'cl': [('cmd', ['changelog']), ('rev', None)],
142 'cl': [('cmd', ['changelog']), ('rev', None)],
143 'sl': [('cmd', ['shortlog']), ('rev', None)],
143 'sl': [('cmd', ['shortlog']), ('rev', None)],
144 'cs': [('cmd', ['changeset']), ('node', None)],
144 'cs': [('cmd', ['changeset']), ('node', None)],
145 'f': [('cmd', ['file']), ('filenode', None)],
145 'f': [('cmd', ['file']), ('filenode', None)],
146 'fl': [('cmd', ['filelog']), ('filenode', None)],
146 'fl': [('cmd', ['filelog']), ('filenode', None)],
147 'fd': [('cmd', ['filediff']), ('node', None)],
147 'fd': [('cmd', ['filediff']), ('node', None)],
148 'fa': [('cmd', ['annotate']), ('filenode', None)],
148 'fa': [('cmd', ['annotate']), ('filenode', None)],
149 'mf': [('cmd', ['manifest']), ('manifest', None)],
149 'mf': [('cmd', ['manifest']), ('manifest', None)],
150 'ca': [('cmd', ['archive']), ('node', None)],
150 'ca': [('cmd', ['archive']), ('node', None)],
151 'tags': [('cmd', ['tags'])],
151 'tags': [('cmd', ['tags'])],
152 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
152 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
153 'static': [('cmd', ['static']), ('file', None)]
153 'static': [('cmd', ['static']), ('file', None)]
154 }
154 }
155
155
156 for k in shortcuts.iterkeys():
156 for k in shortcuts.iterkeys():
157 if form.has_key(k):
157 if form.has_key(k):
158 for name, value in shortcuts[k]:
158 for name, value in shortcuts[k]:
159 if value is None:
159 if value is None:
160 value = form[k]
160 value = form[k]
161 form[name] = value
161 form[name] = value
162 del form[k]
162 del form[k]
163
163
164 def rewrite_request(req):
164 def rewrite_request(req):
165 '''translate new web interface to traditional format'''
165 '''translate new web interface to traditional format'''
166
166
167 req.url = req.env['SCRIPT_NAME']
167 req.url = req.env['SCRIPT_NAME']
168 if not req.url.endswith('/'):
168 if not req.url.endswith('/'):
169 req.url += '/'
169 req.url += '/'
170 if req.env.has_key('REPO_NAME'):
170 if req.env.has_key('REPO_NAME'):
171 req.url += req.env['REPO_NAME'] + '/'
171 req.url += req.env['REPO_NAME'] + '/'
172
172
173 if req.env.get('PATH_INFO'):
173 if req.env.get('PATH_INFO'):
174 parts = req.env.get('PATH_INFO').strip('/').split('/')
174 parts = req.env.get('PATH_INFO').strip('/').split('/')
175 repo_parts = req.env.get('REPO_NAME', '').split('/')
175 repo_parts = req.env.get('REPO_NAME', '').split('/')
176 if parts[:len(repo_parts)] == repo_parts:
176 if parts[:len(repo_parts)] == repo_parts:
177 parts = parts[len(repo_parts):]
177 parts = parts[len(repo_parts):]
178 query = '/'.join(parts)
178 query = '/'.join(parts)
179 else:
179 else:
180 query = req.env['QUERY_STRING'].split('&', 1)[0]
180 query = req.env['QUERY_STRING'].split('&', 1)[0]
181 query = query.split(';', 1)[0]
181 query = query.split(';', 1)[0]
182
182
183 if req.form.has_key('cmd'):
183 if req.form.has_key('cmd'):
184 # old style
184 # old style
185 return
185 return
186
186
187 args = query.split('/', 2)
187 args = query.split('/', 2)
188 if not args or not args[0]:
188 if not args or not args[0]:
189 return
189 return
190
190
191 cmd = args.pop(0)
191 cmd = args.pop(0)
192 style = cmd.rfind('-')
192 style = cmd.rfind('-')
193 if style != -1:
193 if style != -1:
194 req.form['style'] = [cmd[:style]]
194 req.form['style'] = [cmd[:style]]
195 cmd = cmd[style+1:]
195 cmd = cmd[style+1:]
196 # avoid accepting e.g. style parameter as command
196 # avoid accepting e.g. style parameter as command
197 if hasattr(webcommands, cmd):
197 if hasattr(webcommands, cmd):
198 req.form['cmd'] = [cmd]
198 req.form['cmd'] = [cmd]
199
199
200 if args and args[0]:
200 if args and args[0]:
201 node = args.pop(0)
201 node = args.pop(0)
202 req.form['node'] = [node]
202 req.form['node'] = [node]
203 if args:
203 if args:
204 req.form['file'] = args
204 req.form['file'] = args
205
205
206 if cmd == 'static':
206 if cmd == 'static':
207 req.form['file'] = req.form['node']
207 req.form['file'] = req.form['node']
208 elif cmd == 'archive':
208 elif cmd == 'archive':
209 fn = req.form['node'][0]
209 fn = req.form['node'][0]
210 for type_, spec in self.archive_specs.iteritems():
210 for type_, spec in self.archive_specs.iteritems():
211 ext = spec[2]
211 ext = spec[2]
212 if fn.endswith(ext):
212 if fn.endswith(ext):
213 req.form['node'] = [fn[:-len(ext)]]
213 req.form['node'] = [fn[:-len(ext)]]
214 req.form['type'] = [type_]
214 req.form['type'] = [type_]
215
215
216 def sessionvars(**map):
216 def sessionvars(**map):
217 fields = []
217 fields = []
218 if req.form.has_key('style'):
218 if req.form.has_key('style'):
219 style = req.form['style'][0]
219 style = req.form['style'][0]
220 if style != self.config('web', 'style', ''):
220 if style != self.config('web', 'style', ''):
221 fields.append(('style', style))
221 fields.append(('style', style))
222
222
223 separator = req.url[-1] == '?' and ';' or '?'
223 separator = req.url[-1] == '?' and ';' or '?'
224 for name, value in fields:
224 for name, value in fields:
225 yield dict(name=name, value=value, separator=separator)
225 yield dict(name=name, value=value, separator=separator)
226 separator = ';'
226 separator = ';'
227
227
228 self.refresh()
228 self.refresh()
229
229
230 expand_form(req.form)
230 expand_form(req.form)
231 rewrite_request(req)
231 rewrite_request(req)
232
232
233 style = self.config("web", "style", "")
233 style = self.config("web", "style", "")
234 if req.form.has_key('style'):
234 if req.form.has_key('style'):
235 style = req.form['style'][0]
235 style = req.form['style'][0]
236 mapfile = style_map(self.templatepath, style)
236 mapfile = style_map(self.templatepath, style)
237
237
238 proto = req.env.get('wsgi.url_scheme')
238 proto = req.env.get('wsgi.url_scheme')
239 if proto == 'https':
239 if proto == 'https':
240 proto = 'https'
240 proto = 'https'
241 default_port = "443"
241 default_port = "443"
242 else:
242 else:
243 proto = 'http'
243 proto = 'http'
244 default_port = "80"
244 default_port = "80"
245
245
246 port = req.env["SERVER_PORT"]
246 port = req.env["SERVER_PORT"]
247 port = port != default_port and (":" + port) or ""
247 port = port != default_port and (":" + port) or ""
248 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
248 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
249 staticurl = self.config("web", "staticurl") or req.url + 'static/'
249 staticurl = self.config("web", "staticurl") or req.url + 'static/'
250 if not staticurl.endswith('/'):
250 if not staticurl.endswith('/'):
251 staticurl += '/'
251 staticurl += '/'
252
252
253 if not self.reponame:
253 if not self.reponame:
254 self.reponame = (self.config("web", "name")
254 self.reponame = (self.config("web", "name")
255 or req.env.get('REPO_NAME')
255 or req.env.get('REPO_NAME')
256 or req.url.strip('/') or self.repo.root)
256 or req.url.strip('/') or self.repo.root)
257
257
258 self.t = templater.templater(mapfile, templater.common_filters,
258 self.t = templater.templater(mapfile, templater.common_filters,
259 defaults={"url": req.url,
259 defaults={"url": req.url,
260 "staticurl": staticurl,
260 "staticurl": staticurl,
261 "urlbase": urlbase,
261 "urlbase": urlbase,
262 "repo": self.reponame,
262 "repo": self.reponame,
263 "header": header,
263 "header": header,
264 "footer": footer,
264 "footer": footer,
265 "motd": motd,
265 "motd": motd,
266 "rawfileheader": rawfileheader,
266 "rawfileheader": rawfileheader,
267 "sessionvars": sessionvars
267 "sessionvars": sessionvars
268 })
268 })
269
269
270 try:
270 try:
271 if not req.form.has_key('cmd'):
271 if not req.form.has_key('cmd'):
272 req.form['cmd'] = [self.t.cache['default']]
272 req.form['cmd'] = [self.t.cache['default']]
273
273
274 cmd = req.form['cmd'][0]
274 cmd = req.form['cmd'][0]
275
275
276 try:
276 try:
277 method = getattr(webcommands, cmd)
277 method = getattr(webcommands, cmd)
278 method(self, req)
278 method(self, req)
279 except revlog.LookupError, err:
279 except revlog.LookupError, err:
280 req.respond(404, self.t(
280 req.respond(404, self.t(
281 'error', error='revision not found: %s' % err.name))
281 'error', error='revision not found: %s' % err.name))
282 except (hg.RepoError, revlog.RevlogError), inst:
282 except (hg.RepoError, revlog.RevlogError), inst:
283 req.respond('500 Internal Server Error',
283 req.respond('500 Internal Server Error',
284 self.t('error', error=str(inst)))
284 self.t('error', error=str(inst)))
285 except ErrorResponse, inst:
285 except ErrorResponse, inst:
286 req.respond(inst.code, self.t('error', error=inst.message))
286 req.respond(inst.code, self.t('error', error=inst.message))
287 except AttributeError:
287 except AttributeError:
288 req.respond(400,
288 req.respond(400,
289 self.t('error', error='No such method: ' + cmd))
289 self.t('error', error='No such method: ' + cmd))
290 finally:
290 finally:
291 self.t = None
291 self.t = None
292
292
293 def archivelist(self, nodeid):
293 def archivelist(self, nodeid):
294 allowed = self.configlist("web", "allow_archive")
294 allowed = self.configlist("web", "allow_archive")
295 for i, spec in self.archive_specs.iteritems():
295 for i, spec in self.archive_specs.iteritems():
296 if i in allowed or self.configbool("web", "allow" + i):
296 if i in allowed or self.configbool("web", "allow" + i):
297 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
297 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
298
298
299 def listfilediffs(self, files, changeset):
299 def listfilediffs(self, files, changeset):
300 for f in files[:self.maxfiles]:
300 for f in files[:self.maxfiles]:
301 yield self.t("filedifflink", node=hex(changeset), file=f)
301 yield self.t("filedifflink", node=hex(changeset), file=f)
302 if len(files) > self.maxfiles:
302 if len(files) > self.maxfiles:
303 yield self.t("fileellipses")
303 yield self.t("fileellipses")
304
304
305 def siblings(self, siblings=[], hiderev=None, **args):
305 def siblings(self, siblings=[], hiderev=None, **args):
306 siblings = [s for s in siblings if s.node() != nullid]
306 siblings = [s for s in siblings if s.node() != nullid]
307 if len(siblings) == 1 and siblings[0].rev() == hiderev:
307 if len(siblings) == 1 and siblings[0].rev() == hiderev:
308 return
308 return
309 for s in siblings:
309 for s in siblings:
310 d = {'node': hex(s.node()), 'rev': s.rev()}
310 d = {'node': hex(s.node()), 'rev': s.rev()}
311 if hasattr(s, 'path'):
311 if hasattr(s, 'path'):
312 d['file'] = s.path()
312 d['file'] = s.path()
313 d.update(args)
313 d.update(args)
314 yield d
314 yield d
315
315
316 def renamelink(self, fl, node):
316 def renamelink(self, fl, node):
317 r = fl.renamed(node)
317 r = fl.renamed(node)
318 if r:
318 if r:
319 return [dict(file=r[0], node=hex(r[1]))]
319 return [dict(file=r[0], node=hex(r[1]))]
320 return []
320 return []
321
321
322 def nodetagsdict(self, node):
322 def nodetagsdict(self, node):
323 return [{"name": i} for i in self.repo.nodetags(node)]
323 return [{"name": i} for i in self.repo.nodetags(node)]
324
324
325 def nodebranchdict(self, ctx):
325 def nodebranchdict(self, ctx):
326 branches = []
326 branches = []
327 branch = ctx.branch()
327 branch = ctx.branch()
328 # If this is an empty repo, ctx.node() == nullid,
328 # If this is an empty repo, ctx.node() == nullid,
329 # ctx.branch() == 'default', but branchtags() is
329 # ctx.branch() == 'default', but branchtags() is
330 # an empty dict. Using dict.get avoids a traceback.
330 # an empty dict. Using dict.get avoids a traceback.
331 if self.repo.branchtags().get(branch) == ctx.node():
331 if self.repo.branchtags().get(branch) == ctx.node():
332 branches.append({"name": branch})
332 branches.append({"name": branch})
333 return branches
333 return branches
334
334
335 def showtag(self, t1, node=nullid, **args):
335 def showtag(self, t1, node=nullid, **args):
336 for t in self.repo.nodetags(node):
336 for t in self.repo.nodetags(node):
337 yield self.t(t1, tag=t, **args)
337 yield self.t(t1, tag=t, **args)
338
338
339 def diff(self, node1, node2, files):
339 def diff(self, node1, node2, files):
340 def filterfiles(filters, files):
340 def filterfiles(filters, files):
341 l = [x for x in files if x in filters]
341 l = [x for x in files if x in filters]
342
342
343 for t in filters:
343 for t in filters:
344 if t and t[-1] != os.sep:
344 if t and t[-1] != os.sep:
345 t += os.sep
345 t += os.sep
346 l += [x for x in files if x.startswith(t)]
346 l += [x for x in files if x.startswith(t)]
347 return l
347 return l
348
348
349 parity = paritygen(self.stripecount)
349 parity = paritygen(self.stripecount)
350 def diffblock(diff, f, fn):
350 def diffblock(diff, f, fn):
351 yield self.t("diffblock",
351 yield self.t("diffblock",
352 lines=prettyprintlines(diff),
352 lines=prettyprintlines(diff),
353 parity=parity.next(),
353 parity=parity.next(),
354 file=f,
354 file=f,
355 filenode=hex(fn or nullid))
355 filenode=hex(fn or nullid))
356
356
357 def prettyprintlines(diff):
357 def prettyprintlines(diff):
358 for l in diff.splitlines(1):
358 for l in diff.splitlines(1):
359 if l.startswith('+'):
359 if l.startswith('+'):
360 yield self.t("difflineplus", line=l)
360 yield self.t("difflineplus", line=l)
361 elif l.startswith('-'):
361 elif l.startswith('-'):
362 yield self.t("difflineminus", line=l)
362 yield self.t("difflineminus", line=l)
363 elif l.startswith('@'):
363 elif l.startswith('@'):
364 yield self.t("difflineat", line=l)
364 yield self.t("difflineat", line=l)
365 else:
365 else:
366 yield self.t("diffline", line=l)
366 yield self.t("diffline", line=l)
367
367
368 r = self.repo
368 r = self.repo
369 c1 = r.changectx(node1)
369 c1 = r.changectx(node1)
370 c2 = r.changectx(node2)
370 c2 = r.changectx(node2)
371 date1 = util.datestr(c1.date())
371 date1 = util.datestr(c1.date())
372 date2 = util.datestr(c2.date())
372 date2 = util.datestr(c2.date())
373
373
374 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
374 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
375 if files:
375 if files:
376 modified, added, removed = map(lambda x: filterfiles(files, x),
376 modified, added, removed = map(lambda x: filterfiles(files, x),
377 (modified, added, removed))
377 (modified, added, removed))
378
378
379 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
379 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
380 for f in modified:
380 for f in modified:
381 to = c1.filectx(f).data()
381 to = c1.filectx(f).data()
382 tn = c2.filectx(f).data()
382 tn = c2.filectx(f).data()
383 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
383 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
384 opts=diffopts), f, tn)
384 opts=diffopts), f, tn)
385 for f in added:
385 for f in added:
386 to = None
386 to = None
387 tn = c2.filectx(f).data()
387 tn = c2.filectx(f).data()
388 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
388 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
389 opts=diffopts), f, tn)
389 opts=diffopts), f, tn)
390 for f in removed:
390 for f in removed:
391 to = c1.filectx(f).data()
391 to = c1.filectx(f).data()
392 tn = None
392 tn = None
393 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
393 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
394 opts=diffopts), f, tn)
394 opts=diffopts), f, tn)
395
395
396 def changelog(self, ctx, shortlog=False):
396 def changelog(self, ctx, shortlog=False):
397 def changelist(limit=0,**map):
397 def changelist(limit=0,**map):
398 cl = self.repo.changelog
398 cl = self.repo.changelog
399 l = [] # build a list in forward order for efficiency
399 l = [] # build a list in forward order for efficiency
400 for i in xrange(start, end):
400 for i in xrange(start, end):
401 ctx = self.repo.changectx(i)
401 ctx = self.repo.changectx(i)
402 n = ctx.node()
402 n = ctx.node()
403
403
404 l.insert(0, {"parity": parity.next(),
404 l.insert(0, {"parity": parity.next(),
405 "author": ctx.user(),
405 "author": ctx.user(),
406 "parent": self.siblings(ctx.parents(), i - 1),
406 "parent": self.siblings(ctx.parents(), i - 1),
407 "child": self.siblings(ctx.children(), i + 1),
407 "child": self.siblings(ctx.children(), i + 1),
408 "changelogtag": self.showtag("changelogtag",n),
408 "changelogtag": self.showtag("changelogtag",n),
409 "desc": ctx.description(),
409 "desc": ctx.description(),
410 "date": ctx.date(),
410 "date": ctx.date(),
411 "files": self.listfilediffs(ctx.files(), n),
411 "files": self.listfilediffs(ctx.files(), n),
412 "rev": i,
412 "rev": i,
413 "node": hex(n),
413 "node": hex(n),
414 "tags": self.nodetagsdict(n),
414 "tags": self.nodetagsdict(n),
415 "branches": self.nodebranchdict(ctx)})
415 "branches": self.nodebranchdict(ctx)})
416
416
417 if limit > 0:
417 if limit > 0:
418 l = l[:limit]
418 l = l[:limit]
419
419
420 for e in l:
420 for e in l:
421 yield e
421 yield e
422
422
423 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
423 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
424 cl = self.repo.changelog
424 cl = self.repo.changelog
425 count = cl.count()
425 count = cl.count()
426 pos = ctx.rev()
426 pos = ctx.rev()
427 start = max(0, pos - maxchanges + 1)
427 start = max(0, pos - maxchanges + 1)
428 end = min(count, start + maxchanges)
428 end = min(count, start + maxchanges)
429 pos = end - 1
429 pos = end - 1
430 parity = paritygen(self.stripecount, offset=start-end)
430 parity = paritygen(self.stripecount, offset=start-end)
431
431
432 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
432 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
433
433
434 yield self.t(shortlog and 'shortlog' or 'changelog',
434 yield self.t(shortlog and 'shortlog' or 'changelog',
435 changenav=changenav,
435 changenav=changenav,
436 node=hex(cl.tip()),
436 node=hex(cl.tip()),
437 rev=pos, changesets=count,
437 rev=pos, changesets=count,
438 entries=lambda **x: changelist(limit=0,**x),
438 entries=lambda **x: changelist(limit=0,**x),
439 latestentry=lambda **x: changelist(limit=1,**x),
439 latestentry=lambda **x: changelist(limit=1,**x),
440 archives=self.archivelist("tip"))
440 archives=self.archivelist("tip"))
441
441
442 def search(self, query):
442 def search(self, query):
443
443
444 def changelist(**map):
444 def changelist(**map):
445 cl = self.repo.changelog
445 cl = self.repo.changelog
446 count = 0
446 count = 0
447 qw = query.lower().split()
447 qw = query.lower().split()
448
448
449 def revgen():
449 def revgen():
450 for i in xrange(cl.count() - 1, 0, -100):
450 for i in xrange(cl.count() - 1, 0, -100):
451 l = []
451 l = []
452 for j in xrange(max(0, i - 100), i):
452 for j in xrange(max(0, i - 100), i):
453 ctx = self.repo.changectx(j)
453 ctx = self.repo.changectx(j)
454 l.append(ctx)
454 l.append(ctx)
455 l.reverse()
455 l.reverse()
456 for e in l:
456 for e in l:
457 yield e
457 yield e
458
458
459 for ctx in revgen():
459 for ctx in revgen():
460 miss = 0
460 miss = 0
461 for q in qw:
461 for q in qw:
462 if not (q in ctx.user().lower() or
462 if not (q in ctx.user().lower() or
463 q in ctx.description().lower() or
463 q in ctx.description().lower() or
464 q in " ".join(ctx.files()).lower()):
464 q in " ".join(ctx.files()).lower()):
465 miss = 1
465 miss = 1
466 break
466 break
467 if miss:
467 if miss:
468 continue
468 continue
469
469
470 count += 1
470 count += 1
471 n = ctx.node()
471 n = ctx.node()
472
472
473 yield self.t('searchentry',
473 yield self.t('searchentry',
474 parity=parity.next(),
474 parity=parity.next(),
475 author=ctx.user(),
475 author=ctx.user(),
476 parent=self.siblings(ctx.parents()),
476 parent=self.siblings(ctx.parents()),
477 child=self.siblings(ctx.children()),
477 child=self.siblings(ctx.children()),
478 changelogtag=self.showtag("changelogtag",n),
478 changelogtag=self.showtag("changelogtag",n),
479 desc=ctx.description(),
479 desc=ctx.description(),
480 date=ctx.date(),
480 date=ctx.date(),
481 files=self.listfilediffs(ctx.files(), n),
481 files=self.listfilediffs(ctx.files(), n),
482 rev=ctx.rev(),
482 rev=ctx.rev(),
483 node=hex(n),
483 node=hex(n),
484 tags=self.nodetagsdict(n),
484 tags=self.nodetagsdict(n),
485 branches=self.nodebranchdict(ctx))
485 branches=self.nodebranchdict(ctx))
486
486
487 if count >= self.maxchanges:
487 if count >= self.maxchanges:
488 break
488 break
489
489
490 cl = self.repo.changelog
490 cl = self.repo.changelog
491 parity = paritygen(self.stripecount)
491 parity = paritygen(self.stripecount)
492
492
493 yield self.t('search',
493 yield self.t('search',
494 query=query,
494 query=query,
495 node=hex(cl.tip()),
495 node=hex(cl.tip()),
496 entries=changelist,
496 entries=changelist,
497 archives=self.archivelist("tip"))
497 archives=self.archivelist("tip"))
498
498
499 def changeset(self, ctx):
499 def changeset(self, ctx):
500 n = ctx.node()
500 n = ctx.node()
501 parents = ctx.parents()
501 parents = ctx.parents()
502 p1 = parents[0].node()
502 p1 = parents[0].node()
503
503
504 files = []
504 files = []
505 parity = paritygen(self.stripecount)
505 parity = paritygen(self.stripecount)
506 for f in ctx.files():
506 for f in ctx.files():
507 files.append(self.t("filenodelink",
507 files.append(self.t("filenodelink",
508 node=hex(n), file=f,
508 node=hex(n), file=f,
509 parity=parity.next()))
509 parity=parity.next()))
510
510
511 def diff(**map):
511 def diff(**map):
512 yield self.diff(p1, n, None)
512 yield self.diff(p1, n, None)
513
513
514 yield self.t('changeset',
514 yield self.t('changeset',
515 diff=diff,
515 diff=diff,
516 rev=ctx.rev(),
516 rev=ctx.rev(),
517 node=hex(n),
517 node=hex(n),
518 parent=self.siblings(parents),
518 parent=self.siblings(parents),
519 child=self.siblings(ctx.children()),
519 child=self.siblings(ctx.children()),
520 changesettag=self.showtag("changesettag",n),
520 changesettag=self.showtag("changesettag",n),
521 author=ctx.user(),
521 author=ctx.user(),
522 desc=ctx.description(),
522 desc=ctx.description(),
523 date=ctx.date(),
523 date=ctx.date(),
524 files=files,
524 files=files,
525 archives=self.archivelist(hex(n)),
525 archives=self.archivelist(hex(n)),
526 tags=self.nodetagsdict(n),
526 tags=self.nodetagsdict(n),
527 branches=self.nodebranchdict(ctx))
527 branches=self.nodebranchdict(ctx))
528
528
529 def filelog(self, fctx):
529 def filelog(self, fctx):
530 f = fctx.path()
530 f = fctx.path()
531 fl = fctx.filelog()
531 fl = fctx.filelog()
532 count = fl.count()
532 count = fl.count()
533 pagelen = self.maxshortchanges
533 pagelen = self.maxshortchanges
534 pos = fctx.filerev()
534 pos = fctx.filerev()
535 start = max(0, pos - pagelen + 1)
535 start = max(0, pos - pagelen + 1)
536 end = min(count, start + pagelen)
536 end = min(count, start + pagelen)
537 pos = end - 1
537 pos = end - 1
538 parity = paritygen(self.stripecount, offset=start-end)
538 parity = paritygen(self.stripecount, offset=start-end)
539
539
540 def entries(limit=0, **map):
540 def entries(limit=0, **map):
541 l = []
541 l = []
542
542
543 for i in xrange(start, end):
543 for i in xrange(start, end):
544 ctx = fctx.filectx(i)
544 ctx = fctx.filectx(i)
545 n = fl.node(i)
545 n = fl.node(i)
546
546
547 l.insert(0, {"parity": parity.next(),
547 l.insert(0, {"parity": parity.next(),
548 "filerev": i,
548 "filerev": i,
549 "file": f,
549 "file": f,
550 "node": hex(ctx.node()),
550 "node": hex(ctx.node()),
551 "author": ctx.user(),
551 "author": ctx.user(),
552 "date": ctx.date(),
552 "date": ctx.date(),
553 "rename": self.renamelink(fl, n),
553 "rename": self.renamelink(fl, n),
554 "parent": self.siblings(fctx.parents()),
554 "parent": self.siblings(fctx.parents()),
555 "child": self.siblings(fctx.children()),
555 "child": self.siblings(fctx.children()),
556 "desc": ctx.description()})
556 "desc": ctx.description()})
557
557
558 if limit > 0:
558 if limit > 0:
559 l = l[:limit]
559 l = l[:limit]
560
560
561 for e in l:
561 for e in l:
562 yield e
562 yield e
563
563
564 nodefunc = lambda x: fctx.filectx(fileid=x)
564 nodefunc = lambda x: fctx.filectx(fileid=x)
565 nav = revnavgen(pos, pagelen, count, nodefunc)
565 nav = revnavgen(pos, pagelen, count, nodefunc)
566 yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
566 yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
567 entries=lambda **x: entries(limit=0, **x),
567 entries=lambda **x: entries(limit=0, **x),
568 latestentry=lambda **x: entries(limit=1, **x))
568 latestentry=lambda **x: entries(limit=1, **x))
569
569
570 def filerevision(self, fctx):
570 def filerevision(self, fctx):
571 f = fctx.path()
571 f = fctx.path()
572 text = fctx.data()
572 text = fctx.data()
573 fl = fctx.filelog()
573 fl = fctx.filelog()
574 n = fctx.filenode()
574 n = fctx.filenode()
575 parity = paritygen(self.stripecount)
575 parity = paritygen(self.stripecount)
576
576
577 mt = mimetypes.guess_type(f)[0]
577 mt = mimetypes.guess_type(f)[0]
578 rawtext = text
578 rawtext = text
579 if util.binary(text):
579 if util.binary(text):
580 mt = mt or 'application/octet-stream'
580 mt = mt or 'application/octet-stream'
581 text = "(binary:%s)" % mt
581 text = "(binary:%s)" % mt
582 mt = mt or 'text/plain'
582 mt = mt or 'text/plain'
583
583
584 def lines():
584 def lines():
585 for l, t in enumerate(text.splitlines(1)):
585 for l, t in enumerate(text.splitlines(1)):
586 yield {"line": t,
586 yield {"line": t,
587 "linenumber": "% 6d" % (l + 1),
587 "linenumber": "% 6d" % (l + 1),
588 "parity": parity.next()}
588 "parity": parity.next()}
589
589
590 yield self.t("filerevision",
590 yield self.t("filerevision",
591 file=f,
591 file=f,
592 path=_up(f),
592 path=_up(f),
593 text=lines(),
593 text=lines(),
594 raw=rawtext,
594 raw=rawtext,
595 mimetype=mt,
595 mimetype=mt,
596 rev=fctx.rev(),
596 rev=fctx.rev(),
597 node=hex(fctx.node()),
597 node=hex(fctx.node()),
598 author=fctx.user(),
598 author=fctx.user(),
599 date=fctx.date(),
599 date=fctx.date(),
600 desc=fctx.description(),
600 desc=fctx.description(),
601 parent=self.siblings(fctx.parents()),
601 parent=self.siblings(fctx.parents()),
602 child=self.siblings(fctx.children()),
602 child=self.siblings(fctx.children()),
603 rename=self.renamelink(fl, n),
603 rename=self.renamelink(fl, n),
604 permissions=fctx.manifest().flags(f))
604 permissions=fctx.manifest().flags(f))
605
605
606 def fileannotate(self, fctx):
606 def fileannotate(self, fctx):
607 f = fctx.path()
607 f = fctx.path()
608 n = fctx.filenode()
608 n = fctx.filenode()
609 fl = fctx.filelog()
609 fl = fctx.filelog()
610 parity = paritygen(self.stripecount)
610 parity = paritygen(self.stripecount)
611
611
612 def annotate(**map):
612 def annotate(**map):
613 last = None
613 last = None
614 for f, l in fctx.annotate(follow=True):
614 for f, l in fctx.annotate(follow=True):
615 fnode = f.filenode()
615 fnode = f.filenode()
616 name = self.repo.ui.shortuser(f.user())
616 name = self.repo.ui.shortuser(f.user())
617
617
618 if last != fnode:
618 if last != fnode:
619 last = fnode
619 last = fnode
620
620
621 yield {"parity": parity.next(),
621 yield {"parity": parity.next(),
622 "node": hex(f.node()),
622 "node": hex(f.node()),
623 "rev": f.rev(),
623 "rev": f.rev(),
624 "author": name,
624 "author": name,
625 "file": f.path(),
625 "file": f.path(),
626 "line": l}
626 "line": l}
627
627
628 yield self.t("fileannotate",
628 yield self.t("fileannotate",
629 file=f,
629 file=f,
630 annotate=annotate,
630 annotate=annotate,
631 path=_up(f),
631 path=_up(f),
632 rev=fctx.rev(),
632 rev=fctx.rev(),
633 node=hex(fctx.node()),
633 node=hex(fctx.node()),
634 author=fctx.user(),
634 author=fctx.user(),
635 date=fctx.date(),
635 date=fctx.date(),
636 desc=fctx.description(),
636 desc=fctx.description(),
637 rename=self.renamelink(fl, n),
637 rename=self.renamelink(fl, n),
638 parent=self.siblings(fctx.parents()),
638 parent=self.siblings(fctx.parents()),
639 child=self.siblings(fctx.children()),
639 child=self.siblings(fctx.children()),
640 permissions=fctx.manifest().flags(f))
640 permissions=fctx.manifest().flags(f))
641
641
642 def manifest(self, ctx, path):
642 def manifest(self, ctx, path):
643 mf = ctx.manifest()
643 mf = ctx.manifest()
644 node = ctx.node()
644 node = ctx.node()
645
645
646 files = {}
646 files = {}
647 parity = paritygen(self.stripecount)
647 parity = paritygen(self.stripecount)
648
648
649 if path and path[-1] != "/":
649 if path and path[-1] != "/":
650 path += "/"
650 path += "/"
651 l = len(path)
651 l = len(path)
652 abspath = "/" + path
652 abspath = "/" + path
653
653
654 for f, n in mf.items():
654 for f, n in mf.items():
655 if f[:l] != path:
655 if f[:l] != path:
656 continue
656 continue
657 remain = f[l:]
657 remain = f[l:]
658 if "/" in remain:
658 if "/" in remain:
659 short = remain[:remain.index("/") + 1] # bleah
659 short = remain[:remain.index("/") + 1] # bleah
660 files[short] = (f, None)
660 files[short] = (f, None)
661 else:
661 else:
662 short = os.path.basename(remain)
662 short = os.path.basename(remain)
663 files[short] = (f, n)
663 files[short] = (f, n)
664
664
665 if not files:
665 if not files:
666 raise ErrorResponse(404, 'Path not found: ' + path)
666 raise ErrorResponse(404, 'Path not found: ' + path)
667
667
668 def filelist(**map):
668 def filelist(**map):
669 fl = files.keys()
669 fl = files.keys()
670 fl.sort()
670 fl.sort()
671 for f in fl:
671 for f in fl:
672 full, fnode = files[f]
672 full, fnode = files[f]
673 if not fnode:
673 if not fnode:
674 continue
674 continue
675
675
676 fctx = ctx.filectx(full)
676 fctx = ctx.filectx(full)
677 yield {"file": full,
677 yield {"file": full,
678 "parity": parity.next(),
678 "parity": parity.next(),
679 "basename": f,
679 "basename": f,
680 "date": fctx.changectx().date(),
680 "date": fctx.changectx().date(),
681 "size": fctx.size(),
681 "size": fctx.size(),
682 "permissions": mf.flags(full)}
682 "permissions": mf.flags(full)}
683
683
684 def dirlist(**map):
684 def dirlist(**map):
685 fl = files.keys()
685 fl = files.keys()
686 fl.sort()
686 fl.sort()
687 for f in fl:
687 for f in fl:
688 full, fnode = files[f]
688 full, fnode = files[f]
689 if fnode:
689 if fnode:
690 continue
690 continue
691
691
692 yield {"parity": parity.next(),
692 yield {"parity": parity.next(),
693 "path": "%s%s" % (abspath, f),
693 "path": "%s%s" % (abspath, f),
694 "basename": f[:-1]}
694 "basename": f[:-1]}
695
695
696 yield self.t("manifest",
696 yield self.t("manifest",
697 rev=ctx.rev(),
697 rev=ctx.rev(),
698 node=hex(node),
698 node=hex(node),
699 path=abspath,
699 path=abspath,
700 up=_up(abspath),
700 up=_up(abspath),
701 upparity=parity.next(),
701 upparity=parity.next(),
702 fentries=filelist,
702 fentries=filelist,
703 dentries=dirlist,
703 dentries=dirlist,
704 archives=self.archivelist(hex(node)),
704 archives=self.archivelist(hex(node)),
705 tags=self.nodetagsdict(node),
705 tags=self.nodetagsdict(node),
706 branches=self.nodebranchdict(ctx))
706 branches=self.nodebranchdict(ctx))
707
707
708 def tags(self):
708 def tags(self):
709 i = self.repo.tagslist()
709 i = self.repo.tagslist()
710 i.reverse()
710 i.reverse()
711 parity = paritygen(self.stripecount)
711 parity = paritygen(self.stripecount)
712
712
713 def entries(notip=False,limit=0, **map):
713 def entries(notip=False,limit=0, **map):
714 count = 0
714 count = 0
715 for k, n in i:
715 for k, n in i:
716 if notip and k == "tip":
716 if notip and k == "tip":
717 continue
717 continue
718 if limit > 0 and count >= limit:
718 if limit > 0 and count >= limit:
719 continue
719 continue
720 count = count + 1
720 count = count + 1
721 yield {"parity": parity.next(),
721 yield {"parity": parity.next(),
722 "tag": k,
722 "tag": k,
723 "date": self.repo.changectx(n).date(),
723 "date": self.repo.changectx(n).date(),
724 "node": hex(n)}
724 "node": hex(n)}
725
725
726 yield self.t("tags",
726 yield self.t("tags",
727 node=hex(self.repo.changelog.tip()),
727 node=hex(self.repo.changelog.tip()),
728 entries=lambda **x: entries(False,0, **x),
728 entries=lambda **x: entries(False,0, **x),
729 entriesnotip=lambda **x: entries(True,0, **x),
729 entriesnotip=lambda **x: entries(True,0, **x),
730 latestentry=lambda **x: entries(True,1, **x))
730 latestentry=lambda **x: entries(True,1, **x))
731
731
732 def summary(self):
732 def summary(self):
733 i = self.repo.tagslist()
733 i = self.repo.tagslist()
734 i.reverse()
734 i.reverse()
735
735
736 def tagentries(**map):
736 def tagentries(**map):
737 parity = paritygen(self.stripecount)
737 parity = paritygen(self.stripecount)
738 count = 0
738 count = 0
739 for k, n in i:
739 for k, n in i:
740 if k == "tip": # skip tip
740 if k == "tip": # skip tip
741 continue;
741 continue;
742
742
743 count += 1
743 count += 1
744 if count > 10: # limit to 10 tags
744 if count > 10: # limit to 10 tags
745 break;
745 break;
746
746
747 yield self.t("tagentry",
747 yield self.t("tagentry",
748 parity=parity.next(),
748 parity=parity.next(),
749 tag=k,
749 tag=k,
750 node=hex(n),
750 node=hex(n),
751 date=self.repo.changectx(n).date())
751 date=self.repo.changectx(n).date())
752
752
753
753
754 def branches(**map):
754 def branches(**map):
755 parity = paritygen(self.stripecount)
755 parity = paritygen(self.stripecount)
756
756
757 b = self.repo.branchtags()
757 b = self.repo.branchtags()
758 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
758 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
759 l.sort()
759 l.sort()
760
760
761 for r,n,t in l:
761 for r,n,t in l:
762 ctx = self.repo.changectx(n)
762 ctx = self.repo.changectx(n)
763
763
764 yield {'parity': parity.next(),
764 yield {'parity': parity.next(),
765 'branch': t,
765 'branch': t,
766 'node': hex(n),
766 'node': hex(n),
767 'date': ctx.date()}
767 'date': ctx.date()}
768
768
769 def changelist(**map):
769 def changelist(**map):
770 parity = paritygen(self.stripecount, offset=start-end)
770 parity = paritygen(self.stripecount, offset=start-end)
771 l = [] # build a list in forward order for efficiency
771 l = [] # build a list in forward order for efficiency
772 for i in xrange(start, end):
772 for i in xrange(start, end):
773 ctx = self.repo.changectx(i)
773 ctx = self.repo.changectx(i)
774 n = ctx.node()
774 n = ctx.node()
775 hn = hex(n)
775 hn = hex(n)
776
776
777 l.insert(0, self.t(
777 l.insert(0, self.t(
778 'shortlogentry',
778 'shortlogentry',
779 parity=parity.next(),
779 parity=parity.next(),
780 author=ctx.user(),
780 author=ctx.user(),
781 desc=ctx.description(),
781 desc=ctx.description(),
782 date=ctx.date(),
782 date=ctx.date(),
783 rev=i,
783 rev=i,
784 node=hn,
784 node=hn,
785 tags=self.nodetagsdict(n),
785 tags=self.nodetagsdict(n),
786 branches=self.nodebranchdict(ctx)))
786 branches=self.nodebranchdict(ctx)))
787
787
788 yield l
788 yield l
789
789
790 cl = self.repo.changelog
790 cl = self.repo.changelog
791 count = cl.count()
791 count = cl.count()
792 start = max(0, count - self.maxchanges)
792 start = max(0, count - self.maxchanges)
793 end = min(count, start + self.maxchanges)
793 end = min(count, start + self.maxchanges)
794
794
795 yield self.t("summary",
795 yield self.t("summary",
796 desc=self.config("web", "description", "unknown"),
796 desc=self.config("web", "description", "unknown"),
797 owner=(self.config("ui", "username") or # preferred
797 owner=(self.config("ui", "username") or # preferred
798 self.config("web", "contact") or # deprecated
798 self.config("web", "contact") or # deprecated
799 self.config("web", "author", "unknown")), # also
799 self.config("web", "author", "unknown")), # also
800 lastchange=cl.read(cl.tip())[2],
800 lastchange=cl.read(cl.tip())[2],
801 tags=tagentries,
801 tags=tagentries,
802 branches=branches,
802 branches=branches,
803 shortlog=changelist,
803 shortlog=changelist,
804 node=hex(cl.tip()),
804 node=hex(cl.tip()),
805 archives=self.archivelist("tip"))
805 archives=self.archivelist("tip"))
806
806
807 def filediff(self, fctx):
807 def filediff(self, fctx):
808 n = fctx.node()
808 n = fctx.node()
809 path = fctx.path()
809 path = fctx.path()
810 parents = fctx.parents()
810 parents = fctx.parents()
811 p1 = parents and parents[0].node() or nullid
811 p1 = parents and parents[0].node() or nullid
812
812
813 def diff(**map):
813 def diff(**map):
814 yield self.diff(p1, n, [path])
814 yield self.diff(p1, n, [path])
815
815
816 yield self.t("filediff",
816 yield self.t("filediff",
817 file=path,
817 file=path,
818 node=hex(n),
818 node=hex(n),
819 rev=fctx.rev(),
819 rev=fctx.rev(),
820 parent=self.siblings(parents),
820 parent=self.siblings(parents),
821 child=self.siblings(fctx.children()),
821 child=self.siblings(fctx.children()),
822 diff=diff)
822 diff=diff)
823
823
824 archive_specs = {
824 archive_specs = {
825 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
825 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
826 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
826 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
827 'zip': ('application/zip', 'zip', '.zip', None),
827 'zip': ('application/zip', 'zip', '.zip', None),
828 }
828 }
829
829
830 def archive(self, req, key, type_):
830 def archive(self, req, key, type_):
831 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
831 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
832 cnode = self.repo.lookup(key)
832 cnode = self.repo.lookup(key)
833 arch_version = key
833 arch_version = key
834 if cnode == key or key == 'tip':
834 if cnode == key or key == 'tip':
835 arch_version = short(cnode)
835 arch_version = short(cnode)
836 name = "%s-%s" % (reponame, arch_version)
836 name = "%s-%s" % (reponame, arch_version)
837 mimetype, artype, extension, encoding = self.archive_specs[type_]
837 mimetype, artype, extension, encoding = self.archive_specs[type_]
838 headers = [('Content-type', mimetype),
838 headers = [('Content-type', mimetype),
839 ('Content-disposition', 'attachment; filename=%s%s' %
839 ('Content-disposition', 'attachment; filename=%s%s' %
840 (name, extension))]
840 (name, extension))]
841 if encoding:
841 if encoding:
842 headers.append(('Content-encoding', encoding))
842 headers.append(('Content-encoding', encoding))
843 req.header(headers)
843 req.header(headers)
844 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
844 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
845
845
846 # add tags to things
846 # add tags to things
847 # tags -> list of changesets corresponding to tags
847 # tags -> list of changesets corresponding to tags
848 # find tag, changeset, file
848 # find tag, changeset, file
849
849
850 def cleanpath(self, path):
850 def cleanpath(self, path):
851 path = path.lstrip('/')
851 path = path.lstrip('/')
852 return util.canonpath(self.repo.root, '', path)
852 return util.canonpath(self.repo.root, '', path)
853
853
854 def changectx(self, req):
854 def changectx(self, req):
855 if req.form.has_key('node'):
855 if req.form.has_key('node'):
856 changeid = req.form['node'][0]
856 changeid = req.form['node'][0]
857 elif req.form.has_key('manifest'):
857 elif req.form.has_key('manifest'):
858 changeid = req.form['manifest'][0]
858 changeid = req.form['manifest'][0]
859 else:
859 else:
860 changeid = self.repo.changelog.count() - 1
860 changeid = self.repo.changelog.count() - 1
861
861
862 try:
862 try:
863 ctx = self.repo.changectx(changeid)
863 ctx = self.repo.changectx(changeid)
864 except hg.RepoError:
864 except hg.RepoError:
865 man = self.repo.manifest
865 man = self.repo.manifest
866 mn = man.lookup(changeid)
866 mn = man.lookup(changeid)
867 ctx = self.repo.changectx(man.linkrev(mn))
867 ctx = self.repo.changectx(man.linkrev(mn))
868
868
869 return ctx
869 return ctx
870
870
871 def filectx(self, req):
871 def filectx(self, req):
872 path = self.cleanpath(req.form['file'][0])
872 path = self.cleanpath(req.form['file'][0])
873 if req.form.has_key('node'):
873 if req.form.has_key('node'):
874 changeid = req.form['node'][0]
874 changeid = req.form['node'][0]
875 else:
875 else:
876 changeid = req.form['filenode'][0]
876 changeid = req.form['filenode'][0]
877 try:
877 try:
878 ctx = self.repo.changectx(changeid)
878 ctx = self.repo.changectx(changeid)
879 fctx = ctx.filectx(path)
879 fctx = ctx.filectx(path)
880 except hg.RepoError:
880 except hg.RepoError:
881 fctx = self.repo.filectx(path, fileid=changeid)
881 fctx = self.repo.filectx(path, fileid=changeid)
882
882
883 return fctx
883 return fctx
884
884
885 def check_perm(self, req, op, default):
885 def check_perm(self, req, op, default):
886 '''check permission for operation based on user auth.
886 '''check permission for operation based on user auth.
887 return true if op allowed, else false.
887 return true if op allowed, else false.
888 default is policy to use if no config given.'''
888 default is policy to use if no config given.'''
889
889
890 user = req.env.get('REMOTE_USER')
890 user = req.env.get('REMOTE_USER')
891
891
892 deny = self.configlist('web', 'deny_' + op)
892 deny = self.configlist('web', 'deny_' + op)
893 if deny and (not user or deny == ['*'] or user in deny):
893 if deny and (not user or deny == ['*'] or user in deny):
894 return False
894 return False
895
895
896 allow = self.configlist('web', 'allow_' + op)
896 allow = self.configlist('web', 'allow_' + op)
897 return (allow and (allow == ['*'] or user in allow)) or default
897 return (allow and (allow == ['*'] or user in allow)) or default
@@ -1,320 +1,320 b''
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import cStringIO, zlib, tempfile, errno, os, sys
8 import cStringIO, zlib, bz2, tempfile, errno, os, sys
9 from mercurial import revlog, util, streamclone
9 from mercurial import revlog, util, streamclone
10 from mercurial.i18n import gettext as _
10 from mercurial.i18n import gettext as _
11 from mercurial.node import *
11 from mercurial.node import *
12 from common import staticfile
12 from common import staticfile
13
13
14 def log(web, req):
14 def log(web, req):
15 if req.form.has_key('file') and req.form['file'][0]:
15 if req.form.has_key('file') and req.form['file'][0]:
16 filelog(web, req)
16 filelog(web, req)
17 else:
17 else:
18 changelog(web, req)
18 changelog(web, req)
19
19
20 def file(web, req):
20 def file(web, req):
21 path = web.cleanpath(req.form.get('file', [''])[0])
21 path = web.cleanpath(req.form.get('file', [''])[0])
22 if path:
22 if path:
23 try:
23 try:
24 req.write(web.filerevision(web.filectx(req)))
24 req.write(web.filerevision(web.filectx(req)))
25 return
25 return
26 except revlog.LookupError:
26 except revlog.LookupError:
27 pass
27 pass
28
28
29 req.write(web.manifest(web.changectx(req), path))
29 req.write(web.manifest(web.changectx(req), path))
30
30
31 def changelog(web, req, shortlog = False):
31 def changelog(web, req, shortlog = False):
32 if req.form.has_key('node'):
32 if req.form.has_key('node'):
33 ctx = web.changectx(req)
33 ctx = web.changectx(req)
34 else:
34 else:
35 if req.form.has_key('rev'):
35 if req.form.has_key('rev'):
36 hi = req.form['rev'][0]
36 hi = req.form['rev'][0]
37 else:
37 else:
38 hi = web.repo.changelog.count() - 1
38 hi = web.repo.changelog.count() - 1
39 try:
39 try:
40 ctx = web.repo.changectx(hi)
40 ctx = web.repo.changectx(hi)
41 except hg.RepoError:
41 except hg.RepoError:
42 req.write(web.search(hi)) # XXX redirect to 404 page?
42 req.write(web.search(hi)) # XXX redirect to 404 page?
43 return
43 return
44
44
45 req.write(web.changelog(ctx, shortlog = shortlog))
45 req.write(web.changelog(ctx, shortlog = shortlog))
46
46
47 def shortlog(web, req):
47 def shortlog(web, req):
48 changelog(web, req, shortlog = True)
48 changelog(web, req, shortlog = True)
49
49
50 def changeset(web, req):
50 def changeset(web, req):
51 req.write(web.changeset(web.changectx(req)))
51 req.write(web.changeset(web.changectx(req)))
52
52
53 rev = changeset
53 rev = changeset
54
54
55 def manifest(web, req):
55 def manifest(web, req):
56 req.write(web.manifest(web.changectx(req),
56 req.write(web.manifest(web.changectx(req),
57 web.cleanpath(req.form['path'][0])))
57 web.cleanpath(req.form['path'][0])))
58
58
59 def tags(web, req):
59 def tags(web, req):
60 req.write(web.tags())
60 req.write(web.tags())
61
61
62 def summary(web, req):
62 def summary(web, req):
63 req.write(web.summary())
63 req.write(web.summary())
64
64
65 def filediff(web, req):
65 def filediff(web, req):
66 req.write(web.filediff(web.filectx(req)))
66 req.write(web.filediff(web.filectx(req)))
67
67
68 diff = filediff
68 diff = filediff
69
69
70 def annotate(web, req):
70 def annotate(web, req):
71 req.write(web.fileannotate(web.filectx(req)))
71 req.write(web.fileannotate(web.filectx(req)))
72
72
73 def filelog(web, req):
73 def filelog(web, req):
74 req.write(web.filelog(web.filectx(req)))
74 req.write(web.filelog(web.filectx(req)))
75
75
76 def lookup(web, req):
76 def lookup(web, req):
77 try:
77 try:
78 r = hex(web.repo.lookup(req.form['key'][0]))
78 r = hex(web.repo.lookup(req.form['key'][0]))
79 success = 1
79 success = 1
80 except Exception,inst:
80 except Exception,inst:
81 r = str(inst)
81 r = str(inst)
82 success = 0
82 success = 0
83 resp = "%s %s\n" % (success, r)
83 resp = "%s %s\n" % (success, r)
84 req.httphdr("application/mercurial-0.1", length=len(resp))
84 req.httphdr("application/mercurial-0.1", length=len(resp))
85 req.write(resp)
85 req.write(resp)
86
86
87 def heads(web, req):
87 def heads(web, req):
88 resp = " ".join(map(hex, web.repo.heads())) + "\n"
88 resp = " ".join(map(hex, web.repo.heads())) + "\n"
89 req.httphdr("application/mercurial-0.1", length=len(resp))
89 req.httphdr("application/mercurial-0.1", length=len(resp))
90 req.write(resp)
90 req.write(resp)
91
91
92 def branches(web, req):
92 def branches(web, req):
93 nodes = []
93 nodes = []
94 if req.form.has_key('nodes'):
94 if req.form.has_key('nodes'):
95 nodes = map(bin, req.form['nodes'][0].split(" "))
95 nodes = map(bin, req.form['nodes'][0].split(" "))
96 resp = cStringIO.StringIO()
96 resp = cStringIO.StringIO()
97 for b in web.repo.branches(nodes):
97 for b in web.repo.branches(nodes):
98 resp.write(" ".join(map(hex, b)) + "\n")
98 resp.write(" ".join(map(hex, b)) + "\n")
99 resp = resp.getvalue()
99 resp = resp.getvalue()
100 req.httphdr("application/mercurial-0.1", length=len(resp))
100 req.httphdr("application/mercurial-0.1", length=len(resp))
101 req.write(resp)
101 req.write(resp)
102
102
103 def between(web, req):
103 def between(web, req):
104 if req.form.has_key('pairs'):
104 if req.form.has_key('pairs'):
105 pairs = [map(bin, p.split("-"))
105 pairs = [map(bin, p.split("-"))
106 for p in req.form['pairs'][0].split(" ")]
106 for p in req.form['pairs'][0].split(" ")]
107 resp = cStringIO.StringIO()
107 resp = cStringIO.StringIO()
108 for b in web.repo.between(pairs):
108 for b in web.repo.between(pairs):
109 resp.write(" ".join(map(hex, b)) + "\n")
109 resp.write(" ".join(map(hex, b)) + "\n")
110 resp = resp.getvalue()
110 resp = resp.getvalue()
111 req.httphdr("application/mercurial-0.1", length=len(resp))
111 req.httphdr("application/mercurial-0.1", length=len(resp))
112 req.write(resp)
112 req.write(resp)
113
113
114 def changegroup(web, req):
114 def changegroup(web, req):
115 req.httphdr("application/mercurial-0.1")
115 req.httphdr("application/mercurial-0.1")
116 nodes = []
116 nodes = []
117 if not web.allowpull:
117 if not web.allowpull:
118 return
118 return
119
119
120 if req.form.has_key('roots'):
120 if req.form.has_key('roots'):
121 nodes = map(bin, req.form['roots'][0].split(" "))
121 nodes = map(bin, req.form['roots'][0].split(" "))
122
122
123 z = zlib.compressobj()
123 z = zlib.compressobj()
124 f = web.repo.changegroup(nodes, 'serve')
124 f = web.repo.changegroup(nodes, 'serve')
125 while 1:
125 while 1:
126 chunk = f.read(4096)
126 chunk = f.read(4096)
127 if not chunk:
127 if not chunk:
128 break
128 break
129 req.write(z.compress(chunk))
129 req.write(z.compress(chunk))
130
130
131 req.write(z.flush())
131 req.write(z.flush())
132
132
133 def changegroupsubset(web, req):
133 def changegroupsubset(web, req):
134 req.httphdr("application/mercurial-0.1")
134 req.httphdr("application/mercurial-0.1")
135 bases = []
135 bases = []
136 heads = []
136 heads = []
137 if not web.allowpull:
137 if not web.allowpull:
138 return
138 return
139
139
140 if req.form.has_key('bases'):
140 if req.form.has_key('bases'):
141 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
141 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
142 if req.form.has_key('heads'):
142 if req.form.has_key('heads'):
143 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
143 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
144
144
145 z = zlib.compressobj()
145 z = zlib.compressobj()
146 f = web.repo.changegroupsubset(bases, heads, 'serve')
146 f = web.repo.changegroupsubset(bases, heads, 'serve')
147 while 1:
147 while 1:
148 chunk = f.read(4096)
148 chunk = f.read(4096)
149 if not chunk:
149 if not chunk:
150 break
150 break
151 req.write(z.compress(chunk))
151 req.write(z.compress(chunk))
152
152
153 req.write(z.flush())
153 req.write(z.flush())
154
154
155 def archive(web, req):
155 def archive(web, req):
156 type_ = req.form['type'][0]
156 type_ = req.form['type'][0]
157 allowed = web.configlist("web", "allow_archive")
157 allowed = web.configlist("web", "allow_archive")
158 if (type_ in web.archives and (type_ in allowed or
158 if (type_ in web.archives and (type_ in allowed or
159 web.configbool("web", "allow" + type_, False))):
159 web.configbool("web", "allow" + type_, False))):
160 web.archive(req, req.form['node'][0], type_)
160 web.archive(req, req.form['node'][0], type_)
161 return
161 return
162
162
163 req.respond(400, web.t('error',
163 req.respond(400, web.t('error',
164 error='Unsupported archive type: %s' % type_))
164 error='Unsupported archive type: %s' % type_))
165
165
166 def static(web, req):
166 def static(web, req):
167 fname = req.form['file'][0]
167 fname = req.form['file'][0]
168 # a repo owner may set web.static in .hg/hgrc to get any file
168 # a repo owner may set web.static in .hg/hgrc to get any file
169 # readable by the user running the CGI script
169 # readable by the user running the CGI script
170 static = web.config("web", "static",
170 static = web.config("web", "static",
171 os.path.join(web.templatepath, "static"),
171 os.path.join(web.templatepath, "static"),
172 untrusted=False)
172 untrusted=False)
173 req.write(staticfile(static, fname, req))
173 req.write(staticfile(static, fname, req))
174
174
175 def capabilities(web, req):
175 def capabilities(web, req):
176 caps = ['lookup', 'changegroupsubset']
176 caps = ['lookup', 'changegroupsubset']
177 if web.configbool('server', 'uncompressed'):
177 if web.configbool('server', 'uncompressed'):
178 caps.append('stream=%d' % web.repo.changelog.version)
178 caps.append('stream=%d' % web.repo.changelog.version)
179 # XXX: make configurable and/or share code with do_unbundle:
179 # XXX: make configurable and/or share code with do_unbundle:
180 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
180 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
181 if unbundleversions:
181 if unbundleversions:
182 caps.append('unbundle=%s' % ','.join(unbundleversions))
182 caps.append('unbundle=%s' % ','.join(unbundleversions))
183 resp = ' '.join(caps)
183 resp = ' '.join(caps)
184 req.httphdr("application/mercurial-0.1", length=len(resp))
184 req.httphdr("application/mercurial-0.1", length=len(resp))
185 req.write(resp)
185 req.write(resp)
186
186
187 def unbundle(web, req):
187 def unbundle(web, req):
188 def bail(response, headers={}):
188 def bail(response, headers={}):
189 length = int(req.env['CONTENT_LENGTH'])
189 length = int(req.env['CONTENT_LENGTH'])
190 for s in util.filechunkiter(req, limit=length):
190 for s in util.filechunkiter(req, limit=length):
191 # drain incoming bundle, else client will not see
191 # drain incoming bundle, else client will not see
192 # response when run outside cgi script
192 # response when run outside cgi script
193 pass
193 pass
194 req.httphdr("application/mercurial-0.1", headers=headers)
194 req.httphdr("application/mercurial-0.1", headers=headers)
195 req.write('0\n')
195 req.write('0\n')
196 req.write(response)
196 req.write(response)
197
197
198 # require ssl by default, auth info cannot be sniffed and
198 # require ssl by default, auth info cannot be sniffed and
199 # replayed
199 # replayed
200 ssl_req = web.configbool('web', 'push_ssl', True)
200 ssl_req = web.configbool('web', 'push_ssl', True)
201 if ssl_req:
201 if ssl_req:
202 if req.env.get('wsgi.url_scheme') != 'https':
202 if req.env.get('wsgi.url_scheme') != 'https':
203 bail(_('ssl required\n'))
203 bail(_('ssl required\n'))
204 return
204 return
205 proto = 'https'
205 proto = 'https'
206 else:
206 else:
207 proto = 'http'
207 proto = 'http'
208
208
209 # do not allow push unless explicitly allowed
209 # do not allow push unless explicitly allowed
210 if not web.check_perm(req, 'push', False):
210 if not web.check_perm(req, 'push', False):
211 bail(_('push not authorized\n'),
211 bail(_('push not authorized\n'),
212 headers={'status': '401 Unauthorized'})
212 headers={'status': '401 Unauthorized'})
213 return
213 return
214
214
215 their_heads = req.form['heads'][0].split(' ')
215 their_heads = req.form['heads'][0].split(' ')
216
216
217 def check_heads():
217 def check_heads():
218 heads = map(hex, web.repo.heads())
218 heads = map(hex, web.repo.heads())
219 return their_heads == [hex('force')] or their_heads == heads
219 return their_heads == [hex('force')] or their_heads == heads
220
220
221 # fail early if possible
221 # fail early if possible
222 if not check_heads():
222 if not check_heads():
223 bail(_('unsynced changes\n'))
223 bail(_('unsynced changes\n'))
224 return
224 return
225
225
226 req.httphdr("application/mercurial-0.1")
226 req.httphdr("application/mercurial-0.1")
227
227
228 # do not lock repo until all changegroup data is
228 # do not lock repo until all changegroup data is
229 # streamed. save to temporary file.
229 # streamed. save to temporary file.
230
230
231 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
231 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
232 fp = os.fdopen(fd, 'wb+')
232 fp = os.fdopen(fd, 'wb+')
233 try:
233 try:
234 length = int(req.env['CONTENT_LENGTH'])
234 length = int(req.env['CONTENT_LENGTH'])
235 for s in util.filechunkiter(req, limit=length):
235 for s in util.filechunkiter(req, limit=length):
236 fp.write(s)
236 fp.write(s)
237
237
238 try:
238 try:
239 lock = web.repo.lock()
239 lock = web.repo.lock()
240 try:
240 try:
241 if not check_heads():
241 if not check_heads():
242 req.write('0\n')
242 req.write('0\n')
243 req.write(_('unsynced changes\n'))
243 req.write(_('unsynced changes\n'))
244 return
244 return
245
245
246 fp.seek(0)
246 fp.seek(0)
247 header = fp.read(6)
247 header = fp.read(6)
248 if not header.startswith("HG"):
248 if not header.startswith("HG"):
249 # old client with uncompressed bundle
249 # old client with uncompressed bundle
250 def generator(f):
250 def generator(f):
251 yield header
251 yield header
252 for chunk in f:
252 for chunk in f:
253 yield chunk
253 yield chunk
254 elif not header.startswith("HG10"):
254 elif not header.startswith("HG10"):
255 req.write("0\n")
255 req.write("0\n")
256 req.write(_("unknown bundle version\n"))
256 req.write(_("unknown bundle version\n"))
257 return
257 return
258 elif header == "HG10GZ":
258 elif header == "HG10GZ":
259 def generator(f):
259 def generator(f):
260 zd = zlib.decompressobj()
260 zd = zlib.decompressobj()
261 for chunk in f:
261 for chunk in f:
262 yield zd.decompress(chunk)
262 yield zd.decompress(chunk)
263 elif header == "HG10BZ":
263 elif header == "HG10BZ":
264 def generator(f):
264 def generator(f):
265 zd = bz2.BZ2Decompressor()
265 zd = bz2.BZ2Decompressor()
266 zd.decompress("BZ")
266 zd.decompress("BZ")
267 for chunk in f:
267 for chunk in f:
268 yield zd.decompress(chunk)
268 yield zd.decompress(chunk)
269 elif header == "HG10UN":
269 elif header == "HG10UN":
270 def generator(f):
270 def generator(f):
271 for chunk in f:
271 for chunk in f:
272 yield chunk
272 yield chunk
273 else:
273 else:
274 req.write("0\n")
274 req.write("0\n")
275 req.write(_("unknown bundle compression type\n"))
275 req.write(_("unknown bundle compression type\n"))
276 return
276 return
277 gen = generator(util.filechunkiter(fp, 4096))
277 gen = generator(util.filechunkiter(fp, 4096))
278
278
279 # send addchangegroup output to client
279 # send addchangegroup output to client
280
280
281 old_stdout = sys.stdout
281 old_stdout = sys.stdout
282 sys.stdout = cStringIO.StringIO()
282 sys.stdout = cStringIO.StringIO()
283
283
284 try:
284 try:
285 url = 'remote:%s:%s' % (proto,
285 url = 'remote:%s:%s' % (proto,
286 req.env.get('REMOTE_HOST', ''))
286 req.env.get('REMOTE_HOST', ''))
287 try:
287 try:
288 ret = web.repo.addchangegroup(
288 ret = web.repo.addchangegroup(
289 util.chunkbuffer(gen), 'serve', url)
289 util.chunkbuffer(gen), 'serve', url)
290 except util.Abort, inst:
290 except util.Abort, inst:
291 sys.stdout.write("abort: %s\n" % inst)
291 sys.stdout.write("abort: %s\n" % inst)
292 ret = 0
292 ret = 0
293 finally:
293 finally:
294 val = sys.stdout.getvalue()
294 val = sys.stdout.getvalue()
295 sys.stdout = old_stdout
295 sys.stdout = old_stdout
296 req.write('%d\n' % ret)
296 req.write('%d\n' % ret)
297 req.write(val)
297 req.write(val)
298 finally:
298 finally:
299 del lock
299 del lock
300 except (OSError, IOError), inst:
300 except (OSError, IOError), inst:
301 req.write('0\n')
301 req.write('0\n')
302 filename = getattr(inst, 'filename', '')
302 filename = getattr(inst, 'filename', '')
303 # Don't send our filesystem layout to the client
303 # Don't send our filesystem layout to the client
304 if filename.startswith(web.repo.root):
304 if filename.startswith(web.repo.root):
305 filename = filename[len(web.repo.root)+1:]
305 filename = filename[len(web.repo.root)+1:]
306 else:
306 else:
307 filename = ''
307 filename = ''
308 error = getattr(inst, 'strerror', 'Unknown error')
308 error = getattr(inst, 'strerror', 'Unknown error')
309 if inst.errno == errno.ENOENT:
309 if inst.errno == errno.ENOENT:
310 code = 404
310 code = 404
311 else:
311 else:
312 code = 500
312 code = 500
313 req.respond(code, '%s: %s\n' % (error, filename))
313 req.respond(code, '%s: %s\n' % (error, filename))
314 finally:
314 finally:
315 fp.close()
315 fp.close()
316 os.unlink(tempname)
316 os.unlink(tempname)
317
317
318 def stream_out(web, req):
318 def stream_out(web, req):
319 req.httphdr("application/mercurial-0.1")
319 req.httphdr("application/mercurial-0.1")
320 streamclone.stream_out(web.repo, req, untrusted=True)
320 streamclone.stream_out(web.repo, req, untrusted=True)
General Comments 0
You need to be logged in to leave comments. Login now