##// END OF EJS Templates
hgweb: move HTTP content types out of header templates...
Dirkjan Ochtman -
r5928:3340aa5a default
parent child Browse files
Show More
@@ -1,908 +1,906 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
9 import os, mimetypes, re
10 from mercurial.node import *
10 from mercurial.node import *
11 from mercurial import mdiff, ui, hg, util, archival, patch, hook
11 from mercurial import mdiff, ui, hg, util, archival, patch, hook
12 from mercurial import revlog, templater
12 from mercurial import revlog, templater
13 from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact
13 from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact
14 from request import wsgirequest
14 from request import wsgirequest
15 import webcommands, protocol
15 import webcommands, protocol
16
16
17 shortcuts = {
17 shortcuts = {
18 'cl': [('cmd', ['changelog']), ('rev', None)],
18 'cl': [('cmd', ['changelog']), ('rev', None)],
19 'sl': [('cmd', ['shortlog']), ('rev', None)],
19 'sl': [('cmd', ['shortlog']), ('rev', None)],
20 'cs': [('cmd', ['changeset']), ('node', None)],
20 'cs': [('cmd', ['changeset']), ('node', None)],
21 'f': [('cmd', ['file']), ('filenode', None)],
21 'f': [('cmd', ['file']), ('filenode', None)],
22 'fl': [('cmd', ['filelog']), ('filenode', None)],
22 'fl': [('cmd', ['filelog']), ('filenode', None)],
23 'fd': [('cmd', ['filediff']), ('node', None)],
23 'fd': [('cmd', ['filediff']), ('node', None)],
24 'fa': [('cmd', ['annotate']), ('filenode', None)],
24 'fa': [('cmd', ['annotate']), ('filenode', None)],
25 'mf': [('cmd', ['manifest']), ('manifest', None)],
25 'mf': [('cmd', ['manifest']), ('manifest', None)],
26 'ca': [('cmd', ['archive']), ('node', None)],
26 'ca': [('cmd', ['archive']), ('node', None)],
27 'tags': [('cmd', ['tags'])],
27 'tags': [('cmd', ['tags'])],
28 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
28 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
29 'static': [('cmd', ['static']), ('file', None)]
29 'static': [('cmd', ['static']), ('file', None)]
30 }
30 }
31
31
32 def _up(p):
32 def _up(p):
33 if p[0] != "/":
33 if p[0] != "/":
34 p = "/" + p
34 p = "/" + p
35 if p[-1] == "/":
35 if p[-1] == "/":
36 p = p[:-1]
36 p = p[:-1]
37 up = os.path.dirname(p)
37 up = os.path.dirname(p)
38 if up == "/":
38 if up == "/":
39 return "/"
39 return "/"
40 return up + "/"
40 return up + "/"
41
41
42 def revnavgen(pos, pagelen, limit, nodefunc):
42 def revnavgen(pos, pagelen, limit, nodefunc):
43 def seq(factor, limit=None):
43 def seq(factor, limit=None):
44 if limit:
44 if limit:
45 yield limit
45 yield limit
46 if limit >= 20 and limit <= 40:
46 if limit >= 20 and limit <= 40:
47 yield 50
47 yield 50
48 else:
48 else:
49 yield 1 * factor
49 yield 1 * factor
50 yield 3 * factor
50 yield 3 * factor
51 for f in seq(factor * 10):
51 for f in seq(factor * 10):
52 yield f
52 yield f
53
53
54 def nav(**map):
54 def nav(**map):
55 l = []
55 l = []
56 last = 0
56 last = 0
57 for f in seq(1, pagelen):
57 for f in seq(1, pagelen):
58 if f < pagelen or f <= last:
58 if f < pagelen or f <= last:
59 continue
59 continue
60 if f > limit:
60 if f > limit:
61 break
61 break
62 last = f
62 last = f
63 if pos + f < limit:
63 if pos + f < limit:
64 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
64 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
65 if pos - f >= 0:
65 if pos - f >= 0:
66 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
66 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
67
67
68 try:
68 try:
69 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
69 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
70
70
71 for label, node in l:
71 for label, node in l:
72 yield {"label": label, "node": node}
72 yield {"label": label, "node": node}
73
73
74 yield {"label": "tip", "node": "tip"}
74 yield {"label": "tip", "node": "tip"}
75 except hg.RepoError:
75 except hg.RepoError:
76 pass
76 pass
77
77
78 return nav
78 return nav
79
79
80 class hgweb(object):
80 class hgweb(object):
81 def __init__(self, repo, name=None):
81 def __init__(self, repo, name=None):
82 if isinstance(repo, str):
82 if isinstance(repo, str):
83 parentui = ui.ui(report_untrusted=False, interactive=False)
83 parentui = ui.ui(report_untrusted=False, interactive=False)
84 self.repo = hg.repository(parentui, repo)
84 self.repo = hg.repository(parentui, repo)
85 else:
85 else:
86 self.repo = repo
86 self.repo = repo
87
87
88 hook.redirect(True)
88 hook.redirect(True)
89 self.mtime = -1
89 self.mtime = -1
90 self.reponame = name
90 self.reponame = name
91 self.archives = 'zip', 'gz', 'bz2'
91 self.archives = 'zip', 'gz', 'bz2'
92 self.stripecount = 1
92 self.stripecount = 1
93 # a repo owner may set web.templates in .hg/hgrc to get any file
93 # a repo owner may set web.templates in .hg/hgrc to get any file
94 # readable by the user running the CGI script
94 # readable by the user running the CGI script
95 self.templatepath = self.config("web", "templates",
95 self.templatepath = self.config("web", "templates",
96 templater.templatepath(),
96 templater.templatepath(),
97 untrusted=False)
97 untrusted=False)
98
98
99 # The CGI scripts are often run by a user different from the repo owner.
99 # The CGI scripts are often run by a user different from the repo owner.
100 # Trust the settings from the .hg/hgrc files by default.
100 # Trust the settings from the .hg/hgrc files by default.
101 def config(self, section, name, default=None, untrusted=True):
101 def config(self, section, name, default=None, untrusted=True):
102 return self.repo.ui.config(section, name, default,
102 return self.repo.ui.config(section, name, default,
103 untrusted=untrusted)
103 untrusted=untrusted)
104
104
105 def configbool(self, section, name, default=False, untrusted=True):
105 def configbool(self, section, name, default=False, untrusted=True):
106 return self.repo.ui.configbool(section, name, default,
106 return self.repo.ui.configbool(section, name, default,
107 untrusted=untrusted)
107 untrusted=untrusted)
108
108
109 def configlist(self, section, name, default=None, untrusted=True):
109 def configlist(self, section, name, default=None, untrusted=True):
110 return self.repo.ui.configlist(section, name, default,
110 return self.repo.ui.configlist(section, name, default,
111 untrusted=untrusted)
111 untrusted=untrusted)
112
112
113 def refresh(self):
113 def refresh(self):
114 mtime = get_mtime(self.repo.root)
114 mtime = get_mtime(self.repo.root)
115 if mtime != self.mtime:
115 if mtime != self.mtime:
116 self.mtime = mtime
116 self.mtime = mtime
117 self.repo = hg.repository(self.repo.ui, self.repo.root)
117 self.repo = hg.repository(self.repo.ui, self.repo.root)
118 self.maxchanges = int(self.config("web", "maxchanges", 10))
118 self.maxchanges = int(self.config("web", "maxchanges", 10))
119 self.stripecount = int(self.config("web", "stripes", 1))
119 self.stripecount = int(self.config("web", "stripes", 1))
120 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
120 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
121 self.maxfiles = int(self.config("web", "maxfiles", 10))
121 self.maxfiles = int(self.config("web", "maxfiles", 10))
122 self.allowpull = self.configbool("web", "allowpull", True)
122 self.allowpull = self.configbool("web", "allowpull", True)
123 self.encoding = self.config("web", "encoding", util._encoding)
123 self.encoding = self.config("web", "encoding", util._encoding)
124
124
125 def run(self):
125 def run(self):
126 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
126 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
127 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
127 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
128 import mercurial.hgweb.wsgicgi as wsgicgi
128 import mercurial.hgweb.wsgicgi as wsgicgi
129 wsgicgi.launch(self)
129 wsgicgi.launch(self)
130
130
131 def __call__(self, env, respond):
131 def __call__(self, env, respond):
132 req = wsgirequest(env, respond)
132 req = wsgirequest(env, respond)
133 self.run_wsgi(req)
133 self.run_wsgi(req)
134 return req
134 return req
135
135
136 def run_wsgi(self, req):
136 def run_wsgi(self, req):
137
137
138 self.refresh()
138 self.refresh()
139
139
140 # expand form shortcuts
140 # expand form shortcuts
141
141
142 for k in shortcuts.iterkeys():
142 for k in shortcuts.iterkeys():
143 if k in req.form:
143 if k in req.form:
144 for name, value in shortcuts[k]:
144 for name, value in shortcuts[k]:
145 if value is None:
145 if value is None:
146 value = req.form[k]
146 value = req.form[k]
147 req.form[name] = value
147 req.form[name] = value
148 del req.form[k]
148 del req.form[k]
149
149
150 # work with CGI variables to create coherent structure
150 # work with CGI variables to create coherent structure
151 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
151 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
152
152
153 req.url = req.env['SCRIPT_NAME']
153 req.url = req.env['SCRIPT_NAME']
154 if not req.url.endswith('/'):
154 if not req.url.endswith('/'):
155 req.url += '/'
155 req.url += '/'
156 if 'REPO_NAME' in req.env:
156 if 'REPO_NAME' in req.env:
157 req.url += req.env['REPO_NAME'] + '/'
157 req.url += req.env['REPO_NAME'] + '/'
158
158
159 if req.env.get('PATH_INFO'):
159 if req.env.get('PATH_INFO'):
160 parts = req.env.get('PATH_INFO').strip('/').split('/')
160 parts = req.env.get('PATH_INFO').strip('/').split('/')
161 repo_parts = req.env.get('REPO_NAME', '').split('/')
161 repo_parts = req.env.get('REPO_NAME', '').split('/')
162 if parts[:len(repo_parts)] == repo_parts:
162 if parts[:len(repo_parts)] == repo_parts:
163 parts = parts[len(repo_parts):]
163 parts = parts[len(repo_parts):]
164 query = '/'.join(parts)
164 query = '/'.join(parts)
165 else:
165 else:
166 query = req.env['QUERY_STRING'].split('&', 1)[0]
166 query = req.env['QUERY_STRING'].split('&', 1)[0]
167 query = query.split(';', 1)[0]
167 query = query.split(';', 1)[0]
168
168
169 # translate user-visible url structure to internal structure
169 # translate user-visible url structure to internal structure
170
170
171 args = query.split('/', 2)
171 args = query.split('/', 2)
172 if 'cmd' not in req.form and args and args[0]:
172 if 'cmd' not in req.form and args and args[0]:
173
173
174 cmd = args.pop(0)
174 cmd = args.pop(0)
175 style = cmd.rfind('-')
175 style = cmd.rfind('-')
176 if style != -1:
176 if style != -1:
177 req.form['style'] = [cmd[:style]]
177 req.form['style'] = [cmd[:style]]
178 cmd = cmd[style+1:]
178 cmd = cmd[style+1:]
179
179
180 # avoid accepting e.g. style parameter as command
180 # avoid accepting e.g. style parameter as command
181 if hasattr(webcommands, cmd) or hasattr(protocol, cmd):
181 if hasattr(webcommands, cmd) or hasattr(protocol, cmd):
182 req.form['cmd'] = [cmd]
182 req.form['cmd'] = [cmd]
183
183
184 if args and args[0]:
184 if args and args[0]:
185 node = args.pop(0)
185 node = args.pop(0)
186 req.form['node'] = [node]
186 req.form['node'] = [node]
187 if args:
187 if args:
188 req.form['file'] = args
188 req.form['file'] = args
189
189
190 if cmd == 'static':
190 if cmd == 'static':
191 req.form['file'] = req.form['node']
191 req.form['file'] = req.form['node']
192 elif cmd == 'archive':
192 elif cmd == 'archive':
193 fn = req.form['node'][0]
193 fn = req.form['node'][0]
194 for type_, spec in self.archive_specs.iteritems():
194 for type_, spec in self.archive_specs.iteritems():
195 ext = spec[2]
195 ext = spec[2]
196 if fn.endswith(ext):
196 if fn.endswith(ext):
197 req.form['node'] = [fn[:-len(ext)]]
197 req.form['node'] = [fn[:-len(ext)]]
198 req.form['type'] = [type_]
198 req.form['type'] = [type_]
199
199
200 # actually process the request
200 # actually process the request
201
201
202 try:
202 try:
203
203
204 cmd = req.form.get('cmd', [''])[0]
204 cmd = req.form.get('cmd', [''])[0]
205 if hasattr(protocol, cmd):
205 if hasattr(protocol, cmd):
206 method = getattr(protocol, cmd)
206 method = getattr(protocol, cmd)
207 method(self, req)
207 method(self, req)
208 else:
208 else:
209
209
210 tmpl = self.templater(req)
210 tmpl = self.templater(req)
211 if cmd == '':
211 if cmd == '':
212 req.form['cmd'] = [tmpl.cache['default']]
212 req.form['cmd'] = [tmpl.cache['default']]
213 cmd = req.form['cmd'][0]
213 cmd = req.form['cmd'][0]
214
214
215 if cmd == 'file' and 'raw' in req.form.get('style', []):
215 if cmd == 'file' and 'raw' in req.form.get('style', []):
216 webcommands.rawfile(self, req, tmpl)
216 webcommands.rawfile(self, req, tmpl)
217 else:
217 else:
218 getattr(webcommands, cmd)(self, req, tmpl)
218 getattr(webcommands, cmd)(self, req, tmpl)
219
219
220 del tmpl
220 del tmpl
221
221
222 except revlog.LookupError, err:
222 except revlog.LookupError, err:
223 req.respond(404, tmpl(
223 req.respond(404, tmpl(
224 'error', error='revision not found: %s' % err.name))
224 'error', error='revision not found: %s' % err.name))
225 except (hg.RepoError, revlog.RevlogError), inst:
225 except (hg.RepoError, revlog.RevlogError), inst:
226 req.respond('500 Internal Server Error',
226 req.respond('500 Internal Server Error',
227 tmpl('error', error=str(inst)))
227 tmpl('error', error=str(inst)))
228 except ErrorResponse, inst:
228 except ErrorResponse, inst:
229 req.respond(inst.code, tmpl('error', error=inst.message))
229 req.respond(inst.code, tmpl('error', error=inst.message))
230 except AttributeError:
230 except AttributeError:
231 req.respond(400, tmpl('error', error='No such method: ' + cmd))
231 req.respond(400, tmpl('error', error='No such method: ' + cmd))
232
232
233 def templater(self, req):
233 def templater(self, req):
234
234
235 # determine scheme, port and server name
235 # determine scheme, port and server name
236 # this is needed to create absolute urls
236 # this is needed to create absolute urls
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 # some functions for the templater
253 # some functions for the templater
254
254
255 def header(**map):
255 def header(**map):
256 header_file = cStringIO.StringIO(
256 ctype = tmpl('mimetype', encoding=self.encoding)
257 ''.join(tmpl("header", encoding=self.encoding, **map)))
257 req.httphdr(templater.stringify(ctype))
258 msg = mimetools.Message(header_file, 0)
258 yield tmpl('header', encoding=self.encoding, **map)
259 req.header(msg.items())
260 yield header_file.read()
261
259
262 def footer(**map):
260 def footer(**map):
263 yield tmpl("footer", **map)
261 yield tmpl("footer", **map)
264
262
265 def motd(**map):
263 def motd(**map):
266 yield self.config("web", "motd", "")
264 yield self.config("web", "motd", "")
267
265
268 def sessionvars(**map):
266 def sessionvars(**map):
269 fields = []
267 fields = []
270 if 'style' in req.form:
268 if 'style' in req.form:
271 style = req.form['style'][0]
269 style = req.form['style'][0]
272 if style != self.config('web', 'style', ''):
270 if style != self.config('web', 'style', ''):
273 fields.append(('style', style))
271 fields.append(('style', style))
274
272
275 separator = req.url[-1] == '?' and ';' or '?'
273 separator = req.url[-1] == '?' and ';' or '?'
276 for name, value in fields:
274 for name, value in fields:
277 yield dict(name=name, value=value, separator=separator)
275 yield dict(name=name, value=value, separator=separator)
278 separator = ';'
276 separator = ';'
279
277
280 # figure out which style to use
278 # figure out which style to use
281
279
282 style = self.config("web", "style", "")
280 style = self.config("web", "style", "")
283 if 'style' in req.form:
281 if 'style' in req.form:
284 style = req.form['style'][0]
282 style = req.form['style'][0]
285 mapfile = style_map(self.templatepath, style)
283 mapfile = style_map(self.templatepath, style)
286
284
287 if not self.reponame:
285 if not self.reponame:
288 self.reponame = (self.config("web", "name")
286 self.reponame = (self.config("web", "name")
289 or req.env.get('REPO_NAME')
287 or req.env.get('REPO_NAME')
290 or req.url.strip('/') or self.repo.root)
288 or req.url.strip('/') or self.repo.root)
291
289
292 # create the templater
290 # create the templater
293
291
294 tmpl = templater.templater(mapfile, templater.common_filters,
292 tmpl = templater.templater(mapfile, templater.common_filters,
295 defaults={"url": req.url,
293 defaults={"url": req.url,
296 "staticurl": staticurl,
294 "staticurl": staticurl,
297 "urlbase": urlbase,
295 "urlbase": urlbase,
298 "repo": self.reponame,
296 "repo": self.reponame,
299 "header": header,
297 "header": header,
300 "footer": footer,
298 "footer": footer,
301 "motd": motd,
299 "motd": motd,
302 "sessionvars": sessionvars
300 "sessionvars": sessionvars
303 })
301 })
304 return tmpl
302 return tmpl
305
303
306 def archivelist(self, nodeid):
304 def archivelist(self, nodeid):
307 allowed = self.configlist("web", "allow_archive")
305 allowed = self.configlist("web", "allow_archive")
308 for i, spec in self.archive_specs.iteritems():
306 for i, spec in self.archive_specs.iteritems():
309 if i in allowed or self.configbool("web", "allow" + i):
307 if i in allowed or self.configbool("web", "allow" + i):
310 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
308 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
311
309
312 def listfilediffs(self, tmpl, files, changeset):
310 def listfilediffs(self, tmpl, files, changeset):
313 for f in files[:self.maxfiles]:
311 for f in files[:self.maxfiles]:
314 yield tmpl("filedifflink", node=hex(changeset), file=f)
312 yield tmpl("filedifflink", node=hex(changeset), file=f)
315 if len(files) > self.maxfiles:
313 if len(files) > self.maxfiles:
316 yield tmpl("fileellipses")
314 yield tmpl("fileellipses")
317
315
318 def siblings(self, siblings=[], hiderev=None, **args):
316 def siblings(self, siblings=[], hiderev=None, **args):
319 siblings = [s for s in siblings if s.node() != nullid]
317 siblings = [s for s in siblings if s.node() != nullid]
320 if len(siblings) == 1 and siblings[0].rev() == hiderev:
318 if len(siblings) == 1 and siblings[0].rev() == hiderev:
321 return
319 return
322 for s in siblings:
320 for s in siblings:
323 d = {'node': hex(s.node()), 'rev': s.rev()}
321 d = {'node': hex(s.node()), 'rev': s.rev()}
324 if hasattr(s, 'path'):
322 if hasattr(s, 'path'):
325 d['file'] = s.path()
323 d['file'] = s.path()
326 d.update(args)
324 d.update(args)
327 yield d
325 yield d
328
326
329 def renamelink(self, fl, node):
327 def renamelink(self, fl, node):
330 r = fl.renamed(node)
328 r = fl.renamed(node)
331 if r:
329 if r:
332 return [dict(file=r[0], node=hex(r[1]))]
330 return [dict(file=r[0], node=hex(r[1]))]
333 return []
331 return []
334
332
335 def nodetagsdict(self, node):
333 def nodetagsdict(self, node):
336 return [{"name": i} for i in self.repo.nodetags(node)]
334 return [{"name": i} for i in self.repo.nodetags(node)]
337
335
338 def nodebranchdict(self, ctx):
336 def nodebranchdict(self, ctx):
339 branches = []
337 branches = []
340 branch = ctx.branch()
338 branch = ctx.branch()
341 # If this is an empty repo, ctx.node() == nullid,
339 # If this is an empty repo, ctx.node() == nullid,
342 # ctx.branch() == 'default', but branchtags() is
340 # ctx.branch() == 'default', but branchtags() is
343 # an empty dict. Using dict.get avoids a traceback.
341 # an empty dict. Using dict.get avoids a traceback.
344 if self.repo.branchtags().get(branch) == ctx.node():
342 if self.repo.branchtags().get(branch) == ctx.node():
345 branches.append({"name": branch})
343 branches.append({"name": branch})
346 return branches
344 return branches
347
345
348 def showtag(self, tmpl, t1, node=nullid, **args):
346 def showtag(self, tmpl, t1, node=nullid, **args):
349 for t in self.repo.nodetags(node):
347 for t in self.repo.nodetags(node):
350 yield tmpl(t1, tag=t, **args)
348 yield tmpl(t1, tag=t, **args)
351
349
352 def diff(self, tmpl, node1, node2, files):
350 def diff(self, tmpl, node1, node2, files):
353 def filterfiles(filters, files):
351 def filterfiles(filters, files):
354 l = [x for x in files if x in filters]
352 l = [x for x in files if x in filters]
355
353
356 for t in filters:
354 for t in filters:
357 if t and t[-1] != os.sep:
355 if t and t[-1] != os.sep:
358 t += os.sep
356 t += os.sep
359 l += [x for x in files if x.startswith(t)]
357 l += [x for x in files if x.startswith(t)]
360 return l
358 return l
361
359
362 parity = paritygen(self.stripecount)
360 parity = paritygen(self.stripecount)
363 def diffblock(diff, f, fn):
361 def diffblock(diff, f, fn):
364 yield tmpl("diffblock",
362 yield tmpl("diffblock",
365 lines=prettyprintlines(diff),
363 lines=prettyprintlines(diff),
366 parity=parity.next(),
364 parity=parity.next(),
367 file=f,
365 file=f,
368 filenode=hex(fn or nullid))
366 filenode=hex(fn or nullid))
369
367
370 def prettyprintlines(diff):
368 def prettyprintlines(diff):
371 for l in diff.splitlines(1):
369 for l in diff.splitlines(1):
372 if l.startswith('+'):
370 if l.startswith('+'):
373 yield tmpl("difflineplus", line=l)
371 yield tmpl("difflineplus", line=l)
374 elif l.startswith('-'):
372 elif l.startswith('-'):
375 yield tmpl("difflineminus", line=l)
373 yield tmpl("difflineminus", line=l)
376 elif l.startswith('@'):
374 elif l.startswith('@'):
377 yield tmpl("difflineat", line=l)
375 yield tmpl("difflineat", line=l)
378 else:
376 else:
379 yield tmpl("diffline", line=l)
377 yield tmpl("diffline", line=l)
380
378
381 r = self.repo
379 r = self.repo
382 c1 = r.changectx(node1)
380 c1 = r.changectx(node1)
383 c2 = r.changectx(node2)
381 c2 = r.changectx(node2)
384 date1 = util.datestr(c1.date())
382 date1 = util.datestr(c1.date())
385 date2 = util.datestr(c2.date())
383 date2 = util.datestr(c2.date())
386
384
387 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
385 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
388 if files:
386 if files:
389 modified, added, removed = map(lambda x: filterfiles(files, x),
387 modified, added, removed = map(lambda x: filterfiles(files, x),
390 (modified, added, removed))
388 (modified, added, removed))
391
389
392 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
390 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
393 for f in modified:
391 for f in modified:
394 to = c1.filectx(f).data()
392 to = c1.filectx(f).data()
395 tn = c2.filectx(f).data()
393 tn = c2.filectx(f).data()
396 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
394 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
397 opts=diffopts), f, tn)
395 opts=diffopts), f, tn)
398 for f in added:
396 for f in added:
399 to = None
397 to = None
400 tn = c2.filectx(f).data()
398 tn = c2.filectx(f).data()
401 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
399 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
402 opts=diffopts), f, tn)
400 opts=diffopts), f, tn)
403 for f in removed:
401 for f in removed:
404 to = c1.filectx(f).data()
402 to = c1.filectx(f).data()
405 tn = None
403 tn = None
406 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
404 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
407 opts=diffopts), f, tn)
405 opts=diffopts), f, tn)
408
406
409 def changelog(self, tmpl, ctx, shortlog=False):
407 def changelog(self, tmpl, ctx, shortlog=False):
410 def changelist(limit=0,**map):
408 def changelist(limit=0,**map):
411 cl = self.repo.changelog
409 cl = self.repo.changelog
412 l = [] # build a list in forward order for efficiency
410 l = [] # build a list in forward order for efficiency
413 for i in xrange(start, end):
411 for i in xrange(start, end):
414 ctx = self.repo.changectx(i)
412 ctx = self.repo.changectx(i)
415 n = ctx.node()
413 n = ctx.node()
416
414
417 l.insert(0, {"parity": parity.next(),
415 l.insert(0, {"parity": parity.next(),
418 "author": ctx.user(),
416 "author": ctx.user(),
419 "parent": self.siblings(ctx.parents(), i - 1),
417 "parent": self.siblings(ctx.parents(), i - 1),
420 "child": self.siblings(ctx.children(), i + 1),
418 "child": self.siblings(ctx.children(), i + 1),
421 "changelogtag": self.showtag("changelogtag",n),
419 "changelogtag": self.showtag("changelogtag",n),
422 "desc": ctx.description(),
420 "desc": ctx.description(),
423 "date": ctx.date(),
421 "date": ctx.date(),
424 "files": self.listfilediffs(tmpl, ctx.files(), n),
422 "files": self.listfilediffs(tmpl, ctx.files(), n),
425 "rev": i,
423 "rev": i,
426 "node": hex(n),
424 "node": hex(n),
427 "tags": self.nodetagsdict(n),
425 "tags": self.nodetagsdict(n),
428 "branches": self.nodebranchdict(ctx)})
426 "branches": self.nodebranchdict(ctx)})
429
427
430 if limit > 0:
428 if limit > 0:
431 l = l[:limit]
429 l = l[:limit]
432
430
433 for e in l:
431 for e in l:
434 yield e
432 yield e
435
433
436 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
434 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
437 cl = self.repo.changelog
435 cl = self.repo.changelog
438 count = cl.count()
436 count = cl.count()
439 pos = ctx.rev()
437 pos = ctx.rev()
440 start = max(0, pos - maxchanges + 1)
438 start = max(0, pos - maxchanges + 1)
441 end = min(count, start + maxchanges)
439 end = min(count, start + maxchanges)
442 pos = end - 1
440 pos = end - 1
443 parity = paritygen(self.stripecount, offset=start-end)
441 parity = paritygen(self.stripecount, offset=start-end)
444
442
445 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
443 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
446
444
447 return tmpl(shortlog and 'shortlog' or 'changelog',
445 return tmpl(shortlog and 'shortlog' or 'changelog',
448 changenav=changenav,
446 changenav=changenav,
449 node=hex(cl.tip()),
447 node=hex(cl.tip()),
450 rev=pos, changesets=count,
448 rev=pos, changesets=count,
451 entries=lambda **x: changelist(limit=0,**x),
449 entries=lambda **x: changelist(limit=0,**x),
452 latestentry=lambda **x: changelist(limit=1,**x),
450 latestentry=lambda **x: changelist(limit=1,**x),
453 archives=self.archivelist("tip"))
451 archives=self.archivelist("tip"))
454
452
455 def search(self, tmpl, query):
453 def search(self, tmpl, query):
456
454
457 def changelist(**map):
455 def changelist(**map):
458 cl = self.repo.changelog
456 cl = self.repo.changelog
459 count = 0
457 count = 0
460 qw = query.lower().split()
458 qw = query.lower().split()
461
459
462 def revgen():
460 def revgen():
463 for i in xrange(cl.count() - 1, 0, -100):
461 for i in xrange(cl.count() - 1, 0, -100):
464 l = []
462 l = []
465 for j in xrange(max(0, i - 100), i):
463 for j in xrange(max(0, i - 100), i):
466 ctx = self.repo.changectx(j)
464 ctx = self.repo.changectx(j)
467 l.append(ctx)
465 l.append(ctx)
468 l.reverse()
466 l.reverse()
469 for e in l:
467 for e in l:
470 yield e
468 yield e
471
469
472 for ctx in revgen():
470 for ctx in revgen():
473 miss = 0
471 miss = 0
474 for q in qw:
472 for q in qw:
475 if not (q in ctx.user().lower() or
473 if not (q in ctx.user().lower() or
476 q in ctx.description().lower() or
474 q in ctx.description().lower() or
477 q in " ".join(ctx.files()).lower()):
475 q in " ".join(ctx.files()).lower()):
478 miss = 1
476 miss = 1
479 break
477 break
480 if miss:
478 if miss:
481 continue
479 continue
482
480
483 count += 1
481 count += 1
484 n = ctx.node()
482 n = ctx.node()
485
483
486 yield tmpl('searchentry',
484 yield tmpl('searchentry',
487 parity=parity.next(),
485 parity=parity.next(),
488 author=ctx.user(),
486 author=ctx.user(),
489 parent=self.siblings(ctx.parents()),
487 parent=self.siblings(ctx.parents()),
490 child=self.siblings(ctx.children()),
488 child=self.siblings(ctx.children()),
491 changelogtag=self.showtag("changelogtag",n),
489 changelogtag=self.showtag("changelogtag",n),
492 desc=ctx.description(),
490 desc=ctx.description(),
493 date=ctx.date(),
491 date=ctx.date(),
494 files=self.listfilediffs(tmpl, ctx.files(), n),
492 files=self.listfilediffs(tmpl, ctx.files(), n),
495 rev=ctx.rev(),
493 rev=ctx.rev(),
496 node=hex(n),
494 node=hex(n),
497 tags=self.nodetagsdict(n),
495 tags=self.nodetagsdict(n),
498 branches=self.nodebranchdict(ctx))
496 branches=self.nodebranchdict(ctx))
499
497
500 if count >= self.maxchanges:
498 if count >= self.maxchanges:
501 break
499 break
502
500
503 cl = self.repo.changelog
501 cl = self.repo.changelog
504 parity = paritygen(self.stripecount)
502 parity = paritygen(self.stripecount)
505
503
506 return tmpl('search',
504 return tmpl('search',
507 query=query,
505 query=query,
508 node=hex(cl.tip()),
506 node=hex(cl.tip()),
509 entries=changelist,
507 entries=changelist,
510 archives=self.archivelist("tip"))
508 archives=self.archivelist("tip"))
511
509
512 def changeset(self, tmpl, ctx):
510 def changeset(self, tmpl, ctx):
513 n = ctx.node()
511 n = ctx.node()
514 parents = ctx.parents()
512 parents = ctx.parents()
515 p1 = parents[0].node()
513 p1 = parents[0].node()
516
514
517 files = []
515 files = []
518 parity = paritygen(self.stripecount)
516 parity = paritygen(self.stripecount)
519 for f in ctx.files():
517 for f in ctx.files():
520 files.append(tmpl("filenodelink",
518 files.append(tmpl("filenodelink",
521 node=hex(n), file=f,
519 node=hex(n), file=f,
522 parity=parity.next()))
520 parity=parity.next()))
523
521
524 def diff(**map):
522 def diff(**map):
525 yield self.diff(tmpl, p1, n, None)
523 yield self.diff(tmpl, p1, n, None)
526
524
527 return tmpl('changeset',
525 return tmpl('changeset',
528 diff=diff,
526 diff=diff,
529 rev=ctx.rev(),
527 rev=ctx.rev(),
530 node=hex(n),
528 node=hex(n),
531 parent=self.siblings(parents),
529 parent=self.siblings(parents),
532 child=self.siblings(ctx.children()),
530 child=self.siblings(ctx.children()),
533 changesettag=self.showtag("changesettag",n),
531 changesettag=self.showtag("changesettag",n),
534 author=ctx.user(),
532 author=ctx.user(),
535 desc=ctx.description(),
533 desc=ctx.description(),
536 date=ctx.date(),
534 date=ctx.date(),
537 files=files,
535 files=files,
538 archives=self.archivelist(hex(n)),
536 archives=self.archivelist(hex(n)),
539 tags=self.nodetagsdict(n),
537 tags=self.nodetagsdict(n),
540 branches=self.nodebranchdict(ctx))
538 branches=self.nodebranchdict(ctx))
541
539
542 def filelog(self, tmpl, fctx):
540 def filelog(self, tmpl, fctx):
543 f = fctx.path()
541 f = fctx.path()
544 fl = fctx.filelog()
542 fl = fctx.filelog()
545 count = fl.count()
543 count = fl.count()
546 pagelen = self.maxshortchanges
544 pagelen = self.maxshortchanges
547 pos = fctx.filerev()
545 pos = fctx.filerev()
548 start = max(0, pos - pagelen + 1)
546 start = max(0, pos - pagelen + 1)
549 end = min(count, start + pagelen)
547 end = min(count, start + pagelen)
550 pos = end - 1
548 pos = end - 1
551 parity = paritygen(self.stripecount, offset=start-end)
549 parity = paritygen(self.stripecount, offset=start-end)
552
550
553 def entries(limit=0, **map):
551 def entries(limit=0, **map):
554 l = []
552 l = []
555
553
556 for i in xrange(start, end):
554 for i in xrange(start, end):
557 ctx = fctx.filectx(i)
555 ctx = fctx.filectx(i)
558 n = fl.node(i)
556 n = fl.node(i)
559
557
560 l.insert(0, {"parity": parity.next(),
558 l.insert(0, {"parity": parity.next(),
561 "filerev": i,
559 "filerev": i,
562 "file": f,
560 "file": f,
563 "node": hex(ctx.node()),
561 "node": hex(ctx.node()),
564 "author": ctx.user(),
562 "author": ctx.user(),
565 "date": ctx.date(),
563 "date": ctx.date(),
566 "rename": self.renamelink(fl, n),
564 "rename": self.renamelink(fl, n),
567 "parent": self.siblings(fctx.parents()),
565 "parent": self.siblings(fctx.parents()),
568 "child": self.siblings(fctx.children()),
566 "child": self.siblings(fctx.children()),
569 "desc": ctx.description()})
567 "desc": ctx.description()})
570
568
571 if limit > 0:
569 if limit > 0:
572 l = l[:limit]
570 l = l[:limit]
573
571
574 for e in l:
572 for e in l:
575 yield e
573 yield e
576
574
577 nodefunc = lambda x: fctx.filectx(fileid=x)
575 nodefunc = lambda x: fctx.filectx(fileid=x)
578 nav = revnavgen(pos, pagelen, count, nodefunc)
576 nav = revnavgen(pos, pagelen, count, nodefunc)
579 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
577 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
580 entries=lambda **x: entries(limit=0, **x),
578 entries=lambda **x: entries(limit=0, **x),
581 latestentry=lambda **x: entries(limit=1, **x))
579 latestentry=lambda **x: entries(limit=1, **x))
582
580
583 def filerevision(self, tmpl, fctx):
581 def filerevision(self, tmpl, fctx):
584 f = fctx.path()
582 f = fctx.path()
585 text = fctx.data()
583 text = fctx.data()
586 fl = fctx.filelog()
584 fl = fctx.filelog()
587 n = fctx.filenode()
585 n = fctx.filenode()
588 parity = paritygen(self.stripecount)
586 parity = paritygen(self.stripecount)
589
587
590 mt = mimetypes.guess_type(f)[0]
588 mt = mimetypes.guess_type(f)[0]
591 rawtext = text
589 rawtext = text
592 if util.binary(text):
590 if util.binary(text):
593 mt = mt or 'application/octet-stream'
591 mt = mt or 'application/octet-stream'
594 text = "(binary:%s)" % mt
592 text = "(binary:%s)" % mt
595 mt = mt or 'text/plain'
593 mt = mt or 'text/plain'
596
594
597 def lines():
595 def lines():
598 for l, t in enumerate(text.splitlines(1)):
596 for l, t in enumerate(text.splitlines(1)):
599 yield {"line": t,
597 yield {"line": t,
600 "linenumber": "% 6d" % (l + 1),
598 "linenumber": "% 6d" % (l + 1),
601 "parity": parity.next()}
599 "parity": parity.next()}
602
600
603 return tmpl("filerevision",
601 return tmpl("filerevision",
604 file=f,
602 file=f,
605 path=_up(f),
603 path=_up(f),
606 text=lines(),
604 text=lines(),
607 raw=rawtext,
605 raw=rawtext,
608 mimetype=mt,
606 mimetype=mt,
609 rev=fctx.rev(),
607 rev=fctx.rev(),
610 node=hex(fctx.node()),
608 node=hex(fctx.node()),
611 author=fctx.user(),
609 author=fctx.user(),
612 date=fctx.date(),
610 date=fctx.date(),
613 desc=fctx.description(),
611 desc=fctx.description(),
614 parent=self.siblings(fctx.parents()),
612 parent=self.siblings(fctx.parents()),
615 child=self.siblings(fctx.children()),
613 child=self.siblings(fctx.children()),
616 rename=self.renamelink(fl, n),
614 rename=self.renamelink(fl, n),
617 permissions=fctx.manifest().flags(f))
615 permissions=fctx.manifest().flags(f))
618
616
619 def fileannotate(self, tmpl, fctx):
617 def fileannotate(self, tmpl, fctx):
620 f = fctx.path()
618 f = fctx.path()
621 n = fctx.filenode()
619 n = fctx.filenode()
622 fl = fctx.filelog()
620 fl = fctx.filelog()
623 parity = paritygen(self.stripecount)
621 parity = paritygen(self.stripecount)
624
622
625 def annotate(**map):
623 def annotate(**map):
626 last = None
624 last = None
627 for f, l in fctx.annotate(follow=True):
625 for f, l in fctx.annotate(follow=True):
628 fnode = f.filenode()
626 fnode = f.filenode()
629 name = self.repo.ui.shortuser(f.user())
627 name = self.repo.ui.shortuser(f.user())
630
628
631 if last != fnode:
629 if last != fnode:
632 last = fnode
630 last = fnode
633
631
634 yield {"parity": parity.next(),
632 yield {"parity": parity.next(),
635 "node": hex(f.node()),
633 "node": hex(f.node()),
636 "rev": f.rev(),
634 "rev": f.rev(),
637 "author": name,
635 "author": name,
638 "file": f.path(),
636 "file": f.path(),
639 "line": l}
637 "line": l}
640
638
641 return tmpl("fileannotate",
639 return tmpl("fileannotate",
642 file=f,
640 file=f,
643 annotate=annotate,
641 annotate=annotate,
644 path=_up(f),
642 path=_up(f),
645 rev=fctx.rev(),
643 rev=fctx.rev(),
646 node=hex(fctx.node()),
644 node=hex(fctx.node()),
647 author=fctx.user(),
645 author=fctx.user(),
648 date=fctx.date(),
646 date=fctx.date(),
649 desc=fctx.description(),
647 desc=fctx.description(),
650 rename=self.renamelink(fl, n),
648 rename=self.renamelink(fl, n),
651 parent=self.siblings(fctx.parents()),
649 parent=self.siblings(fctx.parents()),
652 child=self.siblings(fctx.children()),
650 child=self.siblings(fctx.children()),
653 permissions=fctx.manifest().flags(f))
651 permissions=fctx.manifest().flags(f))
654
652
655 def manifest(self, tmpl, ctx, path):
653 def manifest(self, tmpl, ctx, path):
656 mf = ctx.manifest()
654 mf = ctx.manifest()
657 node = ctx.node()
655 node = ctx.node()
658
656
659 files = {}
657 files = {}
660 parity = paritygen(self.stripecount)
658 parity = paritygen(self.stripecount)
661
659
662 if path and path[-1] != "/":
660 if path and path[-1] != "/":
663 path += "/"
661 path += "/"
664 l = len(path)
662 l = len(path)
665 abspath = "/" + path
663 abspath = "/" + path
666
664
667 for f, n in mf.items():
665 for f, n in mf.items():
668 if f[:l] != path:
666 if f[:l] != path:
669 continue
667 continue
670 remain = f[l:]
668 remain = f[l:]
671 if "/" in remain:
669 if "/" in remain:
672 short = remain[:remain.index("/") + 1] # bleah
670 short = remain[:remain.index("/") + 1] # bleah
673 files[short] = (f, None)
671 files[short] = (f, None)
674 else:
672 else:
675 short = os.path.basename(remain)
673 short = os.path.basename(remain)
676 files[short] = (f, n)
674 files[short] = (f, n)
677
675
678 if not files:
676 if not files:
679 raise ErrorResponse(404, 'Path not found: ' + path)
677 raise ErrorResponse(404, 'Path not found: ' + path)
680
678
681 def filelist(**map):
679 def filelist(**map):
682 fl = files.keys()
680 fl = files.keys()
683 fl.sort()
681 fl.sort()
684 for f in fl:
682 for f in fl:
685 full, fnode = files[f]
683 full, fnode = files[f]
686 if not fnode:
684 if not fnode:
687 continue
685 continue
688
686
689 fctx = ctx.filectx(full)
687 fctx = ctx.filectx(full)
690 yield {"file": full,
688 yield {"file": full,
691 "parity": parity.next(),
689 "parity": parity.next(),
692 "basename": f,
690 "basename": f,
693 "date": fctx.changectx().date(),
691 "date": fctx.changectx().date(),
694 "size": fctx.size(),
692 "size": fctx.size(),
695 "permissions": mf.flags(full)}
693 "permissions": mf.flags(full)}
696
694
697 def dirlist(**map):
695 def dirlist(**map):
698 fl = files.keys()
696 fl = files.keys()
699 fl.sort()
697 fl.sort()
700 for f in fl:
698 for f in fl:
701 full, fnode = files[f]
699 full, fnode = files[f]
702 if fnode:
700 if fnode:
703 continue
701 continue
704
702
705 yield {"parity": parity.next(),
703 yield {"parity": parity.next(),
706 "path": "%s%s" % (abspath, f),
704 "path": "%s%s" % (abspath, f),
707 "basename": f[:-1]}
705 "basename": f[:-1]}
708
706
709 return tmpl("manifest",
707 return tmpl("manifest",
710 rev=ctx.rev(),
708 rev=ctx.rev(),
711 node=hex(node),
709 node=hex(node),
712 path=abspath,
710 path=abspath,
713 up=_up(abspath),
711 up=_up(abspath),
714 upparity=parity.next(),
712 upparity=parity.next(),
715 fentries=filelist,
713 fentries=filelist,
716 dentries=dirlist,
714 dentries=dirlist,
717 archives=self.archivelist(hex(node)),
715 archives=self.archivelist(hex(node)),
718 tags=self.nodetagsdict(node),
716 tags=self.nodetagsdict(node),
719 branches=self.nodebranchdict(ctx))
717 branches=self.nodebranchdict(ctx))
720
718
721 def tags(self, tmpl):
719 def tags(self, tmpl):
722 i = self.repo.tagslist()
720 i = self.repo.tagslist()
723 i.reverse()
721 i.reverse()
724 parity = paritygen(self.stripecount)
722 parity = paritygen(self.stripecount)
725
723
726 def entries(notip=False,limit=0, **map):
724 def entries(notip=False,limit=0, **map):
727 count = 0
725 count = 0
728 for k, n in i:
726 for k, n in i:
729 if notip and k == "tip":
727 if notip and k == "tip":
730 continue
728 continue
731 if limit > 0 and count >= limit:
729 if limit > 0 and count >= limit:
732 continue
730 continue
733 count = count + 1
731 count = count + 1
734 yield {"parity": parity.next(),
732 yield {"parity": parity.next(),
735 "tag": k,
733 "tag": k,
736 "date": self.repo.changectx(n).date(),
734 "date": self.repo.changectx(n).date(),
737 "node": hex(n)}
735 "node": hex(n)}
738
736
739 return tmpl("tags",
737 return tmpl("tags",
740 node=hex(self.repo.changelog.tip()),
738 node=hex(self.repo.changelog.tip()),
741 entries=lambda **x: entries(False,0, **x),
739 entries=lambda **x: entries(False,0, **x),
742 entriesnotip=lambda **x: entries(True,0, **x),
740 entriesnotip=lambda **x: entries(True,0, **x),
743 latestentry=lambda **x: entries(True,1, **x))
741 latestentry=lambda **x: entries(True,1, **x))
744
742
745 def summary(self, tmpl):
743 def summary(self, tmpl):
746 i = self.repo.tagslist()
744 i = self.repo.tagslist()
747 i.reverse()
745 i.reverse()
748
746
749 def tagentries(**map):
747 def tagentries(**map):
750 parity = paritygen(self.stripecount)
748 parity = paritygen(self.stripecount)
751 count = 0
749 count = 0
752 for k, n in i:
750 for k, n in i:
753 if k == "tip": # skip tip
751 if k == "tip": # skip tip
754 continue;
752 continue;
755
753
756 count += 1
754 count += 1
757 if count > 10: # limit to 10 tags
755 if count > 10: # limit to 10 tags
758 break;
756 break;
759
757
760 yield tmpl("tagentry",
758 yield tmpl("tagentry",
761 parity=parity.next(),
759 parity=parity.next(),
762 tag=k,
760 tag=k,
763 node=hex(n),
761 node=hex(n),
764 date=self.repo.changectx(n).date())
762 date=self.repo.changectx(n).date())
765
763
766
764
767 def branches(**map):
765 def branches(**map):
768 parity = paritygen(self.stripecount)
766 parity = paritygen(self.stripecount)
769
767
770 b = self.repo.branchtags()
768 b = self.repo.branchtags()
771 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
769 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
772 l.sort()
770 l.sort()
773
771
774 for r,n,t in l:
772 for r,n,t in l:
775 ctx = self.repo.changectx(n)
773 ctx = self.repo.changectx(n)
776
774
777 yield {'parity': parity.next(),
775 yield {'parity': parity.next(),
778 'branch': t,
776 'branch': t,
779 'node': hex(n),
777 'node': hex(n),
780 'date': ctx.date()}
778 'date': ctx.date()}
781
779
782 def changelist(**map):
780 def changelist(**map):
783 parity = paritygen(self.stripecount, offset=start-end)
781 parity = paritygen(self.stripecount, offset=start-end)
784 l = [] # build a list in forward order for efficiency
782 l = [] # build a list in forward order for efficiency
785 for i in xrange(start, end):
783 for i in xrange(start, end):
786 ctx = self.repo.changectx(i)
784 ctx = self.repo.changectx(i)
787 n = ctx.node()
785 n = ctx.node()
788 hn = hex(n)
786 hn = hex(n)
789
787
790 l.insert(0, tmpl(
788 l.insert(0, tmpl(
791 'shortlogentry',
789 'shortlogentry',
792 parity=parity.next(),
790 parity=parity.next(),
793 author=ctx.user(),
791 author=ctx.user(),
794 desc=ctx.description(),
792 desc=ctx.description(),
795 date=ctx.date(),
793 date=ctx.date(),
796 rev=i,
794 rev=i,
797 node=hn,
795 node=hn,
798 tags=self.nodetagsdict(n),
796 tags=self.nodetagsdict(n),
799 branches=self.nodebranchdict(ctx)))
797 branches=self.nodebranchdict(ctx)))
800
798
801 yield l
799 yield l
802
800
803 cl = self.repo.changelog
801 cl = self.repo.changelog
804 count = cl.count()
802 count = cl.count()
805 start = max(0, count - self.maxchanges)
803 start = max(0, count - self.maxchanges)
806 end = min(count, start + self.maxchanges)
804 end = min(count, start + self.maxchanges)
807
805
808 return tmpl("summary",
806 return tmpl("summary",
809 desc=self.config("web", "description", "unknown"),
807 desc=self.config("web", "description", "unknown"),
810 owner=get_contact(self.config) or "unknown",
808 owner=get_contact(self.config) or "unknown",
811 lastchange=cl.read(cl.tip())[2],
809 lastchange=cl.read(cl.tip())[2],
812 tags=tagentries,
810 tags=tagentries,
813 branches=branches,
811 branches=branches,
814 shortlog=changelist,
812 shortlog=changelist,
815 node=hex(cl.tip()),
813 node=hex(cl.tip()),
816 archives=self.archivelist("tip"))
814 archives=self.archivelist("tip"))
817
815
818 def filediff(self, tmpl, fctx):
816 def filediff(self, tmpl, fctx):
819 n = fctx.node()
817 n = fctx.node()
820 path = fctx.path()
818 path = fctx.path()
821 parents = fctx.parents()
819 parents = fctx.parents()
822 p1 = parents and parents[0].node() or nullid
820 p1 = parents and parents[0].node() or nullid
823
821
824 def diff(**map):
822 def diff(**map):
825 yield self.diff(tmpl, p1, n, [path])
823 yield self.diff(tmpl, p1, n, [path])
826
824
827 return tmpl("filediff",
825 return tmpl("filediff",
828 file=path,
826 file=path,
829 node=hex(n),
827 node=hex(n),
830 rev=fctx.rev(),
828 rev=fctx.rev(),
831 parent=self.siblings(parents),
829 parent=self.siblings(parents),
832 child=self.siblings(fctx.children()),
830 child=self.siblings(fctx.children()),
833 diff=diff)
831 diff=diff)
834
832
835 archive_specs = {
833 archive_specs = {
836 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
834 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
837 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
835 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
838 'zip': ('application/zip', 'zip', '.zip', None),
836 'zip': ('application/zip', 'zip', '.zip', None),
839 }
837 }
840
838
841 def archive(self, tmpl, req, key, type_):
839 def archive(self, tmpl, req, key, type_):
842 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
840 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
843 cnode = self.repo.lookup(key)
841 cnode = self.repo.lookup(key)
844 arch_version = key
842 arch_version = key
845 if cnode == key or key == 'tip':
843 if cnode == key or key == 'tip':
846 arch_version = short(cnode)
844 arch_version = short(cnode)
847 name = "%s-%s" % (reponame, arch_version)
845 name = "%s-%s" % (reponame, arch_version)
848 mimetype, artype, extension, encoding = self.archive_specs[type_]
846 mimetype, artype, extension, encoding = self.archive_specs[type_]
849 headers = [('Content-type', mimetype),
847 headers = [('Content-type', mimetype),
850 ('Content-disposition', 'attachment; filename=%s%s' %
848 ('Content-disposition', 'attachment; filename=%s%s' %
851 (name, extension))]
849 (name, extension))]
852 if encoding:
850 if encoding:
853 headers.append(('Content-encoding', encoding))
851 headers.append(('Content-encoding', encoding))
854 req.header(headers)
852 req.header(headers)
855 archival.archive(self.repo, req, cnode, artype, prefix=name)
853 archival.archive(self.repo, req, cnode, artype, prefix=name)
856
854
857 # add tags to things
855 # add tags to things
858 # tags -> list of changesets corresponding to tags
856 # tags -> list of changesets corresponding to tags
859 # find tag, changeset, file
857 # find tag, changeset, file
860
858
861 def cleanpath(self, path):
859 def cleanpath(self, path):
862 path = path.lstrip('/')
860 path = path.lstrip('/')
863 return util.canonpath(self.repo.root, '', path)
861 return util.canonpath(self.repo.root, '', path)
864
862
865 def changectx(self, req):
863 def changectx(self, req):
866 if 'node' in req.form:
864 if 'node' in req.form:
867 changeid = req.form['node'][0]
865 changeid = req.form['node'][0]
868 elif 'manifest' in req.form:
866 elif 'manifest' in req.form:
869 changeid = req.form['manifest'][0]
867 changeid = req.form['manifest'][0]
870 else:
868 else:
871 changeid = self.repo.changelog.count() - 1
869 changeid = self.repo.changelog.count() - 1
872
870
873 try:
871 try:
874 ctx = self.repo.changectx(changeid)
872 ctx = self.repo.changectx(changeid)
875 except hg.RepoError:
873 except hg.RepoError:
876 man = self.repo.manifest
874 man = self.repo.manifest
877 mn = man.lookup(changeid)
875 mn = man.lookup(changeid)
878 ctx = self.repo.changectx(man.linkrev(mn))
876 ctx = self.repo.changectx(man.linkrev(mn))
879
877
880 return ctx
878 return ctx
881
879
882 def filectx(self, req):
880 def filectx(self, req):
883 path = self.cleanpath(req.form['file'][0])
881 path = self.cleanpath(req.form['file'][0])
884 if 'node' in req.form:
882 if 'node' in req.form:
885 changeid = req.form['node'][0]
883 changeid = req.form['node'][0]
886 else:
884 else:
887 changeid = req.form['filenode'][0]
885 changeid = req.form['filenode'][0]
888 try:
886 try:
889 ctx = self.repo.changectx(changeid)
887 ctx = self.repo.changectx(changeid)
890 fctx = ctx.filectx(path)
888 fctx = ctx.filectx(path)
891 except hg.RepoError:
889 except hg.RepoError:
892 fctx = self.repo.filectx(path, fileid=changeid)
890 fctx = self.repo.filectx(path, fileid=changeid)
893
891
894 return fctx
892 return fctx
895
893
896 def check_perm(self, req, op, default):
894 def check_perm(self, req, op, default):
897 '''check permission for operation based on user auth.
895 '''check permission for operation based on user auth.
898 return true if op allowed, else false.
896 return true if op allowed, else false.
899 default is policy to use if no config given.'''
897 default is policy to use if no config given.'''
900
898
901 user = req.env.get('REMOTE_USER')
899 user = req.env.get('REMOTE_USER')
902
900
903 deny = self.configlist('web', 'deny_' + op)
901 deny = self.configlist('web', 'deny_' + op)
904 if deny and (not user or deny == ['*'] or user in deny):
902 if deny and (not user or deny == ['*'] or user in deny):
905 return False
903 return False
906
904
907 allow = self.configlist('web', 'allow_' + op)
905 allow = self.configlist('web', 'allow_' + op)
908 return (allow and (allow == ['*'] or user in allow)) or default
906 return (allow and (allow == ['*'] or user in allow)) or default
@@ -1,276 +1,275 b''
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
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, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 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, mimetools, cStringIO
9 import os
10 from mercurial.i18n import gettext as _
10 from mercurial.i18n import gettext as _
11 from mercurial import ui, hg, util, templater
11 from mercurial import ui, hg, util, templater
12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen, \
12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen, \
13 get_contact
13 get_contact
14 from hgweb_mod import hgweb
14 from hgweb_mod import hgweb
15 from request import wsgirequest
15 from request import wsgirequest
16
16
17 # This is a stopgap
17 # This is a stopgap
18 class hgwebdir(object):
18 class hgwebdir(object):
19 def __init__(self, config, parentui=None):
19 def __init__(self, config, parentui=None):
20 def cleannames(items):
20 def cleannames(items):
21 return [(util.pconvert(name).strip('/'), path)
21 return [(util.pconvert(name).strip('/'), path)
22 for name, path in items]
22 for name, path in items]
23
23
24 self.parentui = parentui or ui.ui(report_untrusted=False,
24 self.parentui = parentui or ui.ui(report_untrusted=False,
25 interactive = False)
25 interactive = False)
26 self.motd = None
26 self.motd = None
27 self.style = None
27 self.style = None
28 self.stripecount = None
28 self.stripecount = None
29 self.repos_sorted = ('name', False)
29 self.repos_sorted = ('name', False)
30 if isinstance(config, (list, tuple)):
30 if isinstance(config, (list, tuple)):
31 self.repos = cleannames(config)
31 self.repos = cleannames(config)
32 self.repos_sorted = ('', False)
32 self.repos_sorted = ('', False)
33 elif isinstance(config, dict):
33 elif isinstance(config, dict):
34 self.repos = cleannames(config.items())
34 self.repos = cleannames(config.items())
35 self.repos.sort()
35 self.repos.sort()
36 else:
36 else:
37 if isinstance(config, util.configparser):
37 if isinstance(config, util.configparser):
38 cp = config
38 cp = config
39 else:
39 else:
40 cp = util.configparser()
40 cp = util.configparser()
41 cp.read(config)
41 cp.read(config)
42 self.repos = []
42 self.repos = []
43 if cp.has_section('web'):
43 if cp.has_section('web'):
44 if cp.has_option('web', 'motd'):
44 if cp.has_option('web', 'motd'):
45 self.motd = cp.get('web', 'motd')
45 self.motd = cp.get('web', 'motd')
46 if cp.has_option('web', 'style'):
46 if cp.has_option('web', 'style'):
47 self.style = cp.get('web', 'style')
47 self.style = cp.get('web', 'style')
48 if cp.has_option('web', 'stripes'):
48 if cp.has_option('web', 'stripes'):
49 self.stripecount = int(cp.get('web', 'stripes'))
49 self.stripecount = int(cp.get('web', 'stripes'))
50 if cp.has_section('paths'):
50 if cp.has_section('paths'):
51 self.repos.extend(cleannames(cp.items('paths')))
51 self.repos.extend(cleannames(cp.items('paths')))
52 if cp.has_section('collections'):
52 if cp.has_section('collections'):
53 for prefix, root in cp.items('collections'):
53 for prefix, root in cp.items('collections'):
54 for path in util.walkrepos(root):
54 for path in util.walkrepos(root):
55 repo = os.path.normpath(path)
55 repo = os.path.normpath(path)
56 name = repo
56 name = repo
57 if name.startswith(prefix):
57 if name.startswith(prefix):
58 name = name[len(prefix):]
58 name = name[len(prefix):]
59 self.repos.append((name.lstrip(os.sep), repo))
59 self.repos.append((name.lstrip(os.sep), repo))
60 self.repos.sort()
60 self.repos.sort()
61
61
62 def run(self):
62 def run(self):
63 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
63 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
64 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
64 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
65 import mercurial.hgweb.wsgicgi as wsgicgi
65 import mercurial.hgweb.wsgicgi as wsgicgi
66 wsgicgi.launch(self)
66 wsgicgi.launch(self)
67
67
68 def __call__(self, env, respond):
68 def __call__(self, env, respond):
69 req = wsgirequest(env, respond)
69 req = wsgirequest(env, respond)
70 self.run_wsgi(req)
70 self.run_wsgi(req)
71 return req
71 return req
72
72
73 def run_wsgi(self, req):
73 def run_wsgi(self, req):
74
74
75 try:
75 try:
76 try:
76 try:
77
77
78 virtual = req.env.get("PATH_INFO", "").strip('/')
78 virtual = req.env.get("PATH_INFO", "").strip('/')
79
79
80 # a static file
80 # a static file
81 if virtual.startswith('static/') or 'static' in req.form:
81 if virtual.startswith('static/') or 'static' in req.form:
82 static = os.path.join(templater.templatepath(), 'static')
82 static = os.path.join(templater.templatepath(), 'static')
83 if virtual.startswith('static/'):
83 if virtual.startswith('static/'):
84 fname = virtual[7:]
84 fname = virtual[7:]
85 else:
85 else:
86 fname = req.form['static'][0]
86 fname = req.form['static'][0]
87 req.write(staticfile(static, fname, req))
87 req.write(staticfile(static, fname, req))
88 return
88 return
89
89
90 # top-level index
90 # top-level index
91 elif not virtual:
91 elif not virtual:
92 tmpl = self.templater(req)
92 tmpl = self.templater(req)
93 self.makeindex(req, tmpl)
93 self.makeindex(req, tmpl)
94 return
94 return
95
95
96 # nested indexes and hgwebs
96 # nested indexes and hgwebs
97 repos = dict(self.repos)
97 repos = dict(self.repos)
98 while virtual:
98 while virtual:
99 real = repos.get(virtual)
99 real = repos.get(virtual)
100 if real:
100 if real:
101 req.env['REPO_NAME'] = virtual
101 req.env['REPO_NAME'] = virtual
102 try:
102 try:
103 repo = hg.repository(self.parentui, real)
103 repo = hg.repository(self.parentui, real)
104 hgweb(repo).run_wsgi(req)
104 hgweb(repo).run_wsgi(req)
105 return
105 return
106 except IOError, inst:
106 except IOError, inst:
107 raise ErrorResponse(500, inst.strerror)
107 raise ErrorResponse(500, inst.strerror)
108 except hg.RepoError, inst:
108 except hg.RepoError, inst:
109 raise ErrorResponse(500, str(inst))
109 raise ErrorResponse(500, str(inst))
110
110
111 # browse subdirectories
111 # browse subdirectories
112 subdir = virtual + '/'
112 subdir = virtual + '/'
113 if [r for r in repos if r.startswith(subdir)]:
113 if [r for r in repos if r.startswith(subdir)]:
114 tmpl = self.templater(req)
114 tmpl = self.templater(req)
115 self.makeindex(req, tmpl, subdir)
115 self.makeindex(req, tmpl, subdir)
116 return
116 return
117
117
118 up = virtual.rfind('/')
118 up = virtual.rfind('/')
119 if up < 0:
119 if up < 0:
120 break
120 break
121 virtual = virtual[:up]
121 virtual = virtual[:up]
122
122
123 # prefixes not found
123 # prefixes not found
124 tmpl = self.templater(req)
124 tmpl = self.templater(req)
125 req.respond(404, tmpl("notfound", repo=virtual))
125 req.respond(404, tmpl("notfound", repo=virtual))
126
126
127 except ErrorResponse, err:
127 except ErrorResponse, err:
128 tmpl = self.templater(req)
128 tmpl = self.templater(req)
129 req.respond(err.code, tmpl('error', error=err.message or ''))
129 req.respond(err.code, tmpl('error', error=err.message or ''))
130 finally:
130 finally:
131 tmpl = None
131 tmpl = None
132
132
133 def makeindex(self, req, tmpl, subdir=""):
133 def makeindex(self, req, tmpl, subdir=""):
134
134
135 def archivelist(ui, nodeid, url):
135 def archivelist(ui, nodeid, url):
136 allowed = ui.configlist("web", "allow_archive", untrusted=True)
136 allowed = ui.configlist("web", "allow_archive", untrusted=True)
137 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
137 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
138 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
138 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
139 untrusted=True):
139 untrusted=True):
140 yield {"type" : i[0], "extension": i[1],
140 yield {"type" : i[0], "extension": i[1],
141 "node": nodeid, "url": url}
141 "node": nodeid, "url": url}
142
142
143 def entries(sortcolumn="", descending=False, subdir="", **map):
143 def entries(sortcolumn="", descending=False, subdir="", **map):
144 def sessionvars(**map):
144 def sessionvars(**map):
145 fields = []
145 fields = []
146 if 'style' in req.form:
146 if 'style' in req.form:
147 style = req.form['style'][0]
147 style = req.form['style'][0]
148 if style != get('web', 'style', ''):
148 if style != get('web', 'style', ''):
149 fields.append(('style', style))
149 fields.append(('style', style))
150
150
151 separator = url[-1] == '?' and ';' or '?'
151 separator = url[-1] == '?' and ';' or '?'
152 for name, value in fields:
152 for name, value in fields:
153 yield dict(name=name, value=value, separator=separator)
153 yield dict(name=name, value=value, separator=separator)
154 separator = ';'
154 separator = ';'
155
155
156 rows = []
156 rows = []
157 parity = paritygen(self.stripecount)
157 parity = paritygen(self.stripecount)
158 for name, path in self.repos:
158 for name, path in self.repos:
159 if not name.startswith(subdir):
159 if not name.startswith(subdir):
160 continue
160 continue
161 name = name[len(subdir):]
161 name = name[len(subdir):]
162
162
163 u = ui.ui(parentui=self.parentui)
163 u = ui.ui(parentui=self.parentui)
164 try:
164 try:
165 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
165 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
166 except Exception, e:
166 except Exception, e:
167 u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
167 u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
168 continue
168 continue
169 def get(section, name, default=None):
169 def get(section, name, default=None):
170 return u.config(section, name, default, untrusted=True)
170 return u.config(section, name, default, untrusted=True)
171
171
172 if u.configbool("web", "hidden", untrusted=True):
172 if u.configbool("web", "hidden", untrusted=True):
173 continue
173 continue
174
174
175 parts = [req.env['PATH_INFO'], name]
175 parts = [req.env['PATH_INFO'], name]
176 if req.env['SCRIPT_NAME']:
176 if req.env['SCRIPT_NAME']:
177 parts.insert(0, req.env['SCRIPT_NAME'])
177 parts.insert(0, req.env['SCRIPT_NAME'])
178 url = ('/'.join(parts).replace("//", "/")) + '/'
178 url = ('/'.join(parts).replace("//", "/")) + '/'
179
179
180 # update time with local timezone
180 # update time with local timezone
181 try:
181 try:
182 d = (get_mtime(path), util.makedate()[1])
182 d = (get_mtime(path), util.makedate()[1])
183 except OSError:
183 except OSError:
184 continue
184 continue
185
185
186 contact = get_contact(get)
186 contact = get_contact(get)
187 description = get("web", "description", "")
187 description = get("web", "description", "")
188 name = get("web", "name", name)
188 name = get("web", "name", name)
189 row = dict(contact=contact or "unknown",
189 row = dict(contact=contact or "unknown",
190 contact_sort=contact.upper() or "unknown",
190 contact_sort=contact.upper() or "unknown",
191 name=name,
191 name=name,
192 name_sort=name,
192 name_sort=name,
193 url=url,
193 url=url,
194 description=description or "unknown",
194 description=description or "unknown",
195 description_sort=description.upper() or "unknown",
195 description_sort=description.upper() or "unknown",
196 lastchange=d,
196 lastchange=d,
197 lastchange_sort=d[1]-d[0],
197 lastchange_sort=d[1]-d[0],
198 sessionvars=sessionvars,
198 sessionvars=sessionvars,
199 archives=archivelist(u, "tip", url))
199 archives=archivelist(u, "tip", url))
200 if (not sortcolumn
200 if (not sortcolumn
201 or (sortcolumn, descending) == self.repos_sorted):
201 or (sortcolumn, descending) == self.repos_sorted):
202 # fast path for unsorted output
202 # fast path for unsorted output
203 row['parity'] = parity.next()
203 row['parity'] = parity.next()
204 yield row
204 yield row
205 else:
205 else:
206 rows.append((row["%s_sort" % sortcolumn], row))
206 rows.append((row["%s_sort" % sortcolumn], row))
207 if rows:
207 if rows:
208 rows.sort()
208 rows.sort()
209 if descending:
209 if descending:
210 rows.reverse()
210 rows.reverse()
211 for key, row in rows:
211 for key, row in rows:
212 row['parity'] = parity.next()
212 row['parity'] = parity.next()
213 yield row
213 yield row
214
214
215 sortable = ["name", "description", "contact", "lastchange"]
215 sortable = ["name", "description", "contact", "lastchange"]
216 sortcolumn, descending = self.repos_sorted
216 sortcolumn, descending = self.repos_sorted
217 if 'sort' in req.form:
217 if 'sort' in req.form:
218 sortcolumn = req.form['sort'][0]
218 sortcolumn = req.form['sort'][0]
219 descending = sortcolumn.startswith('-')
219 descending = sortcolumn.startswith('-')
220 if descending:
220 if descending:
221 sortcolumn = sortcolumn[1:]
221 sortcolumn = sortcolumn[1:]
222 if sortcolumn not in sortable:
222 if sortcolumn not in sortable:
223 sortcolumn = ""
223 sortcolumn = ""
224
224
225 sort = [("sort_%s" % column,
225 sort = [("sort_%s" % column,
226 "%s%s" % ((not descending and column == sortcolumn)
226 "%s%s" % ((not descending and column == sortcolumn)
227 and "-" or "", column))
227 and "-" or "", column))
228 for column in sortable]
228 for column in sortable]
229
229 req.write(tmpl("index", entries=entries, subdir=subdir,
230 req.write(tmpl("index", entries=entries, subdir=subdir,
230 sortcolumn=sortcolumn, descending=descending,
231 sortcolumn=sortcolumn, descending=descending,
231 **dict(sort)))
232 **dict(sort)))
232
233
233 def templater(self, req):
234 def templater(self, req):
234
235
235 def header(**map):
236 def header(**map):
236 header_file = cStringIO.StringIO(
237 ctype = tmpl('mimetype', encoding=util._encoding)
237 ''.join(tmpl("header", encoding=util._encoding, **map)))
238 req.httphdr(templater.stringify(ctype))
238 msg = mimetools.Message(header_file, 0)
239 yield tmpl('header', encoding=util._encoding, **map)
239 req.header(msg.items())
240 yield header_file.read()
241
240
242 def footer(**map):
241 def footer(**map):
243 yield tmpl("footer", **map)
242 yield tmpl("footer", **map)
244
243
245 def motd(**map):
244 def motd(**map):
246 if self.motd is not None:
245 if self.motd is not None:
247 yield self.motd
246 yield self.motd
248 else:
247 else:
249 yield config('web', 'motd', '')
248 yield config('web', 'motd', '')
250
249
251 def config(section, name, default=None, untrusted=True):
250 def config(section, name, default=None, untrusted=True):
252 return self.parentui.config(section, name, default, untrusted)
251 return self.parentui.config(section, name, default, untrusted)
253
252
254 url = req.env.get('SCRIPT_NAME', '')
253 url = req.env.get('SCRIPT_NAME', '')
255 if not url.endswith('/'):
254 if not url.endswith('/'):
256 url += '/'
255 url += '/'
257
256
258 staticurl = config('web', 'staticurl') or url + 'static/'
257 staticurl = config('web', 'staticurl') or url + 'static/'
259 if not staticurl.endswith('/'):
258 if not staticurl.endswith('/'):
260 staticurl += '/'
259 staticurl += '/'
261
260
262 style = self.style
261 style = self.style
263 if style is None:
262 if style is None:
264 style = config('web', 'style', '')
263 style = config('web', 'style', '')
265 if 'style' in req.form:
264 if 'style' in req.form:
266 style = req.form['style'][0]
265 style = req.form['style'][0]
267 if self.stripecount is None:
266 if self.stripecount is None:
268 self.stripecount = int(config('web', 'stripes', 1))
267 self.stripecount = int(config('web', 'stripes', 1))
269 mapfile = style_map(templater.templatepath(), style)
268 mapfile = style_map(templater.templatepath(), style)
270 tmpl = templater.templater(mapfile, templater.common_filters,
269 tmpl = templater.templater(mapfile, templater.common_filters,
271 defaults={"header": header,
270 defaults={"header": header,
272 "footer": footer,
271 "footer": footer,
273 "motd": motd,
272 "motd": motd,
274 "url": url,
273 "url": url,
275 "staticurl": staticurl})
274 "staticurl": staticurl})
276 return tmpl
275 return tmpl
@@ -1,4 +1,2 b''
1 Content-type: application/atom+xml; charset={encoding}
2
3 <?xml version="1.0" encoding="{encoding}"?>
1 <?xml version="1.0" encoding="{encoding}"?>
4 <feed xmlns="http://www.w3.org/2005/Atom"> No newline at end of file
2 <feed xmlns="http://www.w3.org/2005/Atom">
@@ -1,9 +1,10 b''
1 default = 'changelog'
1 default = 'changelog'
2 feedupdated = '<updated>#date|rfc3339date#</updated>'
2 feedupdated = '<updated>#date|rfc3339date#</updated>'
3 mimetype = 'application/atom+xml; charset={encoding}'
3 header = header.tmpl
4 header = header.tmpl
4 changelog = changelog.tmpl
5 changelog = changelog.tmpl
5 changelogentry = changelogentry.tmpl
6 changelogentry = changelogentry.tmpl
6 filelog = filelog.tmpl
7 filelog = filelog.tmpl
7 filelogentry = filelogentry.tmpl
8 filelogentry = filelogentry.tmpl
8 tags = tags.tmpl
9 tags = tags.tmpl
9 tagentry = tagentry.tmpl
10 tagentry = tagentry.tmpl
@@ -1,10 +1,8 b''
1 Content-type: text/html; charset={encoding}
2
3 <?xml version="1.0" encoding="{encoding}"?>
1 <?xml version="1.0" encoding="{encoding}"?>
4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
5 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
6 <head>
4 <head>
7 <link rel="icon" href="{staticurl}hgicon.png" type="image/png" />
5 <link rel="icon" href="{staticurl}hgicon.png" type="image/png" />
8 <meta name="robots" content="index, nofollow"/>
6 <meta name="robots" content="index, nofollow"/>
9 <link rel="stylesheet" href="{staticurl}style-gitweb.css" type="text/css" />
7 <link rel="stylesheet" href="{staticurl}style-gitweb.css" type="text/css" />
10
8
@@ -1,58 +1,59 b''
1 default = 'summary'
1 default = 'summary'
2 mimetype = 'text/html; charset={encoding}'
2 header = header.tmpl
3 header = header.tmpl
3 footer = footer.tmpl
4 footer = footer.tmpl
4 search = search.tmpl
5 search = search.tmpl
5 changelog = changelog.tmpl
6 changelog = changelog.tmpl
6 summary = summary.tmpl
7 summary = summary.tmpl
7 error = error.tmpl
8 error = error.tmpl
8 notfound = notfound.tmpl
9 notfound = notfound.tmpl
9 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
10 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
10 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
11 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
11 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
12 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
12 filedifflink = '<a href="#url#diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#file|escape#</a> '
13 filedifflink = '<a href="#url#diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#file|escape#</a> '
13 filenodelink = '<tr class="parity#parity#"><td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">#file|escape#</a></td><td></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> | <a href="#url#diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">diff</a> | <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a></td></tr>'
14 filenodelink = '<tr class="parity#parity#"><td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">#file|escape#</a></td><td></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> | <a href="#url#diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">diff</a> | <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a></td></tr>'
14 fileellipses = '...'
15 fileellipses = '...'
15 changelogentry = changelogentry.tmpl
16 changelogentry = changelogentry.tmpl
16 searchentry = changelogentry.tmpl
17 searchentry = changelogentry.tmpl
17 changeset = changeset.tmpl
18 changeset = changeset.tmpl
18 manifest = manifest.tmpl
19 manifest = manifest.tmpl
19 manifestdirentry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a></td></tr>'
20 manifestdirentry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a></td></tr>'
20 manifestfileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td style="font-family:monospace" align=right>#date|isodate#</td><td style="font-family:monospace" align=right>#size#</td><td class="list"><a class="list" href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a></td></tr>'
21 manifestfileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td style="font-family:monospace" align=right>#date|isodate#</td><td style="font-family:monospace" align=right>#size#</td><td class="list"><a class="list" href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a></td></tr>'
21 filerevision = filerevision.tmpl
22 filerevision = filerevision.tmpl
22 fileannotate = fileannotate.tmpl
23 fileannotate = fileannotate.tmpl
23 filediff = filediff.tmpl
24 filediff = filediff.tmpl
24 filelog = filelog.tmpl
25 filelog = filelog.tmpl
25 fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr"> #linenumber#</span> #line|escape#</pre></div>'
26 fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr"> #linenumber#</span> #line|escape#</pre></div>'
26 annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
27 annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
27 difflineplus = '<span style="color:#008800;">#line|escape#</span>'
28 difflineplus = '<span style="color:#008800;">#line|escape#</span>'
28 difflineminus = '<span style="color:#cc0000;">#line|escape#</span>'
29 difflineminus = '<span style="color:#cc0000;">#line|escape#</span>'
29 difflineat = '<span style="color:#990099;">#line|escape#</span>'
30 difflineat = '<span style="color:#990099;">#line|escape#</span>'
30 diffline = '<span>#line|escape#</span>'
31 diffline = '<span>#line|escape#</span>'
31 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
32 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
32 changesetparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
33 changesetparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
33 filerevparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
34 filerevparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
34 filerename = '{file|escape}@'
35 filerename = '{file|escape}@'
35 filelogrename = '| <a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">base</a>'
36 filelogrename = '| <a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">base</a>'
36 fileannotateparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
37 fileannotateparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
37 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
38 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
38 changesetchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
39 changesetchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
39 filerevchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
40 filerevchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
40 fileannotatechild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
41 fileannotatechild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
41 tags = tags.tmpl
42 tags = tags.tmpl
42 tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>#tag|escape#</b></a></td><td class="link"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/#node|short#{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>'
43 tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>#tag|escape#</b></a></td><td class="link"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/#node|short#{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>'
43 branchentry = '<tr class="parity{parity}"><td class="age"><i>{date|age} ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></a></td><td>{branch|escape}</td><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/{node|short}{sessionvars%urlparameter}">manifest</a></td></tr>'
44 branchentry = '<tr class="parity{parity}"><td class="age"><i>{date|age} ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></a></td><td>{branch|escape}</td><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/{node|short}{sessionvars%urlparameter}">manifest</a></td></tr>'
44 diffblock = '<pre>#lines#</pre>'
45 diffblock = '<pre>#lines#</pre>'
45 filediffparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
46 filediffparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
46 filelogparent = '<tr><td align="right">parent #rev#:&nbsp;</td><td><a href="{url}file/{node|short}/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
47 filelogparent = '<tr><td align="right">parent #rev#:&nbsp;</td><td><a href="{url}file/{node|short}/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
47 filediffchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
48 filediffchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
48 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="{url}file{node|short}/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
49 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="{url}file{node|short}/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
49 shortlog = shortlog.tmpl
50 shortlog = shortlog.tmpl
50 tagtag = '<span class="tagtag" title="{name}">{name}</span> '
51 tagtag = '<span class="tagtag" title="{name}">{name}</span> '
51 branchtag = '<span class="branchtag" title="{name}">{name}</span> '
52 branchtag = '<span class="branchtag" title="{name}">{name}</span> '
52 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author|person#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b> <span class="logtags">{branches%branchtag}{tags%tagtag}</span></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>'
53 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author|person#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b> <span class="logtags">{branches%branchtag}{tags%tagtag}</span></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>'
53 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url}diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url}annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> #rename%filelogrename#</td></tr>'
54 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url}diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url}annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> #rename%filelogrename#</td></tr>'
54 archiveentry = ' | <a href="{url}archive/{node|short}{extension}">#type|escape#</a> '
55 archiveentry = ' | <a href="{url}archive/{node|short}{extension}">#type|escape#</a> '
55 indexentry = '<tr class="parity#parity#"><td><a class="list" href="#url#{sessionvars%urlparameter}"><b>#name|escape#</b></a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks">#archives%archiveentry#</td><td><a class="rss_logo" href="#url#rss-log">RSS</a> <a class="rss_logo" href="#url#atom-log">Atom</a></td></tr>'
56 indexentry = '<tr class="parity#parity#"><td><a class="list" href="#url#{sessionvars%urlparameter}"><b>#name|escape#</b></a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks">#archives%archiveentry#</td><td><a class="rss_logo" href="#url#rss-log">RSS</a> <a class="rss_logo" href="#url#atom-log">Atom</a></td></tr>'
56 index = index.tmpl
57 index = index.tmpl
57 urlparameter = '#separator##name#=#value|urlescape#'
58 urlparameter = '#separator##name#=#value|urlescape#'
58 hiddenformentry = '<input type="hidden" name="#name#" value="#value|escape#" />'
59 hiddenformentry = '<input type="hidden" name="#name#" value="#value|escape#" />'
@@ -1,8 +1,6 b''
1 Content-type: text/html; charset={encoding}
2
3 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
4 <html>
2 <html>
5 <head>
3 <head>
6 <link rel="icon" href="#staticurl#hgicon.png" type="image/png">
4 <link rel="icon" href="#staticurl#hgicon.png" type="image/png">
7 <meta name="robots" content="index, nofollow" />
5 <meta name="robots" content="index, nofollow" />
8 <link rel="stylesheet" href="#staticurl#style.css" type="text/css" />
6 <link rel="stylesheet" href="#staticurl#style.css" type="text/css" />
@@ -1,56 +1,57 b''
1 default = 'shortlog'
1 default = 'shortlog'
2 mimetype = 'text/html; charset={encoding}'
2 header = header.tmpl
3 header = header.tmpl
3 footer = footer.tmpl
4 footer = footer.tmpl
4 search = search.tmpl
5 search = search.tmpl
5 changelog = changelog.tmpl
6 changelog = changelog.tmpl
6 shortlog = shortlog.tmpl
7 shortlog = shortlog.tmpl
7 shortlogentry = shortlogentry.tmpl
8 shortlogentry = shortlogentry.tmpl
8 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
9 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
9 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
10 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
10 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
11 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
11 filedifflink = '<a href="#url#diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#file|escape#</a> '
12 filedifflink = '<a href="#url#diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#file|escape#</a> '
12 filenodelink = '<a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#file|escape#</a> '
13 filenodelink = '<a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#file|escape#</a> '
13 fileellipses = '...'
14 fileellipses = '...'
14 changelogentry = changelogentry.tmpl
15 changelogentry = changelogentry.tmpl
15 searchentry = changelogentry.tmpl
16 searchentry = changelogentry.tmpl
16 changeset = changeset.tmpl
17 changeset = changeset.tmpl
17 manifest = manifest.tmpl
18 manifest = manifest.tmpl
18 manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td>&nbsp;<td>&nbsp;<td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#/</a>'
19 manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td>&nbsp;<td>&nbsp;<td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#/</a>'
19 manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td align=right><tt class="date">#date|isodate#</tt>&nbsp;<td align=right><tt>#size#</tt>&nbsp;<td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a>'
20 manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td align=right><tt class="date">#date|isodate#</tt>&nbsp;<td align=right><tt>#size#</tt>&nbsp;<td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a>'
20 filerevision = filerevision.tmpl
21 filerevision = filerevision.tmpl
21 fileannotate = fileannotate.tmpl
22 fileannotate = fileannotate.tmpl
22 filediff = filediff.tmpl
23 filediff = filediff.tmpl
23 filelog = filelog.tmpl
24 filelog = filelog.tmpl
24 fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>'
25 fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>'
25 filelogentry = filelogentry.tmpl
26 filelogentry = filelogentry.tmpl
26 annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
27 annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
27 difflineplus = '<span class="plusline">#line|escape#</span>'
28 difflineplus = '<span class="plusline">#line|escape#</span>'
28 difflineminus = '<span class="minusline">#line|escape#</span>'
29 difflineminus = '<span class="minusline">#line|escape#</span>'
29 difflineat = '<span class="atline">#line|escape#</span>'
30 difflineat = '<span class="atline">#line|escape#</span>'
30 diffline = '#line|escape#'
31 diffline = '#line|escape#'
31 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
32 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
32 changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
33 changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
33 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
34 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
34 filerename = '{file|escape}@'
35 filerename = '{file|escape}@'
35 filelogrename = '<tr><th>base:</th><td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#file|escape#@#node|short#</a></td></tr>'
36 filelogrename = '<tr><th>base:</th><td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#file|escape#@#node|short#</a></td></tr>'
36 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
37 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
37 changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
38 changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
38 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
39 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
39 filerevchild = '<tr><td class="metatag">child:</td><td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
40 filerevchild = '<tr><td class="metatag">child:</td><td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
40 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
41 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
41 tags = tags.tmpl
42 tags = tags.tmpl
42 tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="#url#rev/#node|short#{sessionvars%urlparameter}">#tag|escape#</a></li>'
43 tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="#url#rev/#node|short#{sessionvars%urlparameter}">#tag|escape#</a></li>'
43 diffblock = '<pre class="parity#parity#">#lines#</pre>'
44 diffblock = '<pre class="parity#parity#">#lines#</pre>'
44 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
45 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
45 changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
46 changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
46 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
47 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
47 filelogparent = '<tr><th>parent #rev#:</th><td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
48 filelogparent = '<tr><th>parent #rev#:</th><td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
48 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
49 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
49 filelogchild = '<tr><th>child #rev#:</th><td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
50 filelogchild = '<tr><th>child #rev#:</th><td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
50 indexentry = '<tr class="parity#parity#"><td><a href="#url#{sessionvars%urlparameter}">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#rss-log">RSS</a> <a href="#url#atom-log">Atom</a> #archives%archiveentry#</td></tr>'
51 indexentry = '<tr class="parity#parity#"><td><a href="#url#{sessionvars%urlparameter}">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#rss-log">RSS</a> <a href="#url#atom-log">Atom</a> #archives%archiveentry#</td></tr>'
51 index = index.tmpl
52 index = index.tmpl
52 archiveentry = '<a href="#url#archive/#node|short##extension|urlescape#">#type|escape#</a> '
53 archiveentry = '<a href="#url#archive/#node|short##extension|urlescape#">#type|escape#</a> '
53 notfound = notfound.tmpl
54 notfound = notfound.tmpl
54 error = error.tmpl
55 error = error.tmpl
55 urlparameter = '#separator##name#=#value|urlescape#'
56 urlparameter = '#separator##name#=#value|urlescape#'
56 hiddenformentry = '<input type="hidden" name="#name#" value="#value|escape#" />'
57 hiddenformentry = '<input type="hidden" name="#name#" value="#value|escape#" />'
@@ -1,8 +1,6 b''
1 Content-type: text/html
2
3 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
4 <html>
2 <html>
5 <head>
3 <head>
6 <link rel="icon" href="?static=hgicon.png" type="image/png">
4 <link rel="icon" href="?static=hgicon.png" type="image/png">
7 <meta name="robots" content="index, nofollow" />
5 <meta name="robots" content="index, nofollow" />
8 <link rel="stylesheet" href="?static=style.css" type="text/css" />
6 <link rel="stylesheet" href="?static=style.css" type="text/css" />
@@ -1,53 +1,54 b''
1 default = 'changelog'
1 default = 'changelog'
2 mimetype = 'text/html'
2 header = header.tmpl
3 header = header.tmpl
3 footer = footer.tmpl
4 footer = footer.tmpl
4 search = search.tmpl
5 search = search.tmpl
5 changelog = changelog.tmpl
6 changelog = changelog.tmpl
6 shortlog = shortlog.tmpl
7 shortlog = shortlog.tmpl
7 shortlogentry = shortlogentry.tmpl
8 shortlogentry = shortlogentry.tmpl
8 naventry = '<a href="?cl={node|short}">{label|escape}</a> '
9 naventry = '<a href="?cl={node|short}">{label|escape}</a> '
9 navshortentry = '<a href="?sl={node|short}">{label|escape}</a> '
10 navshortentry = '<a href="?sl={node|short}">{label|escape}</a> '
10 filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> '
11 filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> '
11 filenodelink = '<a href="?f=#node|short#;file=#file|urlescape#">#file|escape#</a> '
12 filenodelink = '<a href="?f=#node|short#;file=#file|urlescape#">#file|escape#</a> '
12 fileellipses = '...'
13 fileellipses = '...'
13 changelogentry = changelogentry.tmpl
14 changelogentry = changelogentry.tmpl
14 searchentry = changelogentry.tmpl
15 searchentry = changelogentry.tmpl
15 changeset = changeset.tmpl
16 changeset = changeset.tmpl
16 manifest = manifest.tmpl
17 manifest = manifest.tmpl
17 manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td><a href="?mf=#node|short#;path=#path|urlescape#">#basename|escape#/</a>'
18 manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td><a href="?mf=#node|short#;path=#path|urlescape#">#basename|escape#/</a>'
18 manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td><a href="?f=#node|short#;file=#file|urlescape#">#basename|escape#</a>'
19 manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td><a href="?f=#node|short#;file=#file|urlescape#">#basename|escape#</a>'
19 filerevision = filerevision.tmpl
20 filerevision = filerevision.tmpl
20 fileannotate = fileannotate.tmpl
21 fileannotate = fileannotate.tmpl
21 filediff = filediff.tmpl
22 filediff = filediff.tmpl
22 filelog = filelog.tmpl
23 filelog = filelog.tmpl
23 fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>'
24 fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>'
24 filelogentry = filelogentry.tmpl
25 filelogentry = filelogentry.tmpl
25 annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="?fa=#node|short#;file=#file|urlescape#">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
26 annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="?fa=#node|short#;file=#file|urlescape#">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
26 difflineplus = '<span class="plusline">#line|escape#</span>'
27 difflineplus = '<span class="plusline">#line|escape#</span>'
27 difflineminus = '<span class="minusline">#line|escape#</span>'
28 difflineminus = '<span class="minusline">#line|escape#</span>'
28 difflineat = '<span class="atline">#line|escape#</span>'
29 difflineat = '<span class="atline">#line|escape#</span>'
29 diffline = '#line|escape#'
30 diffline = '#line|escape#'
30 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
31 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
31 changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
32 changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
32 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
33 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
33 filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
34 filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
34 filelogrename = '<tr><th>base:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
35 filelogrename = '<tr><th>base:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
35 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
36 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
36 changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
37 changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
37 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
38 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
38 filerevchild = '<tr><td class="metatag">child:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
39 filerevchild = '<tr><td class="metatag">child:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
39 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
40 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
40 tags = tags.tmpl
41 tags = tags.tmpl
41 tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="?cs=#node|short#">#tag|escape#</a></li>'
42 tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="?cs=#node|short#">#tag|escape#</a></li>'
42 diffblock = '<pre class="parity#parity#">#lines#</pre>'
43 diffblock = '<pre class="parity#parity#">#lines#</pre>'
43 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
44 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
44 changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
45 changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
45 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
46 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
46 filelogparent = '<tr><th>parent #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
47 filelogparent = '<tr><th>parent #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
47 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
48 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
48 filelogchild = '<tr><th>child #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
49 filelogchild = '<tr><th>child #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
49 indexentry = '<tr class="parity#parity#"><td><a href="#url#">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#?cl=tip;style=rss">RSS</a> <a href="#url#atom-log">Atom</a> #archives%archiveentry#</td></tr>'
50 indexentry = '<tr class="parity#parity#"><td><a href="#url#">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#?cl=tip;style=rss">RSS</a> <a href="#url#atom-log">Atom</a> #archives%archiveentry#</td></tr>'
50 index = index.tmpl
51 index = index.tmpl
51 archiveentry = '<a href="#url#?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
52 archiveentry = '<a href="#url#?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
52 notfound = notfound.tmpl
53 notfound = notfound.tmpl
53 error = error.tmpl
54 error = error.tmpl
@@ -1,22 +1,23 b''
1 header = header.tmpl
1 mimetype = 'text/plain; charset={encoding}'
2 header = ''
2 footer = ''
3 footer = ''
3 changeset = changeset.tmpl
4 changeset = changeset.tmpl
4 difflineplus = '#line#'
5 difflineplus = '#line#'
5 difflineminus = '#line#'
6 difflineminus = '#line#'
6 difflineat = '#line#'
7 difflineat = '#line#'
7 diffline = '#line#'
8 diffline = '#line#'
8 changesetparent = '# Parent #node#'
9 changesetparent = '# Parent #node#'
9 changesetchild = '# Child #node#'
10 changesetchild = '# Child #node#'
10 filenodelink = ''
11 filenodelink = ''
11 fileline = '#line#'
12 fileline = '#line#'
12 diffblock = '#lines#'
13 diffblock = '#lines#'
13 filediff = filediff.tmpl
14 filediff = filediff.tmpl
14 fileannotate = fileannotate.tmpl
15 fileannotate = fileannotate.tmpl
15 annotateline = '#author#@#rev#: #line#'
16 annotateline = '#author#@#rev#: #line#'
16 manifest = manifest.tmpl
17 manifest = manifest.tmpl
17 manifestdirentry = 'drwxr-xr-x {basename}\n'
18 manifestdirentry = 'drwxr-xr-x {basename}\n'
18 manifestfileentry = '{permissions|permissions} {size} {basename}\n'
19 manifestfileentry = '{permissions|permissions} {size} {basename}\n'
19 index = index.tmpl
20 index = index.tmpl
20 notfound = notfound.tmpl
21 notfound = notfound.tmpl
21 error = error.tmpl
22 error = error.tmpl
22 indexentry = '#url#\n'
23 indexentry = '#url#\n'
@@ -1,7 +1,5 b''
1 Content-type: text/xml; charset={encoding}
2
3 <?xml version="1.0" encoding="{encoding}"?>
1 <?xml version="1.0" encoding="{encoding}"?>
4 <rss version="2.0">
2 <rss version="2.0">
5 <channel>
3 <channel>
6 <link>{urlbase}{url}</link>
4 <link>{urlbase}{url}</link>
7 <language>en-us</language>
5 <language>en-us</language>
@@ -1,8 +1,9 b''
1 default = 'changelog'
1 default = 'changelog'
2 mimetype = 'text/xml; charset={encoding}'
2 header = header.tmpl
3 header = header.tmpl
3 changelog = changelog.tmpl
4 changelog = changelog.tmpl
4 changelogentry = changelogentry.tmpl
5 changelogentry = changelogentry.tmpl
5 filelog = filelog.tmpl
6 filelog = filelog.tmpl
6 filelogentry = filelogentry.tmpl
7 filelogentry = filelogentry.tmpl
7 tags = tags.tmpl
8 tags = tags.tmpl
8 tagentry = tagentry.tmpl
9 tagentry = tagentry.tmpl
@@ -1,72 +1,72 b''
1 changeset: 0:4cbec7e6f8c4
1 changeset: 0:4cbec7e6f8c4
2 tag: tip
2 tag: tip
3 user: Testing
3 user: Testing
4 date: Thu Jan 01 00:00:00 1970 +0000
4 date: Thu Jan 01 00:00:00 1970 +0000
5 summary: test
5 summary: test
6
6
7 ---- HEADERS
7 ---- HEADERS
8 200 Script output follows
8 200 Script output follows
9 ---- DATA
9 ---- DATA
10 [('content-type', 'application/atom+xml; charset=ascii')]
10 [('Content-type', 'application/atom+xml; charset=ascii')]
11 <?xml version="1.0" encoding="ascii"?>
11 <?xml version="1.0" encoding="ascii"?>
12 <feed xmlns="http://www.w3.org/2005/Atom">
12 <feed xmlns="http://www.w3.org/2005/Atom">
13 <!-- Changelog -->
13 <!-- Changelog -->
14 <id>http://127.0.0.1/</id>
14 <id>http://127.0.0.1/</id>
15 <link rel="self" href="http://127.0.0.1/atom-log"/>
15 <link rel="self" href="http://127.0.0.1/atom-log"/>
16 <link rel="alternate" href="http://127.0.0.1/"/>
16 <link rel="alternate" href="http://127.0.0.1/"/>
17 <title>repo Changelog</title>
17 <title>repo Changelog</title>
18 <updated>1970-01-01T00:00:00+00:00</updated>
18 <updated>1970-01-01T00:00:00+00:00</updated>
19
19
20 <entry>
20 <entry>
21 <title>test</title>
21 <title>test</title>
22 <id>http://www.selenic.com/mercurial/#changeset-4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e</id>
22 <id>http://www.selenic.com/mercurial/#changeset-4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e</id>
23 <link href="http://127.0.0.1/rev/4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e"/>
23 <link href="http://127.0.0.1/rev/4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e"/>
24 <author>
24 <author>
25 <name>Testing</name>
25 <name>Testing</name>
26 <email>&#84;&#101;&#115;&#116;&#105;&#110;&#103;</email>
26 <email>&#84;&#101;&#115;&#116;&#105;&#110;&#103;</email>
27 </author>
27 </author>
28 <updated>1970-01-01T00:00:00+00:00</updated>
28 <updated>1970-01-01T00:00:00+00:00</updated>
29 <published>1970-01-01T00:00:00+00:00</published>
29 <published>1970-01-01T00:00:00+00:00</published>
30 <content type="xhtml">
30 <content type="xhtml">
31 <div xmlns="http://www.w3.org/1999/xhtml">
31 <div xmlns="http://www.w3.org/1999/xhtml">
32 <pre xml:space="preserve">test</pre>
32 <pre xml:space="preserve">test</pre>
33 </div>
33 </div>
34 </content>
34 </content>
35 </entry>
35 </entry>
36
36
37 </feed>
37 </feed>
38
38
39 ---- ERRORS
39 ---- ERRORS
40
40
41 ---- HEADERS
41 ---- HEADERS
42 200 Script output follows
42 200 Script output follows
43 ---- DATA
43 ---- DATA
44 [('content-type', 'text/plain; charset=ascii')]
44 [('Content-type', 'text/plain; charset=ascii')]
45
45
46 -rw-r--r-- 4 bar
46 -rw-r--r-- 4 bar
47
47
48
48
49
49
50 ---- ERRORS
50 ---- ERRORS
51
51
52 ---- HEADERS
52 ---- HEADERS
53 200 Script output follows
53 200 Script output follows
54 ---- DATA
54 ---- DATA
55 [('content-type', 'text/plain; charset=ascii')]
55 [('Content-type', 'text/plain; charset=ascii')]
56
56
57 /repo/
57 /repo/
58
58
59
59
60 ---- ERRORS
60 ---- ERRORS
61
61
62 ---- HEADERS
62 ---- HEADERS
63 200 Script output follows
63 200 Script output follows
64 ---- DATA
64 ---- DATA
65 [('content-type', 'text/plain; charset=ascii')]
65 [('Content-type', 'text/plain; charset=ascii')]
66
66
67 -rw-r--r-- 4 bar
67 -rw-r--r-- 4 bar
68
68
69
69
70
70
71 ---- ERRORS
71 ---- ERRORS
72
72
@@ -1,12 +1,12 b''
1 changeset: 0:61c9426e69fe
1 changeset: 0:61c9426e69fe
2 tag: tip
2 tag: tip
3 user: test
3 user: test
4 date: Thu Jan 01 00:00:00 1970 +0000
4 date: Thu Jan 01 00:00:00 1970 +0000
5 summary: test
5 summary: test
6
6
7 ---- HEADERS
7 ---- HEADERS
8 200 Script output follows
8 200 Script output follows
9 ---- DATA
9 ---- DATA
10 [('content-type', 'text/html; charset=ascii')]
10 [('Content-type', 'text/html; charset=ascii')]
11 ---- ERRORS
11 ---- ERRORS
12
12
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now