##// END OF EJS Templates
hgweb: plug followlines action in annotate view...
Denis Laxalde -
r32994:1c97df5e default
parent child Browse files
Show More
@@ -1,1383 +1,1385
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import cgi
10 import cgi
11 import copy
11 import copy
12 import mimetypes
12 import mimetypes
13 import os
13 import os
14 import re
14 import re
15
15
16 from ..i18n import _
16 from ..i18n import _
17 from ..node import hex, short
17 from ..node import hex, short
18
18
19 from .common import (
19 from .common import (
20 ErrorResponse,
20 ErrorResponse,
21 HTTP_FORBIDDEN,
21 HTTP_FORBIDDEN,
22 HTTP_NOT_FOUND,
22 HTTP_NOT_FOUND,
23 HTTP_OK,
23 HTTP_OK,
24 get_contact,
24 get_contact,
25 paritygen,
25 paritygen,
26 staticfile,
26 staticfile,
27 )
27 )
28
28
29 from .. import (
29 from .. import (
30 archival,
30 archival,
31 dagop,
31 dagop,
32 encoding,
32 encoding,
33 error,
33 error,
34 graphmod,
34 graphmod,
35 revset,
35 revset,
36 revsetlang,
36 revsetlang,
37 scmutil,
37 scmutil,
38 smartset,
38 smartset,
39 templatefilters,
39 templatefilters,
40 templater,
40 templater,
41 util,
41 util,
42 )
42 )
43
43
44 from . import (
44 from . import (
45 webutil,
45 webutil,
46 )
46 )
47
47
48 __all__ = []
48 __all__ = []
49 commands = {}
49 commands = {}
50
50
51 class webcommand(object):
51 class webcommand(object):
52 """Decorator used to register a web command handler.
52 """Decorator used to register a web command handler.
53
53
54 The decorator takes as its positional arguments the name/path the
54 The decorator takes as its positional arguments the name/path the
55 command should be accessible under.
55 command should be accessible under.
56
56
57 Usage:
57 Usage:
58
58
59 @webcommand('mycommand')
59 @webcommand('mycommand')
60 def mycommand(web, req, tmpl):
60 def mycommand(web, req, tmpl):
61 pass
61 pass
62 """
62 """
63
63
64 def __init__(self, name):
64 def __init__(self, name):
65 self.name = name
65 self.name = name
66
66
67 def __call__(self, func):
67 def __call__(self, func):
68 __all__.append(self.name)
68 __all__.append(self.name)
69 commands[self.name] = func
69 commands[self.name] = func
70 return func
70 return func
71
71
72 @webcommand('log')
72 @webcommand('log')
73 def log(web, req, tmpl):
73 def log(web, req, tmpl):
74 """
74 """
75 /log[/{revision}[/{path}]]
75 /log[/{revision}[/{path}]]
76 --------------------------
76 --------------------------
77
77
78 Show repository or file history.
78 Show repository or file history.
79
79
80 For URLs of the form ``/log/{revision}``, a list of changesets starting at
80 For URLs of the form ``/log/{revision}``, a list of changesets starting at
81 the specified changeset identifier is shown. If ``{revision}`` is not
81 the specified changeset identifier is shown. If ``{revision}`` is not
82 defined, the default is ``tip``. This form is equivalent to the
82 defined, the default is ``tip``. This form is equivalent to the
83 ``changelog`` handler.
83 ``changelog`` handler.
84
84
85 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
85 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
86 file will be shown. This form is equivalent to the ``filelog`` handler.
86 file will be shown. This form is equivalent to the ``filelog`` handler.
87 """
87 """
88
88
89 if 'file' in req.form and req.form['file'][0]:
89 if 'file' in req.form and req.form['file'][0]:
90 return filelog(web, req, tmpl)
90 return filelog(web, req, tmpl)
91 else:
91 else:
92 return changelog(web, req, tmpl)
92 return changelog(web, req, tmpl)
93
93
94 @webcommand('rawfile')
94 @webcommand('rawfile')
95 def rawfile(web, req, tmpl):
95 def rawfile(web, req, tmpl):
96 guessmime = web.configbool('web', 'guessmime', False)
96 guessmime = web.configbool('web', 'guessmime', False)
97
97
98 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
98 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
99 if not path:
99 if not path:
100 content = manifest(web, req, tmpl)
100 content = manifest(web, req, tmpl)
101 req.respond(HTTP_OK, web.ctype)
101 req.respond(HTTP_OK, web.ctype)
102 return content
102 return content
103
103
104 try:
104 try:
105 fctx = webutil.filectx(web.repo, req)
105 fctx = webutil.filectx(web.repo, req)
106 except error.LookupError as inst:
106 except error.LookupError as inst:
107 try:
107 try:
108 content = manifest(web, req, tmpl)
108 content = manifest(web, req, tmpl)
109 req.respond(HTTP_OK, web.ctype)
109 req.respond(HTTP_OK, web.ctype)
110 return content
110 return content
111 except ErrorResponse:
111 except ErrorResponse:
112 raise inst
112 raise inst
113
113
114 path = fctx.path()
114 path = fctx.path()
115 text = fctx.data()
115 text = fctx.data()
116 mt = 'application/binary'
116 mt = 'application/binary'
117 if guessmime:
117 if guessmime:
118 mt = mimetypes.guess_type(path)[0]
118 mt = mimetypes.guess_type(path)[0]
119 if mt is None:
119 if mt is None:
120 if util.binary(text):
120 if util.binary(text):
121 mt = 'application/binary'
121 mt = 'application/binary'
122 else:
122 else:
123 mt = 'text/plain'
123 mt = 'text/plain'
124 if mt.startswith('text/'):
124 if mt.startswith('text/'):
125 mt += '; charset="%s"' % encoding.encoding
125 mt += '; charset="%s"' % encoding.encoding
126
126
127 req.respond(HTTP_OK, mt, path, body=text)
127 req.respond(HTTP_OK, mt, path, body=text)
128 return []
128 return []
129
129
130 def _filerevision(web, req, tmpl, fctx):
130 def _filerevision(web, req, tmpl, fctx):
131 f = fctx.path()
131 f = fctx.path()
132 text = fctx.data()
132 text = fctx.data()
133 parity = paritygen(web.stripecount)
133 parity = paritygen(web.stripecount)
134 ishead = fctx.filerev() in fctx.filelog().headrevs()
134 ishead = fctx.filerev() in fctx.filelog().headrevs()
135
135
136 if util.binary(text):
136 if util.binary(text):
137 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
137 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
138 text = '(binary:%s)' % mt
138 text = '(binary:%s)' % mt
139
139
140 def lines():
140 def lines():
141 for lineno, t in enumerate(text.splitlines(True)):
141 for lineno, t in enumerate(text.splitlines(True)):
142 yield {"line": t,
142 yield {"line": t,
143 "lineid": "l%d" % (lineno + 1),
143 "lineid": "l%d" % (lineno + 1),
144 "linenumber": "% 6d" % (lineno + 1),
144 "linenumber": "% 6d" % (lineno + 1),
145 "parity": next(parity)}
145 "parity": next(parity)}
146
146
147 return tmpl("filerevision",
147 return tmpl("filerevision",
148 file=f,
148 file=f,
149 path=webutil.up(f),
149 path=webutil.up(f),
150 text=lines(),
150 text=lines(),
151 symrev=webutil.symrevorshortnode(req, fctx),
151 symrev=webutil.symrevorshortnode(req, fctx),
152 rename=webutil.renamelink(fctx),
152 rename=webutil.renamelink(fctx),
153 permissions=fctx.manifest().flags(f),
153 permissions=fctx.manifest().flags(f),
154 ishead=int(ishead),
154 ishead=int(ishead),
155 **webutil.commonentry(web.repo, fctx))
155 **webutil.commonentry(web.repo, fctx))
156
156
157 @webcommand('file')
157 @webcommand('file')
158 def file(web, req, tmpl):
158 def file(web, req, tmpl):
159 """
159 """
160 /file/{revision}[/{path}]
160 /file/{revision}[/{path}]
161 -------------------------
161 -------------------------
162
162
163 Show information about a directory or file in the repository.
163 Show information about a directory or file in the repository.
164
164
165 Info about the ``path`` given as a URL parameter will be rendered.
165 Info about the ``path`` given as a URL parameter will be rendered.
166
166
167 If ``path`` is a directory, information about the entries in that
167 If ``path`` is a directory, information about the entries in that
168 directory will be rendered. This form is equivalent to the ``manifest``
168 directory will be rendered. This form is equivalent to the ``manifest``
169 handler.
169 handler.
170
170
171 If ``path`` is a file, information about that file will be shown via
171 If ``path`` is a file, information about that file will be shown via
172 the ``filerevision`` template.
172 the ``filerevision`` template.
173
173
174 If ``path`` is not defined, information about the root directory will
174 If ``path`` is not defined, information about the root directory will
175 be rendered.
175 be rendered.
176 """
176 """
177 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
177 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
178 if not path:
178 if not path:
179 return manifest(web, req, tmpl)
179 return manifest(web, req, tmpl)
180 try:
180 try:
181 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
181 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
182 except error.LookupError as inst:
182 except error.LookupError as inst:
183 try:
183 try:
184 return manifest(web, req, tmpl)
184 return manifest(web, req, tmpl)
185 except ErrorResponse:
185 except ErrorResponse:
186 raise inst
186 raise inst
187
187
188 def _search(web, req, tmpl):
188 def _search(web, req, tmpl):
189 MODE_REVISION = 'rev'
189 MODE_REVISION = 'rev'
190 MODE_KEYWORD = 'keyword'
190 MODE_KEYWORD = 'keyword'
191 MODE_REVSET = 'revset'
191 MODE_REVSET = 'revset'
192
192
193 def revsearch(ctx):
193 def revsearch(ctx):
194 yield ctx
194 yield ctx
195
195
196 def keywordsearch(query):
196 def keywordsearch(query):
197 lower = encoding.lower
197 lower = encoding.lower
198 qw = lower(query).split()
198 qw = lower(query).split()
199
199
200 def revgen():
200 def revgen():
201 cl = web.repo.changelog
201 cl = web.repo.changelog
202 for i in xrange(len(web.repo) - 1, 0, -100):
202 for i in xrange(len(web.repo) - 1, 0, -100):
203 l = []
203 l = []
204 for j in cl.revs(max(0, i - 99), i):
204 for j in cl.revs(max(0, i - 99), i):
205 ctx = web.repo[j]
205 ctx = web.repo[j]
206 l.append(ctx)
206 l.append(ctx)
207 l.reverse()
207 l.reverse()
208 for e in l:
208 for e in l:
209 yield e
209 yield e
210
210
211 for ctx in revgen():
211 for ctx in revgen():
212 miss = 0
212 miss = 0
213 for q in qw:
213 for q in qw:
214 if not (q in lower(ctx.user()) or
214 if not (q in lower(ctx.user()) or
215 q in lower(ctx.description()) or
215 q in lower(ctx.description()) or
216 q in lower(" ".join(ctx.files()))):
216 q in lower(" ".join(ctx.files()))):
217 miss = 1
217 miss = 1
218 break
218 break
219 if miss:
219 if miss:
220 continue
220 continue
221
221
222 yield ctx
222 yield ctx
223
223
224 def revsetsearch(revs):
224 def revsetsearch(revs):
225 for r in revs:
225 for r in revs:
226 yield web.repo[r]
226 yield web.repo[r]
227
227
228 searchfuncs = {
228 searchfuncs = {
229 MODE_REVISION: (revsearch, 'exact revision search'),
229 MODE_REVISION: (revsearch, 'exact revision search'),
230 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
230 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
231 MODE_REVSET: (revsetsearch, 'revset expression search'),
231 MODE_REVSET: (revsetsearch, 'revset expression search'),
232 }
232 }
233
233
234 def getsearchmode(query):
234 def getsearchmode(query):
235 try:
235 try:
236 ctx = web.repo[query]
236 ctx = web.repo[query]
237 except (error.RepoError, error.LookupError):
237 except (error.RepoError, error.LookupError):
238 # query is not an exact revision pointer, need to
238 # query is not an exact revision pointer, need to
239 # decide if it's a revset expression or keywords
239 # decide if it's a revset expression or keywords
240 pass
240 pass
241 else:
241 else:
242 return MODE_REVISION, ctx
242 return MODE_REVISION, ctx
243
243
244 revdef = 'reverse(%s)' % query
244 revdef = 'reverse(%s)' % query
245 try:
245 try:
246 tree = revsetlang.parse(revdef)
246 tree = revsetlang.parse(revdef)
247 except error.ParseError:
247 except error.ParseError:
248 # can't parse to a revset tree
248 # can't parse to a revset tree
249 return MODE_KEYWORD, query
249 return MODE_KEYWORD, query
250
250
251 if revsetlang.depth(tree) <= 2:
251 if revsetlang.depth(tree) <= 2:
252 # no revset syntax used
252 # no revset syntax used
253 return MODE_KEYWORD, query
253 return MODE_KEYWORD, query
254
254
255 if any((token, (value or '')[:3]) == ('string', 're:')
255 if any((token, (value or '')[:3]) == ('string', 're:')
256 for token, value, pos in revsetlang.tokenize(revdef)):
256 for token, value, pos in revsetlang.tokenize(revdef)):
257 return MODE_KEYWORD, query
257 return MODE_KEYWORD, query
258
258
259 funcsused = revsetlang.funcsused(tree)
259 funcsused = revsetlang.funcsused(tree)
260 if not funcsused.issubset(revset.safesymbols):
260 if not funcsused.issubset(revset.safesymbols):
261 return MODE_KEYWORD, query
261 return MODE_KEYWORD, query
262
262
263 mfunc = revset.match(web.repo.ui, revdef)
263 mfunc = revset.match(web.repo.ui, revdef)
264 try:
264 try:
265 revs = mfunc(web.repo)
265 revs = mfunc(web.repo)
266 return MODE_REVSET, revs
266 return MODE_REVSET, revs
267 # ParseError: wrongly placed tokens, wrongs arguments, etc
267 # ParseError: wrongly placed tokens, wrongs arguments, etc
268 # RepoLookupError: no such revision, e.g. in 'revision:'
268 # RepoLookupError: no such revision, e.g. in 'revision:'
269 # Abort: bookmark/tag not exists
269 # Abort: bookmark/tag not exists
270 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
270 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
271 except (error.ParseError, error.RepoLookupError, error.Abort,
271 except (error.ParseError, error.RepoLookupError, error.Abort,
272 LookupError):
272 LookupError):
273 return MODE_KEYWORD, query
273 return MODE_KEYWORD, query
274
274
275 def changelist(**map):
275 def changelist(**map):
276 count = 0
276 count = 0
277
277
278 for ctx in searchfunc[0](funcarg):
278 for ctx in searchfunc[0](funcarg):
279 count += 1
279 count += 1
280 n = ctx.node()
280 n = ctx.node()
281 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
281 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
282 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
282 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
283
283
284 yield tmpl('searchentry',
284 yield tmpl('searchentry',
285 parity=next(parity),
285 parity=next(parity),
286 changelogtag=showtags,
286 changelogtag=showtags,
287 files=files,
287 files=files,
288 **webutil.commonentry(web.repo, ctx))
288 **webutil.commonentry(web.repo, ctx))
289
289
290 if count >= revcount:
290 if count >= revcount:
291 break
291 break
292
292
293 query = req.form['rev'][0]
293 query = req.form['rev'][0]
294 revcount = web.maxchanges
294 revcount = web.maxchanges
295 if 'revcount' in req.form:
295 if 'revcount' in req.form:
296 try:
296 try:
297 revcount = int(req.form.get('revcount', [revcount])[0])
297 revcount = int(req.form.get('revcount', [revcount])[0])
298 revcount = max(revcount, 1)
298 revcount = max(revcount, 1)
299 tmpl.defaults['sessionvars']['revcount'] = revcount
299 tmpl.defaults['sessionvars']['revcount'] = revcount
300 except ValueError:
300 except ValueError:
301 pass
301 pass
302
302
303 lessvars = copy.copy(tmpl.defaults['sessionvars'])
303 lessvars = copy.copy(tmpl.defaults['sessionvars'])
304 lessvars['revcount'] = max(revcount / 2, 1)
304 lessvars['revcount'] = max(revcount / 2, 1)
305 lessvars['rev'] = query
305 lessvars['rev'] = query
306 morevars = copy.copy(tmpl.defaults['sessionvars'])
306 morevars = copy.copy(tmpl.defaults['sessionvars'])
307 morevars['revcount'] = revcount * 2
307 morevars['revcount'] = revcount * 2
308 morevars['rev'] = query
308 morevars['rev'] = query
309
309
310 mode, funcarg = getsearchmode(query)
310 mode, funcarg = getsearchmode(query)
311
311
312 if 'forcekw' in req.form:
312 if 'forcekw' in req.form:
313 showforcekw = ''
313 showforcekw = ''
314 showunforcekw = searchfuncs[mode][1]
314 showunforcekw = searchfuncs[mode][1]
315 mode = MODE_KEYWORD
315 mode = MODE_KEYWORD
316 funcarg = query
316 funcarg = query
317 else:
317 else:
318 if mode != MODE_KEYWORD:
318 if mode != MODE_KEYWORD:
319 showforcekw = searchfuncs[MODE_KEYWORD][1]
319 showforcekw = searchfuncs[MODE_KEYWORD][1]
320 else:
320 else:
321 showforcekw = ''
321 showforcekw = ''
322 showunforcekw = ''
322 showunforcekw = ''
323
323
324 searchfunc = searchfuncs[mode]
324 searchfunc = searchfuncs[mode]
325
325
326 tip = web.repo['tip']
326 tip = web.repo['tip']
327 parity = paritygen(web.stripecount)
327 parity = paritygen(web.stripecount)
328
328
329 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
329 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
330 entries=changelist, archives=web.archivelist("tip"),
330 entries=changelist, archives=web.archivelist("tip"),
331 morevars=morevars, lessvars=lessvars,
331 morevars=morevars, lessvars=lessvars,
332 modedesc=searchfunc[1],
332 modedesc=searchfunc[1],
333 showforcekw=showforcekw, showunforcekw=showunforcekw)
333 showforcekw=showforcekw, showunforcekw=showunforcekw)
334
334
335 @webcommand('changelog')
335 @webcommand('changelog')
336 def changelog(web, req, tmpl, shortlog=False):
336 def changelog(web, req, tmpl, shortlog=False):
337 """
337 """
338 /changelog[/{revision}]
338 /changelog[/{revision}]
339 -----------------------
339 -----------------------
340
340
341 Show information about multiple changesets.
341 Show information about multiple changesets.
342
342
343 If the optional ``revision`` URL argument is absent, information about
343 If the optional ``revision`` URL argument is absent, information about
344 all changesets starting at ``tip`` will be rendered. If the ``revision``
344 all changesets starting at ``tip`` will be rendered. If the ``revision``
345 argument is present, changesets will be shown starting from the specified
345 argument is present, changesets will be shown starting from the specified
346 revision.
346 revision.
347
347
348 If ``revision`` is absent, the ``rev`` query string argument may be
348 If ``revision`` is absent, the ``rev`` query string argument may be
349 defined. This will perform a search for changesets.
349 defined. This will perform a search for changesets.
350
350
351 The argument for ``rev`` can be a single revision, a revision set,
351 The argument for ``rev`` can be a single revision, a revision set,
352 or a literal keyword to search for in changeset data (equivalent to
352 or a literal keyword to search for in changeset data (equivalent to
353 :hg:`log -k`).
353 :hg:`log -k`).
354
354
355 The ``revcount`` query string argument defines the maximum numbers of
355 The ``revcount`` query string argument defines the maximum numbers of
356 changesets to render.
356 changesets to render.
357
357
358 For non-searches, the ``changelog`` template will be rendered.
358 For non-searches, the ``changelog`` template will be rendered.
359 """
359 """
360
360
361 query = ''
361 query = ''
362 if 'node' in req.form:
362 if 'node' in req.form:
363 ctx = webutil.changectx(web.repo, req)
363 ctx = webutil.changectx(web.repo, req)
364 symrev = webutil.symrevorshortnode(req, ctx)
364 symrev = webutil.symrevorshortnode(req, ctx)
365 elif 'rev' in req.form:
365 elif 'rev' in req.form:
366 return _search(web, req, tmpl)
366 return _search(web, req, tmpl)
367 else:
367 else:
368 ctx = web.repo['tip']
368 ctx = web.repo['tip']
369 symrev = 'tip'
369 symrev = 'tip'
370
370
371 def changelist():
371 def changelist():
372 revs = []
372 revs = []
373 if pos != -1:
373 if pos != -1:
374 revs = web.repo.changelog.revs(pos, 0)
374 revs = web.repo.changelog.revs(pos, 0)
375 curcount = 0
375 curcount = 0
376 for rev in revs:
376 for rev in revs:
377 curcount += 1
377 curcount += 1
378 if curcount > revcount + 1:
378 if curcount > revcount + 1:
379 break
379 break
380
380
381 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
381 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
382 entry['parity'] = next(parity)
382 entry['parity'] = next(parity)
383 yield entry
383 yield entry
384
384
385 if shortlog:
385 if shortlog:
386 revcount = web.maxshortchanges
386 revcount = web.maxshortchanges
387 else:
387 else:
388 revcount = web.maxchanges
388 revcount = web.maxchanges
389
389
390 if 'revcount' in req.form:
390 if 'revcount' in req.form:
391 try:
391 try:
392 revcount = int(req.form.get('revcount', [revcount])[0])
392 revcount = int(req.form.get('revcount', [revcount])[0])
393 revcount = max(revcount, 1)
393 revcount = max(revcount, 1)
394 tmpl.defaults['sessionvars']['revcount'] = revcount
394 tmpl.defaults['sessionvars']['revcount'] = revcount
395 except ValueError:
395 except ValueError:
396 pass
396 pass
397
397
398 lessvars = copy.copy(tmpl.defaults['sessionvars'])
398 lessvars = copy.copy(tmpl.defaults['sessionvars'])
399 lessvars['revcount'] = max(revcount / 2, 1)
399 lessvars['revcount'] = max(revcount / 2, 1)
400 morevars = copy.copy(tmpl.defaults['sessionvars'])
400 morevars = copy.copy(tmpl.defaults['sessionvars'])
401 morevars['revcount'] = revcount * 2
401 morevars['revcount'] = revcount * 2
402
402
403 count = len(web.repo)
403 count = len(web.repo)
404 pos = ctx.rev()
404 pos = ctx.rev()
405 parity = paritygen(web.stripecount)
405 parity = paritygen(web.stripecount)
406
406
407 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
407 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
408
408
409 entries = list(changelist())
409 entries = list(changelist())
410 latestentry = entries[:1]
410 latestentry = entries[:1]
411 if len(entries) > revcount:
411 if len(entries) > revcount:
412 nextentry = entries[-1:]
412 nextentry = entries[-1:]
413 entries = entries[:-1]
413 entries = entries[:-1]
414 else:
414 else:
415 nextentry = []
415 nextentry = []
416
416
417 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
417 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
418 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
418 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
419 entries=entries,
419 entries=entries,
420 latestentry=latestentry, nextentry=nextentry,
420 latestentry=latestentry, nextentry=nextentry,
421 archives=web.archivelist("tip"), revcount=revcount,
421 archives=web.archivelist("tip"), revcount=revcount,
422 morevars=morevars, lessvars=lessvars, query=query)
422 morevars=morevars, lessvars=lessvars, query=query)
423
423
424 @webcommand('shortlog')
424 @webcommand('shortlog')
425 def shortlog(web, req, tmpl):
425 def shortlog(web, req, tmpl):
426 """
426 """
427 /shortlog
427 /shortlog
428 ---------
428 ---------
429
429
430 Show basic information about a set of changesets.
430 Show basic information about a set of changesets.
431
431
432 This accepts the same parameters as the ``changelog`` handler. The only
432 This accepts the same parameters as the ``changelog`` handler. The only
433 difference is the ``shortlog`` template will be rendered instead of the
433 difference is the ``shortlog`` template will be rendered instead of the
434 ``changelog`` template.
434 ``changelog`` template.
435 """
435 """
436 return changelog(web, req, tmpl, shortlog=True)
436 return changelog(web, req, tmpl, shortlog=True)
437
437
438 @webcommand('changeset')
438 @webcommand('changeset')
439 def changeset(web, req, tmpl):
439 def changeset(web, req, tmpl):
440 """
440 """
441 /changeset[/{revision}]
441 /changeset[/{revision}]
442 -----------------------
442 -----------------------
443
443
444 Show information about a single changeset.
444 Show information about a single changeset.
445
445
446 A URL path argument is the changeset identifier to show. See ``hg help
446 A URL path argument is the changeset identifier to show. See ``hg help
447 revisions`` for possible values. If not defined, the ``tip`` changeset
447 revisions`` for possible values. If not defined, the ``tip`` changeset
448 will be shown.
448 will be shown.
449
449
450 The ``changeset`` template is rendered. Contents of the ``changesettag``,
450 The ``changeset`` template is rendered. Contents of the ``changesettag``,
451 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
451 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
452 templates related to diffs may all be used to produce the output.
452 templates related to diffs may all be used to produce the output.
453 """
453 """
454 ctx = webutil.changectx(web.repo, req)
454 ctx = webutil.changectx(web.repo, req)
455
455
456 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
456 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
457
457
458 rev = webcommand('rev')(changeset)
458 rev = webcommand('rev')(changeset)
459
459
460 def decodepath(path):
460 def decodepath(path):
461 """Hook for mapping a path in the repository to a path in the
461 """Hook for mapping a path in the repository to a path in the
462 working copy.
462 working copy.
463
463
464 Extensions (e.g., largefiles) can override this to remap files in
464 Extensions (e.g., largefiles) can override this to remap files in
465 the virtual file system presented by the manifest command below."""
465 the virtual file system presented by the manifest command below."""
466 return path
466 return path
467
467
468 @webcommand('manifest')
468 @webcommand('manifest')
469 def manifest(web, req, tmpl):
469 def manifest(web, req, tmpl):
470 """
470 """
471 /manifest[/{revision}[/{path}]]
471 /manifest[/{revision}[/{path}]]
472 -------------------------------
472 -------------------------------
473
473
474 Show information about a directory.
474 Show information about a directory.
475
475
476 If the URL path arguments are omitted, information about the root
476 If the URL path arguments are omitted, information about the root
477 directory for the ``tip`` changeset will be shown.
477 directory for the ``tip`` changeset will be shown.
478
478
479 Because this handler can only show information for directories, it
479 Because this handler can only show information for directories, it
480 is recommended to use the ``file`` handler instead, as it can handle both
480 is recommended to use the ``file`` handler instead, as it can handle both
481 directories and files.
481 directories and files.
482
482
483 The ``manifest`` template will be rendered for this handler.
483 The ``manifest`` template will be rendered for this handler.
484 """
484 """
485 if 'node' in req.form:
485 if 'node' in req.form:
486 ctx = webutil.changectx(web.repo, req)
486 ctx = webutil.changectx(web.repo, req)
487 symrev = webutil.symrevorshortnode(req, ctx)
487 symrev = webutil.symrevorshortnode(req, ctx)
488 else:
488 else:
489 ctx = web.repo['tip']
489 ctx = web.repo['tip']
490 symrev = 'tip'
490 symrev = 'tip'
491 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
491 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
492 mf = ctx.manifest()
492 mf = ctx.manifest()
493 node = ctx.node()
493 node = ctx.node()
494
494
495 files = {}
495 files = {}
496 dirs = {}
496 dirs = {}
497 parity = paritygen(web.stripecount)
497 parity = paritygen(web.stripecount)
498
498
499 if path and path[-1] != "/":
499 if path and path[-1] != "/":
500 path += "/"
500 path += "/"
501 l = len(path)
501 l = len(path)
502 abspath = "/" + path
502 abspath = "/" + path
503
503
504 for full, n in mf.iteritems():
504 for full, n in mf.iteritems():
505 # the virtual path (working copy path) used for the full
505 # the virtual path (working copy path) used for the full
506 # (repository) path
506 # (repository) path
507 f = decodepath(full)
507 f = decodepath(full)
508
508
509 if f[:l] != path:
509 if f[:l] != path:
510 continue
510 continue
511 remain = f[l:]
511 remain = f[l:]
512 elements = remain.split('/')
512 elements = remain.split('/')
513 if len(elements) == 1:
513 if len(elements) == 1:
514 files[remain] = full
514 files[remain] = full
515 else:
515 else:
516 h = dirs # need to retain ref to dirs (root)
516 h = dirs # need to retain ref to dirs (root)
517 for elem in elements[0:-1]:
517 for elem in elements[0:-1]:
518 if elem not in h:
518 if elem not in h:
519 h[elem] = {}
519 h[elem] = {}
520 h = h[elem]
520 h = h[elem]
521 if len(h) > 1:
521 if len(h) > 1:
522 break
522 break
523 h[None] = None # denotes files present
523 h[None] = None # denotes files present
524
524
525 if mf and not files and not dirs:
525 if mf and not files and not dirs:
526 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
526 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
527
527
528 def filelist(**map):
528 def filelist(**map):
529 for f in sorted(files):
529 for f in sorted(files):
530 full = files[f]
530 full = files[f]
531
531
532 fctx = ctx.filectx(full)
532 fctx = ctx.filectx(full)
533 yield {"file": full,
533 yield {"file": full,
534 "parity": next(parity),
534 "parity": next(parity),
535 "basename": f,
535 "basename": f,
536 "date": fctx.date(),
536 "date": fctx.date(),
537 "size": fctx.size(),
537 "size": fctx.size(),
538 "permissions": mf.flags(full)}
538 "permissions": mf.flags(full)}
539
539
540 def dirlist(**map):
540 def dirlist(**map):
541 for d in sorted(dirs):
541 for d in sorted(dirs):
542
542
543 emptydirs = []
543 emptydirs = []
544 h = dirs[d]
544 h = dirs[d]
545 while isinstance(h, dict) and len(h) == 1:
545 while isinstance(h, dict) and len(h) == 1:
546 k, v = h.items()[0]
546 k, v = h.items()[0]
547 if v:
547 if v:
548 emptydirs.append(k)
548 emptydirs.append(k)
549 h = v
549 h = v
550
550
551 path = "%s%s" % (abspath, d)
551 path = "%s%s" % (abspath, d)
552 yield {"parity": next(parity),
552 yield {"parity": next(parity),
553 "path": path,
553 "path": path,
554 "emptydirs": "/".join(emptydirs),
554 "emptydirs": "/".join(emptydirs),
555 "basename": d}
555 "basename": d}
556
556
557 return tmpl("manifest",
557 return tmpl("manifest",
558 symrev=symrev,
558 symrev=symrev,
559 path=abspath,
559 path=abspath,
560 up=webutil.up(abspath),
560 up=webutil.up(abspath),
561 upparity=next(parity),
561 upparity=next(parity),
562 fentries=filelist,
562 fentries=filelist,
563 dentries=dirlist,
563 dentries=dirlist,
564 archives=web.archivelist(hex(node)),
564 archives=web.archivelist(hex(node)),
565 **webutil.commonentry(web.repo, ctx))
565 **webutil.commonentry(web.repo, ctx))
566
566
567 @webcommand('tags')
567 @webcommand('tags')
568 def tags(web, req, tmpl):
568 def tags(web, req, tmpl):
569 """
569 """
570 /tags
570 /tags
571 -----
571 -----
572
572
573 Show information about tags.
573 Show information about tags.
574
574
575 No arguments are accepted.
575 No arguments are accepted.
576
576
577 The ``tags`` template is rendered.
577 The ``tags`` template is rendered.
578 """
578 """
579 i = list(reversed(web.repo.tagslist()))
579 i = list(reversed(web.repo.tagslist()))
580 parity = paritygen(web.stripecount)
580 parity = paritygen(web.stripecount)
581
581
582 def entries(notip, latestonly, **map):
582 def entries(notip, latestonly, **map):
583 t = i
583 t = i
584 if notip:
584 if notip:
585 t = [(k, n) for k, n in i if k != "tip"]
585 t = [(k, n) for k, n in i if k != "tip"]
586 if latestonly:
586 if latestonly:
587 t = t[:1]
587 t = t[:1]
588 for k, n in t:
588 for k, n in t:
589 yield {"parity": next(parity),
589 yield {"parity": next(parity),
590 "tag": k,
590 "tag": k,
591 "date": web.repo[n].date(),
591 "date": web.repo[n].date(),
592 "node": hex(n)}
592 "node": hex(n)}
593
593
594 return tmpl("tags",
594 return tmpl("tags",
595 node=hex(web.repo.changelog.tip()),
595 node=hex(web.repo.changelog.tip()),
596 entries=lambda **x: entries(False, False, **x),
596 entries=lambda **x: entries(False, False, **x),
597 entriesnotip=lambda **x: entries(True, False, **x),
597 entriesnotip=lambda **x: entries(True, False, **x),
598 latestentry=lambda **x: entries(True, True, **x))
598 latestentry=lambda **x: entries(True, True, **x))
599
599
600 @webcommand('bookmarks')
600 @webcommand('bookmarks')
601 def bookmarks(web, req, tmpl):
601 def bookmarks(web, req, tmpl):
602 """
602 """
603 /bookmarks
603 /bookmarks
604 ----------
604 ----------
605
605
606 Show information about bookmarks.
606 Show information about bookmarks.
607
607
608 No arguments are accepted.
608 No arguments are accepted.
609
609
610 The ``bookmarks`` template is rendered.
610 The ``bookmarks`` template is rendered.
611 """
611 """
612 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
612 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
613 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
613 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
614 i = sorted(i, key=sortkey, reverse=True)
614 i = sorted(i, key=sortkey, reverse=True)
615 parity = paritygen(web.stripecount)
615 parity = paritygen(web.stripecount)
616
616
617 def entries(latestonly, **map):
617 def entries(latestonly, **map):
618 t = i
618 t = i
619 if latestonly:
619 if latestonly:
620 t = i[:1]
620 t = i[:1]
621 for k, n in t:
621 for k, n in t:
622 yield {"parity": next(parity),
622 yield {"parity": next(parity),
623 "bookmark": k,
623 "bookmark": k,
624 "date": web.repo[n].date(),
624 "date": web.repo[n].date(),
625 "node": hex(n)}
625 "node": hex(n)}
626
626
627 if i:
627 if i:
628 latestrev = i[0][1]
628 latestrev = i[0][1]
629 else:
629 else:
630 latestrev = -1
630 latestrev = -1
631
631
632 return tmpl("bookmarks",
632 return tmpl("bookmarks",
633 node=hex(web.repo.changelog.tip()),
633 node=hex(web.repo.changelog.tip()),
634 lastchange=[{"date": web.repo[latestrev].date()}],
634 lastchange=[{"date": web.repo[latestrev].date()}],
635 entries=lambda **x: entries(latestonly=False, **x),
635 entries=lambda **x: entries(latestonly=False, **x),
636 latestentry=lambda **x: entries(latestonly=True, **x))
636 latestentry=lambda **x: entries(latestonly=True, **x))
637
637
638 @webcommand('branches')
638 @webcommand('branches')
639 def branches(web, req, tmpl):
639 def branches(web, req, tmpl):
640 """
640 """
641 /branches
641 /branches
642 ---------
642 ---------
643
643
644 Show information about branches.
644 Show information about branches.
645
645
646 All known branches are contained in the output, even closed branches.
646 All known branches are contained in the output, even closed branches.
647
647
648 No arguments are accepted.
648 No arguments are accepted.
649
649
650 The ``branches`` template is rendered.
650 The ``branches`` template is rendered.
651 """
651 """
652 entries = webutil.branchentries(web.repo, web.stripecount)
652 entries = webutil.branchentries(web.repo, web.stripecount)
653 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
653 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
654 return tmpl('branches', node=hex(web.repo.changelog.tip()),
654 return tmpl('branches', node=hex(web.repo.changelog.tip()),
655 entries=entries, latestentry=latestentry)
655 entries=entries, latestentry=latestentry)
656
656
657 @webcommand('summary')
657 @webcommand('summary')
658 def summary(web, req, tmpl):
658 def summary(web, req, tmpl):
659 """
659 """
660 /summary
660 /summary
661 --------
661 --------
662
662
663 Show a summary of repository state.
663 Show a summary of repository state.
664
664
665 Information about the latest changesets, bookmarks, tags, and branches
665 Information about the latest changesets, bookmarks, tags, and branches
666 is captured by this handler.
666 is captured by this handler.
667
667
668 The ``summary`` template is rendered.
668 The ``summary`` template is rendered.
669 """
669 """
670 i = reversed(web.repo.tagslist())
670 i = reversed(web.repo.tagslist())
671
671
672 def tagentries(**map):
672 def tagentries(**map):
673 parity = paritygen(web.stripecount)
673 parity = paritygen(web.stripecount)
674 count = 0
674 count = 0
675 for k, n in i:
675 for k, n in i:
676 if k == "tip": # skip tip
676 if k == "tip": # skip tip
677 continue
677 continue
678
678
679 count += 1
679 count += 1
680 if count > 10: # limit to 10 tags
680 if count > 10: # limit to 10 tags
681 break
681 break
682
682
683 yield tmpl("tagentry",
683 yield tmpl("tagentry",
684 parity=next(parity),
684 parity=next(parity),
685 tag=k,
685 tag=k,
686 node=hex(n),
686 node=hex(n),
687 date=web.repo[n].date())
687 date=web.repo[n].date())
688
688
689 def bookmarks(**map):
689 def bookmarks(**map):
690 parity = paritygen(web.stripecount)
690 parity = paritygen(web.stripecount)
691 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
691 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
692 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
692 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
693 marks = sorted(marks, key=sortkey, reverse=True)
693 marks = sorted(marks, key=sortkey, reverse=True)
694 for k, n in marks[:10]: # limit to 10 bookmarks
694 for k, n in marks[:10]: # limit to 10 bookmarks
695 yield {'parity': next(parity),
695 yield {'parity': next(parity),
696 'bookmark': k,
696 'bookmark': k,
697 'date': web.repo[n].date(),
697 'date': web.repo[n].date(),
698 'node': hex(n)}
698 'node': hex(n)}
699
699
700 def changelist(**map):
700 def changelist(**map):
701 parity = paritygen(web.stripecount, offset=start - end)
701 parity = paritygen(web.stripecount, offset=start - end)
702 l = [] # build a list in forward order for efficiency
702 l = [] # build a list in forward order for efficiency
703 revs = []
703 revs = []
704 if start < end:
704 if start < end:
705 revs = web.repo.changelog.revs(start, end - 1)
705 revs = web.repo.changelog.revs(start, end - 1)
706 for i in revs:
706 for i in revs:
707 ctx = web.repo[i]
707 ctx = web.repo[i]
708
708
709 l.append(tmpl(
709 l.append(tmpl(
710 'shortlogentry',
710 'shortlogentry',
711 parity=next(parity),
711 parity=next(parity),
712 **webutil.commonentry(web.repo, ctx)))
712 **webutil.commonentry(web.repo, ctx)))
713
713
714 for entry in reversed(l):
714 for entry in reversed(l):
715 yield entry
715 yield entry
716
716
717 tip = web.repo['tip']
717 tip = web.repo['tip']
718 count = len(web.repo)
718 count = len(web.repo)
719 start = max(0, count - web.maxchanges)
719 start = max(0, count - web.maxchanges)
720 end = min(count, start + web.maxchanges)
720 end = min(count, start + web.maxchanges)
721
721
722 return tmpl("summary",
722 return tmpl("summary",
723 desc=web.config("web", "description", "unknown"),
723 desc=web.config("web", "description", "unknown"),
724 owner=get_contact(web.config) or "unknown",
724 owner=get_contact(web.config) or "unknown",
725 lastchange=tip.date(),
725 lastchange=tip.date(),
726 tags=tagentries,
726 tags=tagentries,
727 bookmarks=bookmarks,
727 bookmarks=bookmarks,
728 branches=webutil.branchentries(web.repo, web.stripecount, 10),
728 branches=webutil.branchentries(web.repo, web.stripecount, 10),
729 shortlog=changelist,
729 shortlog=changelist,
730 node=tip.hex(),
730 node=tip.hex(),
731 symrev='tip',
731 symrev='tip',
732 archives=web.archivelist("tip"),
732 archives=web.archivelist("tip"),
733 labels=web.configlist('web', 'labels'))
733 labels=web.configlist('web', 'labels'))
734
734
735 @webcommand('filediff')
735 @webcommand('filediff')
736 def filediff(web, req, tmpl):
736 def filediff(web, req, tmpl):
737 """
737 """
738 /diff/{revision}/{path}
738 /diff/{revision}/{path}
739 -----------------------
739 -----------------------
740
740
741 Show how a file changed in a particular commit.
741 Show how a file changed in a particular commit.
742
742
743 The ``filediff`` template is rendered.
743 The ``filediff`` template is rendered.
744
744
745 This handler is registered under both the ``/diff`` and ``/filediff``
745 This handler is registered under both the ``/diff`` and ``/filediff``
746 paths. ``/diff`` is used in modern code.
746 paths. ``/diff`` is used in modern code.
747 """
747 """
748 fctx, ctx = None, None
748 fctx, ctx = None, None
749 try:
749 try:
750 fctx = webutil.filectx(web.repo, req)
750 fctx = webutil.filectx(web.repo, req)
751 except LookupError:
751 except LookupError:
752 ctx = webutil.changectx(web.repo, req)
752 ctx = webutil.changectx(web.repo, req)
753 path = webutil.cleanpath(web.repo, req.form['file'][0])
753 path = webutil.cleanpath(web.repo, req.form['file'][0])
754 if path not in ctx.files():
754 if path not in ctx.files():
755 raise
755 raise
756
756
757 if fctx is not None:
757 if fctx is not None:
758 path = fctx.path()
758 path = fctx.path()
759 ctx = fctx.changectx()
759 ctx = fctx.changectx()
760 basectx = ctx.p1()
760 basectx = ctx.p1()
761
761
762 style = web.config('web', 'style', 'paper')
762 style = web.config('web', 'style', 'paper')
763 if 'style' in req.form:
763 if 'style' in req.form:
764 style = req.form['style'][0]
764 style = req.form['style'][0]
765
765
766 diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style)
766 diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style)
767 if fctx is not None:
767 if fctx is not None:
768 rename = webutil.renamelink(fctx)
768 rename = webutil.renamelink(fctx)
769 ctx = fctx
769 ctx = fctx
770 else:
770 else:
771 rename = []
771 rename = []
772 ctx = ctx
772 ctx = ctx
773 return tmpl("filediff",
773 return tmpl("filediff",
774 file=path,
774 file=path,
775 symrev=webutil.symrevorshortnode(req, ctx),
775 symrev=webutil.symrevorshortnode(req, ctx),
776 rename=rename,
776 rename=rename,
777 diff=diffs,
777 diff=diffs,
778 **webutil.commonentry(web.repo, ctx))
778 **webutil.commonentry(web.repo, ctx))
779
779
780 diff = webcommand('diff')(filediff)
780 diff = webcommand('diff')(filediff)
781
781
782 @webcommand('comparison')
782 @webcommand('comparison')
783 def comparison(web, req, tmpl):
783 def comparison(web, req, tmpl):
784 """
784 """
785 /comparison/{revision}/{path}
785 /comparison/{revision}/{path}
786 -----------------------------
786 -----------------------------
787
787
788 Show a comparison between the old and new versions of a file from changes
788 Show a comparison between the old and new versions of a file from changes
789 made on a particular revision.
789 made on a particular revision.
790
790
791 This is similar to the ``diff`` handler. However, this form features
791 This is similar to the ``diff`` handler. However, this form features
792 a split or side-by-side diff rather than a unified diff.
792 a split or side-by-side diff rather than a unified diff.
793
793
794 The ``context`` query string argument can be used to control the lines of
794 The ``context`` query string argument can be used to control the lines of
795 context in the diff.
795 context in the diff.
796
796
797 The ``filecomparison`` template is rendered.
797 The ``filecomparison`` template is rendered.
798 """
798 """
799 ctx = webutil.changectx(web.repo, req)
799 ctx = webutil.changectx(web.repo, req)
800 if 'file' not in req.form:
800 if 'file' not in req.form:
801 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
801 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
802 path = webutil.cleanpath(web.repo, req.form['file'][0])
802 path = webutil.cleanpath(web.repo, req.form['file'][0])
803
803
804 parsecontext = lambda v: v == 'full' and -1 or int(v)
804 parsecontext = lambda v: v == 'full' and -1 or int(v)
805 if 'context' in req.form:
805 if 'context' in req.form:
806 context = parsecontext(req.form['context'][0])
806 context = parsecontext(req.form['context'][0])
807 else:
807 else:
808 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
808 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
809
809
810 def filelines(f):
810 def filelines(f):
811 if f.isbinary():
811 if f.isbinary():
812 mt = mimetypes.guess_type(f.path())[0]
812 mt = mimetypes.guess_type(f.path())[0]
813 if not mt:
813 if not mt:
814 mt = 'application/octet-stream'
814 mt = 'application/octet-stream'
815 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
815 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
816 return f.data().splitlines()
816 return f.data().splitlines()
817
817
818 fctx = None
818 fctx = None
819 parent = ctx.p1()
819 parent = ctx.p1()
820 leftrev = parent.rev()
820 leftrev = parent.rev()
821 leftnode = parent.node()
821 leftnode = parent.node()
822 rightrev = ctx.rev()
822 rightrev = ctx.rev()
823 rightnode = ctx.node()
823 rightnode = ctx.node()
824 if path in ctx:
824 if path in ctx:
825 fctx = ctx[path]
825 fctx = ctx[path]
826 rightlines = filelines(fctx)
826 rightlines = filelines(fctx)
827 if path not in parent:
827 if path not in parent:
828 leftlines = ()
828 leftlines = ()
829 else:
829 else:
830 pfctx = parent[path]
830 pfctx = parent[path]
831 leftlines = filelines(pfctx)
831 leftlines = filelines(pfctx)
832 else:
832 else:
833 rightlines = ()
833 rightlines = ()
834 pfctx = ctx.parents()[0][path]
834 pfctx = ctx.parents()[0][path]
835 leftlines = filelines(pfctx)
835 leftlines = filelines(pfctx)
836
836
837 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
837 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
838 if fctx is not None:
838 if fctx is not None:
839 rename = webutil.renamelink(fctx)
839 rename = webutil.renamelink(fctx)
840 ctx = fctx
840 ctx = fctx
841 else:
841 else:
842 rename = []
842 rename = []
843 ctx = ctx
843 ctx = ctx
844 return tmpl('filecomparison',
844 return tmpl('filecomparison',
845 file=path,
845 file=path,
846 symrev=webutil.symrevorshortnode(req, ctx),
846 symrev=webutil.symrevorshortnode(req, ctx),
847 rename=rename,
847 rename=rename,
848 leftrev=leftrev,
848 leftrev=leftrev,
849 leftnode=hex(leftnode),
849 leftnode=hex(leftnode),
850 rightrev=rightrev,
850 rightrev=rightrev,
851 rightnode=hex(rightnode),
851 rightnode=hex(rightnode),
852 comparison=comparison,
852 comparison=comparison,
853 **webutil.commonentry(web.repo, ctx))
853 **webutil.commonentry(web.repo, ctx))
854
854
855 @webcommand('annotate')
855 @webcommand('annotate')
856 def annotate(web, req, tmpl):
856 def annotate(web, req, tmpl):
857 """
857 """
858 /annotate/{revision}/{path}
858 /annotate/{revision}/{path}
859 ---------------------------
859 ---------------------------
860
860
861 Show changeset information for each line in a file.
861 Show changeset information for each line in a file.
862
862
863 The ``fileannotate`` template is rendered.
863 The ``fileannotate`` template is rendered.
864 """
864 """
865 fctx = webutil.filectx(web.repo, req)
865 fctx = webutil.filectx(web.repo, req)
866 f = fctx.path()
866 f = fctx.path()
867 parity = paritygen(web.stripecount)
867 parity = paritygen(web.stripecount)
868 ishead = fctx.filerev() in fctx.filelog().headrevs()
868
869
869 # parents() is called once per line and several lines likely belong to
870 # parents() is called once per line and several lines likely belong to
870 # same revision. So it is worth caching.
871 # same revision. So it is worth caching.
871 # TODO there are still redundant operations within basefilectx.parents()
872 # TODO there are still redundant operations within basefilectx.parents()
872 # and from the fctx.annotate() call itself that could be cached.
873 # and from the fctx.annotate() call itself that could be cached.
873 parentscache = {}
874 parentscache = {}
874 def parents(f):
875 def parents(f):
875 rev = f.rev()
876 rev = f.rev()
876 if rev not in parentscache:
877 if rev not in parentscache:
877 parentscache[rev] = []
878 parentscache[rev] = []
878 for p in f.parents():
879 for p in f.parents():
879 entry = {
880 entry = {
880 'node': p.hex(),
881 'node': p.hex(),
881 'rev': p.rev(),
882 'rev': p.rev(),
882 }
883 }
883 parentscache[rev].append(entry)
884 parentscache[rev].append(entry)
884
885
885 for p in parentscache[rev]:
886 for p in parentscache[rev]:
886 yield p
887 yield p
887
888
888 def annotate(**map):
889 def annotate(**map):
889 if fctx.isbinary():
890 if fctx.isbinary():
890 mt = (mimetypes.guess_type(fctx.path())[0]
891 mt = (mimetypes.guess_type(fctx.path())[0]
891 or 'application/octet-stream')
892 or 'application/octet-stream')
892 lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
893 lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
893 else:
894 else:
894 lines = webutil.annotate(fctx, web.repo.ui)
895 lines = webutil.annotate(fctx, web.repo.ui)
895
896
896 previousrev = None
897 previousrev = None
897 blockparitygen = paritygen(1)
898 blockparitygen = paritygen(1)
898 for lineno, ((f, targetline), l) in enumerate(lines):
899 for lineno, ((f, targetline), l) in enumerate(lines):
899 rev = f.rev()
900 rev = f.rev()
900 if rev != previousrev:
901 if rev != previousrev:
901 blockhead = True
902 blockhead = True
902 blockparity = next(blockparitygen)
903 blockparity = next(blockparitygen)
903 else:
904 else:
904 blockhead = None
905 blockhead = None
905 previousrev = rev
906 previousrev = rev
906 yield {"parity": next(parity),
907 yield {"parity": next(parity),
907 "node": f.hex(),
908 "node": f.hex(),
908 "rev": rev,
909 "rev": rev,
909 "author": f.user(),
910 "author": f.user(),
910 "parents": parents(f),
911 "parents": parents(f),
911 "desc": f.description(),
912 "desc": f.description(),
912 "extra": f.extra(),
913 "extra": f.extra(),
913 "file": f.path(),
914 "file": f.path(),
914 "blockhead": blockhead,
915 "blockhead": blockhead,
915 "blockparity": blockparity,
916 "blockparity": blockparity,
916 "targetline": targetline,
917 "targetline": targetline,
917 "line": l,
918 "line": l,
918 "lineno": lineno + 1,
919 "lineno": lineno + 1,
919 "lineid": "l%d" % (lineno + 1),
920 "lineid": "l%d" % (lineno + 1),
920 "linenumber": "% 6d" % (lineno + 1),
921 "linenumber": "% 6d" % (lineno + 1),
921 "revdate": f.date()}
922 "revdate": f.date()}
922
923
923 return tmpl("fileannotate",
924 return tmpl("fileannotate",
924 file=f,
925 file=f,
925 annotate=annotate,
926 annotate=annotate,
926 path=webutil.up(f),
927 path=webutil.up(f),
927 symrev=webutil.symrevorshortnode(req, fctx),
928 symrev=webutil.symrevorshortnode(req, fctx),
928 rename=webutil.renamelink(fctx),
929 rename=webutil.renamelink(fctx),
929 permissions=fctx.manifest().flags(f),
930 permissions=fctx.manifest().flags(f),
931 ishead=int(ishead),
930 **webutil.commonentry(web.repo, fctx))
932 **webutil.commonentry(web.repo, fctx))
931
933
932 @webcommand('filelog')
934 @webcommand('filelog')
933 def filelog(web, req, tmpl):
935 def filelog(web, req, tmpl):
934 """
936 """
935 /filelog/{revision}/{path}
937 /filelog/{revision}/{path}
936 --------------------------
938 --------------------------
937
939
938 Show information about the history of a file in the repository.
940 Show information about the history of a file in the repository.
939
941
940 The ``revcount`` query string argument can be defined to control the
942 The ``revcount`` query string argument can be defined to control the
941 maximum number of entries to show.
943 maximum number of entries to show.
942
944
943 The ``filelog`` template will be rendered.
945 The ``filelog`` template will be rendered.
944 """
946 """
945
947
946 try:
948 try:
947 fctx = webutil.filectx(web.repo, req)
949 fctx = webutil.filectx(web.repo, req)
948 f = fctx.path()
950 f = fctx.path()
949 fl = fctx.filelog()
951 fl = fctx.filelog()
950 except error.LookupError:
952 except error.LookupError:
951 f = webutil.cleanpath(web.repo, req.form['file'][0])
953 f = webutil.cleanpath(web.repo, req.form['file'][0])
952 fl = web.repo.file(f)
954 fl = web.repo.file(f)
953 numrevs = len(fl)
955 numrevs = len(fl)
954 if not numrevs: # file doesn't exist at all
956 if not numrevs: # file doesn't exist at all
955 raise
957 raise
956 rev = webutil.changectx(web.repo, req).rev()
958 rev = webutil.changectx(web.repo, req).rev()
957 first = fl.linkrev(0)
959 first = fl.linkrev(0)
958 if rev < first: # current rev is from before file existed
960 if rev < first: # current rev is from before file existed
959 raise
961 raise
960 frev = numrevs - 1
962 frev = numrevs - 1
961 while fl.linkrev(frev) > rev:
963 while fl.linkrev(frev) > rev:
962 frev -= 1
964 frev -= 1
963 fctx = web.repo.filectx(f, fl.linkrev(frev))
965 fctx = web.repo.filectx(f, fl.linkrev(frev))
964
966
965 revcount = web.maxshortchanges
967 revcount = web.maxshortchanges
966 if 'revcount' in req.form:
968 if 'revcount' in req.form:
967 try:
969 try:
968 revcount = int(req.form.get('revcount', [revcount])[0])
970 revcount = int(req.form.get('revcount', [revcount])[0])
969 revcount = max(revcount, 1)
971 revcount = max(revcount, 1)
970 tmpl.defaults['sessionvars']['revcount'] = revcount
972 tmpl.defaults['sessionvars']['revcount'] = revcount
971 except ValueError:
973 except ValueError:
972 pass
974 pass
973
975
974 lrange = webutil.linerange(req)
976 lrange = webutil.linerange(req)
975
977
976 lessvars = copy.copy(tmpl.defaults['sessionvars'])
978 lessvars = copy.copy(tmpl.defaults['sessionvars'])
977 lessvars['revcount'] = max(revcount / 2, 1)
979 lessvars['revcount'] = max(revcount / 2, 1)
978 morevars = copy.copy(tmpl.defaults['sessionvars'])
980 morevars = copy.copy(tmpl.defaults['sessionvars'])
979 morevars['revcount'] = revcount * 2
981 morevars['revcount'] = revcount * 2
980
982
981 patch = 'patch' in req.form
983 patch = 'patch' in req.form
982 if patch:
984 if patch:
983 lessvars['patch'] = morevars['patch'] = req.form['patch'][0]
985 lessvars['patch'] = morevars['patch'] = req.form['patch'][0]
984 descend = 'descend' in req.form
986 descend = 'descend' in req.form
985 if descend:
987 if descend:
986 lessvars['descend'] = morevars['descend'] = req.form['descend'][0]
988 lessvars['descend'] = morevars['descend'] = req.form['descend'][0]
987
989
988 count = fctx.filerev() + 1
990 count = fctx.filerev() + 1
989 start = max(0, count - revcount) # first rev on this page
991 start = max(0, count - revcount) # first rev on this page
990 end = min(count, start + revcount) # last rev on this page
992 end = min(count, start + revcount) # last rev on this page
991 parity = paritygen(web.stripecount, offset=start - end)
993 parity = paritygen(web.stripecount, offset=start - end)
992
994
993 repo = web.repo
995 repo = web.repo
994 revs = fctx.filelog().revs(start, end - 1)
996 revs = fctx.filelog().revs(start, end - 1)
995 entries = []
997 entries = []
996
998
997 diffstyle = web.config('web', 'style', 'paper')
999 diffstyle = web.config('web', 'style', 'paper')
998 if 'style' in req.form:
1000 if 'style' in req.form:
999 diffstyle = req.form['style'][0]
1001 diffstyle = req.form['style'][0]
1000
1002
1001 def diff(fctx, linerange=None):
1003 def diff(fctx, linerange=None):
1002 ctx = fctx.changectx()
1004 ctx = fctx.changectx()
1003 basectx = ctx.p1()
1005 basectx = ctx.p1()
1004 path = fctx.path()
1006 path = fctx.path()
1005 return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle,
1007 return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle,
1006 linerange=linerange,
1008 linerange=linerange,
1007 lineidprefix='%s-' % ctx.hex()[:12])
1009 lineidprefix='%s-' % ctx.hex()[:12])
1008
1010
1009 linerange = None
1011 linerange = None
1010 if lrange is not None:
1012 if lrange is not None:
1011 linerange = webutil.formatlinerange(*lrange)
1013 linerange = webutil.formatlinerange(*lrange)
1012 # deactivate numeric nav links when linerange is specified as this
1014 # deactivate numeric nav links when linerange is specified as this
1013 # would required a dedicated "revnav" class
1015 # would required a dedicated "revnav" class
1014 nav = None
1016 nav = None
1015 if descend:
1017 if descend:
1016 it = dagop.blockdescendants(fctx, *lrange)
1018 it = dagop.blockdescendants(fctx, *lrange)
1017 else:
1019 else:
1018 it = dagop.blockancestors(fctx, *lrange)
1020 it = dagop.blockancestors(fctx, *lrange)
1019 for i, (c, lr) in enumerate(it, 1):
1021 for i, (c, lr) in enumerate(it, 1):
1020 diffs = None
1022 diffs = None
1021 if patch:
1023 if patch:
1022 diffs = diff(c, linerange=lr)
1024 diffs = diff(c, linerange=lr)
1023 # follow renames accross filtered (not in range) revisions
1025 # follow renames accross filtered (not in range) revisions
1024 path = c.path()
1026 path = c.path()
1025 entries.append(dict(
1027 entries.append(dict(
1026 parity=next(parity),
1028 parity=next(parity),
1027 filerev=c.rev(),
1029 filerev=c.rev(),
1028 file=path,
1030 file=path,
1029 diff=diffs,
1031 diff=diffs,
1030 linerange=webutil.formatlinerange(*lr),
1032 linerange=webutil.formatlinerange(*lr),
1031 **webutil.commonentry(repo, c)))
1033 **webutil.commonentry(repo, c)))
1032 if i == revcount:
1034 if i == revcount:
1033 break
1035 break
1034 lessvars['linerange'] = webutil.formatlinerange(*lrange)
1036 lessvars['linerange'] = webutil.formatlinerange(*lrange)
1035 morevars['linerange'] = lessvars['linerange']
1037 morevars['linerange'] = lessvars['linerange']
1036 else:
1038 else:
1037 for i in revs:
1039 for i in revs:
1038 iterfctx = fctx.filectx(i)
1040 iterfctx = fctx.filectx(i)
1039 diffs = None
1041 diffs = None
1040 if patch:
1042 if patch:
1041 diffs = diff(iterfctx)
1043 diffs = diff(iterfctx)
1042 entries.append(dict(
1044 entries.append(dict(
1043 parity=next(parity),
1045 parity=next(parity),
1044 filerev=i,
1046 filerev=i,
1045 file=f,
1047 file=f,
1046 diff=diffs,
1048 diff=diffs,
1047 rename=webutil.renamelink(iterfctx),
1049 rename=webutil.renamelink(iterfctx),
1048 **webutil.commonentry(repo, iterfctx)))
1050 **webutil.commonentry(repo, iterfctx)))
1049 entries.reverse()
1051 entries.reverse()
1050 revnav = webutil.filerevnav(web.repo, fctx.path())
1052 revnav = webutil.filerevnav(web.repo, fctx.path())
1051 nav = revnav.gen(end - 1, revcount, count)
1053 nav = revnav.gen(end - 1, revcount, count)
1052
1054
1053 latestentry = entries[:1]
1055 latestentry = entries[:1]
1054
1056
1055 return tmpl("filelog",
1057 return tmpl("filelog",
1056 file=f,
1058 file=f,
1057 nav=nav,
1059 nav=nav,
1058 symrev=webutil.symrevorshortnode(req, fctx),
1060 symrev=webutil.symrevorshortnode(req, fctx),
1059 entries=entries,
1061 entries=entries,
1060 descend=descend,
1062 descend=descend,
1061 patch=patch,
1063 patch=patch,
1062 latestentry=latestentry,
1064 latestentry=latestentry,
1063 linerange=linerange,
1065 linerange=linerange,
1064 revcount=revcount,
1066 revcount=revcount,
1065 morevars=morevars,
1067 morevars=morevars,
1066 lessvars=lessvars,
1068 lessvars=lessvars,
1067 **webutil.commonentry(web.repo, fctx))
1069 **webutil.commonentry(web.repo, fctx))
1068
1070
1069 @webcommand('archive')
1071 @webcommand('archive')
1070 def archive(web, req, tmpl):
1072 def archive(web, req, tmpl):
1071 """
1073 """
1072 /archive/{revision}.{format}[/{path}]
1074 /archive/{revision}.{format}[/{path}]
1073 -------------------------------------
1075 -------------------------------------
1074
1076
1075 Obtain an archive of repository content.
1077 Obtain an archive of repository content.
1076
1078
1077 The content and type of the archive is defined by a URL path parameter.
1079 The content and type of the archive is defined by a URL path parameter.
1078 ``format`` is the file extension of the archive type to be generated. e.g.
1080 ``format`` is the file extension of the archive type to be generated. e.g.
1079 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1081 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1080 server configuration.
1082 server configuration.
1081
1083
1082 The optional ``path`` URL parameter controls content to include in the
1084 The optional ``path`` URL parameter controls content to include in the
1083 archive. If omitted, every file in the specified revision is present in the
1085 archive. If omitted, every file in the specified revision is present in the
1084 archive. If included, only the specified file or contents of the specified
1086 archive. If included, only the specified file or contents of the specified
1085 directory will be included in the archive.
1087 directory will be included in the archive.
1086
1088
1087 No template is used for this handler. Raw, binary content is generated.
1089 No template is used for this handler. Raw, binary content is generated.
1088 """
1090 """
1089
1091
1090 type_ = req.form.get('type', [None])[0]
1092 type_ = req.form.get('type', [None])[0]
1091 allowed = web.configlist("web", "allow_archive")
1093 allowed = web.configlist("web", "allow_archive")
1092 key = req.form['node'][0]
1094 key = req.form['node'][0]
1093
1095
1094 if type_ not in web.archivespecs:
1096 if type_ not in web.archivespecs:
1095 msg = 'Unsupported archive type: %s' % type_
1097 msg = 'Unsupported archive type: %s' % type_
1096 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1098 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1097
1099
1098 if not ((type_ in allowed or
1100 if not ((type_ in allowed or
1099 web.configbool("web", "allow" + type_, False))):
1101 web.configbool("web", "allow" + type_, False))):
1100 msg = 'Archive type not allowed: %s' % type_
1102 msg = 'Archive type not allowed: %s' % type_
1101 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1103 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1102
1104
1103 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1105 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1104 cnode = web.repo.lookup(key)
1106 cnode = web.repo.lookup(key)
1105 arch_version = key
1107 arch_version = key
1106 if cnode == key or key == 'tip':
1108 if cnode == key or key == 'tip':
1107 arch_version = short(cnode)
1109 arch_version = short(cnode)
1108 name = "%s-%s" % (reponame, arch_version)
1110 name = "%s-%s" % (reponame, arch_version)
1109
1111
1110 ctx = webutil.changectx(web.repo, req)
1112 ctx = webutil.changectx(web.repo, req)
1111 pats = []
1113 pats = []
1112 matchfn = scmutil.match(ctx, [])
1114 matchfn = scmutil.match(ctx, [])
1113 file = req.form.get('file', None)
1115 file = req.form.get('file', None)
1114 if file:
1116 if file:
1115 pats = ['path:' + file[0]]
1117 pats = ['path:' + file[0]]
1116 matchfn = scmutil.match(ctx, pats, default='path')
1118 matchfn = scmutil.match(ctx, pats, default='path')
1117 if pats:
1119 if pats:
1118 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1120 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1119 if not files:
1121 if not files:
1120 raise ErrorResponse(HTTP_NOT_FOUND,
1122 raise ErrorResponse(HTTP_NOT_FOUND,
1121 'file(s) not found: %s' % file[0])
1123 'file(s) not found: %s' % file[0])
1122
1124
1123 mimetype, artype, extension, encoding = web.archivespecs[type_]
1125 mimetype, artype, extension, encoding = web.archivespecs[type_]
1124 headers = [
1126 headers = [
1125 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1127 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1126 ]
1128 ]
1127 if encoding:
1129 if encoding:
1128 headers.append(('Content-Encoding', encoding))
1130 headers.append(('Content-Encoding', encoding))
1129 req.headers.extend(headers)
1131 req.headers.extend(headers)
1130 req.respond(HTTP_OK, mimetype)
1132 req.respond(HTTP_OK, mimetype)
1131
1133
1132 archival.archive(web.repo, req, cnode, artype, prefix=name,
1134 archival.archive(web.repo, req, cnode, artype, prefix=name,
1133 matchfn=matchfn,
1135 matchfn=matchfn,
1134 subrepos=web.configbool("web", "archivesubrepos"))
1136 subrepos=web.configbool("web", "archivesubrepos"))
1135 return []
1137 return []
1136
1138
1137
1139
1138 @webcommand('static')
1140 @webcommand('static')
1139 def static(web, req, tmpl):
1141 def static(web, req, tmpl):
1140 fname = req.form['file'][0]
1142 fname = req.form['file'][0]
1141 # a repo owner may set web.static in .hg/hgrc to get any file
1143 # a repo owner may set web.static in .hg/hgrc to get any file
1142 # readable by the user running the CGI script
1144 # readable by the user running the CGI script
1143 static = web.config("web", "static", None, untrusted=False)
1145 static = web.config("web", "static", None, untrusted=False)
1144 if not static:
1146 if not static:
1145 tp = web.templatepath or templater.templatepaths()
1147 tp = web.templatepath or templater.templatepaths()
1146 if isinstance(tp, str):
1148 if isinstance(tp, str):
1147 tp = [tp]
1149 tp = [tp]
1148 static = [os.path.join(p, 'static') for p in tp]
1150 static = [os.path.join(p, 'static') for p in tp]
1149 staticfile(static, fname, req)
1151 staticfile(static, fname, req)
1150 return []
1152 return []
1151
1153
1152 @webcommand('graph')
1154 @webcommand('graph')
1153 def graph(web, req, tmpl):
1155 def graph(web, req, tmpl):
1154 """
1156 """
1155 /graph[/{revision}]
1157 /graph[/{revision}]
1156 -------------------
1158 -------------------
1157
1159
1158 Show information about the graphical topology of the repository.
1160 Show information about the graphical topology of the repository.
1159
1161
1160 Information rendered by this handler can be used to create visual
1162 Information rendered by this handler can be used to create visual
1161 representations of repository topology.
1163 representations of repository topology.
1162
1164
1163 The ``revision`` URL parameter controls the starting changeset.
1165 The ``revision`` URL parameter controls the starting changeset.
1164
1166
1165 The ``revcount`` query string argument can define the number of changesets
1167 The ``revcount`` query string argument can define the number of changesets
1166 to show information for.
1168 to show information for.
1167
1169
1168 This handler will render the ``graph`` template.
1170 This handler will render the ``graph`` template.
1169 """
1171 """
1170
1172
1171 if 'node' in req.form:
1173 if 'node' in req.form:
1172 ctx = webutil.changectx(web.repo, req)
1174 ctx = webutil.changectx(web.repo, req)
1173 symrev = webutil.symrevorshortnode(req, ctx)
1175 symrev = webutil.symrevorshortnode(req, ctx)
1174 else:
1176 else:
1175 ctx = web.repo['tip']
1177 ctx = web.repo['tip']
1176 symrev = 'tip'
1178 symrev = 'tip'
1177 rev = ctx.rev()
1179 rev = ctx.rev()
1178
1180
1179 bg_height = 39
1181 bg_height = 39
1180 revcount = web.maxshortchanges
1182 revcount = web.maxshortchanges
1181 if 'revcount' in req.form:
1183 if 'revcount' in req.form:
1182 try:
1184 try:
1183 revcount = int(req.form.get('revcount', [revcount])[0])
1185 revcount = int(req.form.get('revcount', [revcount])[0])
1184 revcount = max(revcount, 1)
1186 revcount = max(revcount, 1)
1185 tmpl.defaults['sessionvars']['revcount'] = revcount
1187 tmpl.defaults['sessionvars']['revcount'] = revcount
1186 except ValueError:
1188 except ValueError:
1187 pass
1189 pass
1188
1190
1189 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1191 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1190 lessvars['revcount'] = max(revcount / 2, 1)
1192 lessvars['revcount'] = max(revcount / 2, 1)
1191 morevars = copy.copy(tmpl.defaults['sessionvars'])
1193 morevars = copy.copy(tmpl.defaults['sessionvars'])
1192 morevars['revcount'] = revcount * 2
1194 morevars['revcount'] = revcount * 2
1193
1195
1194 count = len(web.repo)
1196 count = len(web.repo)
1195 pos = rev
1197 pos = rev
1196
1198
1197 uprev = min(max(0, count - 1), rev + revcount)
1199 uprev = min(max(0, count - 1), rev + revcount)
1198 downrev = max(0, rev - revcount)
1200 downrev = max(0, rev - revcount)
1199 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1201 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1200
1202
1201 tree = []
1203 tree = []
1202 if pos != -1:
1204 if pos != -1:
1203 allrevs = web.repo.changelog.revs(pos, 0)
1205 allrevs = web.repo.changelog.revs(pos, 0)
1204 revs = []
1206 revs = []
1205 for i in allrevs:
1207 for i in allrevs:
1206 revs.append(i)
1208 revs.append(i)
1207 if len(revs) >= revcount:
1209 if len(revs) >= revcount:
1208 break
1210 break
1209
1211
1210 # We have to feed a baseset to dagwalker as it is expecting smartset
1212 # We have to feed a baseset to dagwalker as it is expecting smartset
1211 # object. This does not have a big impact on hgweb performance itself
1213 # object. This does not have a big impact on hgweb performance itself
1212 # since hgweb graphing code is not itself lazy yet.
1214 # since hgweb graphing code is not itself lazy yet.
1213 dag = graphmod.dagwalker(web.repo, smartset.baseset(revs))
1215 dag = graphmod.dagwalker(web.repo, smartset.baseset(revs))
1214 # As we said one line above... not lazy.
1216 # As we said one line above... not lazy.
1215 tree = list(graphmod.colored(dag, web.repo))
1217 tree = list(graphmod.colored(dag, web.repo))
1216
1218
1217 def getcolumns(tree):
1219 def getcolumns(tree):
1218 cols = 0
1220 cols = 0
1219 for (id, type, ctx, vtx, edges) in tree:
1221 for (id, type, ctx, vtx, edges) in tree:
1220 if type != graphmod.CHANGESET:
1222 if type != graphmod.CHANGESET:
1221 continue
1223 continue
1222 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1224 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1223 max([edge[1] for edge in edges] or [0]))
1225 max([edge[1] for edge in edges] or [0]))
1224 return cols
1226 return cols
1225
1227
1226 def graphdata(usetuples, encodestr):
1228 def graphdata(usetuples, encodestr):
1227 data = []
1229 data = []
1228
1230
1229 row = 0
1231 row = 0
1230 for (id, type, ctx, vtx, edges) in tree:
1232 for (id, type, ctx, vtx, edges) in tree:
1231 if type != graphmod.CHANGESET:
1233 if type != graphmod.CHANGESET:
1232 continue
1234 continue
1233 node = str(ctx)
1235 node = str(ctx)
1234 age = encodestr(templatefilters.age(ctx.date()))
1236 age = encodestr(templatefilters.age(ctx.date()))
1235 desc = templatefilters.firstline(encodestr(ctx.description()))
1237 desc = templatefilters.firstline(encodestr(ctx.description()))
1236 desc = cgi.escape(templatefilters.nonempty(desc))
1238 desc = cgi.escape(templatefilters.nonempty(desc))
1237 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1239 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1238 branch = cgi.escape(encodestr(ctx.branch()))
1240 branch = cgi.escape(encodestr(ctx.branch()))
1239 try:
1241 try:
1240 branchnode = web.repo.branchtip(branch)
1242 branchnode = web.repo.branchtip(branch)
1241 except error.RepoLookupError:
1243 except error.RepoLookupError:
1242 branchnode = None
1244 branchnode = None
1243 branch = branch, branchnode == ctx.node()
1245 branch = branch, branchnode == ctx.node()
1244
1246
1245 if usetuples:
1247 if usetuples:
1246 data.append((node, vtx, edges, desc, user, age, branch,
1248 data.append((node, vtx, edges, desc, user, age, branch,
1247 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1249 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1248 [cgi.escape(encodestr(x))
1250 [cgi.escape(encodestr(x))
1249 for x in ctx.bookmarks()]))
1251 for x in ctx.bookmarks()]))
1250 else:
1252 else:
1251 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1253 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1252 'color': (edge[2] - 1) % 6 + 1,
1254 'color': (edge[2] - 1) % 6 + 1,
1253 'width': edge[3], 'bcolor': edge[4]}
1255 'width': edge[3], 'bcolor': edge[4]}
1254 for edge in edges]
1256 for edge in edges]
1255
1257
1256 data.append(
1258 data.append(
1257 {'node': node,
1259 {'node': node,
1258 'col': vtx[0],
1260 'col': vtx[0],
1259 'color': (vtx[1] - 1) % 6 + 1,
1261 'color': (vtx[1] - 1) % 6 + 1,
1260 'edges': edgedata,
1262 'edges': edgedata,
1261 'row': row,
1263 'row': row,
1262 'nextrow': row + 1,
1264 'nextrow': row + 1,
1263 'desc': desc,
1265 'desc': desc,
1264 'user': user,
1266 'user': user,
1265 'age': age,
1267 'age': age,
1266 'bookmarks': webutil.nodebookmarksdict(
1268 'bookmarks': webutil.nodebookmarksdict(
1267 web.repo, ctx.node()),
1269 web.repo, ctx.node()),
1268 'branches': webutil.nodebranchdict(web.repo, ctx),
1270 'branches': webutil.nodebranchdict(web.repo, ctx),
1269 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1271 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1270 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1272 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1271
1273
1272 row += 1
1274 row += 1
1273
1275
1274 return data
1276 return data
1275
1277
1276 cols = getcolumns(tree)
1278 cols = getcolumns(tree)
1277 rows = len(tree)
1279 rows = len(tree)
1278 canvasheight = (rows + 1) * bg_height - 27
1280 canvasheight = (rows + 1) * bg_height - 27
1279
1281
1280 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1282 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1281 uprev=uprev,
1283 uprev=uprev,
1282 lessvars=lessvars, morevars=morevars, downrev=downrev,
1284 lessvars=lessvars, morevars=morevars, downrev=downrev,
1283 cols=cols, rows=rows,
1285 cols=cols, rows=rows,
1284 canvaswidth=(cols + 1) * bg_height,
1286 canvaswidth=(cols + 1) * bg_height,
1285 truecanvasheight=rows * bg_height,
1287 truecanvasheight=rows * bg_height,
1286 canvasheight=canvasheight, bg_height=bg_height,
1288 canvasheight=canvasheight, bg_height=bg_height,
1287 # {jsdata} will be passed to |json, so it must be in utf-8
1289 # {jsdata} will be passed to |json, so it must be in utf-8
1288 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1290 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1289 nodes=lambda **x: graphdata(False, str),
1291 nodes=lambda **x: graphdata(False, str),
1290 node=ctx.hex(), changenav=changenav)
1292 node=ctx.hex(), changenav=changenav)
1291
1293
1292 def _getdoc(e):
1294 def _getdoc(e):
1293 doc = e[0].__doc__
1295 doc = e[0].__doc__
1294 if doc:
1296 if doc:
1295 doc = _(doc).partition('\n')[0]
1297 doc = _(doc).partition('\n')[0]
1296 else:
1298 else:
1297 doc = _('(no help text available)')
1299 doc = _('(no help text available)')
1298 return doc
1300 return doc
1299
1301
1300 @webcommand('help')
1302 @webcommand('help')
1301 def help(web, req, tmpl):
1303 def help(web, req, tmpl):
1302 """
1304 """
1303 /help[/{topic}]
1305 /help[/{topic}]
1304 ---------------
1306 ---------------
1305
1307
1306 Render help documentation.
1308 Render help documentation.
1307
1309
1308 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1310 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1309 is defined, that help topic will be rendered. If not, an index of
1311 is defined, that help topic will be rendered. If not, an index of
1310 available help topics will be rendered.
1312 available help topics will be rendered.
1311
1313
1312 The ``help`` template will be rendered when requesting help for a topic.
1314 The ``help`` template will be rendered when requesting help for a topic.
1313 ``helptopics`` will be rendered for the index of help topics.
1315 ``helptopics`` will be rendered for the index of help topics.
1314 """
1316 """
1315 from .. import commands, help as helpmod # avoid cycle
1317 from .. import commands, help as helpmod # avoid cycle
1316
1318
1317 topicname = req.form.get('node', [None])[0]
1319 topicname = req.form.get('node', [None])[0]
1318 if not topicname:
1320 if not topicname:
1319 def topics(**map):
1321 def topics(**map):
1320 for entries, summary, _doc in helpmod.helptable:
1322 for entries, summary, _doc in helpmod.helptable:
1321 yield {'topic': entries[0], 'summary': summary}
1323 yield {'topic': entries[0], 'summary': summary}
1322
1324
1323 early, other = [], []
1325 early, other = [], []
1324 primary = lambda s: s.partition('|')[0]
1326 primary = lambda s: s.partition('|')[0]
1325 for c, e in commands.table.iteritems():
1327 for c, e in commands.table.iteritems():
1326 doc = _getdoc(e)
1328 doc = _getdoc(e)
1327 if 'DEPRECATED' in doc or c.startswith('debug'):
1329 if 'DEPRECATED' in doc or c.startswith('debug'):
1328 continue
1330 continue
1329 cmd = primary(c)
1331 cmd = primary(c)
1330 if cmd.startswith('^'):
1332 if cmd.startswith('^'):
1331 early.append((cmd[1:], doc))
1333 early.append((cmd[1:], doc))
1332 else:
1334 else:
1333 other.append((cmd, doc))
1335 other.append((cmd, doc))
1334
1336
1335 early.sort()
1337 early.sort()
1336 other.sort()
1338 other.sort()
1337
1339
1338 def earlycommands(**map):
1340 def earlycommands(**map):
1339 for c, doc in early:
1341 for c, doc in early:
1340 yield {'topic': c, 'summary': doc}
1342 yield {'topic': c, 'summary': doc}
1341
1343
1342 def othercommands(**map):
1344 def othercommands(**map):
1343 for c, doc in other:
1345 for c, doc in other:
1344 yield {'topic': c, 'summary': doc}
1346 yield {'topic': c, 'summary': doc}
1345
1347
1346 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1348 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1347 othercommands=othercommands, title='Index')
1349 othercommands=othercommands, title='Index')
1348
1350
1349 # Render an index of sub-topics.
1351 # Render an index of sub-topics.
1350 if topicname in helpmod.subtopics:
1352 if topicname in helpmod.subtopics:
1351 topics = []
1353 topics = []
1352 for entries, summary, _doc in helpmod.subtopics[topicname]:
1354 for entries, summary, _doc in helpmod.subtopics[topicname]:
1353 topics.append({
1355 topics.append({
1354 'topic': '%s.%s' % (topicname, entries[0]),
1356 'topic': '%s.%s' % (topicname, entries[0]),
1355 'basename': entries[0],
1357 'basename': entries[0],
1356 'summary': summary,
1358 'summary': summary,
1357 })
1359 })
1358
1360
1359 return tmpl('helptopics', topics=topics, title=topicname,
1361 return tmpl('helptopics', topics=topics, title=topicname,
1360 subindex=True)
1362 subindex=True)
1361
1363
1362 u = webutil.wsgiui.load()
1364 u = webutil.wsgiui.load()
1363 u.verbose = True
1365 u.verbose = True
1364
1366
1365 # Render a page from a sub-topic.
1367 # Render a page from a sub-topic.
1366 if '.' in topicname:
1368 if '.' in topicname:
1367 # TODO implement support for rendering sections, like
1369 # TODO implement support for rendering sections, like
1368 # `hg help` works.
1370 # `hg help` works.
1369 topic, subtopic = topicname.split('.', 1)
1371 topic, subtopic = topicname.split('.', 1)
1370 if topic not in helpmod.subtopics:
1372 if topic not in helpmod.subtopics:
1371 raise ErrorResponse(HTTP_NOT_FOUND)
1373 raise ErrorResponse(HTTP_NOT_FOUND)
1372 else:
1374 else:
1373 topic = topicname
1375 topic = topicname
1374 subtopic = None
1376 subtopic = None
1375
1377
1376 try:
1378 try:
1377 doc = helpmod.help_(u, commands, topic, subtopic=subtopic)
1379 doc = helpmod.help_(u, commands, topic, subtopic=subtopic)
1378 except error.UnknownCommand:
1380 except error.UnknownCommand:
1379 raise ErrorResponse(HTTP_NOT_FOUND)
1381 raise ErrorResponse(HTTP_NOT_FOUND)
1380 return tmpl('help', topic=topicname, doc=doc)
1382 return tmpl('help', topic=topicname, doc=doc)
1381
1383
1382 # tell hggettext to extract docstrings from these functions:
1384 # tell hggettext to extract docstrings from these functions:
1383 i18nfunctions = commands.values()
1385 i18nfunctions = commands.values()
@@ -1,75 +1,80
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
2 <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
3 <link rel="alternate" type="application/atom+xml"
3 <link rel="alternate" type="application/atom+xml"
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
5 <link rel="alternate" type="application/rss+xml"
5 <link rel="alternate" type="application/rss+xml"
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
7 </head>
7 </head>
8 <body>
8 <body>
9
9
10 <div class="page_header">
10 <div class="page_header">
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate
13 </div>
13 </div>
14
14
15 <div class="page_nav">
15 <div class="page_nav">
16 <div>
16 <div>
17 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
17 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
18 <a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a> |
18 <a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a> |
19 <a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a> |
19 <a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a> |
20 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
20 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
21 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
21 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
22 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
22 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
23 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
23 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
24 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">files</a> |
24 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">files</a> |
25 <a href="{url|urlescape}rev/{symrev}{sessionvars%urlparameter}">changeset</a> |
25 <a href="{url|urlescape}rev/{symrev}{sessionvars%urlparameter}">changeset</a> |
26 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
26 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
27 <a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
27 <a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
28 <a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
28 <a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
29 annotate |
29 annotate |
30 <a href="{url|urlescape}diff/{symrev}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
30 <a href="{url|urlescape}diff/{symrev}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
31 <a href="{url|urlescape}comparison/{symrev}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
31 <a href="{url|urlescape}comparison/{symrev}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
32 <a href="{url|urlescape}raw-file/{symrev}/{file|urlescape}">raw</a> |
32 <a href="{url|urlescape}raw-file/{symrev}/{file|urlescape}">raw</a> |
33 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
33 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
34 </div>
34 </div>
35 {searchform}
35 {searchform}
36 </div>
36 </div>
37
37
38 <div class="title">{file|escape}</div>
38 <div class="title">{file|escape}</div>
39
39
40 <div class="title_text">
40 <div class="title_text">
41 <table cellspacing="0">
41 <table cellspacing="0">
42 <tr>
42 <tr>
43 <td>author</td>
43 <td>author</td>
44 <td>{author|obfuscate}</td>
44 <td>{author|obfuscate}</td>
45 </tr>
45 </tr>
46 <tr>
46 <tr>
47 <td></td>
47 <td></td>
48 <td class="date age">{date|rfc822date}</td>
48 <td class="date age">{date|rfc822date}</td>
49 </tr>
49 </tr>
50 {branch%filerevbranch}
50 {branch%filerevbranch}
51 <tr>
51 <tr>
52 <td>changeset {rev}</td>
52 <td>changeset {rev}</td>
53 <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
53 <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
54 </tr>
54 </tr>
55 {parent%fileannotateparent}
55 {parent%fileannotateparent}
56 {child%fileannotatechild}
56 {child%fileannotatechild}
57 <tr>
57 <tr>
58 <td>permissions</td>
58 <td>permissions</td>
59 <td style="font-family:monospace">{permissions|permissions}</td>
59 <td style="font-family:monospace">{permissions|permissions}</td>
60 </tr>
60 </tr>
61 </table>
61 </table>
62 </div>
62 </div>
63
63
64 <div class="page_path description">
64 <div class="page_path description">
65 {desc|strip|escape|websub|addbreaks|nonempty}
65 {desc|strip|escape|websub|addbreaks|nonempty}
66 </div>
66 </div>
67 <div class="page_body">
67 <div class="page_body">
68 <table>
68 <table>
69 <tbody>
69 <tbody class="sourcelines"
70 data-logurl="{url|urlescape}log/{symrev}/{file|urlescape}"
71 data-selectabletag="TR"
72 data-ishead="{ishead}">
70 {annotate%annotateline}
73 {annotate%annotateline}
71 </tbody>
74 </tbody>
72 </table>
75 </table>
73 </div>
76 </div>
74
77
78 <script type="text/javascript" src="{staticurl|urlescape}followlines.js"></script>
79
75 {footer}
80 {footer}
@@ -1,84 +1,89
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape} annotate</title>
2 <title>{repo|escape}: {file|escape} annotate</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="container">
6 <div class="container">
7 <div class="menu">
7 <div class="menu">
8 <div class="logo">
8 <div class="logo">
9 <a href="{logourl}">
9 <a href="{logourl}">
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
11 </div>
11 </div>
12 <ul>
12 <ul>
13 <li><a href="{url|urlescape}shortlog/{symrev}{sessionvars%urlparameter}">log</a></li>
13 <li><a href="{url|urlescape}shortlog/{symrev}{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url|urlescape}graph/{symrev}{sessionvars%urlparameter}">graph</a></li>
14 <li><a href="{url|urlescape}graph/{symrev}{sessionvars%urlparameter}">graph</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
18 </ul>
18 </ul>
19
19
20 <ul>
20 <ul>
21 <li><a href="{url|urlescape}rev/{symrev}{sessionvars%urlparameter}">changeset</a></li>
21 <li><a href="{url|urlescape}rev/{symrev}{sessionvars%urlparameter}">changeset</a></li>
22 <li><a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
22 <li><a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
23 </ul>
23 </ul>
24 <ul>
24 <ul>
25 <li><a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
25 <li><a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
26 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
26 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
27 <li><a href="{url|urlescape}diff/{symrev}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
27 <li><a href="{url|urlescape}diff/{symrev}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
28 <li><a href="{url|urlescape}comparison/{symrev}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
28 <li><a href="{url|urlescape}comparison/{symrev}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
29 <li class="active">annotate</li>
29 <li class="active">annotate</li>
30 <li><a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
30 <li><a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
31 <li><a href="{url|urlescape}raw-file/{symrev}/{file|urlescape}">raw</a></li>
31 <li><a href="{url|urlescape}raw-file/{symrev}/{file|urlescape}">raw</a></li>
32 </ul>
32 </ul>
33 <ul>
33 <ul>
34 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
34 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
35 </ul>
35 </ul>
36 </div>
36 </div>
37
37
38 <div class="main">
38 <div class="main">
39 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
39 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
40 <h3>
40 <h3>
41 annotate {file|escape} @ {rev}:<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
41 annotate {file|escape} @ {rev}:<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
42 {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}
42 {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}
43 </h3>
43 </h3>
44
44
45 {searchform}
45 {searchform}
46
46
47 <div class="description">{desc|strip|escape|websub|nonempty}</div>
47 <div class="description">{desc|strip|escape|websub|nonempty}</div>
48
48
49 <table id="changesetEntry">
49 <table id="changesetEntry">
50 <tr>
50 <tr>
51 <th class="author">author</th>
51 <th class="author">author</th>
52 <td class="author">{author|obfuscate}</td>
52 <td class="author">{author|obfuscate}</td>
53 </tr>
53 </tr>
54 <tr>
54 <tr>
55 <th class="date">date</th>
55 <th class="date">date</th>
56 <td class="date age">{date|rfc822date}</td>
56 <td class="date age">{date|rfc822date}</td>
57 </tr>
57 </tr>
58 <tr>
58 <tr>
59 <th class="author">parents</th>
59 <th class="author">parents</th>
60 <td class="author">{parent%filerevparent}</td>
60 <td class="author">{parent%filerevparent}</td>
61 </tr>
61 </tr>
62 <tr>
62 <tr>
63 <th class="author">children</th>
63 <th class="author">children</th>
64 <td class="author">{child%filerevchild}</td>
64 <td class="author">{child%filerevchild}</td>
65 </tr>
65 </tr>
66 </table>
66 </table>
67
67
68 <div class="overflow">
68 <div class="overflow">
69 <table class="bigtable">
69 <table class="bigtable">
70 <thead>
70 <thead>
71 <tr>
71 <tr>
72 <th class="annotate">rev</th>
72 <th class="annotate">rev</th>
73 <th class="line">&nbsp;&nbsp;line source</th>
73 <th class="line">&nbsp;&nbsp;line source</th>
74 </tr>
74 </tr>
75 </thead>
75 </thead>
76 <tbody class="stripes2">
76 <tbody class="stripes2 sourcelines"
77 data-logurl="{url|urlescape}log/{symrev}/{file|urlescape}"
78 data-selectabletag="TR"
79 data-ishead="{ishead}">
77 {annotate%annotateline}
80 {annotate%annotateline}
78 </tbody>
81 </tbody>
79 </table>
82 </table>
80 </div>
83 </div>
81 </div>
84 </div>
82 </div>
85 </div>
83
86
87 <script type="text/javascript" src="{staticurl|urlescape}followlines.js"></script>
88
84 {footer}
89 {footer}
@@ -1,344 +1,346
1 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
1 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
2 a { color:#0000cc; }
2 a { color:#0000cc; }
3 a:hover, a:visited, a:active { color:#880000; }
3 a:hover, a:visited, a:active { color:#880000; }
4 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
4 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
5 div.page_header a:visited { color:#0000cc; }
5 div.page_header a:visited { color:#0000cc; }
6 div.page_header a:hover { color:#880000; }
6 div.page_header a:hover { color:#880000; }
7 div.page_nav {
7 div.page_nav {
8 padding:8px;
8 padding:8px;
9 display: flex;
9 display: flex;
10 justify-content: space-between;
10 justify-content: space-between;
11 align-items: center;
11 align-items: center;
12 }
12 }
13 div.page_nav a:visited { color:#0000cc; }
13 div.page_nav a:visited { color:#0000cc; }
14 div.extra_nav {
14 div.extra_nav {
15 padding: 8px;
15 padding: 8px;
16 }
16 }
17 div.extra_nav a:visited {
17 div.extra_nav a:visited {
18 color: #0000cc;
18 color: #0000cc;
19 }
19 }
20 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
20 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
21 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
21 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
22 div.page_footer_text { float:left; color:#555555; font-style:italic; }
22 div.page_footer_text { float:left; color:#555555; font-style:italic; }
23 div.page_body { padding:8px; }
23 div.page_body { padding:8px; }
24 div.title, a.title {
24 div.title, a.title {
25 display:block; padding:6px 8px;
25 display:block; padding:6px 8px;
26 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
26 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
27 }
27 }
28 a.title:hover { background-color: #d9d8d1; }
28 a.title:hover { background-color: #d9d8d1; }
29 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
29 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
30 div.log_body { padding:8px 8px 8px 150px; }
30 div.log_body { padding:8px 8px 8px 150px; }
31 .age { white-space:nowrap; }
31 .age { white-space:nowrap; }
32 span.age { position:relative; float:left; width:142px; font-style:italic; }
32 span.age { position:relative; float:left; width:142px; font-style:italic; }
33 div.log_link {
33 div.log_link {
34 padding:0px 8px;
34 padding:0px 8px;
35 font-size:10px; font-family:sans-serif; font-style:normal;
35 font-size:10px; font-family:sans-serif; font-style:normal;
36 position:relative; float:left; width:136px;
36 position:relative; float:left; width:136px;
37 }
37 }
38 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
38 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
39 a.list { text-decoration:none; color:#000000; }
39 a.list { text-decoration:none; color:#000000; }
40 a.list:hover { text-decoration:underline; color:#880000; }
40 a.list:hover { text-decoration:underline; color:#880000; }
41 table { padding:8px 4px; }
41 table { padding:8px 4px; }
42 th { padding:2px 5px; font-size:12px; text-align:left; }
42 th { padding:2px 5px; font-size:12px; text-align:left; }
43 .parity0 { background-color:#ffffff; }
43 .parity0 { background-color:#ffffff; }
44 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
44 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
45 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
45 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
46 pre.sourcelines.stripes > :nth-child(4n+2):hover,
46 pre.sourcelines.stripes > :nth-child(4n+2):hover,
47 pre.sourcelines.stripes > :nth-child(4n+4):hover,
47 pre.sourcelines.stripes > :nth-child(4n+4):hover,
48 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
48 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
49 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
49 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
50 td { padding:2px 5px; font-size:12px; vertical-align:top; }
50 td { padding:2px 5px; font-size:12px; vertical-align:top; }
51 td.closed { background-color: #99f; }
51 td.closed { background-color: #99f; }
52 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
52 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
53 td.indexlinks { white-space: nowrap; }
53 td.indexlinks { white-space: nowrap; }
54 td.indexlinks a {
54 td.indexlinks a {
55 padding: 2px 5px; line-height: 10px;
55 padding: 2px 5px; line-height: 10px;
56 border: 1px solid;
56 border: 1px solid;
57 color: #ffffff; background-color: #7777bb;
57 color: #ffffff; background-color: #7777bb;
58 border-color: #aaaadd #333366 #333366 #aaaadd;
58 border-color: #aaaadd #333366 #333366 #aaaadd;
59 font-weight: bold; text-align: center; text-decoration: none;
59 font-weight: bold; text-align: center; text-decoration: none;
60 font-size: 10px;
60 font-size: 10px;
61 }
61 }
62 td.indexlinks a:hover { background-color: #6666aa; }
62 td.indexlinks a:hover { background-color: #6666aa; }
63 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
63 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
64 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
64 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
65 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
65 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
66
66
67 .search {
67 .search {
68 margin-right: 8px;
68 margin-right: 8px;
69 }
69 }
70
70
71 div#hint {
71 div#hint {
72 position: absolute;
72 position: absolute;
73 display: none;
73 display: none;
74 width: 250px;
74 width: 250px;
75 padding: 5px;
75 padding: 5px;
76 background: #ffc;
76 background: #ffc;
77 border: 1px solid yellow;
77 border: 1px solid yellow;
78 border-radius: 5px;
78 border-radius: 5px;
79 }
79 }
80
80
81 #searchform:hover div#hint { display: block; }
81 #searchform:hover div#hint { display: block; }
82
82
83 tr.thisrev a { color:#999999; text-decoration: none; }
83 tr.thisrev a { color:#999999; text-decoration: none; }
84 tr.thisrev pre { color:#009900; }
84 tr.thisrev pre { color:#009900; }
85 td.annotate {
85 td.annotate {
86 white-space: nowrap;
86 white-space: nowrap;
87 }
87 }
88 div.annotate-info {
88 div.annotate-info {
89 display: none;
89 display: none;
90 position: absolute;
90 position: absolute;
91 background-color: #FFFFFF;
91 background-color: #FFFFFF;
92 border: 1px solid #d9d8d1;
92 border: 1px solid #d9d8d1;
93 text-align: left;
93 text-align: left;
94 color: #000000;
94 color: #000000;
95 padding: 5px;
95 padding: 5px;
96 }
96 }
97 div.annotate-info a { color: #0000FF; text-decoration: underline; }
97 div.annotate-info a { color: #0000FF; text-decoration: underline; }
98 td.annotate:hover div.annotate-info { display: inline; }
98 td.annotate:hover div.annotate-info { display: inline; }
99 .linenr { color:#999999; text-decoration:none }
99 .linenr { color:#999999; text-decoration:none }
100 div.rss_logo { float: right; white-space: nowrap; }
100 div.rss_logo { float: right; white-space: nowrap; }
101 div.rss_logo a {
101 div.rss_logo a {
102 padding:3px 6px; line-height:10px;
102 padding:3px 6px; line-height:10px;
103 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
103 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
104 color:#ffffff; background-color:#ff6600;
104 color:#ffffff; background-color:#ff6600;
105 font-weight:bold; font-family:sans-serif; font-size:10px;
105 font-weight:bold; font-family:sans-serif; font-size:10px;
106 text-align:center; text-decoration:none;
106 text-align:center; text-decoration:none;
107 }
107 }
108 div.rss_logo a:hover { background-color:#ee5500; }
108 div.rss_logo a:hover { background-color:#ee5500; }
109 pre { margin: 0; }
109 pre { margin: 0; }
110 span.logtags span {
110 span.logtags span {
111 padding: 0px 4px;
111 padding: 0px 4px;
112 font-size: 10px;
112 font-size: 10px;
113 font-weight: normal;
113 font-weight: normal;
114 border: 1px solid;
114 border: 1px solid;
115 background-color: #ffaaff;
115 background-color: #ffaaff;
116 border-color: #ffccff #ff00ee #ff00ee #ffccff;
116 border-color: #ffccff #ff00ee #ff00ee #ffccff;
117 }
117 }
118 span.logtags span.tagtag {
118 span.logtags span.tagtag {
119 background-color: #ffffaa;
119 background-color: #ffffaa;
120 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
120 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
121 }
121 }
122 span.logtags span.branchtag {
122 span.logtags span.branchtag {
123 background-color: #aaffaa;
123 background-color: #aaffaa;
124 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
124 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
125 }
125 }
126 span.logtags span.inbranchtag {
126 span.logtags span.inbranchtag {
127 background-color: #d5dde6;
127 background-color: #d5dde6;
128 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
128 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
129 }
129 }
130 span.logtags span.bookmarktag {
130 span.logtags span.bookmarktag {
131 background-color: #afdffa;
131 background-color: #afdffa;
132 border-color: #ccecff #46ace6 #46ace6 #ccecff;
132 border-color: #ccecff #46ace6 #46ace6 #ccecff;
133 }
133 }
134 span.difflineplus { color:#008800; }
134 span.difflineplus { color:#008800; }
135 span.difflineminus { color:#cc0000; }
135 span.difflineminus { color:#cc0000; }
136 span.difflineat { color:#990099; }
136 span.difflineat { color:#990099; }
137 div.diffblocks { counter-reset: lineno; }
137 div.diffblocks { counter-reset: lineno; }
138 div.diffblock { counter-increment: lineno; }
138 div.diffblock { counter-increment: lineno; }
139 pre.sourcelines { position: relative; counter-reset: lineno; }
139 pre.sourcelines { position: relative; counter-reset: lineno; }
140 pre.sourcelines > span {
140 pre.sourcelines > span {
141 display: inline-block;
141 display: inline-block;
142 box-sizing: border-box;
142 box-sizing: border-box;
143 width: 100%;
143 width: 100%;
144 padding: 0 0 0 5em;
144 padding: 0 0 0 5em;
145 counter-increment: lineno;
145 counter-increment: lineno;
146 vertical-align: top;
146 vertical-align: top;
147 }
147 }
148 pre.sourcelines > span:before {
148 pre.sourcelines > span:before {
149 -moz-user-select: -moz-none;
149 -moz-user-select: -moz-none;
150 -khtml-user-select: none;
150 -khtml-user-select: none;
151 -webkit-user-select: none;
151 -webkit-user-select: none;
152 -ms-user-select: none;
152 -ms-user-select: none;
153 user-select: none;
153 user-select: none;
154 display: inline-block;
154 display: inline-block;
155 margin-left: -5em;
155 margin-left: -5em;
156 width: 4em;
156 width: 4em;
157 color: #999;
157 color: #999;
158 text-align: right;
158 text-align: right;
159 content: counters(lineno,".");
159 content: counters(lineno,".");
160 float: left;
160 float: left;
161 }
161 }
162 pre.sourcelines > a {
162 pre.sourcelines > a {
163 display: inline-block;
163 display: inline-block;
164 position: absolute;
164 position: absolute;
165 left: 0px;
165 left: 0px;
166 width: 4em;
166 width: 4em;
167 height: 1em;
167 height: 1em;
168 }
168 }
169 tr:target td,
169 tr:target td,
170 pre.sourcelines > span:target,
170 pre.sourcelines > span:target,
171 pre.sourcelines.stripes > span:target {
171 pre.sourcelines.stripes > span:target {
172 background-color: #bfdfff;
172 background-color: #bfdfff;
173 }
173 }
174
174
175 .description {
175 .description {
176 font-family: monospace;
176 font-family: monospace;
177 }
177 }
178
178
179 /* Followlines */
179 /* Followlines */
180 div.page_body table tbody.sourcelines > tr.followlines-select:hover,
180 div.page_body pre.sourcelines > span.followlines-select:hover {
181 div.page_body pre.sourcelines > span.followlines-select:hover {
181 cursor: cell;
182 cursor: cell;
182 }
183 }
183
184
185 tbody.sourcelines > tr.followlines-selected,
184 pre.sourcelines > span.followlines-selected {
186 pre.sourcelines > span.followlines-selected {
185 background-color: #99C7E9 !important;
187 background-color: #99C7E9 !important;
186 }
188 }
187
189
188 div#followlines {
190 div#followlines {
189 background-color: #B7B7B7;
191 background-color: #B7B7B7;
190 border: 1px solid #CCC;
192 border: 1px solid #CCC;
191 border-radius: 5px;
193 border-radius: 5px;
192 padding: 4px;
194 padding: 4px;
193 position: fixed;
195 position: fixed;
194 }
196 }
195
197
196 div.followlines-cancel {
198 div.followlines-cancel {
197 text-align: right;
199 text-align: right;
198 }
200 }
199
201
200 div.followlines-cancel > button {
202 div.followlines-cancel > button {
201 line-height: 80%;
203 line-height: 80%;
202 padding: 0;
204 padding: 0;
203 border: 0;
205 border: 0;
204 border-radius: 2px;
206 border-radius: 2px;
205 background-color: inherit;
207 background-color: inherit;
206 font-weight: bold;
208 font-weight: bold;
207 }
209 }
208
210
209 div.followlines-cancel > button:hover {
211 div.followlines-cancel > button:hover {
210 color: #FFFFFF;
212 color: #FFFFFF;
211 background-color: #CF1F1F;
213 background-color: #CF1F1F;
212 }
214 }
213
215
214 div.followlines-link {
216 div.followlines-link {
215 margin: 2px;
217 margin: 2px;
216 margin-top: 4px;
218 margin-top: 4px;
217 font-family: sans-serif;
219 font-family: sans-serif;
218 }
220 }
219
221
220 div#followlines-tooltip {
222 div#followlines-tooltip {
221 display: none;
223 display: none;
222 position: fixed;
224 position: fixed;
223 background-color: #ffc;
225 background-color: #ffc;
224 border: 1px solid #999;
226 border: 1px solid #999;
225 padding: 2px;
227 padding: 2px;
226 }
228 }
227
229
228 .sourcelines:hover > div#followlines-tooltip {
230 .sourcelines:hover > div#followlines-tooltip {
229 display: inline;
231 display: inline;
230 }
232 }
231
233
232 .sourcelines:hover > div#followlines-tooltip.hidden {
234 .sourcelines:hover > div#followlines-tooltip.hidden {
233 display: none;
235 display: none;
234 }
236 }
235 /* Graph */
237 /* Graph */
236 div#wrapper {
238 div#wrapper {
237 position: relative;
239 position: relative;
238 margin: 0;
240 margin: 0;
239 padding: 0;
241 padding: 0;
240 margin-top: 3px;
242 margin-top: 3px;
241 }
243 }
242
244
243 canvas {
245 canvas {
244 position: absolute;
246 position: absolute;
245 z-index: 5;
247 z-index: 5;
246 top: -0.9em;
248 top: -0.9em;
247 margin: 0;
249 margin: 0;
248 }
250 }
249
251
250 ul#nodebgs {
252 ul#nodebgs {
251 list-style: none inside none;
253 list-style: none inside none;
252 padding: 0;
254 padding: 0;
253 margin: 0;
255 margin: 0;
254 top: -0.7em;
256 top: -0.7em;
255 }
257 }
256
258
257 ul#graphnodes li, ul#nodebgs li {
259 ul#graphnodes li, ul#nodebgs li {
258 height: 39px;
260 height: 39px;
259 }
261 }
260
262
261 ul#graphnodes {
263 ul#graphnodes {
262 position: absolute;
264 position: absolute;
263 z-index: 10;
265 z-index: 10;
264 top: -0.8em;
266 top: -0.8em;
265 list-style: none inside none;
267 list-style: none inside none;
266 padding: 0;
268 padding: 0;
267 }
269 }
268
270
269 ul#graphnodes li .info {
271 ul#graphnodes li .info {
270 display: block;
272 display: block;
271 font-size: 100%;
273 font-size: 100%;
272 position: relative;
274 position: relative;
273 top: -3px;
275 top: -3px;
274 font-style: italic;
276 font-style: italic;
275 }
277 }
276
278
277 /* Comparison */
279 /* Comparison */
278 .legend {
280 .legend {
279 padding: 1.5% 0 1.5% 0;
281 padding: 1.5% 0 1.5% 0;
280 }
282 }
281
283
282 .legendinfo {
284 .legendinfo {
283 border: 1px solid #d9d8d1;
285 border: 1px solid #d9d8d1;
284 font-size: 80%;
286 font-size: 80%;
285 text-align: center;
287 text-align: center;
286 padding: 0.5%;
288 padding: 0.5%;
287 }
289 }
288
290
289 .equal {
291 .equal {
290 background-color: #ffffff;
292 background-color: #ffffff;
291 }
293 }
292
294
293 .delete {
295 .delete {
294 background-color: #faa;
296 background-color: #faa;
295 color: #333;
297 color: #333;
296 }
298 }
297
299
298 .insert {
300 .insert {
299 background-color: #ffa;
301 background-color: #ffa;
300 }
302 }
301
303
302 .replace {
304 .replace {
303 background-color: #e8e8e8;
305 background-color: #e8e8e8;
304 }
306 }
305
307
306 .comparison {
308 .comparison {
307 overflow-x: auto;
309 overflow-x: auto;
308 }
310 }
309
311
310 .header th {
312 .header th {
311 text-align: center;
313 text-align: center;
312 }
314 }
313
315
314 .block {
316 .block {
315 border-top: 1px solid #d9d8d1;
317 border-top: 1px solid #d9d8d1;
316 }
318 }
317
319
318 .scroll-loading {
320 .scroll-loading {
319 -webkit-animation: change_color 1s linear 0s infinite alternate;
321 -webkit-animation: change_color 1s linear 0s infinite alternate;
320 -moz-animation: change_color 1s linear 0s infinite alternate;
322 -moz-animation: change_color 1s linear 0s infinite alternate;
321 -o-animation: change_color 1s linear 0s infinite alternate;
323 -o-animation: change_color 1s linear 0s infinite alternate;
322 animation: change_color 1s linear 0s infinite alternate;
324 animation: change_color 1s linear 0s infinite alternate;
323 }
325 }
324
326
325 @-webkit-keyframes change_color {
327 @-webkit-keyframes change_color {
326 from { background-color: #A0CEFF; } to { }
328 from { background-color: #A0CEFF; } to { }
327 }
329 }
328 @-moz-keyframes change_color {
330 @-moz-keyframes change_color {
329 from { background-color: #A0CEFF; } to { }
331 from { background-color: #A0CEFF; } to { }
330 }
332 }
331 @-o-keyframes change_color {
333 @-o-keyframes change_color {
332 from { background-color: #A0CEFF; } to { }
334 from { background-color: #A0CEFF; } to { }
333 }
335 }
334 @keyframes change_color {
336 @keyframes change_color {
335 from { background-color: #A0CEFF; } to { }
337 from { background-color: #A0CEFF; } to { }
336 }
338 }
337
339
338 .scroll-loading-error {
340 .scroll-loading-error {
339 background-color: #FFCCCC !important;
341 background-color: #FFCCCC !important;
340 }
342 }
341
343
342 #doc {
344 #doc {
343 margin: 0 8px;
345 margin: 0 8px;
344 }
346 }
@@ -1,487 +1,489
1 body {
1 body {
2 margin: 0;
2 margin: 0;
3 padding: 0;
3 padding: 0;
4 background: white;
4 background: white;
5 color: black;
5 color: black;
6 font-family: sans-serif;
6 font-family: sans-serif;
7 }
7 }
8
8
9 .container {
9 .container {
10 padding-left: 115px;
10 padding-left: 115px;
11 }
11 }
12
12
13 .main {
13 .main {
14 position: relative;
14 position: relative;
15 background: white;
15 background: white;
16 padding: 2em 2em 2em 0;
16 padding: 2em 2em 2em 0;
17 }
17 }
18
18
19 #.main {
19 #.main {
20 width: 98%;
20 width: 98%;
21 }
21 }
22
22
23 .overflow {
23 .overflow {
24 width: 100%;
24 width: 100%;
25 overflow: auto;
25 overflow: auto;
26 }
26 }
27
27
28 .menu {
28 .menu {
29 width: 90px;
29 width: 90px;
30 margin: 0;
30 margin: 0;
31 font-size: 80%;
31 font-size: 80%;
32 text-align: left;
32 text-align: left;
33 position: absolute;
33 position: absolute;
34 top: 20px;
34 top: 20px;
35 left: 20px;
35 left: 20px;
36 right: auto;
36 right: auto;
37 }
37 }
38
38
39 .menu ul {
39 .menu ul {
40 list-style: none;
40 list-style: none;
41 padding: 0;
41 padding: 0;
42 margin: 10px 0 0 0;
42 margin: 10px 0 0 0;
43 border-left: 2px solid #999;
43 border-left: 2px solid #999;
44 }
44 }
45
45
46 .menu li {
46 .menu li {
47 margin-bottom: 3px;
47 margin-bottom: 3px;
48 padding: 2px 4px;
48 padding: 2px 4px;
49 background: white;
49 background: white;
50 color: black;
50 color: black;
51 font-weight: normal;
51 font-weight: normal;
52 }
52 }
53
53
54 .menu li.active {
54 .menu li.active {
55 font-weight: bold;
55 font-weight: bold;
56 }
56 }
57
57
58 .menu img {
58 .menu img {
59 width: 75px;
59 width: 75px;
60 height: 90px;
60 height: 90px;
61 border: 0;
61 border: 0;
62 }
62 }
63
63
64 div.atom-logo {
64 div.atom-logo {
65 margin-top: 10px;
65 margin-top: 10px;
66 }
66 }
67
67
68 .atom-logo img{
68 .atom-logo img{
69 width: 14px;
69 width: 14px;
70 height: 14px;
70 height: 14px;
71 border: 0;
71 border: 0;
72 }
72 }
73
73
74 .menu a { color: black; display: block; }
74 .menu a { color: black; display: block; }
75
75
76 .search {
76 .search {
77 position: absolute;
77 position: absolute;
78 top: .7em;
78 top: .7em;
79 right: 2em;
79 right: 2em;
80 }
80 }
81
81
82 form.search div#hint {
82 form.search div#hint {
83 display: none;
83 display: none;
84 position: absolute;
84 position: absolute;
85 top: 40px;
85 top: 40px;
86 right: 0px;
86 right: 0px;
87 width: 190px;
87 width: 190px;
88 padding: 5px;
88 padding: 5px;
89 background: #ffc;
89 background: #ffc;
90 font-size: 70%;
90 font-size: 70%;
91 border: 1px solid yellow;
91 border: 1px solid yellow;
92 border-radius: 5px;
92 border-radius: 5px;
93 }
93 }
94
94
95 form.search:hover div#hint { display: block; }
95 form.search:hover div#hint { display: block; }
96
96
97 a { text-decoration:none; }
97 a { text-decoration:none; }
98 .age { white-space:nowrap; }
98 .age { white-space:nowrap; }
99 .date { white-space:nowrap; }
99 .date { white-space:nowrap; }
100 .indexlinks { white-space:nowrap; }
100 .indexlinks { white-space:nowrap; }
101 .parity0,
101 .parity0,
102 .stripes4 > :nth-child(4n+1),
102 .stripes4 > :nth-child(4n+1),
103 .stripes2 > :nth-child(2n+1) { background-color: #f0f0f0; }
103 .stripes2 > :nth-child(2n+1) { background-color: #f0f0f0; }
104 .parity1,
104 .parity1,
105 .stripes4 > :nth-child(4n+3),
105 .stripes4 > :nth-child(4n+3),
106 .stripes2 > :nth-child(2n+2) { background-color: white; }
106 .stripes2 > :nth-child(2n+2) { background-color: white; }
107 .plusline { color: green; }
107 .plusline { color: green; }
108 .minusline { color: #dc143c; } /* crimson */
108 .minusline { color: #dc143c; } /* crimson */
109 .atline { color: purple; }
109 .atline { color: purple; }
110
110
111 .diffstat-table {
111 .diffstat-table {
112 margin-top: 1em;
112 margin-top: 1em;
113 }
113 }
114 .diffstat-file {
114 .diffstat-file {
115 white-space: nowrap;
115 white-space: nowrap;
116 font-size: 90%;
116 font-size: 90%;
117 }
117 }
118 .diffstat-total {
118 .diffstat-total {
119 white-space: nowrap;
119 white-space: nowrap;
120 font-size: 90%;
120 font-size: 90%;
121 }
121 }
122 .diffstat-graph {
122 .diffstat-graph {
123 width: 100%;
123 width: 100%;
124 }
124 }
125 .diffstat-add {
125 .diffstat-add {
126 background-color: green;
126 background-color: green;
127 float: left;
127 float: left;
128 }
128 }
129 .diffstat-remove {
129 .diffstat-remove {
130 background-color: red;
130 background-color: red;
131 float: left;
131 float: left;
132 }
132 }
133
133
134 .navigate {
134 .navigate {
135 text-align: right;
135 text-align: right;
136 font-size: 60%;
136 font-size: 60%;
137 margin: 1em 0;
137 margin: 1em 0;
138 }
138 }
139
139
140 .tag {
140 .tag {
141 color: #999;
141 color: #999;
142 font-size: 70%;
142 font-size: 70%;
143 font-weight: normal;
143 font-weight: normal;
144 margin-left: .5em;
144 margin-left: .5em;
145 vertical-align: baseline;
145 vertical-align: baseline;
146 }
146 }
147
147
148 .branchhead {
148 .branchhead {
149 color: #000;
149 color: #000;
150 font-size: 80%;
150 font-size: 80%;
151 font-weight: normal;
151 font-weight: normal;
152 margin-left: .5em;
152 margin-left: .5em;
153 vertical-align: baseline;
153 vertical-align: baseline;
154 }
154 }
155
155
156 ul#graphnodes .branchhead {
156 ul#graphnodes .branchhead {
157 font-size: 75%;
157 font-size: 75%;
158 }
158 }
159
159
160 .branchname {
160 .branchname {
161 color: #000;
161 color: #000;
162 font-size: 60%;
162 font-size: 60%;
163 font-weight: normal;
163 font-weight: normal;
164 margin-left: .5em;
164 margin-left: .5em;
165 vertical-align: baseline;
165 vertical-align: baseline;
166 }
166 }
167
167
168 h3 .branchname {
168 h3 .branchname {
169 font-size: 80%;
169 font-size: 80%;
170 }
170 }
171
171
172 /* Common */
172 /* Common */
173 pre { margin: 0; }
173 pre { margin: 0; }
174
174
175 h2 { font-size: 120%; border-bottom: 1px solid #999; }
175 h2 { font-size: 120%; border-bottom: 1px solid #999; }
176 h2 a { color: #000; }
176 h2 a { color: #000; }
177 h3 {
177 h3 {
178 margin-top: +.7em;
178 margin-top: +.7em;
179 font-size: 100%;
179 font-size: 100%;
180 }
180 }
181
181
182 /* log and tags tables */
182 /* log and tags tables */
183 .bigtable {
183 .bigtable {
184 border-bottom: 1px solid #999;
184 border-bottom: 1px solid #999;
185 border-collapse: collapse;
185 border-collapse: collapse;
186 font-size: 90%;
186 font-size: 90%;
187 width: 100%;
187 width: 100%;
188 font-weight: normal;
188 font-weight: normal;
189 text-align: left;
189 text-align: left;
190 }
190 }
191
191
192 .bigtable td {
192 .bigtable td {
193 vertical-align: top;
193 vertical-align: top;
194 }
194 }
195
195
196 .bigtable th {
196 .bigtable th {
197 padding: 1px 4px;
197 padding: 1px 4px;
198 border-bottom: 1px solid #999;
198 border-bottom: 1px solid #999;
199 }
199 }
200 .bigtable tr { border: none; }
200 .bigtable tr { border: none; }
201 .bigtable .age { width: 7em; }
201 .bigtable .age { width: 7em; }
202 .bigtable .author { width: 15em; }
202 .bigtable .author { width: 15em; }
203 .bigtable .description { }
203 .bigtable .description { }
204 .bigtable .description .base { font-size: 70%; float: right; line-height: 1.66; }
204 .bigtable .description .base { font-size: 70%; float: right; line-height: 1.66; }
205 .bigtable .node { width: 5em; font-family: monospace;}
205 .bigtable .node { width: 5em; font-family: monospace;}
206 .bigtable .permissions { width: 8em; text-align: left;}
206 .bigtable .permissions { width: 8em; text-align: left;}
207 .bigtable .size { width: 5em; text-align: right; }
207 .bigtable .size { width: 5em; text-align: right; }
208 .bigtable .annotate { text-align: right; }
208 .bigtable .annotate { text-align: right; }
209 .bigtable td.annotate { font-size: smaller; }
209 .bigtable td.annotate { font-size: smaller; }
210 .bigtable td.source { font-size: inherit; }
210 .bigtable td.source { font-size: inherit; }
211 tr.thisrev a { color:#999999; text-decoration: none; }
211 tr.thisrev a { color:#999999; text-decoration: none; }
212 tr.thisrev td.source { color:#009900; }
212 tr.thisrev td.source { color:#009900; }
213 td.annotate {
213 td.annotate {
214 white-space: nowrap;
214 white-space: nowrap;
215 }
215 }
216 div.annotate-info {
216 div.annotate-info {
217 display: none;
217 display: none;
218 position: absolute;
218 position: absolute;
219 background-color: #FFFFFF;
219 background-color: #FFFFFF;
220 border: 1px solid #999;
220 border: 1px solid #999;
221 text-align: left;
221 text-align: left;
222 color: #000000;
222 color: #000000;
223 padding: 5px;
223 padding: 5px;
224 }
224 }
225 div.annotate-info a { color: #0000FF; }
225 div.annotate-info a { color: #0000FF; }
226 td.annotate:hover div.annotate-info { display: inline; }
226 td.annotate:hover div.annotate-info { display: inline; }
227
227
228 .source, .sourcefirst {
228 .source, .sourcefirst {
229 font-family: monospace;
229 font-family: monospace;
230 white-space: pre;
230 white-space: pre;
231 padding: 1px 4px;
231 padding: 1px 4px;
232 font-size: 90%;
232 font-size: 90%;
233 }
233 }
234 .sourcefirst { border-bottom: 1px solid #999; font-weight: bold; }
234 .sourcefirst { border-bottom: 1px solid #999; font-weight: bold; }
235 .source a { color: #999; font-size: smaller; font-family: monospace;}
235 .source a { color: #999; font-size: smaller; font-family: monospace;}
236 .bottomline { border-bottom: 1px solid #999; }
236 .bottomline { border-bottom: 1px solid #999; }
237
237
238 .sourcelines {
238 .sourcelines {
239 font-size: 90%;
239 font-size: 90%;
240 position: relative;
240 position: relative;
241 counter-reset: lineno;
241 counter-reset: lineno;
242 }
242 }
243
243
244 .wrap > span {
244 .wrap > span {
245 white-space: pre-wrap;
245 white-space: pre-wrap;
246 }
246 }
247
247
248 .linewraptoggle {
248 .linewraptoggle {
249 float: right;
249 float: right;
250 }
250 }
251
251
252 .diffblocks { counter-reset: lineno; }
252 .diffblocks { counter-reset: lineno; }
253 .diffblocks > div { counter-increment: lineno; }
253 .diffblocks > div { counter-increment: lineno; }
254
254
255 .sourcelines > span {
255 .sourcelines > span {
256 display: inline-block;
256 display: inline-block;
257 box-sizing: border-box;
257 box-sizing: border-box;
258 width: 100%;
258 width: 100%;
259 padding: 1px 0px 1px 5em;
259 padding: 1px 0px 1px 5em;
260 counter-increment: lineno;
260 counter-increment: lineno;
261 }
261 }
262
262
263 .sourcelines > span:before {
263 .sourcelines > span:before {
264 -moz-user-select: -moz-none;
264 -moz-user-select: -moz-none;
265 -khtml-user-select: none;
265 -khtml-user-select: none;
266 -webkit-user-select: none;
266 -webkit-user-select: none;
267 -ms-user-select: none;
267 -ms-user-select: none;
268 user-select: none;
268 user-select: none;
269 display: inline-block;
269 display: inline-block;
270 margin-left: -5em;
270 margin-left: -5em;
271 width: 4em;
271 width: 4em;
272 font-size: smaller;
272 font-size: smaller;
273 color: #999;
273 color: #999;
274 text-align: right;
274 text-align: right;
275 content: counters(lineno, ".");
275 content: counters(lineno, ".");
276 float: left;
276 float: left;
277 }
277 }
278
278
279 .sourcelines > span:target, tr:target td {
279 .sourcelines > span:target, tr:target td {
280 background-color: #bfdfff;
280 background-color: #bfdfff;
281 }
281 }
282
282
283 div.overflow table tbody.sourcelines > tr.followlines-select:hover,
283 div.overflow pre.sourcelines > span.followlines-select:hover {
284 div.overflow pre.sourcelines > span.followlines-select:hover {
284 cursor: cell;
285 cursor: cell;
285 }
286 }
286
287
288 tbody.sourcelines > tr.followlines-selected,
287 pre.sourcelines > span.followlines-selected {
289 pre.sourcelines > span.followlines-selected {
288 background-color: #99C7E9;
290 background-color: #99C7E9;
289 }
291 }
290
292
291 div#followlines {
293 div#followlines {
292 background-color: #B7B7B7;
294 background-color: #B7B7B7;
293 border: 1px solid #CCC;
295 border: 1px solid #CCC;
294 border-radius: 5px;
296 border-radius: 5px;
295 padding: 4px;
297 padding: 4px;
296 position: fixed;
298 position: fixed;
297 }
299 }
298
300
299 div.followlines-cancel {
301 div.followlines-cancel {
300 text-align: right;
302 text-align: right;
301 }
303 }
302
304
303 div.followlines-cancel > button {
305 div.followlines-cancel > button {
304 line-height: 80%;
306 line-height: 80%;
305 padding: 0;
307 padding: 0;
306 border: 0;
308 border: 0;
307 border-radius: 2px;
309 border-radius: 2px;
308 background-color: inherit;
310 background-color: inherit;
309 font-weight: bold;
311 font-weight: bold;
310 }
312 }
311
313
312 div.followlines-cancel > button:hover {
314 div.followlines-cancel > button:hover {
313 color: #FFFFFF;
315 color: #FFFFFF;
314 background-color: #CF1F1F;
316 background-color: #CF1F1F;
315 }
317 }
316
318
317 div.followlines-link {
319 div.followlines-link {
318 margin: 2px;
320 margin: 2px;
319 margin-top: 4px;
321 margin-top: 4px;
320 font-family: sans-serif;
322 font-family: sans-serif;
321 }
323 }
322
324
323 div#followlines-tooltip {
325 div#followlines-tooltip {
324 display: none;
326 display: none;
325 position: fixed;
327 position: fixed;
326 background-color: #ffc;
328 background-color: #ffc;
327 border: 1px solid #999;
329 border: 1px solid #999;
328 padding: 2px;
330 padding: 2px;
329 }
331 }
330
332
331 .sourcelines:hover > div#followlines-tooltip {
333 .sourcelines:hover > div#followlines-tooltip {
332 display: inline;
334 display: inline;
333 }
335 }
334
336
335 .sourcelines:hover > div#followlines-tooltip.hidden {
337 .sourcelines:hover > div#followlines-tooltip.hidden {
336 display: none;
338 display: none;
337 }
339 }
338
340
339 .sourcelines > a {
341 .sourcelines > a {
340 display: inline-block;
342 display: inline-block;
341 position: absolute;
343 position: absolute;
342 left: 0px;
344 left: 0px;
343 width: 4em;
345 width: 4em;
344 height: 1em;
346 height: 1em;
345 }
347 }
346
348
347 .fileline { font-family: monospace; }
349 .fileline { font-family: monospace; }
348 .fileline img { border: 0; }
350 .fileline img { border: 0; }
349
351
350 .tagEntry .closed { color: #99f; }
352 .tagEntry .closed { color: #99f; }
351
353
352 /* Changeset entry */
354 /* Changeset entry */
353 #changesetEntry {
355 #changesetEntry {
354 border-collapse: collapse;
356 border-collapse: collapse;
355 font-size: 90%;
357 font-size: 90%;
356 width: 100%;
358 width: 100%;
357 margin-bottom: 1em;
359 margin-bottom: 1em;
358 }
360 }
359
361
360 #changesetEntry th {
362 #changesetEntry th {
361 padding: 1px 4px;
363 padding: 1px 4px;
362 width: 4em;
364 width: 4em;
363 text-align: right;
365 text-align: right;
364 font-weight: normal;
366 font-weight: normal;
365 color: #999;
367 color: #999;
366 margin-right: .5em;
368 margin-right: .5em;
367 vertical-align: top;
369 vertical-align: top;
368 }
370 }
369
371
370 div.description {
372 div.description {
371 border-left: 2px solid #999;
373 border-left: 2px solid #999;
372 margin: 1em 0 1em 0;
374 margin: 1em 0 1em 0;
373 padding: .3em;
375 padding: .3em;
374 white-space: pre;
376 white-space: pre;
375 font-family: monospace;
377 font-family: monospace;
376 }
378 }
377
379
378 /* Graph */
380 /* Graph */
379 div#wrapper {
381 div#wrapper {
380 position: relative;
382 position: relative;
381 border-top: 1px solid black;
383 border-top: 1px solid black;
382 border-bottom: 1px solid black;
384 border-bottom: 1px solid black;
383 margin: 0;
385 margin: 0;
384 padding: 0;
386 padding: 0;
385 }
387 }
386
388
387 canvas {
389 canvas {
388 position: absolute;
390 position: absolute;
389 z-index: 5;
391 z-index: 5;
390 top: -0.7em;
392 top: -0.7em;
391 margin: 0;
393 margin: 0;
392 }
394 }
393
395
394 ul#graphnodes {
396 ul#graphnodes {
395 position: absolute;
397 position: absolute;
396 z-index: 10;
398 z-index: 10;
397 top: -1.0em;
399 top: -1.0em;
398 list-style: none inside none;
400 list-style: none inside none;
399 padding: 0;
401 padding: 0;
400 }
402 }
401
403
402 ul#nodebgs {
404 ul#nodebgs {
403 list-style: none inside none;
405 list-style: none inside none;
404 padding: 0;
406 padding: 0;
405 margin: 0;
407 margin: 0;
406 top: -0.7em;
408 top: -0.7em;
407 }
409 }
408
410
409 ul#graphnodes li, ul#nodebgs li {
411 ul#graphnodes li, ul#nodebgs li {
410 height: 39px;
412 height: 39px;
411 }
413 }
412
414
413 ul#graphnodes li .info {
415 ul#graphnodes li .info {
414 display: block;
416 display: block;
415 font-size: 70%;
417 font-size: 70%;
416 position: relative;
418 position: relative;
417 top: -3px;
419 top: -3px;
418 }
420 }
419
421
420 /* Comparison */
422 /* Comparison */
421 .legend {
423 .legend {
422 padding: 1.5% 0 1.5% 0;
424 padding: 1.5% 0 1.5% 0;
423 }
425 }
424
426
425 .legendinfo {
427 .legendinfo {
426 border: 1px solid #999;
428 border: 1px solid #999;
427 font-size: 80%;
429 font-size: 80%;
428 text-align: center;
430 text-align: center;
429 padding: 0.5%;
431 padding: 0.5%;
430 }
432 }
431
433
432 .equal {
434 .equal {
433 background-color: #ffffff;
435 background-color: #ffffff;
434 }
436 }
435
437
436 .delete {
438 .delete {
437 background-color: #faa;
439 background-color: #faa;
438 color: #333;
440 color: #333;
439 }
441 }
440
442
441 .insert {
443 .insert {
442 background-color: #ffa;
444 background-color: #ffa;
443 }
445 }
444
446
445 .replace {
447 .replace {
446 background-color: #e8e8e8;
448 background-color: #e8e8e8;
447 }
449 }
448
450
449 .header {
451 .header {
450 text-align: center;
452 text-align: center;
451 }
453 }
452
454
453 .block {
455 .block {
454 border-top: 1px solid #999;
456 border-top: 1px solid #999;
455 }
457 }
456
458
457 .breadcrumb {
459 .breadcrumb {
458 color: gray;
460 color: gray;
459 }
461 }
460
462
461 .breadcrumb a {
463 .breadcrumb a {
462 color: blue;
464 color: blue;
463 }
465 }
464
466
465 .scroll-loading {
467 .scroll-loading {
466 -webkit-animation: change_color 1s linear 0s infinite alternate;
468 -webkit-animation: change_color 1s linear 0s infinite alternate;
467 -moz-animation: change_color 1s linear 0s infinite alternate;
469 -moz-animation: change_color 1s linear 0s infinite alternate;
468 -o-animation: change_color 1s linear 0s infinite alternate;
470 -o-animation: change_color 1s linear 0s infinite alternate;
469 animation: change_color 1s linear 0s infinite alternate;
471 animation: change_color 1s linear 0s infinite alternate;
470 }
472 }
471
473
472 @-webkit-keyframes change_color {
474 @-webkit-keyframes change_color {
473 from { background-color: #A0CEFF; } to { }
475 from { background-color: #A0CEFF; } to { }
474 }
476 }
475 @-moz-keyframes change_color {
477 @-moz-keyframes change_color {
476 from { background-color: #A0CEFF; } to { }
478 from { background-color: #A0CEFF; } to { }
477 }
479 }
478 @-o-keyframes change_color {
480 @-o-keyframes change_color {
479 from { background-color: #A0CEFF; } to { }
481 from { background-color: #A0CEFF; } to { }
480 }
482 }
481 @keyframes change_color {
483 @keyframes change_color {
482 from { background-color: #A0CEFF; } to { }
484 from { background-color: #A0CEFF; } to { }
483 }
485 }
484
486
485 .scroll-loading-error {
487 .scroll-loading-error {
486 background-color: #FFCCCC !important;
488 background-color: #FFCCCC !important;
487 }
489 }
@@ -1,834 +1,836
1 #require serve
1 #require serve
2
2
3 Some tests for hgweb. Tests static files, plain files and different 404's.
3 Some tests for hgweb. Tests static files, plain files and different 404's.
4
4
5 $ hg init test
5 $ hg init test
6 $ cd test
6 $ cd test
7 $ mkdir da
7 $ mkdir da
8 $ echo foo > da/foo
8 $ echo foo > da/foo
9 $ echo foo > foo
9 $ echo foo > foo
10 $ hg ci -Ambase
10 $ hg ci -Ambase
11 adding da/foo
11 adding da/foo
12 adding foo
12 adding foo
13 $ hg bookmark -r0 '@'
13 $ hg bookmark -r0 '@'
14 $ hg bookmark -r0 'a b c'
14 $ hg bookmark -r0 'a b c'
15 $ hg bookmark -r0 'd/e/f'
15 $ hg bookmark -r0 'd/e/f'
16 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
16 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
17 $ cat hg.pid >> $DAEMON_PIDS
17 $ cat hg.pid >> $DAEMON_PIDS
18
18
19 manifest
19 manifest
20
20
21 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=raw')
21 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=raw')
22 200 Script output follows
22 200 Script output follows
23
23
24
24
25 drwxr-xr-x da
25 drwxr-xr-x da
26 -rw-r--r-- 4 foo
26 -rw-r--r-- 4 foo
27
27
28
28
29 $ (get-with-headers.py localhost:$HGPORT 'file/tip/da?style=raw')
29 $ (get-with-headers.py localhost:$HGPORT 'file/tip/da?style=raw')
30 200 Script output follows
30 200 Script output follows
31
31
32
32
33 -rw-r--r-- 4 foo
33 -rw-r--r-- 4 foo
34
34
35
35
36
36
37 plain file
37 plain file
38
38
39 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?style=raw'
39 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?style=raw'
40 200 Script output follows
40 200 Script output follows
41
41
42 foo
42 foo
43
43
44 should give a 404 - static file that does not exist
44 should give a 404 - static file that does not exist
45
45
46 $ get-with-headers.py localhost:$HGPORT 'static/bogus'
46 $ get-with-headers.py localhost:$HGPORT 'static/bogus'
47 404 Not Found
47 404 Not Found
48
48
49 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
49 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
50 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
50 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
51 <head>
51 <head>
52 <link rel="icon" href="/static/hgicon.png" type="image/png" />
52 <link rel="icon" href="/static/hgicon.png" type="image/png" />
53 <meta name="robots" content="index, nofollow" />
53 <meta name="robots" content="index, nofollow" />
54 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
54 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
55 <script type="text/javascript" src="/static/mercurial.js"></script>
55 <script type="text/javascript" src="/static/mercurial.js"></script>
56
56
57 <title>test: error</title>
57 <title>test: error</title>
58 </head>
58 </head>
59 <body>
59 <body>
60
60
61 <div class="container">
61 <div class="container">
62 <div class="menu">
62 <div class="menu">
63 <div class="logo">
63 <div class="logo">
64 <a href="https://mercurial-scm.org/">
64 <a href="https://mercurial-scm.org/">
65 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
65 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
66 </div>
66 </div>
67 <ul>
67 <ul>
68 <li><a href="/shortlog">log</a></li>
68 <li><a href="/shortlog">log</a></li>
69 <li><a href="/graph">graph</a></li>
69 <li><a href="/graph">graph</a></li>
70 <li><a href="/tags">tags</a></li>
70 <li><a href="/tags">tags</a></li>
71 <li><a href="/bookmarks">bookmarks</a></li>
71 <li><a href="/bookmarks">bookmarks</a></li>
72 <li><a href="/branches">branches</a></li>
72 <li><a href="/branches">branches</a></li>
73 </ul>
73 </ul>
74 <ul>
74 <ul>
75 <li><a href="/help">help</a></li>
75 <li><a href="/help">help</a></li>
76 </ul>
76 </ul>
77 </div>
77 </div>
78
78
79 <div class="main">
79 <div class="main">
80
80
81 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
81 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
82 <h3>error</h3>
82 <h3>error</h3>
83
83
84
84
85 <form class="search" action="/log">
85 <form class="search" action="/log">
86
86
87 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
87 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
88 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
88 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
89 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
89 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
90 </form>
90 </form>
91
91
92 <div class="description">
92 <div class="description">
93 <p>
93 <p>
94 An error occurred while processing your request:
94 An error occurred while processing your request:
95 </p>
95 </p>
96 <p>
96 <p>
97 Not Found
97 Not Found
98 </p>
98 </p>
99 </div>
99 </div>
100 </div>
100 </div>
101 </div>
101 </div>
102
102
103
103
104
104
105 </body>
105 </body>
106 </html>
106 </html>
107
107
108 [1]
108 [1]
109
109
110 should give a 404 - bad revision
110 should give a 404 - bad revision
111
111
112 $ get-with-headers.py localhost:$HGPORT 'file/spam/foo?style=raw'
112 $ get-with-headers.py localhost:$HGPORT 'file/spam/foo?style=raw'
113 404 Not Found
113 404 Not Found
114
114
115
115
116 error: revision not found: spam
116 error: revision not found: spam
117 [1]
117 [1]
118
118
119 should give a 400 - bad command
119 should give a 400 - bad command
120
120
121 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?cmd=spam&style=raw'
121 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?cmd=spam&style=raw'
122 400* (glob)
122 400* (glob)
123
123
124
124
125 error: no such method: spam
125 error: no such method: spam
126 [1]
126 [1]
127
127
128 $ get-with-headers.py --headeronly localhost:$HGPORT '?cmd=spam'
128 $ get-with-headers.py --headeronly localhost:$HGPORT '?cmd=spam'
129 400 no such method: spam
129 400 no such method: spam
130 [1]
130 [1]
131
131
132 should give a 400 - bad command as a part of url path (issue4071)
132 should give a 400 - bad command as a part of url path (issue4071)
133
133
134 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam'
134 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam'
135 400 no such method: spam
135 400 no such method: spam
136 [1]
136 [1]
137
137
138 $ get-with-headers.py --headeronly localhost:$HGPORT 'raw-spam'
138 $ get-with-headers.py --headeronly localhost:$HGPORT 'raw-spam'
139 400 no such method: spam
139 400 no such method: spam
140 [1]
140 [1]
141
141
142 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam/tip/foo'
142 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam/tip/foo'
143 400 no such method: spam
143 400 no such method: spam
144 [1]
144 [1]
145
145
146 should give a 404 - file does not exist
146 should give a 404 - file does not exist
147
147
148 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork?style=raw'
148 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork?style=raw'
149 404 Not Found
149 404 Not Found
150
150
151
151
152 error: bork@2ef0ac749a14: not found in manifest
152 error: bork@2ef0ac749a14: not found in manifest
153 [1]
153 [1]
154 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork'
154 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork'
155 404 Not Found
155 404 Not Found
156
156
157 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
157 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
158 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
158 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
159 <head>
159 <head>
160 <link rel="icon" href="/static/hgicon.png" type="image/png" />
160 <link rel="icon" href="/static/hgicon.png" type="image/png" />
161 <meta name="robots" content="index, nofollow" />
161 <meta name="robots" content="index, nofollow" />
162 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
162 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
163 <script type="text/javascript" src="/static/mercurial.js"></script>
163 <script type="text/javascript" src="/static/mercurial.js"></script>
164
164
165 <title>test: error</title>
165 <title>test: error</title>
166 </head>
166 </head>
167 <body>
167 <body>
168
168
169 <div class="container">
169 <div class="container">
170 <div class="menu">
170 <div class="menu">
171 <div class="logo">
171 <div class="logo">
172 <a href="https://mercurial-scm.org/">
172 <a href="https://mercurial-scm.org/">
173 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
173 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
174 </div>
174 </div>
175 <ul>
175 <ul>
176 <li><a href="/shortlog">log</a></li>
176 <li><a href="/shortlog">log</a></li>
177 <li><a href="/graph">graph</a></li>
177 <li><a href="/graph">graph</a></li>
178 <li><a href="/tags">tags</a></li>
178 <li><a href="/tags">tags</a></li>
179 <li><a href="/bookmarks">bookmarks</a></li>
179 <li><a href="/bookmarks">bookmarks</a></li>
180 <li><a href="/branches">branches</a></li>
180 <li><a href="/branches">branches</a></li>
181 </ul>
181 </ul>
182 <ul>
182 <ul>
183 <li><a href="/help">help</a></li>
183 <li><a href="/help">help</a></li>
184 </ul>
184 </ul>
185 </div>
185 </div>
186
186
187 <div class="main">
187 <div class="main">
188
188
189 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
189 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
190 <h3>error</h3>
190 <h3>error</h3>
191
191
192
192
193 <form class="search" action="/log">
193 <form class="search" action="/log">
194
194
195 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
195 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
196 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
196 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
197 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
197 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
198 </form>
198 </form>
199
199
200 <div class="description">
200 <div class="description">
201 <p>
201 <p>
202 An error occurred while processing your request:
202 An error occurred while processing your request:
203 </p>
203 </p>
204 <p>
204 <p>
205 bork@2ef0ac749a14: not found in manifest
205 bork@2ef0ac749a14: not found in manifest
206 </p>
206 </p>
207 </div>
207 </div>
208 </div>
208 </div>
209 </div>
209 </div>
210
210
211
211
212
212
213 </body>
213 </body>
214 </html>
214 </html>
215
215
216 [1]
216 [1]
217 $ get-with-headers.py localhost:$HGPORT 'diff/tip/bork?style=raw'
217 $ get-with-headers.py localhost:$HGPORT 'diff/tip/bork?style=raw'
218 404 Not Found
218 404 Not Found
219
219
220
220
221 error: bork@2ef0ac749a14: not found in manifest
221 error: bork@2ef0ac749a14: not found in manifest
222 [1]
222 [1]
223
223
224 try bad style
224 try bad style
225
225
226 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=foobar')
226 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=foobar')
227 200 Script output follows
227 200 Script output follows
228
228
229 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
229 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
230 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
230 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
231 <head>
231 <head>
232 <link rel="icon" href="/static/hgicon.png" type="image/png" />
232 <link rel="icon" href="/static/hgicon.png" type="image/png" />
233 <meta name="robots" content="index, nofollow" />
233 <meta name="robots" content="index, nofollow" />
234 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
234 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
235 <script type="text/javascript" src="/static/mercurial.js"></script>
235 <script type="text/javascript" src="/static/mercurial.js"></script>
236
236
237 <title>test: 2ef0ac749a14 /</title>
237 <title>test: 2ef0ac749a14 /</title>
238 </head>
238 </head>
239 <body>
239 <body>
240
240
241 <div class="container">
241 <div class="container">
242 <div class="menu">
242 <div class="menu">
243 <div class="logo">
243 <div class="logo">
244 <a href="https://mercurial-scm.org/">
244 <a href="https://mercurial-scm.org/">
245 <img src="/static/hglogo.png" alt="mercurial" /></a>
245 <img src="/static/hglogo.png" alt="mercurial" /></a>
246 </div>
246 </div>
247 <ul>
247 <ul>
248 <li><a href="/shortlog/tip">log</a></li>
248 <li><a href="/shortlog/tip">log</a></li>
249 <li><a href="/graph/tip">graph</a></li>
249 <li><a href="/graph/tip">graph</a></li>
250 <li><a href="/tags">tags</a></li>
250 <li><a href="/tags">tags</a></li>
251 <li><a href="/bookmarks">bookmarks</a></li>
251 <li><a href="/bookmarks">bookmarks</a></li>
252 <li><a href="/branches">branches</a></li>
252 <li><a href="/branches">branches</a></li>
253 </ul>
253 </ul>
254 <ul>
254 <ul>
255 <li><a href="/rev/tip">changeset</a></li>
255 <li><a href="/rev/tip">changeset</a></li>
256 <li class="active">browse</li>
256 <li class="active">browse</li>
257 </ul>
257 </ul>
258 <ul>
258 <ul>
259
259
260 </ul>
260 </ul>
261 <ul>
261 <ul>
262 <li><a href="/help">help</a></li>
262 <li><a href="/help">help</a></li>
263 </ul>
263 </ul>
264 </div>
264 </div>
265
265
266 <div class="main">
266 <div class="main">
267 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
267 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
268 <h3>
268 <h3>
269 directory / @ 0:<a href="/rev/2ef0ac749a14">2ef0ac749a14</a>
269 directory / @ 0:<a href="/rev/2ef0ac749a14">2ef0ac749a14</a>
270 <span class="tag">tip</span> <span class="tag">@</span> <span class="tag">a b c</span> <span class="tag">d/e/f</span>
270 <span class="tag">tip</span> <span class="tag">@</span> <span class="tag">a b c</span> <span class="tag">d/e/f</span>
271 </h3>
271 </h3>
272
272
273
273
274 <form class="search" action="/log">
274 <form class="search" action="/log">
275
275
276 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
276 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
277 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
277 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
278 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
278 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
279 </form>
279 </form>
280
280
281 <table class="bigtable">
281 <table class="bigtable">
282 <thead>
282 <thead>
283 <tr>
283 <tr>
284 <th class="name">name</th>
284 <th class="name">name</th>
285 <th class="size">size</th>
285 <th class="size">size</th>
286 <th class="permissions">permissions</th>
286 <th class="permissions">permissions</th>
287 </tr>
287 </tr>
288 </thead>
288 </thead>
289 <tbody class="stripes2">
289 <tbody class="stripes2">
290 <tr class="fileline">
290 <tr class="fileline">
291 <td class="name"><a href="/file/tip/">[up]</a></td>
291 <td class="name"><a href="/file/tip/">[up]</a></td>
292 <td class="size"></td>
292 <td class="size"></td>
293 <td class="permissions">drwxr-xr-x</td>
293 <td class="permissions">drwxr-xr-x</td>
294 </tr>
294 </tr>
295
295
296 <tr class="fileline">
296 <tr class="fileline">
297 <td class="name">
297 <td class="name">
298 <a href="/file/tip/da">
298 <a href="/file/tip/da">
299 <img src="/static/coal-folder.png" alt="dir."/> da/
299 <img src="/static/coal-folder.png" alt="dir."/> da/
300 </a>
300 </a>
301 <a href="/file/tip/da/">
301 <a href="/file/tip/da/">
302
302
303 </a>
303 </a>
304 </td>
304 </td>
305 <td class="size"></td>
305 <td class="size"></td>
306 <td class="permissions">drwxr-xr-x</td>
306 <td class="permissions">drwxr-xr-x</td>
307 </tr>
307 </tr>
308
308
309 <tr class="fileline">
309 <tr class="fileline">
310 <td class="filename">
310 <td class="filename">
311 <a href="/file/tip/foo">
311 <a href="/file/tip/foo">
312 <img src="/static/coal-file.png" alt="file"/> foo
312 <img src="/static/coal-file.png" alt="file"/> foo
313 </a>
313 </a>
314 </td>
314 </td>
315 <td class="size">4</td>
315 <td class="size">4</td>
316 <td class="permissions">-rw-r--r--</td>
316 <td class="permissions">-rw-r--r--</td>
317 </tr>
317 </tr>
318 </tbody>
318 </tbody>
319 </table>
319 </table>
320 </div>
320 </div>
321 </div>
321 </div>
322
322
323
323
324 </body>
324 </body>
325 </html>
325 </html>
326
326
327
327
328 stop and restart
328 stop and restart
329
329
330 $ killdaemons.py
330 $ killdaemons.py
331 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
331 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
332 $ cat hg.pid >> $DAEMON_PIDS
332 $ cat hg.pid >> $DAEMON_PIDS
333
333
334 Test the access/error files are opened in append mode
334 Test the access/error files are opened in append mode
335
335
336 $ $PYTHON -c "print len(file('access.log').readlines()), 'log lines written'"
336 $ $PYTHON -c "print len(file('access.log').readlines()), 'log lines written'"
337 14 log lines written
337 14 log lines written
338
338
339 static file
339 static file
340
340
341 $ get-with-headers.py --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server
341 $ get-with-headers.py --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server
342 200 Script output follows
342 200 Script output follows
343 content-length: 8349
343 content-length: 8463
344 content-type: text/css
344 content-type: text/css
345
345
346 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
346 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
347 a { color:#0000cc; }
347 a { color:#0000cc; }
348 a:hover, a:visited, a:active { color:#880000; }
348 a:hover, a:visited, a:active { color:#880000; }
349 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
349 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
350 div.page_header a:visited { color:#0000cc; }
350 div.page_header a:visited { color:#0000cc; }
351 div.page_header a:hover { color:#880000; }
351 div.page_header a:hover { color:#880000; }
352 div.page_nav {
352 div.page_nav {
353 padding:8px;
353 padding:8px;
354 display: flex;
354 display: flex;
355 justify-content: space-between;
355 justify-content: space-between;
356 align-items: center;
356 align-items: center;
357 }
357 }
358 div.page_nav a:visited { color:#0000cc; }
358 div.page_nav a:visited { color:#0000cc; }
359 div.extra_nav {
359 div.extra_nav {
360 padding: 8px;
360 padding: 8px;
361 }
361 }
362 div.extra_nav a:visited {
362 div.extra_nav a:visited {
363 color: #0000cc;
363 color: #0000cc;
364 }
364 }
365 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
365 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
366 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
366 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
367 div.page_footer_text { float:left; color:#555555; font-style:italic; }
367 div.page_footer_text { float:left; color:#555555; font-style:italic; }
368 div.page_body { padding:8px; }
368 div.page_body { padding:8px; }
369 div.title, a.title {
369 div.title, a.title {
370 display:block; padding:6px 8px;
370 display:block; padding:6px 8px;
371 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
371 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
372 }
372 }
373 a.title:hover { background-color: #d9d8d1; }
373 a.title:hover { background-color: #d9d8d1; }
374 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
374 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
375 div.log_body { padding:8px 8px 8px 150px; }
375 div.log_body { padding:8px 8px 8px 150px; }
376 .age { white-space:nowrap; }
376 .age { white-space:nowrap; }
377 span.age { position:relative; float:left; width:142px; font-style:italic; }
377 span.age { position:relative; float:left; width:142px; font-style:italic; }
378 div.log_link {
378 div.log_link {
379 padding:0px 8px;
379 padding:0px 8px;
380 font-size:10px; font-family:sans-serif; font-style:normal;
380 font-size:10px; font-family:sans-serif; font-style:normal;
381 position:relative; float:left; width:136px;
381 position:relative; float:left; width:136px;
382 }
382 }
383 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
383 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
384 a.list { text-decoration:none; color:#000000; }
384 a.list { text-decoration:none; color:#000000; }
385 a.list:hover { text-decoration:underline; color:#880000; }
385 a.list:hover { text-decoration:underline; color:#880000; }
386 table { padding:8px 4px; }
386 table { padding:8px 4px; }
387 th { padding:2px 5px; font-size:12px; text-align:left; }
387 th { padding:2px 5px; font-size:12px; text-align:left; }
388 .parity0 { background-color:#ffffff; }
388 .parity0 { background-color:#ffffff; }
389 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
389 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
390 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
390 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
391 pre.sourcelines.stripes > :nth-child(4n+2):hover,
391 pre.sourcelines.stripes > :nth-child(4n+2):hover,
392 pre.sourcelines.stripes > :nth-child(4n+4):hover,
392 pre.sourcelines.stripes > :nth-child(4n+4):hover,
393 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
393 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
394 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
394 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
395 td { padding:2px 5px; font-size:12px; vertical-align:top; }
395 td { padding:2px 5px; font-size:12px; vertical-align:top; }
396 td.closed { background-color: #99f; }
396 td.closed { background-color: #99f; }
397 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
397 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
398 td.indexlinks { white-space: nowrap; }
398 td.indexlinks { white-space: nowrap; }
399 td.indexlinks a {
399 td.indexlinks a {
400 padding: 2px 5px; line-height: 10px;
400 padding: 2px 5px; line-height: 10px;
401 border: 1px solid;
401 border: 1px solid;
402 color: #ffffff; background-color: #7777bb;
402 color: #ffffff; background-color: #7777bb;
403 border-color: #aaaadd #333366 #333366 #aaaadd;
403 border-color: #aaaadd #333366 #333366 #aaaadd;
404 font-weight: bold; text-align: center; text-decoration: none;
404 font-weight: bold; text-align: center; text-decoration: none;
405 font-size: 10px;
405 font-size: 10px;
406 }
406 }
407 td.indexlinks a:hover { background-color: #6666aa; }
407 td.indexlinks a:hover { background-color: #6666aa; }
408 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
408 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
409 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
409 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
410 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
410 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
411
411
412 .search {
412 .search {
413 margin-right: 8px;
413 margin-right: 8px;
414 }
414 }
415
415
416 div#hint {
416 div#hint {
417 position: absolute;
417 position: absolute;
418 display: none;
418 display: none;
419 width: 250px;
419 width: 250px;
420 padding: 5px;
420 padding: 5px;
421 background: #ffc;
421 background: #ffc;
422 border: 1px solid yellow;
422 border: 1px solid yellow;
423 border-radius: 5px;
423 border-radius: 5px;
424 }
424 }
425
425
426 #searchform:hover div#hint { display: block; }
426 #searchform:hover div#hint { display: block; }
427
427
428 tr.thisrev a { color:#999999; text-decoration: none; }
428 tr.thisrev a { color:#999999; text-decoration: none; }
429 tr.thisrev pre { color:#009900; }
429 tr.thisrev pre { color:#009900; }
430 td.annotate {
430 td.annotate {
431 white-space: nowrap;
431 white-space: nowrap;
432 }
432 }
433 div.annotate-info {
433 div.annotate-info {
434 display: none;
434 display: none;
435 position: absolute;
435 position: absolute;
436 background-color: #FFFFFF;
436 background-color: #FFFFFF;
437 border: 1px solid #d9d8d1;
437 border: 1px solid #d9d8d1;
438 text-align: left;
438 text-align: left;
439 color: #000000;
439 color: #000000;
440 padding: 5px;
440 padding: 5px;
441 }
441 }
442 div.annotate-info a { color: #0000FF; text-decoration: underline; }
442 div.annotate-info a { color: #0000FF; text-decoration: underline; }
443 td.annotate:hover div.annotate-info { display: inline; }
443 td.annotate:hover div.annotate-info { display: inline; }
444 .linenr { color:#999999; text-decoration:none }
444 .linenr { color:#999999; text-decoration:none }
445 div.rss_logo { float: right; white-space: nowrap; }
445 div.rss_logo { float: right; white-space: nowrap; }
446 div.rss_logo a {
446 div.rss_logo a {
447 padding:3px 6px; line-height:10px;
447 padding:3px 6px; line-height:10px;
448 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
448 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
449 color:#ffffff; background-color:#ff6600;
449 color:#ffffff; background-color:#ff6600;
450 font-weight:bold; font-family:sans-serif; font-size:10px;
450 font-weight:bold; font-family:sans-serif; font-size:10px;
451 text-align:center; text-decoration:none;
451 text-align:center; text-decoration:none;
452 }
452 }
453 div.rss_logo a:hover { background-color:#ee5500; }
453 div.rss_logo a:hover { background-color:#ee5500; }
454 pre { margin: 0; }
454 pre { margin: 0; }
455 span.logtags span {
455 span.logtags span {
456 padding: 0px 4px;
456 padding: 0px 4px;
457 font-size: 10px;
457 font-size: 10px;
458 font-weight: normal;
458 font-weight: normal;
459 border: 1px solid;
459 border: 1px solid;
460 background-color: #ffaaff;
460 background-color: #ffaaff;
461 border-color: #ffccff #ff00ee #ff00ee #ffccff;
461 border-color: #ffccff #ff00ee #ff00ee #ffccff;
462 }
462 }
463 span.logtags span.tagtag {
463 span.logtags span.tagtag {
464 background-color: #ffffaa;
464 background-color: #ffffaa;
465 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
465 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
466 }
466 }
467 span.logtags span.branchtag {
467 span.logtags span.branchtag {
468 background-color: #aaffaa;
468 background-color: #aaffaa;
469 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
469 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
470 }
470 }
471 span.logtags span.inbranchtag {
471 span.logtags span.inbranchtag {
472 background-color: #d5dde6;
472 background-color: #d5dde6;
473 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
473 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
474 }
474 }
475 span.logtags span.bookmarktag {
475 span.logtags span.bookmarktag {
476 background-color: #afdffa;
476 background-color: #afdffa;
477 border-color: #ccecff #46ace6 #46ace6 #ccecff;
477 border-color: #ccecff #46ace6 #46ace6 #ccecff;
478 }
478 }
479 span.difflineplus { color:#008800; }
479 span.difflineplus { color:#008800; }
480 span.difflineminus { color:#cc0000; }
480 span.difflineminus { color:#cc0000; }
481 span.difflineat { color:#990099; }
481 span.difflineat { color:#990099; }
482 div.diffblocks { counter-reset: lineno; }
482 div.diffblocks { counter-reset: lineno; }
483 div.diffblock { counter-increment: lineno; }
483 div.diffblock { counter-increment: lineno; }
484 pre.sourcelines { position: relative; counter-reset: lineno; }
484 pre.sourcelines { position: relative; counter-reset: lineno; }
485 pre.sourcelines > span {
485 pre.sourcelines > span {
486 display: inline-block;
486 display: inline-block;
487 box-sizing: border-box;
487 box-sizing: border-box;
488 width: 100%;
488 width: 100%;
489 padding: 0 0 0 5em;
489 padding: 0 0 0 5em;
490 counter-increment: lineno;
490 counter-increment: lineno;
491 vertical-align: top;
491 vertical-align: top;
492 }
492 }
493 pre.sourcelines > span:before {
493 pre.sourcelines > span:before {
494 -moz-user-select: -moz-none;
494 -moz-user-select: -moz-none;
495 -khtml-user-select: none;
495 -khtml-user-select: none;
496 -webkit-user-select: none;
496 -webkit-user-select: none;
497 -ms-user-select: none;
497 -ms-user-select: none;
498 user-select: none;
498 user-select: none;
499 display: inline-block;
499 display: inline-block;
500 margin-left: -5em;
500 margin-left: -5em;
501 width: 4em;
501 width: 4em;
502 color: #999;
502 color: #999;
503 text-align: right;
503 text-align: right;
504 content: counters(lineno,".");
504 content: counters(lineno,".");
505 float: left;
505 float: left;
506 }
506 }
507 pre.sourcelines > a {
507 pre.sourcelines > a {
508 display: inline-block;
508 display: inline-block;
509 position: absolute;
509 position: absolute;
510 left: 0px;
510 left: 0px;
511 width: 4em;
511 width: 4em;
512 height: 1em;
512 height: 1em;
513 }
513 }
514 tr:target td,
514 tr:target td,
515 pre.sourcelines > span:target,
515 pre.sourcelines > span:target,
516 pre.sourcelines.stripes > span:target {
516 pre.sourcelines.stripes > span:target {
517 background-color: #bfdfff;
517 background-color: #bfdfff;
518 }
518 }
519
519
520 .description {
520 .description {
521 font-family: monospace;
521 font-family: monospace;
522 }
522 }
523
523
524 /* Followlines */
524 /* Followlines */
525 div.page_body table tbody.sourcelines > tr.followlines-select:hover,
525 div.page_body pre.sourcelines > span.followlines-select:hover {
526 div.page_body pre.sourcelines > span.followlines-select:hover {
526 cursor: cell;
527 cursor: cell;
527 }
528 }
528
529
530 tbody.sourcelines > tr.followlines-selected,
529 pre.sourcelines > span.followlines-selected {
531 pre.sourcelines > span.followlines-selected {
530 background-color: #99C7E9 !important;
532 background-color: #99C7E9 !important;
531 }
533 }
532
534
533 div#followlines {
535 div#followlines {
534 background-color: #B7B7B7;
536 background-color: #B7B7B7;
535 border: 1px solid #CCC;
537 border: 1px solid #CCC;
536 border-radius: 5px;
538 border-radius: 5px;
537 padding: 4px;
539 padding: 4px;
538 position: fixed;
540 position: fixed;
539 }
541 }
540
542
541 div.followlines-cancel {
543 div.followlines-cancel {
542 text-align: right;
544 text-align: right;
543 }
545 }
544
546
545 div.followlines-cancel > button {
547 div.followlines-cancel > button {
546 line-height: 80%;
548 line-height: 80%;
547 padding: 0;
549 padding: 0;
548 border: 0;
550 border: 0;
549 border-radius: 2px;
551 border-radius: 2px;
550 background-color: inherit;
552 background-color: inherit;
551 font-weight: bold;
553 font-weight: bold;
552 }
554 }
553
555
554 div.followlines-cancel > button:hover {
556 div.followlines-cancel > button:hover {
555 color: #FFFFFF;
557 color: #FFFFFF;
556 background-color: #CF1F1F;
558 background-color: #CF1F1F;
557 }
559 }
558
560
559 div.followlines-link {
561 div.followlines-link {
560 margin: 2px;
562 margin: 2px;
561 margin-top: 4px;
563 margin-top: 4px;
562 font-family: sans-serif;
564 font-family: sans-serif;
563 }
565 }
564
566
565 div#followlines-tooltip {
567 div#followlines-tooltip {
566 display: none;
568 display: none;
567 position: fixed;
569 position: fixed;
568 background-color: #ffc;
570 background-color: #ffc;
569 border: 1px solid #999;
571 border: 1px solid #999;
570 padding: 2px;
572 padding: 2px;
571 }
573 }
572
574
573 .sourcelines:hover > div#followlines-tooltip {
575 .sourcelines:hover > div#followlines-tooltip {
574 display: inline;
576 display: inline;
575 }
577 }
576
578
577 .sourcelines:hover > div#followlines-tooltip.hidden {
579 .sourcelines:hover > div#followlines-tooltip.hidden {
578 display: none;
580 display: none;
579 }
581 }
580 /* Graph */
582 /* Graph */
581 div#wrapper {
583 div#wrapper {
582 position: relative;
584 position: relative;
583 margin: 0;
585 margin: 0;
584 padding: 0;
586 padding: 0;
585 margin-top: 3px;
587 margin-top: 3px;
586 }
588 }
587
589
588 canvas {
590 canvas {
589 position: absolute;
591 position: absolute;
590 z-index: 5;
592 z-index: 5;
591 top: -0.9em;
593 top: -0.9em;
592 margin: 0;
594 margin: 0;
593 }
595 }
594
596
595 ul#nodebgs {
597 ul#nodebgs {
596 list-style: none inside none;
598 list-style: none inside none;
597 padding: 0;
599 padding: 0;
598 margin: 0;
600 margin: 0;
599 top: -0.7em;
601 top: -0.7em;
600 }
602 }
601
603
602 ul#graphnodes li, ul#nodebgs li {
604 ul#graphnodes li, ul#nodebgs li {
603 height: 39px;
605 height: 39px;
604 }
606 }
605
607
606 ul#graphnodes {
608 ul#graphnodes {
607 position: absolute;
609 position: absolute;
608 z-index: 10;
610 z-index: 10;
609 top: -0.8em;
611 top: -0.8em;
610 list-style: none inside none;
612 list-style: none inside none;
611 padding: 0;
613 padding: 0;
612 }
614 }
613
615
614 ul#graphnodes li .info {
616 ul#graphnodes li .info {
615 display: block;
617 display: block;
616 font-size: 100%;
618 font-size: 100%;
617 position: relative;
619 position: relative;
618 top: -3px;
620 top: -3px;
619 font-style: italic;
621 font-style: italic;
620 }
622 }
621
623
622 /* Comparison */
624 /* Comparison */
623 .legend {
625 .legend {
624 padding: 1.5% 0 1.5% 0;
626 padding: 1.5% 0 1.5% 0;
625 }
627 }
626
628
627 .legendinfo {
629 .legendinfo {
628 border: 1px solid #d9d8d1;
630 border: 1px solid #d9d8d1;
629 font-size: 80%;
631 font-size: 80%;
630 text-align: center;
632 text-align: center;
631 padding: 0.5%;
633 padding: 0.5%;
632 }
634 }
633
635
634 .equal {
636 .equal {
635 background-color: #ffffff;
637 background-color: #ffffff;
636 }
638 }
637
639
638 .delete {
640 .delete {
639 background-color: #faa;
641 background-color: #faa;
640 color: #333;
642 color: #333;
641 }
643 }
642
644
643 .insert {
645 .insert {
644 background-color: #ffa;
646 background-color: #ffa;
645 }
647 }
646
648
647 .replace {
649 .replace {
648 background-color: #e8e8e8;
650 background-color: #e8e8e8;
649 }
651 }
650
652
651 .comparison {
653 .comparison {
652 overflow-x: auto;
654 overflow-x: auto;
653 }
655 }
654
656
655 .header th {
657 .header th {
656 text-align: center;
658 text-align: center;
657 }
659 }
658
660
659 .block {
661 .block {
660 border-top: 1px solid #d9d8d1;
662 border-top: 1px solid #d9d8d1;
661 }
663 }
662
664
663 .scroll-loading {
665 .scroll-loading {
664 -webkit-animation: change_color 1s linear 0s infinite alternate;
666 -webkit-animation: change_color 1s linear 0s infinite alternate;
665 -moz-animation: change_color 1s linear 0s infinite alternate;
667 -moz-animation: change_color 1s linear 0s infinite alternate;
666 -o-animation: change_color 1s linear 0s infinite alternate;
668 -o-animation: change_color 1s linear 0s infinite alternate;
667 animation: change_color 1s linear 0s infinite alternate;
669 animation: change_color 1s linear 0s infinite alternate;
668 }
670 }
669
671
670 @-webkit-keyframes change_color {
672 @-webkit-keyframes change_color {
671 from { background-color: #A0CEFF; } to { }
673 from { background-color: #A0CEFF; } to { }
672 }
674 }
673 @-moz-keyframes change_color {
675 @-moz-keyframes change_color {
674 from { background-color: #A0CEFF; } to { }
676 from { background-color: #A0CEFF; } to { }
675 }
677 }
676 @-o-keyframes change_color {
678 @-o-keyframes change_color {
677 from { background-color: #A0CEFF; } to { }
679 from { background-color: #A0CEFF; } to { }
678 }
680 }
679 @keyframes change_color {
681 @keyframes change_color {
680 from { background-color: #A0CEFF; } to { }
682 from { background-color: #A0CEFF; } to { }
681 }
683 }
682
684
683 .scroll-loading-error {
685 .scroll-loading-error {
684 background-color: #FFCCCC !important;
686 background-color: #FFCCCC !important;
685 }
687 }
686
688
687 #doc {
689 #doc {
688 margin: 0 8px;
690 margin: 0 8px;
689 }
691 }
690 304 Not Modified
692 304 Not Modified
691
693
692
694
693 phase changes are refreshed (issue4061)
695 phase changes are refreshed (issue4061)
694
696
695 $ echo bar >> foo
697 $ echo bar >> foo
696 $ hg ci -msecret --secret
698 $ hg ci -msecret --secret
697 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
699 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
698 200 Script output follows
700 200 Script output follows
699
701
700
702
701 # HG changelog
703 # HG changelog
702 # Node ID 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
704 # Node ID 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
703
705
704 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
706 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
705 revision: 0
707 revision: 0
706 user: test
708 user: test
707 date: Thu, 01 Jan 1970 00:00:00 +0000
709 date: Thu, 01 Jan 1970 00:00:00 +0000
708 summary: base
710 summary: base
709 branch: default
711 branch: default
710 tag: tip
712 tag: tip
711 bookmark: @
713 bookmark: @
712 bookmark: a b c
714 bookmark: a b c
713 bookmark: d/e/f
715 bookmark: d/e/f
714
716
715
717
716 $ hg phase --draft tip
718 $ hg phase --draft tip
717 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
719 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
718 200 Script output follows
720 200 Script output follows
719
721
720
722
721 # HG changelog
723 # HG changelog
722 # Node ID a084749e708a9c4c0a5b652a2a446322ce290e04
724 # Node ID a084749e708a9c4c0a5b652a2a446322ce290e04
723
725
724 changeset: a084749e708a9c4c0a5b652a2a446322ce290e04
726 changeset: a084749e708a9c4c0a5b652a2a446322ce290e04
725 revision: 1
727 revision: 1
726 user: test
728 user: test
727 date: Thu, 01 Jan 1970 00:00:00 +0000
729 date: Thu, 01 Jan 1970 00:00:00 +0000
728 summary: secret
730 summary: secret
729 branch: default
731 branch: default
730 tag: tip
732 tag: tip
731
733
732 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
734 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
733 revision: 0
735 revision: 0
734 user: test
736 user: test
735 date: Thu, 01 Jan 1970 00:00:00 +0000
737 date: Thu, 01 Jan 1970 00:00:00 +0000
736 summary: base
738 summary: base
737 bookmark: @
739 bookmark: @
738 bookmark: a b c
740 bookmark: a b c
739 bookmark: d/e/f
741 bookmark: d/e/f
740
742
741
743
742
744
743 access bookmarks
745 access bookmarks
744
746
745 $ get-with-headers.py localhost:$HGPORT 'rev/@?style=paper' | egrep '^200|changeset 0:'
747 $ get-with-headers.py localhost:$HGPORT 'rev/@?style=paper' | egrep '^200|changeset 0:'
746 200 Script output follows
748 200 Script output follows
747 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
749 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
748
750
749 $ get-with-headers.py localhost:$HGPORT 'rev/%40?style=paper' | egrep '^200|changeset 0:'
751 $ get-with-headers.py localhost:$HGPORT 'rev/%40?style=paper' | egrep '^200|changeset 0:'
750 200 Script output follows
752 200 Script output follows
751 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
753 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
752
754
753 $ get-with-headers.py localhost:$HGPORT 'rev/a%20b%20c?style=paper' | egrep '^200|changeset 0:'
755 $ get-with-headers.py localhost:$HGPORT 'rev/a%20b%20c?style=paper' | egrep '^200|changeset 0:'
754 200 Script output follows
756 200 Script output follows
755 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
757 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
756
758
757 $ get-with-headers.py localhost:$HGPORT 'rev/d%252Fe%252Ff?style=paper' | egrep '^200|changeset 0:'
759 $ get-with-headers.py localhost:$HGPORT 'rev/d%252Fe%252Ff?style=paper' | egrep '^200|changeset 0:'
758 200 Script output follows
760 200 Script output follows
759 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
761 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
760
762
761 no style can be loaded from directories other than the specified paths
763 no style can be loaded from directories other than the specified paths
762
764
763 $ mkdir -p x/templates/fallback
765 $ mkdir -p x/templates/fallback
764 $ cat <<EOF > x/templates/fallback/map
766 $ cat <<EOF > x/templates/fallback/map
765 > default = 'shortlog'
767 > default = 'shortlog'
766 > shortlog = 'fall back to default\n'
768 > shortlog = 'fall back to default\n'
767 > mimetype = 'text/plain'
769 > mimetype = 'text/plain'
768 > EOF
770 > EOF
769 $ cat <<EOF > x/map
771 $ cat <<EOF > x/map
770 > default = 'shortlog'
772 > default = 'shortlog'
771 > shortlog = 'access to outside of templates directory\n'
773 > shortlog = 'access to outside of templates directory\n'
772 > mimetype = 'text/plain'
774 > mimetype = 'text/plain'
773 > EOF
775 > EOF
774
776
775 $ killdaemons.py
777 $ killdaemons.py
776 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log \
778 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log \
777 > --config web.style=fallback --config web.templates=x/templates
779 > --config web.style=fallback --config web.templates=x/templates
778 $ cat hg.pid >> $DAEMON_PIDS
780 $ cat hg.pid >> $DAEMON_PIDS
779
781
780 $ get-with-headers.py localhost:$HGPORT "?style=`pwd`/x"
782 $ get-with-headers.py localhost:$HGPORT "?style=`pwd`/x"
781 200 Script output follows
783 200 Script output follows
782
784
783 fall back to default
785 fall back to default
784
786
785 $ get-with-headers.py localhost:$HGPORT '?style=..'
787 $ get-with-headers.py localhost:$HGPORT '?style=..'
786 200 Script output follows
788 200 Script output follows
787
789
788 fall back to default
790 fall back to default
789
791
790 $ get-with-headers.py localhost:$HGPORT '?style=./..'
792 $ get-with-headers.py localhost:$HGPORT '?style=./..'
791 200 Script output follows
793 200 Script output follows
792
794
793 fall back to default
795 fall back to default
794
796
795 $ get-with-headers.py localhost:$HGPORT '?style=.../.../'
797 $ get-with-headers.py localhost:$HGPORT '?style=.../.../'
796 200 Script output follows
798 200 Script output follows
797
799
798 fall back to default
800 fall back to default
799
801
800 errors
802 errors
801
803
802 $ cat errors.log
804 $ cat errors.log
803
805
804 Uncaught exceptions result in a logged error and canned HTTP response
806 Uncaught exceptions result in a logged error and canned HTTP response
805
807
806 $ killdaemons.py
808 $ killdaemons.py
807 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
809 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
808 $ cat hg.pid >> $DAEMON_PIDS
810 $ cat hg.pid >> $DAEMON_PIDS
809
811
810 $ get-with-headers.py localhost:$HGPORT 'raiseerror' transfer-encoding content-type
812 $ get-with-headers.py localhost:$HGPORT 'raiseerror' transfer-encoding content-type
811 500 Internal Server Error
813 500 Internal Server Error
812 transfer-encoding: chunked
814 transfer-encoding: chunked
813
815
814 Internal Server Error (no-eol)
816 Internal Server Error (no-eol)
815 [1]
817 [1]
816
818
817 $ killdaemons.py
819 $ killdaemons.py
818 $ head -1 errors.log
820 $ head -1 errors.log
819 .* Exception happened during processing request '/raiseerror': (re)
821 .* Exception happened during processing request '/raiseerror': (re)
820
822
821 Uncaught exception after partial content sent
823 Uncaught exception after partial content sent
822
824
823 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
825 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
824 $ cat hg.pid >> $DAEMON_PIDS
826 $ cat hg.pid >> $DAEMON_PIDS
825 $ get-with-headers.py localhost:$HGPORT 'raiseerror?partialresponse=1' transfer-encoding content-type
827 $ get-with-headers.py localhost:$HGPORT 'raiseerror?partialresponse=1' transfer-encoding content-type
826 200 Script output follows
828 200 Script output follows
827 transfer-encoding: chunked
829 transfer-encoding: chunked
828 content-type: text/plain
830 content-type: text/plain
829
831
830 partial content
832 partial content
831 Internal Server Error (no-eol)
833 Internal Server Error (no-eol)
832
834
833 $ killdaemons.py
835 $ killdaemons.py
834 $ cd ..
836 $ cd ..
@@ -1,993 +1,998
1 #require pygments serve
1 #require pygments serve
2
2
3 $ cat <<EOF >> $HGRCPATH
3 $ cat <<EOF >> $HGRCPATH
4 > [extensions]
4 > [extensions]
5 > highlight =
5 > highlight =
6 > [web]
6 > [web]
7 > pygments_style = friendly
7 > pygments_style = friendly
8 > highlightfiles = **.py and size('<100KB')
8 > highlightfiles = **.py and size('<100KB')
9 > EOF
9 > EOF
10 $ hg init test
10 $ hg init test
11 $ cd test
11 $ cd test
12
12
13 $ filterhtml () {
13 $ filterhtml () {
14 > sed -e "s/class=\"k\"/class=\"kn\"/g" \
14 > sed -e "s/class=\"k\"/class=\"kn\"/g" \
15 > -e "s/class=\"mf\"/class=\"mi\"/g" \
15 > -e "s/class=\"mf\"/class=\"mi\"/g" \
16 > -e "s/class=\"vm\"/class=\"n\"/g" \
16 > -e "s/class=\"vm\"/class=\"n\"/g" \
17 > -e "s/class=\"\([cs]\)[h12]\"/class=\"\1\"/g"
17 > -e "s/class=\"\([cs]\)[h12]\"/class=\"\1\"/g"
18 > }
18 > }
19
19
20 create random Python file to exercise Pygments
20 create random Python file to exercise Pygments
21
21
22 $ cat <<EOF > primes.py
22 $ cat <<EOF > primes.py
23 > """Fun with generators. Corresponding Haskell implementation:
23 > """Fun with generators. Corresponding Haskell implementation:
24 >
24 >
25 > primes = 2 : sieve [3, 5..]
25 > primes = 2 : sieve [3, 5..]
26 > where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]
26 > where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]
27 > """
27 > """
28 >
28 >
29 > from itertools import dropwhile, ifilter, islice, count, chain
29 > from itertools import dropwhile, ifilter, islice, count, chain
30 >
30 >
31 > def primes():
31 > def primes():
32 > """Generate all primes."""
32 > """Generate all primes."""
33 > def sieve(ns):
33 > def sieve(ns):
34 > p = ns.next()
34 > p = ns.next()
35 > # It is important to yield *here* in order to stop the
35 > # It is important to yield *here* in order to stop the
36 > # infinite recursion.
36 > # infinite recursion.
37 > yield p
37 > yield p
38 > ns = ifilter(lambda n: n % p != 0, ns)
38 > ns = ifilter(lambda n: n % p != 0, ns)
39 > for n in sieve(ns):
39 > for n in sieve(ns):
40 > yield n
40 > yield n
41 >
41 >
42 > odds = ifilter(lambda i: i % 2 == 1, count())
42 > odds = ifilter(lambda i: i % 2 == 1, count())
43 > return chain([2], sieve(dropwhile(lambda n: n < 3, odds)))
43 > return chain([2], sieve(dropwhile(lambda n: n < 3, odds)))
44 >
44 >
45 > if __name__ == "__main__":
45 > if __name__ == "__main__":
46 > import sys
46 > import sys
47 > try:
47 > try:
48 > n = int(sys.argv[1])
48 > n = int(sys.argv[1])
49 > except (ValueError, IndexError):
49 > except (ValueError, IndexError):
50 > n = 10
50 > n = 10
51 > p = primes()
51 > p = primes()
52 > print "The first %d primes: %s" % (n, list(islice(p, n)))
52 > print "The first %d primes: %s" % (n, list(islice(p, n)))
53 > EOF
53 > EOF
54 $ echo >> primes.py # to test html markup with an empty line just before EOF
54 $ echo >> primes.py # to test html markup with an empty line just before EOF
55 $ hg ci -Ama
55 $ hg ci -Ama
56 adding primes.py
56 adding primes.py
57
57
58 hg serve
58 hg serve
59
59
60 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
60 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
61 $ cat hg.pid >> $DAEMON_PIDS
61 $ cat hg.pid >> $DAEMON_PIDS
62
62
63 hgweb filerevision, html
63 hgweb filerevision, html
64
64
65 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py') | filterhtml
65 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py') | filterhtml
66 200 Script output follows
66 200 Script output follows
67
67
68 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
68 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
69 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
69 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
70 <head>
70 <head>
71 <link rel="icon" href="/static/hgicon.png" type="image/png" />
71 <link rel="icon" href="/static/hgicon.png" type="image/png" />
72 <meta name="robots" content="index, nofollow" />
72 <meta name="robots" content="index, nofollow" />
73 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
73 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
74 <script type="text/javascript" src="/static/mercurial.js"></script>
74 <script type="text/javascript" src="/static/mercurial.js"></script>
75
75
76 <link rel="stylesheet" href="/highlightcss" type="text/css" />
76 <link rel="stylesheet" href="/highlightcss" type="text/css" />
77 <title>test: 1af356141006 primes.py</title>
77 <title>test: 1af356141006 primes.py</title>
78 </head>
78 </head>
79 <body>
79 <body>
80
80
81 <div class="container">
81 <div class="container">
82 <div class="menu">
82 <div class="menu">
83 <div class="logo">
83 <div class="logo">
84 <a href="https://mercurial-scm.org/">
84 <a href="https://mercurial-scm.org/">
85 <img src="/static/hglogo.png" alt="mercurial" /></a>
85 <img src="/static/hglogo.png" alt="mercurial" /></a>
86 </div>
86 </div>
87 <ul>
87 <ul>
88 <li><a href="/shortlog/tip">log</a></li>
88 <li><a href="/shortlog/tip">log</a></li>
89 <li><a href="/graph/tip">graph</a></li>
89 <li><a href="/graph/tip">graph</a></li>
90 <li><a href="/tags">tags</a></li>
90 <li><a href="/tags">tags</a></li>
91 <li><a href="/bookmarks">bookmarks</a></li>
91 <li><a href="/bookmarks">bookmarks</a></li>
92 <li><a href="/branches">branches</a></li>
92 <li><a href="/branches">branches</a></li>
93 </ul>
93 </ul>
94 <ul>
94 <ul>
95 <li><a href="/rev/tip">changeset</a></li>
95 <li><a href="/rev/tip">changeset</a></li>
96 <li><a href="/file/tip/">browse</a></li>
96 <li><a href="/file/tip/">browse</a></li>
97 </ul>
97 </ul>
98 <ul>
98 <ul>
99 <li class="active">file</li>
99 <li class="active">file</li>
100 <li><a href="/file/tip/primes.py">latest</a></li>
100 <li><a href="/file/tip/primes.py">latest</a></li>
101 <li><a href="/diff/tip/primes.py">diff</a></li>
101 <li><a href="/diff/tip/primes.py">diff</a></li>
102 <li><a href="/comparison/tip/primes.py">comparison</a></li>
102 <li><a href="/comparison/tip/primes.py">comparison</a></li>
103 <li><a href="/annotate/tip/primes.py">annotate</a></li>
103 <li><a href="/annotate/tip/primes.py">annotate</a></li>
104 <li><a href="/log/tip/primes.py">file log</a></li>
104 <li><a href="/log/tip/primes.py">file log</a></li>
105 <li><a href="/raw-file/tip/primes.py">raw</a></li>
105 <li><a href="/raw-file/tip/primes.py">raw</a></li>
106 </ul>
106 </ul>
107 <ul>
107 <ul>
108 <li><a href="/help">help</a></li>
108 <li><a href="/help">help</a></li>
109 </ul>
109 </ul>
110 </div>
110 </div>
111
111
112 <div class="main">
112 <div class="main">
113 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
113 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
114 <h3>
114 <h3>
115 view primes.py @ 0:<a href="/rev/1af356141006">1af356141006</a>
115 view primes.py @ 0:<a href="/rev/1af356141006">1af356141006</a>
116 <span class="tag">tip</span>
116 <span class="tag">tip</span>
117 </h3>
117 </h3>
118
118
119
119
120 <form class="search" action="/log">
120 <form class="search" action="/log">
121
121
122 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
122 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
123 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
123 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
124 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
124 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
125 </form>
125 </form>
126
126
127 <div class="description">a</div>
127 <div class="description">a</div>
128
128
129 <table id="changesetEntry">
129 <table id="changesetEntry">
130 <tr>
130 <tr>
131 <th class="author">author</th>
131 <th class="author">author</th>
132 <td class="author">&#116;&#101;&#115;&#116;</td>
132 <td class="author">&#116;&#101;&#115;&#116;</td>
133 </tr>
133 </tr>
134 <tr>
134 <tr>
135 <th class="date">date</th>
135 <th class="date">date</th>
136 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
136 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
137 </tr>
137 </tr>
138 <tr>
138 <tr>
139 <th class="author">parents</th>
139 <th class="author">parents</th>
140 <td class="author"></td>
140 <td class="author"></td>
141 </tr>
141 </tr>
142 <tr>
142 <tr>
143 <th class="author">children</th>
143 <th class="author">children</th>
144 <td class="author"></td>
144 <td class="author"></td>
145 </tr>
145 </tr>
146 </table>
146 </table>
147
147
148 <div class="overflow">
148 <div class="overflow">
149 <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
149 <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
150 <div class="sourcefirst"> line source</div>
150 <div class="sourcefirst"> line source</div>
151 <pre class="sourcelines stripes4 wrap bottomline"
151 <pre class="sourcelines stripes4 wrap bottomline"
152 data-logurl="/log/tip/primes.py"
152 data-logurl="/log/tip/primes.py"
153 data-selectabletag="SPAN"
153 data-selectabletag="SPAN"
154 data-ishead="1">
154 data-ishead="1">
155
155
156 <span id="l1"><span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></span><a href="#l1"></a>
156 <span id="l1"><span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></span><a href="#l1"></a>
157 <span id="l2"></span><a href="#l2"></a>
157 <span id="l2"></span><a href="#l2"></a>
158 <span id="l3"><span class="sd">primes = 2 : sieve [3, 5..]</span></span><a href="#l3"></a>
158 <span id="l3"><span class="sd">primes = 2 : sieve [3, 5..]</span></span><a href="#l3"></a>
159 <span id="l4"><span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></span><a href="#l4"></a>
159 <span id="l4"><span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></span><a href="#l4"></a>
160 <span id="l5"><span class="sd">&quot;&quot;&quot;</span></span><a href="#l5"></a>
160 <span id="l5"><span class="sd">&quot;&quot;&quot;</span></span><a href="#l5"></a>
161 <span id="l6"></span><a href="#l6"></a>
161 <span id="l6"></span><a href="#l6"></a>
162 <span id="l7"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></span><a href="#l7"></a>
162 <span id="l7"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></span><a href="#l7"></a>
163 <span id="l8"></span><a href="#l8"></a>
163 <span id="l8"></span><a href="#l8"></a>
164 <span id="l9"><span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></span><a href="#l9"></a>
164 <span id="l9"><span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></span><a href="#l9"></a>
165 <span id="l10"> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></span><a href="#l10"></a>
165 <span id="l10"> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></span><a href="#l10"></a>
166 <span id="l11"> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l11"></a>
166 <span id="l11"> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l11"></a>
167 <span id="l12"> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></span><a href="#l12"></a>
167 <span id="l12"> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></span><a href="#l12"></a>
168 <span id="l13"> <span class="c"># It is important to yield *here* in order to stop the</span></span><a href="#l13"></a>
168 <span id="l13"> <span class="c"># It is important to yield *here* in order to stop the</span></span><a href="#l13"></a>
169 <span id="l14"> <span class="c"># infinite recursion.</span></span><a href="#l14"></a>
169 <span id="l14"> <span class="c"># infinite recursion.</span></span><a href="#l14"></a>
170 <span id="l15"> <span class="kn">yield</span> <span class="n">p</span></span><a href="#l15"></a>
170 <span id="l15"> <span class="kn">yield</span> <span class="n">p</span></span><a href="#l15"></a>
171 <span id="l16"> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></span><a href="#l16"></a>
171 <span id="l16"> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></span><a href="#l16"></a>
172 <span id="l17"> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l17"></a>
172 <span id="l17"> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l17"></a>
173 <span id="l18"> <span class="kn">yield</span> <span class="n">n</span></span><a href="#l18"></a>
173 <span id="l18"> <span class="kn">yield</span> <span class="n">n</span></span><a href="#l18"></a>
174 <span id="l19"></span><a href="#l19"></a>
174 <span id="l19"></span><a href="#l19"></a>
175 <span id="l20"> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></span><a href="#l20"></a>
175 <span id="l20"> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></span><a href="#l20"></a>
176 <span id="l21"> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></span><a href="#l21"></a>
176 <span id="l21"> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></span><a href="#l21"></a>
177 <span id="l22"></span><a href="#l22"></a>
177 <span id="l22"></span><a href="#l22"></a>
178 <span id="l23"><span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></span><a href="#l23"></a>
178 <span id="l23"><span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></span><a href="#l23"></a>
179 <span id="l24"> <span class="kn">import</span> <span class="nn">sys</span></span><a href="#l24"></a>
179 <span id="l24"> <span class="kn">import</span> <span class="nn">sys</span></span><a href="#l24"></a>
180 <span id="l25"> <span class="kn">try</span><span class="p">:</span></span><a href="#l25"></a>
180 <span id="l25"> <span class="kn">try</span><span class="p">:</span></span><a href="#l25"></a>
181 <span id="l26"> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></span><a href="#l26"></a>
181 <span id="l26"> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></span><a href="#l26"></a>
182 <span id="l27"> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></span><a href="#l27"></a>
182 <span id="l27"> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></span><a href="#l27"></a>
183 <span id="l28"> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></span><a href="#l28"></a>
183 <span id="l28"> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></span><a href="#l28"></a>
184 <span id="l29"> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></span><a href="#l29"></a>
184 <span id="l29"> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></span><a href="#l29"></a>
185 <span id="l30"> <span class="kn">print</span> <span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></span><a href="#l30"></a>
185 <span id="l30"> <span class="kn">print</span> <span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></span><a href="#l30"></a>
186 <span id="l31"></span><a href="#l31"></a>
186 <span id="l31"></span><a href="#l31"></a>
187 </pre>
187 </pre>
188 </div>
188 </div>
189
189
190 <script type="text/javascript" src="/static/followlines.js"></script>
190 <script type="text/javascript" src="/static/followlines.js"></script>
191
191
192 </div>
192 </div>
193 </div>
193 </div>
194
194
195
195
196
196
197 </body>
197 </body>
198 </html>
198 </html>
199
199
200
200
201 hgweb fileannotate, html
201 hgweb fileannotate, html
202
202
203 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py') | filterhtml
203 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py') | filterhtml
204 200 Script output follows
204 200 Script output follows
205
205
206 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
206 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
207 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
207 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
208 <head>
208 <head>
209 <link rel="icon" href="/static/hgicon.png" type="image/png" />
209 <link rel="icon" href="/static/hgicon.png" type="image/png" />
210 <meta name="robots" content="index, nofollow" />
210 <meta name="robots" content="index, nofollow" />
211 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
211 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
212 <script type="text/javascript" src="/static/mercurial.js"></script>
212 <script type="text/javascript" src="/static/mercurial.js"></script>
213
213
214 <link rel="stylesheet" href="/highlightcss" type="text/css" />
214 <link rel="stylesheet" href="/highlightcss" type="text/css" />
215 <title>test: primes.py annotate</title>
215 <title>test: primes.py annotate</title>
216 </head>
216 </head>
217 <body>
217 <body>
218
218
219 <div class="container">
219 <div class="container">
220 <div class="menu">
220 <div class="menu">
221 <div class="logo">
221 <div class="logo">
222 <a href="https://mercurial-scm.org/">
222 <a href="https://mercurial-scm.org/">
223 <img src="/static/hglogo.png" alt="mercurial" /></a>
223 <img src="/static/hglogo.png" alt="mercurial" /></a>
224 </div>
224 </div>
225 <ul>
225 <ul>
226 <li><a href="/shortlog/tip">log</a></li>
226 <li><a href="/shortlog/tip">log</a></li>
227 <li><a href="/graph/tip">graph</a></li>
227 <li><a href="/graph/tip">graph</a></li>
228 <li><a href="/tags">tags</a></li>
228 <li><a href="/tags">tags</a></li>
229 <li><a href="/bookmarks">bookmarks</a></li>
229 <li><a href="/bookmarks">bookmarks</a></li>
230 <li><a href="/branches">branches</a></li>
230 <li><a href="/branches">branches</a></li>
231 </ul>
231 </ul>
232
232
233 <ul>
233 <ul>
234 <li><a href="/rev/tip">changeset</a></li>
234 <li><a href="/rev/tip">changeset</a></li>
235 <li><a href="/file/tip/">browse</a></li>
235 <li><a href="/file/tip/">browse</a></li>
236 </ul>
236 </ul>
237 <ul>
237 <ul>
238 <li><a href="/file/tip/primes.py">file</a></li>
238 <li><a href="/file/tip/primes.py">file</a></li>
239 <li><a href="/file/tip/primes.py">latest</a></li>
239 <li><a href="/file/tip/primes.py">latest</a></li>
240 <li><a href="/diff/tip/primes.py">diff</a></li>
240 <li><a href="/diff/tip/primes.py">diff</a></li>
241 <li><a href="/comparison/tip/primes.py">comparison</a></li>
241 <li><a href="/comparison/tip/primes.py">comparison</a></li>
242 <li class="active">annotate</li>
242 <li class="active">annotate</li>
243 <li><a href="/log/tip/primes.py">file log</a></li>
243 <li><a href="/log/tip/primes.py">file log</a></li>
244 <li><a href="/raw-file/tip/primes.py">raw</a></li>
244 <li><a href="/raw-file/tip/primes.py">raw</a></li>
245 </ul>
245 </ul>
246 <ul>
246 <ul>
247 <li><a href="/help">help</a></li>
247 <li><a href="/help">help</a></li>
248 </ul>
248 </ul>
249 </div>
249 </div>
250
250
251 <div class="main">
251 <div class="main">
252 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
252 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
253 <h3>
253 <h3>
254 annotate primes.py @ 0:<a href="/rev/1af356141006">1af356141006</a>
254 annotate primes.py @ 0:<a href="/rev/1af356141006">1af356141006</a>
255 <span class="tag">tip</span>
255 <span class="tag">tip</span>
256 </h3>
256 </h3>
257
257
258
258
259 <form class="search" action="/log">
259 <form class="search" action="/log">
260
260
261 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
261 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
262 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
262 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
263 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
263 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
264 </form>
264 </form>
265
265
266 <div class="description">a</div>
266 <div class="description">a</div>
267
267
268 <table id="changesetEntry">
268 <table id="changesetEntry">
269 <tr>
269 <tr>
270 <th class="author">author</th>
270 <th class="author">author</th>
271 <td class="author">&#116;&#101;&#115;&#116;</td>
271 <td class="author">&#116;&#101;&#115;&#116;</td>
272 </tr>
272 </tr>
273 <tr>
273 <tr>
274 <th class="date">date</th>
274 <th class="date">date</th>
275 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
275 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
276 </tr>
276 </tr>
277 <tr>
277 <tr>
278 <th class="author">parents</th>
278 <th class="author">parents</th>
279 <td class="author"></td>
279 <td class="author"></td>
280 </tr>
280 </tr>
281 <tr>
281 <tr>
282 <th class="author">children</th>
282 <th class="author">children</th>
283 <td class="author"></td>
283 <td class="author"></td>
284 </tr>
284 </tr>
285 </table>
285 </table>
286
286
287 <div class="overflow">
287 <div class="overflow">
288 <table class="bigtable">
288 <table class="bigtable">
289 <thead>
289 <thead>
290 <tr>
290 <tr>
291 <th class="annotate">rev</th>
291 <th class="annotate">rev</th>
292 <th class="line">&nbsp;&nbsp;line source</th>
292 <th class="line">&nbsp;&nbsp;line source</th>
293 </tr>
293 </tr>
294 </thead>
294 </thead>
295 <tbody class="stripes2">
295 <tbody class="stripes2 sourcelines"
296 data-logurl="/log/tip/primes.py"
297 data-selectabletag="TR"
298 data-ishead="1">
296
299
297 <tr id="l1" class="thisrev">
300 <tr id="l1" class="thisrev">
298 <td class="annotate parity0">
301 <td class="annotate parity0">
299 <a href="/annotate/1af356141006/primes.py#l1">
302 <a href="/annotate/1af356141006/primes.py#l1">
300 0
303 0
301 </a>
304 </a>
302 <div class="annotate-info">
305 <div class="annotate-info">
303 <div>
306 <div>
304 <a href="/annotate/1af356141006/primes.py#l1">
307 <a href="/annotate/1af356141006/primes.py#l1">
305 1af356141006</a>
308 1af356141006</a>
306 a
309 a
307 </div>
310 </div>
308 <div><em>&#116;&#101;&#115;&#116;</em></div>
311 <div><em>&#116;&#101;&#115;&#116;</em></div>
309 <div>parents: </div>
312 <div>parents: </div>
310 <a href="/diff/1af356141006/primes.py">diff</a>
313 <a href="/diff/1af356141006/primes.py">diff</a>
311 <a href="/rev/1af356141006">changeset</a>
314 <a href="/rev/1af356141006">changeset</a>
312 </div>
315 </div>
313 </td>
316 </td>
314 <td class="source"><a href="#l1"> 1</a> <span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></td>
317 <td class="source"><a href="#l1"> 1</a> <span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></td>
315 </tr>
318 </tr>
316 <tr id="l2" class="thisrev">
319 <tr id="l2" class="thisrev">
317 <td class="annotate parity0">
320 <td class="annotate parity0">
318
321
319 <div class="annotate-info">
322 <div class="annotate-info">
320 <div>
323 <div>
321 <a href="/annotate/1af356141006/primes.py#l2">
324 <a href="/annotate/1af356141006/primes.py#l2">
322 1af356141006</a>
325 1af356141006</a>
323 a
326 a
324 </div>
327 </div>
325 <div><em>&#116;&#101;&#115;&#116;</em></div>
328 <div><em>&#116;&#101;&#115;&#116;</em></div>
326 <div>parents: </div>
329 <div>parents: </div>
327 <a href="/diff/1af356141006/primes.py">diff</a>
330 <a href="/diff/1af356141006/primes.py">diff</a>
328 <a href="/rev/1af356141006">changeset</a>
331 <a href="/rev/1af356141006">changeset</a>
329 </div>
332 </div>
330 </td>
333 </td>
331 <td class="source"><a href="#l2"> 2</a> </td>
334 <td class="source"><a href="#l2"> 2</a> </td>
332 </tr>
335 </tr>
333 <tr id="l3" class="thisrev">
336 <tr id="l3" class="thisrev">
334 <td class="annotate parity0">
337 <td class="annotate parity0">
335
338
336 <div class="annotate-info">
339 <div class="annotate-info">
337 <div>
340 <div>
338 <a href="/annotate/1af356141006/primes.py#l3">
341 <a href="/annotate/1af356141006/primes.py#l3">
339 1af356141006</a>
342 1af356141006</a>
340 a
343 a
341 </div>
344 </div>
342 <div><em>&#116;&#101;&#115;&#116;</em></div>
345 <div><em>&#116;&#101;&#115;&#116;</em></div>
343 <div>parents: </div>
346 <div>parents: </div>
344 <a href="/diff/1af356141006/primes.py">diff</a>
347 <a href="/diff/1af356141006/primes.py">diff</a>
345 <a href="/rev/1af356141006">changeset</a>
348 <a href="/rev/1af356141006">changeset</a>
346 </div>
349 </div>
347 </td>
350 </td>
348 <td class="source"><a href="#l3"> 3</a> <span class="sd">primes = 2 : sieve [3, 5..]</span></td>
351 <td class="source"><a href="#l3"> 3</a> <span class="sd">primes = 2 : sieve [3, 5..]</span></td>
349 </tr>
352 </tr>
350 <tr id="l4" class="thisrev">
353 <tr id="l4" class="thisrev">
351 <td class="annotate parity0">
354 <td class="annotate parity0">
352
355
353 <div class="annotate-info">
356 <div class="annotate-info">
354 <div>
357 <div>
355 <a href="/annotate/1af356141006/primes.py#l4">
358 <a href="/annotate/1af356141006/primes.py#l4">
356 1af356141006</a>
359 1af356141006</a>
357 a
360 a
358 </div>
361 </div>
359 <div><em>&#116;&#101;&#115;&#116;</em></div>
362 <div><em>&#116;&#101;&#115;&#116;</em></div>
360 <div>parents: </div>
363 <div>parents: </div>
361 <a href="/diff/1af356141006/primes.py">diff</a>
364 <a href="/diff/1af356141006/primes.py">diff</a>
362 <a href="/rev/1af356141006">changeset</a>
365 <a href="/rev/1af356141006">changeset</a>
363 </div>
366 </div>
364 </td>
367 </td>
365 <td class="source"><a href="#l4"> 4</a> <span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></td>
368 <td class="source"><a href="#l4"> 4</a> <span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></td>
366 </tr>
369 </tr>
367 <tr id="l5" class="thisrev">
370 <tr id="l5" class="thisrev">
368 <td class="annotate parity0">
371 <td class="annotate parity0">
369
372
370 <div class="annotate-info">
373 <div class="annotate-info">
371 <div>
374 <div>
372 <a href="/annotate/1af356141006/primes.py#l5">
375 <a href="/annotate/1af356141006/primes.py#l5">
373 1af356141006</a>
376 1af356141006</a>
374 a
377 a
375 </div>
378 </div>
376 <div><em>&#116;&#101;&#115;&#116;</em></div>
379 <div><em>&#116;&#101;&#115;&#116;</em></div>
377 <div>parents: </div>
380 <div>parents: </div>
378 <a href="/diff/1af356141006/primes.py">diff</a>
381 <a href="/diff/1af356141006/primes.py">diff</a>
379 <a href="/rev/1af356141006">changeset</a>
382 <a href="/rev/1af356141006">changeset</a>
380 </div>
383 </div>
381 </td>
384 </td>
382 <td class="source"><a href="#l5"> 5</a> <span class="sd">&quot;&quot;&quot;</span></td>
385 <td class="source"><a href="#l5"> 5</a> <span class="sd">&quot;&quot;&quot;</span></td>
383 </tr>
386 </tr>
384 <tr id="l6" class="thisrev">
387 <tr id="l6" class="thisrev">
385 <td class="annotate parity0">
388 <td class="annotate parity0">
386
389
387 <div class="annotate-info">
390 <div class="annotate-info">
388 <div>
391 <div>
389 <a href="/annotate/1af356141006/primes.py#l6">
392 <a href="/annotate/1af356141006/primes.py#l6">
390 1af356141006</a>
393 1af356141006</a>
391 a
394 a
392 </div>
395 </div>
393 <div><em>&#116;&#101;&#115;&#116;</em></div>
396 <div><em>&#116;&#101;&#115;&#116;</em></div>
394 <div>parents: </div>
397 <div>parents: </div>
395 <a href="/diff/1af356141006/primes.py">diff</a>
398 <a href="/diff/1af356141006/primes.py">diff</a>
396 <a href="/rev/1af356141006">changeset</a>
399 <a href="/rev/1af356141006">changeset</a>
397 </div>
400 </div>
398 </td>
401 </td>
399 <td class="source"><a href="#l6"> 6</a> </td>
402 <td class="source"><a href="#l6"> 6</a> </td>
400 </tr>
403 </tr>
401 <tr id="l7" class="thisrev">
404 <tr id="l7" class="thisrev">
402 <td class="annotate parity0">
405 <td class="annotate parity0">
403
406
404 <div class="annotate-info">
407 <div class="annotate-info">
405 <div>
408 <div>
406 <a href="/annotate/1af356141006/primes.py#l7">
409 <a href="/annotate/1af356141006/primes.py#l7">
407 1af356141006</a>
410 1af356141006</a>
408 a
411 a
409 </div>
412 </div>
410 <div><em>&#116;&#101;&#115;&#116;</em></div>
413 <div><em>&#116;&#101;&#115;&#116;</em></div>
411 <div>parents: </div>
414 <div>parents: </div>
412 <a href="/diff/1af356141006/primes.py">diff</a>
415 <a href="/diff/1af356141006/primes.py">diff</a>
413 <a href="/rev/1af356141006">changeset</a>
416 <a href="/rev/1af356141006">changeset</a>
414 </div>
417 </div>
415 </td>
418 </td>
416 <td class="source"><a href="#l7"> 7</a> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></td>
419 <td class="source"><a href="#l7"> 7</a> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></td>
417 </tr>
420 </tr>
418 <tr id="l8" class="thisrev">
421 <tr id="l8" class="thisrev">
419 <td class="annotate parity0">
422 <td class="annotate parity0">
420
423
421 <div class="annotate-info">
424 <div class="annotate-info">
422 <div>
425 <div>
423 <a href="/annotate/1af356141006/primes.py#l8">
426 <a href="/annotate/1af356141006/primes.py#l8">
424 1af356141006</a>
427 1af356141006</a>
425 a
428 a
426 </div>
429 </div>
427 <div><em>&#116;&#101;&#115;&#116;</em></div>
430 <div><em>&#116;&#101;&#115;&#116;</em></div>
428 <div>parents: </div>
431 <div>parents: </div>
429 <a href="/diff/1af356141006/primes.py">diff</a>
432 <a href="/diff/1af356141006/primes.py">diff</a>
430 <a href="/rev/1af356141006">changeset</a>
433 <a href="/rev/1af356141006">changeset</a>
431 </div>
434 </div>
432 </td>
435 </td>
433 <td class="source"><a href="#l8"> 8</a> </td>
436 <td class="source"><a href="#l8"> 8</a> </td>
434 </tr>
437 </tr>
435 <tr id="l9" class="thisrev">
438 <tr id="l9" class="thisrev">
436 <td class="annotate parity0">
439 <td class="annotate parity0">
437
440
438 <div class="annotate-info">
441 <div class="annotate-info">
439 <div>
442 <div>
440 <a href="/annotate/1af356141006/primes.py#l9">
443 <a href="/annotate/1af356141006/primes.py#l9">
441 1af356141006</a>
444 1af356141006</a>
442 a
445 a
443 </div>
446 </div>
444 <div><em>&#116;&#101;&#115;&#116;</em></div>
447 <div><em>&#116;&#101;&#115;&#116;</em></div>
445 <div>parents: </div>
448 <div>parents: </div>
446 <a href="/diff/1af356141006/primes.py">diff</a>
449 <a href="/diff/1af356141006/primes.py">diff</a>
447 <a href="/rev/1af356141006">changeset</a>
450 <a href="/rev/1af356141006">changeset</a>
448 </div>
451 </div>
449 </td>
452 </td>
450 <td class="source"><a href="#l9"> 9</a> <span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></td>
453 <td class="source"><a href="#l9"> 9</a> <span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></td>
451 </tr>
454 </tr>
452 <tr id="l10" class="thisrev">
455 <tr id="l10" class="thisrev">
453 <td class="annotate parity0">
456 <td class="annotate parity0">
454
457
455 <div class="annotate-info">
458 <div class="annotate-info">
456 <div>
459 <div>
457 <a href="/annotate/1af356141006/primes.py#l10">
460 <a href="/annotate/1af356141006/primes.py#l10">
458 1af356141006</a>
461 1af356141006</a>
459 a
462 a
460 </div>
463 </div>
461 <div><em>&#116;&#101;&#115;&#116;</em></div>
464 <div><em>&#116;&#101;&#115;&#116;</em></div>
462 <div>parents: </div>
465 <div>parents: </div>
463 <a href="/diff/1af356141006/primes.py">diff</a>
466 <a href="/diff/1af356141006/primes.py">diff</a>
464 <a href="/rev/1af356141006">changeset</a>
467 <a href="/rev/1af356141006">changeset</a>
465 </div>
468 </div>
466 </td>
469 </td>
467 <td class="source"><a href="#l10"> 10</a> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></td>
470 <td class="source"><a href="#l10"> 10</a> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></td>
468 </tr>
471 </tr>
469 <tr id="l11" class="thisrev">
472 <tr id="l11" class="thisrev">
470 <td class="annotate parity0">
473 <td class="annotate parity0">
471
474
472 <div class="annotate-info">
475 <div class="annotate-info">
473 <div>
476 <div>
474 <a href="/annotate/1af356141006/primes.py#l11">
477 <a href="/annotate/1af356141006/primes.py#l11">
475 1af356141006</a>
478 1af356141006</a>
476 a
479 a
477 </div>
480 </div>
478 <div><em>&#116;&#101;&#115;&#116;</em></div>
481 <div><em>&#116;&#101;&#115;&#116;</em></div>
479 <div>parents: </div>
482 <div>parents: </div>
480 <a href="/diff/1af356141006/primes.py">diff</a>
483 <a href="/diff/1af356141006/primes.py">diff</a>
481 <a href="/rev/1af356141006">changeset</a>
484 <a href="/rev/1af356141006">changeset</a>
482 </div>
485 </div>
483 </td>
486 </td>
484 <td class="source"><a href="#l11"> 11</a> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
487 <td class="source"><a href="#l11"> 11</a> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
485 </tr>
488 </tr>
486 <tr id="l12" class="thisrev">
489 <tr id="l12" class="thisrev">
487 <td class="annotate parity0">
490 <td class="annotate parity0">
488
491
489 <div class="annotate-info">
492 <div class="annotate-info">
490 <div>
493 <div>
491 <a href="/annotate/1af356141006/primes.py#l12">
494 <a href="/annotate/1af356141006/primes.py#l12">
492 1af356141006</a>
495 1af356141006</a>
493 a
496 a
494 </div>
497 </div>
495 <div><em>&#116;&#101;&#115;&#116;</em></div>
498 <div><em>&#116;&#101;&#115;&#116;</em></div>
496 <div>parents: </div>
499 <div>parents: </div>
497 <a href="/diff/1af356141006/primes.py">diff</a>
500 <a href="/diff/1af356141006/primes.py">diff</a>
498 <a href="/rev/1af356141006">changeset</a>
501 <a href="/rev/1af356141006">changeset</a>
499 </div>
502 </div>
500 </td>
503 </td>
501 <td class="source"><a href="#l12"> 12</a> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></td>
504 <td class="source"><a href="#l12"> 12</a> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></td>
502 </tr>
505 </tr>
503 <tr id="l13" class="thisrev">
506 <tr id="l13" class="thisrev">
504 <td class="annotate parity0">
507 <td class="annotate parity0">
505
508
506 <div class="annotate-info">
509 <div class="annotate-info">
507 <div>
510 <div>
508 <a href="/annotate/1af356141006/primes.py#l13">
511 <a href="/annotate/1af356141006/primes.py#l13">
509 1af356141006</a>
512 1af356141006</a>
510 a
513 a
511 </div>
514 </div>
512 <div><em>&#116;&#101;&#115;&#116;</em></div>
515 <div><em>&#116;&#101;&#115;&#116;</em></div>
513 <div>parents: </div>
516 <div>parents: </div>
514 <a href="/diff/1af356141006/primes.py">diff</a>
517 <a href="/diff/1af356141006/primes.py">diff</a>
515 <a href="/rev/1af356141006">changeset</a>
518 <a href="/rev/1af356141006">changeset</a>
516 </div>
519 </div>
517 </td>
520 </td>
518 <td class="source"><a href="#l13"> 13</a> <span class="c"># It is important to yield *here* in order to stop the</span></td>
521 <td class="source"><a href="#l13"> 13</a> <span class="c"># It is important to yield *here* in order to stop the</span></td>
519 </tr>
522 </tr>
520 <tr id="l14" class="thisrev">
523 <tr id="l14" class="thisrev">
521 <td class="annotate parity0">
524 <td class="annotate parity0">
522
525
523 <div class="annotate-info">
526 <div class="annotate-info">
524 <div>
527 <div>
525 <a href="/annotate/1af356141006/primes.py#l14">
528 <a href="/annotate/1af356141006/primes.py#l14">
526 1af356141006</a>
529 1af356141006</a>
527 a
530 a
528 </div>
531 </div>
529 <div><em>&#116;&#101;&#115;&#116;</em></div>
532 <div><em>&#116;&#101;&#115;&#116;</em></div>
530 <div>parents: </div>
533 <div>parents: </div>
531 <a href="/diff/1af356141006/primes.py">diff</a>
534 <a href="/diff/1af356141006/primes.py">diff</a>
532 <a href="/rev/1af356141006">changeset</a>
535 <a href="/rev/1af356141006">changeset</a>
533 </div>
536 </div>
534 </td>
537 </td>
535 <td class="source"><a href="#l14"> 14</a> <span class="c"># infinite recursion.</span></td>
538 <td class="source"><a href="#l14"> 14</a> <span class="c"># infinite recursion.</span></td>
536 </tr>
539 </tr>
537 <tr id="l15" class="thisrev">
540 <tr id="l15" class="thisrev">
538 <td class="annotate parity0">
541 <td class="annotate parity0">
539
542
540 <div class="annotate-info">
543 <div class="annotate-info">
541 <div>
544 <div>
542 <a href="/annotate/1af356141006/primes.py#l15">
545 <a href="/annotate/1af356141006/primes.py#l15">
543 1af356141006</a>
546 1af356141006</a>
544 a
547 a
545 </div>
548 </div>
546 <div><em>&#116;&#101;&#115;&#116;</em></div>
549 <div><em>&#116;&#101;&#115;&#116;</em></div>
547 <div>parents: </div>
550 <div>parents: </div>
548 <a href="/diff/1af356141006/primes.py">diff</a>
551 <a href="/diff/1af356141006/primes.py">diff</a>
549 <a href="/rev/1af356141006">changeset</a>
552 <a href="/rev/1af356141006">changeset</a>
550 </div>
553 </div>
551 </td>
554 </td>
552 <td class="source"><a href="#l15"> 15</a> <span class="kn">yield</span> <span class="n">p</span></td>
555 <td class="source"><a href="#l15"> 15</a> <span class="kn">yield</span> <span class="n">p</span></td>
553 </tr>
556 </tr>
554 <tr id="l16" class="thisrev">
557 <tr id="l16" class="thisrev">
555 <td class="annotate parity0">
558 <td class="annotate parity0">
556
559
557 <div class="annotate-info">
560 <div class="annotate-info">
558 <div>
561 <div>
559 <a href="/annotate/1af356141006/primes.py#l16">
562 <a href="/annotate/1af356141006/primes.py#l16">
560 1af356141006</a>
563 1af356141006</a>
561 a
564 a
562 </div>
565 </div>
563 <div><em>&#116;&#101;&#115;&#116;</em></div>
566 <div><em>&#116;&#101;&#115;&#116;</em></div>
564 <div>parents: </div>
567 <div>parents: </div>
565 <a href="/diff/1af356141006/primes.py">diff</a>
568 <a href="/diff/1af356141006/primes.py">diff</a>
566 <a href="/rev/1af356141006">changeset</a>
569 <a href="/rev/1af356141006">changeset</a>
567 </div>
570 </div>
568 </td>
571 </td>
569 <td class="source"><a href="#l16"> 16</a> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></td>
572 <td class="source"><a href="#l16"> 16</a> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></td>
570 </tr>
573 </tr>
571 <tr id="l17" class="thisrev">
574 <tr id="l17" class="thisrev">
572 <td class="annotate parity0">
575 <td class="annotate parity0">
573
576
574 <div class="annotate-info">
577 <div class="annotate-info">
575 <div>
578 <div>
576 <a href="/annotate/1af356141006/primes.py#l17">
579 <a href="/annotate/1af356141006/primes.py#l17">
577 1af356141006</a>
580 1af356141006</a>
578 a
581 a
579 </div>
582 </div>
580 <div><em>&#116;&#101;&#115;&#116;</em></div>
583 <div><em>&#116;&#101;&#115;&#116;</em></div>
581 <div>parents: </div>
584 <div>parents: </div>
582 <a href="/diff/1af356141006/primes.py">diff</a>
585 <a href="/diff/1af356141006/primes.py">diff</a>
583 <a href="/rev/1af356141006">changeset</a>
586 <a href="/rev/1af356141006">changeset</a>
584 </div>
587 </div>
585 </td>
588 </td>
586 <td class="source"><a href="#l17"> 17</a> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
589 <td class="source"><a href="#l17"> 17</a> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
587 </tr>
590 </tr>
588 <tr id="l18" class="thisrev">
591 <tr id="l18" class="thisrev">
589 <td class="annotate parity0">
592 <td class="annotate parity0">
590
593
591 <div class="annotate-info">
594 <div class="annotate-info">
592 <div>
595 <div>
593 <a href="/annotate/1af356141006/primes.py#l18">
596 <a href="/annotate/1af356141006/primes.py#l18">
594 1af356141006</a>
597 1af356141006</a>
595 a
598 a
596 </div>
599 </div>
597 <div><em>&#116;&#101;&#115;&#116;</em></div>
600 <div><em>&#116;&#101;&#115;&#116;</em></div>
598 <div>parents: </div>
601 <div>parents: </div>
599 <a href="/diff/1af356141006/primes.py">diff</a>
602 <a href="/diff/1af356141006/primes.py">diff</a>
600 <a href="/rev/1af356141006">changeset</a>
603 <a href="/rev/1af356141006">changeset</a>
601 </div>
604 </div>
602 </td>
605 </td>
603 <td class="source"><a href="#l18"> 18</a> <span class="kn">yield</span> <span class="n">n</span></td>
606 <td class="source"><a href="#l18"> 18</a> <span class="kn">yield</span> <span class="n">n</span></td>
604 </tr>
607 </tr>
605 <tr id="l19" class="thisrev">
608 <tr id="l19" class="thisrev">
606 <td class="annotate parity0">
609 <td class="annotate parity0">
607
610
608 <div class="annotate-info">
611 <div class="annotate-info">
609 <div>
612 <div>
610 <a href="/annotate/1af356141006/primes.py#l19">
613 <a href="/annotate/1af356141006/primes.py#l19">
611 1af356141006</a>
614 1af356141006</a>
612 a
615 a
613 </div>
616 </div>
614 <div><em>&#116;&#101;&#115;&#116;</em></div>
617 <div><em>&#116;&#101;&#115;&#116;</em></div>
615 <div>parents: </div>
618 <div>parents: </div>
616 <a href="/diff/1af356141006/primes.py">diff</a>
619 <a href="/diff/1af356141006/primes.py">diff</a>
617 <a href="/rev/1af356141006">changeset</a>
620 <a href="/rev/1af356141006">changeset</a>
618 </div>
621 </div>
619 </td>
622 </td>
620 <td class="source"><a href="#l19"> 19</a> </td>
623 <td class="source"><a href="#l19"> 19</a> </td>
621 </tr>
624 </tr>
622 <tr id="l20" class="thisrev">
625 <tr id="l20" class="thisrev">
623 <td class="annotate parity0">
626 <td class="annotate parity0">
624
627
625 <div class="annotate-info">
628 <div class="annotate-info">
626 <div>
629 <div>
627 <a href="/annotate/1af356141006/primes.py#l20">
630 <a href="/annotate/1af356141006/primes.py#l20">
628 1af356141006</a>
631 1af356141006</a>
629 a
632 a
630 </div>
633 </div>
631 <div><em>&#116;&#101;&#115;&#116;</em></div>
634 <div><em>&#116;&#101;&#115;&#116;</em></div>
632 <div>parents: </div>
635 <div>parents: </div>
633 <a href="/diff/1af356141006/primes.py">diff</a>
636 <a href="/diff/1af356141006/primes.py">diff</a>
634 <a href="/rev/1af356141006">changeset</a>
637 <a href="/rev/1af356141006">changeset</a>
635 </div>
638 </div>
636 </td>
639 </td>
637 <td class="source"><a href="#l20"> 20</a> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></td>
640 <td class="source"><a href="#l20"> 20</a> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></td>
638 </tr>
641 </tr>
639 <tr id="l21" class="thisrev">
642 <tr id="l21" class="thisrev">
640 <td class="annotate parity0">
643 <td class="annotate parity0">
641
644
642 <div class="annotate-info">
645 <div class="annotate-info">
643 <div>
646 <div>
644 <a href="/annotate/1af356141006/primes.py#l21">
647 <a href="/annotate/1af356141006/primes.py#l21">
645 1af356141006</a>
648 1af356141006</a>
646 a
649 a
647 </div>
650 </div>
648 <div><em>&#116;&#101;&#115;&#116;</em></div>
651 <div><em>&#116;&#101;&#115;&#116;</em></div>
649 <div>parents: </div>
652 <div>parents: </div>
650 <a href="/diff/1af356141006/primes.py">diff</a>
653 <a href="/diff/1af356141006/primes.py">diff</a>
651 <a href="/rev/1af356141006">changeset</a>
654 <a href="/rev/1af356141006">changeset</a>
652 </div>
655 </div>
653 </td>
656 </td>
654 <td class="source"><a href="#l21"> 21</a> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></td>
657 <td class="source"><a href="#l21"> 21</a> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></td>
655 </tr>
658 </tr>
656 <tr id="l22" class="thisrev">
659 <tr id="l22" class="thisrev">
657 <td class="annotate parity0">
660 <td class="annotate parity0">
658
661
659 <div class="annotate-info">
662 <div class="annotate-info">
660 <div>
663 <div>
661 <a href="/annotate/1af356141006/primes.py#l22">
664 <a href="/annotate/1af356141006/primes.py#l22">
662 1af356141006</a>
665 1af356141006</a>
663 a
666 a
664 </div>
667 </div>
665 <div><em>&#116;&#101;&#115;&#116;</em></div>
668 <div><em>&#116;&#101;&#115;&#116;</em></div>
666 <div>parents: </div>
669 <div>parents: </div>
667 <a href="/diff/1af356141006/primes.py">diff</a>
670 <a href="/diff/1af356141006/primes.py">diff</a>
668 <a href="/rev/1af356141006">changeset</a>
671 <a href="/rev/1af356141006">changeset</a>
669 </div>
672 </div>
670 </td>
673 </td>
671 <td class="source"><a href="#l22"> 22</a> </td>
674 <td class="source"><a href="#l22"> 22</a> </td>
672 </tr>
675 </tr>
673 <tr id="l23" class="thisrev">
676 <tr id="l23" class="thisrev">
674 <td class="annotate parity0">
677 <td class="annotate parity0">
675
678
676 <div class="annotate-info">
679 <div class="annotate-info">
677 <div>
680 <div>
678 <a href="/annotate/1af356141006/primes.py#l23">
681 <a href="/annotate/1af356141006/primes.py#l23">
679 1af356141006</a>
682 1af356141006</a>
680 a
683 a
681 </div>
684 </div>
682 <div><em>&#116;&#101;&#115;&#116;</em></div>
685 <div><em>&#116;&#101;&#115;&#116;</em></div>
683 <div>parents: </div>
686 <div>parents: </div>
684 <a href="/diff/1af356141006/primes.py">diff</a>
687 <a href="/diff/1af356141006/primes.py">diff</a>
685 <a href="/rev/1af356141006">changeset</a>
688 <a href="/rev/1af356141006">changeset</a>
686 </div>
689 </div>
687 </td>
690 </td>
688 <td class="source"><a href="#l23"> 23</a> <span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></td>
691 <td class="source"><a href="#l23"> 23</a> <span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></td>
689 </tr>
692 </tr>
690 <tr id="l24" class="thisrev">
693 <tr id="l24" class="thisrev">
691 <td class="annotate parity0">
694 <td class="annotate parity0">
692
695
693 <div class="annotate-info">
696 <div class="annotate-info">
694 <div>
697 <div>
695 <a href="/annotate/1af356141006/primes.py#l24">
698 <a href="/annotate/1af356141006/primes.py#l24">
696 1af356141006</a>
699 1af356141006</a>
697 a
700 a
698 </div>
701 </div>
699 <div><em>&#116;&#101;&#115;&#116;</em></div>
702 <div><em>&#116;&#101;&#115;&#116;</em></div>
700 <div>parents: </div>
703 <div>parents: </div>
701 <a href="/diff/1af356141006/primes.py">diff</a>
704 <a href="/diff/1af356141006/primes.py">diff</a>
702 <a href="/rev/1af356141006">changeset</a>
705 <a href="/rev/1af356141006">changeset</a>
703 </div>
706 </div>
704 </td>
707 </td>
705 <td class="source"><a href="#l24"> 24</a> <span class="kn">import</span> <span class="nn">sys</span></td>
708 <td class="source"><a href="#l24"> 24</a> <span class="kn">import</span> <span class="nn">sys</span></td>
706 </tr>
709 </tr>
707 <tr id="l25" class="thisrev">
710 <tr id="l25" class="thisrev">
708 <td class="annotate parity0">
711 <td class="annotate parity0">
709
712
710 <div class="annotate-info">
713 <div class="annotate-info">
711 <div>
714 <div>
712 <a href="/annotate/1af356141006/primes.py#l25">
715 <a href="/annotate/1af356141006/primes.py#l25">
713 1af356141006</a>
716 1af356141006</a>
714 a
717 a
715 </div>
718 </div>
716 <div><em>&#116;&#101;&#115;&#116;</em></div>
719 <div><em>&#116;&#101;&#115;&#116;</em></div>
717 <div>parents: </div>
720 <div>parents: </div>
718 <a href="/diff/1af356141006/primes.py">diff</a>
721 <a href="/diff/1af356141006/primes.py">diff</a>
719 <a href="/rev/1af356141006">changeset</a>
722 <a href="/rev/1af356141006">changeset</a>
720 </div>
723 </div>
721 </td>
724 </td>
722 <td class="source"><a href="#l25"> 25</a> <span class="kn">try</span><span class="p">:</span></td>
725 <td class="source"><a href="#l25"> 25</a> <span class="kn">try</span><span class="p">:</span></td>
723 </tr>
726 </tr>
724 <tr id="l26" class="thisrev">
727 <tr id="l26" class="thisrev">
725 <td class="annotate parity0">
728 <td class="annotate parity0">
726
729
727 <div class="annotate-info">
730 <div class="annotate-info">
728 <div>
731 <div>
729 <a href="/annotate/1af356141006/primes.py#l26">
732 <a href="/annotate/1af356141006/primes.py#l26">
730 1af356141006</a>
733 1af356141006</a>
731 a
734 a
732 </div>
735 </div>
733 <div><em>&#116;&#101;&#115;&#116;</em></div>
736 <div><em>&#116;&#101;&#115;&#116;</em></div>
734 <div>parents: </div>
737 <div>parents: </div>
735 <a href="/diff/1af356141006/primes.py">diff</a>
738 <a href="/diff/1af356141006/primes.py">diff</a>
736 <a href="/rev/1af356141006">changeset</a>
739 <a href="/rev/1af356141006">changeset</a>
737 </div>
740 </div>
738 </td>
741 </td>
739 <td class="source"><a href="#l26"> 26</a> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></td>
742 <td class="source"><a href="#l26"> 26</a> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></td>
740 </tr>
743 </tr>
741 <tr id="l27" class="thisrev">
744 <tr id="l27" class="thisrev">
742 <td class="annotate parity0">
745 <td class="annotate parity0">
743
746
744 <div class="annotate-info">
747 <div class="annotate-info">
745 <div>
748 <div>
746 <a href="/annotate/1af356141006/primes.py#l27">
749 <a href="/annotate/1af356141006/primes.py#l27">
747 1af356141006</a>
750 1af356141006</a>
748 a
751 a
749 </div>
752 </div>
750 <div><em>&#116;&#101;&#115;&#116;</em></div>
753 <div><em>&#116;&#101;&#115;&#116;</em></div>
751 <div>parents: </div>
754 <div>parents: </div>
752 <a href="/diff/1af356141006/primes.py">diff</a>
755 <a href="/diff/1af356141006/primes.py">diff</a>
753 <a href="/rev/1af356141006">changeset</a>
756 <a href="/rev/1af356141006">changeset</a>
754 </div>
757 </div>
755 </td>
758 </td>
756 <td class="source"><a href="#l27"> 27</a> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></td>
759 <td class="source"><a href="#l27"> 27</a> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></td>
757 </tr>
760 </tr>
758 <tr id="l28" class="thisrev">
761 <tr id="l28" class="thisrev">
759 <td class="annotate parity0">
762 <td class="annotate parity0">
760
763
761 <div class="annotate-info">
764 <div class="annotate-info">
762 <div>
765 <div>
763 <a href="/annotate/1af356141006/primes.py#l28">
766 <a href="/annotate/1af356141006/primes.py#l28">
764 1af356141006</a>
767 1af356141006</a>
765 a
768 a
766 </div>
769 </div>
767 <div><em>&#116;&#101;&#115;&#116;</em></div>
770 <div><em>&#116;&#101;&#115;&#116;</em></div>
768 <div>parents: </div>
771 <div>parents: </div>
769 <a href="/diff/1af356141006/primes.py">diff</a>
772 <a href="/diff/1af356141006/primes.py">diff</a>
770 <a href="/rev/1af356141006">changeset</a>
773 <a href="/rev/1af356141006">changeset</a>
771 </div>
774 </div>
772 </td>
775 </td>
773 <td class="source"><a href="#l28"> 28</a> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></td>
776 <td class="source"><a href="#l28"> 28</a> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></td>
774 </tr>
777 </tr>
775 <tr id="l29" class="thisrev">
778 <tr id="l29" class="thisrev">
776 <td class="annotate parity0">
779 <td class="annotate parity0">
777
780
778 <div class="annotate-info">
781 <div class="annotate-info">
779 <div>
782 <div>
780 <a href="/annotate/1af356141006/primes.py#l29">
783 <a href="/annotate/1af356141006/primes.py#l29">
781 1af356141006</a>
784 1af356141006</a>
782 a
785 a
783 </div>
786 </div>
784 <div><em>&#116;&#101;&#115;&#116;</em></div>
787 <div><em>&#116;&#101;&#115;&#116;</em></div>
785 <div>parents: </div>
788 <div>parents: </div>
786 <a href="/diff/1af356141006/primes.py">diff</a>
789 <a href="/diff/1af356141006/primes.py">diff</a>
787 <a href="/rev/1af356141006">changeset</a>
790 <a href="/rev/1af356141006">changeset</a>
788 </div>
791 </div>
789 </td>
792 </td>
790 <td class="source"><a href="#l29"> 29</a> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></td>
793 <td class="source"><a href="#l29"> 29</a> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></td>
791 </tr>
794 </tr>
792 <tr id="l30" class="thisrev">
795 <tr id="l30" class="thisrev">
793 <td class="annotate parity0">
796 <td class="annotate parity0">
794
797
795 <div class="annotate-info">
798 <div class="annotate-info">
796 <div>
799 <div>
797 <a href="/annotate/1af356141006/primes.py#l30">
800 <a href="/annotate/1af356141006/primes.py#l30">
798 1af356141006</a>
801 1af356141006</a>
799 a
802 a
800 </div>
803 </div>
801 <div><em>&#116;&#101;&#115;&#116;</em></div>
804 <div><em>&#116;&#101;&#115;&#116;</em></div>
802 <div>parents: </div>
805 <div>parents: </div>
803 <a href="/diff/1af356141006/primes.py">diff</a>
806 <a href="/diff/1af356141006/primes.py">diff</a>
804 <a href="/rev/1af356141006">changeset</a>
807 <a href="/rev/1af356141006">changeset</a>
805 </div>
808 </div>
806 </td>
809 </td>
807 <td class="source"><a href="#l30"> 30</a> <span class="kn">print</span> <span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></td>
810 <td class="source"><a href="#l30"> 30</a> <span class="kn">print</span> <span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></td>
808 </tr>
811 </tr>
809 <tr id="l31" class="thisrev">
812 <tr id="l31" class="thisrev">
810 <td class="annotate parity0">
813 <td class="annotate parity0">
811
814
812 <div class="annotate-info">
815 <div class="annotate-info">
813 <div>
816 <div>
814 <a href="/annotate/1af356141006/primes.py#l31">
817 <a href="/annotate/1af356141006/primes.py#l31">
815 1af356141006</a>
818 1af356141006</a>
816 a
819 a
817 </div>
820 </div>
818 <div><em>&#116;&#101;&#115;&#116;</em></div>
821 <div><em>&#116;&#101;&#115;&#116;</em></div>
819 <div>parents: </div>
822 <div>parents: </div>
820 <a href="/diff/1af356141006/primes.py">diff</a>
823 <a href="/diff/1af356141006/primes.py">diff</a>
821 <a href="/rev/1af356141006">changeset</a>
824 <a href="/rev/1af356141006">changeset</a>
822 </div>
825 </div>
823 </td>
826 </td>
824 <td class="source"><a href="#l31"> 31</a> </td>
827 <td class="source"><a href="#l31"> 31</a> </td>
825 </tr>
828 </tr>
826 </tbody>
829 </tbody>
827 </table>
830 </table>
828 </div>
831 </div>
829 </div>
832 </div>
830 </div>
833 </div>
831
834
835 <script type="text/javascript" src="/static/followlines.js"></script>
836
832
837
833
838
834 </body>
839 </body>
835 </html>
840 </html>
836
841
837
842
838 hgweb fileannotate, raw
843 hgweb fileannotate, raw
839
844
840 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py?style=raw') \
845 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py?style=raw') \
841 > | sed "s/test@//" > a
846 > | sed "s/test@//" > a
842 $ echo "200 Script output follows" > b
847 $ echo "200 Script output follows" > b
843 $ echo "" >> b
848 $ echo "" >> b
844 $ echo "" >> b
849 $ echo "" >> b
845 $ hg annotate "primes.py" >> b
850 $ hg annotate "primes.py" >> b
846 $ echo "" >> b
851 $ echo "" >> b
847 $ echo "" >> b
852 $ echo "" >> b
848 $ echo "" >> b
853 $ echo "" >> b
849 $ echo "" >> b
854 $ echo "" >> b
850 $ cmp b a || diff -u b a
855 $ cmp b a || diff -u b a
851
856
852 hgweb filerevision, raw
857 hgweb filerevision, raw
853
858
854 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py?style=raw') \
859 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py?style=raw') \
855 > > a
860 > > a
856 $ echo "200 Script output follows" > b
861 $ echo "200 Script output follows" > b
857 $ echo "" >> b
862 $ echo "" >> b
858 $ hg cat primes.py >> b
863 $ hg cat primes.py >> b
859 $ cmp b a || diff -u b a
864 $ cmp b a || diff -u b a
860
865
861 hgweb highlightcss friendly
866 hgweb highlightcss friendly
862
867
863 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
868 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
864 $ head -n 4 out
869 $ head -n 4 out
865 200 Script output follows
870 200 Script output follows
866
871
867 /* pygments_style = friendly */
872 /* pygments_style = friendly */
868
873
869 $ rm out
874 $ rm out
870
875
871 errors encountered
876 errors encountered
872
877
873 $ cat errors.log
878 $ cat errors.log
874 $ killdaemons.py
879 $ killdaemons.py
875
880
876 Change the pygments style
881 Change the pygments style
877
882
878 $ cat > .hg/hgrc <<EOF
883 $ cat > .hg/hgrc <<EOF
879 > [web]
884 > [web]
880 > pygments_style = fruity
885 > pygments_style = fruity
881 > EOF
886 > EOF
882
887
883 hg serve again
888 hg serve again
884
889
885 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
890 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
886 $ cat hg.pid >> $DAEMON_PIDS
891 $ cat hg.pid >> $DAEMON_PIDS
887
892
888 hgweb highlightcss fruity
893 hgweb highlightcss fruity
889
894
890 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
895 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
891 $ head -n 4 out
896 $ head -n 4 out
892 200 Script output follows
897 200 Script output follows
893
898
894 /* pygments_style = fruity */
899 /* pygments_style = fruity */
895
900
896 $ rm out
901 $ rm out
897
902
898 errors encountered
903 errors encountered
899
904
900 $ cat errors.log
905 $ cat errors.log
901 $ killdaemons.py
906 $ killdaemons.py
902
907
903 only highlight C source files
908 only highlight C source files
904
909
905 $ cat > .hg/hgrc <<EOF
910 $ cat > .hg/hgrc <<EOF
906 > [web]
911 > [web]
907 > highlightfiles = **.c
912 > highlightfiles = **.c
908 > EOF
913 > EOF
909
914
910 hg serve again
915 hg serve again
911
916
912 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
917 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
913 $ cat hg.pid >> $DAEMON_PIDS
918 $ cat hg.pid >> $DAEMON_PIDS
914
919
915 test that fileset in highlightfiles works and primes.py is not highlighted
920 test that fileset in highlightfiles works and primes.py is not highlighted
916
921
917 $ get-with-headers.py localhost:$HGPORT 'file/tip/primes.py' | grep 'id="l11"'
922 $ get-with-headers.py localhost:$HGPORT 'file/tip/primes.py' | grep 'id="l11"'
918 <span id="l11"> def sieve(ns):</span><a href="#l11"></a>
923 <span id="l11"> def sieve(ns):</span><a href="#l11"></a>
919
924
920 errors encountered
925 errors encountered
921
926
922 $ cat errors.log
927 $ cat errors.log
923 $ cd ..
928 $ cd ..
924 $ hg init eucjp
929 $ hg init eucjp
925 $ cd eucjp
930 $ cd eucjp
926 $ $PYTHON -c 'print("\265\376")' >> eucjp.txt # Japanese kanji "Kyo"
931 $ $PYTHON -c 'print("\265\376")' >> eucjp.txt # Japanese kanji "Kyo"
927 $ hg ci -Ama
932 $ hg ci -Ama
928 adding eucjp.txt
933 adding eucjp.txt
929 $ hgserveget () {
934 $ hgserveget () {
930 > killdaemons.py
935 > killdaemons.py
931 > echo % HGENCODING="$1" hg serve
936 > echo % HGENCODING="$1" hg serve
932 > HGENCODING="$1" hg serve -p $HGPORT -d -n test --pid-file=hg.pid -E errors.log
937 > HGENCODING="$1" hg serve -p $HGPORT -d -n test --pid-file=hg.pid -E errors.log
933 > cat hg.pid >> $DAEMON_PIDS
938 > cat hg.pid >> $DAEMON_PIDS
934 >
939 >
935 > echo % hgweb filerevision, html
940 > echo % hgweb filerevision, html
936 > get-with-headers.py localhost:$HGPORT "file/tip/$2" \
941 > get-with-headers.py localhost:$HGPORT "file/tip/$2" \
937 > | grep '<div class="parity0 source">'
942 > | grep '<div class="parity0 source">'
938 > echo % errors encountered
943 > echo % errors encountered
939 > cat errors.log
944 > cat errors.log
940 > }
945 > }
941 $ hgserveget euc-jp eucjp.txt
946 $ hgserveget euc-jp eucjp.txt
942 % HGENCODING=euc-jp hg serve
947 % HGENCODING=euc-jp hg serve
943 % hgweb filerevision, html
948 % hgweb filerevision, html
944 % errors encountered
949 % errors encountered
945 $ hgserveget utf-8 eucjp.txt
950 $ hgserveget utf-8 eucjp.txt
946 % HGENCODING=utf-8 hg serve
951 % HGENCODING=utf-8 hg serve
947 % hgweb filerevision, html
952 % hgweb filerevision, html
948 % errors encountered
953 % errors encountered
949 $ hgserveget us-ascii eucjp.txt
954 $ hgserveget us-ascii eucjp.txt
950 % HGENCODING=us-ascii hg serve
955 % HGENCODING=us-ascii hg serve
951 % hgweb filerevision, html
956 % hgweb filerevision, html
952 % errors encountered
957 % errors encountered
953
958
954 We attempt to highlight unknown files by default
959 We attempt to highlight unknown files by default
955
960
956 $ killdaemons.py
961 $ killdaemons.py
957
962
958 $ cat > .hg/hgrc << EOF
963 $ cat > .hg/hgrc << EOF
959 > [web]
964 > [web]
960 > highlightfiles = **
965 > highlightfiles = **
961 > EOF
966 > EOF
962
967
963 $ cat > unknownfile << EOF
968 $ cat > unknownfile << EOF
964 > #!$PYTHON
969 > #!$PYTHON
965 > def foo():
970 > def foo():
966 > pass
971 > pass
967 > EOF
972 > EOF
968
973
969 $ hg add unknownfile
974 $ hg add unknownfile
970 $ hg commit -m unknown unknownfile
975 $ hg commit -m unknown unknownfile
971
976
972 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
977 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
973 $ cat hg.pid >> $DAEMON_PIDS
978 $ cat hg.pid >> $DAEMON_PIDS
974
979
975 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
980 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
976 <span id="l2"><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span></span><a href="#l2"></a>
981 <span id="l2"><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span></span><a href="#l2"></a>
977
982
978 We can prevent Pygments from falling back to a non filename-based
983 We can prevent Pygments from falling back to a non filename-based
979 detection mode
984 detection mode
980
985
981 $ cat > .hg/hgrc << EOF
986 $ cat > .hg/hgrc << EOF
982 > [web]
987 > [web]
983 > highlightfiles = **
988 > highlightfiles = **
984 > highlightonlymatchfilename = true
989 > highlightonlymatchfilename = true
985 > EOF
990 > EOF
986
991
987 $ killdaemons.py
992 $ killdaemons.py
988 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
993 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
989 $ cat hg.pid >> $DAEMON_PIDS
994 $ cat hg.pid >> $DAEMON_PIDS
990 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
995 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
991 <span id="l2">def foo():</span><a href="#l2"></a>
996 <span id="l2">def foo():</span><a href="#l2"></a>
992
997
993 $ cd ..
998 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now